aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actioncable/README.md2
-rw-r--r--actioncable/lib/action_cable/connection/base.rb18
-rw-r--r--actionpack/CHANGELOG.md38
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb97
-rw-r--r--actionpack/lib/action_dispatch/testing/test_response.rb6
-rw-r--r--actionpack/test/controller/integration_test.rb56
-rw-r--r--actionview/lib/action_view/dependency_tracker.rb24
-rw-r--r--actionview/lib/action_view/template/error.rb4
-rw-r--r--activerecord/CHANGELOG.md4
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb8
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb10
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb24
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb22
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb4
-rw-r--r--activerecord/lib/active_record/querying.rb6
-rw-r--r--activerecord/lib/active_record/reflection.rb54
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb32
-rw-r--r--activerecord/lib/active_record/statement_cache.rb2
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb10
-rw-r--r--activesupport/CHANGELOG.md4
-rw-r--r--activesupport/lib/active_support/core_ext/array/access.rb14
-rw-r--r--activesupport/lib/active_support/deprecation/behaviors.rb2
-rw-r--r--activesupport/test/core_ext/array/access_test.rb2
-rw-r--r--activesupport/test/deprecation_test.rb4
-rw-r--r--guides/source/active_record_migrations.md4
-rw-r--r--guides/source/active_record_querying.md22
-rw-r--r--guides/source/active_support_core_extensions.md2
-rw-r--r--guides/source/association_basics.md4
-rw-r--r--guides/source/security.md4
-rw-r--r--railties/CHANGELOG.md10
-rw-r--r--railties/lib/rails/generators/app_base.rb4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt2
-rw-r--r--railties/lib/rails/generators/test_unit/model/templates/fixtures.yml2
-rw-r--r--railties/test/application/rake_test.rb9
-rw-r--r--railties/test/generators/app_generator_test.rb23
-rw-r--r--railties/test/generators/model_generator_test.rb10
-rw-r--r--railties/test/isolation/abstract_unit.rb15
41 files changed, 463 insertions, 120 deletions
diff --git a/actioncable/README.md b/actioncable/README.md
index 5029f583cb..3ed8a23a29 100644
--- a/actioncable/README.md
+++ b/actioncable/README.md
@@ -188,7 +188,7 @@ can be reached as remote procedure calls via a subscription's `perform` method.
The appearance example was all about exposing server functionality to client-side invocation over the WebSocket connection.
But the great thing about WebSockets is that it's a two-way street. So now let's show an example where the server invokes
-action on the client.
+an action on the client.
This is a web notification channel that allows you to trigger client-side web notifications when you broadcast to the right
streams:
diff --git a/actioncable/lib/action_cable/connection/base.rb b/actioncable/lib/action_cable/connection/base.rb
index b5f898436a..e23789978c 100644
--- a/actioncable/lib/action_cable/connection/base.rb
+++ b/actioncable/lib/action_cable/connection/base.rb
@@ -185,12 +185,14 @@ module ActionCable
end
def respond_to_successful_request
+ logger.info successful_request_message
websocket.rack_response
end
def respond_to_invalid_request
close if websocket.alive?
+ logger.error invalid_request_message
logger.info finished_request_message
[ 404, { 'Content-Type' => 'text/plain' }, [ 'Page not found' ] ]
end
@@ -205,7 +207,7 @@ module ActionCable
'Started %s "%s"%s for %s at %s' % [
request.request_method,
request.filtered_path,
- websocket.possible? ? ' [WebSocket]' : '',
+ websocket.possible? ? ' [WebSocket]' : '[non-WebSocket]',
request.ip,
Time.now.to_s ]
end
@@ -213,10 +215,22 @@ module ActionCable
def finished_request_message
'Finished "%s"%s for %s at %s' % [
request.filtered_path,
- websocket.possible? ? ' [WebSocket]' : '',
+ websocket.possible? ? ' [WebSocket]' : '[non-WebSocket]',
request.ip,
Time.now.to_s ]
end
+
+ def invalid_request_message
+ 'Failed to upgrade to WebSocket (REQUEST_METHOD: %s, HTTP_CONNECTION: %s, HTTP_UPGRADE: %s)' % [
+ env["REQUEST_METHOD"], env["HTTP_CONNECTION"], env["HTTP_UPGRADE"]
+ ]
+ end
+
+ def successful_request_message
+ 'Successfully upgraded to WebSocket (REQUEST_METHOD: %s, HTTP_CONNECTION: %s, HTTP_UPGRADE: %s)' % [
+ env["REQUEST_METHOD"], env["HTTP_CONNECTION"], env["HTTP_UPGRADE"]
+ ]
+ end
end
end
end
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 93e598e493..bf964d06e9 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,41 @@
+* Add request encoding and response parsing to integration tests.
+
+ What previously was:
+
+ ```ruby
+ require 'test_helper'
+
+ class ApiTest < ActionDispatch::IntegrationTest
+ test 'creates articles' do
+ assert_difference -> { Article.count } do
+ post articles_path(format: :json),
+ params: { article: { title: 'Ahoy!' } }.to_json,
+ headers: { 'Content-Type' => 'application/json' }
+ end
+
+ assert_equal({ id: Article.last.id, title: 'Ahoy!' }, JSON.parse(response.body))
+ end
+ end
+ ```
+
+ Can now be written as:
+
+ ```ruby
+ require 'test_helper'
+
+ class ApiTest < ActionDispatch::IntegrationTest
+ test 'creates articles' do
+ assert_difference -> { Article.count } do
+ post articles_path, { article: { title: 'Ahoy!' } }, as: :json
+ end
+
+ assert_equal({ id: Article.last.id, title: 'Ahoy!' }, response.parsed_body)
+ end
+ end
+ ```
+
+ *Kasper Timm Hansen*
+
* Add image/svg+xml as a default mime type.
*DHH*
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 6f51accee7..742bce1ca6 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -321,7 +321,9 @@ module ActionDispatch
end
# Performs the actual request.
- def process(method, path, params: nil, headers: nil, env: nil, xhr: false)
+ def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
+ request_encoder = RequestEncoder.encoder(as)
+
if path =~ %r{://}
location = URI.parse(path)
https! URI::HTTPS === location if location.scheme
@@ -330,14 +332,17 @@ module ActionDispatch
url_host += ":#{location.port}" if default != location.port
host! url_host
end
- path = location.query ? "#{location.path}?#{location.query}" : location.path
+ path = request_encoder.append_format_to location.path
+ path = location.query ? "#{path}?#{location.query}" : path
+ else
+ path = request_encoder.append_format_to path
end
hostname, port = host.split(':')
request_env = {
:method => method,
- :params => params,
+ :params => request_encoder.encode_params(params),
"SERVER_NAME" => hostname,
"SERVER_PORT" => port || (https? ? "443" : "80"),
@@ -347,7 +352,7 @@ module ActionDispatch
"REQUEST_URI" => path,
"HTTP_HOST" => host,
"REMOTE_ADDR" => remote_addr,
- "CONTENT_TYPE" => "application/x-www-form-urlencoded",
+ "CONTENT_TYPE" => request_encoder.content_type,
"HTTP_ACCEPT" => accept
}
@@ -376,6 +381,7 @@ module ActionDispatch
response = _mock_session.last_response
@response = ActionDispatch::TestResponse.from_response(response)
@response.request = @request
+ @response.response_parser = request_encoder
@html_document = nil
@url_options = nil
@@ -387,6 +393,53 @@ module ActionDispatch
def build_full_uri(path, env)
"#{env['rack.url_scheme']}://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{path}"
end
+
+ class RequestEncoder # :nodoc:
+ @encoders = {}
+
+ def initialize(mime_name, param_encoder, response_parser, url_encoded_form = false)
+ @mime = Mime[mime_name]
+
+ unless @mime
+ raise ArgumentError, "Can't register a request encoder for " \
+ "unregistered MIME Type: #{mime_name}. See `Mime::Type.register`."
+ end
+
+ @url_encoded_form = url_encoded_form
+ @path_format = ".#{@mime.symbol}" unless @url_encoded_form
+ @response_parser = response_parser || -> body { body }
+ @param_encoder = param_encoder || :"to_#{@mime.symbol}".to_proc
+ end
+
+ def append_format_to(path)
+ path << @path_format unless @url_encoded_form
+ path
+ end
+
+ def content_type
+ @mime.to_s
+ end
+
+ def encode_params(params)
+ @param_encoder.call(params)
+ end
+
+ def parse_body(body)
+ @response_parser.call(body)
+ end
+
+ def self.encoder(name)
+ @encoders[name] || WWWFormEncoder
+ end
+
+ def self.register_encoder(mime_name, param_encoder: nil, response_parser: nil)
+ @encoders[mime_name] = new(mime_name, param_encoder, response_parser)
+ end
+
+ register_encoder :json, response_parser: -> body { JSON.parse(body) }
+
+ WWWFormEncoder = new(:url_encoded_form, -> params { params }, nil, true)
+ end
end
module Runner
@@ -643,6 +696,38 @@ module ActionDispatch
# end
# end
#
+ # You can also test your JSON API easily by setting what the request should
+ # be encoded as:
+ #
+ # require 'test_helper'
+ #
+ # class ApiTest < ActionDispatch::IntegrationTest
+ # test 'creates articles' do
+ # assert_difference -> { Article.count } do
+ # post articles_path, params: { article: { title: 'Ahoy!' } }, as: :json
+ # end
+ #
+ # assert_response :success
+ # assert_equal({ id: Arcticle.last.id, title: 'Ahoy!' }, response.parsed_body)
+ # end
+ # end
+ #
+ # The `as` option sets the format to JSON, sets the content type to
+ # 'application/json' and encodes the parameters as JSON.
+ #
+ # Calling `parsed_body` on the response parses the response body as what
+ # the last request was encoded as. If the request wasn't encoded `as` something,
+ # it's the same as calling `body`.
+ #
+ # For any custom MIME Types you've registered, you can even add your own encoders with:
+ #
+ # ActionDispatch::IntegrationTest.register_encoder :wibble,
+ # param_encoder: -> params { params.to_wibble },
+ # response_parser: -> body { body }
+ #
+ # Where `param_encoder` defines how the params should be encoded and
+ # `response_parser` defines how the response body should be parsed.
+ #
# Consult the Rails Testing Guide for more.
class IntegrationTest < ActiveSupport::TestCase
@@ -671,5 +756,9 @@ module ActionDispatch
def document_root_element
html_document.root
end
+
+ def self.register_encoder(*args)
+ Integration::Session::RequestEncoder.register_encoder(*args)
+ end
end
end
diff --git a/actionpack/lib/action_dispatch/testing/test_response.rb b/actionpack/lib/action_dispatch/testing/test_response.rb
index 4b79a90242..58d3e6eb0f 100644
--- a/actionpack/lib/action_dispatch/testing/test_response.rb
+++ b/actionpack/lib/action_dispatch/testing/test_response.rb
@@ -18,5 +18,11 @@ module ActionDispatch
# Was there a server-side error?
alias_method :error?, :server_error?
+
+ attr_writer :response_parser # :nodoc:
+
+ def parsed_body
+ @response_parser.parse_body(body)
+ end
end
end
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index d0a1d1285f..cb524bacb2 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -1126,3 +1126,59 @@ class IntegrationRequestsWithSessionSetup < ActionDispatch::IntegrationTest
assert_equal({"user_name"=>"david"}, cookies.to_hash)
end
end
+
+class IntegrationRequestEncodersTest < ActionDispatch::IntegrationTest
+ class FooController < ActionController::Base
+ def foos_json
+ render json: params.permit(:foo)
+ end
+
+ def foos_wibble
+ render plain: 'ok'
+ end
+ end
+
+ def test_encoding_as_json
+ post_to_foos as: :json do
+ assert_response :success
+ assert_match 'foos_json.json', request.path
+ assert_equal 'application/json', request.content_type
+ assert_equal({ 'foo' => 'fighters' }, request.request_parameters)
+ assert_equal({ 'foo' => 'fighters' }, response.parsed_body)
+ end
+ end
+
+ def test_encoding_as_without_mime_registration
+ assert_raise ArgumentError do
+ ActionDispatch::IntegrationTest.register_encoder :wibble
+ end
+ end
+
+ def test_registering_custom_encoder
+ Mime::Type.register 'text/wibble', :wibble
+
+ ActionDispatch::IntegrationTest.register_encoder(:wibble,
+ param_encoder: -> params { params })
+
+ post_to_foos as: :wibble do
+ assert_response :success
+ assert_match 'foos_wibble.wibble', request.path
+ assert_equal 'text/wibble', request.content_type
+ assert_equal Hash.new, request.request_parameters # Unregistered MIME Type can't be parsed.
+ assert_equal 'ok', response.parsed_body
+ end
+ ensure
+ Mime::Type.unregister :wibble
+ end
+
+ private
+ def post_to_foos(as:)
+ with_routing do |routes|
+ routes.draw { post ':action' => FooController }
+
+ post "/foos_#{as}", params: { foo: 'fighters' }, as: as
+
+ yield
+ end
+ end
+end
diff --git a/actionview/lib/action_view/dependency_tracker.rb b/actionview/lib/action_view/dependency_tracker.rb
index 5a4c3ea3fe..7731773040 100644
--- a/actionview/lib/action_view/dependency_tracker.rb
+++ b/actionview/lib/action_view/dependency_tracker.rb
@@ -7,18 +7,20 @@ module ActionView
def self.find_dependencies(name, template, view_paths = nil)
tracker = @trackers[template.handler]
- return [] unless tracker.present?
+ return [] unless tracker
- if tracker.respond_to?(:supports_view_paths?) && tracker.supports_view_paths?
- tracker.call(name, template, view_paths)
- else
- tracker.call(name, template)
- end
+ tracker.call(name, template, view_paths)
end
def self.register_tracker(extension, tracker)
handler = Template.handler_for_extension(extension)
- @trackers[handler] = tracker
+ if tracker.respond_to?(:supports_view_paths?)
+ @trackers[handler] = tracker
+ else
+ @trackers[handler] = lambda { |name, template, _|
+ tracker.call(name, template)
+ }
+ end
end
def self.remove_tracker(handler)
@@ -151,11 +153,11 @@ module ActionView
def resolve_directories(wildcard_dependencies)
return [] unless @view_paths
- wildcard_dependencies.each_with_object([]) do |query, templates|
- @view_paths.find_all_with_query(query).each do |template|
- templates << "#{File.dirname(query)}/#{File.basename(template).split('.').first}"
+ wildcard_dependencies.flat_map { |query, templates|
+ @view_paths.find_all_with_query(query).map do |template|
+ "#{File.dirname(query)}/#{File.basename(template).split('.').first}"
end
- end
+ }.sort
end
def explicit_dependencies
diff --git a/actionview/lib/action_view/template/error.rb b/actionview/lib/action_view/template/error.rb
index b03b197cb5..ccee785d3e 100644
--- a/actionview/lib/action_view/template/error.rb
+++ b/actionview/lib/action_view/template/error.rb
@@ -59,6 +59,9 @@ module ActionView
class Error < ActionViewError #:nodoc:
SOURCE_CODE_RADIUS = 3
+ # Override to prevent #cause resetting during re-raise.
+ attr_reader :cause
+
def initialize(template, original_exception = nil)
if original_exception
ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \
@@ -67,6 +70,7 @@ module ActionView
super($!.message)
set_backtrace($!.backtrace)
+ @cause = $!
@template, @sub_templates = template, nil
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index a4cf6023e1..be96f9fa2a 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Add ActiveRecord `#second_to_last` and `#third_to_last` methods.
+
+ *Brian Christian*
+
* Added `numeric` helper into migrations.
Example:
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 9f2c7292ea..2dca6b612e 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -136,6 +136,14 @@ module ActiveRecord
first_nth_or_last(:forty_two, *args)
end
+ def third_to_last(*args)
+ first_nth_or_last(:third_to_last, *args)
+ end
+
+ def second_to_last(*args)
+ first_nth_or_last(:second_to_last, *args)
+ end
+
def last(*args)
first_nth_or_last(:last, *args)
end
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index fe693cfbb6..2a9627a474 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -197,6 +197,16 @@ module ActiveRecord
@association.forty_two(*args)
end
+ # Same as #first except returns only the third-to-last record.
+ def third_to_last(*args)
+ @association.third_to_last(*args)
+ end
+
+ # Same as #first except returns only the second-to-last record.
+ def second_to_last(*args)
+ @association.second_to_last(*args)
+ end
+
# Returns the last record, or the last +n+ records, from the collection.
# If the collection is empty, the first form returns +nil+, and the second
# form returns an empty array.
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 423a93964e..e902eb7531 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -34,30 +34,6 @@ module ActiveRecord
BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
- class AttributeMethodCache
- def initialize
- @module = Module.new
- @method_cache = Concurrent::Map.new
- end
-
- def [](name)
- @method_cache.compute_if_absent(name) do
- safe_name = name.unpack('h*'.freeze).first
- temp_method = "__temp__#{safe_name}"
- ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
- @module.module_eval method_body(temp_method, safe_name), __FILE__, __LINE__
- @module.instance_method temp_method
- end
- end
-
- private
-
- # Override this method in the subclasses for method body.
- def method_body(method_name, const_name)
- raise NotImplementedError, "Subclasses must implement a method_body(method_name, const_name) method."
- end
- end
-
class GeneratedAttributeMethods < Module; end # :nodoc:
module ClassMethods
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 5197e21fa4..ab2ecaa7c5 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -1,8 +1,11 @@
module ActiveRecord
module AttributeMethods
module Read
- ReaderMethodCache = Class.new(AttributeMethodCache) {
- private
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ protected
+
# We want to generate the methods via module_eval rather than
# define_method, because define_method is slower on dispatch.
# Evaluating many similar methods may use more memory as the instruction
@@ -21,21 +24,6 @@ module ActiveRecord
# to allocate an object on each call to the attribute method.
# Making it frozen means that it doesn't get duped when used to
# key the @attributes in read_attribute.
- def method_body(method_name, const_name)
- <<-EOMETHOD
- def #{method_name}
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
- _read_attribute(name) { |n| missing_attribute(n, caller) }
- end
- EOMETHOD
- end
- }.new
-
- extend ActiveSupport::Concern
-
- module ClassMethods
- protected
-
def define_method_attribute(name)
safe_name = name.unpack('h*'.freeze).first
temp_method = "__temp__#{safe_name}"
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index bbf2a51a0e..5599b590ca 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -1,19 +1,6 @@
module ActiveRecord
module AttributeMethods
module Write
- WriterMethodCache = Class.new(AttributeMethodCache) {
- private
-
- def method_body(method_name, const_name)
- <<-EOMETHOD
- def #{method_name}(value)
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
- write_attribute(name, value)
- end
- EOMETHOD
- end
- }.new
-
extend ActiveSupport::Concern
included do
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index 7e0c9f7837..bb5119d64e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -27,10 +27,10 @@ module ActiveRecord
end
# Returns an ActiveRecord::Result instance.
- def select_all(arel, name = nil, binds = [])
+ def select_all(arel, name = nil, binds = [], preparable: nil)
arel, binds = binds_from_relation arel, binds
sql = to_sql(arel, binds)
- if arel.is_a?(String)
+ if arel.is_a?(String) && preparable.nil?
preparable = false
else
preparable = visitor.preparable
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 5e27cfe507..33dbab41cb 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -61,11 +61,11 @@ module ActiveRecord
@query_cache.clear
end
- def select_all(arel, name = nil, binds = [])
+ def select_all(arel, name = nil, binds = [], preparable: nil)
if @query_cache_enabled && !locked?(arel)
arel, binds = binds_from_relation arel, binds
sql = to_sql(arel, binds)
- cache_sql(sql, binds) { super(sql, name, binds) }
+ cache_sql(sql, binds) { super(sql, name, binds, preparable: visitor.preparable) }
else
super
end
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 1f429cfd94..de5b42e987 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module Querying
delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, to: :all
- delegate :second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!, :forty_two, :forty_two!, to: :all
+ delegate :second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!, :forty_two, :forty_two!, :third_to_last, :third_to_last!, :second_to_last, :second_to_last!, to: :all
delegate :first_or_create, :first_or_create!, :first_or_initialize, to: :all
delegate :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, to: :all
delegate :find_by, :find_by!, to: :all
@@ -35,8 +35,8 @@ module ActiveRecord
#
# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
# Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }]
- def find_by_sql(sql, binds = [])
- result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds)
+ def find_by_sql(sql, binds = [], preparable: nil)
+ result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable)
column_types = result_set.column_types.dup
columns_hash.each_key { |k| column_types.delete k }
message_bus = ActiveSupport::Notifications.instrumenter
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 99c0e71f97..956fe7c51e 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -239,6 +239,10 @@ module ActiveRecord
def alias_candidate(name)
"#{plural_name}_#{name}"
end
+
+ def chain
+ collect_join_chain
+ end
end
# Base class for AggregateReflection and AssociationReflection. Objects of
@@ -421,7 +425,7 @@ module ActiveRecord
# A chain of reflections from this one back to the owner. For more see the explanation in
# ThroughReflection.
- def chain
+ def collect_join_chain
[self]
end
@@ -495,6 +499,18 @@ module ActiveRecord
VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
INVALID_AUTOMATIC_INVERSE_OPTIONS = [:conditions, :through, :polymorphic, :foreign_key]
+ def add_as_source(seed)
+ seed
+ end
+
+ def add_as_polymorphic_through(reflection, seed)
+ seed + [PolymorphicReflection.new(self, reflection)]
+ end
+
+ def add_as_through(seed)
+ seed + [self]
+ end
+
protected
def actual_source_reflection # FIXME: this is a horrible name
@@ -742,19 +758,8 @@ module ActiveRecord
# # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
# <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
#
- def chain
- @chain ||= begin
- a = source_reflection.chain
- b = through_reflection.chain.map(&:dup)
-
- if options[:source_type]
- b[0] = PolymorphicReflection.new(b[0], self)
- end
-
- chain = a + b
- chain[0] = self # Use self so we don't lose the information from :source_type
- chain
- end
+ def collect_join_chain
+ collect_join_reflections [self]
end
# This is for clearing cache on the reflection. Useful for tests that need to compare
@@ -913,6 +918,27 @@ module ActiveRecord
scope_chain
end
+ def add_as_source(seed)
+ collect_join_reflections seed
+ end
+
+ def add_as_polymorphic_through(reflection, seed)
+ collect_join_reflections(seed + [PolymorphicReflection.new(self, reflection)])
+ end
+
+ def add_as_through(seed)
+ collect_join_reflections(seed + [self])
+ end
+
+ def collect_join_reflections(seed)
+ a = source_reflection.add_as_source seed
+ if options[:source_type]
+ through_reflection.add_as_polymorphic_through self, a
+ else
+ through_reflection.add_as_through a
+ end
+ end
+
protected
def actual_source_reflection # FIXME: this is a horrible name
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index d48bcea28a..a4280c5f33 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -242,6 +242,38 @@ module ActiveRecord
find_nth! 41
end
+ # Find the third-to-last record.
+ # If no order is defined it will order by primary key.
+ #
+ # Person.third_to_last # returns the third-to-last object fetched by SELECT * FROM people
+ # Person.offset(3).third_to_last # returns the third-to-last object from OFFSET 3
+ # Person.where(["user_name = :u", { u: user_name }]).third_to_last
+ def third_to_last
+ find_nth(-3)
+ end
+
+ # Same as #third_to_last but raises ActiveRecord::RecordNotFound if no record
+ # is found.
+ def third_to_last!
+ find_nth!(-3)
+ end
+
+ # Find the second-to-last record.
+ # If no order is defined it will order by primary key.
+ #
+ # Person.second_to_last # returns the second-to-last object fetched by SELECT * FROM people
+ # Person.offset(3).second_to_last # returns the second-to-last object from OFFSET 3
+ # Person.where(["user_name = :u", { u: user_name }]).second_to_last
+ def second_to_last
+ find_nth(-2)
+ end
+
+ # Same as #second_to_last but raises ActiveRecord::RecordNotFound if no record
+ # is found.
+ def second_to_last!
+ find_nth!(-2)
+ end
+
# Returns true if a record exists in the table that matches the +id+ or
# conditions given, or false otherwise. The argument can take six forms:
#
diff --git a/activerecord/lib/active_record/statement_cache.rb b/activerecord/lib/active_record/statement_cache.rb
index f6b0efb88a..6c896ccea6 100644
--- a/activerecord/lib/active_record/statement_cache.rb
+++ b/activerecord/lib/active_record/statement_cache.rb
@@ -106,7 +106,7 @@ module ActiveRecord
sql = query_builder.sql_for bind_values, connection
- klass.find_by_sql sql, bind_values
+ klass.find_by_sql(sql, bind_values, preparable: true)
end
alias :call :execute
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index ecaa521283..e975f4fbdd 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -408,6 +408,16 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
assert_no_queries do
+ bulbs.third_to_last()
+ bulbs.third_to_last({})
+ end
+
+ assert_no_queries do
+ bulbs.second_to_last()
+ bulbs.second_to_last({})
+ end
+
+ assert_no_queries do
bulbs.last()
bulbs.last({})
end
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index bd333da081..3d4cc8fae6 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Add `Array#second_to_last` and `Array#third_to_last` methods.
+
+ *Brian Christian*
+
* Fix regression in `Hash#dig` for HashWithIndifferentAccess.
*Jon Moss*
diff --git a/activesupport/lib/active_support/core_ext/array/access.rb b/activesupport/lib/active_support/core_ext/array/access.rb
index 3177d8498e..37d833887a 100644
--- a/activesupport/lib/active_support/core_ext/array/access.rb
+++ b/activesupport/lib/active_support/core_ext/array/access.rb
@@ -73,4 +73,18 @@ class Array
def forty_two
self[41]
end
+
+ # Equal to <tt>self[-3]</tt>.
+ #
+ # %w( a b c d e ).third_to_last # => "c"
+ def third_to_last
+ self[-3]
+ end
+
+ # Equal to <tt>self[-2]</tt>.
+ #
+ # %w( a b c d e ).second_to_last # => "d"
+ def second_to_last
+ self[-2]
+ end
end
diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb
index 0de891f1a2..dc24e2d0e1 100644
--- a/activesupport/lib/active_support/deprecation/behaviors.rb
+++ b/activesupport/lib/active_support/deprecation/behaviors.rb
@@ -11,7 +11,7 @@ module ActiveSupport
DEFAULT_BEHAVIORS = {
raise: ->(message, callstack) {
e = DeprecationException.new(message)
- e.set_backtrace(callstack)
+ e.set_backtrace(callstack.map(&:to_s))
raise e
},
diff --git a/activesupport/test/core_ext/array/access_test.rb b/activesupport/test/core_ext/array/access_test.rb
index 3f1e0c4cb4..1d834667f0 100644
--- a/activesupport/test/core_ext/array/access_test.rb
+++ b/activesupport/test/core_ext/array/access_test.rb
@@ -26,6 +26,8 @@ class AccessTest < ActiveSupport::TestCase
assert_equal array[3], array.fourth
assert_equal array[4], array.fifth
assert_equal array[41], array.forty_two
+ assert_equal array[-3], array.third_to_last
+ assert_equal array[-2], array.second_to_last
end
def test_without
diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb
index 58a0a3964d..45c88b79cb 100644
--- a/activesupport/test/deprecation_test.rb
+++ b/activesupport/test/deprecation_test.rb
@@ -105,13 +105,13 @@ class DeprecationTest < ActiveSupport::TestCase
ActiveSupport::Deprecation.behavior = :raise
message = 'Revise this deprecated stuff now!'
- callstack = %w(foo bar baz)
+ callstack = caller_locations
e = assert_raise ActiveSupport::DeprecationException do
ActiveSupport::Deprecation.behavior.first.call(message, callstack)
end
assert_equal message, e.message
- assert_equal callstack, e.backtrace
+ assert_equal callstack.map(&:to_s), e.backtrace.map(&:to_s)
end
def test_default_stderr_behavior
diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md
index 83f4b951ee..bd7dbd0f11 100644
--- a/guides/source/active_record_migrations.md
+++ b/guides/source/active_record_migrations.md
@@ -883,7 +883,7 @@ Changing Existing Migrations
----------------------------
Occasionally you will make a mistake when writing a migration. If you have
-already run the migration then you cannot just edit the migration and run the
+already run the migration, then you cannot just edit the migration and run the
migration again: Rails thinks it has already run the migration and so will do
nothing when you run `rails db:migrate`. You must rollback the migration (for
example with `bin/rails db:rollback`), edit your migration and then run
@@ -933,7 +933,7 @@ There are two ways to dump the schema. This is set in `config/application.rb`
by the `config.active_record.schema_format` setting, which may be either `:sql`
or `:ruby`.
-If `:ruby` is selected then the schema is stored in `db/schema.rb`. If you look
+If `:ruby` is selected, then the schema is stored in `db/schema.rb`. If you look
at this file you'll find that it looks an awful lot like one very big
migration:
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 63658e7c8b..1235c04c50 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -1296,6 +1296,28 @@ Using a class method is the preferred way to accept arguments for scopes. These
category.articles.created_before(time)
```
+### Using conditionals
+
+Your scope can utilize conditionals:
+
+```ruby
+class Article < ApplicationRecord
+ scope :created_before, ->(time) { where("created_at < ?", time) if time.present? }
+end
+```
+
+Like the other examples, this will behave similarly to a class method.
+
+```ruby
+class Article < ApplicationRecord
+ def self.created_before(time)
+ where("created_at < ?", time) if time.present?
+ end
+end
+```
+
+However, there is one important caveat: A scope will always return an `ActiveRecord::Relation` object, even if the conditional evaluates to `false`, whereas a class method, will return `nil`. This can cause `NoMethodError` when chaining class methods with conditionals, if any of the conditionals return `false`.
+
### Applying a default scope
If we wish for a scope to be applied across all queries to the model we can use the
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index 0aca6db9b6..10122629b2 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -2240,7 +2240,7 @@ Similarly, `from` returns the tail from the element at the passed index to the e
[].from(0) # => []
```
-The methods `second`, `third`, `fourth`, and `fifth` return the corresponding element (`first` is built-in). Thanks to social wisdom and positive constructiveness all around, `forty_two` is also available.
+The methods `second`, `third`, `fourth`, and `fifth` return the corresponding element, as do `second_to_last` and `third_to_last` (`first` and `last` are built-in). Thanks to social wisdom and positive constructiveness all around, `forty_two` is also available.
```ruby
%w(a b c d).third # => "c"
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index 3386791cdb..09ab64837a 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -713,7 +713,7 @@ By default, Active Record doesn't know about the connection between these associ
```ruby
a = Author.first
-b = c.books.first
+b = a.books.first
a.first_name == b.author.first_name # => true
a.first_name = 'Manny'
a.first_name == b.author.first_name # => false
@@ -735,7 +735,7 @@ With these changes, Active Record will only load one copy of the author object,
```ruby
a = author.first
-b = c.books.first
+b = a.books.first
a.first_name == b.author.first_name # => true
a.first_name = 'Manny'
a.first_name == b.author.first_name # => true
diff --git a/guides/source/security.md b/guides/source/security.md
index b30bed3767..98324141cc 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -23,7 +23,7 @@ Web application frameworks are made to help developers build web applications. S
In general there is no such thing as plug-n-play security. Security depends on the people using the framework, and sometimes on the development method. And it depends on all layers of a web application environment: The back-end storage, the web server and the web application itself (and possibly other layers or applications).
-The Gartner Group however estimates that 75% of attacks are at the web application layer, and found out "that out of 300 audited sites, 97% are vulnerable to attack". This is because web applications are relatively easy to attack, as they are simple to understand and manipulate, even by the lay person.
+The Gartner Group, however, estimates that 75% of attacks are at the web application layer, and found out "that out of 300 audited sites, 97% are vulnerable to attack". This is because web applications are relatively easy to attack, as they are simple to understand and manipulate, even by the lay person.
The threats against web applications include user account hijacking, bypass of access control, reading or modifying sensitive data, or presenting fraudulent content. Or an attacker might be able to install a Trojan horse program or unsolicited e-mail sending software, aim at financial enrichment or cause brand name damage by modifying company resources. In order to prevent attacks, minimize their impact and remove points of attack, first of all, you have to fully understand the attack methods in order to find the correct countermeasures. That is what this guide aims at.
@@ -62,7 +62,7 @@ Many web applications have an authentication system: a user provides a user name
Hence, the cookie serves as temporary authentication for the web application. Anyone who seizes a cookie from someone else, may use the web application as this user - with possibly severe consequences. Here are some ways to hijack a session, and their countermeasures:
-* Sniff the cookie in an insecure network. A wireless LAN can be an example of such a network. In an unencrypted wireless LAN it is especially easy to listen to the traffic of all connected clients. For the web application builder this means to _provide a secure connection over SSL_. In Rails 3.1 and later, this could be accomplished by always forcing SSL connection in your application config file:
+* Sniff the cookie in an insecure network. A wireless LAN can be an example of such a network. In an unencrypted wireless LAN, it is especially easy to listen to the traffic of all connected clients. For the web application builder this means to _provide a secure connection over SSL_. In Rails 3.1 and later, this could be accomplished by always forcing SSL connection in your application config file:
```ruby
config.force_ssl = true
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 8f4dc736a8..2506baac16 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,13 @@
+* The Gemfiles of new applications include spring-watcher-listen on Linux and
+ Mac OS X (unless --skip-spring).
+
+ *Xavier Noria*
+
+* New applications are generated with the evented file system monitor enabled
+ on Linux and Mac OS X.
+
+ *Xavier Noria*
+
* Add dummy files for apple-touch-icon.png and apple-touch-icon.png. GH#23427
*Alexey Zabelin*
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 9b1e16a7a3..8f8c2ec9e1 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -390,6 +390,10 @@ module Rails
!options[:skip_spring] && !options.dev? && Process.respond_to?(:fork) && !RUBY_PLATFORM.include?("cygwin")
end
+ def os_supports_listen_out_of_the_box?
+ RbConfig::CONFIG['host_os'] =~ /darwin|linux/
+ end
+
def run_bundle
bundle_command('install') if bundle_install?
end
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index 3825dc4e38..c3fad31f23 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -38,9 +38,15 @@ group :development do
gem 'web-console', '~> 3.0'
<%- end -%>
<%- end -%>
+<% if os_supports_listen_out_of_the_box? -%>
+ gem 'listen', '~> 3.0.5'
+<% end -%>
<% if spring_install? -%>
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
+<% if os_supports_listen_out_of_the_box? -%>
+ gem 'spring-watcher-listen', '~> 2.0.0'
+<% end -%>
<% end -%>
end
<% end -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
index d51b2ec199..bd5c0b10f6 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
@@ -19,7 +19,7 @@ default: &default
encoding: unicode
# For details on connection pooling, see rails configuration guide
# http://guides.rubyonrails.org/configuring.html#database-pooling
- pool: 5
+ pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
index fd41372d9c..3451ade158 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -58,5 +58,5 @@ Rails.application.configure do
# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
- # config.file_watcher = ActiveSupport::EventedFileUpdateChecker
+ <%= '# ' unless os_supports_listen_out_of_the_box? %>config.file_watcher = ActiveSupport::EventedFileUpdateChecker
end
diff --git a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml
index 2656767eb4..0681780c97 100644
--- a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml
+++ b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml
@@ -6,7 +6,7 @@
<%- if attribute.password_digest? -%>
password_digest: <%%= BCrypt::Password.create('secret') %>
<%- elsif attribute.reference? -%>
- <%= yaml_key_value(attribute.column_name.sub(/_id$/, ''), attribute.default) %>
+ <%= yaml_key_value(attribute.column_name.sub(/_id$/, ''), attribute.default || name) %>
<%- else -%>
<%= yaml_key_value(attribute.column_name, attribute.default) %>
<%- end -%>
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index 745a3e3ec5..3d3e47de8d 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -291,12 +291,11 @@ module ApplicationTests
assert_no_match(/Errors running/, output)
end
- def test_scaffold_with_references_columns_tests_pass_when_belongs_to_is_optional
- app_file "config/initializers/active_record_belongs_to_required_by_default.rb",
- "Rails.application.config.active_record.belongs_to_required_by_default = false"
-
+ def test_scaffold_with_references_columns_tests_pass_by_default
output = Dir.chdir(app_path) do
- `bin/rails generate scaffold LineItems product:references cart:belongs_to;
+ `bin/rails generate model Product;
+ bin/rails generate model Cart;
+ bin/rails generate scaffold LineItems product:references cart:belongs_to;
RAILS_ENV=test bin/rails db:migrate test`
end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index f483a0bcbd..be05e779ea 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -479,6 +479,29 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_inclusion_of_listen_related_gems
+ run_generator
+ if RbConfig::CONFIG['host_os'] =~ /darwin|linux/
+ assert_gem 'listen'
+ assert_gem 'spring-watcher-listen'
+ else
+ assert_file 'Gemfile' do |content|
+ assert_no_match(/listen/, content)
+ end
+ end
+ end
+
+ def test_evented_file_update_checker_config
+ run_generator
+ assert_file 'config/environments/development.rb' do |content|
+ if RbConfig::CONFIG['host_os'] =~ /darwin|linux/
+ assert_match(/^\s*config.file_watcher = ActiveSupport::EventedFileUpdateChecker/, content)
+ else
+ assert_match(/^\s*# config.file_watcher = ActiveSupport::EventedFileUpdateChecker/, content)
+ end
+ end
+ end
+
def test_template_from_dir_pwd
FileUtils.cd(Rails.root)
assert_match(/It works from file!/, run_generator([destination_root, "-m", "lib/template.rb"]))
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index 814f4c050e..c8c8f0aa3b 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -298,18 +298,18 @@ class ModelGeneratorTest < Rails::Generators::TestCase
def test_fixtures_use_the_references_ids
run_generator ["LineItem", "product:references", "cart:belongs_to"]
- assert_file "test/fixtures/line_items.yml", /product: \n cart: /
+ assert_file "test/fixtures/line_items.yml", /product: one\n cart: one/
assert_generated_fixture("test/fixtures/line_items.yml",
- {"one"=>{"product"=>nil, "cart"=>nil}, "two"=>{"product"=>nil, "cart"=>nil}})
+ {"one"=>{"product"=>"one", "cart"=>"one"}, "two"=>{"product"=>"two", "cart"=>"two"}})
end
def test_fixtures_use_the_references_ids_and_type
run_generator ["LineItem", "product:references{polymorphic}", "cart:belongs_to"]
- assert_file "test/fixtures/line_items.yml", /product: \n product_type: Product\n cart: /
+ assert_file "test/fixtures/line_items.yml", /product: one\n product_type: Product\n cart: one/
assert_generated_fixture("test/fixtures/line_items.yml",
- {"one"=>{"product"=>nil, "product_type"=>"Product", "cart"=>nil},
- "two"=>{"product"=>nil, "product_type"=>"Product", "cart"=>nil}})
+ {"one"=>{"product"=>"one", "product_type"=>"Product", "cart"=>"one"},
+ "two"=>{"product"=>"two", "product_type"=>"Product", "cart"=>"two"}})
end
def test_fixtures_respect_reserved_yml_keywords
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index dddf8bd257..e7a261fa1f 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -154,6 +154,8 @@ module TestHelpers
config.action_controller.allow_forgery_protection = false
config.log_level = :info
RUBY
+
+ remove_from_env_config('development', 'config.file_watcher.*')
end
def teardown_app
@@ -272,10 +274,17 @@ module TestHelpers
end
def remove_from_config(str)
- file = "#{app_path}/config/application.rb"
+ remove_from_file("#{app_path}/config/application.rb", str)
+ end
+
+ def remove_from_env_config(env, str)
+ remove_from_file("#{app_path}/config/environments/#{env}.rb", str)
+ end
+
+ def remove_from_file(file, str)
contents = File.read(file)
- contents.sub!(/#{str}/, "")
- File.open(file, "w+") { |f| f.puts contents }
+ contents.sub!(/#{str}/, '')
+ File.write(file, contents)
end
def app_file(path, contents, mode = 'w')