diff options
Diffstat (limited to 'activesupport')
38 files changed, 370 insertions, 229 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 5a7cddb079..20c720c861 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,7 +1,21 @@ ## Rails 4.0.0 (unreleased) ## -* `ActiveSupport::Notifications::Instrumenter#instrument` should yield - its payload. +* An `ActiveSupport::Subscriber` class has been extracted from + `ActiveSupport::LogSubscriber`, allowing you to use the event attachment + API for other kinds of subscribers. + + *Daniel Schierbeck* + +* `Class#class_attribute` accepts an `instance_predicate` option which + defaults to `true`. If set to `false` the predicate method will not + be defined. + + *Agis Anastasopoulos* + +* `fast_xs` support has been removed. Use `String#encode(xml: :attr)`. + +* `ActiveSupport::Notifications::Instrumenter#instrument` should + yield its payload. *stopdropandrew* @@ -88,7 +102,7 @@ * Improve `String#squish` to handle Unicode whitespace. *Antoine Lyset* -* Standardize on `to_time` returning an instance of `Time` in the local system timezone +* Standardise on `to_time` returning an instance of `Time` in the local system timezone across `String`, `Time`, `Date`, `DateTime` and `ActiveSupport::TimeWithZone`. *Andrew White* @@ -154,7 +168,7 @@ * Remove surrogate unicode character encoding from `ActiveSupport::JSON.encode` The encoding scheme was broken for unicode characters outside the basic multilingual plane; - since JSON is assumed to be UTF-8, and we already force the encoding to UTF-8, + since json is assumed to be UTF-8, and we already force the encoding to UTF-8, simply pass through the un-encoded characters. *Brett Carter* @@ -373,7 +387,7 @@ * An optional block can be passed to `HashWithIndifferentAccess#update` and `#merge`. The block will be invoked for each duplicated key, and used to resolve the conflict, - thus replicating the behavior of the corresponding methods on the `Hash` class. + thus replicating the behaviour of the corresponding methods on the `Hash` class. *Leo Cassarani* diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 6bfac15289..2368e5ebd4 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -522,7 +522,7 @@ module ActiveSupport def handle_expired_entry(entry, key, options) if entry && entry.expired? race_ttl = options[:race_condition_ttl].to_i - if race_ttl && (Time.now - entry.expires_at <= race_ttl) + if race_ttl && (Time.now.to_f - entry.expires_at <= race_ttl) # When an entry has :race_condition_ttl defined, put the stale entry back into the cache # for a brief period while the entry is begin recalculated. entry.expires_at = Time.now + race_ttl @@ -562,38 +562,38 @@ module ActiveSupport # +:compress+, +:compress_threshold+, and +:expires_in+. def initialize(value, options = {}) if should_compress?(value, options) - @v = compress(value) - @c = true + @value = compress(value) + @compressed = true else - @v = value - end - if expires_in = options[:expires_in] - @x = (Time.now + expires_in).to_i + @value = value end + @created_at = Time.now.to_f + @expires_in = options[:expires_in] + @expires_in = @expires_in.to_f if @expires_in end def value - convert_version_3_entry! if defined?(@value) - compressed? ? uncompress(@v) : @v + convert_version_4beta1_entry! if defined?(@v) + compressed? ? uncompress(@value) : @value end # Check if the entry is expired. The +expires_in+ parameter can override # the value set when the entry was created. def expired? - convert_version_3_entry! if defined?(@value) - if defined?(@x) - @x && @x < Time.now.to_i - else - false - end + convert_version_4beta1_entry! if defined?(@value) + @expires_in && @created_at + @expires_in <= Time.now.to_f end def expires_at - Time.at(@x) if defined?(@x) + @expires_in ? @created_at + @expires_in : nil end def expires_at=(value) - @x = value.to_i + if value + @expires_in = value.to_f - @created_at + else + @expires_in = nil + end end # Returns the size of the cached value. This could be less than @@ -606,9 +606,9 @@ module ActiveSupport when NilClass 0 when String - @v.bytesize + @value.bytesize else - @s = Marshal.dump(@v).bytesize + @s = Marshal.dump(@value).bytesize end end end @@ -616,12 +616,12 @@ module ActiveSupport # Duplicate the value in a class. This is used by cache implementations that don't natively # serialize entries to protect against accidental cache modifications. def dup_value! - convert_version_3_entry! if defined?(@value) - if @v && !compressed? && !(@v.is_a?(Numeric) || @v == true || @v == false) - if @v.is_a?(String) - @v = @v.dup + convert_version_4beta1_entry! if defined?(@v) + if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false) + if @value.is_a?(String) + @value = @value.dup else - @v = Marshal.load(Marshal.dump(@v)) + @value = Marshal.load(Marshal.dump(@value)) end end end @@ -637,7 +637,7 @@ module ActiveSupport end def compressed? - defined?(@c) ? @c : false + defined?(@compressed) ? @compressed : false end def compress(value) @@ -650,19 +650,19 @@ module ActiveSupport # The internals of this method changed between Rails 3.x and 4.0. This method provides the glue # to ensure that cache entries created under the old version still work with the new class definition. - def convert_version_3_entry! - if defined?(@value) - @v = @value - remove_instance_variable(:@value) + def convert_version_4beta1_entry! + if defined?(@v) + @value = @v + remove_instance_variable(:@v) end - if defined?(@compressed) - @c = @compressed - remove_instance_variable(:@compressed) + if defined?(@c) + @compressed = @c + remove_instance_variable(:@c) end - if defined?(@expires_in) && defined?(@created_at) && @expires_in && @created_at - @x = (@created_at + @expires_in).to_i - remove_instance_variable(:@created_at) - remove_instance_variable(:@expires_in) + if defined?(@x) && @x + @created_at ||= Time.now.to_f + @expires_in = @x - @created_at + remove_instance_variable(:@x) end end end diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb index db5f228a70..fb42c4a41e 100644 --- a/activesupport/lib/active_support/cache/strategy/local_cache.rb +++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb @@ -8,6 +8,23 @@ module ActiveSupport # duration of a block. Repeated calls to the cache for the same key will hit the # in-memory cache for faster access. module LocalCache + # Class for storing and registering the local caches. + class LocalCacheRegistry # :nodoc: + extend ActiveSupport::PerThreadRegistry + + def initialize + @registry = {} + end + + def cache_for(local_cache_key) + @registry[local_cache_key] + end + + def set_cache_for(local_cache_key, value) + @registry[local_cache_key] = value + end + end + # Simple memory backed cache. This cache is not thread safe and is intended only # for serving as a temporary memory cache for a single thread. class LocalStore < Store @@ -41,24 +58,18 @@ module ActiveSupport # Use a local cache for the duration of block. def with_local_cache - save_val = Thread.current[thread_local_key] - begin - Thread.current[thread_local_key] = LocalStore.new - yield - ensure - Thread.current[thread_local_key] = save_val - end + use_temporary_local_cache(LocalStore.new) { yield } end #-- # This class wraps up local storage for middlewares. Only the middleware method should # construct them. class Middleware # :nodoc: - attr_reader :name, :thread_local_key + attr_reader :name, :local_cache_key - def initialize(name, thread_local_key) + def initialize(name, local_cache_key) @name = name - @thread_local_key = thread_local_key + @local_cache_key = local_cache_key @app = nil end @@ -68,10 +79,10 @@ module ActiveSupport end def call(env) - Thread.current[thread_local_key] = LocalStore.new + LocalCacheRegistry.set_cache_for(local_cache_key, LocalStore.new) @app.call(env) ensure - Thread.current[thread_local_key] = nil + LocalCacheRegistry.set_cache_for(local_cache_key, nil) end end @@ -80,7 +91,7 @@ module ActiveSupport def middleware @middleware ||= Middleware.new( "ActiveSupport::Cache::Strategy::LocalCache", - thread_local_key) + local_cache_key) end def clear(options = nil) # :nodoc: @@ -95,29 +106,13 @@ module ActiveSupport def increment(name, amount = 1, options = nil) # :nodoc: value = bypass_local_cache{super} - if local_cache - local_cache.mute do - if value - local_cache.write(name, value, options) - else - local_cache.delete(name, options) - end - end - end + increment_or_decrement(value, name, amount, options) value end def decrement(name, amount = 1, options = nil) # :nodoc: value = bypass_local_cache{super} - if local_cache - local_cache.mute do - if value - local_cache.write(name, value, options) - else - local_cache.delete(name, options) - end - end - end + increment_or_decrement(value, name, amount, options) value end @@ -146,21 +141,37 @@ module ActiveSupport end private - def thread_local_key - @thread_local_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, '_').to_sym + def increment_or_decrement(value, name, amount, options) + if local_cache + local_cache.mute do + if value + local_cache.write(name, value, options) + else + local_cache.delete(name, options) + end + end + end + end + + def local_cache_key + @local_cache_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, '_').to_sym end def local_cache - Thread.current[thread_local_key] + LocalCacheRegistry.cache_for(local_cache_key) end def bypass_local_cache - save_cache = Thread.current[thread_local_key] + use_temporary_local_cache(nil) { yield } + end + + def use_temporary_local_cache(temporary_cache) + save_cache = LocalCacheRegistry.cache_for(local_cache_key) begin - Thread.current[thread_local_key] = nil + LocalCacheRegistry.set_cache_for(local_cache_key, temporary_cache) yield ensure - Thread.current[thread_local_key] = save_cache + LocalCacheRegistry.set_cache_for(local_cache_key, save_cache) end end end diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index e81686b3a6..f95bae580c 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -61,6 +61,8 @@ module ActiveSupport extend ActiveSupport::DescendantsTracker end + CALLBACK_FILTER_TYPES = [:before, :after, :around] + # Runs the callbacks for the given event. # # Calls the before and around callbacks in the order they were set, yields @@ -417,7 +419,7 @@ module ActiveSupport # This is used internally to append, prepend and skip callbacks to the # CallbackChain. def __update_callbacks(name, filters = [], block = nil) #:nodoc: - type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before + type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before options = filters.last.is_a?(Hash) ? filters.pop : {} filters.unshift(block) if block diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 430a35fbaf..3807ee63b1 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -12,7 +12,7 @@ class Array # pass an option key that doesn't exist in the list below, it will raise an # <tt>ArgumentError</tt>. # - # Options: + # ==== Options # # * <tt>:words_connector</tt> - The sign or word used to join the elements # in arrays with two or more elements (default: ", "). @@ -24,6 +24,8 @@ class Array # the connector options defined on the 'support.array' namespace in the # corresponding dictionary file. # + # ==== Examples + # # [].to_sentence # => "" # ['one'].to_sentence # => "one" # ['one', 'two'].to_sentence # => "one and two" @@ -38,10 +40,10 @@ class Array # ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ') # # => "one or two or at least three" # - # Examples using <tt>:locale</tt> option: + # Using <tt>:locale</tt> option: # # # Given this locale dictionary: - # # + # # # # es: # # support: # # array: diff --git a/activesupport/lib/active_support/core_ext/array/uniq_by.rb b/activesupport/lib/active_support/core_ext/array/uniq_by.rb index ca3b7748cd..23573c97de 100644 --- a/activesupport/lib/active_support/core_ext/array/uniq_by.rb +++ b/activesupport/lib/active_support/core_ext/array/uniq_by.rb @@ -1,5 +1,5 @@ class Array - # *DEPRECATED*: Use +Array#uniq+ instead. + # *DEPRECATED*: Use <tt>Array#uniq</tt> instead. # # Returns a unique array based on the criteria in the block. # @@ -9,7 +9,7 @@ class Array uniq(&block) end - # *DEPRECATED*: Use +Array#uniq!+ instead. + # *DEPRECATED*: Use <tt>Array#uniq!</tt> instead. # # Same as +uniq_by+, but modifies +self+. def uniq_by!(&block) diff --git a/activesupport/lib/active_support/core_ext/benchmark.rb b/activesupport/lib/active_support/core_ext/benchmark.rb index 2d110155a5..eb25b2bc44 100644 --- a/activesupport/lib/active_support/core_ext/benchmark.rb +++ b/activesupport/lib/active_support/core_ext/benchmark.rb @@ -1,6 +1,13 @@ require 'benchmark' class << Benchmark + # Benchmark realtime in milliseconds. + # + # Benchmark.realtime { User.all } + # # => 8.0e-05 + # + # Benchmark.ms { User.all } + # # => 0.074 def ms 1000 * realtime { yield } end diff --git a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb index 5dc5710c53..39b8cea807 100644 --- a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb +++ b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb @@ -1,4 +1,5 @@ require 'bigdecimal' +require 'bigdecimal/util' require 'yaml' class BigDecimal diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb index e51ab9ddbc..6d49b7b6e1 100644 --- a/activesupport/lib/active_support/core_ext/class/attribute.rb +++ b/activesupport/lib/active_support/core_ext/class/attribute.rb @@ -44,7 +44,8 @@ class Class # Base.setting # => [] # Subclass.setting # => [:foo] # - # For convenience, a query method is defined as well: + # For convenience, an instance predicate method is defined as well. + # To skip it, pass <tt>instance_predicate: false</tt>. # # Subclass.setting? # => false # @@ -72,10 +73,11 @@ class Class # double assignment is used to avoid "assigned but unused variable" warning instance_reader = instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true) instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true) + instance_predicate = options.fetch(:instance_predicate, true) attrs.each do |name| define_singleton_method(name) { nil } - define_singleton_method("#{name}?") { !!public_send(name) } + define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate ivar = "@#{name}" @@ -109,7 +111,7 @@ class Class self.class.public_send name end end - define_method("#{name}?") { !!public_send(name) } + define_method("#{name}?") { !!public_send(name) } if instance_predicate end attr_writer name if instance_writer diff --git a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb index fa1dbfdf06..34859617c9 100644 --- a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb @@ -32,7 +32,7 @@ class Class def cattr_reader(*syms) options = syms.extract_options! syms.each do |sym| - raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/ + raise NameError.new("invalid class attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/ class_eval(<<-EOS, __FILE__, __LINE__ + 1) unless defined? @@#{sym} @@#{sym} = nil @@ -93,7 +93,7 @@ class Class def cattr_writer(*syms) options = syms.extract_options! syms.each do |sym| - raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/ + raise NameError.new("invalid class attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/ class_eval(<<-EOS, __FILE__, __LINE__ + 1) unless defined? @@#{sym} @@#{sym} = nil 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 1f78b9eb5a..5b89ace66b 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 @@ -93,7 +93,7 @@ module DateAndTime # Returns a new date/time at the end of the quarter. # Example: 31st March, 30th June, 30th September. - # DateTIme objects will have a time set to 23:59:59. + # DateTime objects will have a time set to 23:59:59. def end_of_quarter last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month } beginning_of_month.change(:month => last_quarter_month).end_of_month diff --git a/activesupport/lib/active_support/core_ext/module/deprecation.rb b/activesupport/lib/active_support/core_ext/module/deprecation.rb index cc45cee5b8..d873de197f 100644 --- a/activesupport/lib/active_support/core_ext/module/deprecation.rb +++ b/activesupport/lib/active_support/core_ext/module/deprecation.rb @@ -14,8 +14,8 @@ class Module # method where you can implement your custom warning behavior. # # class MyLib::Deprecator - # def deprecation_warning(deprecated_method_name, message, caller_backtrace) - # message = "#{method_name} is deprecated and will be removed from MyLibrary | #{message}" + # def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil) + # message = "#{deprecated_method_name} is deprecated and will be removed from MyLibrary | #{message}" # Kernel.warn message # end # end diff --git a/activesupport/lib/active_support/core_ext/string.rb b/activesupport/lib/active_support/core_ext/string.rb index 5d7cb81e38..c656db2c6c 100644 --- a/activesupport/lib/active_support/core_ext/string.rb +++ b/activesupport/lib/active_support/core_ext/string.rb @@ -4,7 +4,6 @@ require 'active_support/core_ext/string/multibyte' require 'active_support/core_ext/string/starts_ends_with' require 'active_support/core_ext/string/inflections' require 'active_support/core_ext/string/access' -require 'active_support/core_ext/string/xchar' require 'active_support/core_ext/string/behavior' require 'active_support/core_ext/string/output_safety' require 'active_support/core_ext/string/exclude' diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb index a1b3f79748..c62bb41416 100644 --- a/activesupport/lib/active_support/core_ext/string/filters.rb +++ b/activesupport/lib/active_support/core_ext/string/filters.rb @@ -50,6 +50,6 @@ class String length_with_room_for_omission end - self[0...stop] + options[:omission] + "#{self[0...stop]}#{options[:omission]}" end end diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb index 6522145572..0b506a6030 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -41,7 +41,7 @@ class String # # If the optional parameter +locale+ is specified, # the word will be singularized as a word of that language. - # By default, this paramter is set to <tt>:en</tt>. + # By default, this parameter is set to <tt>:en</tt>. # You must define your own inflection rules for languages other than English. # # 'posts'.singularize # => "post" diff --git a/activesupport/lib/active_support/core_ext/string/xchar.rb b/activesupport/lib/active_support/core_ext/string/xchar.rb deleted file mode 100644 index f9a5b4fb64..0000000000 --- a/activesupport/lib/active_support/core_ext/string/xchar.rb +++ /dev/null @@ -1,18 +0,0 @@ -begin - # See http://fast-xs.rubyforge.org/ by Eric Wong. - # Also included with hpricot. - require 'fast_xs' -rescue LoadError - # fast_xs extension unavailable -else - begin - require 'builder' - rescue LoadError - # builder demands the first shot at defining String#to_xs - end - - class String - alias_method :original_xs, :to_xs if method_defined?(:to_xs) - alias_method :to_xs, :fast_xs - end -end diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index 837db05dcc..1b20592e4c 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -223,7 +223,7 @@ module ActiveSupport def deep_stringify_keys; dup end undef :symbolize_keys! undef :deep_symbolize_keys! - def symbolize_keys; to_hash.symbolize_keys end + def symbolize_keys; to_hash.symbolize_keys! end def deep_symbolize_keys; to_hash.deep_symbolize_keys end def to_options!; self end diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb index 71654dbb87..37124fb7ae 100644 --- a/activesupport/lib/active_support/key_generator.rb +++ b/activesupport/lib/active_support/key_generator.rb @@ -39,7 +39,7 @@ module ActiveSupport end end - class DummyKeyGenerator # :nodoc: + class LegacyKeyGenerator # :nodoc: SECRET_MIN_LENGTH = 30 # Characters def initialize(secret) diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb index 21a04a9152..e95dc5a866 100644 --- a/activesupport/lib/active_support/log_subscriber.rb +++ b/activesupport/lib/active_support/log_subscriber.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/class/attribute' +require 'active_support/subscriber' module ActiveSupport # ActiveSupport::LogSubscriber is an object set to consume @@ -33,7 +34,7 @@ module ActiveSupport # Log subscriber also has some helpers to deal with logging and automatically # flushes all logs when the request finishes (via action_dispatch.callback # notification) in a Rails environment. - class LogSubscriber + class LogSubscriber < Subscriber # Embed in a String to clear all previous ANSI sequences. CLEAR = "\e[0m" BOLD = "\e[1m" @@ -53,26 +54,15 @@ module ActiveSupport class << self def logger - if defined?(Rails) && Rails.respond_to?(:logger) - @logger ||= Rails.logger + @logger ||= if defined?(Rails) && Rails.respond_to?(:logger) + Rails.logger end - @logger end attr_writer :logger - def attach_to(namespace, log_subscriber=new, notifier=ActiveSupport::Notifications) - log_subscribers << log_subscriber - - log_subscriber.public_methods(false).each do |event| - next if %w{ start finish }.include?(event.to_s) - - notifier.subscribe("#{event}.#{namespace}", log_subscriber) - end - end - def log_subscribers - @@log_subscribers ||= [] + subscribers end # Flush all log_subscribers' logger. @@ -81,39 +71,18 @@ module ActiveSupport end end - def initialize - @queue_key = [self.class.name, object_id].join "-" - super - end - def logger LogSubscriber.logger end def start(name, id, payload) - return unless logger - - e = ActiveSupport::Notifications::Event.new(name, Time.now, nil, id, payload) - parent = event_stack.last - parent << e if parent - - event_stack.push e + super if logger end def finish(name, id, payload) - return unless logger - - finished = Time.now - event = event_stack.pop - event.end = finished - event.payload.merge!(payload) - - method = name.split('.').first - begin - send(method, event) - rescue Exception => e - logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}" - end + super if logger + rescue Exception => e + logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}" end protected @@ -136,11 +105,5 @@ module ActiveSupport bold = bold ? BOLD : "" "#{bold}#{color}#{text}#{CLEAR}" end - - private - - def event_stack - Thread.current[@queue_key] ||= [] - end end end diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index ce40a7d689..96025e7614 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -28,7 +28,7 @@ module ActiveSupport end class InvalidMessage < StandardError; end - OpenSSLCipherError = OpenSSL::Cipher.const_defined?(:CipherError) ? OpenSSL::Cipher::CipherError : OpenSSL::CipherError + OpenSSLCipherError = OpenSSL::Cipher::CipherError # Initialize a new MessageEncryptor. +secret+ must be at least as long as # the cipher key size. For the default 'aes-256-cbc' cipher, this is 256 @@ -66,12 +66,11 @@ module ActiveSupport def _encrypt(value) cipher = new_cipher - # Rely on OpenSSL for the initialization vector - iv = cipher.random_iv - cipher.encrypt cipher.key = @secret - cipher.iv = iv + + # Rely on OpenSSL for the initialization vector + iv = cipher.random_iv encrypted_data = cipher.update(@serializer.dump(value)) encrypted_data << cipher.final diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb index 705a4693b7..c45358bba9 100644 --- a/activesupport/lib/active_support/notifications.rb +++ b/activesupport/lib/active_support/notifications.rb @@ -1,5 +1,6 @@ require 'active_support/notifications/instrumenter' require 'active_support/notifications/fanout' +require 'active_support/per_thread_registry' module ActiveSupport # = Notifications @@ -177,7 +178,27 @@ module ActiveSupport end def instrumenter - Thread.current[:"instrumentation_#{notifier.object_id}"] ||= Instrumenter.new(notifier) + InstrumentationRegistry.instrumenter_for(notifier) + end + end + + # This class is a registry which holds all of the +Instrumenter+ objects + # in a particular thread local. To access the +Instrumenter+ object for a + # particular +notifier+, you can call the following method: + # + # InstrumentationRegistry.instrumenter_for(notifier) + # + # The instrumenters for multiple notifiers are held in a single instance of + # this class. + class InstrumentationRegistry # :nodoc: + extend ActiveSupport::PerThreadRegistry + + def initialize + @registry = {} + end + + def instrumenter_for(notifier) + @registry[notifier] ||= Instrumenter.new(notifier) end end diff --git a/activesupport/lib/active_support/per_thread_registry.rb b/activesupport/lib/active_support/per_thread_registry.rb new file mode 100644 index 0000000000..aa682fb36c --- /dev/null +++ b/activesupport/lib/active_support/per_thread_registry.rb @@ -0,0 +1,52 @@ +module ActiveSupport + # This module is used to encapsulate access to thread local variables. + # + # Instead of polluting the thread locals namespace: + # + # Thread.current[:connection_handler] + # + # you define a class that extends this module: + # + # module ActiveRecord + # class RuntimeRegistry + # extend ActiveSupport::PerThreadRegistry + # + # attr_accessor :connection_handler + # end + # end + # + # and invoke the declared instance accessors as class methods. So + # + # ActiveRecord::RuntimeRegistry.connection_handler = connection_handler + # + # sets a connection handler local to the current thread, and + # + # ActiveRecord::RuntimeRegistry.connection_handler + # + # returns a connection handler local to the current thread. + # + # This feature is accomplished by instantiating the class and storing the + # instance as a thread local keyed by the class name. In the example above + # a key "ActiveRecord::RuntimeRegistry" is stored in <tt>Thread.current</tt>. + # The class methods proxy to said thread local instance. + # + # If the class has an initializer, it must accept no arguments. + module PerThreadRegistry + 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| + per_thread_registry_instance.public_send(name, *a, &b) + end + + send(name, *args, &block) + end + + private + + def per_thread_registry_instance + Thread.current[name] ||= new + end + end +end diff --git a/activesupport/lib/active_support/subscriber.rb b/activesupport/lib/active_support/subscriber.rb new file mode 100644 index 0000000000..ab76cf5c85 --- /dev/null +++ b/activesupport/lib/active_support/subscriber.rb @@ -0,0 +1,75 @@ +module ActiveSupport + # ActiveSupport::Subscriber is an object set to consume + # ActiveSupport::Notifications. The subscriber dispatches notifications to + # a registered object based on its given namespace. + # + # An example would be Active Record subscriber responsible for collecting + # statistics about queries: + # + # module ActiveRecord + # class StatsSubscriber < ActiveSupport::Subscriber + # def sql(event) + # Statsd.timing("sql.#{event.payload[:name]}", event.duration) + # end + # end + # end + # + # And it's finally registered as: + # + # ActiveRecord::StatsSubscriber.attach_to :active_record + # + # Since we need to know all instance methods before attaching the log + # subscriber, the line above should be called after your subscriber definition. + # + # After configured, whenever a "sql.active_record" notification is published, + # it will properly dispatch the event (ActiveSupport::Notifications::Event) to + # the +sql+ method. + class Subscriber + class << self + + # Attach the subscriber to a namespace. + def attach_to(namespace, subscriber=new, notifier=ActiveSupport::Notifications) + subscribers << subscriber + + subscriber.public_methods(false).each do |event| + next if %w{ start finish }.include?(event.to_s) + + notifier.subscribe("#{event}.#{namespace}", subscriber) + end + end + + def subscribers + @@subscribers ||= [] + end + end + + def initialize + @queue_key = [self.class.name, object_id].join "-" + super + end + + def start(name, id, payload) + e = ActiveSupport::Notifications::Event.new(name, Time.now, nil, id, payload) + parent = event_stack.last + parent << e if parent + + event_stack.push e + end + + def finish(name, id, payload) + finished = Time.now + event = event_stack.pop + event.end = finished + event.payload.merge!(payload) + + method = name.split('.').first + send(method, event) + end + + private + + def event_stack + Thread.current[@queue_key] ||= [] + end + end +end diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 21a0620c22..3cf82a24b9 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -5,7 +5,7 @@ module ActiveSupport # The TimeZone class serves as a wrapper around TZInfo::Timezone instances. # It allows us to do the following: # - # * Limit the set of zones provided by TZInfo to a meaningful subset of 142 + # * Limit the set of zones provided by TZInfo to a meaningful subset of 146 # zones. # * Retrieve and display zones with a friendlier name # (e.g., "Eastern Time (US & Canada)" instead of "America/New_York"). @@ -177,6 +177,7 @@ module ActiveSupport "Wellington" => "Pacific/Auckland", "Nuku'alofa" => "Pacific/Tongatapu", "Tokelau Is." => "Pacific/Fakaofo", + "Chatham Is." => "Pacific/Chatham", "Samoa" => "Pacific/Apia" } diff --git a/activesupport/lib/active_support/xml_mini/libxmlsax.rb b/activesupport/lib/active_support/xml_mini/libxmlsax.rb index acc018fd2d..70a95299ec 100644 --- a/activesupport/lib/active_support/xml_mini/libxmlsax.rb +++ b/activesupport/lib/active_support/xml_mini/libxmlsax.rb @@ -32,7 +32,7 @@ module ActiveSupport end def on_start_element(name, attrs = {}) - new_hash = { CONTENT_KEY => '' }.merge(attrs) + new_hash = { CONTENT_KEY => '' }.merge!(attrs) new_hash[HASH_SIZE_KEY] = new_hash.size + 1 case current_hash[name] diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb index 30b94aac47..be2d6a4cb1 100644 --- a/activesupport/lib/active_support/xml_mini/nokogirisax.rb +++ b/activesupport/lib/active_support/xml_mini/nokogirisax.rb @@ -38,7 +38,7 @@ module ActiveSupport end def start_element(name, attrs = []) - new_hash = { CONTENT_KEY => '' }.merge(Hash[attrs]) + new_hash = { CONTENT_KEY => '' }.merge!(Hash[attrs]) new_hash[HASH_SIZE_KEY] = new_hash.size + 1 case current_hash[name] diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index acd320dbe0..bcc200cf33 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -405,7 +405,7 @@ module CacheStoreBehavior end # https://rails.lighthouseapp.com/projects/8994/tickets/6225-memcachestore-cant-deal-with-umlauts-and-special-characters -# The error is caused by charcter encodings that can't be compared with ASCII-8BIT regular expressions and by special +# The error is caused by character encodings that can't be compared with ASCII-8BIT regular expressions and by special # characters like the umlaut in UTF-8. module EncodedKeyCacheBehavior Encoding.list.each do |encoding| @@ -943,29 +943,28 @@ class CacheEntryTest < ActiveSupport::TestCase assert_equal value.bytesize, entry.size end - def test_restoring_version_3_entries - version_3_entry = ActiveSupport::Cache::Entry.allocate - version_3_entry.instance_variable_set(:@value, "hello") - version_3_entry.instance_variable_set(:@created_at, Time.now - 60) - entry = Marshal.load(Marshal.dump(version_3_entry)) + def test_restoring_version_4beta1_entries + version_4beta1_entry = ActiveSupport::Cache::Entry.allocate + version_4beta1_entry.instance_variable_set(:@v, "hello") + version_4beta1_entry.instance_variable_set(:@x, Time.now.to_i + 60) + entry = Marshal.load(Marshal.dump(version_4beta1_entry)) assert_equal "hello", entry.value assert_equal false, entry.expired? end - def test_restoring_compressed_version_3_entries - version_3_entry = ActiveSupport::Cache::Entry.allocate - version_3_entry.instance_variable_set(:@value, Zlib::Deflate.deflate(Marshal.dump("hello"))) - version_3_entry.instance_variable_set(:@compressed, true) - entry = Marshal.load(Marshal.dump(version_3_entry)) + def test_restoring_compressed_version_4beta1_entries + version_4beta1_entry = ActiveSupport::Cache::Entry.allocate + version_4beta1_entry.instance_variable_set(:@v, Zlib::Deflate.deflate(Marshal.dump("hello"))) + version_4beta1_entry.instance_variable_set(:@c, true) + entry = Marshal.load(Marshal.dump(version_4beta1_entry)) assert_equal "hello", entry.value end - def test_restoring_expired_version_3_entries - version_3_entry = ActiveSupport::Cache::Entry.allocate - version_3_entry.instance_variable_set(:@value, "hello") - version_3_entry.instance_variable_set(:@created_at, Time.now - 60) - version_3_entry.instance_variable_set(:@expires_in, 58.9) - entry = Marshal.load(Marshal.dump(version_3_entry)) + def test_restoring_expired_version_4beta1_entries + version_4beta1_entry = ActiveSupport::Cache::Entry.allocate + version_4beta1_entry.instance_variable_set(:@v, "hello") + version_4beta1_entry.instance_variable_set(:@x, Time.now.to_i - 1) + entry = Marshal.load(Marshal.dump(version_4beta1_entry)) assert_equal "hello", entry.value assert_equal true, entry.expired? end diff --git a/activesupport/test/callback_inheritance_test.rb b/activesupport/test/callback_inheritance_test.rb index 6be8ea8b84..1adfe4edf4 100644 --- a/activesupport/test/callback_inheritance_test.rb +++ b/activesupport/test/callback_inheritance_test.rb @@ -109,7 +109,6 @@ class BasicCallbacksTest < ActiveSupport::TestCase @index = GrandParent.new("index").dispatch @update = GrandParent.new("update").dispatch @delete = GrandParent.new("delete").dispatch - @unknown = GrandParent.new("unknown").dispatch end def test_basic_conditional_callback1 @@ -130,7 +129,6 @@ class InheritedCallbacksTest < ActiveSupport::TestCase @index = Parent.new("index").dispatch @update = Parent.new("update").dispatch @delete = Parent.new("delete").dispatch - @unknown = Parent.new("unknown").dispatch end def test_inherited_excluded diff --git a/activesupport/test/core_ext/class/attribute_accessor_test.rb b/activesupport/test/core_ext/class/attribute_accessor_test.rb index 8d827f054e..0d5f39a72b 100644 --- a/activesupport/test/core_ext/class/attribute_accessor_test.rb +++ b/activesupport/test/core_ext/class/attribute_accessor_test.rb @@ -44,16 +44,18 @@ class ClassAttributeAccessorTest < ActiveSupport::TestCase end def test_should_raise_name_error_if_attribute_name_is_invalid - assert_raises NameError do + exception = assert_raises NameError do Class.new do - cattr_reader "invalid attribute name" + cattr_reader "1nvalid" end end + assert_equal "invalid class attribute name: 1nvalid", exception.message - assert_raises NameError do + exception = assert_raises NameError do Class.new do - cattr_writer "invalid attribute name" + cattr_writer "1nvalid" end end + assert_equal "invalid class attribute name: 1nvalid", exception.message end end diff --git a/activesupport/test/core_ext/class/attribute_test.rb b/activesupport/test/core_ext/class/attribute_test.rb index 1c3ba8a7a0..e7a1334db3 100644 --- a/activesupport/test/core_ext/class/attribute_test.rb +++ b/activesupport/test/core_ext/class/attribute_test.rb @@ -27,7 +27,7 @@ class ClassAttributeTest < ActiveSupport::TestCase assert_equal 1, Class.new(@sub).setting end - test 'query method' do + test 'predicate method' do assert_equal false, @klass.setting? @klass.setting = 1 assert_equal true, @klass.setting? @@ -48,7 +48,7 @@ class ClassAttributeTest < ActiveSupport::TestCase assert_equal 1, object.setting end - test 'instance query' do + test 'instance predicate' do object = @klass.new assert_equal false, object.setting? object.setting = 1 @@ -73,6 +73,11 @@ class ClassAttributeTest < ActiveSupport::TestCase assert_raise(NoMethodError) { object.setting = 'boom' } end + test 'disabling instance predicate' do + object = Class.new { class_attribute :setting, instance_predicate: false }.new + assert_raise(NoMethodError) { object.setting? } + end + test 'works well with singleton classes' do object = @klass.new object.singleton_class.setting = 'foo' diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb index 0a1abac767..6781e3c20e 100644 --- a/activesupport/test/core_ext/enumerable_test.rb +++ b/activesupport/test/core_ext/enumerable_test.rb @@ -24,10 +24,8 @@ class EnumerableTests < ActiveSupport::TestCase def test_group_by names = %w(marcel sam david jeremy) klass = Struct.new(:name) - objects = (1..50).inject([]) do |people,| - p = klass.new - p.name = names.sort_by { rand }.first - people << p + objects = (1..50).map do + klass.new names.sample end enum = GenericEnumerable.new(objects) diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index 6aea9d56f1..c3fe89de4b 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -244,11 +244,7 @@ class OrderedHashTest < ActiveSupport::TestCase end def test_each_after_yaml_serialization - values = [] - @deserialized_ordered_hash = YAML.load(YAML.dump(@ordered_hash)) - - @deserialized_ordered_hash.each {|key, value| values << value} - assert_equal @values, values + assert_equal @values, YAML.load(YAML.dump(@ordered_hash)).values end def test_each_when_yielding_to_block_with_splat diff --git a/activesupport/test/xml_mini/jdom_engine_test.rb b/activesupport/test/xml_mini/jdom_engine_test.rb index 2dfdde5c62..904ef7b208 100644 --- a/activesupport/test/xml_mini/jdom_engine_test.rb +++ b/activesupport/test/xml_mini/jdom_engine_test.rb @@ -176,10 +176,11 @@ if RUBY_PLATFORM =~ /java/ end private - def assert_equal_rexml(xml) - hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } - assert_equal(hash, XmlMini.parse(xml)) - end + def assert_equal_rexml(xml) + parsed_xml = XmlMini.parse(xml) + hash = XmlMini.with_backend('REXML') { parsed_xml } + assert_equal(hash, parsed_xml) + end end else diff --git a/activesupport/test/xml_mini/libxml_engine_test.rb b/activesupport/test/xml_mini/libxml_engine_test.rb index 36ac4161ea..e7cb350663 100644 --- a/activesupport/test/xml_mini/libxml_engine_test.rb +++ b/activesupport/test/xml_mini/libxml_engine_test.rb @@ -141,7 +141,7 @@ class LibxmlEngineTest < ActiveSupport::TestCase morning </root> eoxml - XmlMini.parse(io) + assert_equal_rexml(io) end def test_children_with_simple_cdata @@ -193,10 +193,11 @@ class LibxmlEngineTest < ActiveSupport::TestCase private - def assert_equal_rexml(xml) - hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } - assert_equal(hash, XmlMini.parse(xml)) - end + def assert_equal_rexml(xml) + parsed_xml = XmlMini.parse(xml) + hash = XmlMini.with_backend('REXML') { parsed_xml } + assert_equal(hash, parsed_xml) + end end end diff --git a/activesupport/test/xml_mini/libxmlsax_engine_test.rb b/activesupport/test/xml_mini/libxmlsax_engine_test.rb index 82337961a1..07485911c9 100644 --- a/activesupport/test/xml_mini/libxmlsax_engine_test.rb +++ b/activesupport/test/xml_mini/libxmlsax_engine_test.rb @@ -141,7 +141,7 @@ class LibXMLSAXEngineTest < ActiveSupport::TestCase morning </root> eoxml - XmlMini.parse(io) + assert_equal_rexml(io) end def test_children_with_simple_cdata @@ -184,10 +184,11 @@ class LibXMLSAXEngineTest < ActiveSupport::TestCase end private - def assert_equal_rexml(xml) - hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } - assert_equal(hash, XmlMini.parse(xml)) - end + def assert_equal_rexml(xml) + parsed_xml = XmlMini.parse(xml) + hash = XmlMini.with_backend('REXML') { parsed_xml } + assert_equal(hash, parsed_xml) + end end end diff --git a/activesupport/test/xml_mini/nokogiri_engine_test.rb b/activesupport/test/xml_mini/nokogiri_engine_test.rb index 71f57e43d2..937517786e 100644 --- a/activesupport/test/xml_mini/nokogiri_engine_test.rb +++ b/activesupport/test/xml_mini/nokogiri_engine_test.rb @@ -155,7 +155,7 @@ class NokogiriEngineTest < ActiveSupport::TestCase morning </root> eoxml - XmlMini.parse(io) + assert_equal_rexml(io) end def test_children_with_simple_cdata @@ -206,10 +206,11 @@ class NokogiriEngineTest < ActiveSupport::TestCase end private - def assert_equal_rexml(xml) - hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } - assert_equal(hash, XmlMini.parse(xml)) - end + def assert_equal_rexml(xml) + parsed_xml = XmlMini.parse(xml) + hash = XmlMini.with_backend('REXML') { parsed_xml } + assert_equal(hash, parsed_xml) + end end end diff --git a/activesupport/test/xml_mini/nokogirisax_engine_test.rb b/activesupport/test/xml_mini/nokogirisax_engine_test.rb index 884494e95e..d4f63f6bd0 100644 --- a/activesupport/test/xml_mini/nokogirisax_engine_test.rb +++ b/activesupport/test/xml_mini/nokogirisax_engine_test.rb @@ -156,7 +156,7 @@ class NokogiriSAXEngineTest < ActiveSupport::TestCase morning </root> eoxml - XmlMini.parse(io) + assert_equal_rexml(io) end def test_children_with_simple_cdata @@ -207,10 +207,11 @@ class NokogiriSAXEngineTest < ActiveSupport::TestCase end private - def assert_equal_rexml(xml) - hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } - assert_equal(hash, XmlMini.parse(xml)) - end + def assert_equal_rexml(xml) + parsed_xml = XmlMini.parse(xml) + hash = XmlMini.with_backend('REXML') { parsed_xml } + assert_equal(hash, parsed_xml) + end end end diff --git a/activesupport/test/xml_mini/rexml_engine_test.rb b/activesupport/test/xml_mini/rexml_engine_test.rb index c4770405f2..70a3b918fd 100644 --- a/activesupport/test/xml_mini/rexml_engine_test.rb +++ b/activesupport/test/xml_mini/rexml_engine_test.rb @@ -24,6 +24,13 @@ class REXMLEngineTest < ActiveSupport::TestCase morning </root> eoxml - XmlMini.parse(io) + assert_equal_rexml(io) end + + private + def assert_equal_rexml(xml) + parsed_xml = XmlMini.parse(xml) + hash = XmlMini.with_backend('REXML') { parsed_xml } + assert_equal(hash, parsed_xml) + end end |