diff --git a/actionmailbox/lib/rails/generators/test_unit/templates/mailbox_test.rb.tt b/actionmailbox/lib/rails/generators/test_unit/templates/mailbox_test.rb.tt index 0b51f29fe4..3e215b4d0b 100644 --- a/actionmailbox/lib/rails/generators/test_unit/templates/mailbox_test.rb.tt +++ b/actionmailbox/lib/rails/generators/test_unit/templates/mailbox_test.rb.tt @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require "test_helper" class <%= class_name %>MailboxTest < ActionMailbox::TestCase diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index b9088e6d86..9ca0bbb9db 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -148,7 +148,7 @@ module ActionController attr_internal :response, :request delegate :session, to: "@_request" delegate :headers, :status=, :location=, :content_type=, - :status, :location, :content_type, to: "@_response" + :status, :location, :content_type, :media_type, to: "@_response" def initialize @_request = nil diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index bf5e7a433f..5c6f7fe396 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -205,7 +205,7 @@ module ActionController #:nodoc: yield collector if block_given? if format = collector.negotiate_format(request) - if content_type && content_type != format + if media_type && media_type != format raise ActionController::RespondToMismatchError end _process_format(format) diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb index b81d3ef539..a251c29d23 100644 --- a/actionpack/lib/action_controller/metal/renderers.rb +++ b/actionpack/lib/action_controller/metal/renderers.rb @@ -157,7 +157,7 @@ module ActionController json = json.to_json(options) unless json.kind_of?(String) if options[:callback].present? - if content_type.nil? || content_type == Mime[:json] + if media_type.nil? || media_type == Mime[:json] self.content_type = Mime[:js] end diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index 7d0a944381..7f7c736965 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -73,7 +73,7 @@ module ActionController end def _set_rendered_content_type(format) - if format && !response.content_type + if format && !response.media_type self.content_type = format.to_s end end diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 69798f99e0..e5cffccf35 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -243,8 +243,12 @@ module ActionDispatch # :nodoc: end # Content type of response. - # It returns just MIME type and does NOT contain charset part. def content_type + super.presence + end + + # Media type of response. + def media_type parsed_content_type_header.mime_type end @@ -458,7 +462,7 @@ module ActionDispatch # :nodoc: end def assign_default_content_type_and_charset! - return if content_type + return if media_type ct = parsed_content_type_header set_content_type(ct.mime_type || Mime[:html].to_s, diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb index 0b15c94122..59113e13f4 100644 --- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb @@ -4,8 +4,6 @@ require "action_dispatch/http/request" require "action_dispatch/middleware/exception_wrapper" require "action_dispatch/routing/inspector" -require "active_support/actionable_error" - require "action_view" require "action_view/base" diff --git a/actionpack/lib/action_dispatch/testing/assertions.rb b/actionpack/lib/action_dispatch/testing/assertions.rb index 08c2969685..dcaf914ac9 100644 --- a/actionpack/lib/action_dispatch/testing/assertions.rb +++ b/actionpack/lib/action_dispatch/testing/assertions.rb @@ -14,7 +14,7 @@ module ActionDispatch include Rails::Dom::Testing::Assertions def html_document - @html_document ||= if @response.content_type.to_s.end_with?("xml") + @html_document ||= if @response.media_type.to_s.end_with?("xml") Nokogiri::XML::Document.parse(@response.body) else Nokogiri::HTML::Document.parse(@response.body) diff --git a/actionpack/lib/action_dispatch/testing/test_response.rb b/actionpack/lib/action_dispatch/testing/test_response.rb index 6f7c86fdcf..f1dd4099c5 100644 --- a/actionpack/lib/action_dispatch/testing/test_response.rb +++ b/actionpack/lib/action_dispatch/testing/test_response.rb @@ -19,7 +19,7 @@ module ActionDispatch end def response_parser - @response_parser ||= RequestEncoder.parser(content_type) + @response_parser ||= RequestEncoder.parser(media_type) end end end diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb index 636b025f2c..aeb0d07195 100644 --- a/actionpack/test/controller/content_type_test.rb +++ b/actionpack/test/controller/content_type_test.rb @@ -66,68 +66,68 @@ class ContentTypeTest < ActionController::TestCase def test_render_defaults get :render_defaults assert_equal "utf-8", @response.charset - assert_equal Mime[:text], @response.content_type + assert_equal Mime[:text], @response.media_type end def test_render_changed_charset_default with_default_charset "utf-16" do get :render_defaults assert_equal "utf-16", @response.charset - assert_equal Mime[:text], @response.content_type + assert_equal Mime[:text], @response.media_type end end # :ported: def test_content_type_from_body get :render_content_type_from_body - assert_equal Mime[:rss], @response.content_type + assert_equal Mime[:rss], @response.media_type assert_equal "utf-8", @response.charset end # :ported: def test_content_type_from_render get :render_content_type_from_render - assert_equal Mime[:rss], @response.content_type + assert_equal Mime[:rss], @response.media_type assert_equal "utf-8", @response.charset end # :ported: def test_charset_from_body get :render_charset_from_body - assert_equal Mime[:text], @response.content_type + assert_equal Mime[:text], @response.media_type assert_equal "utf-16", @response.charset end # :ported: def test_nil_charset_from_body get :render_nil_charset_from_body - assert_equal Mime[:text], @response.content_type + assert_equal Mime[:text], @response.media_type assert_equal "utf-8", @response.charset, @response.headers.inspect end def test_nil_default_for_erb with_default_charset nil do get :render_default_for_erb - assert_equal Mime[:html], @response.content_type + assert_equal Mime[:html], @response.media_type assert_nil @response.charset, @response.headers.inspect end end def test_default_for_erb get :render_default_for_erb - assert_equal Mime[:html], @response.content_type + assert_equal Mime[:html], @response.media_type assert_equal "utf-8", @response.charset end def test_default_for_builder get :render_default_for_builder - assert_equal Mime[:xml], @response.content_type + assert_equal Mime[:xml], @response.media_type assert_equal "utf-8", @response.charset end def test_change_for_builder get :render_change_for_builder - assert_equal Mime[:html], @response.content_type + assert_equal Mime[:html], @response.media_type assert_equal "utf-8", @response.charset end @@ -148,22 +148,22 @@ class AcceptBasedContentTypeTest < ActionController::TestCase def test_render_default_content_types_for_respond_to @request.accept = Mime[:html].to_s get :render_default_content_types_for_respond_to - assert_equal Mime[:html], @response.content_type + assert_equal Mime[:html], @response.media_type @request.accept = Mime[:js].to_s get :render_default_content_types_for_respond_to - assert_equal Mime[:js], @response.content_type + assert_equal Mime[:js], @response.media_type end def test_render_default_content_types_for_respond_to_with_template @request.accept = Mime[:xml].to_s get :render_default_content_types_for_respond_to - assert_equal Mime[:xml], @response.content_type + assert_equal Mime[:xml], @response.media_type end def test_render_default_content_types_for_respond_to_with_overwrite @request.accept = Mime[:rss].to_s get :render_default_content_types_for_respond_to - assert_equal Mime[:xml], @response.content_type + assert_equal Mime[:xml], @response.media_type end end diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index 4dddd98f9f..cce229b30d 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -522,11 +522,11 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest with_test_route_set do get "/get", headers: { "Accept" => "application/json" }, xhr: true assert_equal "application/json", request.accept - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type get "/get", headers: { "HTTP_ACCEPT" => "application/json" }, xhr: true assert_equal "application/json", request.accept - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type end end @@ -986,7 +986,7 @@ class IntegrationRequestEncodersTest < ActionDispatch::IntegrationTest def test_encoding_as_json post_to_foos as: :json do assert_response :success - assert_equal "application/json", request.content_type + assert_equal "application/json", request.media_type assert_equal "application/json", request.accepts.first.to_s assert_equal :json, request.format.ref assert_equal({ "foo" => "fighters" }, request.request_parameters) @@ -1025,7 +1025,7 @@ class IntegrationRequestEncodersTest < ActionDispatch::IntegrationTest post_to_foos as: :wibble do assert_response :success assert_equal "/foos_wibble", request.path - assert_equal "text/wibble", request.content_type + assert_equal "text/wibble", request.media_type assert_equal "text/wibble", request.accepts.first.to_s assert_equal :wibble, request.format.ref assert_equal Hash.new, request.request_parameters # Unregistered MIME Type can't be parsed. diff --git a/actionpack/test/controller/localized_templates_test.rb b/actionpack/test/controller/localized_templates_test.rb index d84a76fb46..5c5cef66d5 100644 --- a/actionpack/test/controller/localized_templates_test.rb +++ b/actionpack/test/controller/localized_templates_test.rb @@ -43,6 +43,6 @@ class LocalizedTemplatesTest < ActionController::TestCase I18n.locale = :it get :hello_world assert_equal "Ciao Mondo", @response.body - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type end end diff --git a/actionpack/test/controller/metal/renderers_test.rb b/actionpack/test/controller/metal/renderers_test.rb index 5f0d125128..f6558f1354 100644 --- a/actionpack/test/controller/metal/renderers_test.rb +++ b/actionpack/test/controller/metal/renderers_test.rb @@ -38,13 +38,13 @@ class RenderersMetalTest < ActionController::TestCase get :one assert_response :success assert_equal({ a: "b" }.to_json, @response.body) - assert_equal "application/json", @response.content_type + assert_equal "application/json", @response.media_type end def test_render_xml get :two assert_response :success assert_equal(" ", @response.body) - assert_equal "text/plain", @response.content_type + assert_equal "text/plain", @response.media_type end end diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb index 2f8f191828..fc16c639fb 100644 --- a/actionpack/test/controller/mime/respond_to_test.rb +++ b/actionpack/test/controller/mime/respond_to_test.rb @@ -423,12 +423,12 @@ class RespondToControllerTest < ActionController::TestCase def test_using_defaults @request.accept = "*/*" get :using_defaults - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "Hello world!", @response.body @request.accept = "application/xml" get :using_defaults - assert_equal "application/xml", @response.content_type + assert_equal "application/xml", @response.media_type assert_equal "

Hello world!

\n", @response.body end @@ -449,12 +449,12 @@ class RespondToControllerTest < ActionController::TestCase def test_using_defaults_with_type_list @request.accept = "*/*" get :using_defaults_with_type_list - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "Hello world!", @response.body @request.accept = "application/xml" get :using_defaults_with_type_list - assert_equal "application/xml", @response.content_type + assert_equal "application/xml", @response.media_type assert_equal "

Hello world!

\n", @response.body end @@ -468,7 +468,7 @@ class RespondToControllerTest < ActionController::TestCase def test_using_non_conflicting_nested_js_then_js @request.accept = "*/*" get :using_non_conflicting_nested_js_then_js - assert_equal "text/javascript", @response.content_type + assert_equal "text/javascript", @response.media_type assert_equal "JS", @response.body end @@ -499,12 +499,12 @@ class RespondToControllerTest < ActionController::TestCase def test_custom_types @request.accept = "application/fancy-xml" get :custom_type_handling - assert_equal "application/fancy-xml", @response.content_type + assert_equal "application/fancy-xml", @response.media_type assert_equal "Fancy XML", @response.body @request.accept = "text/html" get :custom_type_handling - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "HTML", @response.body end @@ -595,7 +595,7 @@ class RespondToControllerTest < ActionController::TestCase @request.accept = "application/json" get :json_with_callback assert_equal "/**/alert(JS)", @response.body - assert_equal "text/javascript", @response.content_type + assert_equal "text/javascript", @response.media_type end def test_xhr @@ -605,13 +605,13 @@ class RespondToControllerTest < ActionController::TestCase def test_custom_constant get :custom_constant_handling, format: "mobile" - assert_equal "text/x-mobile", @response.content_type + assert_equal "text/x-mobile", @response.media_type assert_equal "Mobile", @response.body end def test_custom_constant_handling_without_block get :custom_constant_handling_without_block, format: "mobile" - assert_equal "text/x-mobile", @response.content_type + assert_equal "text/x-mobile", @response.media_type assert_equal "Mobile", @response.body end @@ -664,7 +664,7 @@ class RespondToControllerTest < ActionController::TestCase assert_equal '
Hello future from Firefox!
', @response.body get :iphone_with_html_response_type, format: "iphone" - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal '
Hello iPhone future from iPhone!
', @response.body end @@ -672,7 +672,7 @@ class RespondToControllerTest < ActionController::TestCase @request.accept = "text/iphone" get :iphone_with_html_response_type assert_equal '
Hello iPhone future from iPhone!
', @response.body - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type end def test_invalid_format @@ -702,7 +702,7 @@ class RespondToControllerTest < ActionController::TestCase def test_variant_with_implicit_template_rendering get :variant_with_implicit_template_rendering, params: { v: :mobile } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "mobile", @response.body end @@ -756,137 +756,137 @@ class RespondToControllerTest < ActionController::TestCase def test_variant_with_format_and_custom_render get :variant_with_format_and_custom_render, params: { v: :phone } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "mobile", @response.body end def test_multiple_variants_for_format get :multiple_variants_for_format, params: { v: :tablet } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "tablet", @response.body end def test_no_variant_in_variant_setup get :variant_plus_none_for_format - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "none", @response.body end def test_variant_inline_syntax get :variant_inline_syntax - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "none", @response.body get :variant_inline_syntax, params: { v: :phone } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body end def test_variant_inline_syntax_with_format get :variant_inline_syntax, format: :js - assert_equal "text/javascript", @response.content_type + assert_equal "text/javascript", @response.media_type assert_equal "js", @response.body end def test_variant_inline_syntax_without_block get :variant_inline_syntax_without_block, params: { v: :phone } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body end def test_variant_any get :variant_any, params: { v: :phone } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body get :variant_any, params: { v: :tablet } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "any", @response.body get :variant_any, params: { v: :phablet } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "any", @response.body end def test_variant_any_any get :variant_any_any - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "any", @response.body get :variant_any_any, params: { v: :phone } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body get :variant_any_any, params: { v: :yolo } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "any", @response.body end def test_variant_inline_any get :variant_any, params: { v: :phone } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body get :variant_inline_any, params: { v: :tablet } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "any", @response.body get :variant_inline_any, params: { v: :phablet } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "any", @response.body end def test_variant_inline_any_any get :variant_inline_any_any, params: { v: :phone } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body get :variant_inline_any_any, params: { v: :yolo } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "any", @response.body end def test_variant_any_implicit_render get :variant_any_implicit_render, params: { v: :tablet } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "tablet", @response.body get :variant_any_implicit_render, params: { v: :phablet } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phablet", @response.body end def test_variant_any_with_none get :variant_any_with_none - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "none or phone", @response.body get :variant_any_with_none, params: { v: :phone } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "none or phone", @response.body end def test_format_any_variant_any get :format_any_variant_any, format: :js, params: { v: :tablet } - assert_equal "text/javascript", @response.content_type + assert_equal "text/javascript", @response.media_type assert_equal "tablet", @response.body end def test_variant_negotiation_inline_syntax get :variant_inline_syntax_without_block, params: { v: [:tablet, :phone] } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body end def test_variant_negotiation_block_syntax get :variant_plus_none_for_format, params: { v: [:tablet, :phone] } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body end def test_variant_negotiation_without_block get :variant_inline_syntax_without_block, params: { v: [:tablet, :phone] } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body end end diff --git a/actionpack/test/controller/render_js_test.rb b/actionpack/test/controller/render_js_test.rb index 1efc0b9de1..da8f6e8062 100644 --- a/actionpack/test/controller/render_js_test.rb +++ b/actionpack/test/controller/render_js_test.rb @@ -2,7 +2,6 @@ require "abstract_unit" require "controller/fake_models" -require "pathname" class RenderJSTest < ActionController::TestCase class TestController < ActionController::Base @@ -26,7 +25,7 @@ class RenderJSTest < ActionController::TestCase def test_render_vanilla_js get :render_vanilla_js_hello, xhr: true assert_equal "alert('hello')", @response.body - assert_equal "text/javascript", @response.content_type + assert_equal "text/javascript", @response.media_type end def test_should_render_js_partial diff --git a/actionpack/test/controller/render_json_test.rb b/actionpack/test/controller/render_json_test.rb index 82c1ba26cb..82c6aaafe5 100644 --- a/actionpack/test/controller/render_json_test.rb +++ b/actionpack/test/controller/render_json_test.rb @@ -3,7 +3,6 @@ require "abstract_unit" require "controller/fake_models" require "active_support/logger" -require "pathname" class RenderJsonTest < ActionController::TestCase class JsonRenderable @@ -80,7 +79,7 @@ class RenderJsonTest < ActionController::TestCase def test_render_json_nil get :render_json_nil assert_equal "null", @response.body - assert_equal "application/json", @response.content_type + assert_equal "application/json", @response.media_type end def test_render_json_render_to_string @@ -91,7 +90,7 @@ class RenderJsonTest < ActionController::TestCase def test_render_json get :render_json_hello_world assert_equal '{"hello":"world"}', @response.body - assert_equal "application/json", @response.content_type + assert_equal "application/json", @response.media_type end def test_render_json_with_status @@ -103,31 +102,31 @@ class RenderJsonTest < ActionController::TestCase def test_render_json_with_callback get :render_json_hello_world_with_callback, xhr: true assert_equal '/**/alert({"hello":"world"})', @response.body - assert_equal "text/javascript", @response.content_type + assert_equal "text/javascript", @response.media_type end def test_render_json_with_custom_content_type get :render_json_with_custom_content_type, xhr: true assert_equal '{"hello":"world"}', @response.body - assert_equal "text/javascript", @response.content_type + assert_equal "text/javascript", @response.media_type end def test_render_symbol_json get :render_symbol_json assert_equal '{"hello":"world"}', @response.body - assert_equal "application/json", @response.content_type + assert_equal "application/json", @response.media_type end def test_render_json_with_render_to_string get :render_json_with_render_to_string assert_equal '{"hello":"partial html"}', @response.body - assert_equal "application/json", @response.content_type + assert_equal "application/json", @response.media_type end def test_render_json_forwards_extra_options get :render_json_with_extra_options assert_equal '{"a":"b"}', @response.body - assert_equal "application/json", @response.content_type + assert_equal "application/json", @response.media_type end def test_render_json_calls_to_json_from_object diff --git a/actionpack/test/controller/render_xml_test.rb b/actionpack/test/controller/render_xml_test.rb index a72d14e4bb..28d8e281ab 100644 --- a/actionpack/test/controller/render_xml_test.rb +++ b/actionpack/test/controller/render_xml_test.rb @@ -2,7 +2,6 @@ require "abstract_unit" require "controller/fake_models" -require "pathname" class RenderXmlTest < ActionController::TestCase class XmlRenderable @@ -92,11 +91,11 @@ class RenderXmlTest < ActionController::TestCase def test_should_render_xml_but_keep_custom_content_type get :render_xml_with_custom_content_type - assert_equal "application/atomsvc+xml", @response.content_type + assert_equal "application/atomsvc+xml", @response.media_type end def test_should_use_implicit_content_type get :implicit_content_type, format: "atom" - assert_equal Mime[:atom], @response.content_type + assert_equal Mime[:atom], @response.media_type end end diff --git a/actionpack/test/controller/renderers_test.rb b/actionpack/test/controller/renderers_test.rb index d92de6f5d5..96cce664a4 100644 --- a/actionpack/test/controller/renderers_test.rb +++ b/actionpack/test/controller/renderers_test.rb @@ -73,7 +73,7 @@ class RenderersTest < ActionController::TestCase assert_raise ActionView::MissingTemplate do get :respond_to_mime, format: "csv" end - assert_equal Mime[:csv], @response.content_type + assert_equal Mime[:csv], @response.media_type assert_equal "", @response.body end @@ -83,7 +83,7 @@ class RenderersTest < ActionController::TestCase end @request.accept = "text/csv" get :respond_to_mime, format: "csv" - assert_equal Mime[:csv], @response.content_type + assert_equal Mime[:csv], @response.media_type assert_equal "c,s,v", @response.body ensure ActionController::Renderers.remove :csv diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb index 1d68a359dc..2cd2114db6 100644 --- a/actionpack/test/controller/show_exceptions_test.rb +++ b/actionpack/test/controller/show_exceptions_test.rb @@ -76,7 +76,7 @@ module ShowExceptions @app = ShowExceptionsOverriddenController.action(:boom) get "/", headers: { "HTTP_ACCEPT" => "application/json" } assert_response :internal_server_error - assert_equal "application/json", response.content_type.to_s + assert_equal "application/json", response.media_type assert_equal({ status: 500, error: "Internal Server Error" }.to_json, response.body) end @@ -84,7 +84,7 @@ module ShowExceptions @app = ShowExceptionsOverriddenController.action(:boom) get "/", headers: { "HTTP_ACCEPT" => "application/xml" } assert_response :internal_server_error - assert_equal "application/xml", response.content_type.to_s + assert_equal "application/xml", response.media_type assert_equal({ status: 500, error: "Internal Server Error" }.to_xml, response.body) end @@ -92,7 +92,7 @@ module ShowExceptions @app = ShowExceptionsOverriddenController.action(:boom) get "/", headers: { "HTTP_ACCEPT" => "text/csv" } assert_response :internal_server_error - assert_equal "text/html", response.content_type.to_s + assert_equal "text/html", response.media_type end end @@ -106,7 +106,7 @@ module ShowExceptions get "/", headers: { "HTTP_ACCEPT" => "text/json" } assert_response :internal_server_error - assert_equal "text/plain", response.content_type.to_s + assert_equal "text/plain", response.media_type ensure middleware.instance_variable_set(:@exceptions_app, @exceptions_app) $stderr = STDERR diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb index 3e57e8f4d9..68817ccdea 100644 --- a/actionpack/test/dispatch/debug_exceptions_test.rb +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -208,7 +208,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_response 500 assert_no_match(/
/, body) assert_no_match(//, body) - assert_equal "text/plain", response.content_type + assert_equal "text/plain", response.media_type assert_match(/RuntimeError\npuke/, body) Rails.stub :root, Pathname.new(".") do @@ -222,31 +222,31 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest get "/not_found", headers: xhr_request_env assert_response 404 assert_no_match(//, body) - assert_equal "text/plain", response.content_type + assert_equal "text/plain", response.media_type assert_match(/#{AbstractController::ActionNotFound.name}/, body) get "/method_not_allowed", headers: xhr_request_env assert_response 405 assert_no_match(//, body) - assert_equal "text/plain", response.content_type + assert_equal "text/plain", response.media_type assert_match(/ActionController::MethodNotAllowed/, body) get "/unknown_http_method", headers: xhr_request_env assert_response 405 assert_no_match(//, body) - assert_equal "text/plain", response.content_type + assert_equal "text/plain", response.media_type assert_match(/ActionController::UnknownHttpMethod/, body) get "/bad_request", headers: xhr_request_env assert_response 400 assert_no_match(//, body) - assert_equal "text/plain", response.content_type + assert_equal "text/plain", response.media_type assert_match(/ActionController::BadRequest/, body) get "/parameter_missing", headers: xhr_request_env assert_response 400 assert_no_match(//, body) - assert_equal "text/plain", response.content_type + assert_equal "text/plain", response.media_type assert_match(/ActionController::ParameterMissing/, body) end @@ -257,37 +257,37 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_response 500 assert_no_match(/
/, body) assert_no_match(//, body) - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type assert_match(/RuntimeError: puke/, body) get "/not_found", headers: { "action_dispatch.show_exceptions" => true }, as: :json assert_response 404 assert_no_match(//, body) - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type assert_match(/#{AbstractController::ActionNotFound.name}/, body) get "/method_not_allowed", headers: { "action_dispatch.show_exceptions" => true }, as: :json assert_response 405 assert_no_match(//, body) - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type assert_match(/ActionController::MethodNotAllowed/, body) get "/unknown_http_method", headers: { "action_dispatch.show_exceptions" => true }, as: :json assert_response 405 assert_no_match(//, body) - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type assert_match(/ActionController::UnknownHttpMethod/, body) get "/bad_request", headers: { "action_dispatch.show_exceptions" => true }, as: :json assert_response 400 assert_no_match(//, body) - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type assert_match(/ActionController::BadRequest/, body) get "/parameter_missing", headers: { "action_dispatch.show_exceptions" => true }, as: :json assert_response 400 assert_no_match(//, body) - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type assert_match(/ActionController::ParameterMissing/, body) end @@ -298,7 +298,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_response 500 assert_match(/
/, body) assert_match(//, body) - assert_equal "text/html", response.content_type + assert_equal "text/html", response.media_type assert_match(/puke/, body) end @@ -307,7 +307,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest get "/index.xml", headers: { "action_dispatch.show_exceptions" => true } assert_response 500 - assert_equal "application/xml", response.content_type + assert_equal "application/xml", response.media_type assert_match(/RuntimeError: puke/, body) end @@ -321,7 +321,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest get "/index", headers: { "action_dispatch.show_exceptions" => true }, as: :wibble assert_response 500 - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type assert_match(/RuntimeError: puke/, body) ensure diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 7758b0406a..33cf86a081 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -290,8 +290,8 @@ class ResponseTest < ActiveSupport::TestCase resp.to_a assert_equal("utf-16", resp.charset) - assert_equal(Mime[:xml], resp.content_type) - + assert_equal(Mime[:xml], resp.media_type) + assert_equal("application/xml; charset=utf-16", resp.content_type) assert_equal("application/xml; charset=utf-16", resp.headers["Content-Type"]) end @@ -503,8 +503,8 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest assert_response :success assert_equal("utf-16", @response.charset) - assert_equal(Mime[:xml], @response.content_type) - + assert_equal(Mime[:xml], @response.media_type) + assert_equal("application/xml; charset=utf-16", @response.content_type) assert_equal("application/xml; charset=utf-16", @response.headers["Content-Type"]) end @@ -519,8 +519,8 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest assert_response :success assert_equal("utf-16", @response.charset) - assert_equal(Mime[:xml], @response.content_type) - + assert_equal(Mime[:xml], @response.media_type) + assert_equal("application/xml; charset=utf-16", @response.content_type) assert_equal("application/xml; charset=utf-16", @response.headers["Content-Type"]) end @@ -553,7 +553,26 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest assert_response :success assert_equal("text/csv; charset=utf-16; header=present", @response.headers["Content-Type"]) - assert_equal("text/csv", @response.content_type) + assert_equal("text/csv; charset=utf-16; header=present", @response.content_type) + assert_equal("text/csv", @response.media_type) + assert_equal("utf-16", @response.charset) + end + + test "response Content-Type with optional parameters that set before charset" do + @app = lambda { |env| + [ + 200, + { "Content-Type" => "text/csv; header=present; charset=utf-16" }, + ["Hello"] + ] + } + + get "/" + assert_response :success + + assert_equal("text/csv; header=present; charset=utf-16", @response.headers["Content-Type"]) + assert_equal("text/csv; header=present; charset=utf-16", @response.content_type) + assert_equal("text/csv", @response.media_type) assert_equal("utf-16", @response.charset) end @@ -570,7 +589,8 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest assert_response :success assert_equal('text/csv; header=present; charset="utf-16"', @response.headers["Content-Type"]) - assert_equal("text/csv", @response.content_type) + assert_equal('text/csv; header=present; charset="utf-16"', @response.content_type) + assert_equal("text/csv", @response.media_type) assert_equal("utf-16", @response.charset) end end diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index dcd3e33c46..a1e94384bd 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,6 @@ +* Fix `select_tag` so that it doesn't change `options` when `include_blank` is present. + + *Younes SERRAJ* Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/actionview/CHANGELOG.md) for previous changes. diff --git a/actionview/lib/action_view/cache_expiry.rb b/actionview/lib/action_view/cache_expiry.rb index 820afc093d..3d8ffeaefb 100644 --- a/actionview/lib/action_view/cache_expiry.rb +++ b/actionview/lib/action_view/cache_expiry.rb @@ -16,18 +16,21 @@ module ActionView @watched_dirs = nil @watcher_class = watcher @watcher = nil + @mutex = Mutex.new end def clear_cache_if_necessary - watched_dirs = dirs_to_watch - if watched_dirs != @watched_dirs - @watched_dirs = watched_dirs - @watcher = @watcher_class.new([], watched_dirs) do - clear_cache + @mutex.synchronize do + watched_dirs = dirs_to_watch + if watched_dirs != @watched_dirs + @watched_dirs = watched_dirs + @watcher = @watcher_class.new([], watched_dirs) do + clear_cache + end + @watcher.execute + else + @watcher.execute_if_updated end - @watcher.execute - else - @watcher.execute_if_updated end end diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb index 5d4ff36425..c93ead9653 100644 --- a/actionview/lib/action_view/helpers/form_tag_helper.rb +++ b/actionview/lib/action_view/helpers/form_tag_helper.rb @@ -137,7 +137,8 @@ module ActionView html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name if options.include?(:include_blank) - include_blank = options.delete(:include_blank) + include_blank = options[:include_blank] + options = options.except(:include_blank) options_for_blank_options_tag = { value: "" } if include_blank == true diff --git a/actionview/test/actionpack/controller/render_test.rb b/actionview/test/actionpack/controller/render_test.rb index c8ce7366d1..9b1a720636 100644 --- a/actionview/test/actionpack/controller/render_test.rb +++ b/actionview/test/actionpack/controller/render_test.rb @@ -1003,14 +1003,14 @@ class RenderTest < ActionController::TestCase def test_render_xml get :render_xml_hello assert_equal "\n

Hello David

\n

This is grand!

\n\n", @response.body - assert_equal "application/xml", @response.content_type + assert_equal "application/xml", @response.media_type end # :ported: def test_render_xml_as_string_template get :render_xml_hello_as_string_template assert_equal "\n

Hello David

\n

This is grand!

\n\n", @response.body - assert_equal "application/xml", @response.content_type + assert_equal "application/xml", @response.media_type end # :ported: @@ -1039,7 +1039,7 @@ class RenderTest < ActionController::TestCase def test_rendered_format_without_format get :inline_rendered_format_without_format assert_equal "test", @response.body - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type end def test_partials_list @@ -1077,7 +1077,7 @@ class RenderTest < ActionController::TestCase def test_accessing_local_assigns_in_inline_template get :accessing_local_assigns_in_inline_template, params: { local_name: "Local David" } assert_equal "Goodbye, Local David", @response.body - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type end def test_should_implicitly_render_html_template_from_xhr_request @@ -1264,13 +1264,13 @@ class RenderTest < ActionController::TestCase def test_partial_only get :partial_only assert_equal "only partial", @response.body - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type end def test_should_render_html_formatted_partial get :partial assert_equal "partial html", @response.body - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type end def test_render_html_formatted_partial_even_with_other_mime_time_in_accept @@ -1279,20 +1279,20 @@ class RenderTest < ActionController::TestCase get :partial_html_erb assert_equal "partial.html.erb", @response.body.strip - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type end def test_should_render_html_partial_with_formats get :partial_formats_html assert_equal "partial html", @response.body - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type end def test_render_to_string_partial get :render_to_string_with_partial assert_equal "only partial", @controller.instance_variable_get(:@partial_only) assert_equal "Hello: david", @controller.instance_variable_get(:@partial_with_locals) - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type end def test_render_to_string_with_template_and_html_partial @@ -1300,21 +1300,21 @@ class RenderTest < ActionController::TestCase assert_equal "**only partial**\n", @controller.instance_variable_get(:@text) assert_equal "only partial\n", @controller.instance_variable_get(:@html) assert_equal "only html partial\n", @response.body - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type end def test_render_to_string_and_render_with_different_formats get :render_to_string_and_render_with_different_formats assert_equal "only partial\n", @controller.instance_variable_get(:@html) assert_equal "**only partial**\n", @response.body - assert_equal "text/plain", @response.content_type + assert_equal "text/plain", @response.media_type end def test_render_template_within_a_template_with_other_format get :render_template_within_a_template_with_other_format expected = "only html partial

This is grand!

" assert_equal expected, @response.body.strip - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type end def test_partial_with_counter diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb index 9ece9f3ad1..70c5ae6771 100644 --- a/actionview/test/template/form_tag_helper_test.rb +++ b/actionview/test/template/form_tag_helper_test.rb @@ -301,6 +301,13 @@ class FormTagHelperTest < ActionView::TestCase assert_dom_equal expected, actual end + def test_select_tag_with_include_blank_doesnt_change_options + options = { include_blank: true, prompt: "string" } + expected_options = options.dup + select_tag "places", raw(""), options + expected_options.each { |k, v| assert_equal v, options[k] } + end + def test_select_tag_with_include_blank_false actual = select_tag "places", raw(""), include_blank: false expected = %() diff --git a/activemodel/lib/active_model/type/helpers/time_value.rb b/activemodel/lib/active_model/type/helpers/time_value.rb index 735b9a75a6..dab196d653 100644 --- a/activemodel/lib/active_model/type/helpers/time_value.rb +++ b/activemodel/lib/active_model/type/helpers/time_value.rb @@ -22,10 +22,17 @@ module ActiveModel end def apply_seconds_precision(value) - return value unless precision && value.respond_to?(:usec) - number_of_insignificant_digits = 6 - precision + return value unless precision && value.respond_to?(:nsec) + + number_of_insignificant_digits = 9 - precision round_power = 10**number_of_insignificant_digits - value.change(usec: value.usec - value.usec % round_power) + rounded_off_nsec = value.nsec % round_power + + if rounded_off_nsec > 0 + value.change(nsec: value.nsec - rounded_off_nsec) + else + value + end end def type_cast_for_schema(value) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 8c5342edb2..6f08b1b8fe 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,13 @@ +* Fix invalid schema when primary key column has a comment + + Fixes #29966 + + *Guilherme Goettems Schneider* + +* Fix table comment also being applied to the primary key column + + *Guilherme Goettems Schneider* + * Allow generated `create_table` migrations to include or skip timestamps. *Michael Duchemin* diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 220043c061..fd32eaaf3a 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -185,12 +185,14 @@ module ActiveRecord /ix def disallow_raw_sql!(args, permit: COLUMN_NAME) # :nodoc: - unexpected = args.reject do |arg| - Arel.arel_node?(arg) || + unexpected = nil + args.each do |arg| + next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || arg.to_s.split(/\s*,\s*/).all? { |part| permit.match?(part) } + (unexpected ||= []) << arg end - return if unexpected.none? + return unless unexpected if allow_unsafe_raw_sql == :deprecated ActiveSupport::Deprecation.warn( @@ -437,7 +439,7 @@ module ActiveRecord def attributes_for_update(attribute_names) attribute_names &= self.class.column_names attribute_names.delete_if do |name| - readonly_attribute?(name) + self.class.readonly_attribute?(name) end end @@ -460,10 +462,6 @@ module ActiveRecord end end - def readonly_attribute?(name) - self.class.readonly_attributes.include?(name) - end - def pk_attribute?(name) name == @primary_key end 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 4ff3cb0071..7628ef5537 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -3,6 +3,7 @@ require "thread" require "concurrent/map" require "monitor" +require "weakref" module ActiveRecord # Raised when a connection could not be obtained within the connection @@ -294,28 +295,37 @@ module ActiveRecord @frequency = frequency end - @@mutex = Mutex.new - @@pools = {} + @mutex = Mutex.new + @pools = {} - def self.register_pool(pool, frequency) # :nodoc: - @@mutex.synchronize do - if @@pools.key?(frequency) - @@pools[frequency] << pool - else - @@pools[frequency] = [pool] + class << self + def register_pool(pool, frequency) # :nodoc: + @mutex.synchronize do + unless @pools.key?(frequency) + @pools[frequency] = [] + spawn_thread(frequency) + end + @pools[frequency] << WeakRef.new(pool) + end + end + + private + + def spawn_thread(frequency) Thread.new(frequency) do |t| loop do sleep t - @@mutex.synchronize do - @@pools[frequency].each do |p| + @mutex.synchronize do + @pools[frequency].select!(&:weakref_alive?) + @pools[frequency].each do |p| p.reap p.flush + rescue WeakRef::RefError end end end end end - end end def run 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 688eea75e8..dbd533b4b3 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -264,8 +264,7 @@ module ActiveRecord if_not_exists: false, options: nil, as: nil, - comment: nil, - ** + comment: nil ) @conn = conn @columns_hash = {} diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb index 622e00fffb..fb56e712be 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb @@ -15,7 +15,7 @@ module ActiveRecord def column_spec_for_primary_key(column) return {} if default_primary_key?(column) spec = { id: schema_type(column).inspect } - spec.merge!(prepare_column_options(column).except!(:null)) + spec.merge!(prepare_column_options(column).except!(:null, :comment)) spec[:default] ||= "nil" if explicit_primary_key_default?(column) spec end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index f97842b3f5..cf57af5473 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -291,25 +291,25 @@ module ActiveRecord # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id # # See also TableDefinition#column for details on how to create columns. - def create_table(table_name, **options) - td = create_table_definition(table_name, options) + def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options) + td = create_table_definition( + table_name, options.extract!(:temporary, :if_not_exists, :options, :as, :comment) + ) - if options[:id] != false && !options[:as] - pk = options.fetch(:primary_key) do - Base.get_primary_key table_name.to_s.singularize - end + if id && !td.as + pk = primary_key || Base.get_primary_key(table_name.to_s.singularize) if pk.is_a?(Array) td.primary_keys pk else - td.primary_key pk, options.fetch(:id, :primary_key), options + td.primary_key pk, id, options end end yield td if block_given? - if options[:force] - drop_table(table_name, options.merge(if_exists: true)) + if force + drop_table(table_name, force: force, if_exists: true) end result = execute schema_creation.accept td @@ -321,7 +321,7 @@ module ActiveRecord end if supports_comments? && !supports_comments_in_create? - if table_comment = options[:comment].presence + if table_comment = td.comment.presence change_table_comment(table_name, table_comment) end @@ -518,14 +518,15 @@ module ActiveRecord # Available options are (none of these exists by default): # * :limit - # Requests a maximum column length. This is the number of characters for a :string column - # and number of bytes for :text, :binary and :integer columns. + # and number of bytes for :text, :binary, and :integer columns. # This option is ignored by some backends. # * :default - # The column's default value. Use +nil+ for +NULL+. # * :null - # Allows or disallows +NULL+ values in the column. # * :precision - - # Specifies the precision for the :decimal and :numeric columns. + # Specifies the precision for the :decimal, :numeric, + # :datetime, and :time columns. # * :scale - # Specifies the scale for the :decimal and :numeric columns. # * :collation - diff --git a/activerecord/lib/active_record/integration.rb b/activerecord/lib/active_record/integration.rb index 573a823dbc..4a97061731 100644 --- a/activerecord/lib/active_record/integration.rb +++ b/activerecord/lib/active_record/integration.rb @@ -93,7 +93,7 @@ module ActiveRecord # cache_version, but this method can be overwritten to return something else. # # Note, this method will return nil if ActiveRecord::Base.cache_versioning is set to - # +false+ (which it is by default until Rails 6.0). + # +false+. def cache_version return unless cache_versioning diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index a58c5fd48a..b7bc4b3b8e 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -939,7 +939,7 @@ module ActiveRecord end def verify_readonly_attribute(name) - raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name) + raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name) end def _raise_record_not_destroyed diff --git a/activerecord/lib/active_record/readonly_attributes.rb b/activerecord/lib/active_record/readonly_attributes.rb index 7bc26993d5..c851ed52c3 100644 --- a/activerecord/lib/active_record/readonly_attributes.rb +++ b/activerecord/lib/active_record/readonly_attributes.rb @@ -19,6 +19,10 @@ module ActiveRecord def readonly_attributes _attr_readonly end + + def readonly_attribute?(name) # :nodoc: + _attr_readonly.include?(name) + end end end end diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 04a1c03474..a5862ae06b 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -59,19 +59,26 @@ module ActiveRecord attribute_names.index_with(time || current_time_from_proper_timezone) end + def timestamp_attributes_for_create_in_model + @timestamp_attributes_for_create_in_model ||= + (timestamp_attributes_for_create & column_names).freeze + end + + def timestamp_attributes_for_update_in_model + @timestamp_attributes_for_update_in_model ||= + (timestamp_attributes_for_update & column_names).freeze + end + + def all_timestamp_attributes_in_model + @all_timestamp_attributes_in_model ||= + (timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model).freeze + end + + def current_time_from_proper_timezone + default_timezone == :utc ? Time.now.utc : Time.now + end + private - def timestamp_attributes_for_create_in_model - timestamp_attributes_for_create.select { |c| column_names.include?(c) } - end - - def timestamp_attributes_for_update_in_model - timestamp_attributes_for_update.select { |c| column_names.include?(c) } - end - - def all_timestamp_attributes_in_model - timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model - end - def timestamp_attributes_for_create ["created_at", "created_on"] end @@ -80,8 +87,11 @@ module ActiveRecord ["updated_at", "updated_on"] end - def current_time_from_proper_timezone - default_timezone == :utc ? Time.now.utc : Time.now + def reload_schema_from_cache + @timestamp_attributes_for_create_in_model = nil + @timestamp_attributes_for_update_in_model = nil + @all_timestamp_attributes_in_model = nil + super end end @@ -124,19 +134,19 @@ module ActiveRecord end def timestamp_attributes_for_create_in_model - self.class.send(:timestamp_attributes_for_create_in_model) + self.class.timestamp_attributes_for_create_in_model end def timestamp_attributes_for_update_in_model - self.class.send(:timestamp_attributes_for_update_in_model) + self.class.timestamp_attributes_for_update_in_model end def all_timestamp_attributes_in_model - self.class.send(:all_timestamp_attributes_in_model) + self.class.all_timestamp_attributes_in_model end def current_time_from_proper_timezone - self.class.send(:current_time_from_proper_timezone) + self.class.current_time_from_proper_timezone end def max_updated_column_timestamp diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index 49f754be63..cbe48a374f 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -37,8 +37,8 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase def test_eager_association_loading_with_hmt_does_not_table_name_collide_when_joining_associations authors = Author.joins(:posts).eager_load(:comments).where(posts: { tags_count: 1 }).order(:id).to_a - assert_equal 3, assert_no_queries { authors.size } - assert_equal 10, assert_no_queries { authors[0].comments.size } + assert_equal 3, assert_queries(0) { authors.size } + assert_equal 10, assert_queries(0) { authors[0].comments.size } end def test_eager_association_loading_grafts_stashed_associations_to_correct_parent @@ -103,14 +103,14 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase firms = Firm.all.merge!(includes: { account: { firm: :account } }, order: "companies.id").to_a assert_equal 2, firms.size assert_equal firms.first.account, firms.first.account.firm.account - assert_equal companies(:first_firm).account, assert_no_queries { firms.first.account.firm.account } - assert_equal companies(:first_firm).account.firm.account, assert_no_queries { firms.first.account.firm.account } + assert_equal companies(:first_firm).account, assert_queries(0) { firms.first.account.firm.account } + assert_equal companies(:first_firm).account.firm.account, assert_queries(0) { firms.first.account.firm.account } end def test_eager_association_loading_with_has_many_sti topics = Topic.all.merge!(includes: :replies, order: "topics.id").to_a first, second, = topics(:first).replies.size, topics(:second).replies.size - assert_no_queries do + assert_queries(0) do assert_equal first, topics[0].replies.size assert_equal second, topics[1].replies.size end @@ -131,13 +131,13 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase replies = Reply.all.merge!(includes: :topic, order: "topics.id").to_a assert_includes replies, topics(:second) assert_not_includes replies, topics(:first) - assert_equal topics(:first), assert_no_queries { replies.first.topic } + assert_equal topics(:first), assert_queries(0) { replies.first.topic } end def test_eager_association_loading_with_multiple_stis_and_order author = Author.all.merge!(includes: { posts: [ :special_comments, :very_special_comment ] }, order: ["authors.name", "comments.body", "very_special_comments_posts.body"], where: "posts.id = 4").first assert_equal authors(:david), author - assert_no_queries do + assert_queries(0) do author.posts.first.special_comments author.posts.first.very_special_comment end @@ -146,7 +146,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase def test_eager_association_loading_of_stis_with_multiple_references authors = Author.all.merge!(includes: { posts: { special_comments: { post: [ :special_comments, :very_special_comment ] } } }, order: "comments.body, very_special_comments_posts.body", where: "posts.id = 4").to_a assert_equal [authors(:david)], authors - assert_no_queries do + assert_queries(0) do authors.first.posts.first.special_comments.first.post.special_comments authors.first.posts.first.special_comments.first.post.very_special_comment end @@ -155,14 +155,14 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase def test_eager_association_loading_where_first_level_returns_nil authors = Author.all.merge!(includes: { post_about_thinking: :comments }, order: "authors.id DESC").to_a assert_equal [authors(:bob), authors(:mary), authors(:david)], authors - assert_no_queries do + assert_queries(0) do authors[2].post_about_thinking.comments.first end end def test_preload_through_missing_records post = Post.where.not(author_id: Author.select(:id)).preload(author: { comments: :post }).first! - assert_no_queries { assert_nil post.author } + assert_queries(0) { assert_nil post.author } end def test_eager_association_loading_with_missing_first_record @@ -172,12 +172,12 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase def test_eager_association_loading_with_recursive_cascading_four_levels_has_many_through source = Vertex.all.merge!(includes: { sinks: { sinks: { sinks: :sinks } } }, order: "vertices.id").first - assert_equal vertices(:vertex_4), assert_no_queries { source.sinks.first.sinks.first.sinks.first } + assert_equal vertices(:vertex_4), assert_queries(0) { source.sinks.first.sinks.first.sinks.first } end def test_eager_association_loading_with_recursive_cascading_four_levels_has_and_belongs_to_many sink = Vertex.all.merge!(includes: { sources: { sources: { sources: :sources } } }, order: "vertices.id DESC").first - assert_equal vertices(:vertex_1), assert_no_queries { sink.sources.first.sources.first.sources.first.sources.first } + assert_equal vertices(:vertex_1), assert_queries(0) { sink.sources.first.sources.first.sources.first.sources.first } end def test_eager_association_loading_with_cascaded_interdependent_one_level_and_two_levels diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 9ed25ca7c2..f7aad9d775 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -1245,7 +1245,7 @@ class EagerAssociationTest < ActiveRecord::TestCase Post.all.merge!(select: "posts.*, authors.name as author_name", includes: :comments, joins: :author, order: "posts.id").to_a end assert_equal "David", posts[0].author_name - assert_equal posts(:welcome).comments, assert_no_queries { posts[0].comments } + assert_equal posts(:welcome).comments.sort_by(&:id), assert_no_queries { posts[0].comments.sort_by(&:id) } end def test_eager_loading_with_conditions_on_join_model_preloads @@ -1257,8 +1257,8 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_preload_belongs_to_uses_exclusive_scope - people = Person.males.merge(includes: :primary_contact).to_a - assert_not_equal people.length, 0 + people = Person.males.includes(:primary_contact).to_a + assert_equal 2, people.length people.each do |person| assert_no_queries { assert_not_nil person.primary_contact } assert_equal Person.find(person.id).primary_contact, person.primary_contact @@ -1267,16 +1267,17 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_preload_has_many_uses_exclusive_scope people = Person.males.includes(:agents).to_a + assert_equal 2, people.length people.each do |person| - assert_equal Person.find(person.id).agents, person.agents + assert_equal Person.find(person.id).agents.sort_by(&:id), person.agents.sort_by(&:id) end end def test_preload_has_many_using_primary_key - expected = Firm.first.clients_using_primary_key.to_a + expected = Firm.first.clients_using_primary_key.sort_by(&:id) firm = Firm.includes(:clients_using_primary_key).first assert_no_queries do - assert_equal expected, firm.clients_using_primary_key + assert_equal expected, firm.clients_using_primary_key.sort_by(&:id) end end diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index 0e9dafeee6..25cfa0a723 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -550,7 +550,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase developer = project.developers.first - assert_no_queries do + assert_queries(0) do assert_predicate project.developers, :loaded? assert_includes project.developers, developer end @@ -745,7 +745,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase def test_get_ids_for_loaded_associations developer = developers(:david) developer.projects.reload - assert_no_queries do + assert_queries(0) do developer.project_ids developer.project_ids end @@ -873,7 +873,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase def test_has_and_belongs_to_many_associations_on_new_records_use_null_relations projects = Developer.new.projects - assert_no_queries do + assert_queries(0) do assert_equal [], projects assert_equal [], projects.where(title: "omg") assert_equal [], projects.pluck(:title) diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 0d13174e12..e42af3686e 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -837,7 +837,7 @@ class CalculationsTest < ActiveRecord::TestCase def test_pluck_columns_with_same_name expected = [["The First Topic", "The Second Topic of the day"], ["The Third Topic of the day", "The Fourth Topic of the day"]] - actual = Topic.joins(:replies) + actual = Topic.joins(:replies).order(:id) .pluck("topics.title", "replies_topics.title") assert_equal expected, actual end diff --git a/activerecord/test/cases/comment_test.rb b/activerecord/test/cases/comment_test.rb index 584e03d196..25e2f20676 100644 --- a/activerecord/test/cases/comment_test.rb +++ b/activerecord/test/cases/comment_test.rb @@ -14,6 +14,9 @@ if ActiveRecord::Base.connection.supports_comments? class BlankComment < ActiveRecord::Base end + class PkCommented < ActiveRecord::Base + end + setup do @connection = ActiveRecord::Base.connection @@ -35,8 +38,13 @@ if ActiveRecord::Base.connection.supports_comments? t.index :absent_comment end + @connection.create_table("pk_commenteds", comment: "Table comment", id: false, force: true) do |t| + t.integer :id, comment: "Primary key comment", primary_key: true + end + Commented.reset_column_information BlankComment.reset_column_information + PkCommented.reset_column_information end teardown do @@ -44,6 +52,11 @@ if ActiveRecord::Base.connection.supports_comments? @connection.drop_table "blank_comments", if_exists: true end + def test_default_primary_key_comment + column = Commented.columns_hash["id"] + assert_nil column.comment + end + def test_column_created_in_block column = Commented.columns_hash["name"] assert_equal :string, column.type @@ -164,5 +177,17 @@ if ActiveRecord::Base.connection.supports_comments? column = Commented.columns_hash["name"] assert_nil column.comment end + + def test_comment_on_primary_key + column = PkCommented.columns_hash["id"] + assert_equal "Primary key comment", column.comment + assert_equal "Table comment", @connection.table_comment("pk_commenteds") + end + + def test_schema_dump_with_primary_key_comment + output = dump_table_schema "pk_commenteds" + assert_match %r[create_table "pk_commenteds",.*\s+comment: "Table comment"], output + assert_no_match %r[create_table "pk_commenteds",.*\s+comment: "Primary key comment"], output + end end end diff --git a/activerecord/test/models/face.rb b/activerecord/test/models/face.rb index e900fd40fb..45ccc442ba 100644 --- a/activerecord/test/models/face.rb +++ b/activerecord/test/models/face.rb @@ -6,7 +6,7 @@ class Face < ActiveRecord::Base belongs_to :polymorphic_man, polymorphic: true, inverse_of: :polymorphic_face # Oracle identifier length is limited to 30 bytes or less, `polymorphic` renamed `poly` belongs_to :poly_man_without_inverse, polymorphic: true - # These is a "broken" inverse_of for the purposes of testing + # These are "broken" inverse_of associations for the purposes of testing belongs_to :horrible_man, class_name: "Man", inverse_of: :horrible_face belongs_to :horrible_polymorphic_man, polymorphic: true, inverse_of: :horrible_polymorphic_face diff --git a/activestorage/CHANGELOG.md b/activestorage/CHANGELOG.md index 351d8687a4..2d9fe56858 100644 --- a/activestorage/CHANGELOG.md +++ b/activestorage/CHANGELOG.md @@ -1,6 +1,15 @@ +* Image analysis is skipped if ImageMagick returns an error. + + `ActiveStorage::Analyzer::ImageAnalyzer#metadata` would previously raise a + `MiniMagick::Error`, which caused persistent `ActiveStorage::AnalyzeJob` + failures. It now logs the error and returns `{}`, resulting in no metadata + being added to the offending image blob. + + *George Claghorn* + * Method calls on singular attachments return `nil` when no file is attached. - Previously, assuming the following User model, `user.avatar.filename` would + Previously, assuming the following User model, `user.avatar.filename` would raise a `Module::DelegationError` if no avatar was attached: ```ruby @@ -8,7 +17,7 @@ has_one_attached :avatar end ``` - + They now return `nil`. *Matthew Tanous* diff --git a/activestorage/lib/active_storage/analyzer/image_analyzer.rb b/activestorage/lib/active_storage/analyzer/image_analyzer.rb index c8bc8fe953..bd1bef3076 100644 --- a/activestorage/lib/active_storage/analyzer/image_analyzer.rb +++ b/activestorage/lib/active_storage/analyzer/image_analyzer.rb @@ -43,6 +43,9 @@ module ActiveStorage rescue LoadError logger.info "Skipping image analysis because the mini_magick gem isn't installed" {} + rescue MiniMagick::Error => error + logger.error "Skipping image analysis due to an ImageMagick error: #{error.message}" + {} end def rotated_image?(image) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 3d1dd41085..955eb7eef9 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -25,4 +25,5 @@ *Jordan Thomas* + Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/activesupport/CHANGELOG.md) for previous changes. diff --git a/activesupport/lib/active_support/core_ext/hash/deep_transform_values.rb b/activesupport/lib/active_support/core_ext/hash/deep_transform_values.rb index 720a1f67c8..18acb1e70c 100644 --- a/activesupport/lib/active_support/core_ext/hash/deep_transform_values.rb +++ b/activesupport/lib/active_support/core_ext/hash/deep_transform_values.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true class Hash - # Returns a new hash with all keys converted by the block operation. - # This includes the keys from the root hash and from all + # Returns a new hash with all values converted by the block operation. + # This includes the values from the root hash and from all # nested hashes and arrays. # # hash = { person: { name: 'Rob', age: '28' } } diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb index 2f88010d27..b8996ecb10 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -297,7 +297,7 @@ class Module rescue NoMethodError if #{target}.nil? if #{allow_nil == true} - return nil + nil else raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil" end diff --git a/activesupport/lib/active_support/evented_file_update_checker.rb b/activesupport/lib/active_support/evented_file_update_checker.rb index 3893b0de0e..84caa00b58 100644 --- a/activesupport/lib/active_support/evented_file_update_checker.rb +++ b/activesupport/lib/active_support/evented_file_update_checker.rb @@ -107,6 +107,7 @@ module ActiveSupport private def boot! + normalize_dirs! Listen.to(*@dtw, &method(:changed)).start end @@ -114,6 +115,12 @@ module ActiveSupport Listen.stop end + def normalize_dirs! + @dirs.transform_keys! do |dir| + dir.exist? ? dir.realpath : dir + end + end + def changed(modified, added, removed) unless updated? @updated.make_true if (modified + added + removed).any? { |f| watching?(f) } @@ -187,13 +194,6 @@ module ActiveSupport lcsp end - # Returns the deepest existing ascendant, which could be the argument itself. - def existing_parent(dir) - dir.ascend do |ascendant| - break ascendant if ascendant.directory? - end - end - # Filters out directories which are descendants of others in the collection (stable). def filter_out_descendants(dirs) return dirs if dirs.length < 2 diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb index 555c0ad1d3..a7a6112b0f 100644 --- a/activesupport/lib/active_support/notifications.rb +++ b/activesupport/lib/active_support/notifications.rb @@ -231,18 +231,16 @@ module ActiveSupport # ActiveSupport::Notifications.subscribe(/render/) do |event| # @event = event # end - def subscribe(*args, &block) - pattern, callback = *args - notifier.subscribe(pattern, callback, false, &block) + def subscribe(pattern = nil, callback = nil, &block) + notifier.subscribe(pattern, callback, monotonic: false, &block) end - def monotonic_subscribe(*args, &block) - pattern, callback = *args - notifier.subscribe(pattern, callback, true, &block) + def monotonic_subscribe(pattern = nil, callback = nil, &block) + notifier.subscribe(pattern, callback, monotonic: true, &block) end - def subscribed(callback, pattern, monotonic: false, &block) - subscriber = notifier.subscribe(pattern, callback, monotonic) + def subscribed(callback, pattern = nil, monotonic: false, &block) + subscriber = notifier.subscribe(pattern, callback, monotonic: monotonic) yield ensure unsubscribe(subscriber) diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index c37bec4ee5..aa602329ec 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -20,8 +20,8 @@ module ActiveSupport super end - def subscribe(pattern = nil, callable = nil, monotonic = false, &block) - subscriber = Subscribers.new(monotonic, pattern, callable || block) + def subscribe(pattern = nil, callable = nil, monotonic: false, &block) + subscriber = Subscribers.new(pattern, callable || block, monotonic) synchronize do if String === pattern @string_subscribers[pattern] << subscriber @@ -84,7 +84,7 @@ module ActiveSupport end module Subscribers # :nodoc: - def self.new(monotonic, pattern, listener) + def self.new(pattern, listener, monotonic) subscriber_class = monotonic ? MonotonicTimed : Timed if listener.respond_to?(:start) && listener.respond_to?(:finish) diff --git a/activesupport/lib/active_support/testing/parallelization.rb b/activesupport/lib/active_support/testing/parallelization.rb index e760bf5ce3..f50a5e0554 100644 --- a/activesupport/lib/active_support/testing/parallelization.rb +++ b/activesupport/lib/active_support/testing/parallelization.rb @@ -27,6 +27,10 @@ module ActiveSupport @queue << o end + def length + @queue.length + end + def pop; @queue.pop; end end @@ -109,6 +113,10 @@ module ActiveSupport def shutdown @queue_size.times { @queue << nil } @pool.each { |pid| Process.waitpid pid } + + if @queue.length > 0 + raise "Queue not empty, but all workers have finished. This probably means that a worker crashed and #{@queue.length} tests were missed." + end end private diff --git a/activesupport/test/core_ext/object/duplicable_test.rb b/activesupport/test/core_ext/object/duplicable_test.rb index 5203434ae6..c9af2cb624 100644 --- a/activesupport/test/core_ext/object/duplicable_test.rb +++ b/activesupport/test/core_ext/object/duplicable_test.rb @@ -6,13 +6,8 @@ require "active_support/core_ext/object/duplicable" require "active_support/core_ext/numeric/time" class DuplicableTest < ActiveSupport::TestCase - if RUBY_VERSION >= "2.5.0" - RAISE_DUP = [method(:puts)] - ALLOW_DUP = ["1", "symbol_from_string".to_sym, Object.new, /foo/, [], {}, Time.now, Class.new, Module.new, BigDecimal("4.56"), nil, false, true, 1, 2.3, Complex(1), Rational(1)] - else - RAISE_DUP = [method(:puts), Complex(1), Rational(1)] - ALLOW_DUP = ["1", "symbol_from_string".to_sym, Object.new, /foo/, [], {}, Time.now, Class.new, Module.new, BigDecimal("4.56"), nil, false, true, 1, 2.3] - end + RAISE_DUP = [method(:puts)] + ALLOW_DUP = ["1", "symbol_from_string".to_sym, Object.new, /foo/, [], {}, Time.now, Class.new, Module.new, BigDecimal("4.56"), nil, false, true, 1, 2.3, Complex(1), Rational(1)] def test_duplicable rubinius_skip "* Method#dup is allowed at the moment on Rubinius\n" \ diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index d4e709137e..003a0dbccb 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -481,17 +481,14 @@ class DependenciesTest < ActiveSupport::TestCase end end - # This raises only on 2.5.. (warns on ..2.4) - if RUBY_VERSION > "2.5" - def test_access_thru_and_upwards_fails - with_autoloading_fixtures do - assert_not defined?(ModuleFolder) - assert_raise(NameError) { ModuleFolder::Object } - assert_raise(NameError) { ModuleFolder::NestedClass::Object } - end - ensure - remove_constants(:ModuleFolder) + def test_access_thru_and_upwards_fails + with_autoloading_fixtures do + assert_not defined?(ModuleFolder) + assert_raise(NameError) { ModuleFolder::Object } + assert_raise(NameError) { ModuleFolder::NestedClass::Object } end + ensure + remove_constants(:ModuleFolder) end def test_non_existing_const_raises_name_error_with_fully_qualified_name diff --git a/activesupport/test/evented_file_update_checker_test.rb b/activesupport/test/evented_file_update_checker_test.rb index b2d5eb94c2..4d5a9bed7a 100644 --- a/activesupport/test/evented_file_update_checker_test.rb +++ b/activesupport/test/evented_file_update_checker_test.rb @@ -77,32 +77,48 @@ class EventedFileUpdateCheckerTest < ActiveSupport::TestCase Process.wait(pid) end + test "should detect changes through symlink" do + actual_dir = File.join(tmpdir, "actual") + linked_dir = File.join(tmpdir, "linked") + + Dir.mkdir(actual_dir) + FileUtils.ln_s(actual_dir, linked_dir) + + checker = new_checker([], linked_dir => ".rb") { } + + assert_not_predicate checker, :updated? + + FileUtils.touch(File.join(actual_dir, "a.rb")) + wait + + assert_predicate checker, :updated? + assert checker.execute_if_updated + end + test "updated should become true when nonexistent directory is added later" do - Dir.mktmpdir do |dir| - watched_dir = File.join(dir, "app") - unwatched_dir = File.join(dir, "node_modules") - not_exist_watched_dir = File.join(dir, "test") + watched_dir = File.join(tmpdir, "app") + unwatched_dir = File.join(tmpdir, "node_modules") + not_exist_watched_dir = File.join(tmpdir, "test") - Dir.mkdir(watched_dir) - Dir.mkdir(unwatched_dir) + Dir.mkdir(watched_dir) + Dir.mkdir(unwatched_dir) - checker = new_checker([], watched_dir => ".rb", not_exist_watched_dir => ".rb") { } + checker = new_checker([], watched_dir => ".rb", not_exist_watched_dir => ".rb") { } - FileUtils.touch(File.join(watched_dir, "a.rb")) - wait - assert_predicate checker, :updated? - assert checker.execute_if_updated + FileUtils.touch(File.join(watched_dir, "a.rb")) + wait + assert_predicate checker, :updated? + assert checker.execute_if_updated - Dir.mkdir(not_exist_watched_dir) - wait - assert_predicate checker, :updated? - assert checker.execute_if_updated + Dir.mkdir(not_exist_watched_dir) + wait + assert_predicate checker, :updated? + assert checker.execute_if_updated - FileUtils.touch(File.join(unwatched_dir, "a.rb")) - wait - assert_not_predicate checker, :updated? - assert_not checker.execute_if_updated - end + FileUtils.touch(File.join(unwatched_dir, "a.rb")) + wait + assert_not_predicate checker, :updated? + assert_not checker.execute_if_updated end end @@ -156,14 +172,6 @@ class EventedFileUpdateCheckerPathHelperTest < ActiveSupport::TestCase assert_nil @ph.longest_common_subpath([]) end - test "#existing_parent returns the most specific existing ascendant" do - wd = Pathname.getwd - - assert_equal wd, @ph.existing_parent(wd) - assert_equal wd, @ph.existing_parent(wd.join("non-existing/directory")) - assert_equal pn("/"), @ph.existing_parent(pn("/non-existing/directory")) - end - test "#filter_out_descendants returns the same collection if there are no descendants (empty)" do assert_equal [], @ph.filter_out_descendants([]) end diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index 3b98749f1b..02b90b0297 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -113,6 +113,24 @@ module Notifications assert_equal expected, events end + def test_subscribed_all_messages + name = "foo" + name2 = name * 2 + expected = [name, name2, name] + + events = [] + callback = lambda { |*_| events << _.first } + ActiveSupport::Notifications.subscribed(callback) do + ActiveSupport::Notifications.instrument(name) + ActiveSupport::Notifications.instrument(name2) + ActiveSupport::Notifications.instrument(name) + end + assert_equal expected, events + + ActiveSupport::Notifications.instrument(name) + assert_equal expected, events + end + def test_subscribing_to_instrumentation_while_inside_it # the repro requires that there are no evented subscribers for the "foo" event, # so we have to duplicate some of the setup code diff --git a/guides/source/6_0_release_notes.md b/guides/source/6_0_release_notes.md index fa45e7240d..e421ae1ac7 100644 --- a/guides/source/6_0_release_notes.md +++ b/guides/source/6_0_release_notes.md @@ -215,6 +215,10 @@ Please refer to the [Changelog][action-pack] for detailed changes. ### Notable changes +* Change `ActionDispatch::Response#content_type` returning Content-Type + header as it is. + ([Pull Request](https://github.com/rails/rails/pull/36034)) + * Raise an `ArgumentError` if a resource param contains a colon. ([Pull Request](https://github.com/rails/rails/pull/35236)) diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index e40f16e62d..5fb030fad4 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -1821,6 +1821,21 @@ Client.limit(1).pluck(:name) # => ["David"] ``` +NOTE: You should also know that using `pluck` will trigger eager loading if the relation object contains include values, even if the eager loading is not necessary for the query. For example: + +```ruby +# store association for reusing it +assoc = Company.includes(:account) +assoc.pluck(:id) +# SELECT "companies"."id" FROM "companies" LEFT OUTER JOIN "accounts" ON "accounts"."id" = "companies"."account_id" +``` + +One way to avoid this is to `unscope` the includes: + +```ruby +assoc.unscope(:includes).pluck(:id) +``` + ### `ids` `ids` can be used to pluck all the IDs for the relation using the table's primary key. diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index 1a057832d4..21901a7158 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -2633,14 +2633,12 @@ The method `stringify_keys` returns a hash that has a stringified version of the # => {"" => nil, "1" => 1, "a" => :a} ``` -In case of key collision, one of the values will be chosen. The chosen value may not always be the same given the same hash: +In case of key collision, the value will be the one most recently inserted into the hash: ```ruby {"a" => 1, a: 2}.stringify_keys -# The result could either be +# The result will be # => {"a"=>2} -# or -# => {"a"=>1} ``` This method may be useful for example to easily accept both symbols and strings as options. For instance `ActionView::Helpers::FormHelper` defines: @@ -2677,14 +2675,12 @@ The method `symbolize_keys` returns a hash that has a symbolized version of the WARNING. Note in the previous example only one key was symbolized. -In case of key collision, one of the values will be chosen. The chosen value may not always be the same given the same hash: +In case of key collision, the value will be the one most recently inserted into the hash: ```ruby {"a" => 1, a: 2}.symbolize_keys -# The result could either be +# The result will be # => {:a=>2} -# or -# => {:a=>1} ``` This method may be useful for example to easily accept both symbols and strings as options. For instance `ActionController::UrlRewriter` defines diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 0b54683b5a..cc64c7eac6 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -69,7 +69,7 @@ These configuration methods are to be called on a `Rails::Railtie` object, such * `config.cache_classes` controls whether or not application classes and modules should be reloaded on each request. Defaults to `false` in development mode, and `true` in test and production modes. * `config.beginning_of_week` sets the default beginning of week for the -application. Accepts a valid week day symbol (e.g. `:monday`). +application. Accepts a valid day of week as a symbol (e.g. `:monday`). * `config.cache_store` configures which cache store to use for Rails caching. Options include one of the symbols `:memory_store`, `:file_store`, `:mem_cache_store`, `:null_store`, `:redis_cache_store`, or an object that implements the cache API. Defaults to `:file_store`. diff --git a/guides/source/testing.md b/guides/source/testing.md index 9540bb2af5..41bc54b924 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -1144,7 +1144,7 @@ test "ajax request" do get article_url(article), xhr: true assert_equal 'hello world', @response.body - assert_equal "text/javascript", @response.content_type + assert_equal "text/javascript", @response.media_type end ``` diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index b8a5c39f39..1110592d5e 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -134,6 +134,28 @@ Action Cable JavaScript API: + ActionCable.logger.enabled = false ``` +### `ActionDispatch::Response#content_type` now returned Content-Type header as it is. + +Previously, `ActionDispatch::Response#content_type` returned value does NOT contain charset part. +This behavior changed to returned Content-Type header containing charset part as it is. + +If you want just MIME type, please use `ActionDispatch::Response#media_type` instead. + +Before: + +```ruby +resp = ActionDispatch::Response.new(200, "Content-Type" => "text/csv; header=present; charset=utf-16") +resp.content_type #=> "text/csv; header=present" +``` + +After: + +```ruby +resp = ActionDispatch::Response.new(200, "Content-Type" => "text/csv; header=present; charset=utf-16") +resp.content_type #=> "text/csv; header=present; charset=utf-16" +resp.media_type #=> "text/csv" +``` + ### Autoloading The default configuration for Rails 6 diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index f2f46d6e25..7336e235f6 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -243,7 +243,10 @@ module Rails # can change in Ruby 1.8.7 when we FileUtils.cd. RAILS_DEV_PATH = File.expand_path("../../../../../..", __dir__) - class AppGenerator < AppBase # :nodoc: + class AppGenerator < AppBase + + # :stopdoc: + WEBPACKS = %w( react vue angular elm stimulus ) add_shared_options_for "application" @@ -492,6 +495,8 @@ module Rails "rails new #{arguments.map(&:usage).join(' ')} [options]" end + # :startdoc: + private # Define file as an alias to create_file for backwards compatibility. diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt index 9a7267c783..b8c1f21c0b 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt @@ -6,7 +6,7 @@ <%%= csp_meta_tag %> <%- if options[:skip_javascript] -%> - <%%= stylesheet_link_tag 'application', media: 'all' %> + <%%= stylesheet_link_tag 'application', media: 'all' %> <%- else -%> <%- unless options[:skip_turbolinks] -%> <%%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb index 1ab45abcd0..7fc918898b 100644 --- a/railties/test/application/test_runner_test.rb +++ b/railties/test/application/test_runner_test.rb @@ -564,6 +564,24 @@ module ApplicationTests assert_no_match "create_table(:users)", output end + def test_run_in_parallel_with_process_worker_crash + exercise_parallelization_regardless_of_machine_core_count(with: :processes) + + file_name = app_file("test/models/parallel_test.rb", <<-RUBY) + require 'test_helper' + + class ParallelTest < ActiveSupport::TestCase + def test_crash + Kernel.exit 1 + end + end + RUBY + + output = run_test_command(file_name) + + assert_match %r{Queue not empty, but all workers have finished. This probably means that a worker crashed and 1 tests were missed.}, output + end + def test_run_in_parallel_with_threads exercise_parallelization_regardless_of_machine_core_count(with: :threads)