diff options
Diffstat (limited to 'activesupport/lib/active_support')
19 files changed, 118 insertions, 75 deletions
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 6967c164ab..d769e2c8ea 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -333,8 +333,9 @@ module ActiveSupport # the cache with the given key, then that data is returned. Otherwise, # +nil+ is returned. # - # Note, if data was written with the <tt>:expires_in<tt> or <tt>:version</tt> options, - # both of these conditions are applied before the data is returned. + # Note, if data was written with the <tt>:expires_in</tt> or + # <tt>:version</tt> options, both of these conditions are applied before + # the data is returned. # # Options are passed to the underlying cache implementation. def read(name, options = nil) @@ -712,17 +713,14 @@ module ActiveSupport DEFAULT_COMPRESS_LIMIT = 1.kilobyte # Creates a new cache entry for the specified value. Options supported are - # +:compress+, +:compress_threshold+, and +:expires_in+. - def initialize(value, options = {}) - @value = value - if should_compress?(options) - compress! - end - - @version = options[:version] + # +:compress+, +:compress_threshold+, +:version+ and +:expires_in+. + def initialize(value, compress: true, compress_threshold: DEFAULT_COMPRESS_LIMIT, version: nil, expires_in: nil, **) + @value = value + @version = version @created_at = Time.now.to_f - @expires_in = options[:expires_in] - @expires_in = @expires_in.to_f if @expires_in + @expires_in = expires_in && expires_in.to_f + + compress!(compress_threshold) if compress end def value @@ -754,17 +752,13 @@ module ActiveSupport # Returns the size of the cached value. This could be less than # <tt>value.size</tt> if the data is compressed. def size - if defined?(@s) - @s + case value + when NilClass + 0 + when String + @value.bytesize else - case value - when NilClass - 0 - when String - @value.bytesize - else - @s = Marshal.dump(@value).bytesize - end + @s ||= Marshal.dump(@value).bytesize end end @@ -781,31 +775,35 @@ module ActiveSupport end private - def should_compress?(options) - if @value && options.fetch(:compress, true) - compress_threshold = options.fetch(:compress_threshold, DEFAULT_COMPRESS_LIMIT) - serialized_value_size = (@value.is_a?(String) ? @value : marshaled_value).bytesize + def compress!(compress_threshold) + case @value + when nil, true, false, Numeric + uncompressed_size = 0 + when String + uncompressed_size = @value.bytesize + else + serialized = Marshal.dump(@value) + uncompressed_size = serialized.bytesize + end + + if uncompressed_size >= compress_threshold + serialized ||= Marshal.dump(@value) + compressed = Zlib::Deflate.deflate(serialized) - serialized_value_size >= compress_threshold + if compressed.bytesize < uncompressed_size + @value = compressed + @compressed = true + end end end def compressed? - defined?(@compressed) ? @compressed : false - end - - def compress! - @value = Zlib::Deflate.deflate(marshaled_value) - @compressed = true + defined?(@compressed) end def uncompress(value) Marshal.load(Zlib::Inflate.inflate(value)) end - - def marshaled_value - @marshaled_value ||= Marshal.dump(@value) - end end end end diff --git a/activesupport/lib/active_support/cache/redis_cache_store.rb b/activesupport/lib/active_support/cache/redis_cache_store.rb index a1cb6db25d..11c574258f 100644 --- a/activesupport/lib/active_support/cache/redis_cache_store.rb +++ b/activesupport/lib/active_support/cache/redis_cache_store.rb @@ -62,8 +62,9 @@ module ActiveSupport end end - DELETE_GLOB_LUA = "for i, name in ipairs(redis.call('KEYS', ARGV[1])) do redis.call('DEL', name); end" - private_constant :DELETE_GLOB_LUA + # The maximum number of entries to receive per SCAN call. + SCAN_BATCH_SIZE = 1000 + private_constant :SCAN_BATCH_SIZE # Support raw values in the local cache strategy. module LocalCacheWithRaw # :nodoc: @@ -231,12 +232,18 @@ module ActiveSupport # Failsafe: Raises errors. def delete_matched(matcher, options = nil) instrument :delete_matched, matcher do - case matcher - when String - redis.with { |c| c.eval DELETE_GLOB_LUA, [], [namespace_key(matcher, options)] } - else + unless String === matcher raise ArgumentError, "Only Redis glob strings are supported: #{matcher.inspect}" end + redis.with do |c| + pattern = namespace_key(matcher, options) + cursor = "0" + # Fetch keys in batches using SCAN to avoid blocking the Redis server. + begin + cursor, keys = c.scan(cursor, match: pattern, count: SCAN_BATCH_SIZE) + c.del(*keys) unless keys.empty? + end until cursor == "0" + end end end @@ -286,7 +293,7 @@ module ActiveSupport # Failsafe: Raises errors. def clear(options = nil) failsafe :clear do - if namespace = merged_options(options)[namespace] + if namespace = merged_options(options)[:namespace] delete_matched "*", namespace: namespace else redis.with { |c| c.flushdb } diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 9a3728d986..a1b841ec3d 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -809,7 +809,9 @@ module ActiveSupport names.each do |name| name = name.to_sym - set_callbacks name, CallbackChain.new(name, options) + ([self] + ActiveSupport::DescendantsTracker.descendants(self)).each do |target| + target.set_callbacks name, CallbackChain.new(name, options) + end module_eval <<-RUBY, __FILE__, __LINE__ + 1 def _run_#{name}_callbacks(&block) diff --git a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb index f6cb1a384c..de13f00e60 100644 --- a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb @@ -60,6 +60,16 @@ module DateAndTime !WEEKEND_DAYS.include?(wday) end + # Returns true if the date/time before <tt>date_or_time</tt>. + def before?(date_or_time) + self < date_or_time + end + + # Returns true if the date/time after <tt>date_or_time</tt>. + def after?(date_or_time) + self > date_or_time + end + # Returns a new date/time the specified number of days ago. def days_ago(days) advance(days: -days) diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb index f01d01e6aa..edde4f46b9 100644 --- a/activesupport/lib/active_support/core_ext/enumerable.rb +++ b/activesupport/lib/active_support/core_ext/enumerable.rb @@ -6,7 +6,7 @@ module Enumerable # We can't use Refinements here because Refinements with Module which will be prepended # doesn't work well https://bugs.ruby-lang.org/issues/13446 - alias :_original_sum_with_required_identity :sum + alias :_original_sum_with_required_identity :sum # :nodoc: private :_original_sum_with_required_identity # Calculates a sum from the elements. diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb index 580baffa2b..01fee0fb74 100644 --- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb @@ -163,10 +163,10 @@ class Module # parent class. Similarly if parent class changes the value then that would # change the value of subclasses too. # - # class Male < Person + # class Citizen < Person # end # - # Male.new.hair_colors << :blue + # Citizen.new.hair_colors << :blue # Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue] # # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>. diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb index ec3497173f..7f42f44efb 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -22,8 +22,9 @@ class Module # ==== Options # * <tt>:to</tt> - Specifies the target object # * <tt>:prefix</tt> - Prefixes the new method with the target name or a custom prefix - # * <tt>:allow_nil</tt> - if set to true, prevents a +Module::DelegationError+ + # * <tt>:allow_nil</tt> - If set to true, prevents a +Module::DelegationError+ # from being raised + # * <tt>:private</tt> - If set to true, changes method visibility to private # # The macro receives one or more method names (specified as symbols or # strings) and the name of the target object via the <tt>:to</tt> option diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb index 2c004f4c9e..7075b5b869 100644 --- a/activesupport/lib/active_support/deprecation/reporting.rb +++ b/activesupport/lib/active_support/deprecation/reporting.rb @@ -104,7 +104,7 @@ module ActiveSupport end end - RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/" def ignored_callstack(path) path.start_with?(RAILS_GEM_ROOT) || path.start_with?(RbConfig::CONFIG["rubylibdir"]) diff --git a/activesupport/lib/active_support/encrypted_configuration.rb b/activesupport/lib/active_support/encrypted_configuration.rb index dab953d5d5..3c6da10548 100644 --- a/activesupport/lib/active_support/encrypted_configuration.rb +++ b/activesupport/lib/active_support/encrypted_configuration.rb @@ -38,10 +38,6 @@ module ActiveSupport @options ||= ActiveSupport::InheritableOptions.new(config) end - def serialize(config) - config.present? ? YAML.dump(config) : "" - end - def deserialize(config) config.present? ? YAML.load(config, content_path) : {} end diff --git a/activesupport/lib/active_support/encrypted_file.rb b/activesupport/lib/active_support/encrypted_file.rb index 671b6b6a69..c66f1b557e 100644 --- a/activesupport/lib/active_support/encrypted_file.rb +++ b/activesupport/lib/active_support/encrypted_file.rb @@ -57,7 +57,7 @@ module ActiveSupport private def writing(contents) - tmp_file = "#{content_path.basename}.#{Process.pid}" + tmp_file = "#{Process.pid}.#{content_path.basename.to_s.chomp('.enc')}" tmp_path = Pathname.new File.join(Dir.tmpdir, tmp_file) tmp_path.binwrite contents diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb index d60b3eff30..39dab1cc71 100644 --- a/activesupport/lib/active_support/i18n.rb +++ b/activesupport/lib/active_support/i18n.rb @@ -13,3 +13,4 @@ require "active_support/lazy_load_hooks" ActiveSupport.run_load_hooks(:i18n) I18n.load_path << File.expand_path("locale/en.yml", __dir__) +I18n.load_path << File.expand_path("locale/en.rb", __dir__) diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index 7e782e2a93..339b93b8da 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -341,18 +341,7 @@ module ActiveSupport # ordinal(-11) # => "th" # ordinal(-1021) # => "st" def ordinal(number) - abs_number = number.to_i.abs - - if (11..13).include?(abs_number % 100) - "th" - else - case abs_number % 10 - when 1; "st" - when 2; "nd" - when 3; "rd" - else "th" - end - end + I18n.translate("number.nth.ordinals", number: number) end # Turns a number into an ordinal string used to denote the position in an @@ -365,7 +354,7 @@ module ActiveSupport # ordinalize(-11) # => "-11th" # ordinalize(-1021) # => "-1021st" def ordinalize(number) - "#{number}#{ordinal(number)}" + I18n.translate("number.nth.ordinalized", number: number) end private diff --git a/activesupport/lib/active_support/locale/en.rb b/activesupport/lib/active_support/locale/en.rb new file mode 100644 index 0000000000..26c2280c95 --- /dev/null +++ b/activesupport/lib/active_support/locale/en.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +{ + en: { + number: { + nth: { + ordinals: lambda do |_key, number:, **_options| + abs_number = number.to_i.abs + + if (11..13).cover?(abs_number % 100) + "th" + else + case abs_number % 10 + when 1 then "st" + when 2 then "nd" + when 3 then "rd" + else "th" + end + end + end, + + ordinalized: lambda do |_key, number:, **_options| + "#{number}#{ActiveSupport::Inflector.ordinal(number)}" + end + } + } + } +} diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index 5236c776dd..8b73270894 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -3,6 +3,7 @@ require "openssl" require "base64" require "active_support/core_ext/array/extract_options" +require "active_support/core_ext/module/attribute_accessors" require "active_support/message_verifier" require "active_support/messages/metadata" diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index a698b4e61e..4e42db4500 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -43,23 +43,23 @@ module ActiveSupport # Parallelizes the test suite. # - # Takes a `workers` argument that controls how many times the process + # Takes a +workers+ argument that controls how many times the process # is forked. For each process a new database will be created suffixed # with the worker number. # # test-database-0 # test-database-1 # - # If `ENV["PARALLEL_WORKERS"]` is set the workers argument will be ignored + # If <tt>ENV["PARALLEL_WORKERS"]</tt> is set the workers argument will be ignored # and the environment variable will be used instead. This is useful for CI # environments, or other environments where you may need more workers than # you do for local testing. # - # If the number of workers is set to `1` or fewer, the tests will not be + # If the number of workers is set to +1+ or fewer, the tests will not be # parallelized. # # The default parallelization method is to fork processes. If you'd like to - # use threads instead you can pass `with: :threads` to the `parallelize` + # use threads instead you can pass <tt>with: :threads</tt> to the +parallelize+ # method. Note the threaded parallelization does not create multiple # database and will not work with system tests at this time. # diff --git a/activesupport/lib/active_support/testing/setup_and_teardown.rb b/activesupport/lib/active_support/testing/setup_and_teardown.rb index 1dbf3c5da0..35236f1401 100644 --- a/activesupport/lib/active_support/testing/setup_and_teardown.rb +++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb @@ -44,8 +44,15 @@ module ActiveSupport end def after_teardown # :nodoc: - run_callbacks :teardown + begin + run_callbacks :teardown + rescue => e + error = e + end + super + ensure + raise error if error end end end diff --git a/activesupport/lib/active_support/testing/stream.rb b/activesupport/lib/active_support/testing/stream.rb index d070a1793d..127cfe1e12 100644 --- a/activesupport/lib/active_support/testing/stream.rb +++ b/activesupport/lib/active_support/testing/stream.rb @@ -33,7 +33,7 @@ module ActiveSupport yield stream_io.rewind - return captured_stream.read + captured_stream.read ensure captured_stream.close captured_stream.unlink diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 20650ce714..7e71318404 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -225,6 +225,8 @@ module ActiveSupport def <=>(other) utc <=> other end + alias_method :before?, :< + alias_method :after?, :> # Returns true if the current object's time is within the specified # +min+ and +max+ time. diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 9dfaddb825..5f709c5fd9 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -279,7 +279,8 @@ module ActiveSupport def zones_map @zones_map ||= MAPPING.each_with_object({}) do |(name, _), zones| - zones[name] = self[name] + timezone = self[name] + zones[name] = timezone if timezone end end end |