commit
60af9db374
|
@ -1,5 +1,3 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class <%= class_name %>MailboxTest < ActionMailbox::TestCase
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 "<p>Hello world!</p>\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 "<p>Hello world!</p>\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 '<html><div id="html">Hello future from Firefox!</div></html>', @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 '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body
|
||||
end
|
||||
|
||||
|
@ -672,7 +672,7 @@ class RespondToControllerTest < ActionController::TestCase
|
|||
@request.accept = "text/iphone"
|
||||
get :iphone_with_html_response_type
|
||||
assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -208,7 +208,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
|
|||
assert_response 500
|
||||
assert_no_match(/<header>/, body)
|
||||
assert_no_match(/<body>/, 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>/, 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>/, 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>/, 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>/, 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>/, 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(/<header>/, body)
|
||||
assert_no_match(/<body>/, 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>/, 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>/, 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>/, 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>/, 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>/, 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(/<header>/, body)
|
||||
assert_match(/<body>/, 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1003,14 +1003,14 @@ class RenderTest < ActionController::TestCase
|
|||
def test_render_xml
|
||||
get :render_xml_hello
|
||||
assert_equal "<html>\n <p>Hello David</p>\n<p>This is grand!</p>\n</html>\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 "<html>\n <p>Hello David</p>\n<p>This is grand!</p>\n</html>\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 "<strong>only partial</strong>\n", @controller.instance_variable_get(:@html)
|
||||
assert_equal "<strong>only html partial</strong>\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 "<strong>only partial</strong>\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<p>This is grand!</p>"
|
||||
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
|
||||
|
|
|
@ -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("<option>Home</option><option>Work</option><option>Pub</option>"), 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("<option>Home</option><option>Work</option><option>Pub</option>"), include_blank: false
|
||||
expected = %(<select id="places" name="places"><option>Home</option><option>Work</option><option>Pub</option></select>)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -264,8 +264,7 @@ module ActiveRecord
|
|||
if_not_exists: false,
|
||||
options: nil,
|
||||
as: nil,
|
||||
comment: nil,
|
||||
**
|
||||
comment: nil
|
||||
)
|
||||
@conn = conn
|
||||
@columns_hash = {}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
# * <tt>:limit</tt> -
|
||||
# Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
|
||||
# and number of bytes for <tt>:text</tt>, <tt>:binary</tt> and <tt>:integer</tt> columns.
|
||||
# and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
|
||||
# This option is ignored by some backends.
|
||||
# * <tt>:default</tt> -
|
||||
# The column's default value. Use +nil+ for +NULL+.
|
||||
# * <tt>:null</tt> -
|
||||
# Allows or disallows +NULL+ values in the column.
|
||||
# * <tt>:precision</tt> -
|
||||
# Specifies the precision for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
|
||||
# Specifies the precision for the <tt>:decimal</tt>, <tt>:numeric</tt>,
|
||||
# <tt>:datetime</tt>, and <tt>:time</tt> columns.
|
||||
# * <tt>:scale</tt> -
|
||||
# Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
|
||||
# * <tt>:collation</tt> -
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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' } }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`.
|
||||
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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' %>
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue