diff options
117 files changed, 834 insertions, 396 deletions
diff --git a/Gemfile.lock b/Gemfile.lock index 58d2941170..4d58065000 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -137,7 +137,7 @@ PATH activesupport (= 5.0.0.alpha) arel (= 7.0.0.alpha) activesupport (5.0.0.alpha) - concurrent-ruby (~> 1.0.0.pre3, < 2.0.0) + concurrent-ruby (~> 1.0.0.pre5, < 2.0.0) i18n (~> 0.7) json (~> 1.7, >= 1.7.7) method_source @@ -201,7 +201,7 @@ GEM coffee-script-source execjs coffee-script-source (1.9.1.1) - concurrent-ruby (1.0.0.pre4) + concurrent-ruby (1.0.0.pre5) connection_pool (2.2.0) dalli (2.7.4) dante (0.2.0) diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index 85d3629514..285b2cfcb5 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -8,6 +8,12 @@ silence_warnings do Encoding.default_external = "UTF-8" end +module Rails + def self.root + File.expand_path('../', File.dirname(__FILE__)) + end +end + require 'active_support/testing/autorun' require 'active_support/testing/method_call_assertions' require 'action_mailer' @@ -26,12 +32,6 @@ I18n.enforce_available_locales = false FIXTURE_LOAD_PATH = File.expand_path('fixtures', File.dirname(__FILE__)) ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH -module Rails - def self.root - File.expand_path('../', File.dirname(__FILE__)) - end -end - # Skips the current run on Rubinius using Minitest::Assertions#skip def rubinius_skip(message = '') skip message if RUBY_ENGINE == 'rbx' diff --git a/actionmailer/test/delivery_methods_test.rb b/actionmailer/test/delivery_methods_test.rb index d17e774092..2786fe0d07 100644 --- a/actionmailer/test/delivery_methods_test.rb +++ b/actionmailer/test/delivery_methods_test.rb @@ -31,8 +31,8 @@ class DefaultsDeliveryMethodsTest < ActiveSupport::TestCase assert_equal settings, ActionMailer::Base.smtp_settings end - test "default file delivery settings" do - settings = {location: "#{Dir.tmpdir}/mails"} + test "default file delivery settings (with Rails.root)" do + settings = {location: "#{Rails.root}/tmp/mails"} assert_equal settings, ActionMailer::Base.file_settings end diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 4f95a9bab9..40f447ada7 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -17,11 +17,6 @@ *Agis Anastasopoulos* -* Deprecate `config.static_cache_control` in favor of - `config.public_file_server.headers` - - *Yuki Nishijima* - * Add the ability of returning arbitrary headers to ActionDispatch::Static Now ActionDispatch::Static can accept HTTP headers so that developers diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index 4501202b8c..7f349f2741 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -149,7 +149,7 @@ module AbstractController # ==== Parameters # * <tt>action_name</tt> - The name of an action to be tested def available_action?(action_name) - _find_action_name(action_name).present? + _find_action_name(action_name) end # Returns true if the given controller is capable of rendering diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb index 5260dc0336..5c0ada37be 100644 --- a/actionpack/lib/action_controller/metal/exceptions.rb +++ b/actionpack/lib/action_controller/metal/exceptions.rb @@ -3,12 +3,19 @@ module ActionController end class BadRequest < ActionControllerError #:nodoc: - attr_reader :original_exception - def initialize(msg = nil, e = nil) + if e + ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \ + "Exceptions will automatically capture the original exception.", caller) + end + super(msg) - @original_exception = e - set_backtrace e.backtrace if e + set_backtrace $!.backtrace if $! + end + + def original_exception + ActiveSupport::Deprecation.warn("#original_exception is deprecated. Use #cause instead.", caller) + cause end end diff --git a/actionpack/lib/action_controller/metal/rescue.rb b/actionpack/lib/action_controller/metal/rescue.rb index 68cc9a9c9b..81b9a7b9ed 100644 --- a/actionpack/lib/action_controller/metal/rescue.rb +++ b/actionpack/lib/action_controller/metal/rescue.rb @@ -7,10 +7,8 @@ module ActionController #:nodoc: include ActiveSupport::Rescuable def rescue_with_handler(exception) - if (exception.respond_to?(:original_exception) && - (orig_exception = exception.original_exception) && - handler_for_rescue(orig_exception)) - exception = orig_exception + if exception.cause && handler_for_rescue(exception.cause) + exception = exception.cause end super(exception) end diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb index 248ecfd676..c9df787351 100644 --- a/actionpack/lib/action_dispatch/http/parameters.rb +++ b/actionpack/lib/action_dispatch/http/parameters.rb @@ -55,11 +55,11 @@ module ActionDispatch begin strategy.call(raw_post) - rescue => e # JSON or Ruby code block errors + rescue # JSON or Ruby code block errors my_logger = logger || ActiveSupport::Logger.new($stderr) my_logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{raw_post}" - raise ParamsParser::ParseError.new(e.message, e) + raise ParamsParser::ParseError end end diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 35e3ac304f..ea61ad0c02 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -344,7 +344,7 @@ module ActionDispatch set_header k, Request::Utils.normalize_encode_params(rack_query_params) end rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e - raise ActionController::BadRequest.new("Invalid query parameters: #{e.message}", e) + raise ActionController::BadRequest.new("Invalid query parameters: #{e.message}") end alias :query_parameters :GET @@ -360,7 +360,7 @@ module ActionDispatch self.request_parameters = Request::Utils.normalize_encode_params(super || {}) raise rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e - raise ActionController::BadRequest.new("Invalid request parameters: #{e.message}", e) + raise ActionController::BadRequest.new("Invalid request parameters: #{e.message}") end alias :request_parameters :POST diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb index 5fd984cd07..3b61824cc9 100644 --- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb +++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb @@ -37,7 +37,7 @@ module ActionDispatch @backtrace_cleaner = backtrace_cleaner @exception = original_exception(exception) - expand_backtrace if exception.is_a?(SyntaxError) || exception.try(:original_exception).try(:is_a?, SyntaxError) + expand_backtrace if exception.is_a?(SyntaxError) || exception.cause.is_a?(SyntaxError) end def rescue_template @@ -106,17 +106,13 @@ module ActionDispatch end def original_exception(exception) - if registered_original_exception?(exception) - exception.original_exception + if @@rescue_responses.has_key?(exception.cause.class.name) + exception.cause else exception end end - def registered_original_exception?(exception) - exception.respond_to?(:original_exception) && @@rescue_responses.has_key?(exception.original_exception.class.name) - end - def clean_backtrace(*args) if backtrace_cleaner backtrace_cleaner.clean(backtrace, *args) diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb index 18af0a583a..c2a4f46e67 100644 --- a/actionpack/lib/action_dispatch/middleware/params_parser.rb +++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb @@ -10,11 +10,25 @@ module ActionDispatch # Raised when raw data from the request cannot be parsed by the parser # defined for request's content mime type. class ParseError < StandardError - attr_reader :original_exception - def initialize(message, original_exception) - super(message) - @original_exception = original_exception + def initialize(message = nil, original_exception = nil) + if message + ActiveSupport::Deprecation.warn("Passing #message is deprecated and has no effect. " \ + "#{self.class} will automatically capture the message " \ + "of the original exception.", caller) + end + + if original_exception + ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \ + "Exceptions will automatically capture the original exception.", caller) + end + + super($!.message) + end + + def original_exception + ActiveSupport::Deprecation.warn("#original_exception is deprecated. Use #cause instead.", caller) + cause end end diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index 9e50fea3fc..5fb5953811 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -7,14 +7,22 @@ require 'action_dispatch/request/session' module ActionDispatch module Session class SessionRestoreError < StandardError #:nodoc: - attr_reader :original_exception - def initialize(const_error) - @original_exception = const_error + def initialize(const_error = nil) + if const_error + ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \ + "Exceptions will automatically capture the original exception.", caller) + end super("Session contains objects whose class definition isn't available.\n" + "Remember to require the classes for all objects kept in the session.\n" + - "(Original exception: #{const_error.message} [#{const_error.class}])\n") + "(Original exception: #{$!.message} [#{$!.class}])\n") + set_backtrace $!.backtrace + end + + def original_exception + ActiveSupport::Deprecation.warn("#original_exception is deprecated. Use #cause instead.", caller) + cause end end @@ -59,8 +67,8 @@ module ActionDispatch begin # Note that the regexp does not allow $1 to end with a ':' $1.constantize - rescue LoadError, NameError => e - raise ActionDispatch::Session::SessionRestoreError, e, e.backtrace + rescue LoadError, NameError + raise ActionDispatch::Session::SessionRestoreError end retry else diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb index c1e8b6cae3..5060da9369 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb @@ -1,6 +1,6 @@ <header> <h1> - <%= @exception.original_exception.class.to_s %> in + <%= @exception.cause.class.to_s %> in <%= @request.parameters["controller"].camelize if @request.parameters["controller"] %>#<%= @request.parameters["action"] %> </h1> </header> diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb index 77bcd26726..78d52acd96 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb @@ -1,4 +1,4 @@ -<%= @exception.original_exception.class.to_s %> in <%= @request.parameters["controller"].camelize if @request.parameters["controller"] %>#<%= @request.parameters["action"] %> +<%= @exception.cause.class.to_s %> in <%= @request.parameters["controller"].camelize if @request.parameters["controller"] %>#<%= @request.parameters["action"] %> Showing <%= @exception.file_name %> where line #<%= @exception.line_number %> raised: <%= @exception.message %> diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb index 1051c787f3..78ef860548 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb @@ -18,7 +18,7 @@ module ActionDispatch # assert_recognizes({controller: 'items', action: 'create'}, {path: 'items', method: :post}) # # You can also pass in +extras+ with a hash containing URL parameters that would normally be in the query string. This can be used - # to assert that values in the query string string will end up in the params hash correctly. To test query strings you must use the + # to assert that values in the query string will end up in the params hash correctly. To test query strings you must use the # extras argument, appending the query string on the path directly will not work. For example: # # # Asserts that a path of '/items/list/1?view=print' returns the correct options diff --git a/actionpack/test/controller/live_stream_test.rb b/actionpack/test/controller/live_stream_test.rb index 4224ac2a1b..843dafac06 100644 --- a/actionpack/test/controller/live_stream_test.rb +++ b/actionpack/test/controller/live_stream_test.rb @@ -1,5 +1,5 @@ require 'abstract_unit' -require 'concurrent/atomics' +require 'concurrent/atomic/count_down_latch' Thread.abort_on_exception = true module ActionController diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index 7835d2768a..6ae33be3c8 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -170,7 +170,7 @@ class ACLogSubscriberTest < ActionController::TestCase def test_process_action_with_view_runtime get :show wait - assert_match(/Completed 200 OK in [\d]ms/, logs[1]) + assert_match(/Completed 200 OK in \d+ms/, logs[1]) end def test_append_info_to_payload_is_called_even_with_exception diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb index f53f061e10..f42bef883f 100644 --- a/actionpack/test/controller/rescue_test.rb +++ b/actionpack/test/controller/rescue_test.rb @@ -132,11 +132,19 @@ class RescueController < ActionController::Base end def io_error_in_view - raise ActionView::TemplateError.new(nil, IOError.new('this is io error')) + begin + raise IOError.new('this is io error') + rescue + raise ActionView::TemplateError.new(nil) + end end def zero_division_error_in_view - raise ActionView::TemplateError.new(nil, ZeroDivisionError.new('this is zero division error')) + begin + raise ZeroDivisionError.new('this is zero division error') + rescue + raise ActionView::TemplateError.new(nil) + end end protected diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb index 93258fbceb..89c3e75a50 100644 --- a/actionpack/test/dispatch/debug_exceptions_test.rb +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -42,7 +42,11 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest when "/unprocessable_entity" raise ActionController::InvalidAuthenticityToken when "/not_found_original_exception" - raise ActionView::Template::Error.new('template', AbstractController::ActionNotFound.new) + begin + raise AbstractController::ActionNotFound.new + rescue + raise ActionView::Template::Error.new('template') + end when "/missing_template" raise ActionView::MissingTemplate.new(%w(foo), 'foo/index', %w(foo), false, 'mailer') when "/bad_request" @@ -56,12 +60,12 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest when "/syntax_error_into_view" begin eval 'broke_syntax =' - rescue Exception => e + rescue Exception template = ActionView::Template.new(File.read(__FILE__), __FILE__, ActionView::Template::Handlers::Raw.new, {}) - raise ActionView::Template::Error.new(template, e) + raise ActionView::Template::Error.new(template) end when "/framework_raises" method_that_raises diff --git a/actionpack/test/dispatch/live_response_test.rb b/actionpack/test/dispatch/live_response_test.rb index 55becc1c91..160cdc1582 100644 --- a/actionpack/test/dispatch/live_response_test.rb +++ b/actionpack/test/dispatch/live_response_test.rb @@ -1,5 +1,5 @@ require 'abstract_unit' -require 'concurrent/atomics' +require 'concurrent/atomic/count_down_latch' module ActionController module Live diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb index 28ebaed663..a3992ad008 100644 --- a/actionpack/test/dispatch/request/json_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb @@ -76,8 +76,8 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest $stderr = StringIO.new # suppress the log json = "[\"person]\": {\"name\": \"David\"}}" exception = assert_raise(ActionDispatch::ParamsParser::ParseError) { post "/parse", json, {'CONTENT_TYPE' => 'application/json', 'action_dispatch.show_exceptions' => false} } - assert_equal JSON::ParserError, exception.original_exception.class - assert_equal exception.original_exception.message, exception.message + assert_equal JSON::ParserError, exception.cause.class + assert_equal exception.cause.message, exception.message ensure $stderr = STDERR end diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index e9896a71f4..22240699d9 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -1012,8 +1012,8 @@ class RequestParameters < BaseRequestTest request.parameters end - assert e.original_exception - assert_equal e.original_exception.backtrace, e.backtrace + assert_not_nil e.cause + assert_equal e.cause.backtrace, e.backtrace end end diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb index 15dd702161..ffdf775836 100644 --- a/actionpack/test/dispatch/show_exceptions_test.rb +++ b/actionpack/test/dispatch/show_exceptions_test.rb @@ -9,13 +9,21 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest when "/not_found" raise AbstractController::ActionNotFound when "/bad_params" - raise ActionDispatch::ParamsParser::ParseError.new("", StandardError.new) + begin + raise StandardError.new + rescue + raise ActionDispatch::ParamsParser::ParseError + end when "/method_not_allowed" raise ActionController::MethodNotAllowed, 'PUT' when "/unknown_http_method" raise ActionController::UnknownHttpMethod when "/not_found_original_exception" - raise ActionView::Template::Error.new('template', AbstractController::ActionNotFound.new) + begin + raise AbstractController::ActionNotFound.new + rescue + raise ActionView::Template::Error.new('template') + end else raise "puke!" end diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 65314184c8..e5f5961326 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,7 @@ +* Restrict `url_for :back` to valid, non-JavaScript URLs. GH#14444 + + *Damien Burke* + * Allow `date_select` helper selected option to accept hash like the default options. *Lecky Lao* diff --git a/actionview/lib/action_view/dependency_tracker.rb b/actionview/lib/action_view/dependency_tracker.rb index 7716955fd9..5a4c3ea3fe 100644 --- a/actionview/lib/action_view/dependency_tracker.rb +++ b/actionview/lib/action_view/dependency_tracker.rb @@ -1,4 +1,4 @@ -require 'concurrent' +require 'concurrent/map' require 'action_view/path_set' module ActionView diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb index 12e9723a02..6f2f9ca53c 100644 --- a/actionview/lib/action_view/digestor.rb +++ b/actionview/lib/action_view/digestor.rb @@ -1,4 +1,4 @@ -require 'concurrent' +require 'concurrent/map' require 'action_view/dependency_tracker' require 'monitor' diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb index 5684de35e8..baebc34b4b 100644 --- a/actionview/lib/action_view/helpers/url_helper.rb +++ b/actionview/lib/action_view/helpers/url_helper.rb @@ -41,11 +41,21 @@ module ActionView end def _back_url # :nodoc: - referrer = controller.respond_to?(:request) && controller.request.env["HTTP_REFERER"] - referrer || 'javascript:history.back()' + _filtered_referrer || 'javascript:history.back()' end protected :_back_url + def _filtered_referrer # :nodoc: + if controller.respond_to?(:request) + referrer = controller.request.env["HTTP_REFERER"] + if referrer && URI(referrer).scheme != 'javascript' + referrer + end + end + rescue URI::InvalidURIError + end + protected :_filtered_referrer + # Creates an anchor element of the given +name+ using a URL created by the set of +options+. # See the valid options in the documentation for +url_for+. It's also possible to # pass a String instead of an options hash, which generates an anchor element that uses the diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb index ec6edfaaa3..d3935788ef 100644 --- a/actionview/lib/action_view/lookup_context.rb +++ b/actionview/lib/action_view/lookup_context.rb @@ -1,4 +1,4 @@ -require 'concurrent' +require 'concurrent/map' require 'active_support/core_ext/module/remove_method' require 'active_support/core_ext/module/attribute_accessors' require 'action_view/template/resolver' diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb index 39c8658ffe..a78bf0a6e9 100644 --- a/actionview/lib/action_view/renderer/partial_renderer.rb +++ b/actionview/lib/action_view/renderer/partial_renderer.rb @@ -1,5 +1,5 @@ require 'action_view/renderer/partial_renderer/collection_caching' -require 'concurrent' +require 'concurrent/map' module ActionView class PartialIteration diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb index 0ed208f27e..15fc2b71a3 100644 --- a/actionview/lib/action_view/template.rb +++ b/actionview/lib/action_view/template.rb @@ -325,7 +325,7 @@ module ActionView template = refresh(view) template.encode! end - raise Template::Error.new(template, e) + raise Template::Error.new(template) end end diff --git a/actionview/lib/action_view/template/error.rb b/actionview/lib/action_view/template/error.rb index 390bce98a2..b03b197cb5 100644 --- a/actionview/lib/action_view/template/error.rb +++ b/actionview/lib/action_view/template/error.rb @@ -59,13 +59,20 @@ module ActionView class Error < ActionViewError #:nodoc: SOURCE_CODE_RADIUS = 3 - attr_reader :original_exception + def initialize(template, original_exception = nil) + if original_exception + ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \ + "Exceptions will automatically capture the original exception.", caller) + end + + super($!.message) + set_backtrace($!.backtrace) + @template, @sub_templates = template, nil + end - def initialize(template, original_exception) - super(original_exception.message) - @template, @original_exception = template, original_exception - @sub_templates = nil - set_backtrace(original_exception.backtrace) + def original_exception + ActiveSupport::Deprecation.warn("#original_exception is deprecated. Use #cause instead.", caller) + cause end def file_name diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb index 00fc28a522..51bc59edae 100644 --- a/actionview/test/template/render_test.rb +++ b/actionview/test/template/render_test.rb @@ -352,8 +352,8 @@ module RenderTestCases exception = assert_raises ActionView::Template::Error do @controller_view.render("partial_name_local_variable") end - assert_instance_of NameError, exception.original_exception - assert_equal :partial_name_local_variable, exception.original_exception.name + assert_instance_of NameError, exception.cause + assert_equal :partial_name_local_variable, exception.cause.name end # TODO: The reason for this test is unclear, improve documentation @@ -590,14 +590,14 @@ class LazyViewRenderTest < ActiveSupport::TestCase def test_render_utf8_template_with_incompatible_external_encoding with_external_encoding Encoding::SHIFT_JIS do e = assert_raises(ActionView::Template::Error) { @view.render(:file => "test/utf8", :formats => [:html], :layouts => "layouts/yield") } - assert_match 'Your template was not saved as valid Shift_JIS', e.original_exception.message + assert_match 'Your template was not saved as valid Shift_JIS', e.cause.message end end def test_render_utf8_template_with_partial_with_incompatible_encoding with_external_encoding Encoding::SHIFT_JIS do e = assert_raises(ActionView::Template::Error) { @view.render(:file => "test/utf8_magic_with_bare_partial", :formats => [:html], :layouts => "layouts/yield") } - assert_match 'Your template was not saved as valid Shift_JIS', e.original_exception.message + assert_match 'Your template was not saved as valid Shift_JIS', e.cause.message end end diff --git a/actionview/test/template/template_error_test.rb b/actionview/test/template/template_error_test.rb index 3971ec809c..54c1d53b60 100644 --- a/actionview/test/template/template_error_test.rb +++ b/actionview/test/template/template_error_test.rb @@ -2,19 +2,34 @@ require "abstract_unit" class TemplateErrorTest < ActiveSupport::TestCase def test_provides_original_message - error = ActionView::Template::Error.new("test", Exception.new("original")) + error = begin + raise Exception.new("original") + rescue Exception + raise ActionView::Template::Error.new("test") rescue $! + end + assert_equal "original", error.message end def test_provides_original_backtrace - original_exception = Exception.new - original_exception.set_backtrace(%W[ foo bar baz ]) - error = ActionView::Template::Error.new("test", original_exception) + error = begin + original_exception = Exception.new + original_exception.set_backtrace(%W[ foo bar baz ]) + raise original_exception + rescue Exception + raise ActionView::Template::Error.new("test") rescue $! + end + assert_equal %W[ foo bar baz ], error.backtrace end def test_provides_useful_inspect - error = ActionView::Template::Error.new("test", Exception.new("original")) + error = begin + raise Exception.new("original") + rescue Exception + raise ActionView::Template::Error.new("test") rescue $! + end + assert_equal "#<ActionView::Template::Error: original>", error.inspect end end diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb index 50b7865f88..48d0a9a47a 100644 --- a/actionview/test/template/url_helper_test.rb +++ b/actionview/test/template/url_helper_test.rb @@ -50,6 +50,23 @@ class UrlHelperTest < ActiveSupport::TestCase assert_equal 'javascript:history.back()', url_for(:back) end + def test_url_for_with_back_and_no_controller + @controller = nil + assert_equal 'javascript:history.back()', url_for(:back) + end + + def test_url_for_with_back_and_javascript_referer + referer = 'javascript:alert(document.cookie)' + @controller = Struct.new(:request).new(Struct.new(:env).new("HTTP_REFERER" => referer)) + assert_equal 'javascript:history.back()', url_for(:back) + end + + def test_url_for_with_invalid_referer + referer = 'THIS IS NOT A URL' + @controller = Struct.new(:request).new(Struct.new(:env).new("HTTP_REFERER" => referer)) + assert_equal 'javascript:history.back()', url_for(:back) + end + def test_button_to_with_straight_url assert_dom_equal %{<form method="post" action="http://www.example.com" class="button_to"><input type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com") end @@ -523,6 +540,20 @@ class UrlHelperTest < ActiveSupport::TestCase mail_to('feedback@example.com', '<img src="/feedback.png" />'.html_safe) end + def test_mail_to_with_html_safe_string + assert_dom_equal( + %{<a href="mailto:david@loudthinking.com">david@loudthinking.com</a>}, + mail_to("david@loudthinking.com".html_safe) + ) + end + + def test_mail_to_with_nil + assert_dom_equal( + %{<a href="mailto:"></a>}, + mail_to(nil) + ) + end + def test_mail_to_returns_html_safe_string assert mail_to("david@loudthinking.com").html_safe? end diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb index 8e462bfe5d..e56bc79328 100644 --- a/activejob/lib/active_job/arguments.rb +++ b/activejob/lib/active_job/arguments.rb @@ -3,16 +3,23 @@ require 'active_support/core_ext/hash' module ActiveJob # Raised when an exception is raised during job arguments deserialization. # - # Wraps the original exception raised as +original_exception+. + # Wraps the original exception raised as +cause+. class DeserializationError < StandardError + def initialize(e = nil) #:nodoc: + if e + ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \ + "Exceptions will automatically capture the original exception.", caller) + end + + super("Error while trying to deserialize arguments: #{$!.message}") + set_backtrace $!.backtrace + end + # The original exception that was raised during deserialization of job # arguments. - attr_reader :original_exception - - def initialize(e) #:nodoc: - super("Error while trying to deserialize arguments: #{e.message}") - @original_exception = e - set_backtrace e.backtrace + def original_exception + ActiveSupport::Deprecation.warn("#original_exception is deprecated. Use #cause instead.", caller) + cause end end @@ -41,8 +48,8 @@ module ActiveJob # All other types are deserialized using GlobalID. def deserialize(arguments) arguments.map { |argument| deserialize_argument(argument) } - rescue => e - raise DeserializationError.new(e) + rescue + raise DeserializationError end private diff --git a/activejob/lib/active_job/async_job.rb b/activejob/lib/active_job/async_job.rb index 6c1c070994..ed7a6e8d9b 100644 --- a/activejob/lib/active_job/async_job.rb +++ b/activejob/lib/active_job/async_job.rb @@ -1,4 +1,7 @@ -require 'concurrent' +require 'concurrent/map' +require 'concurrent/scheduled_task' +require 'concurrent/executor/thread_pool_executor' +require 'concurrent/utility/processor_counter' module ActiveJob # == Active Job Async Job diff --git a/activejob/test/adapters/async.rb b/activejob/test/adapters/async.rb index df58027599..5fcfb89566 100644 --- a/activejob/test/adapters/async.rb +++ b/activejob/test/adapters/async.rb @@ -1,4 +1,3 @@ -require 'concurrent' require 'active_job/async_job' ActiveJob::Base.queue_adapter = :async diff --git a/activejob/test/jobs/rescue_job.rb b/activejob/test/jobs/rescue_job.rb index f1b9c9349e..4f6376c850 100644 --- a/activejob/test/jobs/rescue_job.rb +++ b/activejob/test/jobs/rescue_job.rb @@ -11,7 +11,7 @@ class RescueJob < ActiveJob::Base rescue_from(ActiveJob::DeserializationError) do |e| JobBuffer.add('rescued from DeserializationError') - JobBuffer.add("DeserializationError original exception was #{e.original_exception.class.name}") + JobBuffer.add("DeserializationError original exception was #{e.cause.class.name}") end def perform(person = "david") diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index 1963a3fc4e..cc6285f932 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -1,4 +1,4 @@ -require 'concurrent' +require 'concurrent/map' require 'mutex_m' module ActiveModel diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 90cf71521a..c0e3840b40 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,75 @@ +* Change connection management middleware to return a new response with + a body proxy, rather than mutating the original. + + *Kevin Buchanan* + +* Make `db:migrate:status` to render `1_some.rb` format migrate files. + + These files are in `db/migrate`: + + * 1_valid_people_have_last_names.rb + * 20150819202140_irreversible_migration.rb + * 20150823202140_add_admin_flag_to_users.rb + * 20150823202141_migration_tests.rb + * 2_we_need_reminders.rb + * 3_innocent_jointable.rb + + Before: + + $ bundle exec rake db:migrate:status + ... + + Status Migration ID Migration Name + -------------------------------------------------- + up 001 ********** NO FILE ********** + up 002 ********** NO FILE ********** + up 003 ********** NO FILE ********** + up 20150819202140 Irreversible migration + up 20150823202140 Add admin flag to users + up 20150823202141 Migration tests + + After: + + $ bundle exec rake db:migrate:status + ... + + Status Migration ID Migration Name + -------------------------------------------------- + up 001 Valid people have last names + up 002 We need reminders + up 003 Innocent jointable + up 20150819202140 Irreversible migration + up 20150823202140 Add admin flag to users + up 20150823202141 Migration tests + + *Yuichiro Kaneko* + +* Define `ActiveRecord::Sanitization.sanitize_sql_for_order` and use it inside + `preprocess_order_args`. + + *Yuichiro Kaneko* + +* Allow bigint with default nil for avoiding auto increment primary key. + + *Ryuta Kamizono* + +* Remove `DEFAULT_CHARSET` and `DEFAULT_COLLATION` in `MySQLDatabaseTasks`. + + We should omit the collation entirely rather than providing a default. + Then the choice is the responsibility of the server and MySQL distribution. + + *Ryuta Kamizono* + +* Alias `ActiveRecord::Relation#left_joins` to + `ActiveRecord::Relation#left_outer_joins`. + + *Takashi Kokubun* + +* Use advisory locking to raise a `ConcurrentMigrationError` instead of + attempting to migrate when another migration is currently running. + + *Sam Davies* + * Added `ActiveRecord::Relation#left_outer_joins`. Example: @@ -13,7 +85,7 @@ *Aaron Suggs* * Avoid disabling errors on the PostgreSQL connection when enabling the - standard_conforming_strings setting. Errors were previously disabled because + `standard_conforming_strings` setting. Errors were previously disabled because the setting wasn't writable in Postgres 8.1 and didn't exist in earlier versions. Now Rails only supports Postgres 8.2+ we're fine to assume the setting exists. Disabling errors caused problems when using a connection @@ -25,7 +97,7 @@ *Harry Marr* -* Set `scope.reordering_value` to `true` if :reordering values are specified. +* Set `scope.reordering_value` to `true` if `:reordering`-values are specified. Fixes #21886. @@ -84,13 +156,13 @@ validates_numericality_of :pitch end - - Old style - - `guitar.errors["tuning_pegs.pitch"] = ["is not a number"]` + # Old style + guitar.errors["tuning_pegs.pitch"] = ["is not a number"] - - New style (if defined globally, or set in has_many_relationship) - - `guitar.errors["tuning_pegs[1].pitch"] = ["is not a number"]` + # New style (if defined globally, or set in has_many_relationship) + guitar.errors["tuning_pegs[1].pitch"] = ["is not a number"] - *Michael Probber and Terence Sun* + *Michael Probber*, *Terence Sun* * Exit with non-zero status for failed database rake tasks. @@ -103,21 +175,23 @@ *Rafael Sales* -* Add ability to default to `uuid` as primary key when generating database migrations +* Add ability to default to `uuid` as primary key when generating database migrations. + + Example: - config.generators do |g| - g.orm :active_record, primary_key_type: :uuid - end + config.generators do |g| + g.orm :active_record, primary_key_type: :uuid + end *Jon McCartie* -* Don't cache arguments in #find_by if they are an ActiveRecord::Relation +* Don't cache arguments in `#find_by` if they are an `ActiveRecord::Relation`. Fixes #20817 *Hiroaki Izu* -* Qualify column name inserted by `group` in calculation +* Qualify column name inserted by `group` in calculation. Giving `group` an unqualified column name now works, even if the relation has `JOIN` with another table which also has a column of the name. @@ -142,7 +216,7 @@ *Jake Worth* * Add an immutable string type to help reduce memory usage for apps which do - not need mutation detection on Strings. + not need mutation detection on strings. *Sean Griffin* @@ -160,7 +234,7 @@ *Yves Senn* -* No longer pass depreacted option `-i` to `pg_dump`. +* No longer pass deprecated option `-i` to `pg_dump`. *Paul Sadauskas* @@ -175,7 +249,7 @@ *Matthew Draper*, *Jean Boussier* -* Remove unused `pk_and_sequence_for` in AbstractMysqlAdapter. +* Remove unused `pk_and_sequence_for` in `AbstractMysqlAdapter`. *Ryuta Kamizono* @@ -201,7 +275,7 @@ *Jimmy Bourassa* -* Fixed taking precision into count when assigning a value to timestamp attribute +* Fixed taking precision into count when assigning a value to timestamp attribute. Timestamp column can have less precision than ruby timestamp In result in how big a fraction of a second can be stored in the @@ -231,7 +305,7 @@ *Yves Senn*, *Matthew Draper* * Add `ActiveRecord::Base.ignored_columns` to make some columns - invisible from ActiveRecord. + invisible from Active Record. *Jean Boussier* diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index cbdd4950a6..4ae585d3f5 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -1,7 +1,7 @@ require 'active_support/core_ext/enumerable' require 'active_support/core_ext/string/filters' require 'mutex_m' -require 'concurrent' +require 'concurrent/map' module ActiveRecord # = Active Record Attribute Methods diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 0d850c7625..486b7b6d25 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -1,5 +1,5 @@ require 'thread' -require 'concurrent' +require 'concurrent/map' require 'monitor' module ActiveRecord @@ -960,12 +960,11 @@ module ActiveRecord def call(env) testing = env['rack.test'] - response = @app.call(env) - response[2] = ::Rack::BodyProxy.new(response[2]) do + status, headers, body = @app.call(env) + proxy = ::Rack::BodyProxy.new(body) do ActiveRecord::Base.clear_active_connections! unless testing end - - response + [status, headers, proxy] rescue Exception ActiveRecord::Base.clear_active_connections! unless testing raise diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index e2ef56798b..abf0124562 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -202,13 +202,9 @@ module ActiveRecord # end # end # - # The table definitions - # The Columns are stored as a ColumnDefinition in the #columns attribute. class TableDefinition include ColumnMethods - # An array of ColumnDefinition objects, representing the column changes - # that have been defined. attr_accessor :indexes attr_reader :name, :temporary, :options, :as, :foreign_keys, :native diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index f5b2e9fa9d..55910865e5 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -538,7 +538,7 @@ module ActiveRecord def translate_exception(exception, message) # override in derived class - ActiveRecord::StatementInvalid.new(message, exception) + ActiveRecord::StatementInvalid.new(message) end def without_prepared_statement?(binds) 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 b775c18c1b..3e3bbc267b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -867,9 +867,9 @@ module ActiveRecord def translate_exception(exception, message) case error_number(exception) when 1062 - RecordNotUnique.new(message, exception) + RecordNotUnique.new(message) when 1452 - InvalidForeignKey.new(message, exception) + InvalidForeignKey.new(message) else super end diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb index 29e8c73d46..ca7dfda80d 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb @@ -3,7 +3,7 @@ module ActiveRecord module MySQL module ColumnMethods def primary_key(name, type = :primary_key, **options) - options[:auto_increment] = true if type == :bigint + options[:auto_increment] = true if type == :bigint && !options.key?(:default) super end diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb index 3c48d0554e..9dee3172f4 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb @@ -4,8 +4,11 @@ module ActiveRecord module ColumnDumper def column_spec_for_primary_key(column) spec = {} - if column.auto_increment? - spec[:id] = ':bigint' if column.bigint? + if column.bigint? + spec[:id] = ':bigint' + spec[:default] = schema_default(column) || 'nil' unless column.auto_increment? + spec[:unsigned] = 'true' if column.unsigned? + elsif column.auto_increment? spec[:unsigned] = 'true' if column.unsigned? return if spec.empty? else diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index 42c4a14f00..773ecbe126 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -20,7 +20,7 @@ module ActiveRecord ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config) rescue Mysql2::Error => error if error.message.include?("Unknown database") - raise ActiveRecord::NoDatabaseError.new(error.message, error) + raise ActiveRecord::NoDatabaseError else raise end diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index fddb318553..89d18ee14e 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -38,7 +38,7 @@ module ActiveRecord ConnectionAdapters::MysqlAdapter.new(mysql, logger, options, config) rescue Mysql::Error => error if error.message.include?("Unknown database") - raise ActiveRecord::NoDatabaseError.new(error.message, error) + raise ActiveRecord::NoDatabaseError else raise end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 6200cc8d29..ed6ab8235f 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -402,9 +402,9 @@ module ActiveRecord case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE) when UNIQUE_VIOLATION - RecordNotUnique.new(message, exception) + RecordNotUnique.new(message) when FOREIGN_KEY_VIOLATION - InvalidForeignKey.new(message, exception) + InvalidForeignKey.new(message) else super end @@ -589,7 +589,7 @@ module ActiveRecord @connection.exec_prepared(stmt_key, type_casted_binds) end rescue ActiveRecord::StatementInvalid => e - pgerror = e.original_exception + pgerror = e.cause # Get the PG code for the failure. Annoyingly, the code for # prepared statements whose return value may have changed is @@ -645,7 +645,7 @@ module ActiveRecord configure_connection rescue ::PG::Error => error if error.message.include?("does not exist") - raise ActiveRecord::NoDatabaseError.new(error.message, error) + raise ActiveRecord::NoDatabaseError else raise end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 9028c1fcb9..32fe275bfb 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -33,7 +33,7 @@ module ActiveRecord ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config) rescue Errno::ENOENT => error if error.message.include?("No such file or directory") - raise ActiveRecord::NoDatabaseError.new(error.message, error) + raise ActiveRecord::NoDatabaseError else raise end @@ -559,7 +559,7 @@ module ActiveRecord # Older versions of SQLite return: # column *column_name* is not unique when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/ - RecordNotUnique.new(message, exception) + RecordNotUnique.new(message) else super end diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 142b6e8599..1250f8a3c3 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -193,8 +193,8 @@ module ActiveRecord } begin statement.execute(hash.values, self, connection).first - rescue TypeError => e - raise ActiveRecord::StatementInvalid.new(e.message, e) + rescue TypeError + raise ActiveRecord::StatementInvalid rescue RangeError nil end diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb index 533c86a6a9..1cd2c2ef8c 100644 --- a/activerecord/lib/active_record/errors.rb +++ b/activerecord/lib/active_record/errors.rb @@ -94,13 +94,21 @@ module ActiveRecord # Superclass for all database execution errors. # - # Wraps the underlying database error as +original_exception+. + # Wraps the underlying database error as +cause+. class StatementInvalid < ActiveRecordError - attr_reader :original_exception def initialize(message = nil, original_exception = nil) - @original_exception = original_exception - super(message) + if original_exception + ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \ + "Exceptions will automatically capture the original exception.", caller) + end + + super(message || $!.try(:message)) + end + + def original_exception + ActiveSupport::Deprecation.warn("#original_exception is deprecated. Use #cause instead.", caller) + cause end end diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 63ec8f6745..1e870d4c44 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -477,6 +477,7 @@ module ActiveRecord class Migration autoload :CommandRecorder, 'active_record/migration/command_recorder' + MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ # :nodoc: # This class is used to verify that all migrations have been run before # loading a web page if config.active_record.migration_error is set to :page_load @@ -995,14 +996,21 @@ module ActiveRecord Array(@migrations_paths) end + def match_to_migration_filename?(filename) # :nodoc: + File.basename(filename) =~ Migration::MigrationFilenameRegexp + end + + def parse_migration_filename(filename) # :nodoc: + File.basename(filename).scan(Migration::MigrationFilenameRegexp).first + end + def migrations(paths) paths = Array(paths) files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }] migrations = files.map do |file| - version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first - + version, name, scope = parse_migration_filename(file) raise IllegalMigrationNameError.new(file) unless version version = version.to_i name = name.camelize diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index b6f3695856..5b1ea16f0b 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -100,12 +100,14 @@ db_namespace = namespace :db do file_list = ActiveRecord::Tasks::DatabaseTasks.migrations_paths.flat_map do |path| - # match "20091231235959_some_name.rb" and "001_some_name.rb" pattern - Dir.foreach(path).grep(/^(\d{3,})_(.+)\.rb$/) do - version = ActiveRecord::SchemaMigration.normalize_migration_number($1) + Dir.foreach(path).map do |file| + next unless ActiveRecord::Migrator.match_to_migration_filename?(file) + + version, name, scope = ActiveRecord::Migrator.parse_migration_filename(file) + version = ActiveRecord::SchemaMigration.normalize_migration_number(version) status = db_list.delete(version) ? 'up' : 'down' - [status, version, $2.humanize] - end + [status, version, (name + scope).humanize] + end.compact end db_list.map! do |version| diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 800de78fe3..2dc52982c9 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1086,11 +1086,7 @@ module ActiveRecord def preprocess_order_args(order_args) order_args.map! do |arg| - if arg.is_a?(Array) && arg.first.to_s.include?('?') - klass.send(:sanitize_sql, arg) - else - arg - end + klass.send(:sanitize_sql_for_order, arg) end order_args.flatten! validate_order_args(order_args) diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index 1cf4b09bf3..0c15f45db9 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -53,6 +53,22 @@ module ActiveRecord end end + # Accepts an array, or string of SQL conditions and sanitizes + # them into a valid SQL fragment for a ORDER clause. + # + # sanitize_sql_for_order(["field(id, ?)", [1,3,2]]) + # # => "field(id, 1,3,2)" + # + # sanitize_sql_for_order("id ASC") + # # => "id ASC" + def sanitize_sql_for_order(condition) + if condition.is_a?(Array) && condition.first.to_s.include?('?') + sanitize_sql_array(condition) + else + condition + end + end + # Accepts a hash of SQL conditions and replaces those attributes # that correspond to a {#composed_of}[rdoc-ref:Aggregations::ClassMethods#composed_of] # relationship with their expanded aggregate attribute values. diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb index 8929aa85c8..100df9c6c6 100644 --- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb @@ -1,8 +1,6 @@ module ActiveRecord module Tasks # :nodoc: class MySQLDatabaseTasks # :nodoc: - DEFAULT_CHARSET = ENV['CHARSET'] || 'utf8' - DEFAULT_COLLATION = ENV['COLLATION'] || 'utf8_unicode_ci' ACCESS_DENIED_ERROR = 1045 delegate :connection, :establish_connection, to: ActiveRecord::Base @@ -87,12 +85,6 @@ module ActiveRecord Hash.new.tap do |options| options[:charset] = configuration['encoding'] if configuration.include? 'encoding' options[:collation] = configuration['collation'] if configuration.include? 'collation' - - # Set default charset only when collation isn't set. - options[:charset] ||= DEFAULT_CHARSET unless options[:collation] - - # Set default collation only when charset is also default. - options[:collation] ||= DEFAULT_COLLATION if options[:charset] == DEFAULT_CHARSET end end diff --git a/activerecord/lib/active_record/type/type_map.rb b/activerecord/lib/active_record/type/type_map.rb index 81d7ed39bb..850a7a4e09 100644 --- a/activerecord/lib/active_record/type/type_map.rb +++ b/activerecord/lib/active_record/type/type_map.rb @@ -1,4 +1,4 @@ -require 'concurrent' +require 'concurrent/map' module ActiveRecord module Type diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index 62579a4a7a..77c47f12d5 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -151,14 +151,16 @@ module ActiveRecord def test_uniqueness_violations_are_translated_to_specific_exception @connection.execute "INSERT INTO subscribers(nick) VALUES('me')" - assert_raises(ActiveRecord::RecordNotUnique) do + error = assert_raises(ActiveRecord::RecordNotUnique) do @connection.execute "INSERT INTO subscribers(nick) VALUES('me')" end + + assert_not_nil error.cause end unless current_adapter?(:SQLite3Adapter) def test_foreign_key_violations_are_translated_to_specific_exception - assert_raises(ActiveRecord::InvalidForeignKey) do + error = assert_raises(ActiveRecord::InvalidForeignKey) do # Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method if @connection.prefetch_primary_key? id_value = @connection.next_sequence_value(@connection.default_sequence_name("fk_test_has_fk", "id")) @@ -167,6 +169,8 @@ module ActiveRecord @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (0)" end end + + assert_not_nil error.cause end def test_foreign_key_violations_are_translated_to_specific_exception_with_validate_false @@ -174,11 +178,13 @@ module ActiveRecord self.table_name = 'fk_test_has_fk' end - assert_raises(ActiveRecord::InvalidForeignKey) do + error = assert_raises(ActiveRecord::InvalidForeignKey) do has_fk = klass_has_fk.new has_fk.fk_id = 1231231231 has_fk.save(validate: false) end + + assert_not_nil error.cause end end @@ -231,11 +237,13 @@ module ActiveRecord unless current_adapter?(:PostgreSQLAdapter) def test_log_invalid_encoding - assert_raise ActiveRecord::StatementInvalid do + error = assert_raise ActiveRecord::StatementInvalid do @connection.send :log, "SELECT 'ы' FROM DUAL" do raise 'ы'.force_encoding(Encoding::ASCII_8BIT) end end + + assert_not_nil error.cause end end end diff --git a/activerecord/test/cases/adapters/mysql/schema_test.rb b/activerecord/test/cases/adapters/mysql/schema_test.rb index a0f3c31e78..04bcf0d839 100644 --- a/activerecord/test/cases/adapters/mysql/schema_test.rb +++ b/activerecord/test/cases/adapters/mysql/schema_test.rb @@ -18,35 +18,26 @@ module ActiveRecord self.table_name = "#{db}.#{table}" def self.name; 'Post'; end end - - @connection.create_table "mysql_doubles" - end - - teardown do - @connection.drop_table "mysql_doubles", if_exists: true - end - - class MysqlDouble < ActiveRecord::Base - self.table_name = "mysql_doubles" end def test_float_limits - @connection.add_column :mysql_doubles, :float_no_limit, :float - @connection.add_column :mysql_doubles, :float_short, :float, limit: 5 - @connection.add_column :mysql_doubles, :float_long, :float, limit: 53 - - @connection.add_column :mysql_doubles, :float_23, :float, limit: 23 - @connection.add_column :mysql_doubles, :float_24, :float, limit: 24 - @connection.add_column :mysql_doubles, :float_25, :float, limit: 25 - MysqlDouble.reset_column_information + @connection.create_table :mysql_doubles do |t| + t.float :float_no_limit + t.float :float_short, limit: 5 + t.float :float_long, limit: 53 + + t.float :float_23, limit: 23 + t.float :float_24, limit: 24 + t.float :float_25, limit: 25 + end - column_no_limit = MysqlDouble.columns.find { |c| c.name == 'float_no_limit' } - column_short = MysqlDouble.columns.find { |c| c.name == 'float_short' } - column_long = MysqlDouble.columns.find { |c| c.name == 'float_long' } + column_no_limit = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_no_limit' } + column_short = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_short' } + column_long = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_long' } - column_23 = MysqlDouble.columns.find { |c| c.name == 'float_23' } - column_24 = MysqlDouble.columns.find { |c| c.name == 'float_24' } - column_25 = MysqlDouble.columns.find { |c| c.name == 'float_25' } + column_23 = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_23' } + column_24 = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_24' } + column_25 = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_25' } # Mysql floats are precision 0..24, Mysql doubles are precision 25..53 assert_equal 24, column_no_limit.limit @@ -56,6 +47,8 @@ module ActiveRecord assert_equal 24, column_23.limit assert_equal 24, column_24.limit assert_equal 53, column_25.limit + ensure + @connection.drop_table "mysql_doubles", if_exists: true end def test_schema diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb index 1ebdca661c..0d9f07917c 100644 --- a/activerecord/test/cases/adapters/mysql2/schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb @@ -20,6 +20,37 @@ module ActiveRecord end end + def test_float_limits + @connection.create_table :mysql_doubles do |t| + t.float :float_no_limit + t.float :float_short, limit: 5 + t.float :float_long, limit: 53 + + t.float :float_23, limit: 23 + t.float :float_24, limit: 24 + t.float :float_25, limit: 25 + end + + column_no_limit = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_no_limit' } + column_short = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_short' } + column_long = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_long' } + + column_23 = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_23' } + column_24 = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_24' } + column_25 = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_25' } + + # Mysql floats are precision 0..24, Mysql doubles are precision 25..53 + assert_equal 24, column_no_limit.limit + assert_equal 24, column_short.limit + assert_equal 53, column_long.limit + + assert_equal 24, column_23.limit + assert_equal 24, column_24.limit + assert_equal 53, column_25.limit + ensure + @connection.drop_table "mysql_doubles", if_exists: true + end + def test_schema assert @omgpost.first end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index dbbcaa075d..d961f4710e 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -26,7 +26,7 @@ require 'models/bird' require 'models/car' require 'models/bulb' require 'rexml/document' -require 'concurrent/atomics' +require 'concurrent/atomic/count_down_latch' class FirstAbstractClass < ActiveRecord::Base self.abstract_class = true @@ -1204,42 +1204,10 @@ class BasicsTest < ActiveRecord::TestCase assert_equal last, Developer.all.merge!(:order => :salary).to_a.last end - def test_abstract_class - assert !ActiveRecord::Base.abstract_class? - assert LoosePerson.abstract_class? - assert !LooseDescendant.abstract_class? - end - def test_abstract_class_table_name assert_nil AbstractCompany.table_name end - def test_descends_from_active_record - assert !ActiveRecord::Base.descends_from_active_record? - - # Abstract subclass of AR::Base. - assert LoosePerson.descends_from_active_record? - - # Concrete subclass of an abstract class. - assert LooseDescendant.descends_from_active_record? - - # Concrete subclass of AR::Base. - assert TightPerson.descends_from_active_record? - - # Concrete subclass of a concrete class but has no type column. - assert TightDescendant.descends_from_active_record? - - # Concrete subclass of AR::Base. - assert Post.descends_from_active_record? - - # Abstract subclass of a concrete class which has a type column. - # This is pathological, as you'll never have Sub < Abstract < Concrete. - assert !StiPost.descends_from_active_record? - - # Concrete subclasses an abstract class which has a type column. - assert !SubStiPost.descends_from_active_record? - end - def test_find_on_abstract_base_class_doesnt_use_type_condition old_class = LooseDescendant Object.send :remove_const, :LooseDescendant @@ -1284,53 +1252,6 @@ class BasicsTest < ActiveRecord::TestCase ActiveRecord::Base.logger = original_logger end - def test_compute_type_success - assert_equal Author, ActiveRecord::Base.send(:compute_type, 'Author') - end - - def test_compute_type_nonexistent_constant - e = assert_raises NameError do - ActiveRecord::Base.send :compute_type, 'NonexistentModel' - end - assert_equal 'uninitialized constant ActiveRecord::Base::NonexistentModel', e.message - assert_equal 'ActiveRecord::Base::NonexistentModel', e.name - end - - def test_compute_type_no_method_error - ActiveSupport::Dependencies.stub(:safe_constantize, proc{ raise NoMethodError }) do - assert_raises NoMethodError do - ActiveRecord::Base.send :compute_type, 'InvalidModel' - end - end - end - - def test_compute_type_on_undefined_method - error = nil - begin - Class.new(Author) do - alias_method :foo, :bar - end - rescue => e - error = e - end - - ActiveSupport::Dependencies.stub(:safe_constantize, proc{ raise e }) do - - exception = assert_raises NameError do - ActiveRecord::Base.send :compute_type, 'InvalidModel' - end - assert_equal error.message, exception.message - end - end - - def test_compute_type_argument_error - ActiveSupport::Dependencies.stub(:safe_constantize, proc{ raise ArgumentError }) do - assert_raises ArgumentError do - ActiveRecord::Base.send :compute_type, 'InvalidModel' - end - end - end - def test_clear_cache! # preheat cache c1 = Post.connection.schema_cache.columns('posts') diff --git a/activerecord/test/cases/connection_management_test.rb b/activerecord/test/cases/connection_management_test.rb index dff6ea0fb0..d43668e57c 100644 --- a/activerecord/test/cases/connection_management_test.rb +++ b/activerecord/test/cases/connection_management_test.rb @@ -92,7 +92,14 @@ module ActiveRecord app = lambda { |_| [200, {}, body] } response_body = ConnectionManagement.new(app).call(@env)[2] assert response_body.respond_to?(:to_path) - assert_equal response_body.to_path, "/path" + assert_equal "/path", response_body.to_path + end + + test "doesn't mutate the original response" do + original_response = [200, {}, 'hi'] + app = lambda { |_| original_response } + ConnectionManagement.new(app).call(@env)[2] + assert_equal 'hi', original_response.last end end end diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index 7ef5c93a48..efa3e0455e 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -1,5 +1,5 @@ require "cases/helper" -require 'concurrent/atomics' +require 'concurrent/atomic/count_down_latch' module ActiveRecord module ConnectionAdapters diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb index f67d85603a..52e3734dd0 100644 --- a/activerecord/test/cases/inheritance_test.rb +++ b/activerecord/test/cases/inheritance_test.rb @@ -1,4 +1,5 @@ require 'cases/helper' +require 'models/author' require 'models/company' require 'models/person' require 'models/post' @@ -55,6 +56,53 @@ class InheritanceTest < ActiveRecord::TestCase end end + def test_compute_type_success + assert_equal Author, ActiveRecord::Base.send(:compute_type, 'Author') + end + + def test_compute_type_nonexistent_constant + e = assert_raises NameError do + ActiveRecord::Base.send :compute_type, 'NonexistentModel' + end + assert_equal 'uninitialized constant ActiveRecord::Base::NonexistentModel', e.message + assert_equal 'ActiveRecord::Base::NonexistentModel', e.name + end + + def test_compute_type_no_method_error + ActiveSupport::Dependencies.stub(:safe_constantize, proc{ raise NoMethodError }) do + assert_raises NoMethodError do + ActiveRecord::Base.send :compute_type, 'InvalidModel' + end + end + end + + def test_compute_type_on_undefined_method + error = nil + begin + Class.new(Author) do + alias_method :foo, :bar + end + rescue => e + error = e + end + + ActiveSupport::Dependencies.stub(:safe_constantize, proc{ raise e }) do + + exception = assert_raises NameError do + ActiveRecord::Base.send :compute_type, 'InvalidModel' + end + assert_equal error.message, exception.message + end + end + + def test_compute_type_argument_error + ActiveSupport::Dependencies.stub(:safe_constantize, proc{ raise ArgumentError }) do + assert_raises ArgumentError do + ActiveRecord::Base.send :compute_type, 'InvalidModel' + end + end + end + def test_should_store_demodulized_class_name_with_store_full_sti_class_option_disabled without_store_full_sti_class do item = Namespaced::Company.new @@ -77,6 +125,32 @@ class InheritanceTest < ActiveRecord::TestCase end end + def test_descends_from_active_record + assert !ActiveRecord::Base.descends_from_active_record? + + # Abstract subclass of AR::Base. + assert LoosePerson.descends_from_active_record? + + # Concrete subclass of an abstract class. + assert LooseDescendant.descends_from_active_record? + + # Concrete subclass of AR::Base. + assert TightPerson.descends_from_active_record? + + # Concrete subclass of a concrete class but has no type column. + assert TightDescendant.descends_from_active_record? + + # Concrete subclass of AR::Base. + assert Post.descends_from_active_record? + + # Abstract subclass of a concrete class which has a type column. + # This is pathological, as you'll never have Sub < Abstract < Concrete. + assert !StiPost.descends_from_active_record? + + # Concrete subclasses an abstract class which has a type column. + assert !SubStiPost.descends_from_active_record? + end + def test_company_descends_from_active_record assert !ActiveRecord::Base.descends_from_active_record? assert AbstractCompany.descends_from_active_record?, 'AbstractCompany should descend from ActiveRecord::Base' @@ -84,6 +158,12 @@ class InheritanceTest < ActiveRecord::TestCase assert !Class.new(Company).descends_from_active_record?, 'Company subclass should not descend from ActiveRecord::Base' end + def test_abstract_class + assert !ActiveRecord::Base.abstract_class? + assert LoosePerson.abstract_class? + assert !LooseDescendant.abstract_class? + end + def test_inheritance_base_class assert_equal Post, Post.base_class assert_equal Post, SpecialPost.base_class @@ -223,7 +303,6 @@ class InheritanceTest < ActiveRecord::TestCase end end - def test_new_with_complex_inheritance assert_nothing_raised { Client.new(type: 'VerySpecialClient') } end diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index 3846ba8e7f..707a2d1da1 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -209,7 +209,7 @@ class LogSubscriberTest < ActiveRecord::TestCase Thread.new { assert_equal 0, ActiveRecord::LogSubscriber.runtime }.join end - unless current_adapter?(:Mysql2Adapter) + if ActiveRecord::Base.connection.prepared_statements def test_binary_data_is_not_logged Binary.create(data: 'some binary data') wait diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 741bec6017..15a0e0516d 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1,6 +1,7 @@ -require "cases/helper" -require "cases/migration/helper" +require 'cases/helper' +require 'cases/migration/helper' require 'bigdecimal/util' +require 'concurrent/atomic/count_down_latch' require 'models/person' require 'models/topic' diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 31686bde3f..acc3103ac6 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -744,9 +744,10 @@ class PersistenceTest < ActiveRecord::TestCase assert !topic.approved? assert_equal "The First Topic", topic.title - assert_raise(ActiveRecord::RecordNotUnique, ActiveRecord::StatementInvalid) do + error = assert_raise(ActiveRecord::RecordNotUnique, ActiveRecord::StatementInvalid) do topic.update_attributes(id: 3, title: "Hm is it possible?") end + assert_not_nil error.cause assert_not_equal "Hm is it possible?", Topic.find(3).title topic.update_attributes(id: 1234) diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index 5e4ba47988..344822c883 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -280,6 +280,32 @@ if current_adapter?(:MysqlAdapter, :Mysql2Adapter) con.reconnect! end end + + class PrimaryKeyBigintNilDefaultTest < ActiveRecord::TestCase + include SchemaDumpingHelper + + self.use_transactional_tests = false + + def setup + @connection = ActiveRecord::Base.connection + @connection.create_table(:bigint_defaults, id: :bigint, default: nil, force: true) + end + + def teardown + @connection.drop_table :bigint_defaults, if_exists: true + end + + test "primary key with bigint allows default override via nil" do + column = @connection.columns(:bigint_defaults).find { |c| c.name == 'id' } + assert column.bigint? + assert_not column.auto_increment? + end + + test "schema dump primary key with bigint default nil" do + schema = dump_table_schema "bigint_defaults" + assert_match %r{create_table "bigint_defaults", id: :bigint, default: nil}, schema + end + end end if current_adapter?(:PostgreSQLAdapter, :MysqlAdapter, :Mysql2Adapter) diff --git a/activerecord/test/cases/relation/mutation_test.rb b/activerecord/test/cases/relation/mutation_test.rb index cc0034ffd1..d0f60a84b5 100644 --- a/activerecord/test/cases/relation/mutation_test.rb +++ b/activerecord/test/cases/relation/mutation_test.rb @@ -22,6 +22,10 @@ module ActiveRecord def sanitize_sql(sql) sql end + + def sanitize_sql_for_order(sql) + sql + end end def relation diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index 675149556f..8794bc8043 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -20,6 +20,10 @@ module ActiveRecord def self.table_name 'fake_table' end + + def self.sanitize_sql_for_order(sql) + sql + end end def test_construction diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb index a93fa57257..723a7618ba 100644 --- a/activerecord/test/cases/tasks/mysql_rake_test.rb +++ b/activerecord/test/cases/tasks/mysql_rake_test.rb @@ -21,28 +21,21 @@ module ActiveRecord ActiveRecord::Tasks::DatabaseTasks.create @configuration end - def test_creates_database_with_default_encoding_and_collation + def test_creates_database_with_no_default_options @connection.expects(:create_database). - with('my-app-db', charset: 'utf8', collation: 'utf8_unicode_ci') + with('my-app-db', {}) ActiveRecord::Tasks::DatabaseTasks.create @configuration end - def test_creates_database_with_given_encoding_and_default_collation - @connection.expects(:create_database). - with('my-app-db', charset: 'utf8', collation: 'utf8_unicode_ci') - - ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge('encoding' => 'utf8') - end - - def test_creates_database_with_given_encoding_and_no_collation + def test_creates_database_with_given_encoding @connection.expects(:create_database). with('my-app-db', charset: 'latin1') ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge('encoding' => 'latin1') end - def test_creates_database_with_given_collation_and_no_encoding + def test_creates_database_with_given_collation @connection.expects(:create_database). with('my-app-db', collation: 'latin1_swedish_ci') @@ -111,7 +104,7 @@ module ActiveRecord def test_database_created_by_root assert_permissions_granted_for "pat" @connection.expects(:create_database). - with('my-app-db', :charset => 'utf8', :collation => 'utf8_unicode_ci') + with('my-app-db', {}) ActiveRecord::Tasks::DatabaseTasks.create @configuration end @@ -203,9 +196,9 @@ module ActiveRecord ActiveRecord::Tasks::DatabaseTasks.purge @configuration end - def test_recreates_database_with_the_default_options + def test_recreates_database_with_no_default_options @connection.expects(:recreate_database). - with('test-db', charset: 'utf8', collation: 'utf8_unicode_ci') + with('test-db', {}) ActiveRecord::Tasks::DatabaseTasks.purge @configuration end diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec index 93878518d7..404c16b29f 100644 --- a/activesupport/activesupport.gemspec +++ b/activesupport/activesupport.gemspec @@ -24,6 +24,6 @@ Gem::Specification.new do |s| s.add_dependency 'json', '~> 1.7', '>= 1.7.7' s.add_dependency 'tzinfo', '~> 1.1' s.add_dependency 'minitest', '~> 5.1' - s.add_dependency 'concurrent-ruby', '~> 1.0.0.pre3', '< 2.0.0' + s.add_dependency 'concurrent-ruby', '~> 1.0.0.pre5', '< 2.0.0' s.add_dependency 'method_source' end diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index b7da30123a..9a88fc286a 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -10,6 +10,7 @@ module ActiveSupport # FileStore implements the Strategy::LocalCache strategy which implements # an in-memory cache inside of a block. class FileStore < Store + prepend Strategy::LocalCache attr_reader :cache_path DIR_FORMATTER = "%03X" @@ -20,7 +21,6 @@ module ActiveSupport def initialize(cache_path, options = nil) super(options) @cache_path = cache_path.to_s - extend Strategy::LocalCache end # Deletes all items from the cache. In this case it deletes all the entries in the specified diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index 47133bf550..e2f536ef1e 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -24,6 +24,31 @@ module ActiveSupport # MemCacheStore implements the Strategy::LocalCache strategy which implements # an in-memory cache inside of a block. class MemCacheStore < Store + # Provide support for raw values in the local cache strategy. + module LocalCacheWithRaw # :nodoc: + protected + def read_entry(key, options) + entry = super + if options[:raw] && local_cache && entry + entry = deserialize_entry(entry.value) + end + entry + end + + def write_entry(key, entry, options) # :nodoc: + retval = super + if options[:raw] && local_cache && retval + raw_entry = Entry.new(entry.value.to_s) + raw_entry.expires_at = entry.expires_at + local_cache.write_entry(key, raw_entry, options) + end + retval + end + end + + prepend Strategy::LocalCache + prepend LocalCacheWithRaw + ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n # Creates a new Dalli::Client instance with specified addresses and options. @@ -63,9 +88,6 @@ module ActiveSupport UNIVERSAL_OPTIONS.each{|name| mem_cache_options.delete(name)} @data = self.class.build_mem_cache(*(addresses + [mem_cache_options])) end - - extend Strategy::LocalCache - extend LocalCacheWithRaw end # Reads multiple values from the cache using a single call to the @@ -181,28 +203,6 @@ module ActiveSupport nil end end - - # Provide support for raw values in the local cache strategy. - module LocalCacheWithRaw # :nodoc: - protected - def read_entry(key, options) - entry = super - if options[:raw] && local_cache && entry - entry = deserialize_entry(entry.value) - end - entry - end - - def write_entry(key, entry, options) # :nodoc: - retval = super - if options[:raw] && local_cache && retval - raw_entry = Entry.new(entry.value.to_s) - raw_entry.expires_at = entry.expires_at - local_cache.write_entry(key, raw_entry, options) - end - retval - end - end end end end diff --git a/activesupport/lib/active_support/cache/null_store.rb b/activesupport/lib/active_support/cache/null_store.rb index 4427eaafcd..0564ce5312 100644 --- a/activesupport/lib/active_support/cache/null_store.rb +++ b/activesupport/lib/active_support/cache/null_store.rb @@ -8,10 +8,7 @@ module ActiveSupport # be cached inside blocks that utilize this strategy. See # ActiveSupport::Cache::Strategy::LocalCache for more details. class NullStore < Store - def initialize(options = nil) - super(options) - extend Strategy::LocalCache - end + prepend Strategy::LocalCache def clear(options = nil) end diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb index fe5bc82c30..4e2d3e9875 100644 --- a/activesupport/lib/active_support/cache/strategy/local_cache.rb +++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb @@ -48,6 +48,10 @@ module ActiveSupport @data.clear end + def fetch(*args, &block) + @data.fetch(*args, &block) + end + def read_entry(key, options) @data[key] end @@ -98,13 +102,12 @@ module ActiveSupport protected def read_entry(key, options) # :nodoc: - if local_cache - entry = local_cache.read_entry(key, options) - unless entry + if cache = local_cache + cache.fetch(key) do entry = super - local_cache.write_entry(key, entry, options) + cache.write_entry(key, entry, options) + entry end - entry else super end diff --git a/activesupport/lib/active_support/concurrency/latch.rb b/activesupport/lib/active_support/concurrency/latch.rb index 7b8df0df04..4abe5ece6f 100644 --- a/activesupport/lib/active_support/concurrency/latch.rb +++ b/activesupport/lib/active_support/concurrency/latch.rb @@ -1,4 +1,4 @@ -require 'concurrent/atomics' +require 'concurrent/atomic/count_down_latch' module ActiveSupport module Concurrency @@ -8,7 +8,7 @@ module ActiveSupport ActiveSupport::Deprecation.warn("ActiveSupport::Concurrency::Latch is deprecated. Please use Concurrent::CountDownLatch instead.") super(count) end - + alias_method :release, :count_down def await diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index dc9f97168a..af18ff746f 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -1,6 +1,6 @@ require 'set' require 'thread' -require 'concurrent' +require 'concurrent/map' require 'pathname' require 'active_support/core_ext/module/aliasing' require 'active_support/core_ext/module/attribute_accessors' diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb index bbe25c9260..f89fc0fe14 100644 --- a/activesupport/lib/active_support/deprecation/reporting.rb +++ b/activesupport/lib/active_support/deprecation/reporting.rb @@ -83,7 +83,7 @@ module ActiveSupport rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/" offending_line = callstack.find { |frame| - !frame.absolute_path.start_with?(rails_gem_root) + frame.absolute_path && !frame.absolute_path.start_with?(rails_gem_root) } || callstack.first [offending_line.path, offending_line.lineno, offending_line.label] end diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb index c3907e9c22..f3e52b48ac 100644 --- a/activesupport/lib/active_support/inflector/inflections.rb +++ b/activesupport/lib/active_support/inflector/inflections.rb @@ -1,4 +1,4 @@ -require 'concurrent' +require 'concurrent/map' require 'active_support/core_ext/array/prepend_and_append' require 'active_support/i18n' diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb index e88b04da3e..7f73f9ddfc 100644 --- a/activesupport/lib/active_support/key_generator.rb +++ b/activesupport/lib/active_support/key_generator.rb @@ -1,4 +1,4 @@ -require 'concurrent' +require 'concurrent/map' require 'openssl' module ActiveSupport diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb index cbc20c103d..588ed67c81 100644 --- a/activesupport/lib/active_support/log_subscriber/test_helper.rb +++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb @@ -10,8 +10,7 @@ module ActiveSupport # class SyncLogSubscriberTest < ActiveSupport::TestCase # include ActiveSupport::LogSubscriber::TestHelper # - # def setup - # super + # setup do # ActiveRecord::LogSubscriber.attach_to(:active_record) # end # diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index 71354dd15f..7798c7ec60 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -1,5 +1,5 @@ require 'mutex_m' -require 'concurrent' +require 'concurrent/map' module ActiveSupport module Notifications diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 9f4bb6762d..6404f65612 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -1,5 +1,5 @@ require 'tzinfo' -require 'concurrent' +require 'concurrent/map' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/try' diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 854fcce4ef..94e73b6df3 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -625,6 +625,14 @@ module LocalCacheBehavior end end + def test_local_cache_of_read_nil + @cache.with_local_cache do + assert_equal nil, @cache.read('foo') + @cache.send(:bypass_local_cache) { @cache.write 'foo', 'bar' } + assert_equal nil, @cache.read('foo') + end + end + def test_local_cache_of_write_nil @cache.with_local_cache do assert @cache.write('foo', nil) diff --git a/activesupport/test/share_lock_test.rb b/activesupport/test/share_lock_test.rb index ad41db608b..465a657308 100644 --- a/activesupport/test/share_lock_test.rb +++ b/activesupport/test/share_lock_test.rb @@ -1,5 +1,5 @@ require 'abstract_unit' -require 'concurrent/atomics' +require 'concurrent/atomic/count_down_latch' require 'active_support/concurrency/share_lock' class ShareLockTest < ActiveSupport::TestCase diff --git a/guides/source/active_model_basics.md b/guides/source/active_model_basics.md index fe2501bd87..8f8256c983 100644 --- a/guides/source/active_model_basics.md +++ b/guides/source/active_model_basics.md @@ -8,10 +8,10 @@ classes. Active Model allows for Action Pack helpers to interact with plain Ruby objects. Active Model also helps build custom ORMs for use outside of the Rails framework. -After reading this guide, you will know: +After reading this guide, you will know: * How an Active Record model behaves. -* How Callbacks and validations work. +* How Callbacks and validations work. * How serializers work. * The Rails internationalization (i18n) framework. @@ -428,7 +428,7 @@ the Active Model API. class PersonTest < ActiveSupport::TestCase include ActiveModel::Lint::Tests - def setup + setup do @model = Person.new end end diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index b7773ea65a..ec31fa9d67 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -185,6 +185,8 @@ SELECT * FROM clients ORDER BY clients.id ASC LIMIT 1 The `first` method returns `nil` if no matching record is found and no exception will be raised. +If your [default scope](active_record_querying.html#applying-a-default-scope) contains an order method, `first` will return the first record according to this ordering. + You can pass in a numerical argument to the `first` method to return up to that number of results. For example ```ruby @@ -221,6 +223,8 @@ SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 The `last` method returns `nil` if no matching record is found and no exception will be raised. +If your [default scope](active_record_querying.html#applying-a-default-scope) contains an order method, `last` will return the last record according to this ordering. + You can pass in a numerical argument to the `last` method to return up to that number of results. For example ```ruby diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index 556b5ede3c..f6fc255c24 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -248,6 +248,13 @@ end @person.try { |p| "#{p.first_name} #{p.last_name}" } ``` +Note that `try` will swallow no-method errors, returning nil instead. If you want to protect against typos, use `try!` instead: + +```ruby +@number.try(:nest) # => nil +@number.try!(:nest) # NoMethodError: undefined method `nest' for 1:Fixnum +``` + NOTE: Defined in `active_support/core_ext/object/try.rb`. ### `class_eval(*args, &block)` diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index 41881abb62..0f2283318a 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -169,7 +169,7 @@ 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_files` is set to true. You should use `app/assets` for +`config.public_file_server.enabled` 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 @@ -1021,7 +1021,7 @@ header](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9) is a W3C specification that describes how a request can be cached. When no CDN is used, a browser will use this information to cache contents. This is very helpful for assets that are not modified so that a browser does not need to re-download a -website's CSS or javascript on every request. Generally we want our Rails server +website's CSS or JavaScript on every request. Generally we want our Rails server to tell our CDN (and browser) that the asset is "public", that means any cache can store the request. Also we commonly want to set `max-age` which is how long the cache will store the object before invalidating the cache. The `max-age` @@ -1029,7 +1029,9 @@ value is set to seconds with a maximum possible value of `31536000` which is one year. You can do this in your rails application by setting ``` -config.static_cache_control = "public, max-age=31536000" +config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=31536000' +} ``` Now when your application serves an asset in production, the CDN will store the diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md index 74cd9bdc7b..c272daac28 100644 --- a/guides/source/association_basics.md +++ b/guides/source/association_basics.md @@ -16,7 +16,7 @@ After reading this guide, you will know: Why Associations? ----------------- -Why do we need associations between models? Because they make common operations simpler and easier in your code. For example, consider a simple Rails application that includes a model for customers and a model for orders. Each customer can have many orders. Without associations, the model declarations would look like this: +In Rails, an _association_ is a connection between two Active Record models. Why do we need associations between models? Because they make common operations simpler and easier in your code. For example, consider a simple Rails application that includes a model for customers and a model for orders. Each customer can have many orders. Without associations, the model declarations would look like this: ```ruby class Customer < ActiveRecord::Base @@ -71,7 +71,7 @@ To learn more about the different types of associations, read the next section o The Types of Associations ------------------------- -In Rails, an _association_ is a connection between two Active Record models. Associations are implemented using macro-style calls, so that you can declaratively add features to your models. For example, by declaring that one model `belongs_to` another, you instruct Rails to maintain Primary Key-Foreign Key information between instances of the two models, and you also get a number of utility methods added to your model. Rails supports six types of associations: +Rails supports six types of associations: * `belongs_to` * `has_one` @@ -80,6 +80,8 @@ In Rails, an _association_ is a connection between two Active Record models. Ass * `has_one :through` * `has_and_belongs_to_many` +Associations are implemented using macro-style calls, so that you can declaratively add features to your models. For example, by declaring that one model `belongs_to` another, you instruct Rails to maintain [Primary Key](https://en.wikipedia.org/wiki/Unique_key)-[Foreign Key](https://en.wikipedia.org/wiki/Foreign_key) information between instances of the two models, and you also get a number of utility methods added to your model. + In the remainder of this guide, you'll learn how to declare and use the various forms of associations. But first, a quick introduction to the situations where each association type is appropriate. ### The `belongs_to` Association @@ -238,13 +240,15 @@ class CreateAppointments < ActiveRecord::Migration end ``` -The collection of join models can be managed via the API. For example, if you assign +The collection of join models can be managed via the [`has_many` association methods](#has-many-association-reference). +For example, if you assign: ```ruby physician.patients = patients ``` -new join models are created for newly associated objects, and if some are gone their rows are deleted. +Then new join models are automatically created for the newly associated objects. +If some that existed previously are now missing, then their join rows are automatically deleted. WARNING: Automatic deletion of join models is direct, no destroy callbacks are triggered. diff --git a/guides/source/configuring.md b/guides/source/configuring.md index e2125cae2e..28388e4957 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -122,7 +122,7 @@ defaults to `:debug` for all environments. The available log levels are: `:debug * `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_files` configures Rails to serve static files. This option defaults to true, but in the production environment it is set to false because the server software (e.g. NGINX or Apache) used to run the application should serve static files instead. If you are running or testing your app in production mode using WEBrick (it is not recommended to use WEBrick in production) set the option to true. Otherwise, you won't be able to use page caching and request for files that exist under the public directory. +* `config.public_file_server.enabled` configures Rails to serve static files from the public directory. This option defaults to true, but in the production environment it is set to false because the server software (e.g. NGINX or Apache) used to run the application should serve static files instead. If you are running or testing your app in production mode using WEBrick (it is not recommended to use WEBrick in production) set the option to true. Otherwise, you won't be able to use page caching and request for files that exist under the public directory. * `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: @@ -199,7 +199,7 @@ The full set of methods that can be used in this block are as follows: Every Rails application comes with a standard set of middleware which it uses in this order in the development environment: * `ActionDispatch::SSL` forces every request to be under HTTPS protocol. Will be available if `config.force_ssl` is set to `true`. Options passed to this can be configured by using `config.ssl_options`. -* `ActionDispatch::Static` is used to serve static assets. Disabled if `config.serve_static_files` is `false`. Set `config.static_index` if you need to serve a static directory index file that is not named `index`. For example, to serve `main.html` instead of `index.html` for directory requests, set `config.static_index` to `"main"`. +* `ActionDispatch::Static` is used to serve static assets. Disabled if `config.public_file_server.enabled` is `false`. Set `config.static_index` if you need to serve a static directory index file that is not named `index`. For example, to serve `main.html` instead of `index.html` for directory requests, set `config.static_index` to `"main"`. * `Rack::Lock` wraps the app in mutex so it can only be called by a single thread at a time. Only enabled when `config.cache_classes` is `false`. * `ActiveSupport::Cache::Strategy::LocalCache` serves as a basic memory backed cache. This cache is not thread safe and is intended only for serving as a temporary memory cache for a single thread. * `Rack::Runtime` sets an `X-Runtime` header, containing the time (in seconds) taken to execute the request. diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md index 6d689804a8..53c3cbf80b 100644 --- a/guides/source/contributing_to_ruby_on_rails.md +++ b/guides/source/contributing_to_ruby_on_rails.md @@ -128,11 +128,11 @@ Contributing to the Rails Documentation Ruby on Rails has two main sets of documentation: the guides, which help you learn about Ruby on Rails, and the API, which serves as a reference. -You can help improve the Rails guides by making them more coherent, consistent or readable, adding missing information, correcting factual errors, fixing typos, or bringing them up to date with the latest edge Rails. To get involved in the translation of Rails guides, please see [Translating Rails Guides](https://wiki.github.com/rails/docrails/translating-rails-guides). +You can help improve the Rails guides by making them more coherent, consistent or readable, adding missing information, correcting factual errors, fixing typos, or bringing them up to date with the latest edge Rails. You can either open a pull request to [Rails](http://github.com/rails/rails) or ask the [Rails core team](http://rubyonrails.org/core) for commit access on -[docrails](http://github.com/rails/docrails) if you contribute regularly. +docrails if you contribute regularly. Please do not open pull requests in docrails, if you'd like to get feedback on your change, ask for it in [Rails](http://github.com/rails/rails) instead. diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md index 0a6e2e5dba..93bb51557a 100644 --- a/guides/source/form_helpers.md +++ b/guides/source/form_helpers.md @@ -657,7 +657,7 @@ NOTE: If the user has not selected a file the corresponding parameter will be an ### Dealing with Ajax -Unlike other forms making an asynchronous file upload form is not as simple as providing `form_for` with `remote: true`. With an Ajax form the serialization is done by JavaScript running inside the browser and since JavaScript cannot read files from your hard drive the file cannot be uploaded. The most common workaround is to use an invisible iframe that serves as the target for the form submission. +Unlike other forms, making an asynchronous file upload form is not as simple as providing `form_for` with `remote: true`. With an Ajax form the serialization is done by JavaScript running inside the browser and since JavaScript cannot read files from your hard drive the file cannot be uploaded. The most common workaround is to use an invisible iframe that serves as the target for the form submission. Customizing Form Builders ------------------------- diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md index 87f869aff3..273fbc08e2 100644 --- a/guides/source/rails_on_rack.md +++ b/guides/source/rails_on_rack.md @@ -213,7 +213,7 @@ Much of Action Controller's functionality is implemented as Middlewares. The fol **`ActionDispatch::Static`** -* Used to serve static files. Disabled if `config.serve_static_files` is `false`. +* Used to serve static files from the public directory. Disabled if `config.public_file_server.enabled` is `false`. **`Rack::Lock`** diff --git a/guides/source/testing.md b/guides/source/testing.md index a07772036b..2f941a8280 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -908,12 +908,12 @@ require 'test_helper' class ArticlesControllerTest < ActionController::TestCase # called before every single test - def setup + setup do @article = articles(:one) end # called after every single test - def teardown + teardown do # when controller is using cache it may be a good idea to reset it afterwards Rails.cache.clear end diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index 1fe95c3422..fa6a01671b 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -1191,8 +1191,10 @@ You can help test performance with these additions to your test environment: ```ruby # Configure static asset server for tests with Cache-Control for performance -config.serve_static_files = true -config.static_cache_control = 'public, max-age=3600' +config.public_file_server.enabled = true +config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=3600' +} ``` ### config/initializers/wrap_parameters.rb diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 967f850ea0..6980ba94e2 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,48 @@ +* Deprecate `serve_static_files` in favor of `public_file_server.enabled`. + + Unifies the static asset options under `public_file_server`. + + To upgrade, replace occurrences of: + + ``` + config.serve_static_files = # false or true + ``` + + in your environment files, with: + + ``` + config.public_file_server.enabled = # false or true + ``` + + *Kasper Timm Hansen* + +* Deprecate `config.static_cache_control` in favor of + `config.public_file_server.headers`. + + To upgrade, replace occurrences of: + + ``` + config.static_cache_control = 'public, max-age=60' + ``` + + in your environment files, with: + + ``` + config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=60' + } + ``` + + `config.public_file_server.headers` can set arbitrary headers, sent along when + a response is delivered. + + *Yuki Nishijima* + +* Route generator should be idempotent + running generators several times no longer require you to cleanup routes.rb + + *Thiago Pinto* + * Allow passing an environment to `config_for`. *Simon Eskildsen* diff --git a/railties/Rakefile b/railties/Rakefile index cf130a5f14..73d881b318 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -5,20 +5,33 @@ task :default => :test desc "Run all unit tests" task :test => 'test:isolated' +dash_i = [ + 'test', + 'lib', + "#{File.dirname(__FILE__)}/../activesupport/lib", + "#{File.dirname(__FILE__)}/../actionpack/lib", + "#{File.dirname(__FILE__)}/../activemodel/lib" +] + +dash_i.reverse_each do |x| + $:.unshift x unless $:.include? x +end +$-w = true + +require 'bundler/setup' unless defined?(Bundler) +require 'active_support' + namespace :test do task :isolated do dirs = (ENV["TEST_DIR"] || ENV["TEST_DIRS"] || "**").split(",") test_files = dirs.map { |dir| "test/#{dir}/*_test.rb" } Dir[*test_files].each do |file| next true if file.include?("fixtures") - dash_i = [ - 'test', - 'lib', - "#{File.dirname(__FILE__)}/../activesupport/lib", - "#{File.dirname(__FILE__)}/../actionpack/lib", - "#{File.dirname(__FILE__)}/../activemodel/lib" - ] - ruby "-w", "-I#{dash_i.join ':'}", file + puts "#{FileUtils::RUBY} -w -I#{dash_i.join ':'} #{file}" + + # We could run these in parallel, but pretty much all of the + # railties tests already run in parallel, so ¯\_(⊙︿⊙)_/¯ + Process.waitpid fork { ARGV.clear; load file } end end end diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index ee9c87b5cf..785671f70b 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -3,6 +3,9 @@ require 'active_support/file_update_checker' require 'rails/engine/configuration' require 'rails/source_annotation_extractor' +require 'active_support/deprecation' +require 'active_support/core_ext/string/strip' # for strip_heredoc + module Rails class Application class Configuration < ::Rails::Engine::Configuration @@ -11,7 +14,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_files, :ssl_options, :static_index, :public_file_server, + :ssl_options, :static_index, :public_file_server, :session_options, :time_zone, :reload_classes_only_on_change, :beginning_of_week, :filter_redirect, :x @@ -26,9 +29,9 @@ module Rails @filter_parameters = [] @filter_redirect = [] @helpers_paths = [] - @serve_static_files = true @static_index = "index" @public_file_server = ActiveSupport::OrderedOptions.new + @public_file_server.enabled = true @force_ssl = false @ssl_options = {} @session_store = :cookie_store @@ -53,13 +56,34 @@ module Rails end def static_cache_control=(value) - ActiveSupport::Deprecation.warn("static_cache_control is deprecated and will be removed in Rails 5.1. " \ - "Please use `config.public_file_server.headers = {'Cache-Control' => #{value}} " \ - "instead.") + ActiveSupport::Deprecation.warn <<-eow.strip_heredoc + `static_cache_control` is deprecated and will be removed in Rails 5.1. + Please use + `config.public_file_server.headers = { 'Cache-Control' => '#{value}' }` + instead. + eow @static_cache_control = value end + def serve_static_files + ActiveSupport::Deprecation.warn <<-eow.strip_heredoc + `serve_static_files` is deprecated and will be removed in Rails 5.1. + Please use `public_file_server.enabled` instead. + eow + + @public_file_server.enabled + end + + def serve_static_files=(value) + ActiveSupport::Deprecation.warn <<-eow.strip_heredoc + `serve_static_files` is deprecated and will be removed in Rails 5.1. + Please use `public_file_server.enabled = #{value}` instead. + eow + + @public_file_server.enabled = value + end + def encoding=(value) @encoding = value silence_warnings do diff --git a/railties/lib/rails/application/default_middleware_stack.rb b/railties/lib/rails/application/default_middleware_stack.rb index 387d92db73..b0d481c21a 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_files + if config.public_file_server.enabled headers = config.public_file_server.headers || {} headers['Cache-Control'.freeze] = config.static_cache_control if config.static_cache_control diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index b4356f71e0..5bbd2f1aed 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -235,7 +235,7 @@ module Rails sentinel = /\.routes\.draw do\s*\n/m in_root do - inject_into_file 'config/routes.rb', " #{routing_code}\n", { after: sentinel, verbose: false, force: true } + inject_into_file 'config/routes.rb', " #{routing_code}\n", { after: sentinel, verbose: false, force: false } end end diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt index e29f0bacaa..4dd20a9d2e 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt @@ -15,8 +15,10 @@ Rails.application.configure do # Enable/disable caching. By default caching is disabled. if Rails.root.join('tmp/caching-dev.txt').exist? config.action_controller.perform_caching = true - config.static_cache_control = "public, max-age=172800" config.cache_store = :memory_store + config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=172800' + } else config.action_controller.perform_caching = false config.cache_store = :null_store 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 0297ab75f6..a5302550fa 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 @@ -16,7 +16,7 @@ Rails.application.configure do # 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? + config.public_file_server.enabled = 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 5165100c22..8133917591 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 file server for tests with Cache-Control for performance. - config.serve_static_files = true + # Configure public file server for tests with Cache-Control for performance. + config.public_file_server.enabled = true config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=3600' } diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb index 9fca213a04..3b1c1b5ed1 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb @@ -1,5 +1,7 @@ -# Avoid CORS issues when API is called from the frontend app -# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests +# Be sure to restart your server when you modify this file. + +# Avoid CORS issues when API is called from the frontend app. +# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. # Read more: https://github.com/cyu/rack-cors diff --git a/railties/lib/rails/generators/rails/plugin/templates/gitignore b/railties/lib/rails/generators/rails/plugin/templates/gitignore index d524fcbc4e..54c78d7927 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/gitignore +++ b/railties/lib/rails/generators/rails/plugin/templates/gitignore @@ -6,5 +6,4 @@ pkg/ <%= dummy_path %>/db/*.sqlite3-journal <%= dummy_path %>/log/*.log <%= dummy_path %>/tmp/ -<%= dummy_path %>/.sass-cache <% end -%> diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb index dca5cf2e5b..18882e1855 100644 --- a/railties/test/application/assets_test.rb +++ b/railties/test/application/assets_test.rb @@ -240,7 +240,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_files = true" + add_to_env_config "production", "config.public_file_server.enabled = true" precompile! RAILS_ENV: 'production' diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 22b615023d..5f3d1879eb 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -308,37 +308,47 @@ 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 + test "In production mode, config.public_file_server.enabled is off by default" do restore_default_config with_rails_env "production" do app 'production' - assert_not app.config.serve_static_files + assert_not app.config.public_file_server.enabled end end - test "In production mode, config.serve_static_files is enabled when RAILS_SERVE_STATIC_FILES is set" do + test "In production mode, config.public_file_server.enabled 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 app 'production' - assert app.config.serve_static_files + assert app.config.public_file_server.enabled end end end - test "In production mode, config.serve_static_files is disabled when RAILS_SERVE_STATIC_FILES is blank" do + test "In production mode, config.public_file_server.enabled 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 app 'production' - assert_not app.config.serve_static_files + assert_not app.config.public_file_server.enabled end end end + test "config.serve_static_files is deprecated" do + make_basic_app do |application| + assert_deprecated do + application.config.serve_static_files = true + end + + assert application.config.public_file_server.enabled + end + end + test "config.static_cache_control is deprecated" do make_basic_app do |application| assert_deprecated do diff --git a/railties/test/application/middleware/sendfile_test.rb b/railties/test/application/middleware/sendfile_test.rb index dc96480d6d..be86f1a3b8 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_files = true + app.config.public_file_server.enabled = 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 138c63266e..1434522cce 100644 --- a/railties/test/application/middleware_test.rb +++ b/railties/test/application/middleware_test.rb @@ -155,8 +155,8 @@ module ApplicationTests assert_not_includes middleware, "ActionDispatch::LoadInterlock" end - test "removes static asset server if serve_static_files is disabled" do - add_to_config "config.serve_static_files = false" + test "removes static asset server if public_file_server.enabled is disabled" do + add_to_config "config.public_file_server.enabled = false" boot! assert !middleware.include?("ActionDispatch::Static") end diff --git a/railties/test/application/rake/migrations_test.rb b/railties/test/application/rake/migrations_test.rb index 2d8bd7c571..6b74707959 100644 --- a/railties/test/application/rake/migrations_test.rb +++ b/railties/test/application/rake/migrations_test.rb @@ -154,6 +154,28 @@ module ApplicationTests end end + test 'running migrations with not timestamp head migration files' do + Dir.chdir(app_path) do + + app_file "db/migrate/1_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration + end + MIGRATION + + app_file "db/migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration + end + MIGRATION + + `bin/rake db:migrate` + + output = `bin/rake db:migrate:status` + + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + end + end + test 'schema generation when dump_schema_after_migration is set' do add_to_config('config.active_record.dump_schema_after_migration = false') diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb index fabba555ef..b4fbea4af4 100644 --- a/railties/test/generators/actions_test.rb +++ b/railties/test/generators/actions_test.rb @@ -235,6 +235,21 @@ class ActionsTest < Rails::Generators::TestCase assert_file 'config/routes.rb', /#{Regexp.escape(route_command)}/ end + def test_route_should_be_idempotent + run_generator + route_path = File.expand_path('config/routes.rb', destination_root) + + # runs first time, not asserting + action :route, "root 'welcome#index'" + content_1 = File.read(route_path) + + # runs second time + action :route, "root 'welcome#index'" + content_2 = File.read(route_path) + + assert_equal content_1, content_2 + end + def test_route_should_add_data_with_an_new_line run_generator action :route, "root 'welcome#index'" diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 2c82f728ee..24386de82a 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -1205,7 +1205,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_files = false") + add_to_config("config.public_file_server.enabled = false") @plugin.write "lib/bukkits.rb", <<-RUBY module Bukkits diff --git a/tasks/release.rb b/tasks/release.rb index 4711974b63..5f0f4a06c1 100644 --- a/tasks/release.rb +++ b/tasks/release.rb @@ -119,6 +119,10 @@ namespace :all do end end + task :bundle do + sh 'bundle check' + end + task :commit do File.open('pkg/commit_message.txt', 'w') do |f| f.puts "# Preparing for #{version} release\n" @@ -135,5 +139,5 @@ namespace :all do sh "git push --tags" end - task :release => %w(ensure_clean_state build commit tag push) + task :release => %w(ensure_clean_state build bundle commit tag push) end |