diff options
Diffstat (limited to 'activesupport')
76 files changed, 467 insertions, 296 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index eaaa3df061..b05a068dc1 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,43 @@ +* `Cache#fetch` instrumentation marks whether it was a `:hit`. + + *Robin Clowers* + +* `assert_difference` and `assert_no_difference` now returns the result of the + yielded block. + + Example: + + post = assert_difference -> { Post.count }, 1 do + Post.create + end + + *Lucas Mazza* + +* Short-circuit `blank?` on date and time values since they are never blank. + + Fixes #21657 + + *Andrew White* + +* Replaced deprecated `ThreadSafe::Cache` with its successor `Concurrent::Map` now that + the thread_safe gem has been merged into concurrent-ruby. + + *Jerry D'Antonio* + +* Updated Unicode version to 8.0.0 + + *Anshul Sharma* + +* `number_to_currency` and `number_with_delimiter` now accept custom `delimiter_pattern` option + to handle placement of delimiter, to support currency formats like INR + + Example: + + number_to_currency(1230000, delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/, unit: '₹', format: "%u %n") + # => '₹ 12,30,000.00' + + *Vipul A M* + * Deprecate `:prefix` option of `number_to_human_size` with no replacement. *Jean Boussier* @@ -252,30 +292,24 @@ The preferred method to halt a callback chain from now on is to explicitly `throw(:abort)`. - In the past, returning `false` in an ActiveSupport callback had the side - effect of halting the callback chain. This is not recommended anymore and, - depending on the value of - `Callbacks::CallbackChain.halt_and_display_warning_on_return_false`, will - either not work at all or display a deprecation warning. + In the past, callbacks could only be halted by explicitly providing a + terminator and by having a callback match the conditions of the terminator. -* Add `Callbacks::CallbackChain.halt_and_display_warning_on_return_false` +* Add `ActiveSupport.halt_callback_chains_on_return_false` - Setting `Callbacks::CallbackChain.halt_and_display_warning_on_return_false` - to `true` will let an app support the deprecated way of halting callback - chains by returning `false`. + Setting `ActiveSupport.halt_callback_chains_on_return_false` + to `true` will let an app support the deprecated way of halting Active Record, + and Active Model callback chains by returning `false`. Setting the value to `false` will tell the app to ignore any `false` value - returned by callbacks, and only halt the chain upon `throw(:abort)`. - - The value can also be set with the Rails configuration option - `config.active_support.halt_callback_chains_on_return_false`. + returned by those callbacks, and only halt the chain upon `throw(:abort)`. When the configuration option is missing, its value is `true`, so older apps ported to Rails 5.0 will not break (but display a deprecation warning). For new Rails 5.0 apps, its value is set to `false` in an initializer, so these apps will support the new behavior by default. - *claudiob* + *claudiob*, *Roque Pinel* * Changes arguments and default value of CallbackChain's `:terminator` option diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec index c18c1c87c9..93878518d7 100644 --- a/activesupport/activesupport.gemspec +++ b/activesupport/activesupport.gemspec @@ -24,6 +24,6 @@ Gem::Specification.new do |s| s.add_dependency 'json', '~> 1.7', '>= 1.7.7' s.add_dependency 'tzinfo', '~> 1.1' s.add_dependency 'minitest', '~> 5.1' - s.add_dependency 'thread_safe','~> 0.3', '>= 0.3.4' - s.add_dependency 'concurrent-ruby', '~> 0.9.0' + s.add_dependency 'concurrent-ruby', '~> 1.0.0.pre3', '< 2.0.0' + s.add_dependency 'method_source' end diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 588d6c49f9..63277a65b4 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -75,11 +75,11 @@ module ActiveSupport cattr_accessor :test_order # :nodoc: def self.halt_callback_chains_on_return_false - Callbacks::CallbackChain.halt_and_display_warning_on_return_false + Callbacks.halt_and_display_warning_on_return_false end def self.halt_callback_chains_on_return_false=(value) - Callbacks::CallbackChain.halt_and_display_warning_on_return_false = value + Callbacks.halt_and_display_warning_on_return_false = value end end diff --git a/activesupport/lib/active_support/array_inquirer.rb b/activesupport/lib/active_support/array_inquirer.rb index e7188d7adb..f59ddf5403 100644 --- a/activesupport/lib/active_support/array_inquirer.rb +++ b/activesupport/lib/active_support/array_inquirer.rb @@ -23,7 +23,7 @@ module ActiveSupport super else candidates.any? do |candidate| - include?(candidate) || include?(candidate.to_sym) + include?(candidate.to_sym) || include?(candidate.to_s) end end end diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 837974bc85..3996f583c2 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -26,7 +26,7 @@ module ActiveSupport end class << self - # Creates a new CacheStore object according to the given options. + # Creates a new Store object according to the given options. # # If no arguments are passed to this method, then a new # ActiveSupport::Cache::MemoryStore object will be returned. @@ -277,13 +277,18 @@ module ActiveSupport options = merged_options(options) key = namespaced_key(name, options) - cached_entry = find_cached_entry(key, name, options) unless options[:force] - entry = handle_expired_entry(cached_entry, key, options) + instrument(:read, name, options) do |payload| + cached_entry = read_entry(key, options) unless options[:force] + payload[:super_operation] = :fetch if payload + entry = handle_expired_entry(cached_entry, key, options) - if entry - get_entry_value(entry, name, options) - else - save_block_result_to_cache(name, options) { |_name| yield _name } + if entry + payload[:hit] = true if payload + get_entry_value(entry, name, options) + else + payload[:hit] = false if payload + save_block_result_to_cache(name, options) { |_name| yield _name } + end end else read(name, options) @@ -556,13 +561,6 @@ module ActiveSupport logger.debug(yield) end - def find_cached_entry(key, name, options) - instrument(:read, name, options) do |payload| - payload[:super_operation] = :fetch if payload - read_entry(key, options) - end - end - def handle_expired_entry(entry, key, options) if entry && entry.expired? race_ttl = options[:race_condition_ttl].to_i diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index fefba5b0fd..252374e817 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -4,6 +4,7 @@ require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/kernel/singleton_class' +require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/string/filters' require 'active_support/deprecation' require 'thread' @@ -66,6 +67,12 @@ module ActiveSupport CALLBACK_FILTER_TYPES = [:before, :after, :around] + # If true, Active Record and Active Model callbacks returning +false+ will + # halt the entire callback chain and display a deprecation message. + # If false, callback chains will only be halted by calling +throw :abort+. + # Defaults to +true+. + mattr_accessor(:halt_and_display_warning_on_return_false) { true } + # Runs the callbacks for the given event. # # Calls the before and around callbacks in the order they were set, yields @@ -126,14 +133,10 @@ module ActiveSupport def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter) halted_lambda = chain_config[:terminator] - if chain_config.key?(:terminator) && user_conditions.any? + if user_conditions.any? halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter) - elsif chain_config.key? :terminator - halting(callback_sequence, user_callback, halted_lambda, filter) - elsif user_conditions.any? - conditional(callback_sequence, user_callback, user_conditions) else - simple callback_sequence, user_callback + halting(callback_sequence, user_callback, halted_lambda, filter) end end @@ -175,42 +178,15 @@ module ActiveSupport end end private_class_method :halting - - def self.conditional(callback_sequence, user_callback, user_conditions) - callback_sequence.before do |env| - target = env.target - value = env.value - - if user_conditions.all? { |c| c.call(target, value) } - user_callback.call target, value - end - - env - end - end - private_class_method :conditional - - def self.simple(callback_sequence, user_callback) - callback_sequence.before do |env| - user_callback.call env.target, env.value - - env - end - end - private_class_method :simple end class After def self.build(callback_sequence, user_callback, user_conditions, chain_config) if chain_config[:skip_after_callbacks_if_terminated] - if chain_config.key?(:terminator) && user_conditions.any? + if user_conditions.any? halting_and_conditional(callback_sequence, user_callback, user_conditions) - elsif chain_config.key?(:terminator) - halting(callback_sequence, user_callback) - elsif user_conditions.any? - conditional callback_sequence, user_callback, user_conditions else - simple callback_sequence, user_callback + halting(callback_sequence, user_callback) end else if user_conditions.any? @@ -273,14 +249,10 @@ module ActiveSupport class Around def self.build(callback_sequence, user_callback, user_conditions, chain_config) - if chain_config.key?(:terminator) && user_conditions.any? + if user_conditions.any? halting_and_conditional(callback_sequence, user_callback, user_conditions) - elsif chain_config.key? :terminator - halting(callback_sequence, user_callback) - elsif user_conditions.any? - conditional(callback_sequence, user_callback, user_conditions) else - simple(callback_sequence, user_callback) + halting(callback_sequence, user_callback) end end @@ -318,33 +290,6 @@ module ActiveSupport end end private_class_method :halting - - def self.conditional(callback_sequence, user_callback, user_conditions) - callback_sequence.around do |env, &run| - target = env.target - value = env.value - - if user_conditions.all? { |c| c.call(target, value) } - user_callback.call(target, value) { - run.call.value - } - env - else - run.call - end - end - end - private_class_method :conditional - - def self.simple(callback_sequence, user_callback) - callback_sequence.around do |env, &run| - user_callback.call(env.target, env.value) { - run.call.value - } - env - end - end - private_class_method :simple end end @@ -512,12 +457,6 @@ module ActiveSupport attr_reader :name, :config - # If true, any callback returning +false+ will halt the entire callback - # chain and display a deprecation message. If false, callback chains will - # only be halted by calling +throw :abort+. Defaults to +true+. - class_attribute :halt_and_display_warning_on_return_false - self.halt_and_display_warning_on_return_false = true - def initialize(name, config) @name = name @config = { @@ -598,23 +537,12 @@ module ActiveSupport Proc.new do |target, result_lambda| terminate = true catch(:abort) do - result = result_lambda.call if result_lambda.is_a?(Proc) - if halt_and_display_warning_on_return_false && result == false - display_deprecation_warning_for_false_terminator - else - terminate = false - end + result_lambda.call if result_lambda.is_a?(Proc) + terminate = false end terminate end end - - def display_deprecation_warning_for_false_terminator - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Returning `false` in a callback will not implicitly halt a callback chain in the next release of Rails. - To explicitly halt a callback chain, please use `throw :abort` instead. - MSG - end end module ClassMethods @@ -748,7 +676,8 @@ module ActiveSupport # # In this example, if any before validate callbacks returns +false+, # any successive before and around callback is not executed. - # Defaults to +false+, meaning no value halts the chain. + # + # The default terminator halts the chain when a callback throws +:abort+. # # * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after # callbacks should be terminated by the <tt>:terminator</tt> option. By @@ -826,6 +755,30 @@ module ActiveSupport def set_callbacks(name, callbacks) send "_#{name}_callbacks=", callbacks end + + def deprecated_false_terminator + Proc.new do |target, result_lambda| + terminate = true + catch(:abort) do + result = result_lambda.call if result_lambda.is_a?(Proc) + if Callbacks.halt_and_display_warning_on_return_false && result == false + display_deprecation_warning_for_false_terminator + else + terminate = false + end + end + terminate + end + end + + private + + def display_deprecation_warning_for_false_terminator + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Returning `false` in Active Record and Active Model callbacks will not implicitly halt a callback chain in the next release of Rails. + To explicitly halt the callback chain, please use `throw :abort` instead. + MSG + end end end end diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index d80df21e7d..8718b7e1e5 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -32,7 +32,7 @@ class Array # ['one', 'two', 'three'].to_sentence # => "one, two, and three" # # ['one', 'two'].to_sentence(passing: 'invalid option') - # # => ArgumentError: Unknown key :passing + # # => ArgumentError: Unknown key: :passing. Valid keys are: :words_connector, :two_words_connector, :last_word_connector, :locale # # ['one', 'two'].to_sentence(two_words_connector: '-') # # => "one-two" @@ -85,7 +85,9 @@ class Array # Extends <tt>Array#to_s</tt> to convert a collection of elements into a # comma separated id list if <tt>:db</tt> argument is given as the format. # - # Blog.all.to_formatted_s(:db) # => "1,2,3" + # Blog.all.to_formatted_s(:db) # => "1,2,3" + # Blog.none.to_formatted_s(:db) # => "null" + # [1,2].to_formatted_s # => "[1, 2]" def to_formatted_s(format = :default) case format when :db diff --git a/activesupport/lib/active_support/core_ext/date.rb b/activesupport/lib/active_support/core_ext/date.rb index 465fedda80..7f0f4639a2 100644 --- a/activesupport/lib/active_support/core_ext/date.rb +++ b/activesupport/lib/active_support/core_ext/date.rb @@ -1,5 +1,5 @@ require 'active_support/core_ext/date/acts_like' +require 'active_support/core_ext/date/blank' require 'active_support/core_ext/date/calculations' require 'active_support/core_ext/date/conversions' require 'active_support/core_ext/date/zones' - diff --git a/activesupport/lib/active_support/core_ext/date/blank.rb b/activesupport/lib/active_support/core_ext/date/blank.rb new file mode 100644 index 0000000000..71627b6a6f --- /dev/null +++ b/activesupport/lib/active_support/core_ext/date/blank.rb @@ -0,0 +1,12 @@ +require 'date' + +class Date #:nodoc: + # No Date is blank: + # + # Date.today.blank? # => false + # + # @return [false] + def blank? + false + end +end diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index c60e833441..d589b67bf7 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -26,7 +26,7 @@ class Date Thread.current[:beginning_of_week] = find_beginning_of_week!(week_start) end - # Returns week start day symbol (e.g. :monday), or raises an ArgumentError for invalid day symbol. + # Returns week start day symbol (e.g. :monday), or raises an +ArgumentError+ for invalid day symbol. def find_beginning_of_week!(week_start) raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.key?(week_start) week_start diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb index 31479a1269..ed8bca77ac 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -75,10 +75,10 @@ class Date # # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007 # - # date.to_time # => Sat Nov 10 00:00:00 0800 2007 - # date.to_time(:local) # => Sat Nov 10 00:00:00 0800 2007 + # date.to_time # => 2007-11-10 00:00:00 0800 + # date.to_time(:local) # => 2007-11-10 00:00:00 0800 # - # date.to_time(:utc) # => Sat Nov 10 00:00:00 UTC 2007 + # date.to_time(:utc) # => 2007-11-10 00:00:00 UTC def to_time(form = :local) ::Time.send(form, year, month, day) end 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 40811dafc0..e079af594d 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 @@ -135,7 +135,7 @@ module DateAndTime end alias :at_end_of_quarter :end_of_quarter - # Return a new date/time at the beginning of the year. + # Returns a new date/time at the beginning of the year. # # today = Date.today # => Fri, 10 Jul 2015 # today.beginning_of_year # => Thu, 01 Jan 2015 diff --git a/activesupport/lib/active_support/core_ext/date_and_time/zones.rb b/activesupport/lib/active_support/core_ext/date_and_time/zones.rb index 96c6df9407..d29a8db5cf 100644 --- a/activesupport/lib/active_support/core_ext/date_and_time/zones.rb +++ b/activesupport/lib/active_support/core_ext/date_and_time/zones.rb @@ -4,7 +4,7 @@ module DateAndTime # if Time.zone_default is set. Otherwise, it returns the current time. # # Time.zone = 'Hawaii' # => 'Hawaii' - # DateTime.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00 + # Time.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00 # Date.new(2000).in_time_zone # => Sat, 01 Jan 2000 00:00:00 HST -10:00 # # This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone @@ -14,7 +14,6 @@ module DateAndTime # and the conversion will be based on that zone instead of <tt>Time.zone</tt>. # # Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00 - # DateTime.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00 # Date.new(2000).in_time_zone('Alaska') # => Sat, 01 Jan 2000 00:00:00 AKST -09:00 def in_time_zone(zone = ::Time.zone) time_zone = ::Time.find_zone! zone diff --git a/activesupport/lib/active_support/core_ext/date_time.rb b/activesupport/lib/active_support/core_ext/date_time.rb index e8a27b9f38..bcb228b09a 100644 --- a/activesupport/lib/active_support/core_ext/date_time.rb +++ b/activesupport/lib/active_support/core_ext/date_time.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/date_time/acts_like' +require 'active_support/core_ext/date_time/blank' require 'active_support/core_ext/date_time/calculations' require 'active_support/core_ext/date_time/conversions' require 'active_support/core_ext/date_time/zones' diff --git a/activesupport/lib/active_support/core_ext/date_time/blank.rb b/activesupport/lib/active_support/core_ext/date_time/blank.rb new file mode 100644 index 0000000000..56981b75fb --- /dev/null +++ b/activesupport/lib/active_support/core_ext/date_time/blank.rb @@ -0,0 +1,12 @@ +require 'date' + +class DateTime #:nodoc: + # No DateTime is ever blank: + # + # DateTime.now.blank? # => false + # + # @return [false] + def blank? + false + end +end diff --git a/activesupport/lib/active_support/core_ext/date_time/conversions.rb b/activesupport/lib/active_support/core_ext/date_time/conversions.rb index 2a9c09fc29..f59d05b214 100644 --- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb @@ -40,6 +40,8 @@ class DateTime alias_method :to_default_s, :to_s if instance_methods(false).include?(:to_s) alias_method :to_s, :to_formatted_s + # Returns a formatted string of the offset from UTC, or an alternative + # string if the time zone is already UTC. # # datetime = DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-6, 24)) # datetime.formatted_offset # => "-06:00" diff --git a/activesupport/lib/active_support/core_ext/file/atomic.rb b/activesupport/lib/active_support/core_ext/file/atomic.rb index fad6fa8d9d..463fd78412 100644 --- a/activesupport/lib/active_support/core_ext/file/atomic.rb +++ b/activesupport/lib/active_support/core_ext/file/atomic.rb @@ -8,43 +8,45 @@ class File # file.write('hello') # end # - # If your temp directory is not on the same filesystem as the file you're - # trying to write, you can provide a different temporary directory. + # This method needs to create a temporary file. By default it will create it + # in the same directory as the destination file. If you don't like this + # behavior you can provide a different directory but it must be on the + # same physical filesystem as the file you're trying to write. # # File.atomic_write('/data/something.important', '/data/tmp') do |file| # file.write('hello') # end - def self.atomic_write(file_name, temp_dir = Dir.tmpdir) + def self.atomic_write(file_name, temp_dir = dirname(file_name)) require 'tempfile' unless defined?(Tempfile) - require 'fileutils' unless defined?(FileUtils) - temp_file = Tempfile.new(basename(file_name), temp_dir) - temp_file.binmode - return_val = yield temp_file - temp_file.close + Tempfile.open(".#{basename(file_name)}", temp_dir) do |temp_file| + temp_file.binmode + return_val = yield temp_file + temp_file.close - if File.exist?(file_name) - # Get original file permissions - old_stat = stat(file_name) - else - # If not possible, probe which are the default permissions in the - # destination directory. - old_stat = probe_stat_in(dirname(file_name)) - end - - # Overwrite original file with temp file - FileUtils.mv(temp_file.path, file_name) + old_stat = if exist?(file_name) + # Get original file permissions + stat(file_name) + elsif temp_dir != dirname(file_name) + # If not possible, probe which are the default permissions in the + # destination directory. + probe_stat_in(dirname(file_name)) + end - # Set correct permissions on new file - begin - chown(old_stat.uid, old_stat.gid, file_name) - # This operation will affect filesystem ACL's - chmod(old_stat.mode, file_name) + if old_stat + # Set correct permissions on new file + begin + chown(old_stat.uid, old_stat.gid, temp_file.path) + # This operation will affect filesystem ACL's + chmod(old_stat.mode, temp_file.path) + rescue Errno::EPERM, Errno::EACCES + # Changing file ownership failed, moving on. + end + end - # Make sure we return the result of the yielded block + # Overwrite original file with temp file + rename(temp_file.path, file_name) return_val - rescue Errno::EPERM, Errno::EACCES - # Changing file ownership failed, moving on. end end diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb index c30044b9ff..07a282e8b6 100644 --- a/activesupport/lib/active_support/core_ext/hash/keys.rb +++ b/activesupport/lib/active_support/core_ext/hash/keys.rb @@ -1,10 +1,14 @@ class Hash - # Returns a new hash with all keys converted using the block operation. + # Returns a new hash with all keys converted using the +block+ operation. # # hash = { name: 'Rob', age: '28' } # - # hash.transform_keys{ |key| key.to_s.upcase } - # # => {"NAME"=>"Rob", "AGE"=>"28"} + # hash.transform_keys { |key| key.to_s.upcase } # => {"NAME"=>"Rob", "AGE"=>"28"} + # + # If you do not provide a +block+, it will return an Enumerator + # for chaining with other methods: + # + # hash.transform_keys.with_index { |k, i| [k, i].join } # => {"name0"=>"Rob", "age1"=>"28"} def transform_keys return enum_for(:transform_keys) unless block_given? result = self.class.new @@ -14,8 +18,8 @@ class Hash result end - # Destructively converts all keys using the block operations. - # Same as transform_keys but modifies +self+. + # Destructively converts all keys using the +block+ operations. + # Same as +transform_keys+ but modifies +self+. def transform_keys! return enum_for(:transform_keys!) unless block_given? keys.each do |key| @@ -60,7 +64,7 @@ class Hash alias_method :to_options!, :symbolize_keys! # Validates all keys in a hash match <tt>*valid_keys</tt>, raising - # ArgumentError on a mismatch. + # +ArgumentError+ on a mismatch. # # Note that keys are treated differently than HashWithIndifferentAccess, # meaning that string and symbol keys will not match. diff --git a/activesupport/lib/active_support/core_ext/hash/transform_values.rb b/activesupport/lib/active_support/core_ext/hash/transform_values.rb index e9bcce761f..9ddb838774 100644 --- a/activesupport/lib/active_support/core_ext/hash/transform_values.rb +++ b/activesupport/lib/active_support/core_ext/hash/transform_values.rb @@ -2,10 +2,15 @@ class Hash # Returns a new hash with the results of running +block+ once for every value. # The keys are unchanged. # - # { a: 1, b: 2, c: 3 }.transform_values { |x| x * 2 } - # # => { a: 2, b: 4, c: 6 } + # { a: 1, b: 2, c: 3 }.transform_values { |x| x * 2 } # => { a: 2, b: 4, c: 6 } + # + # If you do not provide a +block+, it will return an Enumerator + # for chaining with other methods: + # + # { a: 1, b: 2 }.transform_values.with_index { |v, i| [v, i].join.to_i } # => { a: 10, b: 21 } def transform_values return enum_for(:transform_values) unless block_given? + return {} if empty? result = self.class.new each do |key, value| result[key] = yield(value) @@ -13,7 +18,8 @@ class Hash result end - # Destructive +transform_values+ + # Destructively converts all values using the +block+ operations. + # Same as +transform_values+ but modifies +self+. def transform_values! return enum_for(:transform_values!) unless block_given? each do |key, value| diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb index 9189e6d977..8afc258df8 100644 --- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb +++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb @@ -1,5 +1,3 @@ -require 'tempfile' - module Kernel # Sets $VERBOSE to nil for the duration of the block and back to its original # value afterwards. diff --git a/activesupport/lib/active_support/core_ext/marshal.rb b/activesupport/lib/active_support/core_ext/marshal.rb index 20a0856e71..e333b26133 100644 --- a/activesupport/lib/active_support/core_ext/marshal.rb +++ b/activesupport/lib/active_support/core_ext/marshal.rb @@ -6,7 +6,7 @@ module ActiveSupport if exc.message.match(%r|undefined class/module (.+)|) # try loading the class/module $1.constantize - # if it is a IO we need to go back to read the object + # if it is an IO we need to go back to read the object source.rewind if source.respond_to?(:rewind) retry else 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 a77da573fe..bf175a8a70 100644 --- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb @@ -19,9 +19,9 @@ class Module # The attribute name must be a valid method name in Ruby. # # module Foo - # mattr_reader :"1_Badname " + # mattr_reader :"1_Badname" # end - # # => NameError: invalid attribute name + # # => NameError: invalid attribute name: 1_Badname # # If you want to opt out the creation on the instance reader method, pass # <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>. @@ -53,7 +53,7 @@ class Module def mattr_reader(*syms) options = syms.extract_options! syms.each do |sym| - raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/ + raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /\A[_A-Za-z]\w*\z/ class_eval(<<-EOS, __FILE__, __LINE__ + 1) @@#{sym} = nil unless defined? @@#{sym} @@ -119,7 +119,7 @@ class Module def mattr_writer(*syms) options = syms.extract_options! syms.each do |sym| - raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/ + raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /\A[_A-Za-z]\w*\z/ class_eval(<<-EOS, __FILE__, __LINE__ + 1) @@#{sym} = nil unless defined? @@#{sym} diff --git a/activesupport/lib/active_support/core_ext/numeric/conversions.rb b/activesupport/lib/active_support/core_ext/numeric/conversions.rb index 0c8ff79237..d5bb11deed 100644 --- a/activesupport/lib/active_support/core_ext/numeric/conversions.rb +++ b/activesupport/lib/active_support/core_ext/numeric/conversions.rb @@ -41,7 +41,7 @@ class Numeric # 1000.to_s(:percentage, delimiter: '.', separator: ',') # => 1.000,000% # 302.24398923423.to_s(:percentage, precision: 5) # => 302.24399% # 1000.to_s(:percentage, locale: :fr) # => 1 000,000% - # 100.to_s(:percentage, format: '%n %') # => 100 % + # 100.to_s(:percentage, format: '%n %') # => 100.000 % # # Delimited: # 12345678.to_s(:delimited) # => 12,345,678 @@ -78,7 +78,7 @@ class Numeric # 1234567.to_s(:human_size, precision: 2) # => 1.2 MB # 483989.to_s(:human_size, precision: 2) # => 470 KB # 1234567.to_s(:human_size, precision: 2, separator: ',') # => 1,2 MB - # 1234567890123.to_s(:human_size, precision: 5) # => "1.1229 TB" + # 1234567890123.to_s(:human_size, precision: 5) # => "1.1228 TB" # 524288000.to_s(:human_size, precision: 5) # => "500 MB" # # Human-friendly format: diff --git a/activesupport/lib/active_support/core_ext/object/blank.rb b/activesupport/lib/active_support/core_ext/object/blank.rb index 38e43478df..039c50a4a2 100644 --- a/activesupport/lib/active_support/core_ext/object/blank.rb +++ b/activesupport/lib/active_support/core_ext/object/blank.rb @@ -1,12 +1,10 @@ -# encoding: utf-8 - class Object # An object is blank if it's false, empty, or a whitespace string. - # For example, '', ' ', +nil+, [], and {} are all blank. + # For example, +false+, '', ' ', +nil+, [], and {} are all blank. # # This simplifies # - # address.nil? || address.empty? + # !address || address.empty? # # to # @@ -129,3 +127,14 @@ class Numeric #:nodoc: false end end + +class Time #:nodoc: + # No Time is blank: + # + # Time.now.blank? # => false + # + # @return [false] + def blank? + false + end +end diff --git a/activesupport/lib/active_support/core_ext/object/inclusion.rb b/activesupport/lib/active_support/core_ext/object/inclusion.rb index 55f281b213..d4c17dfb07 100644 --- a/activesupport/lib/active_support/core_ext/object/inclusion.rb +++ b/activesupport/lib/active_support/core_ext/object/inclusion.rb @@ -5,7 +5,7 @@ class Object # characters = ["Konata", "Kagami", "Tsukasa"] # "Konata".in?(characters) # => true # - # This will throw an ArgumentError if the argument doesn't respond + # This will throw an +ArgumentError+ if the argument doesn't respond # to +#include?+. def in?(another_object) another_object.include?(self) @@ -18,7 +18,7 @@ class Object # # params[:bucket_type].presence_in %w( project calendar ) # - # This will throw an ArgumentError if the argument doesn't respond to +#include?+. + # This will throw an +ArgumentError+ if the argument doesn't respond to +#include?+. # # @return [Object] def presence_in(another_object) diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb index 69be6c4abc..8c16d95b62 100644 --- a/activesupport/lib/active_support/core_ext/object/try.rb +++ b/activesupport/lib/active_support/core_ext/object/try.rb @@ -8,7 +8,7 @@ module ActiveSupport def try!(*a, &b) if a.empty? && block_given? - if b.arity.zero? + if b.arity == 0 instance_eval(&b) else yield self @@ -94,7 +94,7 @@ class Object # :call-seq: # try!(*a, &b) # - # Same as #try, but raises a NoMethodError exception if the receiver is + # Same as #try, but raises a +NoMethodError+ exception if the receiver is # not +nil+ and does not implement the tried method. # # "a".try!(:upcase) # => "A" diff --git a/activesupport/lib/active_support/core_ext/securerandom.rb b/activesupport/lib/active_support/core_ext/securerandom.rb index 6cdbea1f37..98cf7430f7 100644 --- a/activesupport/lib/active_support/core_ext/securerandom.rb +++ b/activesupport/lib/active_support/core_ext/securerandom.rb @@ -10,8 +10,8 @@ module SecureRandom # # The result may contain alphanumeric characters except 0, O, I and l # - # p SecureRandom.base58 #=> "4kUgL2pdQMSCQtjE" - # p SecureRandom.base58(24) #=> "77TMHrHJFvFDwodq8w7Ev2m7" + # p SecureRandom.base58 # => "4kUgL2pdQMSCQtjE" + # p SecureRandom.base58(24) # => "77TMHrHJFvFDwodq8w7Ev2m7" # def self.base58(n = 16) SecureRandom.random_bytes(n).unpack("C*").map do |byte| diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb index 3e0cb8a7ac..fd79a40e31 100644 --- a/activesupport/lib/active_support/core_ext/string/conversions.rb +++ b/activesupport/lib/active_support/core_ext/string/conversions.rb @@ -14,7 +14,7 @@ class String # "06:12".to_time # => 2012-12-13 06:12:00 +0100 # "2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 +0100 # "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0100 - # "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 05:12:00 UTC + # "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 06:12:00 UTC # "12/13/2012".to_time # => ArgumentError: argument out of range def to_time(form = :local) parts = Date._parse(self, false) diff --git a/activesupport/lib/active_support/core_ext/string/multibyte.rb b/activesupport/lib/active_support/core_ext/string/multibyte.rb index 7055f7f699..cc6f2158e7 100644 --- a/activesupport/lib/active_support/core_ext/string/multibyte.rb +++ b/activesupport/lib/active_support/core_ext/string/multibyte.rb @@ -9,12 +9,10 @@ class String # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string. # - # name = 'Claus Müller' - # name.reverse # => "rell??M sualC" - # name.length # => 13 - # - # name.mb_chars.reverse.to_s # => "rellüM sualC" - # name.mb_chars.length # => 12 + # >> "lj".upcase + # => "lj" + # >> "lj".mb_chars.upcase.to_s + # => "LJ" # # == Method chaining # diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index c676b26b06..8b27ec4413 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -86,7 +86,7 @@ class ERB # use inside HTML attributes. # # If your JSON is being used downstream for insertion into the DOM, be aware of - # whether or not it is being inserted via +html()+. Most JQuery plugins do this. + # whether or not it is being inserted via +html()+. Most jQuery plugins do this. # If that is the case, be sure to +html_escape+ or +sanitize+ any user-generated # content returned by your JSON. # diff --git a/activesupport/lib/active_support/core_ext/string/strip.rb b/activesupport/lib/active_support/core_ext/string/strip.rb index 086c610976..55b9b87352 100644 --- a/activesupport/lib/active_support/core_ext/string/strip.rb +++ b/activesupport/lib/active_support/core_ext/string/strip.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/object/try' - class String # Strips indentation in heredocs. # @@ -17,10 +15,9 @@ class String # # the user would see the usage message aligned against the left margin. # - # Technically, it looks for the least indented line in the whole string, and removes - # that amount of leading whitespace. + # Technically, it looks for the least indented non-empty line + # in the whole string, and removes that amount of leading whitespace. def strip_heredoc - indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0 - gsub(/^[ \t]{#{indent}}/, '') + gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, ''.freeze) end end diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 96156deebb..82e003fc3b 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -3,6 +3,7 @@ require 'active_support/core_ext/time/conversions' require 'active_support/time_with_zone' require 'active_support/core_ext/time/zones' require 'active_support/core_ext/date_and_time/calculations' +require 'active_support/core_ext/date/calculations' class Time include DateAndTime::Calculations @@ -15,9 +16,9 @@ class Time super || (self == Time && other.is_a?(ActiveSupport::TimeWithZone)) end - # Return the number of days in the given month. + # Returns the number of days in the given month. # If no year is specified, it will use the current year. - def days_in_month(month, year = now.year) + def days_in_month(month, year = current.year) if month == 2 && ::Date.gregorian_leap?(year) 29 else @@ -50,9 +51,9 @@ class Time # Returns the number of seconds since 00:00:00. # - # Time.new(2012, 8, 29, 0, 0, 0).seconds_since_midnight # => 0 - # Time.new(2012, 8, 29, 12, 34, 56).seconds_since_midnight # => 45296 - # Time.new(2012, 8, 29, 23, 59, 59).seconds_since_midnight # => 86399 + # Time.new(2012, 8, 29, 0, 0, 0).seconds_since_midnight # => 0.0 + # Time.new(2012, 8, 29, 12, 34, 56).seconds_since_midnight # => 45296.0 + # Time.new(2012, 8, 29, 23, 59, 59).seconds_since_midnight # => 86399.0 def seconds_since_midnight to_i - change(:hour => 0).to_i + (usec / 1.0e+6) end diff --git a/activesupport/lib/active_support/core_ext/time/conversions.rb b/activesupport/lib/active_support/core_ext/time/conversions.rb index dbf1f2f373..eecbac2c20 100644 --- a/activesupport/lib/active_support/core_ext/time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/time/conversions.rb @@ -24,7 +24,7 @@ class Time # # This method is aliased to <tt>to_s</tt>. # - # time = Time.now # => Thu Jan 18 06:10:17 CST 2007 + # time = Time.now # => 2007-01-18 06:10:17 -06:00 # # time.to_formatted_s(:time) # => "06:10" # time.to_s(:time) # => "06:10" @@ -55,7 +55,8 @@ class Time alias_method :to_default_s, :to_s alias_method :to_s, :to_formatted_s - # Returns the UTC offset as an +HH:MM formatted string. + # Returns a formatted string of the offset from UTC, or an alternative + # string if the time zone is already UTC. # # Time.local(2000).formatted_offset # => "-06:00" # Time.local(2000).formatted_offset(false) # => "-0600" diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb index 133d3938eb..877dc84ec8 100644 --- a/activesupport/lib/active_support/core_ext/time/zones.rb +++ b/activesupport/lib/active_support/core_ext/time/zones.rb @@ -53,7 +53,7 @@ class Time # Returns a TimeZone instance matching the time zone provided. # Accepts the time zone in any format supported by <tt>Time.zone=</tt>. - # Raises an ArgumentError for invalid time zones. + # Raises an +ArgumentError+ for invalid time zones. # # Time.find_zone! "America/New_York" # => #<ActiveSupport::TimeZone @name="America/New_York" ...> # Time.find_zone! "EST" # => #<ActiveSupport::TimeZone @name="EST" ...> diff --git a/activesupport/lib/active_support/core_ext/uri.rb b/activesupport/lib/active_support/core_ext/uri.rb index 0b2ff817c3..c6c183edd9 100644 --- a/activesupport/lib/active_support/core_ext/uri.rb +++ b/activesupport/lib/active_support/core_ext/uri.rb @@ -1,5 +1,3 @@ -# encoding: utf-8 - require 'uri' str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese. parser = URI::Parser.new diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index f76ef04f49..16b726bcba 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -1,6 +1,6 @@ require 'set' require 'thread' -require 'thread_safe' +require 'concurrent' require 'pathname' require 'active_support/core_ext/module/aliasing' require 'active_support/core_ext/module/attribute_accessors' @@ -421,13 +421,13 @@ module ActiveSupport #:nodoc: bases.each do |root| expanded_root = File.expand_path(root) - next unless %r{\A#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path + next unless expanded_path.start_with?(expanded_root) - nesting = expanded_path[(expanded_root.size)..-1] - nesting = nesting[1..-1] if nesting && nesting[0] == ?/ - next if nesting.blank? + root_size = expanded_root.size + next if expanded_path[root_size] != ?/.freeze - paths << nesting.camelize + nesting = expanded_path[(root_size + 1)..-1] + paths << nesting.camelize unless nesting.blank? end paths.uniq! @@ -585,7 +585,7 @@ module ActiveSupport #:nodoc: class ClassCache def initialize - @store = ThreadSafe::Cache.new + @store = Concurrent::Map.new end def empty? diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb index 9b264cbb79..6f0ad445fc 100644 --- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -20,7 +20,7 @@ module ActiveSupport private def method_missing(called, *args, &block) - warn caller, called, args + warn caller_locations, called, args target.__send__(called, *args, &block) end end @@ -111,7 +111,7 @@ module ActiveSupport # # PLANETS = %w(mercury venus earth mars jupiter saturn uranus neptune pluto) # - # (In a later update, the orignal implementation of `PLANETS` has been removed.) + # (In a later update, the original implementation of `PLANETS` has been removed.) # # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune) # PLANETS = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('PLANETS', 'PLANETS_POST_2006') diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb index a7d265d732..bbe25c9260 100644 --- a/activesupport/lib/active_support/deprecation/reporting.rb +++ b/activesupport/lib/active_support/deprecation/reporting.rb @@ -14,7 +14,7 @@ module ActiveSupport def warn(message = nil, callstack = nil) return if silenced - callstack ||= caller(2) + callstack ||= caller_locations(2) deprecation_message(callstack, message).tap do |m| behavior.each { |b| b.call(m, callstack) } end @@ -37,7 +37,7 @@ module ActiveSupport end def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil) - caller_backtrace ||= caller(2) + caller_backtrace ||= caller_locations(2) deprecated_method_warning(deprecated_method_name, message).tap do |msg| warn(msg, caller_backtrace) end @@ -79,6 +79,17 @@ module ActiveSupport end def extract_callstack(callstack) + return _extract_callstack(callstack) if callstack.first.is_a? String + + rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/" + offending_line = callstack.find { |frame| + !frame.absolute_path.start_with?(rails_gem_root) + } || callstack.first + [offending_line.path, offending_line.lineno, offending_line.label] + end + + def _extract_callstack(callstack) + warn "Please pass `caller_locations` to the deprecation API" if $VERBOSE rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/" offending_line = callstack.find { |line| !line.start_with?(rails_gem_root) } || callstack.first if offending_line diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb index 42560f3515..c3907e9c22 100644 --- a/activesupport/lib/active_support/inflector/inflections.rb +++ b/activesupport/lib/active_support/inflector/inflections.rb @@ -1,4 +1,4 @@ -require 'thread_safe' +require 'concurrent' require 'active_support/core_ext/array/prepend_and_append' require 'active_support/i18n' @@ -25,7 +25,7 @@ module ActiveSupport # singularization rules that is runs. This guarantees that your rules run # before any of the rules that may already have been loaded. class Inflections - @__instance__ = ThreadSafe::Cache.new + @__instance__ = Concurrent::Map.new class Uncountables < Array def initialize diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index ab5372ac43..595b0339cc 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -1,5 +1,3 @@ -# encoding: utf-8 - require 'active_support/inflections' module ActiveSupport diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb index 7b28eeb6e2..103207fb63 100644 --- a/activesupport/lib/active_support/inflector/transliterate.rb +++ b/activesupport/lib/active_support/inflector/transliterate.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 require 'active_support/core_ext/string/multibyte' require 'active_support/i18n' @@ -70,24 +69,27 @@ module ActiveSupport # parameterize("Donald E. Knuth") # => "donald-e-knuth" # parameterize("^trés|Jolie-- ") # => "tres-jolie" def parameterize(string, sep = '-') - # replace accented chars with their ascii equivalents + # Replace accented chars with their ASCII equivalents. parameterized_string = transliterate(string) - # Turn unwanted chars into the separator + + # Turn unwanted chars into the separator. parameterized_string.gsub!(/[^a-z0-9\-_]+/i, sep) + unless sep.nil? || sep.empty? if sep == "-".freeze - re_duplicate_seperator = /-{2,}/ + re_duplicate_separator = /-{2,}/ re_leading_trailing_separator = /^-|-$/i else re_sep = Regexp.escape(sep) - re_duplicate_seperator = /#{re_sep}{2,}/ + re_duplicate_separator = /#{re_sep}{2,}/ re_leading_trailing_separator = /^#{re_sep}|#{re_sep}$/i end # No more than one of the separator in a row. - parameterized_string.gsub!(re_duplicate_seperator, sep) + parameterized_string.gsub!(re_duplicate_separator, sep) # Remove leading/trailing separator. parameterized_string.gsub!(re_leading_trailing_separator, ''.freeze) end + parameterized_string.downcase! parameterized_string end diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb index 51d2da3a79..6bc3db6ec6 100644 --- a/activesupport/lib/active_support/key_generator.rb +++ b/activesupport/lib/active_support/key_generator.rb @@ -1,4 +1,4 @@ -require 'thread_safe' +require 'concurrent' require 'openssl' module ActiveSupport @@ -28,7 +28,7 @@ module ActiveSupport class CachingKeyGenerator def initialize(key_generator) @key_generator = key_generator - @cache_keys = ThreadSafe::Cache.new + @cache_keys = Concurrent::Map.new end # Returns a derived key suitable for use. The default key_size is chosen diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb index e95dc5a866..e782cd2d4b 100644 --- a/activesupport/lib/active_support/log_subscriber.rb +++ b/activesupport/lib/active_support/log_subscriber.rb @@ -95,7 +95,7 @@ module ActiveSupport METHOD end - # Set color by using a string or one of the defined constants. If a third + # Set color by using a symbol or one of the defined constants. If a third # option is set to +true+, it also adds bold to the string. This is based # on the Highline implementation and will automatically append CLEAR to the # end of the returned String. diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb index 70ac4a4d5c..cbc20c103d 100644 --- a/activesupport/lib/active_support/log_subscriber/test_helper.rb +++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb @@ -11,6 +11,7 @@ module ActiveSupport # include ActiveSupport::LogSubscriber::TestHelper # # def setup + # super # ActiveRecord::LogSubscriber.attach_to(:active_record) # end # diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 45cf6fc1ef..f6a2e7e949 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 require 'active_support/json' require 'active_support/core_ext/string/access' require 'active_support/core_ext/string/behavior' diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb index b6c991a287..586002b03b 100644 --- a/activesupport/lib/active_support/multibyte/unicode.rb +++ b/activesupport/lib/active_support/multibyte/unicode.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 module ActiveSupport module Multibyte module Unicode @@ -11,7 +10,7 @@ module ActiveSupport NORMALIZATION_FORMS = [:c, :kc, :d, :kd] # The Unicode version that is supported by the implementation - UNICODE_VERSION = '7.0.0' + UNICODE_VERSION = '8.0.0' # The default normalization used for operations that require # normalization. It can be set to any of the normalizations @@ -257,7 +256,7 @@ module ActiveSupport # * <tt>string</tt> - The string to perform normalization on. # * <tt>form</tt> - The form you want to normalize in. Should be one of # the following: <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. - # Default is ActiveSupport::Multibyte.default_normalization_form. + # Default is ActiveSupport::Multibyte::Unicode.default_normalization_form. def normalize(string, form=nil) form ||= @default_normalization_form # See http://www.unicode.org/reports/tr15, Table 1 diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb index b9f8e1ab2c..823d68e507 100644 --- a/activesupport/lib/active_support/notifications.rb +++ b/activesupport/lib/active_support/notifications.rb @@ -69,8 +69,8 @@ module ActiveSupport # is able to take the arguments as they come and provide an object-oriented # interface to that data. # - # It is also possible to pass an object as the second parameter passed to the - # <tt>subscribe</tt> method instead of a block: + # It is also possible to pass an object which responds to <tt>call</tt> method + # as the second parameter to the <tt>subscribe</tt> method instead of a block: # # module ActionController # class PageRequest diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index 0131fe2572..71354dd15f 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -1,5 +1,5 @@ require 'mutex_m' -require 'thread_safe' +require 'concurrent' module ActiveSupport module Notifications @@ -12,7 +12,7 @@ module ActiveSupport def initialize @subscribers = [] - @listeners_for = ThreadSafe::Cache.new + @listeners_for = Concurrent::Map.new super end @@ -51,7 +51,7 @@ module ActiveSupport end def listeners_for(name) - # this is correctly done double-checked locking (ThreadSafe::Cache's lookups have volatile semantics) + # this is correctly done double-checked locking (Concurrent::Map's lookups have volatile semantics) @listeners_for[name] || synchronize do # use synchronisation when accessing @subscribers @listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) } diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb index 38a9ce361d..504f96961a 100644 --- a/activesupport/lib/active_support/number_helper.rb +++ b/activesupport/lib/active_support/number_helper.rb @@ -118,7 +118,7 @@ module ActiveSupport # number_to_percentage(1000, locale: :fr) # => 1 000,000% # number_to_percentage:(1000, precision: nil) # => 1000% # number_to_percentage('98a') # => 98a% - # number_to_percentage(100, format: '%n %') # => 100 % + # number_to_percentage(100, format: '%n %') # => 100.000 % def number_to_percentage(number, options = {}) NumberToPercentageConverter.convert(number, options) end @@ -135,6 +135,9 @@ module ActiveSupport # to ","). # * <tt>:separator</tt> - Sets the separator between the # fractional and integer digits (defaults to "."). + # * <tt>:delimiter_pattern</tt> - Sets a custom regular expression used for + # deriving the placement of delimiter. Helpful when using currency formats + # like INR. # # ==== Examples # @@ -147,7 +150,10 @@ module ActiveSupport # number_to_delimited(12345678.05, locale: :fr) # => 12 345 678,05 # number_to_delimited('112a') # => 112a # number_to_delimited(98765432.98, delimiter: ' ', separator: ',') - # # => 98 765 432,98 + # # => 98 765 432,98 + # number_to_delimited("123456.78", + # delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/) + # # => 1,23,456.78 def number_to_delimited(number, options = {}) NumberToDelimitedConverter.convert(number, options) end diff --git a/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb b/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb index d85cc086d7..45ae8f1a93 100644 --- a/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb +++ b/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb @@ -3,7 +3,7 @@ module ActiveSupport class NumberToDelimitedConverter < NumberConverter #:nodoc: self.validate_float = true - DELIMITED_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/ + DEFAULT_DELIMITER_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/ def convert parts.join(options[:separator]) @@ -13,11 +13,16 @@ module ActiveSupport def parts left, right = number.to_s.split('.') - left.gsub!(DELIMITED_REGEX) do |digit_to_delimit| + left.gsub!(delimiter_pattern) do |digit_to_delimit| "#{digit_to_delimit}#{options[:delimiter]}" end [left, right].compact end + + def delimiter_pattern + options.fetch(:delimiter_pattern, DEFAULT_DELIMITER_REGEX) + end + end end end diff --git a/activesupport/lib/active_support/per_thread_registry.rb b/activesupport/lib/active_support/per_thread_registry.rb index ca2e4d5625..506dd950cb 100644 --- a/activesupport/lib/active_support/per_thread_registry.rb +++ b/activesupport/lib/active_support/per_thread_registry.rb @@ -43,9 +43,9 @@ module ActiveSupport protected def method_missing(name, *args, &block) # :nodoc: # Caches the method definition as a singleton method of the receiver. - define_singleton_method(name) do |*a, &b| - instance.public_send(name, *a, &b) - end + # + # By letting #delegate handle it, we avoid an enclosure that'll capture args. + singleton_class.delegate name, to: :instance send(name, *args, &block) end diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb index d87ce3474d..ae8c15d8bf 100644 --- a/activesupport/lib/active_support/testing/assertions.rb +++ b/activesupport/lib/active_support/testing/assertions.rb @@ -68,13 +68,15 @@ module ActiveSupport } before = exps.map(&:call) - yield + retval = yield expressions.zip(exps).each_with_index do |(code, e), i| error = "#{code.inspect} didn't change by #{difference}" error = "#{message}.\n#{error}" if message assert_equal(before[i] + difference, e.call, error) end + + retval end # Assertion that the numeric result of evaluating an expression is not diff --git a/activesupport/lib/active_support/testing/file_fixtures.rb b/activesupport/lib/active_support/testing/file_fixtures.rb index 4c6a0801b8..affb84cda5 100644 --- a/activesupport/lib/active_support/testing/file_fixtures.rb +++ b/activesupport/lib/active_support/testing/file_fixtures.rb @@ -18,7 +18,7 @@ module ActiveSupport # Returns a +Pathname+ to the fixture file named +fixture_name+. # - # Raises ArgumentError if +fixture_name+ can't be found. + # Raises +ArgumentError+ if +fixture_name+ can't be found. def file_fixture(fixture_name) path = Pathname.new(File.join(file_fixture_path, fixture_name)) diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb index 1de0a19998..edf8b30a0a 100644 --- a/activesupport/lib/active_support/testing/isolation.rb +++ b/activesupport/lib/active_support/testing/isolation.rb @@ -41,7 +41,23 @@ module ActiveSupport pid = fork do read.close yield - write.puts [Marshal.dump(self.dup)].pack("m") + begin + if error? + failures.map! { |e| + begin + Marshal.dump e + e + rescue TypeError + ex = Exception.new e.message + ex.set_backtrace e.backtrace + Minitest::UnexpectedError.new ex + end + } + end + result = Marshal.dump(self.dup) + end + + write.puts [result].pack("m") exit! end diff --git a/activesupport/lib/active_support/testing/method_call_assertions.rb b/activesupport/lib/active_support/testing/method_call_assertions.rb index 517f02e3ef..fccaa54f40 100644 --- a/activesupport/lib/active_support/testing/method_call_assertions.rb +++ b/activesupport/lib/active_support/testing/method_call_assertions.rb @@ -1,11 +1,13 @@ +require 'minitest/mock' + module ActiveSupport module Testing module MethodCallAssertions # :nodoc: private - def assert_called(object, method_name, message = nil, times: 1) + def assert_called(object, method_name, message = nil, times: 1, returns: nil) times_called = 0 - object.stub(method_name, -> { times_called += 1 }) { yield } + object.stub(method_name, proc { times_called += 1; returns }) { yield } error = "Expected #{method_name} to be called #{times} times, " \ "but was called #{times_called} times" diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index f8f1b9ac2c..3592dcba39 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -14,7 +14,7 @@ module ActiveSupport # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)' # Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45 EST -05:00 # Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45 EST -05:00 - # Time.zone.at(1170361845) # => Sat, 10 Feb 2007 15:30:45 EST -05:00 + # Time.zone.at(1171139445) # => Sat, 10 Feb 2007 15:30:45 EST -05:00 # Time.zone.now # => Sun, 18 May 2008 13:07:55 EDT -04:00 # Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45 EST -05:00 # @@ -41,6 +41,9 @@ module ActiveSupport 'Time' end + PRECISIONS = Hash.new { |h, n| h[n] = "%FT%T.%#{n}N".freeze } + PRECISIONS[0] = '%FT%T'.freeze + include Comparable attr_reader :time_zone @@ -132,7 +135,7 @@ module ActiveSupport # Returns a string of the object's date, time, zone and offset from UTC. # - # Time.zone.now.httpdate # => "Thu, 04 Dec 2014 11:00:25 EST -05:00" + # Time.zone.now.inspect # => "Thu, 04 Dec 2014 11:00:25 EST -05:00" def inspect "#{time.strftime('%a, %d %b %Y %H:%M:%S')} #{zone} #{formatted_offset}" end @@ -142,11 +145,7 @@ module ActiveSupport # # Time.zone.now.xmlschema # => "2014-12-04T11:02:37-05:00" def xmlschema(fraction_digits = 0) - fraction = if fraction_digits.to_i > 0 - (".%06i" % time.usec)[0, fraction_digits.to_i + 1] - end - - "#{time.strftime("%Y-%m-%dT%H:%M:%S")}#{fraction}#{formatted_offset(true, 'Z')}" + "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z'.freeze)}" end alias_method :iso8601, :xmlschema @@ -246,6 +245,7 @@ module ActiveSupport utc.future? end + # Returns +true+ if +other+ is equal to current object. def eql?(other) other.eql?(utc) end @@ -329,6 +329,11 @@ module ActiveSupport EOV end + # Returns Array of parts of Time in sequence of + # [seconds, minutes, hours, day, month, year, weekday, yearday, dst?, zone]. + # + # now = Time.zone.now # => Tue, 18 Aug 2015 02:29:27 UTC +00:00 + # now.to_a # => [27, 29, 2, 18, 8, 2015, 2, 230, false, "UTC"] def to_a [time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone] end @@ -358,11 +363,15 @@ module ActiveSupport utc.to_r end - # Return an instance of Time in the system timezone. + # Returns an instance of Time in the system timezone. def to_time utc.to_time end + # Returns an instance of DateTime with the timezone's UTC offset + # + # Time.zone.now.to_datetime # => Tue, 18 Aug 2015 02:32:20 +0000 + # Time.current.in_time_zone('Hawaii').to_datetime # => Mon, 17 Aug 2015 16:32:20 -1000 def to_datetime utc.to_datetime.new_offset(Rational(utc_offset, 86_400)) end @@ -378,6 +387,11 @@ module ActiveSupport end alias_method :kind_of?, :is_a? + # An instance of ActiveSupport::TimeWithZone is never blank + def blank? + false + end + def freeze period; utc; time # preload instance variables before freezing super diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 2699a064d7..681f659100 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -1,5 +1,5 @@ require 'tzinfo' -require 'thread_safe' +require 'concurrent' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/try' @@ -23,7 +23,7 @@ module ActiveSupport # config.time_zone = 'Eastern Time (US & Canada)' # end # - # Time.zone # => #<TimeZone:0x514834...> + # Time.zone # => #<ActiveSupport::TimeZone:0x514834...> # Time.zone.name # => "Eastern Time (US & Canada)" # Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00 # @@ -189,13 +189,13 @@ module ActiveSupport UTC_OFFSET_WITH_COLON = '%s%02d:%02d' UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.tr(':', '') - @lazy_zones_map = ThreadSafe::Cache.new + @lazy_zones_map = Concurrent::Map.new class << self # Assumes self represents an offset from UTC in seconds (as returned from # Time#utc_offset) and turns this into an +HH:MM formatted string. # - # TimeZone.seconds_to_utc_offset(-21_600) # => "-06:00" + # ActiveSupport::TimeZone.seconds_to_utc_offset(-21_600) # => "-06:00" def seconds_to_utc_offset(seconds, colon = true) format = colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON sign = (seconds < 0 ? '-' : '+') @@ -285,8 +285,12 @@ module ActiveSupport end end - # Returns the offset of this time zone as a formatted string, of the - # format "+HH:MM". + # Returns a formatted string of the offset from UTC, or an alternative + # string if the time zone is already UTC. + # + # zone = ActiveSupport::TimeZone['Central Time (US & Canada)'] + # zone.formatted_offset # => "-06:00" + # zone.formatted_offset(false) # => "-0600" def formatted_offset(colon=true, alternate_utc_string = nil) utc_offset == 0 && alternate_utc_string || self.class.seconds_to_utc_offset(utc_offset, colon) end @@ -384,7 +388,7 @@ module ActiveSupport time_now.utc.in_time_zone(self) end - # Return the current date in this time zone. + # Returns the current date in this time zone. def today tzinfo.now.to_date end diff --git a/activesupport/lib/active_support/values/unicode_tables.dat b/activesupport/lib/active_support/values/unicode_tables.dat Binary files differindex 760be4c07a..dd2c178fb6 100644 --- a/activesupport/lib/active_support/values/unicode_tables.dat +++ b/activesupport/lib/active_support/values/unicode_tables.dat diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb index 65a8edbabb..c0e23e89f7 100644 --- a/activesupport/test/abstract_unit.rb +++ b/activesupport/test/abstract_unit.rb @@ -38,8 +38,6 @@ def jruby_skip(message = '') skip message if defined?(JRUBY_VERSION) end -require 'minitest/mock' - class ActiveSupport::TestCase include ActiveSupport::Testing::MethodCallAssertions end diff --git a/activesupport/test/array_inquirer_test.rb b/activesupport/test/array_inquirer_test.rb index b25e5cca86..263ab3802b 100644 --- a/activesupport/test/array_inquirer_test.rb +++ b/activesupport/test/array_inquirer_test.rb @@ -3,7 +3,7 @@ require 'active_support/core_ext/array' class ArrayInquirerTest < ActiveSupport::TestCase def setup - @array_inquirer = ActiveSupport::ArrayInquirer.new([:mobile, :tablet]) + @array_inquirer = ActiveSupport::ArrayInquirer.new([:mobile, :tablet, 'api']) end def test_individual @@ -18,6 +18,11 @@ class ArrayInquirerTest < ActiveSupport::TestCase assert_not @array_inquirer.any?(:desktop, :watch) end + def test_any_string_symbol_mismatch + assert @array_inquirer.any?('mobile') + assert @array_inquirer.any?(:api) + end + def test_any_with_block assert @array_inquirer.any? { |v| v == :mobile } assert_not @array_inquirer.any? { |v| v == :desktop } @@ -28,7 +33,7 @@ class ArrayInquirerTest < ActiveSupport::TestCase end def test_inquiry - result = [:mobile, :tablet].inquiry + result = [:mobile, :tablet, 'api'].inquiry assert_instance_of ActiveSupport::ArrayInquirer, result assert_equal @array_inquirer, result diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 74ceff44f9..c235dee5e1 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -490,6 +490,34 @@ module CacheStoreBehavior assert_equal({key => "bar"}, @cache.read_multi(key)) assert @cache.delete(key) end + + def test_cache_hit_instrumentation + key = "test_key" + subscribe_executed = false + ActiveSupport::Notifications.subscribe "cache_read.active_support" do |name, start, finish, id, payload| + subscribe_executed = true + assert_equal :fetch, payload[:super_operation] + assert payload[:hit] + end + assert @cache.write(key, "1", :raw => true) + assert @cache.fetch(key) {} + assert subscribe_executed + ensure + ActiveSupport::Notifications.unsubscribe "cache_read.active_support" + end + + def test_cache_miss_instrumentation + subscribe_executed = false + ActiveSupport::Notifications.subscribe "cache_read.active_support" do |name, start, finish, id, payload| + subscribe_executed = true + assert_equal :fetch, payload[:super_operation] + assert_not payload[:hit] + end + assert_not @cache.fetch("bad_key") {} + assert subscribe_executed + ensure + ActiveSupport::Notifications.unsubscribe "cache_read.active_support" + end end # https://rails.lighthouseapp.com/projects/8994/tickets/6225-memcachestore-cant-deal-with-umlauts-and-special-characters diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb index cda9732cae..3b00ff87a0 100644 --- a/activesupport/test/callbacks_test.rb +++ b/activesupport/test/callbacks_test.rb @@ -766,34 +766,30 @@ module CallbacksTest end class CallbackFalseTerminatorWithoutConfigTest < ActiveSupport::TestCase - def test_returning_false_halts_callback_if_config_variable_is_not_set + def test_returning_false_does_not_halt_callback_if_config_variable_is_not_set obj = CallbackFalseTerminator.new - assert_deprecated do - obj.save - assert_equal :second, obj.halted - assert !obj.saved - end + obj.save + assert_equal nil, obj.halted + assert obj.saved end end class CallbackFalseTerminatorWithConfigTrueTest < ActiveSupport::TestCase def setup - ActiveSupport::Callbacks::CallbackChain.halt_and_display_warning_on_return_false = true + ActiveSupport::Callbacks.halt_and_display_warning_on_return_false = true end - def test_returning_false_halts_callback_if_config_variable_is_true + def test_returning_false_does_not_halt_callback_if_config_variable_is_true obj = CallbackFalseTerminator.new - assert_deprecated do - obj.save - assert_equal :second, obj.halted - assert !obj.saved - end + obj.save + assert_equal nil, obj.halted + assert obj.saved end end class CallbackFalseTerminatorWithConfigFalseTest < ActiveSupport::TestCase def setup - ActiveSupport::Callbacks::CallbackChain.halt_and_display_warning_on_return_false = false + ActiveSupport::Callbacks.halt_and_display_warning_on_return_false = false end def test_returning_false_does_not_halt_callback_if_config_variable_is_false diff --git a/activesupport/test/constantize_test_cases.rb b/activesupport/test/constantize_test_cases.rb index 366e4e5ef0..1115bc0fd8 100644 --- a/activesupport/test/constantize_test_cases.rb +++ b/activesupport/test/constantize_test_cases.rb @@ -100,6 +100,10 @@ module ConstantizeTestCases assert_nil yield("Ace::Gas::ConstantizeTestCases") assert_nil yield("#<Class:0x7b8b718b>::Nested_1") assert_nil yield("Ace::gas") + assert_nil yield('Object::ABC') + assert_nil yield('Object::Object::Object::ABC') + assert_nil yield('A::Object::B') + assert_nil yield('A::Object::Object::Object::B') assert_raises(NameError) do with_autoloading_fixtures do diff --git a/activesupport/test/core_ext/hash/transform_keys_test.rb b/activesupport/test/core_ext/hash/transform_keys_test.rb index a7e12117f3..5a0b99e22c 100644 --- a/activesupport/test/core_ext/hash/transform_keys_test.rb +++ b/activesupport/test/core_ext/hash/transform_keys_test.rb @@ -24,9 +24,21 @@ class TransformKeysTest < ActiveSupport::TestCase assert_equal Enumerator, enumerator.class end + test "transform_keys! returns an Enumerator if no block is given" do + original = { a: 'a', b: 'b' } + enumerator = original.transform_keys! + assert_equal Enumerator, enumerator.class + end + test "transform_keys is chainable with Enumerable methods" do original = { a: 'a', b: 'b' } mapped = original.transform_keys.with_index { |k, i| [k, i].join.to_sym } assert_equal({ a0: 'a', b1: 'b' }, mapped) end + + test "transform_keys! is chainable with Enumerable methods" do + original = { a: 'a', b: 'b' } + original.transform_keys!.with_index { |k, i| [k, i].join.to_sym } + assert_equal({ a0: 'a', b1: 'b' }, original) + end end diff --git a/activesupport/test/core_ext/hash/transform_values_test.rb b/activesupport/test/core_ext/hash/transform_values_test.rb index 45ed11fef7..7c33227dc0 100644 --- a/activesupport/test/core_ext/hash/transform_values_test.rb +++ b/activesupport/test/core_ext/hash/transform_values_test.rb @@ -53,9 +53,21 @@ class TransformValuesTest < ActiveSupport::TestCase assert_equal Enumerator, enumerator.class end + test "transform_values! returns an Enumerator if no block is given" do + original = { a: 'a', b: 'b' } + enumerator = original.transform_values! + assert_equal Enumerator, enumerator.class + end + test "transform_values is chainable with Enumerable methods" do original = { a: 'a', b: 'b' } mapped = original.transform_values.with_index { |v, i| [v, i].join } assert_equal({ a: 'a0', b: 'b1' }, mapped) end + + test "transform_values! is chainable with Enumerable methods" do + original = { a: 'a', b: 'b' } + original.transform_values!.with_index { |v, i| [v, i].join } + assert_equal({ a: 'a0', b: 'b1' }, original) + end end diff --git a/activesupport/test/core_ext/module/attribute_accessor_test.rb b/activesupport/test/core_ext/module/attribute_accessor_test.rb index 128c5e3d1a..0b0f3a2808 100644 --- a/activesupport/test/core_ext/module/attribute_accessor_test.rb +++ b/activesupport/test/core_ext/module/attribute_accessor_test.rb @@ -69,6 +69,20 @@ class ModuleAttributeAccessorTest < ActiveSupport::TestCase end end assert_equal "invalid attribute name: 1nvalid", exception.message + + exception = assert_raises NameError do + Class.new do + mattr_reader "valid_part\ninvalid_part" + end + end + assert_equal "invalid attribute name: valid_part\ninvalid_part", exception.message + + exception = assert_raises NameError do + Class.new do + mattr_writer "valid_part\ninvalid_part" + end + end + assert_equal "invalid attribute name: valid_part\ninvalid_part", exception.message end def test_should_use_default_value_if_block_passed diff --git a/activesupport/test/core_ext/object/blank_test.rb b/activesupport/test/core_ext/object/blank_test.rb index 8a5e385dd7..a142096993 100644 --- a/activesupport/test/core_ext/object/blank_test.rb +++ b/activesupport/test/core_ext/object/blank_test.rb @@ -1,4 +1,3 @@ - require 'abstract_unit' require 'active_support/core_ext/object/blank' diff --git a/activesupport/test/core_ext/object/try_test.rb b/activesupport/test/core_ext/object/try_test.rb index 5ea0f0eca6..25bf0207b8 100644 --- a/activesupport/test/core_ext/object/try_test.rb +++ b/activesupport/test/core_ext/object/try_test.rb @@ -121,7 +121,7 @@ class ObjectTryTest < ActiveSupport::TestCase assert_equal 5, Decorator.new(@string).size end - def test_try_with_overriden_method_on_delegator + def test_try_with_overridden_method_on_delegator assert_equal 'overridden reverse', Decorator.new(@string).reverse end diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index ccb7f02331..c40f0bacbf 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -110,14 +110,14 @@ class TimeWithZoneTest < ActiveSupport::TestCase @twz += 0.1234560001 # advance the time by a fraction of a second assert_equal "1999-12-31T19:00:00.123-05:00", @twz.xmlschema(3) assert_equal "1999-12-31T19:00:00.123456-05:00", @twz.xmlschema(6) - assert_equal "1999-12-31T19:00:00.123456-05:00", @twz.xmlschema(12) + assert_equal "1999-12-31T19:00:00.123456000100-05:00", @twz.xmlschema(12) end def test_xmlschema_with_fractional_seconds_lower_than_hundred_thousand @twz += 0.001234 # advance the time by a fraction assert_equal "1999-12-31T19:00:00.001-05:00", @twz.xmlschema(3) assert_equal "1999-12-31T19:00:00.001234-05:00", @twz.xmlschema(6) - assert_equal "1999-12-31T19:00:00.001234-05:00", @twz.xmlschema(12) + assert_equal "1999-12-31T19:00:00.001234000000-05:00", @twz.xmlschema(12) end def test_xmlschema_with_nil_fractional_seconds diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb index 18a8b92eb9..e6898658b5 100644 --- a/activesupport/test/inflector_test_cases.rb +++ b/activesupport/test/inflector_test_cases.rb @@ -1,4 +1,3 @@ - module InflectorTestCases SingularToPlural = { "search" => "searches", diff --git a/activesupport/test/multibyte_conformance_test.rb b/activesupport/test/multibyte_conformance_test.rb index d8704716e7..2a885e32bf 100644 --- a/activesupport/test/multibyte_conformance_test.rb +++ b/activesupport/test/multibyte_conformance_test.rb @@ -1,4 +1,3 @@ - require 'abstract_unit' require 'multibyte_test_helpers' diff --git a/activesupport/test/multibyte_proxy_test.rb b/activesupport/test/multibyte_proxy_test.rb index 11f5374017..360cf57302 100644 --- a/activesupport/test/multibyte_proxy_test.rb +++ b/activesupport/test/multibyte_proxy_test.rb @@ -1,4 +1,3 @@ - require 'abstract_unit' class MultibyteProxyText < ActiveSupport::TestCase diff --git a/activesupport/test/multibyte_test_helpers.rb b/activesupport/test/multibyte_test_helpers.rb index 2e4b5cc873..58cf5488cd 100644 --- a/activesupport/test/multibyte_test_helpers.rb +++ b/activesupport/test/multibyte_test_helpers.rb @@ -1,4 +1,3 @@ - module MultibyteTestHelpers UNICODE_STRING = 'こにちわ'.freeze ASCII_STRING = 'ohayo'.freeze diff --git a/activesupport/test/number_helper_test.rb b/activesupport/test/number_helper_test.rb index cb5230c4eb..944bce1b41 100644 --- a/activesupport/test/number_helper_test.rb +++ b/activesupport/test/number_helper_test.rb @@ -106,6 +106,7 @@ module ActiveSupport assert_equal("123,456,789.78901", number_helper.number_to_delimited(123456789.78901)) assert_equal("0.78901", number_helper.number_to_delimited(0.78901)) assert_equal("123,456.78", number_helper.number_to_delimited("123456.78")) + assert_equal("1,23,456.78", number_helper.number_to_delimited("123456.78", delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/)) assert_equal("123,456.78", number_helper.number_to_delimited("123456.78".html_safe)) end end diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb index 9e6d1a91d0..18228a2ac5 100644 --- a/activesupport/test/test_case_test.rb +++ b/activesupport/test/test_case_test.rb @@ -56,6 +56,14 @@ class AssertDifferenceTest < ActiveSupport::TestCase end end + def test_assert_difference_retval + incremented = assert_difference '@object.num', +1 do + @object.increment + end + + assert_equal incremented, 1 + end + def test_assert_difference_with_implicit_difference assert_difference '@object.num' do @object.increment diff --git a/activesupport/test/testing/method_call_assertions_test.rb b/activesupport/test/testing/method_call_assertions_test.rb index 652cbda8da..3e5ba7c079 100644 --- a/activesupport/test/testing/method_call_assertions_test.rb +++ b/activesupport/test/testing/method_call_assertions_test.rb @@ -27,6 +27,18 @@ class MethodCallAssertionsTest < ActiveSupport::TestCase end end + def test_assert_called_method_with_arguments + assert_called(@object, :<<) do + @object << 2 + end + end + + def test_assert_called_returns + assert_called(@object, :increment, returns: 10) do + assert_equal 10, @object.increment + end + end + def test_assert_called_failure error = assert_raises(Minitest::Assertion) do assert_called(@object, :increment) do |