aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml8
-rw-r--r--actionpack/CHANGELOG.md11
-rw-r--r--actionpack/actionpack.gemspec2
-rw-r--r--actionpack/lib/action_controller/log_subscriber.rb9
-rw-r--r--actionpack/lib/action_controller/metal/params_wrapper.rb2
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb1
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb17
-rw-r--r--actionpack/lib/action_controller/test_case.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb3
-rw-r--r--actionpack/lib/action_dispatch/request/utils.rb4
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb7
-rw-r--r--actionpack/test/controller/filters_test.rb20
-rw-r--r--actionpack/test/dispatch/request/json_params_parsing_test.rb4
-rw-r--r--actionpack/test/dispatch/request/query_string_parsing_test.rb4
-rw-r--r--actionpack/test/dispatch/request_test.rb7
-rw-r--r--actionpack/test/dispatch/test_request_test.rb2
-rw-r--r--actionview/CHANGELOG.md7
-rw-r--r--actionview/lib/action_view/helpers/capture_helper.rb5
-rw-r--r--actionview/test/template/capture_helper_test.rb4
-rw-r--r--actionview/test/template/tag_helper_test.rb9
-rw-r--r--activejob/lib/active_job/queue_adapters/inline_adapter.rb2
-rw-r--r--activemodel/test/cases/callbacks_test.rb19
-rw-r--r--activemodel/test/cases/validations/callbacks_test.rb15
-rw-r--r--activerecord/CHANGELOG.md15
-rw-r--r--activerecord/lib/active_record/attribute.rb18
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb2
-rw-r--r--activerecord/lib/active_record/attribute_set.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb4
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb2
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb2
-rw-r--r--activerecord/lib/active_record/transactions.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/uuid_test.rb12
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb31
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb5
-rw-r--r--activerecord/test/cases/serialized_attribute_test.rb6
-rw-r--r--activerecord/test/cases/type/unsigned_integer_test.rb1
-rw-r--r--activesupport/CHANGELOG.md16
-rw-r--r--activesupport/activesupport.gemspec4
-rw-r--r--activesupport/lib/active_support.rb4
-rw-r--r--activesupport/lib/active_support/callbacks.rb9
-rw-r--r--activesupport/lib/active_support/rescuable.rb4
-rw-r--r--activesupport/lib/active_support/test_case.rb15
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb11
-rw-r--r--activesupport/test/callbacks_test.rb48
-rw-r--r--activesupport/test/rescuable_test.rb29
-rw-r--r--guides/CHANGELOG.md4
-rw-r--r--guides/bug_report_templates/action_controller_master.rb2
-rw-r--r--guides/bug_report_templates/active_record_master.rb2
-rw-r--r--guides/source/4_2_release_notes.md30
-rw-r--r--guides/source/action_controller_overview.md4
-rw-r--r--guides/source/active_record_querying.md60
-rw-r--r--guides/source/asset_pipeline.md5
-rw-r--r--guides/source/association_basics.md8
-rw-r--r--guides/source/command_line.md6
-rw-r--r--guides/source/configuring.md2
-rw-r--r--guides/source/constant_autoloading_and_reloading.md389
-rw-r--r--guides/source/getting_started.md25
-rw-r--r--guides/source/rails_on_rack.md2
-rw-r--r--guides/source/security.md8
-rw-r--r--guides/source/upgrading_ruby_on_rails.md4
-rw-r--r--railties/lib/rails/application/configuration.rb25
-rw-r--r--railties/lib/rails/application/default_middleware_stack.rb2
-rw-r--r--railties/lib/rails/commands/dbconsole.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/setup24
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt4
-rw-r--r--railties/lib/rails/tasks/statistics.rake6
-rw-r--r--railties/test/application/assets_test.rb2
-rw-r--r--railties/test/application/configuration_test.rb58
-rw-r--r--railties/test/application/middleware/sendfile_test.rb2
-rw-r--r--railties/test/application/middleware_test.rb4
-rw-r--r--railties/test/application/rake/dbs_test.rb25
-rw-r--r--railties/test/railties/engine_test.rb2
-rw-r--r--tasks/release.rb2
77 files changed, 768 insertions, 376 deletions
diff --git a/.travis.yml b/.travis.yml
index 6c4d540a8f..bbeca7116f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,11 @@
language: ruby
+sudo: false
script: 'ci/travis.rb'
before_install:
- - travis_retry gem install bundler
- "rvm current | grep 'jruby' && export AR_JDBC=true || echo"
+before_script:
+ - bundle update
+cache: bundler
env:
global:
- JRUBY_OPTS='-J-Xmx1024M'
@@ -41,11 +44,10 @@ notifications:
on_failure: always
rooms:
- secure: "YA1alef1ESHWGFNVwvmVGCkMe4cUy4j+UcNvMUESraceiAfVyRMAovlQBGs6\n9kBRm7DHYBUXYC2ABQoJbQRLDr/1B5JPf/M8+Qd7BKu8tcDC03U01SMHFLpO\naOs/HLXcDxtnnpL07tGVsm0zhMc5N8tq4/L3SHxK7Vi+TacwQzI="
-bundler_args: --path vendor/bundle --without test
+bundler_args: --without test
services:
- memcached
- redis
- rabbitmq
addons:
postgresql: "9.3"
-
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 3b02994459..115ad54190 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,14 @@
+* Stop converting empty arrays in `params` to `nil`
+
+ This behaviour was introduced in response to CVE-2012-2660, CVE-2012-2694
+ and CVE-2013-0155
+
+ ActiveRecord now issues a safe query when passing an empty array into
+ a where clause, so there is no longer a need to defend against this type
+ of input (any nils are still stripped from the array).
+
+ *Chris Sinjakli*
+
* Fixed usage of optional scopes in URL helpers.
*Alex Robbin*
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index 0f5880c1a7..c0040ec28e 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
s.add_dependency 'activesupport', version
- s.add_dependency 'rack', '~> 1.6.0.beta2'
+ s.add_dependency 'rack', '~> 1.6.0'
s.add_dependency 'rack-test', '~> 0.6.2'
s.add_dependency 'rails-html-sanitizer', '~> 1.0', '>= 1.0.1'
s.add_dependency 'rails-dom-testing', '~> 1.0', '>= 1.0.5'
diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb
index d3f93a5352..87609d8aa7 100644
--- a/actionpack/lib/action_controller/log_subscriber.rb
+++ b/actionpack/lib/action_controller/log_subscriber.rb
@@ -53,15 +53,6 @@ module ActionController
end
end
- def deep_munge(event)
- debug do
- "Value for params[:#{event.payload[:keys].join('][:')}] was set "\
- "to nil, because it was one of [], [null] or [null, null, ...]. "\
- "Go to http://guides.rubyonrails.org/security.html#unsafe-query-generation "\
- "for more information."\
- end
- end
-
%w(write_fragment read_fragment exist_fragment?
expire_fragment expire_page write_page).each do |method|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb
index 09c7a6f946..b44493ff7c 100644
--- a/actionpack/lib/action_controller/metal/params_wrapper.rb
+++ b/actionpack/lib/action_controller/metal/params_wrapper.rb
@@ -86,7 +86,7 @@ module ActionController
new name, format, include, exclude, nil, nil
end
- def initialize(name, format, include, exclude, klass, model) # nodoc
+ def initialize(name, format, include, exclude, klass, model) # :nodoc:
super
@include_set = include
@name_set = name
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index fd20682f8f..d1fab27e17 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -209,6 +209,7 @@ module ActionController #:nodoc:
forgery_protection_strategy.new(self).handle_unverified_request
end
+ #:nodoc:
CROSS_ORIGIN_JAVASCRIPT_WARNING = "Security warning: an embedded " \
"<script> tag on another site requested protected JavaScript. " \
"If you know what you're doing, go ahead and disable forgery " \
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index f08c84de5b..01bbd749c1 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -92,7 +92,11 @@ module ActionController
# params.permit(:c)
# # => ActionController::UnpermittedParameters: found unpermitted keys: a, b
#
- # <tt>ActionController::Parameters</tt> is inherited from
+ # Please note that these options *are not thread-safe*. In a multi-threaded
+ # environment they should only be set once at boot-time and never mutated at
+ # runtime.
+ #
+ # <tt>ActionController::Parameters</tt> inherits from
# <tt>ActiveSupport::HashWithIndifferentAccess</tt>, this means
# that you can fetch values using either <tt>:key</tt> or <tt>"key"</tt>.
#
@@ -100,6 +104,7 @@ module ActionController
# params[:key] # => "value"
# params["key"] # => "value"
class Parameters < ActiveSupport::HashWithIndifferentAccess
+ cattr_accessor :permit_all_parameters, instance_accessor: false
cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false
# By default, never raise an UnpermittedParameters exception if these
@@ -122,16 +127,6 @@ module ActionController
always_permitted_parameters
end
- # Returns the value of +permit_all_parameters+.
- def self.permit_all_parameters
- Thread.current[:action_controller_permit_all_parameters]
- end
-
- # Sets the value of +permit_all_parameters+.
- def self.permit_all_parameters=(value)
- Thread.current[:action_controller_permit_all_parameters] = value
- end
-
# Returns a new instance of <tt>ActionController::Parameters</tt>.
# Also, sets the +permitted+ attribute to the default value of
# <tt>ActionController::Parameters.permit_all_parameters</tt>.
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index cd92962dc3..b9172f8fa3 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -145,6 +145,8 @@ module ActionController
assert(@_layouts.keys.any? {|l| l =~ expected_layout }, msg)
when nil, false
assert(@_layouts.empty?, msg)
+ else
+ raise ArgumentError, "assert_template only accepts a String, Symbol, Regexp, nil or false for :layout"
end
end
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index 9c8f65deac..53a98c5d0a 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -72,11 +72,12 @@ module ActionDispatch
end
end
end
+
# Sets the \variant for template.
def variant=(variant)
if variant.is_a?(Symbol)
@variant = [variant]
- elsif variant.is_a?(Array) && variant.any? && variant.all?{ |v| v.is_a?(Symbol) }
+ elsif variant.nil? || variant.is_a?(Array) && variant.any? && variant.all?{ |v| v.is_a?(Symbol) }
@variant = variant
else
raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols, not a #{variant.class}. " \
diff --git a/actionpack/lib/action_dispatch/request/utils.rb b/actionpack/lib/action_dispatch/request/utils.rb
index 9d4f1aa3c5..1c9371d89c 100644
--- a/actionpack/lib/action_dispatch/request/utils.rb
+++ b/actionpack/lib/action_dispatch/request/utils.rb
@@ -16,10 +16,6 @@ module ActionDispatch
when Array
v.grep(Hash) { |x| deep_munge(x, keys) }
v.compact!
- if v.empty?
- hash[k] = nil
- ActiveSupport::Notifications.instrument("deep_munge.action_controller", keys: keys)
- end
when Hash
deep_munge(v, keys)
end
diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index f2a4503f13..d0dfbfbc74 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -575,6 +575,13 @@ class AssertTemplateTest < ActionController::TestCase
end
end
+ def test_fails_expecting_not_known_layout
+ get :render_with_layout
+ assert_raise(ArgumentError) do
+ assert_template :layout => 1
+ end
+ end
+
def test_passes_with_correct_layout
get :render_with_layout
assert_template :layout => "layouts/standard"
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index 38533dbf23..829729eb1b 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -504,7 +504,6 @@ class FilterTest < ActionController::TestCase
def non_yielding_action
@filters << "it didn't yield"
- @filter_return_value
end
def action_three
@@ -528,32 +527,15 @@ class FilterTest < ActionController::TestCase
end
end
- def test_non_yielding_around_actions_not_returning_false_do_not_raise
+ def test_non_yielding_around_actions_do_not_raise
controller = NonYieldingAroundFilterController.new
- controller.instance_variable_set "@filter_return_value", true
assert_nothing_raised do
test_process(controller, "index")
end
end
- def test_non_yielding_around_actions_returning_false_do_not_raise
- controller = NonYieldingAroundFilterController.new
- controller.instance_variable_set "@filter_return_value", false
- assert_nothing_raised do
- test_process(controller, "index")
- end
- end
-
- def test_after_actions_are_not_run_if_around_action_returns_false
- controller = NonYieldingAroundFilterController.new
- controller.instance_variable_set "@filter_return_value", false
- test_process(controller, "index")
- assert_equal ["filter_one", "it didn't yield"], controller.assigns['filters']
- end
-
def test_after_actions_are_not_run_if_around_action_does_not_yield
controller = NonYieldingAroundFilterController.new
- controller.instance_variable_set "@filter_return_value", true
test_process(controller, "index")
assert_equal ["filter_one", "it didn't yield"], controller.assigns['filters']
end
diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb
index c609075e6b..b765a13fa1 100644
--- a/actionpack/test/dispatch/request/json_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb
@@ -39,7 +39,7 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest
test "nils are stripped from collections" do
assert_parses(
- {"person" => nil},
+ {"person" => []},
"{\"person\":[null]}", { 'CONTENT_TYPE' => 'application/json' }
)
assert_parses(
@@ -47,7 +47,7 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest
"{\"person\":[\"foo\",null]}", { 'CONTENT_TYPE' => 'application/json' }
)
assert_parses(
- {"person" => nil},
+ {"person" => []},
"{\"person\":[null, null]}", { 'CONTENT_TYPE' => 'application/json' }
)
end
diff --git a/actionpack/test/dispatch/request/query_string_parsing_test.rb b/actionpack/test/dispatch/request/query_string_parsing_test.rb
index 4e99c26e03..50daafbb54 100644
--- a/actionpack/test/dispatch/request/query_string_parsing_test.rb
+++ b/actionpack/test/dispatch/request/query_string_parsing_test.rb
@@ -95,8 +95,8 @@ class QueryStringParsingTest < ActionDispatch::IntegrationTest
assert_parses({"action" => nil}, "action")
assert_parses({"action" => {"foo" => nil}}, "action[foo]")
assert_parses({"action" => {"foo" => { "bar" => nil }}}, "action[foo][bar]")
- assert_parses({"action" => {"foo" => { "bar" => nil }}}, "action[foo][bar][]")
- assert_parses({"action" => {"foo" => nil }}, "action[foo][]")
+ assert_parses({"action" => {"foo" => { "bar" => [] }}}, "action[foo][bar][]")
+ assert_parses({"action" => {"foo" => [] }}, "action[foo][]")
assert_parses({"action"=>{"foo"=>[{"bar"=>nil}]}}, "action[foo][][bar]")
end
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index beb9085abe..ee8e915610 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -1143,6 +1143,13 @@ class RequestVariant < BaseRequestTest
end
end
+ test "reset variant" do
+ request = stub_request
+
+ request.variant = nil
+ assert_equal nil, request.variant
+ end
+
test "setting variant with non symbol value" do
request = stub_request
assert_raise ArgumentError do
diff --git a/actionpack/test/dispatch/test_request_test.rb b/actionpack/test/dispatch/test_request_test.rb
index 65ad8677f3..cc35d4594e 100644
--- a/actionpack/test/dispatch/test_request_test.rb
+++ b/actionpack/test/dispatch/test_request_test.rb
@@ -18,7 +18,7 @@ class TestRequestTest < ActiveSupport::TestCase
assert_equal "0.0.0.0", env.delete("REMOTE_ADDR")
assert_equal "Rails Testing", env.delete("HTTP_USER_AGENT")
- assert_equal [1, 2], env.delete("rack.version")
+ assert_equal [1, 3], env.delete("rack.version")
assert_equal "", env.delete("rack.input").string
assert_kind_of StringIO, env.delete("rack.errors")
assert_equal true, env.delete("rack.multithread")
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index dbdf682614..729717608f 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,8 +1 @@
Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/actionview/CHANGELOG.md) for previous changes.
-
-* Restore old behaviour for `capture` to return value as is when passed non-String values.
-
- Fixes #17661.
-
- *Carsten Zimmermann*
-
diff --git a/actionview/lib/action_view/helpers/capture_helper.rb b/actionview/lib/action_view/helpers/capture_helper.rb
index 7884e4f1f1..5a3223968f 100644
--- a/actionview/lib/action_view/helpers/capture_helper.rb
+++ b/actionview/lib/action_view/helpers/capture_helper.rb
@@ -31,12 +31,13 @@ module ActionView
# <head><title><%= @greeting %></title></head>
# <body>
# <b><%= @greeting %></b>
- # </body></html>
+ # </body>
+ # </html>
#
def capture(*args)
value = nil
buffer = with_output_buffer { value = yield(*args) }
- if string = buffer.presence || value
+ if string = buffer.presence || value and string.is_a?(String)
ERB::Util.html_escape string
end
end
diff --git a/actionview/test/template/capture_helper_test.rb b/actionview/test/template/capture_helper_test.rb
index b2b8513d4f..f213da5934 100644
--- a/actionview/test/template/capture_helper_test.rb
+++ b/actionview/test/template/capture_helper_test.rb
@@ -24,8 +24,8 @@ class CaptureHelperTest < ActionView::TestCase
assert_equal 'foobar', string
end
- def test_capture_returns_value_even_if_the_returned_value_is_not_a_string
- assert_equal '1', @av.capture { 1 }
+ def test_capture_returns_nil_if_the_returned_value_is_not_a_string
+ assert_nil @av.capture { 1 }
end
def test_capture_escapes_html
diff --git a/actionview/test/template/tag_helper_test.rb b/actionview/test/template/tag_helper_test.rb
index 2b3915edcd..d037447567 100644
--- a/actionview/test/template/tag_helper_test.rb
+++ b/actionview/test/template/tag_helper_test.rb
@@ -50,6 +50,11 @@ class TagHelperTest < ActionView::TestCase
assert_dom_equal "<div>Hello world!</div>", buffer
end
+ def test_content_tag_with_block_in_erb_containing_non_displayed_erb
+ buffer = render_erb("<%= content_tag(:p) do %><% 1 %><% end %>")
+ assert_dom_equal "<p></p>", buffer
+ end
+
def test_content_tag_with_block_and_options_in_erb
buffer = render_erb("<%= content_tag(:div, :class => 'green') do %>Hello world!<% end %>")
assert_dom_equal %(<div class="green">Hello world!</div>), buffer
@@ -65,8 +70,8 @@ class TagHelperTest < ActionView::TestCase
end
def test_content_tag_with_block_and_non_string_outside_out_of_erb
- assert_equal content_tag("p", "1.0", nil, false),
- content_tag("p") { 1.0 }
+ assert_equal content_tag("p"),
+ content_tag("p") { 3.times { "do_something" } }
end
def test_content_tag_nested_in_content_tag_out_of_erb
diff --git a/activejob/lib/active_job/queue_adapters/inline_adapter.rb b/activejob/lib/active_job/queue_adapters/inline_adapter.rb
index 08e26b7418..e25d88e723 100644
--- a/activejob/lib/active_job/queue_adapters/inline_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/inline_adapter.rb
@@ -15,7 +15,7 @@ module ActiveJob
end
def enqueue_at(*) #:nodoc:
- raise NotImplementedError.new("Use a queueing backend to enqueue jobs in the future. Read more at http://guides.rubyonrails.org/v4.2.0/active_job_basics.html")
+ raise NotImplementedError.new("Use a queueing backend to enqueue jobs in the future. Read more at http://guides.rubyonrails.org/active_job_basics.html")
end
end
end
diff --git a/activemodel/test/cases/callbacks_test.rb b/activemodel/test/cases/callbacks_test.rb
index 5fede098d1..2ac681b8d8 100644
--- a/activemodel/test/cases/callbacks_test.rb
+++ b/activemodel/test/cases/callbacks_test.rb
@@ -7,6 +7,7 @@ class CallbacksTest < ActiveModel::TestCase
model.callbacks << :before_around_create
yield
model.callbacks << :after_around_create
+ false
end
end
@@ -24,16 +25,20 @@ class CallbacksTest < ActiveModel::TestCase
after_create do |model|
model.callbacks << :after_create
+ false
end
after_create "@callbacks << :final_callback"
- def initialize(valid=true)
- @callbacks, @valid = [], valid
+ def initialize(options = {})
+ @callbacks = []
+ @valid = options[:valid]
+ @before_create_returns = options[:before_create_returns]
end
def before_create
@callbacks << :before_create
+ @before_create_returns
end
def create
@@ -51,14 +56,20 @@ class CallbacksTest < ActiveModel::TestCase
:after_around_create, :after_create, :final_callback]
end
- test "after callbacks are always appended" do
+ test "the callback chain is not halted when around or after callbacks return false" do
model = ModelCallbacks.new
model.create
assert_equal model.callbacks.last, :final_callback
end
+ test "the callback chain is halted when a before callback returns false" do
+ model = ModelCallbacks.new(before_create_returns: false)
+ model.create
+ assert_equal model.callbacks.last, :before_create
+ end
+
test "after callbacks are not executed if the block returns false" do
- model = ModelCallbacks.new(false)
+ model = ModelCallbacks.new(valid: false)
model.create
assert_equal model.callbacks, [ :before_create, :before_around_create,
:create, :after_around_create]
diff --git a/activemodel/test/cases/validations/callbacks_test.rb b/activemodel/test/cases/validations/callbacks_test.rb
index 6cd0f4ed4d..5d6d48b824 100644
--- a/activemodel/test/cases/validations/callbacks_test.rb
+++ b/activemodel/test/cases/validations/callbacks_test.rb
@@ -30,11 +30,16 @@ class DogWithTwoValidators < Dog
before_validation { self.history << 'before_validation_marker2' }
end
-class DogValidatorReturningFalse < Dog
+class DogBeforeValidatorReturningFalse < Dog
before_validation { false }
before_validation { self.history << 'before_validation_marker2' }
end
+class DogAfterValidatorReturningFalse < Dog
+ after_validation { false }
+ after_validation { self.history << 'after_validation_marker' }
+end
+
class DogWithMissingName < Dog
before_validation { self.history << 'before_validation_marker' }
validates_presence_of :name
@@ -82,12 +87,18 @@ class CallbacksWithMethodNamesShouldBeCalled < ActiveModel::TestCase
end
def test_further_callbacks_should_not_be_called_if_before_validation_returns_false
- d = DogValidatorReturningFalse.new
+ d = DogBeforeValidatorReturningFalse.new
output = d.valid?
assert_equal [], d.history
assert_equal false, output
end
+ def test_further_callbacks_should_be_called_if_after_validation_returns_false
+ d = DogAfterValidatorReturningFalse.new
+ d.valid?
+ assert_equal ['after_validation_marker'], d.history
+ end
+
def test_validation_test_should_be_done
d = DogWithMissingName.new
output = d.valid?
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 72b770e2d5..6494c7374e 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,18 @@
+* Introduce `force: :cascade` option for `create_table`. Using this option
+ will recreate tables even if they have dependent objects (like foreign keys).
+ `db/schema.rb` now uses `force: :cascade`. This makes it possible to
+ reload the schema when foreign keys are in place.
+
+ *Matthew Draper*, *Yves Senn*
+
+* `db:schema:load` and `db:structure:load` no longer purge the database
+ before loading the schema. This is left for the user to do.
+ `db:test:prepare` will still purge the database.
+
+ Closes #17945.
+
+ *Yves Senn*
+
* Fix undesirable RangeError by Type::Integer. Add Type::UnsignedInteger.
*Ryuta Kamizono*
diff --git a/activerecord/lib/active_record/attribute.rb b/activerecord/lib/active_record/attribute.rb
index 8cc1904575..88536eaac0 100644
--- a/activerecord/lib/active_record/attribute.rb
+++ b/activerecord/lib/active_record/attribute.rb
@@ -9,6 +9,10 @@ module ActiveRecord
FromUser.new(name, value, type)
end
+ def with_cast_value(name, value, type)
+ WithCastValue.new(name, value, type)
+ end
+
def null(name)
Null.new(name)
end
@@ -58,6 +62,10 @@ module ActiveRecord
self.class.from_database(name, value, type)
end
+ def with_cast_value(value)
+ self.class.with_cast_value(name, value, type)
+ end
+
def type_cast(*)
raise NotImplementedError
end
@@ -93,6 +101,16 @@ module ActiveRecord
end
end
+ class WithCastValue < Attribute # :nodoc:
+ def type_cast(value)
+ value
+ end
+
+ def changed_in_place_from?(old_value)
+ false
+ end
+ end
+
class Null < Attribute # :nodoc:
def initialize(name)
super(name, nil, Type::Value.new)
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index b3c8209a74..16804f86bf 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -73,7 +73,7 @@ module ActiveRecord
if should_type_cast
@attributes.write_from_user(attr_name, value)
else
- @attributes.write_from_database(attr_name, value)
+ @attributes.write_cast_value(attr_name, value)
end
value
diff --git a/activerecord/lib/active_record/attribute_set.rb b/activerecord/lib/active_record/attribute_set.rb
index 6b1d7ea79e..66fcaf6945 100644
--- a/activerecord/lib/active_record/attribute_set.rb
+++ b/activerecord/lib/active_record/attribute_set.rb
@@ -39,6 +39,10 @@ module ActiveRecord
attributes[name] = self[name].with_value_from_user(value)
end
+ def write_cast_value(name, value)
+ attributes[name] = self[name].with_cast_value(value)
+ end
+
def freeze
@attributes.freeze
super
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index fd52cdf716..b340e8334b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -132,6 +132,7 @@ module ActiveRecord
# Make a temporary table.
# [<tt>:force</tt>]
# Set to true to drop the table before creating it.
+ # Set to +:cascade+ to drop dependent objects as well.
# Defaults to false.
# [<tt>:as</tt>]
# SQL to use to generate the table. When this option is used, the block is
@@ -361,8 +362,12 @@ module ActiveRecord
# Drops a table from the database.
#
- # Although this command ignores +options+ and the block if one is given, it can be helpful
- # to provide these in a migration's +change+ method so it can be reverted.
+ # [<tt>:force</tt>]
+ # Set to +:cascade+ to drop dependent objects as well.
+ # Defaults to false.
+ #
+ # Although this command ignores most +options+ and the block if one is given,
+ # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
# In that case, +options+ and the block will be used by create_table.
def drop_table(table_name, options = {})
execute "DROP TABLE #{quote_table_name(table_name)}"
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 69582ebb6f..ced80bacc8 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -487,7 +487,7 @@ module ActiveRecord
end
def drop_table(table_name, options = {})
- execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE #{quote_table_name(table_name)}"
+ execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
end
def rename_index(table_name, old_name, new_name)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb
index 033e0324bb..97b4fd3d08 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb
@@ -3,14 +3,7 @@ module ActiveRecord
module PostgreSQL
module OID # :nodoc:
class Uuid < Type::Value # :nodoc:
- RFC_4122 = %r{\A\{?[a-fA-F0-9]{4}-?
- [a-fA-F0-9]{4}-?
- [a-fA-F0-9]{4}-?
- [1-5][a-fA-F0-9]{3}-?
- [8-Bab][a-fA-F0-9]{3}-?
- [a-fA-F0-9]{4}-?
- [a-fA-F0-9]{4}-?
- [a-fA-F0-9]{4}-?\}?\z}x
+ ACCEPTABLE_UUID = %r{\A\{?([a-fA-F0-9]{4}-?){8}\}?\z}x
alias_method :type_cast_for_database, :type_cast_from_database
@@ -19,7 +12,7 @@ module ActiveRecord
end
def type_cast(value)
- value.to_s[RFC_4122, 0]
+ value.to_s[ACCEPTABLE_UUID, 0]
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index f29b793a3f..7ba5437474 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -120,6 +120,10 @@ module ActiveRecord
SQL
end
+ def drop_table(table_name, options = {})
+ execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
+ end
+
# Returns true if schema exists.
def schema_exists?(name)
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 77aa2efc47..3c44a625cc 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -126,7 +126,7 @@ HEADER
else
tbl.print ", id: false"
end
- tbl.print ", force: true"
+ tbl.print ", force: :cascade"
tbl.puts " do |t|"
# then dump all non-primary key columns
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index 1228de2bfd..6ed610e5f0 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -214,12 +214,10 @@ module ActiveRecord
case format
when :ruby
check_schema_file(file)
- purge(configuration)
ActiveRecord::Base.establish_connection(configuration)
load(file)
when :sql
check_schema_file(file)
- purge(configuration)
structure_load(configuration, file)
else
raise ArgumentError, "unknown format #{format.inspect}"
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 01e8f69b02..c4a97db582 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -2,7 +2,9 @@ module ActiveRecord
# See ActiveRecord::Transactions::ClassMethods for documentation.
module Transactions
extend ActiveSupport::Concern
+ #:nodoc:
ACTIONS = [:create, :destroy, :update]
+ #:nodoc:
CALLBACK_WARN_MESSAGE = "Currently, Active Record suppresses errors raised " \
"within `after_rollback`/`after_commit` callbacks and only print them to " \
"the logs. In the next version, these errors will no longer be suppressed. " \
diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
index fac21996ed..d6deb6fb1f 100644
--- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
@@ -69,13 +69,18 @@ class PostgresqlUUIDTest < ActiveRecord::TestCase
assert_equal 'foobar', uuid.guid_before_type_cast
end
- def test_rfc_4122_regex
+ def test_acceptable_uuid_regex
# Valid uuids
['A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11',
'{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}',
'a0eebc999c0b4ef8bb6d6bb9bd380a11',
'a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11',
- '{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}'].each do |valid_uuid|
+ '{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}',
+ # The following is not a valid RFC 4122 UUID, but PG doesn't seem to care,
+ # so we shouldn't block it either. (Pay attention to "fb6d" – the "f" here
+ # is invalid – it must be one of 8, 9, A, B, a, b according to the spec.)
+ '{a0eebc99-9c0b-4ef8-fb6d-6bb9bd380a11}',
+ ].each do |valid_uuid|
uuid = UUIDType.new guid: valid_uuid
assert_not_nil uuid.guid
end
@@ -87,7 +92,6 @@ class PostgresqlUUIDTest < ActiveRecord::TestCase
0.0,
true,
'Z0000C99-9C0B-4EF8-BB6D-6BB9BD380A11',
- '{a0eebc99-9c0b-4ef8-fb6d-6bb9bd380a11}',
'a0eebc999r0b4ef8ab6d6bb9bd380a11',
'a0ee-bc99------4ef8-bb6d-6bb9-bd38-0a11',
'{a0eebc99-bb6d6bb9-bd380a11}'].each do |invalid_uuid|
@@ -126,7 +130,7 @@ class PostgresqlLargeKeysTest < ActiveRecord::TestCase
def test_omg
schema = dump_table_schema "big_serials"
- assert_match "create_table \"big_serials\", id: :bigserial, force: true", schema
+ assert_match "create_table \"big_serials\", id: :bigserial", schema
end
def teardown
diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
index d91e7142b3..d774cfebc4 100644
--- a/activerecord/test/cases/migration/change_schema_test.rb
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -415,5 +415,36 @@ module ActiveRecord
yield
end
end
+
+ if ActiveRecord::Base.connection.supports_foreign_keys?
+ class ChangeSchemaWithDependentObjectsTest < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table :trains
+ @connection.create_table(:wagons) { |t| t.references :train }
+ @connection.add_foreign_key :wagons, :trains
+ end
+
+ teardown do
+ [:wagons, :trains].each do |table|
+ @connection.drop_table(table) if @connection.table_exists?(table)
+ end
+ end
+
+ def test_create_table_with_force_cascade_drops_dependent_objects
+ skip "MySQL > 5.5 does not drop dependent objects with DROP TABLE CASCADE" if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
+ # can't re-create table referenced by foreign key
+ assert_raises(ActiveRecord::StatementInvalid) do
+ @connection.create_table :trains, force: true
+ end
+
+ # can recreate referenced table with force: :cascade
+ @connection.create_table :trains, force: :cascade
+ assert_equal [], @connection.foreign_keys(:wagons)
+ end
+ end
+ end
end
end
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 01c686f934..a094136766 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -40,6 +40,11 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_no_match %r{create_table "schema_migrations"}, output
end
+ def test_schema_dump_uses_force_cascade_on_create_table
+ output = dump_table_schema "authors"
+ assert_match %r{create_table "authors", force: :cascade}, output
+ end
+
def test_schema_dump_excludes_sqlite_sequence
output = standard_dump
assert_no_match %r{create_table "sqlite_sequence"}, output
diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb
index 66f345d805..56a0e92e1d 100644
--- a/activerecord/test/cases/serialized_attribute_test.rb
+++ b/activerecord/test/cases/serialized_attribute_test.rb
@@ -243,8 +243,9 @@ class SerializedAttributeTest < ActiveRecord::TestCase
t = Topic.create(content: "first")
assert_equal("first", t.content)
- t.update_column(:content, Topic.type_for_attribute('content').type_cast_for_database("second"))
- assert_equal("second", t.content)
+ t.update_column(:content, ["second"])
+ assert_equal(["second"], t.content)
+ assert_equal(["second"], t.reload.content)
end
def test_serialized_column_should_unserialize_after_update_attribute
@@ -253,5 +254,6 @@ class SerializedAttributeTest < ActiveRecord::TestCase
t.update_attribute(:content, "second")
assert_equal("second", t.content)
+ assert_equal("second", t.reload.content)
end
end
diff --git a/activerecord/test/cases/type/unsigned_integer_test.rb b/activerecord/test/cases/type/unsigned_integer_test.rb
index 90a66b2a26..b6f673109e 100644
--- a/activerecord/test/cases/type/unsigned_integer_test.rb
+++ b/activerecord/test/cases/type/unsigned_integer_test.rb
@@ -1,5 +1,4 @@
require "cases/helper"
-require "models/company"
module ActiveRecord
module Type
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 912b0287af..3f340958ea 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,19 @@
+* Added support for error dispatcher classes in ActiveSupport::Rescuable. Now it acts closer to Ruby's rescue.
+
+ class BaseController < ApplicationController
+ module ErrorDispatcher
+ def self.===(other)
+ Exception === other && other.respond_to?(:status)
+ end
+ end
+
+ rescue_from ErrorDispatcher do |error|
+ render status: error.status, json: { error: error.to_s }
+ end
+ end
+
+ *Genadi Samokovarov*
+
* Added `#verified` and `#valid_message?` methods to `ActiveSupport::MessageVerifier`
Previously, the only way to decode a message with `ActiveSupport::MessageVerifier` was to use `#verify`, which would raise an exception on invalid messages. Now `#verified` can also be used, which returns `nil` on messages that cannot be decoded.
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index f86f5133fc..678799b94e 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -20,9 +20,9 @@ Gem::Specification.new do |s|
s.rdoc_options.concat ['--encoding', 'UTF-8']
- s.add_dependency 'i18n', '>= 0.7.0.beta1', '< 0.8'
+ s.add_dependency 'i18n', '~> 0.7'
s.add_dependency 'json', '~> 1.7', '>= 1.7.7'
s.add_dependency 'tzinfo', '~> 1.1'
s.add_dependency 'minitest', '~> 5.1'
- s.add_dependency 'thread_safe','~> 0.1'
+ s.add_dependency 'thread_safe','~> 0.3', '>= 0.3.4'
end
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index 94468240a4..34040e9d33 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -73,11 +73,11 @@ module ActiveSupport
@@test_order = nil
- def self.test_order=(new_order)
+ def self.test_order=(new_order) # :nodoc:
@@test_order = new_order
end
- def self.test_order
+ def self.test_order # :nodoc:
@@test_order
end
end
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 24c702b602..95dbc9a0cb 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -659,16 +659,17 @@ module ActiveSupport
# ===== Options
#
# * <tt>:terminator</tt> - Determines when a before filter will halt the
- # callback chain, preventing following callbacks from being called and
- # the event from being triggered. This should be a lambda to be executed.
+ # callback chain, preventing following before and around callbacks from
+ # being called and the event from being triggered.
+ # This should be a lambda to be executed.
# The current object and the return result of the callback will be called
# with the lambda.
#
# define_callbacks :validate, terminator: ->(target, result) { result == false }
#
# In this example, if any before validate callbacks returns +false+,
- # other callbacks are not executed. Defaults to +false+, meaning no value
- # halts the chain.
+ # any successive before and around callback is not executed.
+ # Defaults to +false+, meaning no value halts the chain.
#
# * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after
# callbacks should be terminated by the <tt>:terminator</tt> option. By
diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb
index a7eba91ac5..1a02acd5b1 100644
--- a/activesupport/lib/active_support/rescuable.rb
+++ b/activesupport/lib/active_support/rescuable.rb
@@ -60,7 +60,7 @@ module ActiveSupport
end
klasses.each do |klass|
- key = if klass.is_a?(Class) && klass <= Exception
+ key = if klass.is_a?(Module) && klass.respond_to?(:===)
klass.name
elsif klass.is_a?(String)
klass
@@ -101,7 +101,7 @@ module ActiveSupport
# itself when rescue_from CONSTANT is executed.
klass = self.class.const_get(klass_name) rescue nil
klass ||= klass_name.constantize rescue nil
- exception.is_a?(klass) if klass
+ klass === exception if klass
end
case rescuer
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index a4ba5989b1..98b68455ab 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -16,10 +16,25 @@ module ActiveSupport
Assertion = Minitest::Assertion
class << self
+ # Sets the order in which test cases are run.
+ #
+ # ActiveSupport::TestCase.test_order = :random # => :random
+ #
+ # Valid values are:
+ # * +:random+ (to run tests in random order)
+ # * +:parallel+ (to run tests in parallel)
+ # * +:sorted+ (to run tests alphabetically by method name)
+ # * +:alpha+ (equivalent to +:sorted+)
def test_order=(new_order)
ActiveSupport.test_order = new_order
end
+ # Returns the order in which test cases are run.
+ #
+ # ActiveSupport::TestCase.test_order # => :sorted
+ #
+ # Possible values are +:random+, +:parallel+, +:alpha+, +:sorted+.
+ # Defaults to +:sorted+.
def test_order
test_order = ActiveSupport.test_order
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index fb10b40b84..0c6b4f445b 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -276,6 +276,7 @@ module ActiveSupport
result.in_time_zone(time_zone)
end
end
+ alias_method :since, :+
# Returns a new TimeWithZone object that represents the difference between
# the current object's time and the +other+ time.
@@ -304,16 +305,6 @@ module ActiveSupport
end
end
- def since(other)
- # If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
- # otherwise move forward from #utc, for accuracy when moving across DST boundaries
- if duration_of_variable_length?(other)
- method_missing(:since, other)
- else
- utc.since(other).in_time_zone(time_zone)
- end
- end
-
def ago(other)
since(-other)
end
diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb
index 32c2dfdfc0..d19e5fd6e7 100644
--- a/activesupport/test/callbacks_test.rb
+++ b/activesupport/test/callbacks_test.rb
@@ -49,7 +49,7 @@ module CallbacksTest
def self.before(model)
model.history << [:before_save, :class]
end
-
+
def self.after(model)
model.history << [:after_save, :class]
end
@@ -501,21 +501,20 @@ module CallbacksTest
end
end
- class CallbackTerminator
+ class AbstractCallbackTerminator
include ActiveSupport::Callbacks
- define_callbacks :save, :terminator => ->(_,result) { result == :halt }
-
- set_callback :save, :before, :first
- set_callback :save, :before, :second
- set_callback :save, :around, :around_it
- set_callback :save, :before, :third
- set_callback :save, :after, :first
- set_callback :save, :around, :around_it
- set_callback :save, :after, :second
- set_callback :save, :around, :around_it
- set_callback :save, :after, :third
-
+ def self.set_save_callbacks
+ set_callback :save, :before, :first
+ set_callback :save, :before, :second
+ set_callback :save, :around, :around_it
+ set_callback :save, :before, :third
+ set_callback :save, :after, :first
+ set_callback :save, :around, :around_it
+ set_callback :save, :after, :second
+ set_callback :save, :around, :around_it
+ set_callback :save, :after, :third
+ end
attr_reader :history, :saved, :halted
def initialize
@@ -552,6 +551,17 @@ module CallbacksTest
end
end
+ class CallbackTerminator < AbstractCallbackTerminator
+ define_callbacks :save, terminator: ->(_,result) { result == :halt }
+ set_save_callbacks
+ end
+
+ class CallbackTerminatorSkippingAfterCallbacks < AbstractCallbackTerminator
+ define_callbacks :save, terminator: ->(_,result) { result == :halt },
+ skip_after_callbacks_if_terminated: true
+ set_save_callbacks
+ end
+
class CallbackObject
def before(caller)
caller.record << "before"
@@ -688,7 +698,7 @@ module CallbacksTest
end
class CallbackTerminatorTest < ActiveSupport::TestCase
- def test_termination
+ def test_termination_skips_following_before_and_around_callbacks
terminator = CallbackTerminator.new
terminator.save
assert_equal ["first", "second", "third", "second", "first"], terminator.history
@@ -707,6 +717,14 @@ module CallbacksTest
end
end
+ class CallbackTerminatorSkippingAfterCallbacksTest < ActiveSupport::TestCase
+ def test_termination_skips_after_callbacks
+ terminator = CallbackTerminatorSkippingAfterCallbacks.new
+ terminator.save
+ assert_equal ["first", "second"], terminator.history
+ end
+ end
+
class HyphenatedKeyTest < ActiveSupport::TestCase
def test_save
obj = HyphenatedCallbacks.new
diff --git a/activesupport/test/rescuable_test.rb b/activesupport/test/rescuable_test.rb
index b8af888f7c..bd43ad0797 100644
--- a/activesupport/test/rescuable_test.rb
+++ b/activesupport/test/rescuable_test.rb
@@ -12,6 +12,12 @@ end
class CoolError < StandardError
end
+module WeirdError
+ def self.===(other)
+ Exception === other && other.respond_to?(:weird?)
+ end
+end
+
class Stargate
attr_accessor :result
@@ -29,6 +35,10 @@ class Stargate
@result = e.message
end
+ rescue_from WeirdError do
+ @result = 'weird'
+ end
+
def dispatch(method)
send(method)
rescue Exception => e
@@ -47,6 +57,16 @@ class Stargate
raise MadRonon.new("dex")
end
+ def weird
+ StandardError.new.tap do |exc|
+ def exc.weird?
+ true
+ end
+
+ raise exc
+ end
+ end
+
def sos
@result = 'killed'
end
@@ -91,14 +111,19 @@ class RescuableTest < ActiveSupport::TestCase
assert_equal 'dex', @stargate.result
end
+ def test_rescue_from_error_dispatchers_with_case_operator
+ @stargate.dispatch :weird
+ assert_equal 'weird', @stargate.result
+ end
+
def test_rescues_defined_later_are_added_at_end_of_the_rescue_handlers_array
- expected = ["WraithAttack", "WraithAttack", "NuclearExplosion", "MadRonon"]
+ expected = ["WraithAttack", "WraithAttack", "NuclearExplosion", "MadRonon", "WeirdError"]
result = @stargate.send(:rescue_handlers).collect(&:first)
assert_equal expected, result
end
def test_children_should_inherit_rescue_definitions_from_parents_and_child_rescue_should_be_appended
- expected = ["WraithAttack", "WraithAttack", "NuclearExplosion", "MadRonon", "CoolError"]
+ expected = ["WraithAttack", "WraithAttack", "NuclearExplosion", "MadRonon", "WeirdError", "CoolError"]
result = @cool_stargate.send(:rescue_handlers).collect(&:first)
assert_equal expected, result
end
diff --git a/guides/CHANGELOG.md b/guides/CHANGELOG.md
index afaa404ac1..cd3fa9f65b 100644
--- a/guides/CHANGELOG.md
+++ b/guides/CHANGELOG.md
@@ -1 +1,5 @@
+* New section in Active Record Querying: Understanding The Method Chaining
+
+ *Andrey Nering*
+
Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/guides/CHANGELOG.md) for previous changes.
diff --git a/guides/bug_report_templates/action_controller_master.rb b/guides/bug_report_templates/action_controller_master.rb
index 0e51eaa0db..9be8130884 100644
--- a/guides/bug_report_templates/action_controller_master.rb
+++ b/guides/bug_report_templates/action_controller_master.rb
@@ -3,8 +3,6 @@ unless File.exist?('Gemfile')
source 'https://rubygems.org'
gem 'rails', github: 'rails/rails'
gem 'arel', github: 'rails/arel'
- gem 'rack', github: 'rack/rack'
- gem 'i18n', github: 'svenfuchs/i18n'
GEMFILE
system 'bundle'
diff --git a/guides/bug_report_templates/active_record_master.rb b/guides/bug_report_templates/active_record_master.rb
index e7f5d0d5ff..d95354e12d 100644
--- a/guides/bug_report_templates/active_record_master.rb
+++ b/guides/bug_report_templates/active_record_master.rb
@@ -3,8 +3,6 @@ unless File.exist?('Gemfile')
source 'https://rubygems.org'
gem 'rails', github: 'rails/rails'
gem 'arel', github: 'rails/arel'
- gem 'rack', github: 'rack/rack'
- gem 'i18n', github: 'svenfuchs/i18n'
gem 'sqlite3'
GEMFILE
diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md
index 1e9107a8c6..f5575ac523 100644
--- a/guides/source/4_2_release_notes.md
+++ b/guides/source/4_2_release_notes.md
@@ -509,6 +509,17 @@ Please refer to the [Changelog][action-pack] for detailed changes.
serving assets from your Rails server in production.
([Pull Request](https://github.com/rails/rails/pull/16466))
+* When calling the `process` helpers in an integration test the path needs to have
+ a leading slash. Previously you could omit it but that was a byproduct of the
+ implementation and not an intentional feature, e.g.:
+
+ ```ruby
+ test "list all posts" do
+ get "/posts"
+ assert_response :success
+ end
+ ```
+
Action View
-----------
@@ -544,17 +555,6 @@ Please refer to the [Changelog][action-view] for detailed changes.
* Placeholder I18n follows the same convention as `label` I18n.
([Pull Request](https://github.com/rails/rails/pull/16438))
-* When calling the `process` helpers in an integration test the path needs to have
- a leading slash. Previously you could omit it but that was a byproduct of the
- implementation and not an intentional feature, e.g.:
-
- ```ruby
-   test "list all posts" do
-     get "/posts"
-     assert_response :success 
- end
- ```
-
Action Mailer
-------------
@@ -640,6 +640,10 @@ Please refer to the [Changelog][active-record] for detailed changes.
`Relation` for performing queries and updates is the preferred API.
([Commit](https://github.com/rails/rails/commit/d5902c9e))
+* Deprecated `add_timestamps` and `t.timestamps` without passing the `:null`
+ option. The default of `null: true` will change in Rails 5 to `null: false`.
+ ([Pull Request](https://github.com/rails/rails/pull/16481))
+
* Deprecated `Reflection#source_macro` without replacement as it is no longer
needed in Active Record.
([Pull Request](https://github.com/rails/rails/pull/16373))
@@ -658,6 +662,9 @@ Please refer to the [Changelog][active-record] for detailed changes.
### Notable changes
+* `SchemaDumper` uses `force: :cascade` on `create_table`. This makes it
+ possible to reload a schema when foreign keys are in place.
+
* Added a `:required` option to singular associations, which defines a
presence validation on the association.
([Pull Request](https://github.com/rails/rails/pull/16056))
@@ -824,6 +831,7 @@ Please refer to the [Changelog][active-support] for detailed changes.
`module Foo; extend ActiveSupport::Concern; end` boilerplate.
([Commit](https://github.com/rails/rails/commit/b16c36e688970df2f96f793a759365b248b582ad))
+* New [guide](constant_autoloading_and_reloading.html) about constant autoloading and reloading.
Credits
-------
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index 4e36a62583..57546da389 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -112,8 +112,8 @@ NOTE: The actual URL in this example will be encoded as "/clients?ids%5b%5d=1&id
The value of `params[:ids]` will now be `["1", "2", "3"]`. Note that parameter values are always strings; Rails makes no attempt to guess or cast the type.
-NOTE: Values such as `[]`, `[nil]` or `[nil, nil, ...]` in `params` are replaced
-with `nil` for security reasons by default. See [Security Guide](security.html#unsafe-query-generation)
+NOTE: Values such as `[nil]` or `[nil, nil, ...]` in `params` are replaced
+with `[]` for security reasons by default. See [Security Guide](security.html#unsafe-query-generation)
for more information.
To send a hash you include the key name inside the brackets:
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index e1a465c64f..2e7bb74d0b 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -9,6 +9,7 @@ After reading this guide, you will know:
* How to specify the order, retrieved attributes, grouping, and other properties of the found records.
* How to use eager loading to reduce the number of database queries needed for data retrieval.
* How to use dynamic finders methods.
+* How to use method chaining to use multiple ActiveRecord methods together.
* How to check for the existence of particular records.
* How to perform various calculations on Active Record models.
* How to run EXPLAIN on relations.
@@ -1327,10 +1328,67 @@ You can specify an exclamation point (`!`) on the end of the dynamic finders to
If you want to find both by name and locked, you can chain these finders together by simply typing "`and`" between the fields. For example, `Client.find_by_first_name_and_locked("Ryan", true)`.
+Understanding The Method Chaining
+---------------------------------
+
+The Active Record pattern implements [Method Chaining](http://en.wikipedia.org/wiki/Method_chaining),
+which allow us to use multiple Active Record methods together in a simple and straightforward way.
+
+You can chain a method in a sentence when the previous method called returns `ActiveRecord::Relation`,
+like `all`, `where`, and `joins`. Methods that returns a instance of a single object
+(see [Retrieving a Single Object Section](#retrieving-a-single-object)) have to be be the last
+in the sentence.
+
+There are some examples below. This guide won't cover all the possibilities, just a few as example.
+When a Active Record method is called, the query is not immediately generated and sent to the database,
+this just happen when the data is actually needed. So each example below generate a single query.
+
+### Retrieving filtered data from multiple tables
+
+```ruby
+Person
+ .select('people.id, people.name, comments.text')
+ .joins(:comments)
+ .where('comments.created_at > ?', 1.week.ago)
+```
+
+The result should be something like this:
+
+```sql
+SELECT people.id, people.name, comments.text
+FROM people
+INNER JOIN comments
+ ON comments.person_id = people.id
+WHERE comments.created_at = '2015-01-01'
+```
+
+### Retrieving specific data from multiple tables
+
+```ruby
+Person
+ .select('people.id, people.name, companies.name')
+ .joins(:company)
+ .find_by('people.name' => 'John') # this should be the last
+```
+
+The above should generate:
+
+```sql
+SELECT people.id, people.name, companies.name
+FROM people
+INNER JOIN companies
+ ON companies.person_id = people.id
+WHERE people.name = 'John'
+LIMIT 1
+```
+
+NOTE: Remember that, if `find_by` return more than one registry, it will take just the first
+and ignore the others. Note the `LIMIT 1` statement above.
+
Find or Build a New Object
--------------------------
-NOTE: Some dynamic finders have been deprecated in Rails 4.0 and will be
+NOTE: Some dynamic finders were deprecated in Rails 4.0 and
removed in Rails 4.1. The best practice is to use Active Record scopes
instead. You can find the deprecation gem at
https://github.com/rails/activerecord-deprecated_finders
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index 8764546873..156daf1bac 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -167,9 +167,8 @@ directory. Files in this directory are served by the Sprockets middleware.
Assets can still be placed in the `public` hierarchy. Any assets under `public`
will be served as static files by the application or web server when
-`config.serve_static_assets` is set to true. You should use
-`app/assets` for files that must undergo some pre-processing before they are
-served.
+`config.serve_static_files` is set to true. You should use `app/assets` for
+files that must undergo some pre-processing before they are served.
In production, Rails precompiles these files to `public/assets` by default. The
precompiled copies are then served as static assets by the web server. The files
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index 3a2e522fa7..5c05f0c4b7 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -879,10 +879,12 @@ class Order < ActiveRecord::Base
belongs_to :customer, counter_cache: :count_of_orders
end
class Customer < ActiveRecord::Base
- has_many :orders
+ has_many :orders, counter_cache: :count_of_orders
end
```
+NOTE: You only need to specify the :counter_cache option on the "has_many side" of the association when using a custom name for the counter cache.
+
Counter cache columns are added to the containing model's list of read-only attributes through `attr_readonly`.
##### `:dependent`
@@ -1495,6 +1497,7 @@ The `has_many` association supports these options:
* `:as`
* `:autosave`
* `:class_name`
+* `:counter_cache`
* `:dependent`
* `:foreign_key`
* `:inverse_of`
@@ -1522,6 +1525,9 @@ class Customer < ActiveRecord::Base
end
```
+##### `:counter_cache`
+This option can be used to configure a custom named `:counter_cache`. You only need this option when you customized the name of your `:counter_cache` on the [belongs_to association](#options-for-belongs-to).
+
##### `:dependent`
Controls what happens to the associated objects when their owner is destroyed:
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index e72bc81766..713c91d167 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -24,7 +24,7 @@ There are a few commands that are absolutely critical to your everyday usage of
* `rails dbconsole`
* `rails new app_name`
-All commands can run with ```-h or --help``` to list more information.
+All commands can run with `-h` or `--help` to list more information.
Let's create a simple Rails application to step through each of these commands in context.
@@ -368,7 +368,7 @@ Rake is Ruby Make, a standalone Ruby utility that replaces the Unix utility 'mak
You can get a list of Rake tasks available to you, which will often depend on your current directory, by typing `rake --tasks`. Each task has a description, and should help you find the thing you need.
-To get the full backtrace for running rake task you can pass the option ```--trace``` to command line, for example ```rake db:create --trace```.
+To get the full backtrace for running rake task you can pass the option `--trace` to command line, for example `rake db:create --trace`.
```bash
$ bin/rake --tasks
@@ -384,7 +384,7 @@ rake middleware # Prints out your Rack middleware stack
rake tmp:clear # Clear session, cache, and socket files from tmp/ (narrow w/ tmp:sessions:clear, tmp:cache:clear, tmp:sockets:clear)
rake tmp:create # Creates tmp directories for sessions, cache, sockets, and pids
```
-INFO: You can also use ```rake -T``` to get the list of tasks.
+INFO: You can also use `rake -T` to get the list of tasks.
### `about`
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index 7688962c01..ee46533e16 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -120,7 +120,7 @@ numbers. New applications filter out passwords by adding the following `config.f
* `secrets.secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `secrets.secret_key_base` initialized to a random key present in `config/secrets.yml`.
-* `config.serve_static_assets` configures Rails itself to serve static assets. Defaults to true, but in the production environment is turned off as the server software (e.g. NGINX or Apache) used to run the application should serve static assets instead. Unlike the default setting set this to true when running (absolutely not recommended!) or testing your app in production mode using WEBrick. Otherwise you won't be able use page caching and requests for files that exist regularly under the public directory will anyway hit your Rails app.
+* `config.serve_static_files` configures Rails itself to serve static files. Defaults to true, but in the production environment is turned off as the server software (e.g. NGINX or Apache) used to run the application should serve static assets instead. Unlike the default setting set this to true when running (absolutely not recommended!) or testing your app in production mode using WEBrick. Otherwise you won't be able use page caching and requests for files that exist regularly under the public directory will anyway hit your Rails app.
* `config.session_store` is usually set up in `config/initializers/session_store.rb` and specifies what class to use to store the session. Possible values are `:cookie_store` which is the default, `:mem_cache_store`, and `:disabled`. The last one tells Rails not to deal with sessions. Custom session stores can also be specified:
diff --git a/guides/source/constant_autoloading_and_reloading.md b/guides/source/constant_autoloading_and_reloading.md
index 5cd62c22a8..c54816c4c2 100644
--- a/guides/source/constant_autoloading_and_reloading.md
+++ b/guides/source/constant_autoloading_and_reloading.md
@@ -6,17 +6,10 @@ This guide documents how constant autoloading and reloading works.
After reading this guide, you will know:
* Key aspects of Ruby constants
-
* What is `autoload_paths`
-
* How constant autoloading works
-
* What is `require_dependency`
-
* How constant reloading works
-
-* That autoloading is not based on `Module#autoload`
-
* Solutions to common autoloading gotchas
--------------------------------------------------------------------------------
@@ -27,7 +20,7 @@ Introduction
Ruby on Rails allows applications to be written as if their code was preloaded.
-In a normal Ruby program a class needs to load its dependencies:
+In a normal Ruby program classes need to load their dependencies:
```ruby
require 'application_controller'
@@ -104,12 +97,14 @@ class XML::SAXParser
end
```
-the nesting in (2) is different, `XML` does not belong to it:
+the nesting in (2) is different:
```ruby
[XML::SAXParser]
```
+`XML` does not belong to it.
+
We can see in this example that the name of a class or module that belongs to a
certain nesting does not necessarily correlate with the namespaces at the spot.
@@ -184,6 +179,21 @@ performs a constant assignment equivalent to
Project = Class.new(ActiveRecord::Base)
```
+including setting the name of the class as a side-effect:
+
+```ruby
+Project.name # => "Project"
+```
+
+Constant assignment has a special rule to make that happen: if the object
+being assigned is an anonymous class or module, Ruby sets its name to be the
+one the constant.
+
+INFO. From then on, what happens to the constant and the instance does not
+matter. For example, the constant could be deleted, the class object could be
+assigned to a different constant, be stored in no constant anymore, etc. Once
+the name is set, it doesn't change.
+
Similarly, module creation using the `module` keyword as in
```ruby
@@ -197,16 +207,21 @@ performs a constant assignment equivalent to
Admin = Module.new
```
+including setting the name as a side-effect:
+
+```ruby
+Admin.name # => "Admin"
+```
+
WARNING. The execution context of a block passed to `Class.new` or `Module.new`
is not entirely equivalent to the one of the body of the definitions using the
`class` and `module` keywords. But both idioms result in the same constant
assignment.
Thus, when one informally says "the `String` class", that really means: the
-class object the interpreter creates and stores in a constant called "String" in
-the class object stored in the `Object` constant. `String` is otherwise an
-ordinary Ruby constant and everything related to constants applies to it,
-resolution algorithms, etc.
+class object stored in the constant called "String" in the class object stored
+in the `Object` constant. `String` is otherwise an ordinary Ruby constant and
+everything related to constants applies to it, resolution algorithms, etc.
Likewise, in the controller
@@ -221,17 +236,17 @@ end
`Post` is not syntax for a class. Rather, `Post` is a regular Ruby constant. If
all is good, the constant evaluates to an object that responds to `all`.
-That is why we talk about *constant autoloading*, Rails has the ability to load
-constants on the fly.
+That is why we talk about *constant* autoloading, Rails has the ability to
+load constants on the fly.
### Constants are Stored in Modules
Constants belong to modules in a very literal sense. Classes and modules have
-a constant table, think of it as a hash table.
+a constant table; think of it as a hash table.
-Let's analyze an example to really understand what that means. While in a
-casual setting some abuses of language are customary, the exposition is going
-to be exact here for didactic purposes.
+Let's analyze an example to really understand what that means. While common
+abuses of language like "the `String` class" are convenient, the exposition is
+going to be precise here for didactic purposes.
Let's consider the following module definition:
@@ -259,7 +274,9 @@ Put special attention in the previous paragraphs to the distinction between
class and module objects, constant names, and value objects associated to them
in constant tables.
-### Resolution Algorithm for Relative Constants
+### Resolution Algorithms
+
+#### Resolution Algorithm for Relative Constants
At any given place in the code, let's define *cref* to be the first element of
the nesting if it is not empty, or `Object` otherwise.
@@ -277,9 +294,9 @@ implementation of `const_missing` raises `NameError`, but it can be overridden.
Rails autoloading **does not emulate this algorithm**, but its starting point is
the name of the constant to be autoloaded, and the cref. See more in [Relative
-References](#relative-references).
+References](#autoloading-algorithms-relative-references).
-### Resolution Algorithm for Qualified Constants
+#### Resolution Algorithm for Qualified Constants
Qualified constants look like this:
@@ -288,10 +305,16 @@ Billing::Invoice
```
`Billing::Invoice` is composed of two constants: `Billing` is relative and is
-resolved using the algorithm of the previous section; `Invoice` is qualified by
-`Billing` and we are going to see its resolution next. Let's call *parent* to
-that qualifying class or module object, that is, `Billing` in the example above.
-The algorithm for qualified constants goes like this:
+resolved using the algorithm of the previous section.
+
+INFO. Leading colons would make the first segment absolute rather than
+relative: `::Billing::Invoice`. That would force `Billing` to be looked up
+only as a top-level constant.
+
+`Invoice` on the other hand is qualified by `Billing` and we are going to see
+its resolution next. Let's call *parent* to that qualifying class or module
+object, that is, `Billing` in the example above. The algorithm for qualified
+constants goes like this:
1. The constant is looked up in the parent and its ancestors.
@@ -341,7 +364,7 @@ still be "A::B".
The idea of a parent namespace is at the core of the autoloading algorithms
and helps explain and understand their motivation intuitively, but as you see
that metaphor leaks easily. Given an edge case to reason about, take always into
-account the by "parent namespace" the guide means exactly that specific string
+account that by "parent namespace" the guide means exactly that specific string
derivation.
### Loading Mechanism
@@ -405,11 +428,11 @@ is raised.
We are going to cover how constant autoloading works in more detail later, but
the idea is that when a constant like `Post` is hit and missing, if there's a
-*post.rb* file for example in *app/models* Rails is going to find it, evaluate
+`post.rb` file for example in `app/models` Rails is going to find it, evaluate
it, and have `Post` defined as a side-effect.
Alright, Rails has a collection of directories similar to `$LOAD_PATH` in which
-to lookup that *post.rb*. That collection is called `autoload_paths` and by
+to look up `post.rb`. That collection is called `autoload_paths` and by
default it contains:
* All subdirectories of `app` in the application and engines. For example,
@@ -539,10 +562,10 @@ role.rb
modulus some additional directory lookups we are going to cover soon.
-INFO. 'Constant::Name'.underscore gives the relative path without extension of
+INFO. `'Constant::Name'.underscore` gives the relative path without extension of
the file name where `Constant::Name` is expected to be defined.
-Let's see how does Rails autoload the `Post` constant in the `PostsController`
+Let's see how Rails autoloads the `Post` constant in the `PostsController`
above assuming the application has a `Post` model defined in
`app/models/post.rb`.
@@ -585,8 +608,8 @@ file is loaded. If the file actually defines `Post` all is fine, otherwise
### Qualified References
When a qualified constant is missing Rails does not look for it in the parent
-namespaces. But there's a caveat: Unfortunately, when a constant is missing
-Rails is not able to say if the trigger was a relative or qualified reference.
+namespaces. But there is a caveat: When a constant is missing, Rails is
+unable to tell if the trigger was a relative reference or a qualified one.
For example, consider
@@ -632,24 +655,23 @@ been triggered in the first place. Thus, Rails assumes a qualified reference and
considers the file `admin/user.rb` and directory `admin/user` to be the only
valid options.
-In practice this works quite well as long as the nesting matches all parent
+In practice, this works quite well as long as the nesting matches all parent
namespaces respectively and the constants that make the rule apply are known at
that time.
-But since autoloading happens on demand, if the top-level `User` by chance was
-not yet loaded then Rails has no way to know whether `Admin::User` should load it
-or raise `NameError`.
+However, autoloading happens on demand. If by chance the top-level `User` was
+not yet loaded, then Rails assumes a relative reference by contract.
-These kind of name conflicts are rare in practice, but in case there's one
-`require_dependency` provides a solution by making sure the constant needed to
-trigger the heuristic is defined in the conflicting place.
+Naming conflicts of this kind are rare in practice, but if one occurs,
+`require_dependency` provides a solution by ensuring that the constant needed
+to trigger the heuristic is defined in the conflicting place.
### Automatic Modules
When a module acts as a namespace, Rails does not require the application to
defines a file for it, a directory matching the namespace is enough.
-Suppose an application has a backoffice whose controllers are stored in
+Suppose an application has a back office whose controllers are stored in
`app/controllers/admin`. If the `Admin` module is not yet loaded when
`Admin::UsersController` is hit, Rails needs first to autoload the constant
`Admin`.
@@ -843,13 +865,13 @@ class Admin::UsersController < ApplicationController
end
```
-If Ruby resolves `User` in the former case it checks whether there's a `User`
-constant in the `Admin` module. It does not in the latter case, because `Admin`
-does not belong to the nesting.
+To resolve `User` Ruby checks `Admin` in the former case, but it does not in
+the latter because it does not belong to the nesting. (See [Nesting](#nesting)
+and [Resolution Algorithms](#resolution-algorithms).)
Unfortunately Rails autoloading does not know the nesting in the spot where the
constant was missing and so it is not able to act as Ruby would. In particular,
-if `Admin::User` is autoloadable, it will get autoloaded in either case.
+`Admin::User` will get autoloaded in either case.
Albeit qualified constants with `class` and `module` keywords may technically
work with autoloading in some cases, it is preferable to use relative constants
@@ -867,10 +889,10 @@ end
### Autoloading and STI
-STI (Single Table Inheritance) is a feature of Active Record that easies storing
-records that belong to a hierarchy of classes in one single table. The API of
-such models is aware of the hierarchy and encapsulates some common needs. For
-example, given these classes:
+Single Table Inheritance (STI) is a feature of Active Record that easies
+storing a hierarchy of models in one single table. The API of such models is
+aware of the hierarchy and encapsulates some common needs. For example, given
+these classes:
```ruby
# app/models/polygon.rb
@@ -887,37 +909,35 @@ end
```
`Triangle.create` creates a row that represents a triangle, and
-`Rectangle.create` creates a row that represents a rectangle. If `id` is the ID
-of an existing record, `Polygon.find(id)` returns an object of the correct type.
+`Rectangle.create` creates a row that represents a rectangle. If `id` is the
+ID of an existing record, `Polygon.find(id)` returns an object of the correct
+type.
-Methods that perform operations on collections are also aware of the hierarchy.
-For example, `Polygon.all` returns all the records of the table, because all
+Methods that operate on collections are also aware of the hierarchy. For
+example, `Polygon.all` returns all the records of the table, because all
rectangles and triangles are polygons. Active Record takes care of returning
instances of their corresponding class in the result set.
-When Active Record does this, it autoloads constants as needed. For example, if
-the class of `Polygon.first` is `Rectangle` and it has not yet been loaded,
-Active Record autoloads it and the record is fetched and correctly instantiated,
-transparently.
+Types are autoloaded as needed. For example, if `Polygon.first` is a rectangle
+and `Rectangle` has not yet been loaded, Active Record autoloads it and the
+record is correctly instantiated.
All good, but if instead of performing queries based on the root class we need
-to work on some subclass, then things get interesting.
+to work on some subclass, things get interesting.
While working with `Polygon` you do not need to be aware of all its descendants,
because anything in the table is by definition a polygon, but when working with
subclasses Active Record needs to be able to enumerate the types it is looking
for. Let’s see an example.
-`Rectangle.all` should return all the rectangles in the "polygons" table. In
-particular, no triangle should be fetched. To accomplish this, Active Record
-constraints the query to rows whose type column is “Rectangle”:
+`Rectangle.all` only loads rectangles by adding a type constraint to the query:
```sql
SELECT "polygons".* FROM "polygons"
WHERE "polygons"."type" IN ("Rectangle")
```
-That works, but let’s introduce now a child of `Rectangle`:
+Let’s introduce now a subclass of `Rectangle`:
```ruby
# app/models/square.rb
@@ -925,16 +945,15 @@ class Square < Rectangle
end
```
-`Rectangle.all` should return rectangles **and** squares, the query should
-become
+`Rectangle.all` should now return rectangles **and** squares:
```sql
SELECT "polygons".* FROM "polygons"
WHERE "polygons"."type" IN ("Rectangle", "Square")
```
-But there’s a subtle caveat here: How does Active Record know that the class
-`Square` exists at all?
+But there’s a caveat here: How does Active Record know that the class `Square`
+exists at all?
Even if the file `app/models/square.rb` exists and defines the `Square` class,
if no code yet used that class, `Rectangle.all` issues the query
@@ -944,8 +963,7 @@ SELECT "polygons".* FROM "polygons"
WHERE "polygons"."type" IN ("Rectangle")
```
-That is not a bug in Active Record, as we saw above the query does include all
-*known* descendants of `Rectangle`.
+That is not a bug, the query includes all *known* descendants of `Rectangle`.
A way to ensure this works correctly regardless of the order of execution is to
load the leaves of the tree by hand at the bottom of the file that defines the
@@ -958,15 +976,14 @@ end
require_dependency ‘square’
```
-Only the leaves that are **at least grandchildren** have to be loaded that way.
-Direct subclasses do not need to be preloaded, and if the hierarchy is deeper
-intermediate superclasses will be autoloaded recursively from the bottom because
-their constant will appear in the definitions.
+Only the leaves that are **at least grandchildren** need to be loaded this
+way. Direct subclasses do not need to be preloaded. If the hierarchy is
+deeper, intermediate classes will be autoloaded recursively from the bottom
+because their constant will appear in the class definitions as superclass.
### Autoloading and `require`
-Files defining constants that should be autoloaded should never be loaded with
-`require`:
+Files defining constants to be autoloaded should never be `require`d:
```ruby
require 'user' # DO NOT DO THIS
@@ -976,25 +993,21 @@ class UsersController < ApplicationController
end
```
-If some part of the application autoloads the `User` constant before, then the
-application will interpret `app/models/user.rb` twice in development mode.
+There are two possible gotchas here in development mode:
-As we saw before, in development mode autoloading uses `Kernel#load` by default.
-Since `load` does not store the name of the interpreted file in
-`$LOADED_FEATURES` (`$"`) `require` executes, again, `app/models/user.rb`.
+1. If `User` is autoloaded before reaching the `require`, `app/models/user.rb`
+runs again because `load` does not update `$LOADED_FEATURES`.
-On the other hand, if `app/controllers/users_controllers.rb` happens to be
-evaluated before `User` is autoloaded then dependencies won’t mark `User` as an
-autoloaded constant, and therefore changes to `app/models/user.rb` won’t be
-updated in development mode.
+2. If the `require` runs first Rails does not mark `User` as an autoloaded
+constant and changes to `app/models/user.rb` aren't reloaded.
-Just follow the flow and use constant autoloading always, never mix autoloading
-and `require`. As a last resort, if some file absolutely needs to load a certain
-file by hand use `require_dependency` to play nice with constant autoloading.
-This option is rarely needed in practice, though.
+Just follow the flow and use constant autoloading always, never mix
+autoloading and `require`. As a last resort, if some file absolutely needs to
+load a certain file use `require_dependency` to play nice with constant
+autoloading. This option is rarely needed in practice, though.
Of course, using `require` in autoloaded files to load ordinary 3rd party
-libraries is fine, and Rails is able to distinguish their constants, so they are
+libraries is fine, and Rails is able to distinguish their constants, they are
not marked as autoloaded.
### Autoloading and Initializers
@@ -1002,24 +1015,22 @@ not marked as autoloaded.
Consider this assignment in `config/initializers/set_auth_service.rb`:
```ruby
-AUTH_SERVICE = Rails.env.production? ? RealAuthService : MockedAuthService
+AUTH_SERVICE = if Rails.env.production?
+ RealAuthService
+else
+ MockedAuthService
+end
```
-The purpose of this setup would be that the application code uses always
-`AUTH_SERVICE` and that constant holds the proper class for the runtime
-environment. In development mode `MockedAuthService` gets autoloaded when the
-initializer is run. Let’s suppose we do some requests, change the implementation
-of `MockedAuthService`, and hit the application again. To our surprise the
-changes are not reflected. Why?
+The purpose of this setup would be that the application uses the class that
+corresponds to the environment via `AUTH_SERVICE`. In development mode
+`MockedAuthService` gets autoloaded when the initializer runs. Let’s suppose
+we do some requests, change its implementation, and hit the application again.
+To our surprise the changes are not reflected. Why?
-As we saw earlier, Rails wipes autoloaded constants by removing them from their
-containers using `remove_const`. But the object the constant holds may remain
-stored somewhere else. Constant removal can’t do anything about that.
-
-That is precisely the case in this example. `AUTH_SERVICE` stores the original
-class object which is perfectly functional regardless of the fact that there is
-no longer a constant in `Object` that matches its class name. The class object
-is independent of the constants it may or may not be stored in.
+As [we saw earlier](#constant-reloading), Rails removes autoloaded constants,
+but `AUTH_SERVICE` stores the original class object. Stale, non-reachable
+using the original constant, but perfectly functional.
The following code summarizes the situation:
@@ -1040,10 +1051,10 @@ C # => uninitialized constant C (NameError)
Because of that, it is not a good idea to autoload constants on application
initialization.
-In the case above we could for instance implement a dynamic access point that
-returns something that depends on the environment:
+In the case above we could implement a dynamic access point:
```ruby
+# app/models/auth_service.rb
class AuthService
if Rails.env.production?
def self.instance
@@ -1057,102 +1068,145 @@ class AuthService
end
```
-and have the application use `AuthService.instance` instead of `AUTH_SERVICE`.
-The code in that `AuthService` would be loaded on demand and be
-autoload-friendly.
+and have the application use `AuthService.instance` instead. `AuthService`
+would be loaded on demand and be autoload-friendly.
### `require_dependency` and Initializers
-As we saw before, `require_dependency` loads files in a autoloading-friendly
+As we saw before, `require_dependency` loads files in an autoloading-friendly
way. Normally, though, such a call does not make sense in an initializer.
-`require_dependency` provides a way to ensure a certain constant is defined at
-some point regardless of the execution path, and one could think about doing
-some calls in an initializer to make sure certain constants are loaded upfront,
-for example as an attempt to address the gotcha with STIs.
+One could think about doing some [`require_dependency`](#require-dependency)
+calls in an initializer to make sure certain constants are loaded upfront, for
+example as an attempt to address the [gotcha with STIs](#autoloading-and-sti).
-Problem is, in development mode all autoloaded constants are wiped on a
-subsequent request as soon as there is some relevant change in the file system.
-When that happens the application is in the very same situation the initializer
-wanted to avoid!
+Problem is, in development mode [autoloaded constants are wiped](#constant-reloading)
+if there is any relevant change in the file system. If that happens then
+we are in the very same situation the initializer wanted to avoid!
Calls to `require_dependency` have to be strategically written in autoloaded
spots.
### When Constants aren't Missed
-Let’s imagine that a Rails application has an `Image` model, and a subclass
-`Hotel::Image`:
+#### Relative References
+
+Let's consider a flight simulator. The application has a default flight model
```ruby
-# app/models/image.rb
-class Image
+# app/models/flight_model.rb
+class FlightModel
end
+```
-# app/models/hotel/image.rb
-module Hotel
- class Image < Image
+that can be overridden by each airplane, for instance
+
+```ruby
+# app/models/bell_x1/flight_model.rb
+module BellX1
+ class FlightModel < FlightModel
+ end
+end
+
+# app/models/bell_x1/aircraft.rb
+module BellX1
+ class Aircraft
+ def initialize
+ @flight_model = FlightModel.new
+ end
end
end
```
-No matter which file is interpreted first, `app/models/hotel/image.rb` is
-well-defined.
+The initializer wants to create a `BellX1::FlightModel` and nesting has
+`BellX1`, that looks good. But if the default flight model is loaded and the
+one for the Bell-X1 is not, the interpreter is able to resolve the top-level
+`FlightModel` and autoloading is thus not triggered for `BellX1::FlightModel`.
+
+That code depends on the execution path.
-Now consider a third file with this apparently harmless code:
+These kind of ambiguities can often be resolved using qualified constants:
```ruby
-# app/models/hotel/poster.rb
-module Hotel
- class Poster < Image
+module BellX1
+ class Plane
+ def flight_model
+ @flight_model ||= BellX1::FlightModel.new
+ end
end
end
```
-The intention is to subclass `Hotel::Image`, but which is actually the
-superclass of `Hotel::Poster`? Well, it depends on the order of execution of the
-files:
+Also, `require_dependency` is a solution:
-1. If neither `app/models/image.rb` nor `app/models/hotel/image.rb` have been
-loaded at that point, the superclass is `Hotel::Image` because Rails is told
-`Hotel` is missing a constant called "Image" and loads
-`app/models/hotel/image.rb`. Good.
+```ruby
+require_dependency 'bell_x1/flight_model'
-2. If `app/models/hotel/image.rb` has been loaded at that point, the superclass
-is `Hotel::Image` because Ruby is able to resolve the constant. Good.
+module BellX1
+ class Plane
+ def flight_model
+ @flight_model ||= FlightModel.new
+ end
+ end
+end
+```
-3. Lastly, if only `app/models/image.rb` has been loaded so far, the superclass
-is `Image`. Gotcha!
+#### Qualified References
-The last scenario (3) may be surprising. Why isn't `Hotel::Image` autoloaded?
+Given
-Constant autoloading cannot happen at that point because Ruby is able to
-resolve `Image` as a top-level constant, in consequence autoloading is not
-triggered.
+```ruby
+# app/models/hotel.rb
+class Hotel
+end
-Most of the time, these kind of ambiguities can be resolved using qualified
-constants. In this case we would write
+# app/models/image.rb
+class Image
+end
-```ruby
-module Hotel
- class Poster < Hotel::Image
+# app/models/hotel/image.rb
+class Hotel
+ class Image < Image
end
end
```
-That class definition now is robust. No matter which files have been
-previously loaded, we know for certain that the superclass is unambiguously
-set.
+the expression `Hotel::Image` is ambiguous, depends on the execution path.
+
+As [we saw before](#resolution-algorithm-for-qualified-constants), Ruby looks
+up the constant in `Hotel` and its ancestors. If `app/models/image.rb` has
+been loaded but `app/models/hotel/image.rb` hasn't, Ruby does not find `Image`
+in `Hotel`, but it does in `Object`:
+
+```
+$ bin/rails r 'Image; p Hotel::Image' 2>/dev/null
+Image # NOT Hotel::Image!
+```
+
+The code evaluating `Hotel::Image` needs to make sure
+`app/models/hotel/image.rb` has been loaded, possibly with
+`require_dependency`.
+
+In these cases the interpreter issues a warning though:
+
+```
+warning: toplevel constant Image referenced by Hotel::Image
+```
+
+This surprising constant resolution can be observed with any qualifying class:
-It is interesting to note here that fix works because `Hotel` is a module, and
-`Hotel::Image` won’t look for `Image` in `Object` as it would if `Hotel` was a
-class with `Object` in its ancestors. If `Hotel` was a class we would resort to
-loading `Hotel::Image` with `require_dependency`. Furthermore, with that
-solution the qualified name would no longer be necessary.
+```
+2.1.5 :001 > String::Array
+(irb):1: warning: toplevel constant Array referenced by String::Array
+ => Array
+```
+
+WARNING. To find this gotcha the qualifying namespace has to be a class,
+`Object` is not an ancestor of modules.
### Autoloading within Singleton Classes
-Let’s suppose we have these class definitions:
+Let's suppose we have these class definitions:
```ruby
# app/models/hotel/services.rb
@@ -1171,17 +1225,15 @@ module Hotel
end
```
-1. If `Hotel::Services` is known by the time `Hotel::GeoLocation` is being loaded,
-everything works because `Hotel` belongs to the nesting when the singleton class
-of `Hotel::GeoLocation` is opened, and thus Ruby itself is able to resolve the
-constant.
+If `Hotel::Services` is known by the time `app/models/hotel/geo_location.rb`
+is being loaded, `Services` is resolved by Ruby because `Hotel` belongs to the
+nesting when the singleton class of `Hotel::GeoLocation` is opened.
-2. But if `Hotel::Services` is not known and we rely on autoloading for the
-`Services` constant in `Hotel::GeoLocation`, Rails is not able to find
-`Hotel::Services`. The application raises `NameError`.
+But if `Hotel::Services` is not known, Rails is not able to autoload it, the
+application raises `NameError`.
The reason is that autoloading is triggered for the singleton class, which is
-anonymous, and as we [saw before](#generic-procedure), Rails only checks the
+anonymous, and as [we saw before](#generic-procedure), Rails only checks the
top-level namespace in that edge case.
An easy solution to this caveat is to qualify the constant:
@@ -1228,7 +1280,8 @@ c.user # surprisingly fine, User
c.user # NameError: uninitialized constant C::User
```
-because it detects a parent namespace already has the constant.
+because it detects a parent namespace already has the constant (see [Qualified
+References](#qualified-references).)
As with pure Ruby, within the body of a direct descendant of `BasicObject` use
always absolute constant paths:
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index a5e35f75a0..e68e07a519 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -90,18 +90,18 @@ current version of Ruby installed:
TIP: A number of tools exist to help you quickly install Ruby and Ruby
on Rails on your system. Windows users can use [Rails Installer](http://railsinstaller.org),
while Mac OS X users can use [Tokaido](https://github.com/tokaido/tokaidoapp).
+For more installation methods for most Operating Systems take a look at
+[ruby-lang.org](https://www.ruby-lang.org/en/documentation/installation/).
```bash
$ ruby -v
ruby 2.0.0p353
```
-If you don't have Ruby installed have a look at
-[ruby-lang.org](https://www.ruby-lang.org/en/installation/) for possible ways to
-install Ruby on your platform.
-
-Many popular UNIX-like OSes ship with an acceptable version of SQLite3. Windows
-users and others can find installation instructions at the [SQLite3 website](https://www.sqlite.org).
+Many popular UNIX-like OSes ship with an acceptable version of SQLite3.
+On Windows, if you installed Rails through Rails Installer, you
+already have SQLite installed. Others can find installation instructions
+at the [SQLite3 website](https://www.sqlite.org).
Verify that it is correctly installed and in your PATH:
```bash
@@ -259,9 +259,9 @@ invoke helper
create app/helpers/welcome_helper.rb
invoke assets
invoke coffee
-create app/assets/javascripts/welcome.js.coffee
+create app/assets/javascripts/welcome.coffee
invoke scss
-create app/assets/stylesheets/welcome.css.scss
+create app/assets/stylesheets/welcome.scss
```
Most important of these are of course the controller, located at
@@ -300,8 +300,9 @@ Rails.application.routes.draw do
# ...
```
-This is your application's _routing file_ which holds entries in a special DSL
-(domain-specific language) that tells Rails how to connect incoming requests to
+This is your application's _routing file_ which holds entries in a special
+[DSL (domain-specific language)](http://en.wikipedia.org/wiki/Domain-specific_language)
+that tells Rails how to connect incoming requests to
controllers and actions. This file contains many sample routes on commented
lines, and one of them actually shows you how to connect the root of your site
to a specific controller and action. Find the line beginning with `root` and
@@ -1637,8 +1638,8 @@ This creates five files and one empty directory:
| app/views/comments/ | Views of the controller are stored here |
| test/controllers/comments_controller_test.rb | The test for the controller |
| app/helpers/comments_helper.rb | A view helper file |
-| app/assets/javascripts/comment.js.coffee | CoffeeScript for the controller |
-| app/assets/stylesheets/comment.css.scss | Cascading style sheet for the controller |
+| app/assets/javascripts/comment.coffee | CoffeeScript for the controller |
+| app/assets/stylesheets/comment.scss | Cascading style sheet for the controller |
Like with any blog, our readers will create their comments directly after
reading the article, and once they have added their comment, will be sent back
diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md
index 0dec0e139b..042ebde9bb 100644
--- a/guides/source/rails_on_rack.md
+++ b/guides/source/rails_on_rack.md
@@ -233,7 +233,7 @@ Much of Action Controller's functionality is implemented as Middlewares. The fol
**`ActionDispatch::Static`**
-* Used to serve static assets. Disabled if `config.serve_static_assets` is `false`.
+* Used to serve static files. Disabled if `config.serve_static_files` is `false`.
**`Rack::Lock`**
diff --git a/guides/source/security.md b/guides/source/security.md
index b1c5b22338..b3869b1ba5 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -942,7 +942,7 @@ unless params[:token].nil?
end
```
-When `params[:token]` is one of: `[]`, `[nil]`, `[nil, nil, ...]` or
+When `params[:token]` is one of: `[nil]`, `[nil, nil, ...]` or
`['foo', nil]` it will bypass the test for `nil`, but `IS NULL` or
`IN ('foo', NULL)` where clauses still will be added to the SQL query.
@@ -953,9 +953,9 @@ request:
| JSON | Parameters |
|-----------------------------------|--------------------------|
| `{ "person": null }` | `{ :person => nil }` |
-| `{ "person": [] }` | `{ :person => nil }` |
-| `{ "person": [null] }` | `{ :person => nil }` |
-| `{ "person": [null, null, ...] }` | `{ :person => nil }` |
+| `{ "person": [] }` | `{ :person => [] }` |
+| `{ "person": [null] }` | `{ :person => [] }` |
+| `{ "person": [null, null, ...] }` | `{ :person => [] }` |
| `{ "person": ["foo", null] }` | `{ :person => ["foo"] }` |
It is possible to return to old behaviour and disable `deep_munge` configuring
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 7ef51b6dc0..51c144993c 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -235,8 +235,8 @@ mail = Notifier.notify(user, ...) # Notifier#notify is not yet called at this po
mail = mail.deliver_now # Prints "Called"
```
-This should not result in any noticible differnces for most applications.
-However, if you need some non-mailer methods to be exectuted synchronously, and
+This should not result in any noticeable differences for most applications.
+However, if you need some non-mailer methods to be executed synchronously, and
you were previously relying on the synchronous proxying behavior, you should
define them as class methods on the mailer class directly:
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 268ef2c7aa..fdc741dd08 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -1,5 +1,7 @@
require 'active_support/core_ext/kernel/reporting'
+require 'active_support/core_ext/string/filters'
require 'active_support/file_update_checker'
+require 'active_support/deprecation'
require 'rails/engine/configuration'
require 'rails/source_annotation_extractor'
@@ -11,7 +13,7 @@ module Rails
:eager_load, :exceptions_app, :file_watcher, :filter_parameters,
:force_ssl, :helpers_paths, :logger, :log_formatter, :log_tags,
:railties_order, :relative_url_root, :secret_key_base, :secret_token,
- :serve_static_assets, :ssl_options, :static_cache_control, :session_options,
+ :serve_static_files, :ssl_options, :static_cache_control, :session_options,
:time_zone, :reload_classes_only_on_change,
:beginning_of_week, :filter_redirect, :x
@@ -25,7 +27,7 @@ module Rails
@filter_parameters = []
@filter_redirect = []
@helpers_paths = []
- @serve_static_assets = true
+ @serve_static_files = true
@static_cache_control = nil
@force_ssl = false
@ssl_options = {}
@@ -139,6 +141,25 @@ module Rails
self.generators.colorize_logging = val
end
+ # :nodoc:
+ SERVE_STATIC_ASSETS_DEPRECATION_MESSAGE = <<-MSG.squish
+ The configuration option `config.serve_static_assets` has been renamed
+ to `config.serve_static_files` to clarify its role (it merely enables
+ serving everything in the `public` folder and is unrelated to the asset
+ pipeline). The `serve_static_assets` alias will be removed in Rails 5.0.
+ Please migrate your configuration files accordingly.
+ MSG
+
+ def serve_static_assets
+ ActiveSupport::Deprecation.warn SERVE_STATIC_ASSETS_DEPRECATION_MESSAGE
+ serve_static_files
+ end
+
+ def serve_static_assets=(value)
+ ActiveSupport::Deprecation.warn SERVE_STATIC_ASSETS_DEPRECATION_MESSAGE
+ self.serve_static_files = value
+ end
+
def session_store(*args)
if args.empty?
case @session_store
diff --git a/railties/lib/rails/application/default_middleware_stack.rb b/railties/lib/rails/application/default_middleware_stack.rb
index d1789192ef..02eea82b0c 100644
--- a/railties/lib/rails/application/default_middleware_stack.rb
+++ b/railties/lib/rails/application/default_middleware_stack.rb
@@ -17,7 +17,7 @@ module Rails
middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header
- if config.serve_static_assets
+ if config.serve_static_files
middleware.use ::ActionDispatch::Static, paths["public"].first, config.static_cache_control
end
diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb
index 9014560611..0d8b3de0eb 100644
--- a/railties/lib/rails/commands/dbconsole.rb
+++ b/railties/lib/rails/commands/dbconsole.rb
@@ -178,7 +178,7 @@ module Rails
found = commands.detect do |cmd|
dirs_on_path.detect do |path|
full_path_command = File.join(path, cmd)
- File.executable? full_path_command
+ File.file?(full_path_command) && File.executable?(full_path_command)
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/setup b/railties/lib/rails/generators/rails/app/templates/bin/setup
index 0e22b3fa5c..d5ed4f2e56 100644
--- a/railties/lib/rails/generators/rails/app/templates/bin/setup
+++ b/railties/lib/rails/generators/rails/app/templates/bin/setup
@@ -1,28 +1,30 @@
require 'pathname'
+require 'fileutils'
+include FileUtils
# path to your application root.
APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
-Dir.chdir APP_ROOT do
+chdir APP_ROOT do
# This script is a starting point to setup your application.
- # Add necessary setup steps to this file:
+ # Add necessary setup steps to this file.
- puts "== Installing dependencies =="
- system "gem install bundler --conservative"
- system "bundle check || bundle install"
+ puts '== Installing dependencies =='
+ system 'gem install bundler --conservative'
+ system('bundle check') or system('bundle install')
# puts "\n== Copying sample files =="
- # unless File.exist?("config/database.yml")
- # system "cp config/database.yml.sample config/database.yml"
+ # unless File.exist?('config/database.yml')
+ # cp 'config/database.yml.sample', 'config/database.yml'
# end
puts "\n== Preparing database =="
- system "bin/rake db:setup"
+ system 'ruby bin/rake db:setup'
puts "\n== Removing old logs and tempfiles =="
- system "rm -f log/*"
- system "rm -rf tmp/cache"
+ rm_f Dir.glob('log/*')
+ rm_rf 'tmp/cache'
puts "\n== Restarting application server =="
- system "touch tmp/restart.txt"
+ touch 'tmp/restart.txt'
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index ddc04d446c..677bb3b338 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -20,8 +20,9 @@ Rails.application.configure do
# NGINX, varnish or squid.
# config.action_dispatch.rack_cache = true
- # Disable Rails's static asset server (Apache or NGINX will already do this).
- config.serve_static_assets = false
+ # Disable serving static files from the `/public` folder by default since
+ # Apache or NGINX already handles this.
+ config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present?
<%- unless options.skip_sprockets? -%>
# Compress JavaScripts and CSS.
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
index 03a3568fbe..1c19f08b28 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
@@ -12,8 +12,8 @@ Rails.application.configure do
# preloads Rails for running tests, you may have to set it to true.
config.eager_load = false
- # Configure static asset server for tests with Cache-Control for performance.
- config.serve_static_assets = true
+ # Configure static file server for tests with Cache-Control for performance.
+ config.serve_static_files = true
config.static_cache_control = 'public, max-age=3600'
# Show full error reports and disable caching.
diff --git a/railties/lib/rails/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake
index 4f09ded31d..ba6168e208 100644
--- a/railties/lib/rails/tasks/statistics.rake
+++ b/railties/lib/rails/tasks/statistics.rake
@@ -1,6 +1,6 @@
-# while having global constant is not good,
-# many 3rd party tools depend on it, like rspec-rails, cucumber-rails, etc
-# so if will be removed - deprecation warning is needed
+# While global constants are bad, many 3rd party tools depend on this one (e.g
+# rspec-rails & cucumber-rails). So a deprecation warning is needed if we want
+# to remove it.
STATS_DIRECTORIES = [
%w(Controllers app/controllers),
%w(Helpers app/helpers),
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index 8f091cfdbf..d58a27403e 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -225,7 +225,7 @@ module ApplicationTests
test "assets do not require any assets group gem when manifest file is present" do
app_file "app/assets/javascripts/application.js", "alert();"
- add_to_env_config "production", "config.serve_static_assets = true"
+ add_to_env_config "production", "config.serve_static_files = true"
ENV["RAILS_ENV"] = "production"
precompile!
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 679190dad4..bf6c64b518 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -41,7 +41,7 @@ module ApplicationTests
def setup
build_app
boot_rails
- FileUtils.rm_rf("#{app_path}/config/environments")
+ supress_default_config
end
def teardown
@@ -49,6 +49,15 @@ module ApplicationTests
FileUtils.rm_rf(new_app) if File.directory?(new_app)
end
+ def supress_default_config
+ FileUtils.mv("#{app_path}/config/environments", "#{app_path}/config/__environments__")
+ end
+
+ def restore_default_config
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ FileUtils.mv("#{app_path}/config/__environments__", "#{app_path}/config/environments")
+ end
+
test "Rails.env does not set the RAILS_ENV environment variable which would leak out into rake tasks" do
require "rails"
@@ -280,6 +289,53 @@ module ApplicationTests
assert_equal Pathname.new(app_path).join("somewhere"), Rails.public_path
end
+ test "In production mode, config.serve_static_files is off by default" do
+ restore_default_config
+
+ with_rails_env "production" do
+ require "#{app_path}/config/environment"
+ assert_not app.config.serve_static_files
+ end
+ end
+
+ test "In production mode, config.serve_static_files is enabled when RAILS_SERVE_STATIC_FILES is set" do
+ restore_default_config
+
+ with_rails_env "production" do
+ switch_env "RAILS_SERVE_STATIC_FILES", "1" do
+ require "#{app_path}/config/environment"
+ assert app.config.serve_static_files
+ end
+ end
+ end
+
+ test "In production mode, config.serve_static_files is disabled when RAILS_SERVE_STATIC_FILES is blank" do
+ restore_default_config
+
+ with_rails_env "production" do
+ switch_env "RAILS_SERVE_STATIC_FILES", " " do
+ require "#{app_path}/config/environment"
+ assert_not app.config.serve_static_files
+ end
+ end
+ end
+
+ test "config.serve_static_assets is deprecated" do
+ require "#{app_path}/config/application"
+
+ assert_deprecated(/serve_static_assets/) do
+ app.config.serve_static_assets = false
+ end
+
+ assert_not app.config.serve_static_files
+ assert_deprecated(/serve_static_assets/) { assert_not app.config.serve_static_assets }
+
+ app.config.serve_static_files = true
+
+ assert app.config.serve_static_files
+ assert_deprecated(/serve_static_assets/) { assert app.config.serve_static_assets }
+ end
+
test "Use key_generator when secret_key_base is set" do
make_basic_app do |app|
app.secrets.secret_key_base = 'b3c631c314c0bbca50c1b2843150fe33'
diff --git a/railties/test/application/middleware/sendfile_test.rb b/railties/test/application/middleware/sendfile_test.rb
index eb791f5687..dc96480d6d 100644
--- a/railties/test/application/middleware/sendfile_test.rb
+++ b/railties/test/application/middleware/sendfile_test.rb
@@ -61,7 +61,7 @@ module ApplicationTests
test "files handled by ActionDispatch::Static are handled by Rack::Sendfile" do
make_basic_app do |app|
app.config.action_dispatch.x_sendfile_header = 'X-Sendfile'
- app.config.serve_static_assets = true
+ app.config.serve_static_files = true
app.paths["public"] = File.join(rails_root, "public")
end
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index caef39d16f..c64fe082f3 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -113,8 +113,8 @@ module ApplicationTests
assert !middleware.include?("Rack::Lock")
end
- test "removes static asset server if serve_static_assets is disabled" do
- add_to_config "config.serve_static_assets = false"
+ test "removes static asset server if serve_static_files is disabled" do
+ add_to_config "config.serve_static_files = false"
boot!
assert !middleware.include?("ActionDispatch::Static")
end
diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb
index 0a5873bcbf..c414732f92 100644
--- a/railties/test/application/rake/dbs_test.rb
+++ b/railties/test/application/rake/dbs_test.rb
@@ -156,6 +156,31 @@ module ApplicationTests
end
end
+ test 'db:schema:load and db:structure:load do not purge the existing database' do
+ Dir.chdir(app_path) do
+ `bin/rails runner 'ActiveRecord::Base.connection.create_table(:posts) {|t| t.string :title }'`
+
+ app_file 'db/schema.rb', <<-RUBY
+ ActiveRecord::Schema.define(version: 20140423102712) do
+ create_table(:comments) {}
+ end
+ RUBY
+
+ list_tables = lambda { `bin/rails runner 'p ActiveRecord::Base.connection.tables'`.strip }
+
+ assert_equal '["posts"]', list_tables[]
+ `bin/rake db:schema:load`
+ assert_equal '["posts", "comments", "schema_migrations"]', list_tables[]
+
+ app_file 'db/structure.sql', <<-SQL
+ CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255));
+ SQL
+
+ `bin/rake db:structure:load`
+ assert_equal '["posts", "comments", "schema_migrations", "users"]', list_tables[]
+ end
+ end
+
def db_test_load_structure
Dir.chdir(app_path) do
`rails generate model book title:string;
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index 260ee0eda9..91cdc60bd1 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -1210,7 +1210,7 @@ YAML
test "engine can be properly mounted at root" do
add_to_config("config.action_dispatch.show_exceptions = false")
- add_to_config("config.serve_static_assets = false")
+ add_to_config("config.serve_static_files = false")
@plugin.write "lib/bukkits.rb", <<-RUBY
module Bukkits
diff --git a/tasks/release.rb b/tasks/release.rb
index 729e0761ad..d8c1390eef 100644
--- a/tasks/release.rb
+++ b/tasks/release.rb
@@ -1,4 +1,4 @@
-FRAMEWORKS = %w( activesupport activemodel activerecord actionview actionpack actionmailer railties activejob )
+FRAMEWORKS = %w( activesupport activemodel activerecord actionview actionpack activejob actionmailer railties )
root = File.expand_path('../../', __FILE__)
version = File.read("#{root}/RAILS_VERSION").strip