diff options
26 files changed, 240 insertions, 66 deletions
diff --git a/Gemfile.lock b/Gemfile.lock index 5e7f3d2b74..6a8f9040d7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -70,7 +70,7 @@ PATH i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) - zeitwerk (~> 1.3, >= 1.3.4) + zeitwerk (~> 1.4) rails (6.0.0.beta3) actioncable (= 6.0.0.beta3) actionmailbox (= 6.0.0.beta3) @@ -517,7 +517,7 @@ GEM websocket-extensions (0.1.3) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (1.3.4) + zeitwerk (1.4.0) PLATFORMS java diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index c3e0ea3c89..962d10d81b 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -170,6 +170,7 @@ module Mime def parse(accept_header) if !accept_header.include?(",") accept_header = accept_header.split(PARAMETER_SEPARATOR_REGEXP).first + return [] unless accept_header parse_trailing_star(accept_header) || [Mime::Type.lookup(accept_header)].compact else list, index = [], 0 @@ -221,7 +222,18 @@ module Mime attr_reader :hash + MIME_NAME = "[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}" + MIME_PARAMETER_KEY = "[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}" + MIME_PARAMETER_VALUE = "#{Regexp.escape('"')}?[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}#{Regexp.escape('"')}?" + MIME_PARAMETER = "\s*\;\s+#{MIME_PARAMETER_KEY}(?:\=#{MIME_PARAMETER_VALUE})?" + MIME_REGEXP = /\A(?:\*\/\*|#{MIME_NAME}\/(?:\*|#{MIME_NAME})(?:\s*#{MIME_PARAMETER}\s*)*)\z/ + + class InvalidMimeType < StandardError; end + def initialize(string, symbol = nil, synonyms = []) + unless MIME_REGEXP.match?(string) + raise InvalidMimeType, "#{string.inspect} is not a valid MIME type" + end @symbol, @synonyms = symbol, synonyms @string = string @hash = [@string, @synonyms, @symbol].hash diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb index 7a2fcd6db7..f0c869fba0 100644 --- a/actionpack/lib/action_dispatch/middleware/stack.rb +++ b/actionpack/lib/action_dispatch/middleware/stack.rb @@ -34,7 +34,28 @@ module ActionDispatch end def build(app) - klass.new(app, *args, &block) + InstrumentationProxy.new(klass.new(app, *args, &block), inspect) + end + end + + # This class is used to instrument the execution of a single middleware. + # It proxies the `call` method transparently and instruments the method + # call. + class InstrumentationProxy + EVENT_NAME = "process_middleware.action_dispatch" + + def initialize(middleware, class_name) + @middleware = middleware + + @payload = { + middleware: class_name, + } + end + + def call(env) + ActiveSupport::Notifications.instrument(EVENT_NAME, @payload) do + @middleware.call(env) + end end end diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index d67044b4ac..da3ade652e 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1440,6 +1440,9 @@ module ActionDispatch # Allows you to specify the default value for optional +format+ # segment or disable it by supplying +false+. # + # [:param] + # Allows you to override the default param name of +:id+ in the URL. + # # === Examples # # # routes call <tt>Admin::PostsController</tt> diff --git a/actionpack/lib/action_dispatch/testing/request_encoder.rb b/actionpack/lib/action_dispatch/testing/request_encoder.rb index 9889f61951..6c65bec62f 100644 --- a/actionpack/lib/action_dispatch/testing/request_encoder.rb +++ b/actionpack/lib/action_dispatch/testing/request_encoder.rb @@ -38,8 +38,8 @@ module ActionDispatch end def self.parser(content_type) - mime = Mime::Type.lookup(content_type) - encoder(mime ? mime.ref : nil).response_parser + type = Mime::Type.lookup(content_type).ref if content_type + encoder(type).response_parser end def self.encoder(name) diff --git a/actionpack/test/controller/new_base/content_negotiation_test.rb b/actionpack/test/controller/new_base/content_negotiation_test.rb index 6de91c57b7..00b2798aeb 100644 --- a/actionpack/test/controller/new_base/content_negotiation_test.rb +++ b/actionpack/test/controller/new_base/content_negotiation_test.rb @@ -25,7 +25,7 @@ module ContentNegotiation assert_body "Hello world text/html!" end - test "A js or */* Accept header on xhr will return HTML" do + test "A js or */* Accept header on xhr will return JavaScript" do get "/content_negotiation/basic/hello", headers: { "HTTP_ACCEPT" => "text/javascript, */*" }, xhr: true assert_body "Hello world text/javascript!" end diff --git a/actionpack/test/dispatch/middleware_stack_test.rb b/actionpack/test/dispatch/middleware_stack_test.rb index 5f43e5a3c5..90f2eccd19 100644 --- a/actionpack/test/dispatch/middleware_stack_test.rb +++ b/actionpack/test/dispatch/middleware_stack_test.rb @@ -3,13 +3,24 @@ require "abstract_unit" class MiddlewareStackTest < ActiveSupport::TestCase - class FooMiddleware; end - class BarMiddleware; end - class BazMiddleware; end - class HiyaMiddleware; end - class BlockMiddleware + class Base + def initialize(app) + @app = app + end + + def call(env) + @app.call(env) + end + end + + class FooMiddleware < Base; end + class BarMiddleware < Base; end + class BazMiddleware < Base; end + class HiyaMiddleware < Base; end + class BlockMiddleware < Base attr_reader :block - def initialize(&block) + def initialize(app, &block) + super(app) @block = block end end @@ -109,6 +120,24 @@ class MiddlewareStackTest < ActiveSupport::TestCase assert_equal @stack.last, @stack.last end + test "instruments the execution of middlewares" do + app = @stack.build(proc { |env| [200, {}, []] }) + env = {} + + events = [] + + subscriber = proc do |*args| + events << ActiveSupport::Notifications::Event.new(*args) + end + + ActiveSupport::Notifications.subscribed(subscriber, "process_middleware.action_dispatch") do + app.call(env) + end + + assert_equal 2, events.count + assert_equal ["MiddlewareStackTest::BarMiddleware", "MiddlewareStackTest::FooMiddleware"], events.map { |e| e.payload[:middleware] } + end + test "includes a middleware" do assert_equal true, @stack.include?(ActionDispatch::MiddlewareStack::Middleware.new(BarMiddleware, nil, nil)) end diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 45d91883c0..50f6c06fee 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -174,4 +174,51 @@ class MimeTypeTest < ActiveSupport::TestCase assert_not (Mime[:js] !~ "application/javascript") assert Mime[:html] =~ "application/xhtml+xml" end + + test "can be initialized with wildcards" do + assert_equal "*/*", Mime::Type.new("*/*").to_s + assert_equal "text/*", Mime::Type.new("text/*").to_s + assert_equal "video/*", Mime::Type.new("video/*").to_s + end + + test "can be initialized with parameters" do + assert_equal "text/html; parameter", Mime::Type.new("text/html; parameter").to_s + assert_equal "text/html; parameter=abc", Mime::Type.new("text/html; parameter=abc").to_s + assert_equal 'text/html; parameter="abc"', Mime::Type.new('text/html; parameter="abc"').to_s + assert_equal 'text/html; parameter=abc; parameter2="xyz"', Mime::Type.new('text/html; parameter=abc; parameter2="xyz"').to_s + end + + test "invalid mime types raise error" do + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("too/many/slash") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("missingslash") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("improper/semicolon;") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new('improper/semicolon; parameter=abc; parameter2="xyz";') + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("text/html, text/plain") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("*/html") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new(nil) + end + end end diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb index 07c44307ff..1c577348e5 100644 --- a/actionview/lib/action_view/template/resolver.rb +++ b/actionview/lib/action_view/template/resolver.rb @@ -184,17 +184,21 @@ module ActionView template_paths = reject_files_external_to_app(template_paths) unless outside_app_allowed template_paths.map do |template| - handler, format, variant = extract_handler_and_format_and_variant(template) - - FileTemplate.new(File.expand_path(template), handler, - virtual_path: path.virtual, - format: format, - variant: variant, - locals: locals - ) + build_template(template, path.virtual, locals) end end + def build_template(template, virtual_path, locals) + handler, format, variant = extract_handler_and_format_and_variant(template) + + FileTemplate.new(File.expand_path(template), handler, + virtual_path: virtual_path, + format: format, + variant: variant, + locals: locals + ) + end + def reject_files_external_to_app(files) files.reject { |filename| !inside_path?(@path, filename) } end @@ -385,5 +389,9 @@ module ActionView def self.instances [new(""), new("/")] end + + def build_template(template, virtual_path, locals) + super(template, nil, locals) + end end end diff --git a/actionview/test/template/fallback_file_system_resolver_test.rb b/actionview/test/template/fallback_file_system_resolver_test.rb new file mode 100644 index 0000000000..304cdb8a03 --- /dev/null +++ b/actionview/test/template/fallback_file_system_resolver_test.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require "abstract_unit" + +class FallbackFileSystemResolverTest < ActiveSupport::TestCase + def setup + @root_resolver = ActionView::FallbackFileSystemResolver.new("/") + end + + def test_should_have_no_virtual_path + templates = @root_resolver.find_all("hello_world.erb", "#{FIXTURE_LOAD_PATH}/test", false, locale: [], formats: [:html], variants: [], handlers: [:erb]) + assert_equal 1, templates.size + assert_equal "Hello world!", templates[0].source + assert_nil templates[0].virtual_path + end +end diff --git a/actionview/test/template/html_test.rb b/actionview/test/template/html_test.rb index c5fc8f906c..17f21cbbc5 100644 --- a/actionview/test/template/html_test.rb +++ b/actionview/test/template/html_test.rb @@ -8,9 +8,9 @@ class HTMLTest < ActiveSupport::TestCase end test "formats returns string for recognized MIME type when MIME does not have symbol" do - foo = Mime::Type.lookup("foo") + foo = Mime::Type.lookup("text/foo") assert_nil foo.to_sym - assert_equal "foo", ActionView::Template::HTML.new("", foo).format + assert_equal "text/foo", ActionView::Template::HTML.new("", foo).format end test "formats returns string for unknown MIME type" do diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index 9d24d839c1..2877530917 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -138,6 +138,10 @@ module ActiveRecord "'#{quote_string(value.to_s)}'" end + def sanitize_as_sql_comment(value) # :nodoc: + value.to_s.gsub(%r{ (/ (?: | \g<1>) \*) \+? \s* | \s* (\* (?: | \g<2>) /) }x, "") + end + private def type_casted_binds(binds) if binds.first.is_a?(Array) diff --git a/activerecord/lib/active_record/database_configurations.rb b/activerecord/lib/active_record/database_configurations.rb index a6c702cbbc..4656045fe5 100644 --- a/activerecord/lib/active_record/database_configurations.rb +++ b/activerecord/lib/active_record/database_configurations.rb @@ -106,7 +106,7 @@ module ActiveRecord build_db_config = configs.each_pair.flat_map do |env_name, config| walk_configs(env_name.to_s, "primary", config) - end.compact + end.flatten.compact if url = ENV["DATABASE_URL"] build_url_config(url, build_db_config) diff --git a/activerecord/lib/active_record/insert_all.rb b/activerecord/lib/active_record/insert_all.rb index 3833cb2fcf..98c98d61cd 100644 --- a/activerecord/lib/active_record/insert_all.rb +++ b/activerecord/lib/active_record/insert_all.rb @@ -2,12 +2,14 @@ module ActiveRecord class InsertAll - attr_reader :model, :connection, :inserts, :on_duplicate, :returning, :unique_by + attr_reader :model, :connection, :inserts, :keys + attr_reader :on_duplicate, :returning, :unique_by def initialize(model, inserts, on_duplicate:, returning: nil, unique_by: nil) raise ArgumentError, "Empty list of attributes passed" if inserts.blank? - @model, @connection, @inserts, @on_duplicate, @returning, @unique_by = model, model.connection, inserts, on_duplicate, returning, unique_by + @model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s).to_set + @on_duplicate, @returning, @unique_by = on_duplicate, returning, unique_by @returning = (connection.supports_insert_returning? ? primary_keys : false) if @returning.nil? @returning = false if @returning == [] @@ -21,10 +23,6 @@ module ActiveRecord connection.exec_query to_sql, "Bulk Insert" end - def keys - inserts.first.keys.map(&:to_s) - end - def updatable_columns keys - readonly_columns - unique_by_columns end @@ -37,6 +35,17 @@ module ActiveRecord on_duplicate == :update end + def map_key_with_value + inserts.map do |attributes| + attributes = attributes.stringify_keys + verify_attributes(attributes) + + keys.map do |key| + yield key, attributes[key] + end + end + end + private def ensure_valid_options_for_connection! if returning && !connection.supports_insert_returning? @@ -76,6 +85,12 @@ module ActiveRecord Array.wrap(model.primary_key) end + def verify_attributes(attributes) + if keys != attributes.keys.to_set + raise ArgumentError, "All objects being inserted must have the same keys" + end + end + class Builder attr_reader :model @@ -91,29 +106,11 @@ module ActiveRecord end def values_list - columns = connection.schema_cache.columns_hash(model.table_name) - - column_names = columns.keys.to_set - keys = insert_all.keys.to_set - unknown_columns = keys - column_names + types = extract_types_from_columns_on(model.table_name, keys: insert_all.keys) - unless unknown_columns.empty? - raise UnknownAttributeError.new(model.new, unknown_columns.first) - end - - types = keys.map { |key| [ key, connection.lookup_cast_type_from_column(columns[key]) ] }.to_h - - values_list = insert_all.inserts.map do |attributes| - attributes = attributes.stringify_keys - - unless attributes.keys.to_set == keys - raise ArgumentError, "All objects being inserted must have the same keys" - end - - keys.map do |key| - bind = Relation::QueryAttribute.new(key, attributes[key], types[key]) - connection.with_yaml_fallback(bind.value_for_database) - end + values_list = insert_all.map_key_with_value do |key, value| + bind = Relation::QueryAttribute.new(key, value, types[key]) + connection.with_yaml_fallback(bind.value_for_database) end Arel::InsertManager.new.create_values_list(values_list).to_sql @@ -141,6 +138,15 @@ module ActiveRecord quote_columns(insert_all.keys).join(",") end + def extract_types_from_columns_on(table_name, keys:) + columns = connection.schema_cache.columns_hash(table_name) + + unknown_column = (keys - columns.keys).first + raise UnknownAttributeError.new(model.new, unknown_column) if unknown_column + + keys.map { |key| [ key, connection.lookup_cast_type_from_column(columns[key]) ] }.to_h + end + def quote_columns(columns) columns.map(&connection.method(:quote_column_name)) end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 37179774fa..6d954a2b2e 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -342,6 +342,8 @@ module ActiveRecord # trigger Active Record callbacks or validations. However, values passed to #update_all will still go through # Active Record's normal type casting and serialization. # + # Note: As Active Record callbacks are not triggered, this method will not automatically update +updated_at+/+updated_on+ columns. + # # ==== Parameters # # * +updates+ - A string, array, or hash representing the SET part of an SQL statement. @@ -416,10 +418,10 @@ module ActiveRecord update_all updates end - # Touches all records in the current relation without instantiating records first with the updated_at/on attributes + # Touches all records in the current relation without instantiating records first with the +updated_at+/+updated_on+ attributes # set to the current time or the time specified. # This method can be passed attribute names and an optional time argument. - # If attribute names are passed, they are updated along with updated_at/on attributes. + # If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes. # If no time argument is passed, the current time is used as default. # # === Examples diff --git a/activerecord/lib/arel/visitors/ibm_db.rb b/activerecord/lib/arel/visitors/ibm_db.rb index 0ffc0725f7..5cf958f5f0 100644 --- a/activerecord/lib/arel/visitors/ibm_db.rb +++ b/activerecord/lib/arel/visitors/ibm_db.rb @@ -10,7 +10,8 @@ module Arel # :nodoc: all end def visit_Arel_Nodes_OptimizerHints(o, collector) - collector << "/* <OPTGUIDELINES>#{sanitize_as_sql_comment(o).join}</OPTGUIDELINES> */" + hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join + collector << "/* <OPTGUIDELINES>#{hints}</OPTGUIDELINES> */" end def visit_Arel_Nodes_Limit(o, collector) diff --git a/activerecord/lib/arel/visitors/informix.rb b/activerecord/lib/arel/visitors/informix.rb index cd43be8858..1a4ad1c8d8 100644 --- a/activerecord/lib/arel/visitors/informix.rb +++ b/activerecord/lib/arel/visitors/informix.rb @@ -43,7 +43,8 @@ module Arel # :nodoc: all end def visit_Arel_Nodes_OptimizerHints(o, collector) - collector << "/*+ #{sanitize_as_sql_comment(o).join(", ")} */" + hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join(", ") + collector << "/*+ #{hints} */" end def visit_Arel_Nodes_Offset(o, collector) diff --git a/activerecord/lib/arel/visitors/mssql.rb b/activerecord/lib/arel/visitors/mssql.rb index deb87242b7..8475139870 100644 --- a/activerecord/lib/arel/visitors/mssql.rb +++ b/activerecord/lib/arel/visitors/mssql.rb @@ -82,7 +82,8 @@ module Arel # :nodoc: all end def visit_Arel_Nodes_OptimizerHints(o, collector) - collector << "OPTION (#{sanitize_as_sql_comment(o).join(", ")})" + hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join(", ") + collector << "OPTION (#{hints})" end def get_offset_limit_clause(o) diff --git a/activerecord/lib/arel/visitors/to_sql.rb b/activerecord/lib/arel/visitors/to_sql.rb index 73fef94499..1630226085 100644 --- a/activerecord/lib/arel/visitors/to_sql.rb +++ b/activerecord/lib/arel/visitors/to_sql.rb @@ -166,7 +166,8 @@ module Arel # :nodoc: all end def visit_Arel_Nodes_OptimizerHints(o, collector) - collector << "/*+ #{sanitize_as_sql_comment(o).join(" ")} */" + hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join(" ") + collector << "/*+ #{hints} */" end def collect_nodes_for(nodes, collector, spacer, connector = ", ") @@ -732,10 +733,9 @@ module Arel # :nodoc: all @connection.quote_column_name(name) end - def sanitize_as_sql_comment(o) - o.expr.map { |v| - v.gsub(%r{ (/ (?: | \g<1>) \*) \+? \s* | \s* (\* (?: | \g<2>) /) }x, "") - } + def sanitize_as_sql_comment(value) + return value if Arel::Nodes::SqlLiteral === value + @connection.sanitize_as_sql_comment(value) end def collect_optimizer_hints(o, collector) diff --git a/activestorage/db/update_migrate/20180723000244_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.rb b/activestorage/db/update_migrate/20180723000244_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.rb index 6830203cd6..5472e3c87b 100644 --- a/activestorage/db/update_migrate/20180723000244_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.rb +++ b/activestorage/db/update_migrate/20180723000244_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.rb @@ -1,6 +1,8 @@ class AddForeignKeyConstraintToActiveStorageAttachmentsForBlobId < ActiveRecord::Migration[6.0] def up - unless foreign_key_exists?(:active_storage_attachments, column: :blob_id) + return if foreign_key_exists?(:active_storage_attachments, column: :blob_id) + + if table_exists?(:active_storage_blobs) add_foreign_key :active_storage_attachments, :active_storage_blobs, column: :blob_id end end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index b2330f2c9d..63e2e44597 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,17 @@ +* Fix `Time#advance` to work with dates before 1001-03-07 + + Before: + + Time.utc(1001, 3, 6).advance(years: -1) # => 1000-03-05 00:00:00 UTC + + After + + Time.utc(1001, 3, 6).advance(years: -1) # => 1000-03-06 00:00:00 UTC + + Note that this doesn't affect `DateTime#advance` as that doesn't use a proleptic calendar. + + *Andrew White* + * In Zeitwerk mode, engines are now managed by the `main` autoloader. Engines may reference application constants, if the application is reloaded and we do not reload engines, they won't use the reloaded application code. *Xavier Noria* diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec index e55d73c717..51f5086cca 100644 --- a/activesupport/activesupport.gemspec +++ b/activesupport/activesupport.gemspec @@ -34,5 +34,5 @@ Gem::Specification.new do |s| s.add_dependency "tzinfo", "~> 1.1" s.add_dependency "minitest", "~> 5.1" s.add_dependency "concurrent-ruby", "~> 1.0", ">= 1.0.2" - s.add_dependency "zeitwerk", "~> 1.3", ">= 1.3.4" + s.add_dependency "zeitwerk", "~> 1.4" end diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 120768dec5..f09a6271ad 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -170,8 +170,7 @@ class Time options[:hours] = options.fetch(:hours, 0) + 24 * partial_days end - d = to_date.advance(options) - d = d.gregorian if d.julian? + d = to_date.gregorian.advance(options) time_advanced_by_date = change(year: d.year, month: d.month, day: d.day) seconds_to_advance = \ options.fetch(:seconds, 0) + diff --git a/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb b/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb index e00307d257..c6fdade006 100644 --- a/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb +++ b/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb @@ -21,7 +21,11 @@ module ActiveSupport end def autoloaded_constants - (Rails.autoloaders.main.loaded + Rails.autoloaders.once.loaded).to_a + cpaths = [] + Rails.autoloaders.each do |autoloader| + cpaths.concat(autoloader.loaded_cpaths.to_a) + end + cpaths end def autoloaded?(object) diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index 7078f3506d..590b81b770 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -514,6 +514,8 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal Time.local(1582, 10, 15, 15, 15, 10), Time.local(1582, 10, 14, 15, 15, 10).advance(days: 1) assert_equal Time.local(1582, 10, 5, 15, 15, 10), Time.local(1582, 10, 4, 15, 15, 10).advance(days: 1) assert_equal Time.local(1582, 10, 4, 15, 15, 10), Time.local(1582, 10, 5, 15, 15, 10).advance(days: -1) + assert_equal Time.local(999, 10, 4, 15, 15, 10), Time.local(1000, 10, 4, 15, 15, 10).advance(years: -1) + assert_equal Time.local(1000, 10, 4, 15, 15, 10), Time.local(999, 10, 4, 15, 15, 10).advance(years: 1) end def test_last_week diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index 14cdf1ab7c..3fcfaa9623 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -123,6 +123,8 @@ module TestHelpers adapter: sqlite3 pool: 5 timeout: 5000 + variables: + statement_timeout: 1000 development: primary: <<: *default |