aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib')
-rw-r--r--activesupport/lib/active_support/backtrace_cleaner.rb10
-rw-r--r--activesupport/lib/active_support/benchmarkable.rb10
-rw-r--r--activesupport/lib/active_support/cache.rb141
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb4
-rw-r--r--activesupport/lib/active_support/cache/strategy/local_cache.rb85
-rw-r--r--activesupport/lib/active_support/callbacks.rb647
-rw-r--r--activesupport/lib/active_support/concern.rb8
-rw-r--r--activesupport/lib/active_support/core_ext.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/array/grouping.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/array/uniq_by.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/array/wrap.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/benchmark.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/big_decimal/conversions.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute.rb60
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute_accessors.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/date.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb11
-rw-r--r--activesupport/lib/active_support/core_ext/date/conversions.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/date/infinite_comparable.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/date_and_time/calculations.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/date_time.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb22
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/conversions.rb11
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/zones.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/hash/diff.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/infinite_comparable.rb35
-rw-r--r--activesupport/lib/active_support/core_ext/marshal.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb74
-rw-r--r--activesupport/lib/active_support/core_ext/module/deprecation.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/numeric.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/object/inclusion.rb28
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_param.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/range/include_range.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/string/conversions.rb15
-rw-r--r--activesupport/lib/active_support/core_ext/string/filters.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/indent.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/string/xchar.rb18
-rw-r--r--activesupport/lib/active_support/core_ext/time.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb27
-rw-r--r--activesupport/lib/active_support/core_ext/time/infinite_comparable.rb5
-rw-r--r--activesupport/lib/active_support/dependencies.rb2
-rw-r--r--activesupport/lib/active_support/deprecation/proxy_wrappers.rb6
-rw-r--r--activesupport/lib/active_support/file_update_checker.rb2
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb26
-rw-r--r--activesupport/lib/active_support/i18n.rb8
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb7
-rw-r--r--activesupport/lib/active_support/json/decoding.rb25
-rw-r--r--activesupport/lib/active_support/json/encoding.rb14
-rw-r--r--activesupport/lib/active_support/json/variable.rb18
-rw-r--r--activesupport/lib/active_support/key_generator.rb4
-rw-r--r--activesupport/lib/active_support/lazy_load_hooks.rb2
-rw-r--r--activesupport/lib/active_support/log_subscriber.rb55
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb18
-rw-r--r--activesupport/lib/active_support/message_verifier.rb6
-rw-r--r--activesupport/lib/active_support/multibyte/unicode.rb64
-rw-r--r--activesupport/lib/active_support/notifications.rb27
-rw-r--r--activesupport/lib/active_support/notifications/fanout.rb7
-rw-r--r--activesupport/lib/active_support/notifications/instrumenter.rb4
-rw-r--r--activesupport/lib/active_support/number_helper.rb7
-rw-r--r--activesupport/lib/active_support/ordered_options.rb8
-rw-r--r--activesupport/lib/active_support/per_thread_registry.rb52
-rw-r--r--activesupport/lib/active_support/subscriber.rb93
-rw-r--r--activesupport/lib/active_support/test_case.rb28
-rw-r--r--activesupport/lib/active_support/testing/autorun.rb4
-rw-r--r--activesupport/lib/active_support/testing/constant_lookup.rb2
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb59
-rw-r--r--activesupport/lib/active_support/testing/pending.rb14
-rw-r--r--activesupport/lib/active_support/testing/tagged_logging.rb2
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb12
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb8
-rw-r--r--activesupport/lib/active_support/version.rb13
-rw-r--r--activesupport/lib/active_support/xml_mini/jdom.rb6
-rw-r--r--activesupport/lib/active_support/xml_mini/libxmlsax.rb2
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogirisax.rb2
80 files changed, 1127 insertions, 795 deletions
diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb
index 4b41e6247d..6f9dc679c2 100644
--- a/activesupport/lib/active_support/backtrace_cleaner.rb
+++ b/activesupport/lib/active_support/backtrace_cleaner.rb
@@ -13,17 +13,17 @@ module ActiveSupport
# can focus on the rest.
#
# bc = BacktraceCleaner.new
- # bc.add_filter { |line| line.gsub(Rails.root, '') }
- # bc.add_silencer { |line| line =~ /mongrel|rubygems/ }
- # bc.clean(exception.backtrace) # will strip the Rails.root prefix and skip any lines from mongrel or rubygems
+ # bc.add_filter { |line| line.gsub(Rails.root, '') } # strip the Rails.root prefix
+ # bc.add_silencer { |line| line =~ /mongrel|rubygems/ } # skip any lines from mongrel or rubygems
+ # bc.clean(exception.backtrace) # perform the cleanup
#
# To reconfigure an existing BacktraceCleaner (like the default one in Rails)
# and show as much data as possible, you can always call
# <tt>BacktraceCleaner#remove_silencers!</tt>, which will restore the
# backtrace to a pristine state. If you need to reconfigure an existing
# BacktraceCleaner so that it does not filter or modify the paths of any lines
- # of the backtrace, you can call BacktraceCleaner#remove_filters! These two
- # methods will give you a completely untouched backtrace.
+ # of the backtrace, you can call <tt>BacktraceCleaner#remove_filters!<tt>
+ # These two methods will give you a completely untouched backtrace.
#
# Inspired by the Quiet Backtrace gem by Thoughtbot.
class BacktraceCleaner
diff --git a/activesupport/lib/active_support/benchmarkable.rb b/activesupport/lib/active_support/benchmarkable.rb
index 6413502b53..805b7a714f 100644
--- a/activesupport/lib/active_support/benchmarkable.rb
+++ b/activesupport/lib/active_support/benchmarkable.rb
@@ -45,15 +45,5 @@ module ActiveSupport
yield
end
end
-
- # Silence the logger during the execution of the block.
- def silence
- message = "ActiveSupport::Benchmarkable#silence is deprecated. It will be removed from Rails 4.1."
- ActiveSupport::Deprecation.warn message
- old_logger_level, logger.level = logger.level, ::Logger::ERROR if logger
- yield
- ensure
- logger.level = old_logger_level if logger
- end
end
end
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index edbe697962..c539048a85 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -56,16 +56,7 @@ module ActiveSupport
case store
when Symbol
- store_class_name = store.to_s.camelize
- store_class =
- begin
- require "active_support/cache/#{store}"
- rescue LoadError => e
- raise "Could not find cache store adapter for #{store} (#{e})"
- else
- ActiveSupport::Cache.const_get(store_class_name)
- end
- store_class.new(*parameters)
+ retrieve_store_class(store).new(*parameters)
when nil
ActiveSupport::Cache::MemoryStore.new
else
@@ -73,6 +64,18 @@ module ActiveSupport
end
end
+ # Expands out the +key+ argument into a key that can be used for the
+ # cache store. Optionally accepts a namespace, and all keys will be
+ # scoped within that namespace.
+ #
+ # If the +key+ argument provided is an array, or responds to +to_a+, then
+ # each of elements in the array will be turned into parameters/keys and
+ # concatenated into a single key. For example:
+ #
+ # expand_cache_key([:foo, :bar]) # => "foo/bar"
+ # expand_cache_key([:foo, :bar], "namespace") # => "namespace/foo/bar"
+ #
+ # The +key+ argument can also respond to +cache_key+ or +to_param+.
def expand_cache_key(key, namespace = nil)
expanded_cache_key = namespace ? "#{namespace}/" : ""
@@ -94,6 +97,16 @@ module ActiveSupport
else key.to_param
end.to_s
end
+
+ # Obtains the specified cache store class, given the name of the +store+.
+ # Raises an error when the store class cannot be found.
+ def retrieve_store_class(store)
+ require "active_support/cache/#{store}"
+ rescue LoadError => e
+ raise "Could not find cache store adapter for #{store} (#{e})"
+ else
+ ActiveSupport::Cache.const_get(store.to_s.camelize)
+ end
end
# An abstract cache store class. There are multiple cache store
@@ -215,7 +228,7 @@ module ActiveSupport
#
# Setting <tt>:race_condition_ttl</tt> is very useful in situations where
# a cache entry is used very frequently and is under heavy load. If a
- # cache expires and due to heavy load seven different processes will try
+ # cache expires and due to heavy load several different processes will try
# to read data natively and then they all will try to write to cache. To
# avoid that case the first process to find an expired cache entry will
# bump the cache expiration time by the value set in <tt>:race_condition_ttl</tt>.
@@ -339,12 +352,40 @@ module ActiveSupport
results
end
+ # Fetches data from the cache, using the given keys. If there is data in
+ # the cache with the given keys, then that data is returned. Otherwise,
+ # the supplied block is called for each key for which there was no data,
+ # and the result will be written to the cache and returned.
+ #
+ # Options are passed to the underlying cache implementation.
+ #
+ # Returns an array with the data for each of the names. For example:
+ #
+ # cache.write("bim", "bam")
+ # cache.fetch_multi("bim", "boom") {|key| key * 2 }
+ # #=> ["bam", "boomboom"]
+ #
+ def fetch_multi(*names)
+ options = names.extract_options!
+ options = merged_options(options)
+
+ results = read_multi(*names, options)
+
+ names.map do |name|
+ results.fetch(name) do
+ value = yield name
+ write(name, value, options)
+ value
+ end
+ end
+ end
+
# Writes the value to the cache, with the key.
#
# Options are passed to the underlying cache implementation.
def write(name, value, options = nil)
options = merged_options(options)
- instrument(:write, name, options) do |payload|
+ instrument(:write, name, options) do
entry = Entry.new(value, options)
write_entry(namespaced_key(name, options), entry, options)
end
@@ -355,7 +396,7 @@ module ActiveSupport
# Options are passed to the underlying cache implementation.
def delete(name, options = nil)
options = merged_options(options)
- instrument(:delete, name) do |payload|
+ instrument(:delete, name) do
delete_entry(namespaced_key(name, options), options)
end
end
@@ -365,7 +406,7 @@ module ActiveSupport
# Options are passed to the underlying cache implementation.
def exist?(name, options = nil)
options = merged_options(options)
- instrument(:exist?, name) do |payload|
+ instrument(:exist?, name) do
entry = read_entry(namespaced_key(name, options), options)
entry && !entry.expired?
end
@@ -522,7 +563,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 +603,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 +647,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 +657,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 +678,7 @@ module ActiveSupport
end
def compressed?
- defined?(@c) ? @c : false
+ defined?(@compressed) ? @compressed : false
end
def compress(value)
@@ -650,19 +691,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/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index 8e265ad863..0c55aa8a32 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -150,9 +150,9 @@ module ActiveSupport
# Delete empty directories in the cache.
def delete_empty_directories(dir)
- return if dir == cache_path
+ return if File.realpath(dir) == File.realpath(cache_path)
if Dir.entries(dir).reject {|f| EXCLUDED_DIRS.include?(f)}.empty?
- File.delete(dir) rescue nil
+ Dir.delete(dir) rescue nil
delete_empty_directories(File.dirname(dir))
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 e3e1845868..2cffa342ef 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -1,9 +1,9 @@
-require 'thread_safe'
require 'active_support/concern'
require 'active_support/descendants_tracker'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/kernel/singleton_class'
+require 'thread'
module ActiveSupport
# Callbacks are code hooks that are run at key points in an object's lifecycle.
@@ -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
@@ -74,8 +76,14 @@ module ActiveSupport
# save
# end
def run_callbacks(kind, &block)
- runner_name = self.class.__define_callbacks(kind, self)
- send(runner_name, &block)
+ cbs = send("_#{kind}_callbacks")
+ if cbs.empty?
+ yield if block_given?
+ else
+ runner = cbs.compile
+ e = Filters::Environment.new(self, false, nil, block)
+ runner.call(e).value
+ end
end
private
@@ -86,159 +94,315 @@ module ActiveSupport
def halted_callback_hook(filter)
end
- class Callback #:nodoc:#
- @@_callback_sequence = 0
-
- attr_accessor :chain, :filter, :kind, :options, :klass, :raw_filter
+ module Conditionals # :nodoc:
+ class Value
+ def initialize(&block)
+ @block = block
+ end
+ def call(target, value); @block.call(value); end
+ end
+ end
- def initialize(chain, filter, kind, options, klass)
- @chain, @kind, @klass = chain, kind, klass
- deprecate_per_key_option(options)
- normalize_options!(options)
+ module Filters
+ Environment = Struct.new(:target, :halted, :value, :run_block)
- @raw_filter, @options = filter, options
- @filter = _compile_filter(filter)
- recompile_options!
+ class End
+ def call(env)
+ block = env.run_block
+ env.value = !env.halted && (!block || block.call)
+ env
+ end
end
+ ENDING = End.new
+
+ class Before
+ def self.build(next_callback, user_callback, user_conditions, chain_config, filter)
+ halted_lambda = chain_config[:terminator]
- def deprecate_per_key_option(options)
- if options[:per_key]
- raise NotImplementedError, ":per_key option is no longer supported. Use generic :if and :unless options instead."
+ if chain_config.key?(:terminator) && user_conditions.any?
+ halting_and_conditional(next_callback, user_callback, user_conditions, halted_lambda, filter)
+ elsif chain_config.key? :terminator
+ halting(next_callback, user_callback, halted_lambda, filter)
+ elsif user_conditions.any?
+ conditional(next_callback, user_callback, user_conditions)
+ else
+ simple next_callback, user_callback
+ end
end
- end
- def clone(chain, klass)
- obj = super()
- obj.chain = chain
- obj.klass = klass
- obj.options = @options.dup
- obj.options[:if] = @options[:if].dup
- obj.options[:unless] = @options[:unless].dup
- obj
- end
+ private
- def normalize_options!(options)
- options[:if] = Array(options[:if])
- options[:unless] = Array(options[:unless])
+ def self.halting_and_conditional(next_callback, user_callback, user_conditions, halted_lambda, filter)
+ lambda { |env|
+ target = env.target
+ value = env.value
+ halted = env.halted
+
+ if !halted && user_conditions.all? { |c| c.call(target, value) }
+ result = user_callback.call target, value
+ env.halted = halted_lambda.call(target, result)
+ if env.halted
+ target.send :halted_callback_hook, filter
+ end
+ end
+ next_callback.call env
+ }
+ end
+
+ def self.halting(next_callback, user_callback, halted_lambda, filter)
+ lambda { |env|
+ target = env.target
+ value = env.value
+ halted = env.halted
+
+ unless halted
+ result = user_callback.call target, value
+ env.halted = halted_lambda.call(target, result)
+ if env.halted
+ target.send :halted_callback_hook, filter
+ end
+ end
+ next_callback.call env
+ }
+ end
+
+ def self.conditional(next_callback, user_callback, user_conditions)
+ lambda { |env|
+ target = env.target
+ value = env.value
+
+ if user_conditions.all? { |c| c.call(target, value) }
+ user_callback.call target, value
+ end
+ next_callback.call env
+ }
+ end
+
+ def self.simple(next_callback, user_callback)
+ lambda { |env|
+ user_callback.call env.target, env.value
+ next_callback.call env
+ }
+ end
end
- def name
- chain.name
+ class After
+ def self.build(next_callback, user_callback, user_conditions, chain_config)
+ if chain_config[:skip_after_callbacks_if_terminated]
+ if chain_config.key?(:terminator) && user_conditions.any?
+ halting_and_conditional(next_callback, user_callback, user_conditions)
+ elsif chain_config.key?(:terminator)
+ halting(next_callback, user_callback)
+ elsif user_conditions.any?
+ conditional next_callback, user_callback, user_conditions
+ else
+ simple next_callback, user_callback
+ end
+ else
+ if user_conditions.any?
+ conditional next_callback, user_callback, user_conditions
+ else
+ simple next_callback, user_callback
+ end
+ end
+ end
+
+ private
+
+ def self.halting_and_conditional(next_callback, user_callback, user_conditions)
+ lambda { |env|
+ env = next_callback.call env
+ target = env.target
+ value = env.value
+ halted = env.halted
+
+ if !halted && user_conditions.all? { |c| c.call(target, value) }
+ user_callback.call target, value
+ end
+ env
+ }
+ end
+
+ def self.halting(next_callback, user_callback)
+ lambda { |env|
+ env = next_callback.call env
+ unless env.halted
+ user_callback.call env.target, env.value
+ end
+ env
+ }
+ end
+
+ def self.conditional(next_callback, user_callback, user_conditions)
+ lambda { |env|
+ env = next_callback.call env
+ target = env.target
+ value = env.value
+
+ if user_conditions.all? { |c| c.call(target, value) }
+ user_callback.call target, value
+ end
+ env
+ }
+ end
+
+ def self.simple(next_callback, user_callback)
+ lambda { |env|
+ env = next_callback.call env
+ user_callback.call env.target, env.value
+ env
+ }
+ end
end
- def next_id
- @@_callback_sequence += 1
+ class Around
+ def self.build(next_callback, user_callback, user_conditions, chain_config)
+ if chain_config.key?(:terminator) && user_conditions.any?
+ halting_and_conditional(next_callback, user_callback, user_conditions)
+ elsif chain_config.key? :terminator
+ halting(next_callback, user_callback)
+ elsif user_conditions.any?
+ conditional(next_callback, user_callback, user_conditions)
+ else
+ simple(next_callback, user_callback)
+ end
+ end
+
+ private
+
+ def self.halting_and_conditional(next_callback, user_callback, user_conditions)
+ lambda { |env|
+ target = env.target
+ value = env.value
+ halted = env.halted
+
+ if !halted && user_conditions.all? { |c| c.call(target, value) }
+ user_callback.call(target, value) {
+ env = next_callback.call env
+ env.value
+ }
+ env
+ else
+ next_callback.call env
+ end
+ }
+ end
+
+ def self.halting(next_callback, user_callback)
+ lambda { |env|
+ target = env.target
+ value = env.value
+
+ unless env.halted
+ user_callback.call(target, value) {
+ env = next_callback.call env
+ env.value
+ }
+ env
+ else
+ next_callback.call env
+ end
+ }
+ end
+
+ def self.conditional(next_callback, user_callback, user_conditions)
+ lambda { |env|
+ target = env.target
+ value = env.value
+
+ if user_conditions.all? { |c| c.call(target, value) }
+ user_callback.call(target, value) {
+ env = next_callback.call env
+ env.value
+ }
+ env
+ else
+ next_callback.call env
+ end
+ }
+ end
+
+ def self.simple(next_callback, user_callback)
+ lambda { |env|
+ user_callback.call(env.target, env.value) {
+ env = next_callback.call env
+ env.value
+ }
+ env
+ }
+ end
end
+ end
- def matches?(_kind, _filter)
- @kind == _kind && @filter == _filter
+ class Callback #:nodoc:#
+ def self.build(chain, filter, kind, options)
+ new chain.name, filter, kind, options, chain.config
end
- def duplicates?(other)
- matches?(other.kind, other.filter)
+ attr_accessor :kind, :name
+ attr_reader :chain_config
+
+ def initialize(name, filter, kind, options, chain_config)
+ @chain_config = chain_config
+ @name = name
+ @kind = kind
+ @filter = filter
+ @key = compute_identifier filter
+ @if = Array(options[:if])
+ @unless = Array(options[:unless])
end
- def _update_filter(filter_options, new_options)
- filter_options[:if].concat(Array(new_options[:unless])) if new_options.key?(:unless)
- filter_options[:unless].concat(Array(new_options[:if])) if new_options.key?(:if)
+ def filter; @key; end
+ def raw_filter; @filter; end
+
+ def merge(chain, new_options)
+ options = {
+ :if => @if.dup,
+ :unless => @unless.dup
+ }
+
+ options[:if].concat Array(new_options.fetch(:unless, []))
+ options[:unless].concat Array(new_options.fetch(:if, []))
+
+ self.class.build chain, @filter, @kind, options
end
- def recompile!(_options)
- deprecate_per_key_option(_options)
- _update_filter(self.options, _options)
+ def matches?(_kind, _filter)
+ @kind == _kind && filter == _filter
+ end
- recompile_options!
+ def duplicates?(other)
+ case @filter
+ when Symbol, String
+ matches?(other.kind, other.filter)
+ else
+ false
+ end
end
# Wraps code with filter
- def apply(code)
- case @kind
+ def apply(next_callback)
+ user_conditions = conditions_lambdas
+ user_callback = make_lambda @filter
+
+ case kind
when :before
- <<-RUBY_EVAL
- if !halted && #{@compiled_options}
- # This double assignment is to prevent warnings in 1.9.3 as
- # the `result` variable is not always used except if the
- # terminator code refers to it.
- result = result = #{@filter}
- halted = (#{chain.config[:terminator]})
- if halted
- halted_callback_hook(#{@raw_filter.inspect.inspect})
- end
- end
- #{code}
- RUBY_EVAL
+ Filters::Before.build(next_callback, user_callback, user_conditions, chain_config, @filter)
when :after
- <<-RUBY_EVAL
- #{code}
- if #{!chain.config[:skip_after_callbacks_if_terminated] || "!halted"} && #{@compiled_options}
- #{@filter}
- end
- RUBY_EVAL
+ Filters::After.build(next_callback, user_callback, user_conditions, chain_config)
when :around
- name = define_conditional_callback
- <<-RUBY_EVAL
- #{name}(halted) do
- #{code}
- value
- end
- RUBY_EVAL
+ Filters::Around.build(next_callback, user_callback, user_conditions, chain_config)
end
end
private
- # Compile around filters with conditions into proxy methods
- # that contain the conditions.
- #
- # For `set_callback :save, :around, :filter_name, if: :condition':
- #
- # def _conditional_callback_save_17
- # if condition
- # filter_name do
- # yield self
- # end
- # else
- # yield self
- # end
- # end
- def define_conditional_callback
- name = "_conditional_callback_#{@kind}_#{next_id}"
- @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def #{name}(halted)
- if #{@compiled_options} && !halted
- #{@filter} do
- yield self
- end
- else
- yield self
- end
- end
- RUBY_EVAL
- name
- end
-
- # Options support the same options as filters themselves (and support
- # symbols, string, procs, and objects), so compile a conditional
- # expression based on the options.
- def recompile_options!
- conditions = ["true"]
-
- unless options[:if].empty?
- conditions << Array(_compile_filter(options[:if]))
- end
-
- unless options[:unless].empty?
- conditions << Array(_compile_filter(options[:unless])).map {|f| "!#{f}"}
- end
-
- @compiled_options = conditions.flatten.join(" && ")
+ def invert_lambda(l)
+ lambda { |*args, &blk| !l.call(*args, &blk) }
end
# Filters support:
#
- # Arrays:: Used in conditions. This is used to specify
- # multiple conditions. Used internally to
- # merge conditions from skip_* filters.
# Symbols:: A method to call.
# Strings:: Some content to evaluate.
# Procs:: A proc to call with the object.
@@ -247,89 +411,106 @@ module ActiveSupport
# All of these objects are compiled into methods and handled
# the same after this point:
#
- # Arrays:: Merged together into a single filter.
# Symbols:: Already methods.
- # Strings:: class_eval'ed into methods.
- # Procs:: define_method'ed into methods.
+ # Strings:: class_eval'd into methods.
+ # Procs:: using define_method compiled into methods.
# Objects::
# a method is created that calls the before_foo method
# on the object.
- def _compile_filter(filter)
- method_name = "_callback_#{@kind}_#{next_id}"
+ def make_lambda(filter)
case filter
- when Array
- filter.map {|f| _compile_filter(f)}
when Symbol
- filter
+ lambda { |target, _, &blk| target.send filter, &blk }
when String
- "(#{filter})"
- when Proc
- @klass.send(:define_method, method_name, &filter)
- return method_name if filter.arity <= 0
+ l = eval "lambda { |value| #{filter} }"
+ lambda { |target, value| target.instance_exec(value, &l) }
+ when Conditionals::Value then filter
+ when ::Proc
+ if filter.arity > 1
+ return lambda { |target, _, &block|
+ raise ArgumentError unless block
+ target.instance_exec(target, block, &filter)
+ }
+ end
- method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ")
+ if filter.arity <= 0
+ lambda { |target, _| target.instance_exec(&filter) }
+ else
+ lambda { |target, _| target.instance_exec(target, &filter) }
+ end
else
- @klass.send(:define_method, "#{method_name}_object") { filter }
-
- _normalize_legacy_filter(kind, filter)
- scopes = Array(chain.config[:scope])
- method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_")
+ scopes = Array(chain_config[:scope])
+ method_to_call = scopes.map{ |s| public_send(s) }.join("_")
- @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def #{method_name}(&blk)
- #{method_name}_object.send(:#{method_to_call}, self, &blk)
- end
- RUBY_EVAL
-
- method_name
+ lambda { |target, _, &blk|
+ filter.public_send method_to_call, target, &blk
+ }
end
end
- def _normalize_legacy_filter(kind, filter)
- if !filter.respond_to?(kind) && filter.respond_to?(:filter)
- message = "Filter object with #filter method is deprecated. Define method corresponding " \
- "to filter type (#before, #after or #around)."
- ActiveSupport::Deprecation.warn message
- filter.singleton_class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def #{kind}(context, &block) filter(context, &block) end
- RUBY_EVAL
- elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around && !filter.respond_to?(:around)
- message = "Filter object with #before and #after methods is deprecated. Define #around method instead."
- ActiveSupport::Deprecation.warn message
- def filter.around(context)
- should_continue = before(context)
- yield if should_continue
- after(context)
- end
+ def compute_identifier(filter)
+ case filter
+ when String, ::Proc
+ filter.object_id
+ else
+ filter
end
end
+
+ def conditions_lambdas
+ @if.map { |c| make_lambda c } +
+ @unless.map { |c| invert_lambda make_lambda c }
+ end
end
# An Array with a compile method.
- class CallbackChain < Array #:nodoc:#
+ class CallbackChain #:nodoc:#
+ include Enumerable
+
attr_reader :name, :config
def initialize(name, config)
@name = name
@config = {
- :terminator => "false",
:scope => [ :kind ]
- }.merge(config)
+ }.merge!(config)
+ @chain = []
+ @callbacks = nil
+ @mutex = Mutex.new
end
- def compile
- method = []
- method << "value = nil"
- method << "halted = false"
+ def each(&block); @chain.each(&block); end
+ def index(o); @chain.index(o); end
+ def empty?; @chain.empty?; end
- callbacks = "value = !halted && (!block_given? || yield)"
- reverse_each do |callback|
- callbacks = callback.apply(callbacks)
- end
- method << callbacks
+ def insert(index, o)
+ @callbacks = nil
+ @chain.insert(index, o)
+ end
+
+ def delete(o)
+ @callbacks = nil
+ @chain.delete(o)
+ end
+
+ def clear
+ @callbacks = nil
+ @chain.clear
+ self
+ end
- method << "value"
- method.join("\n")
+ def initialize_copy(other)
+ @callbacks = nil
+ @chain = other.chain.dup
+ @mutex = Mutex.new
+ end
+
+ def compile
+ @callbacks || @mutex.synchronize do
+ @callbacks ||= @chain.reverse.inject(Filters::ENDING) do |chain, callback|
+ callback.apply chain
+ end
+ end
end
def append(*callbacks)
@@ -340,69 +521,45 @@ module ActiveSupport
callbacks.each { |c| prepend_one(c) }
end
+ protected
+ def chain; @chain; end
+
private
def append_one(callback)
+ @callbacks = nil
remove_duplicates(callback)
- push(callback)
+ @chain.push(callback)
end
def prepend_one(callback)
+ @callbacks = nil
remove_duplicates(callback)
- unshift(callback)
+ @chain.unshift(callback)
end
def remove_duplicates(callback)
- delete_if { |c| callback.duplicates?(c) }
+ @callbacks = nil
+ @chain.delete_if { |c| callback.duplicates?(c) }
end
end
module ClassMethods
- # This method defines callback chain method for the given kind
- # if it was not yet defined.
- # This generated method plays caching role.
- def __define_callbacks(kind, object) #:nodoc:
- name = __callback_runner_name(kind)
- unless object.respond_to?(name, true)
- str = object.send("_#{kind}_callbacks").compile
- class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def #{name}() #{str} end
- protected :#{name}
- RUBY_EVAL
- end
- name
- end
-
- def __reset_runner(symbol)
- name = __callback_runner_name(symbol)
- undef_method(name) if method_defined?(name)
- end
-
- def __callback_runner_name_cache
- @__callback_runner_name_cache ||= ThreadSafe::Cache.new {|cache, kind| cache[kind] = __generate_callback_runner_name(kind) }
- end
-
- def __generate_callback_runner_name(kind)
- "_run__#{self.name.hash.abs}__#{kind}__callbacks"
- end
-
- def __callback_runner_name(kind)
- __callback_runner_name_cache[kind]
+ def normalize_callback_params(filters, block) # :nodoc:
+ type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
+ options = filters.last.is_a?(Hash) ? filters.pop : {}
+ filters.unshift(block) if block
+ [type, filters, options.dup]
end
# 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
- options = filters.last.is_a?(Hash) ? filters.pop : {}
- filters.unshift(block) if block
-
+ def __update_callbacks(name) #:nodoc:
([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target|
- chain = target.send("_#{name}_callbacks")
- yield target, chain.dup, type, filters, options
- target.__reset_runner(name)
+ chain = target.get_callbacks name
+ yield target, chain.dup
end
end
@@ -442,16 +599,15 @@ module ActiveSupport
# * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
# existing chain rather than appended.
def set_callback(name, *filter_list, &block)
- mapped = nil
-
- __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
- mapped ||= filters.map do |filter|
- Callback.new(chain, filter, type, options.dup, self)
- end
+ type, filters, options = normalize_callback_params(filter_list, block)
+ self_chain = get_callbacks name
+ mapped = filters.map do |filter|
+ Callback.build(self_chain, filter, type, options)
+ end
+ __update_callbacks(name) do |target, chain|
options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
-
- target.send("_#{name}_callbacks=", chain)
+ target.set_callbacks name, chain
end
end
@@ -463,36 +619,34 @@ module ActiveSupport
# skip_callback :validate, :before, :check_membership, if: -> { self.age > 18 }
# end
def skip_callback(name, *filter_list, &block)
- __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
+ type, filters, options = normalize_callback_params(filter_list, block)
+
+ __update_callbacks(name) do |target, chain|
filters.each do |filter|
filter = chain.find {|c| c.matches?(type, filter) }
if filter && options.any?
- new_filter = filter.clone(chain, self)
+ new_filter = filter.merge(chain, options)
chain.insert(chain.index(filter), new_filter)
- new_filter.recompile!(options)
end
chain.delete(filter)
end
- target.send("_#{name}_callbacks=", chain)
+ target.set_callbacks name, chain
end
end
# Remove all set callbacks for the given event.
def reset_callbacks(symbol)
- callbacks = send("_#{symbol}_callbacks")
+ callbacks = get_callbacks symbol
ActiveSupport::DescendantsTracker.descendants(self).each do |target|
- chain = target.send("_#{symbol}_callbacks").dup
+ chain = target.get_callbacks(symbol).dup
callbacks.each { |c| chain.delete(c) }
- target.send("_#{symbol}_callbacks=", chain)
- target.__reset_runner(symbol)
+ target.set_callbacks symbol, chain
end
- self.send("_#{symbol}_callbacks=", callbacks.dup.clear)
-
- __reset_runner(symbol)
+ self.set_callbacks symbol, callbacks.dup.clear
end
# Define sets of events in the object lifecycle that support callbacks.
@@ -504,7 +658,7 @@ module ActiveSupport
#
# * <tt>:terminator</tt> - Determines when a before filter will halt the
# callback chain, preventing following callbacks from being called and
- # the event from being triggered. This is a string to be eval'ed. The
+ # the event from being triggered. This is a string to be eval'd. The
# result of the callback is available in the +result+ variable.
#
# define_callbacks :validate, terminator: 'result == false'
@@ -564,11 +718,28 @@ module ActiveSupport
# would call <tt>Audit#save</tt>.
def define_callbacks(*callbacks)
config = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
+ if config.key?(:terminator) && String === config[:terminator]
+ ActiveSupport::Deprecation.warn "String based terminators are deprecated, please use a lambda"
+ value = config[:terminator]
+ l = class_eval "lambda { |result| #{value} }", __FILE__, __LINE__
+ config[:terminator] = lambda { |target, result| target.instance_exec(result, &l) }
+ end
+
callbacks.each do |callback|
class_attribute "_#{callback}_callbacks"
- send("_#{callback}_callbacks=", CallbackChain.new(callback, config))
+ set_callbacks callback, CallbackChain.new(callback, config)
end
end
+
+ protected
+
+ def get_callbacks(name)
+ send "_#{name}_callbacks"
+ end
+
+ def set_callbacks(name, callbacks)
+ send "_#{name}_callbacks=", callbacks
+ end
end
end
end
diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb
index eeeba60839..b6ae86b583 100644
--- a/activesupport/lib/active_support/concern.rb
+++ b/activesupport/lib/active_support/concern.rb
@@ -98,6 +98,12 @@ module ActiveSupport
# include Bar # works, Bar takes care now of its dependencies
# end
module Concern
+ class MultipleIncludedBlocks < StandardError #:nodoc:
+ def initialize
+ super "Cannot define multiple 'included' blocks for a Concern"
+ end
+ end
+
def self.extended(base) #:nodoc:
base.instance_variable_set("@_dependencies", [])
end
@@ -117,6 +123,8 @@ module ActiveSupport
def included(base = nil, &block)
if base.nil?
+ raise MultipleIncludedBlocks if instance_variable_defined?("@_included_block")
+
@_included_block = block
else
super
diff --git a/activesupport/lib/active_support/core_ext.rb b/activesupport/lib/active_support/core_ext.rb
index b48bdf08e8..998a59c618 100644
--- a/activesupport/lib/active_support/core_ext.rb
+++ b/activesupport/lib/active_support/core_ext.rb
@@ -1,4 +1,4 @@
-Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].sort.each do |path|
+Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].each do |path|
next if File.basename(path, '.rb') == 'logger'
- require "active_support/core_ext/#{File.basename(path, '.rb')}"
+ require path
end
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/grouping.rb b/activesupport/lib/active_support/core_ext/array/grouping.rb
index 640e6e9328..dbddc7a7b4 100644
--- a/activesupport/lib/active_support/core_ext/array/grouping.rb
+++ b/activesupport/lib/active_support/core_ext/array/grouping.rb
@@ -25,7 +25,7 @@ class Array
# subtracting from number gives how many to add;
# modulo number ensures we don't add group of just fill.
padding = (number - size % number) % number
- collection = dup.concat([fill_with] * padding)
+ collection = dup.concat(Array.new(padding, fill_with))
end
if block_given?
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/array/wrap.rb b/activesupport/lib/active_support/core_ext/array/wrap.rb
index 1245768870..152eb02218 100644
--- a/activesupport/lib/active_support/core_ext/array/wrap.rb
+++ b/activesupport/lib/active_support/core_ext/array/wrap.rb
@@ -15,12 +15,12 @@ class Array
#
# * If the argument responds to +to_ary+ the method is invoked. <tt>Kernel#Array</tt>
# moves on to try +to_a+ if the returned value is +nil+, but <tt>Array.wrap</tt> returns
- # such a +nil+ right away.
+ # +nil+ right away.
# * If the returned value from +to_ary+ is neither +nil+ nor an +Array+ object, <tt>Kernel#Array</tt>
# raises an exception, while <tt>Array.wrap</tt> does not, it just returns the value.
- # * It does not call +to_a+ on the argument, though special-cases +nil+ to return an empty array.
+ # * It does not call +to_a+ on the argument, but returns an empty array if argument is +nil+.
#
- # The last point is particularly worth comparing for some enumerables:
+ # The second point is easily explained with some enumerables:
#
# Array(foo: :bar) # => [[:foo, :bar]]
# Array.wrap(foo: :bar) # => [{:foo=>:bar}]
@@ -29,10 +29,10 @@ class Array
#
# [*object]
#
- # which for +nil+ returns <tt>[]</tt>, and calls to <tt>Array(object)</tt> otherwise.
+ # which returns <tt>[]</tt> for +nil+, but calls to <tt>Array(object)</tt> otherwise.
#
- # Thus, in this case the behavior may be different for +nil+, and the differences with
- # <tt>Kernel#Array</tt> explained above apply to the rest of <tt>object</tt>s.
+ # The differences with <tt>Kernel#Array</tt> explained above
+ # apply to the rest of <tt>object</tt>s.
def self.wrap(object)
if object.nil?
[]
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 5d8d09aa69..6fa9967a28 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
#
@@ -70,45 +71,48 @@ class Class
def class_attribute(*attrs)
options = attrs.extract_options!
# 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_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)
- # We use class_eval here rather than define_method because class_attribute
- # may be used in a performance sensitive context therefore the overhead that
- # define_method introduces may become significant.
attrs.each do |name|
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def self.#{name}() nil end
- def self.#{name}?() !!#{name} end
+ define_singleton_method(name) { nil }
+ define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
- def self.#{name}=(val)
- singleton_class.class_eval do
- remove_possible_method(:#{name})
- define_method(:#{name}) { val }
- end
+ ivar = "@#{name}"
+
+ define_singleton_method("#{name}=") do |val|
+ singleton_class.class_eval do
+ remove_possible_method(name)
+ define_method(name) { val }
+ end
- if singleton_class?
- class_eval do
- remove_possible_method(:#{name})
- def #{name}
- defined?(@#{name}) ? @#{name} : singleton_class.#{name}
+ if singleton_class?
+ class_eval do
+ remove_possible_method(name)
+ define_method(name) do
+ if instance_variable_defined? ivar
+ instance_variable_get ivar
+ else
+ singleton_class.send name
end
end
end
- val
end
+ val
+ end
- if instance_reader
- remove_possible_method :#{name}
- def #{name}
- defined?(@#{name}) ? @#{name} : self.class.#{name}
- end
-
- def #{name}?
- !!#{name}
+ if instance_reader
+ remove_possible_method name
+ define_method(name) do
+ if instance_variable_defined?(ivar)
+ instance_variable_get ivar
+ else
+ self.class.public_send name
end
end
- RUBY
+ define_method("#{name}?") { !!public_send(name) } if instance_predicate
+ end
attr_writer name if instance_writer
end
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.rb b/activesupport/lib/active_support/core_ext/date.rb
index 5f13f5f70f..465fedda80 100644
--- a/activesupport/lib/active_support/core_ext/date.rb
+++ b/activesupport/lib/active_support/core_ext/date.rb
@@ -2,5 +2,4 @@ require 'active_support/core_ext/date/acts_like'
require 'active_support/core_ext/date/calculations'
require 'active_support/core_ext/date/conversions'
require 'active_support/core_ext/date/zones'
-require 'active_support/core_ext/date/infinite_comparable'
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index 106a65610c..06e4847e82 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -119,4 +119,15 @@ class Date
options.fetch(:day, day)
)
end
+
+ # Allow Date to be compared with Time by converting to DateTime and relying on the <=> from there.
+ def compare_with_coercion(other)
+ if other.is_a?(Time)
+ self.to_datetime <=> other
+ else
+ compare_without_coercion(other)
+ end
+ end
+ alias_method :compare_without_coercion, :<=>
+ alias_method :<=>, :compare_with_coercion
end
diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb
index cdf606f28c..0637fe4929 100644
--- a/activesupport/lib/active_support/core_ext/date/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date/conversions.rb
@@ -1,7 +1,6 @@
require 'date'
require 'active_support/inflector/methods'
require 'active_support/core_ext/date/zones'
-require 'active_support/core_ext/module/remove_method'
class Date
DATE_FORMATS = {
diff --git a/activesupport/lib/active_support/core_ext/date/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/date/infinite_comparable.rb
deleted file mode 100644
index ca5d793942..0000000000
--- a/activesupport/lib/active_support/core_ext/date/infinite_comparable.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require 'active_support/core_ext/infinite_comparable'
-
-class Date
- include InfiniteComparable
-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 1f78b9eb5a..0d14cba7cc 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
@@ -109,11 +109,11 @@ module DateAndTime
alias :at_beginning_of_year :beginning_of_year
# Returns a new date/time representing the given day in the next week.
- # Week is assumed to start on +start_day+, default is
- # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
- # DateTime objects have their time set to 0:00.
- def next_week(start_day = Date.beginning_of_week)
- first_hour{ weeks_since(1).beginning_of_week.days_since(days_span(start_day)) }
+ # The +given_day_in_next_week+ defaults to the beginning of the week
+ # which is determined by +Date.beginning_of_week+ or +config.beginning_of_week+
+ # when set. +DateTime+ objects have their time set to 0:00.
+ def next_week(given_day_in_next_week = Date.beginning_of_week)
+ first_hour{ weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week)) }
end
# Short-hand for months_since(1).
diff --git a/activesupport/lib/active_support/core_ext/date_time.rb b/activesupport/lib/active_support/core_ext/date_time.rb
index 024af91738..e8a27b9f38 100644
--- a/activesupport/lib/active_support/core_ext/date_time.rb
+++ b/activesupport/lib/active_support/core_ext/date_time.rb
@@ -2,4 +2,3 @@ require 'active_support/core_ext/date_time/acts_like'
require 'active_support/core_ext/date_time/calculations'
require 'active_support/core_ext/date_time/conversions'
require 'active_support/core_ext/date_time/zones'
-require 'active_support/core_ext/date_time/infinite_comparable'
diff --git a/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
index 1d3682eaf2..937567440b 100644
--- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
@@ -1,3 +1,4 @@
+require 'date'
require 'active_support/deprecation'
class DateTime
@@ -59,7 +60,7 @@ class DateTime
options.fetch(:day, day),
options.fetch(:hour, hour),
options.fetch(:min, options[:hour] ? 0 : min),
- options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec),
+ options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec + sec_fraction),
options.fetch(:offset, offset),
options.fetch(:start, start)
)
@@ -124,6 +125,18 @@ class DateTime
end
alias :at_end_of_hour :end_of_hour
+ # Returns a new DateTime representing the start of the minute (hh:mm:00).
+ def beginning_of_minute
+ change(:sec => 0)
+ end
+ alias :at_beginning_of_minute :beginning_of_minute
+
+ # Returns a new DateTime representing the end of the minute (hh:mm:59).
+ def end_of_minute
+ change(:sec => 59)
+ end
+ alias :at_end_of_minute :end_of_minute
+
# Adjusts DateTime to UTC by adding its offset value; offset is set to 0.
#
# DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600
@@ -142,4 +155,11 @@ class DateTime
def utc_offset
(offset * 86400).to_i
end
+
+ # Layers additional behavior on DateTime#<=> so that Time and
+ # ActiveSupport::TimeWithZone instances can be compared with a DateTime.
+ def <=>(other)
+ super other.to_datetime
+ 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 b7d8414a9d..c44626aed9 100644
--- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb
@@ -1,3 +1,4 @@
+require 'date'
require 'active_support/inflector/methods'
require 'active_support/core_ext/time/conversions'
require 'active_support/core_ext/date_time/calculations'
@@ -79,6 +80,16 @@ class DateTime
seconds_since_unix_epoch.to_i
end
+ # Returns the fraction of a second as microseconds
+ def usec
+ (sec_fraction * 1_000_000).to_i
+ end
+
+ # Returns the fraction of a second as nanoseconds
+ def nsec
+ (sec_fraction * 1_000_000_000).to_i
+ end
+
private
def offset_in_seconds
diff --git a/activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb
deleted file mode 100644
index 8a282b19f2..0000000000
--- a/activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require 'active_support/core_ext/infinite_comparable'
-
-class DateTime
- include InfiniteComparable
-end
diff --git a/activesupport/lib/active_support/core_ext/date_time/zones.rb b/activesupport/lib/active_support/core_ext/date_time/zones.rb
index 6457ffbaf6..01a627f8af 100644
--- a/activesupport/lib/active_support/core_ext/date_time/zones.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/zones.rb
@@ -1,3 +1,4 @@
+require 'date'
require 'active_support/core_ext/time/zones'
class DateTime
diff --git a/activesupport/lib/active_support/core_ext/hash/diff.rb b/activesupport/lib/active_support/core_ext/hash/diff.rb
index 5f3868b5b0..4359213380 100644
--- a/activesupport/lib/active_support/core_ext/hash/diff.rb
+++ b/activesupport/lib/active_support/core_ext/hash/diff.rb
@@ -1,3 +1,5 @@
+require 'active_support/deprecation'
+
class Hash
# Returns a hash that represents the difference between two hashes.
#
diff --git a/activesupport/lib/active_support/core_ext/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/infinite_comparable.rb
deleted file mode 100644
index b78b2deaad..0000000000
--- a/activesupport/lib/active_support/core_ext/infinite_comparable.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require 'active_support/concern'
-require 'active_support/core_ext/module/aliasing'
-require 'active_support/core_ext/object/try'
-
-module InfiniteComparable
- extend ActiveSupport::Concern
-
- included do
- alias_method_chain :<=>, :infinity
- end
-
- define_method :'<=>_with_infinity' do |other|
- if other.class == self.class
- public_send :'<=>_without_infinity', other
- else
- infinite = try(:infinite?)
- other_infinite = other.try(:infinite?)
-
- # inf <=> inf
- if infinite && other_infinite
- infinite <=> other_infinite
- # not_inf <=> inf
- elsif other_infinite
- -other_infinite
- # inf <=> not_inf
- elsif infinite
- infinite
- else
- conversion = "to_#{self.class.name.downcase}"
- other = other.public_send(conversion) if other.respond_to?(conversion)
- public_send :'<=>_without_infinity', other
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/marshal.rb b/activesupport/lib/active_support/core_ext/marshal.rb
index c7a8348b1d..56c79c04bd 100644
--- a/activesupport/lib/active_support/core_ext/marshal.rb
+++ b/activesupport/lib/active_support/core_ext/marshal.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/module/aliasing'
+
module Marshal
class << self
def load_with_autoloading(source)
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index e608eeaf42..3dde87ac2e 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -1,8 +1,10 @@
class Module
- # Provides a delegate class method to easily expose contained objects' public methods
- # as your own. Pass one or more methods (specified as symbols or strings)
- # and the name of the target object via the <tt>:to</tt> option (also a symbol
- # or string). At least one method and the <tt>:to</tt> option are required.
+ # Provides a +delegate+ class method to easily expose contained objects'
+ # public methods as your own.
+ #
+ # The macro receives one or more method names (specified as symbols or
+ # strings) and the name of the target object via the <tt>:to</tt> option
+ # (also a symbol or string).
#
# Delegation is particularly useful with Active Record associations:
#
@@ -89,29 +91,44 @@ class Module
# invoice.customer_name # => 'John Doe'
# invoice.customer_address # => 'Vimmersvej 13'
#
- # If the delegate object is +nil+ an exception is raised, and that happens
- # no matter whether +nil+ responds to the delegated method. You can get a
- # +nil+ instead with the +:allow_nil+ option.
+ # If the target is +nil+ and does not respond to the delegated method a
+ # +NoMethodError+ is raised, as with any other value. Sometimes, however, it
+ # makes sense to be robust to that situation and that is the purpose of the
+ # <tt>:allow_nil</tt> option: If the target is not +nil+, or it is and
+ # responds to the method, everything works as usual. But if it is +nil+ and
+ # does not respond to the delegated method, +nil+ is returned.
#
- # class Foo
- # attr_accessor :bar
- # def initialize(bar = nil)
- # @bar = bar
- # end
- # delegate :zoo, to: :bar
+ # class User < ActiveRecord::Base
+ # has_one :profile
+ # delegate :age, to: :profile
# end
#
- # Foo.new.zoo # raises NoMethodError exception (you called nil.zoo)
+ # User.new.age # raises NoMethodError: undefined method `age'
+ #
+ # But if not having a profile yet is fine and should not be an error
+ # condition:
+ #
+ # class User < ActiveRecord::Base
+ # has_one :profile
+ # delegate :age, to: :profile, allow_nil: true
+ # end
+ #
+ # User.new.age # nil
+ #
+ # Note that if the target is not +nil+ then the call is attempted regardless of the
+ # <tt>:allow_nil</tt> option, and thus an exception is still raised if said object
+ # does not respond to the method:
#
# class Foo
- # attr_accessor :bar
- # def initialize(bar = nil)
+ # def initialize(bar)
# @bar = bar
# end
- # delegate :zoo, to: :bar, allow_nil: true
+ #
+ # delegate :name, to: :@bar, allow_nil: true
# end
#
- # Foo.new.zoo # returns nil
+ # Foo.new("Bar").name # raises NoMethodError: undefined method `name'
+ #
def delegate(*methods)
options = methods.pop
unless options.is_a?(Hash) && to = options[:to]
@@ -142,22 +159,31 @@ class Module
# methods still accept two arguments.
definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'
+ # The following generated methods call the target exactly once, storing
+ # the returned value in a dummy variable.
+ #
+ # Reason is twofold: On one hand doing less calls is in general better.
+ # On the other hand it could be that the target has side-effects,
+ # whereas conceptually, from the user point of view, the delegator should
+ # be doing one call.
if allow_nil
- module_eval(<<-EOS, file, line - 2)
+ module_eval(<<-EOS, file, line - 3)
def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
- if #{to} || #{to}.respond_to?(:#{method}) # if client || client.respond_to?(:name)
- #{to}.#{method}(#{definition}) # client.name(*args, &block)
+ _ = #{to} # _ = client
+ if !_.nil? || nil.respond_to?(:#{method}) # if !_.nil? || nil.respond_to?(:name)
+ _.#{method}(#{definition}) # _.name(*args, &block)
end # end
end # end
EOS
else
exception = %(raise "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
- module_eval(<<-EOS, file, line - 1)
+ module_eval(<<-EOS, file, line - 2)
def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
- #{to}.#{method}(#{definition}) # client.name(*args, &block)
+ _ = #{to} # _ = client
+ _.#{method}(#{definition}) # _.name(*args, &block)
rescue NoMethodError # rescue NoMethodError
- if #{to}.nil? # if client.nil?
+ if _.nil? # if _.nil?
#{exception} # # add helpful message to the exception
else # else
raise # raise
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/numeric.rb b/activesupport/lib/active_support/core_ext/numeric.rb
index d5cfc2ece4..a6bc0624be 100644
--- a/activesupport/lib/active_support/core_ext/numeric.rb
+++ b/activesupport/lib/active_support/core_ext/numeric.rb
@@ -1,4 +1,3 @@
require 'active_support/core_ext/numeric/bytes'
require 'active_support/core_ext/numeric/time'
require 'active_support/core_ext/numeric/conversions'
-require 'active_support/core_ext/numeric/infinite_comparable'
diff --git a/activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb
deleted file mode 100644
index b5f1b0487b..0000000000
--- a/activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'active_support/core_ext/infinite_comparable'
-
-class Float
- include InfiniteComparable
-end
-
-class BigDecimal
- include InfiniteComparable
-end
diff --git a/activesupport/lib/active_support/core_ext/object/inclusion.rb b/activesupport/lib/active_support/core_ext/object/inclusion.rb
index 3fec465ec0..b5671f66d0 100644
--- a/activesupport/lib/active_support/core_ext/object/inclusion.rb
+++ b/activesupport/lib/active_support/core_ext/object/inclusion.rb
@@ -1,25 +1,15 @@
class Object
- # Returns true if this object is included in the argument(s). Argument must be
- # any object which responds to +#include?+ or optionally, multiple arguments can be passed in. Usage:
+ # Returns true if this object is included in the argument. Argument must be
+ # any object which responds to +#include?+. Usage:
#
- # characters = ['Konata', 'Kagami', 'Tsukasa']
- # 'Konata'.in?(characters) # => true
+ # characters = ["Konata", "Kagami", "Tsukasa"]
+ # "Konata".in?(characters) # => true
#
- # character = 'Konata'
- # character.in?('Konata', 'Kagami', 'Tsukasa') # => true
- #
- # This will throw an ArgumentError if a single argument is passed in and it doesn't respond
+ # This will throw an ArgumentError if the argument doesn't respond
# to +#include?+.
- def in?(*args)
- if args.length > 1
- args.include? self
- else
- another_object = args.first
- if another_object.respond_to? :include?
- another_object.include? self
- else
- raise ArgumentError.new 'The single parameter passed to #in? must respond to #include?'
- end
- end
+ def in?(another_object)
+ another_object.include?(self)
+ rescue NoMethodError
+ raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/to_param.rb b/activesupport/lib/active_support/core_ext/object/to_param.rb
index 0d5f3501e5..3b137ce6ae 100644
--- a/activesupport/lib/active_support/core_ext/object/to_param.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_param.rb
@@ -53,6 +53,6 @@ class Hash
def to_param(namespace = nil)
collect do |key, value|
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
- end.sort * '&'
+ end.sort! * '&'
end
end
diff --git a/activesupport/lib/active_support/core_ext/range/include_range.rb b/activesupport/lib/active_support/core_ext/range/include_range.rb
index 3af66aaf2f..3a07401c8a 100644
--- a/activesupport/lib/active_support/core_ext/range/include_range.rb
+++ b/activesupport/lib/active_support/core_ext/range/include_range.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/module/aliasing'
+
class Range
# Extends the default Range#include? to support range comparisons.
# (1..5).include?(1..5) # => true
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/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb
index 428fa1f826..6691fc0995 100644
--- a/activesupport/lib/active_support/core_ext/string/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/string/conversions.rb
@@ -15,24 +15,23 @@ class String
# "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
+ # "12/13/2012".to_time # => ArgumentError: argument out of range
def to_time(form = :local)
parts = Date._parse(self, false)
return if parts.empty?
now = Time.now
- offset = parts[:offset]
- utc_offset = form == :utc ? 0 : now.utc_offset
- adjustment = offset ? offset - utc_offset : 0
-
- Time.send(
- form,
+ time = Time.new(
parts.fetch(:year, now.year),
parts.fetch(:mon, now.month),
parts.fetch(:mday, now.day),
parts.fetch(:hour, 0),
parts.fetch(:min, 0),
- parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0)
- ) - adjustment
+ parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
+ parts.fetch(:offset, form == :utc ? 0 : nil)
+ )
+
+ form == :utc ? time.utc : time.getlocal
end
# Converts a string to a Date value.
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/indent.rb b/activesupport/lib/active_support/core_ext/string/indent.rb
index afc3032272..ce3a69cf5f 100644
--- a/activesupport/lib/active_support/core_ext/string/indent.rb
+++ b/activesupport/lib/active_support/core_ext/string/indent.rb
@@ -29,7 +29,7 @@ class String
# "foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
# "foo".indent(2, "\t") # => "\t\tfoo"
#
- # While +indent_string+ is tipically one space or tab, it may be any string.
+ # While +indent_string+ is typically one space or tab, it may be any string.
#
# The third argument, +indent_empty_lines+, is a flag that says whether
# empty lines should be indented. Default is false.
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index 6522145572..56e8a5f98d 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"
@@ -185,7 +185,7 @@ class String
#
# Singular names are not handled correctly.
#
- # 'business'.classify # => "Busines"
+ # 'business'.classify # => "Business"
def classify
ActiveSupport::Inflector.classify(self)
end
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 5f85cedcf5..dc033ed11b 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -42,7 +42,7 @@ class ERB
# html_escape_once('&lt;&lt; Accept & Checkout')
# # => "&lt;&lt; Accept &amp; Checkout"
def html_escape_once(s)
- result = s.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP) { |special| HTML_ESCAPE[special] }
+ result = s.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
s.html_safe? ? result.html_safe : result
end
@@ -60,7 +60,7 @@ class ERB
# json_escape('{"name":"john","created_at":"2010-04-28T01:39:31Z","id":1}')
# # => {name:john,created_at:2010-04-28T01:39:31Z,id:1}
def json_escape(s)
- result = s.to_s.gsub(JSON_ESCAPE_REGEXP) { |special| JSON_ESCAPE[special] }
+ result = s.to_s.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE)
s.html_safe? ? result.html_safe : result
end
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/core_ext/time.rb b/activesupport/lib/active_support/core_ext/time.rb
index af6b589b71..32cffe237d 100644
--- a/activesupport/lib/active_support/core_ext/time.rb
+++ b/activesupport/lib/active_support/core_ext/time.rb
@@ -3,4 +3,3 @@ require 'active_support/core_ext/time/calculations'
require 'active_support/core_ext/time/conversions'
require 'active_support/core_ext/time/marshal'
require 'active_support/core_ext/time/zones'
-require 'active_support/core_ext/time/infinite_comparable'
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 1f95f62229..c65f20c2d5 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -65,6 +65,18 @@ class Time
def current
::Time.zone ? ::Time.zone.now : ::Time.now
end
+
+ # Layers additional behavior on Time.at so that ActiveSupport::TimeWithZone and DateTime
+ # instances can be used when called with a single argument
+ def at_with_coercion(*args)
+ if args.size == 1 && args.first.acts_like?(:time)
+ at_without_coercion(args.first.to_i)
+ else
+ at_without_coercion(*args)
+ end
+ end
+ alias_method :at_without_coercion, :at
+ alias_method :at, :at_with_coercion
end
# Seconds since midnight: Time.now.seconds_since_midnight
@@ -188,6 +200,21 @@ class Time
end
alias :at_end_of_hour :end_of_hour
+ # Returns a new Time representing the start of the minute (x:xx:00)
+ def beginning_of_minute
+ change(:sec => 0)
+ end
+ alias :at_beginning_of_minute :beginning_of_minute
+
+ # Returns a new Time representing the end of the minute, x:xx:59.999999 (.999999999 in ruby1.9)
+ def end_of_minute
+ change(
+ :sec => 59,
+ :usec => Rational(999999999, 1000)
+ )
+ end
+ alias :at_end_of_minute :end_of_minute
+
# Returns a Range representing the whole day of the current time.
def all_day
beginning_of_day..end_of_day
diff --git a/activesupport/lib/active_support/core_ext/time/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/time/infinite_comparable.rb
deleted file mode 100644
index 63795885f5..0000000000
--- a/activesupport/lib/active_support/core_ext/time/infinite_comparable.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require 'active_support/core_ext/infinite_comparable'
-
-class Time
- include InfiniteComparable
-end
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index fff4c776a9..d38e4b0732 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -213,7 +213,7 @@ module ActiveSupport #:nodoc:
yield
end
rescue Exception => exception # errors from loading file
- exception.blame_file! file
+ exception.blame_file! file if exception.respond_to? :blame_file!
raise
end
diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
index 485dc91063..a03a66b96b 100644
--- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
@@ -25,7 +25,7 @@ module ActiveSupport
end
end
- # This DeprecatedObjectProxy transforms object to depracated object.
+ # This DeprecatedObjectProxy transforms object to deprecated object.
#
# @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!")
# @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!", deprecator_instance)
@@ -52,7 +52,7 @@ module ActiveSupport
end
# This DeprecatedInstanceVariableProxy transforms instance variable to
- # depracated instance variable.
+ # deprecated instance variable.
#
# class Example
# def initialize(deprecator)
@@ -93,7 +93,7 @@ module ActiveSupport
end
end
- # This DeprecatedConstantProxy transforms constant to depracated constant.
+ # This DeprecatedConstantProxy transforms constant to deprecated constant.
#
# OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST')
# OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST', deprecator_instance)
diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb
index 20136dd1b0..d6918bede2 100644
--- a/activesupport/lib/active_support/file_update_checker.rb
+++ b/activesupport/lib/active_support/file_update_checker.rb
@@ -115,7 +115,7 @@ module ActiveSupport
end
def compile_glob(hash)
- hash.freeze # Freeze so changes aren't accidently pushed
+ hash.freeze # Freeze so changes aren't accidentally pushed
return if hash.empty?
globs = hash.map do |key, value|
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 306d80b2df..0a81a8393d 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -78,7 +78,7 @@ module ActiveSupport
end
def self.[](*args)
- new.merge(Hash[*args])
+ new.merge!(Hash[*args])
end
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
@@ -91,7 +91,7 @@ module ActiveSupport
#
# This value can be later fetched using either +:key+ or +'key'+.
def []=(key, value)
- regular_writer(convert_key(key), convert_value(value))
+ regular_writer(convert_key(key), convert_value(value, for: :assignment))
end
alias_method :store, :[]=
@@ -223,13 +223,17 @@ 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
# Convert to a regular hash with string keys.
def to_hash
- Hash.new(default).merge!(self)
+ _new_hash= {}
+ each do |key, value|
+ _new_hash[convert_key(key)] = convert_value(value, for: :to_hash)
+ end
+ Hash.new(default).merge!(_new_hash)
end
protected
@@ -237,12 +241,18 @@ module ActiveSupport
key.kind_of?(Symbol) ? key.to_s : key
end
- def convert_value(value)
+ def convert_value(value, options = {})
if value.is_a? Hash
- value.nested_under_indifferent_access
+ if options[:for] == :to_hash
+ value.to_hash
+ else
+ value.nested_under_indifferent_access
+ end
elsif value.is_a?(Array)
- value = value.dup if value.frozen?
- value.map! { |e| convert_value(e) }
+ unless options[:for] == :assignment
+ value = value.dup
+ end
+ value.map! { |e| convert_value(e, options) }
else
value
end
diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb
index 22521a8e93..6cc98191d4 100644
--- a/activesupport/lib/active_support/i18n.rb
+++ b/activesupport/lib/active_support/i18n.rb
@@ -1,13 +1,13 @@
+require 'active_support/core_ext/hash/deep_merge'
+require 'active_support/core_ext/hash/except'
+require 'active_support/core_ext/hash/slice'
begin
- require 'active_support/core_ext/hash/deep_merge'
- require 'active_support/core_ext/hash/except'
- require 'active_support/core_ext/hash/slice'
require 'i18n'
- require 'active_support/lazy_load_hooks'
rescue LoadError => e
$stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
raise e
end
+require 'active_support/lazy_load_hooks'
ActiveSupport.run_load_hooks(:i18n)
I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml"
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index 39648727fd..665545db3b 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -219,7 +219,12 @@ module ActiveSupport
# unknown.
def constantize(camel_cased_word)
names = camel_cased_word.split('::')
- names.shift if names.empty? || names.first.empty?
+
+ # Trigger a builtin NameError exception including the ill-formed constant in the message.
+ Object.const_get(camel_cased_word) if names.empty?
+
+ # Remove the first blank element in case of '::ClassName' notation.
+ names.shift if names.size > 1 && names.first.empty?
names.inject(Object) do |constant, name|
if constant == Object
diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb
index a4a32b2ad0..30833a4cb1 100644
--- a/activesupport/lib/active_support/json/decoding.rb
+++ b/activesupport/lib/active_support/json/decoding.rb
@@ -1,6 +1,6 @@
require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/module/delegation'
-require 'multi_json'
+require 'json'
module ActiveSupport
# Look for and parse json strings that look like ISO 8601 times.
@@ -13,8 +13,8 @@ module ActiveSupport
#
# ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
# => {"team" => "rails", "players" => "36"}
- def decode(json, options ={})
- data = MultiJson.load(json, options)
+ def decode(json, proc = nil, options = {})
+ data = ::JSON.load(json, proc, options)
if ActiveSupport.parse_json_times
convert_dates_from(data)
else
@@ -22,23 +22,6 @@ module ActiveSupport
end
end
- def engine
- MultiJson.adapter
- end
- alias :backend :engine
-
- def engine=(name)
- MultiJson.use(name)
- end
- alias :backend= :engine=
-
- def with_backend(name)
- old_backend, self.backend = backend, name
- yield
- ensure
- self.backend = old_backend
- end
-
# Returns the class of the error that will be raised when there is an
# error in decoding JSON. Using this method means you won't directly
# depend on the ActiveSupport's JSON implementation, in case it changes
@@ -50,7 +33,7 @@ module ActiveSupport
# Rails.logger.warn("Attempted to decode invalid JSON: #{some_string}")
# end
def parse_error
- MultiJson::DecodeError
+ ::JSON::ParserError
end
private
diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb
index 9bf1ea35b3..77b5d8d227 100644
--- a/activesupport/lib/active_support/json/encoding.rb
+++ b/activesupport/lib/active_support/json/encoding.rb
@@ -1,6 +1,7 @@
+#encoding: us-ascii
+
require 'active_support/core_ext/object/to_json'
require 'active_support/core_ext/module/delegation'
-require 'active_support/json/variable'
require 'bigdecimal'
require 'active_support/core_ext/big_decimal/conversions' # for #to_s
@@ -98,13 +99,18 @@ module ActiveSupport
"\010" => '\b',
"\f" => '\f',
"\n" => '\n',
+ "\xe2\x80\xa8" => '\u2028',
+ "\xe2\x80\xa9" => '\u2029',
"\r" => '\r',
"\t" => '\t',
'"' => '\"',
'\\' => '\\\\',
'>' => '\u003E',
'<' => '\u003C',
- '&' => '\u0026' }
+ '&' => '\u0026',
+ "#{0xe2.chr}#{0x80.chr}#{0xa8.chr}" => '\u2028',
+ "#{0xe2.chr}#{0x80.chr}#{0xa9.chr}" => '\u2029',
+ }
class << self
# If true, use ISO 8601 format for dates and times. Otherwise, fall back
@@ -121,9 +127,9 @@ module ActiveSupport
def escape_html_entities_in_json=(value)
self.escape_regex = \
if @escape_html_entities_in_json = value
- /[\x00-\x1F"\\><&]/
+ /\xe2\x80\xa8|\xe2\x80\xa9|[\x00-\x1F"\\><&]/
else
- /[\x00-\x1F"\\]/
+ /\xe2\x80\xa8|\xe2\x80\xa9|[\x00-\x1F"\\]/
end
end
diff --git a/activesupport/lib/active_support/json/variable.rb b/activesupport/lib/active_support/json/variable.rb
deleted file mode 100644
index d69dab6408..0000000000
--- a/activesupport/lib/active_support/json/variable.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-require 'active_support/deprecation'
-
-module ActiveSupport
- module JSON
- # Deprecated: A string that returns itself as its JSON-encoded form.
- class Variable < String
- def initialize(*args)
- message = 'ActiveSupport::JSON::Variable is deprecated and will be removed in Rails 4.1. ' \
- 'For your own custom JSON literals, define #as_json and #encode_json yourself.'
- ActiveSupport::Deprecation.warn message
- super
- end
-
- def as_json(options = nil) self end #:nodoc:
- def encode_json(encoder) self end #:nodoc:
- end
- end
-end
diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb
index 71654dbb87..598c46bce5 100644
--- a/activesupport/lib/active_support/key_generator.rb
+++ b/activesupport/lib/active_support/key_generator.rb
@@ -4,7 +4,7 @@ require 'openssl'
module ActiveSupport
# KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2
# It can be used to derive a number of keys for various purposes from a given secret.
- # This lets rails applications have a single secure secret, but avoid reusing that
+ # This lets Rails applications have a single secure secret, but avoid reusing that
# key in multiple incompatible contexts.
class KeyGenerator
def initialize(secret, options = {})
@@ -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/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb
index e489512531..e2b8f0f648 100644
--- a/activesupport/lib/active_support/lazy_load_hooks.rb
+++ b/activesupport/lib/active_support/lazy_load_hooks.rb
@@ -1,5 +1,5 @@
module ActiveSupport
- # lazy_load_hooks allows rails to lazily load a lot of components and thus
+ # lazy_load_hooks allows Rails to lazily load a lot of components and thus
# making the app boot faster. Because of this feature now there is no need to
# require <tt>ActiveRecord::Base</tt> at boot time purely to apply
# configuration. Instead a hook is registered that applies configuration once
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..bffdfc6201 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -12,10 +12,11 @@ module ActiveSupport
# This can be used in situations similar to the <tt>MessageVerifier</tt>, but
# where you don't want users to be able to determine the value of the payload.
#
- # key = OpenSSL::Digest::SHA256.new('password').digest # => "\x89\xE0\x156\xAC..."
- # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
- # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
- # crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
+ # salt = SecureRandom.random_bytes(64)
+ # key = ActiveSupport::KeyGenerator.new('password').generate_key(salt) # => "\x89\xE0\x156\xAC..."
+ # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
+ # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
+ # crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
class MessageEncryptor
module NullSerializer #:nodoc:
def self.load(value)
@@ -28,7 +29,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 +67,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/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index a87383fe99..e0cd92ae3c 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -19,10 +19,10 @@ module ActiveSupport
# end
#
# By default it uses Marshal to serialize the message. If you want to use
- # another serialization method, you can set the serializer attribute to
- # something that responds to dump and load, e.g.:
+ # another serialization method, you can set the serializer in the options
+ # hash upon initialization:
#
- # @verifier.serializer = YAML
+ # @verifier = ActiveSupport::MessageVerifier.new('s3Krit', serializer: YAML)
class MessageVerifier
class InvalidSignature < StandardError; end
diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb
index cbc1608349..04e6b71580 100644
--- a/activesupport/lib/active_support/multibyte/unicode.rb
+++ b/activesupport/lib/active_support/multibyte/unicode.rb
@@ -145,7 +145,7 @@ module ActiveSupport
ncp << (HANGUL_TBASE + tindex) unless tindex == 0
decomposed.concat ncp
# if the codepoint is decomposable in with the current decomposition type
- elsif (ncp = database.codepoints[cp].decomp_mapping) and (!database.codepoints[cp].decomp_type || type == :compatability)
+ elsif (ncp = database.codepoints[cp].decomp_mapping) and (!database.codepoints[cp].decomp_type || type == :compatibility)
decomposed.concat decompose(type, ncp.dup)
else
decomposed << cp
@@ -218,51 +218,31 @@ module ActiveSupport
# Passing +true+ will forcibly tidy all bytes, assuming that the string's
# encoding is entirely CP1252 or ISO-8859-1.
def tidy_bytes(string, force = false)
+ return string if string.empty?
+
if force
- return string.unpack("C*").map do |b|
- tidy_byte(b)
- end.flatten.compact.pack("C*").unpack("U*").pack("U*")
+ return string.encode(Encoding::UTF_8, Encoding::Windows_1252, invalid: :replace, undef: :replace)
end
- bytes = string.unpack("C*")
- conts_expected = 0
- last_lead = 0
-
- bytes.each_index do |i|
+ # We can't transcode to the same format, so we choose a nearly-identical encoding.
+ # We're going to 'transcode' bytes from UTF-8 when possible, then fall back to
+ # CP1252 when we get errors. The final string will be 'converted' back to UTF-8
+ # before returning.
+ reader = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_8_MAC)
- byte = bytes[i]
- is_cont = byte > 127 && byte < 192
- is_lead = byte > 191 && byte < 245
- is_unused = byte > 240
- is_restricted = byte > 244
+ source = string.dup
+ out = ''.force_encoding(Encoding::UTF_8_MAC)
- # Impossible or highly unlikely byte? Clean it.
- if is_unused || is_restricted
- bytes[i] = tidy_byte(byte)
- elsif is_cont
- # Not expecting continuation byte? Clean up. Otherwise, now expect one less.
- conts_expected == 0 ? bytes[i] = tidy_byte(byte) : conts_expected -= 1
- else
- if conts_expected > 0
- # Expected continuation, but got ASCII or leading? Clean backwards up to
- # the leading byte.
- (1..(i - last_lead)).each {|j| bytes[i - j] = tidy_byte(bytes[i - j])}
- conts_expected = 0
- end
- if is_lead
- # Final byte is leading? Clean it.
- if i == bytes.length - 1
- bytes[i] = tidy_byte(bytes.last)
- else
- # Valid leading byte? Expect continuations determined by position of
- # first zero bit, with max of 3.
- conts_expected = byte < 224 ? 1 : byte < 240 ? 2 : 3
- last_lead = i
- end
- end
- end
+ loop do
+ reader.primitive_convert(source, out)
+ _, _, _, error_bytes, _ = reader.primitive_errinfo
+ break if error_bytes.nil?
+ out << error_bytes.encode(Encoding::UTF_8_MAC, Encoding::Windows_1252, invalid: :replace, undef: :replace)
end
- bytes.empty? ? "" : bytes.flatten.compact.pack("C*").unpack("U*").pack("U*")
+
+ reader.finish
+
+ out.encode!(Encoding::UTF_8)
end
# Returns the KC normalization of the string by default. NFKC is
@@ -283,9 +263,9 @@ module ActiveSupport
when :c
compose(reorder_characters(decompose(:canonical, codepoints)))
when :kd
- reorder_characters(decompose(:compatability, codepoints))
+ reorder_characters(decompose(:compatibility, codepoints))
when :kc
- compose(reorder_characters(decompose(:compatability, codepoints)))
+ compose(reorder_characters(decompose(:compatibility, codepoints)))
else
raise ArgumentError, "#{form} is not a valid normalization variant", caller
end.pack('U*')
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index 705a4693b7..b32aa75e59 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
@@ -142,8 +143,8 @@ module ActiveSupport
#
# == Default Queue
#
- # Notifications ships with a queue implementation that consumes and publish events
- # to log subscribers in a thread. You can use any queue implementation you want.
+ # Notifications ships with a queue implementation that consumes and publishes events
+ # to all log subscribers. You can use any queue implementation you want.
#
module Notifications
class << self
@@ -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/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb
index 7588fdb67c..99fe03e6d0 100644
--- a/activesupport/lib/active_support/notifications/fanout.rb
+++ b/activesupport/lib/active_support/notifications/fanout.rb
@@ -79,6 +79,13 @@ module ActiveSupport
def initialize(pattern, delegate)
@pattern = pattern
@delegate = delegate
+ @can_publish = delegate.respond_to?(:publish)
+ end
+
+ def publish(name, *args)
+ if @can_publish
+ @delegate.publish name, *args
+ end
end
def start(name, id, payload)
diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb
index 1ee7ca06bb..0c9a729ce5 100644
--- a/activesupport/lib/active_support/notifications/instrumenter.rb
+++ b/activesupport/lib/active_support/notifications/instrumenter.rb
@@ -2,7 +2,7 @@ require 'securerandom'
module ActiveSupport
module Notifications
- # Instrumentors are stored in a thread local.
+ # Instrumenters are stored in a thread local.
class Instrumenter
attr_reader :id
@@ -17,7 +17,7 @@ module ActiveSupport
def instrument(name, payload={})
start name, payload
begin
- yield
+ yield payload
rescue Exception => e
payload[:exception] = [e.class.name, e.message]
raise e
diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb
index 2191471daa..414960d2b1 100644
--- a/activesupport/lib/active_support/number_helper.rb
+++ b/activesupport/lib/active_support/number_helper.rb
@@ -295,7 +295,7 @@ module ActiveSupport
options = format_options(options[:locale]).merge!(options)
- parts = number.to_s.to_str.split('.')
+ parts = number.to_s.split('.')
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
parts.join(options[:separator])
end
@@ -356,7 +356,8 @@ module ActiveSupport
digits, rounded_number = 1, 0
else
digits = (Math.log10(number.abs) + 1).floor
- rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new((10 ** (digits - precision)).to_f.to_s)).round.to_f * 10 ** (digits - precision)
+ multiplier = 10 ** (digits - precision)
+ rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new(multiplier.to_f.to_s)).round.to_f * multiplier
digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed
end
precision -= digits
@@ -580,7 +581,7 @@ module ActiveSupport
unit = case units
when Hash
- units[DECIMAL_UNITS[display_exponent]]
+ units[DECIMAL_UNITS[display_exponent]] || ''
when String, Symbol
I18n.translate(:"#{units}.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
else
diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb
index c9518bda79..e03bb4ca0f 100644
--- a/activesupport/lib/active_support/ordered_options.rb
+++ b/activesupport/lib/active_support/ordered_options.rb
@@ -40,6 +40,14 @@ module ActiveSupport
end
end
+ # +InheritableOptions+ provides a constructor to build an +OrderedOptions+
+ # hash inherited from the another hash.
+ #
+ # Use this if you already have some hash and you want to create a new one based on it.
+ #
+ # h = ActiveSupport::InheritableOptions.new({ girl: 'Mary', boy: 'John' })
+ # h.girl # => 'Mary'
+ # h.boy # => 'John'
class InheritableOptions < OrderedOptions
def initialize(parent = nil)
if parent.kind_of?(OrderedOptions)
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..34c6f900c1
--- /dev/null
+++ b/activesupport/lib/active_support/subscriber.rb
@@ -0,0 +1,93 @@
+require 'active_support/per_thread_registry'
+
+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
+ SubscriberQueueRegistry.get_queue(@queue_key)
+ end
+ end
+
+ # This is a registry for all the event stacks kept for subscribers.
+ #
+ # See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
+ # for further details.
+ class SubscriberQueueRegistry # :nodoc:
+ extend PerThreadRegistry
+
+ def initialize
+ @registry = {}
+ end
+
+ def get_queue(queue_key)
+ @registry[queue_key] ||= []
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index 8b392c36d0..2f27aff2bd 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -1,10 +1,9 @@
gem 'minitest' # make sure we get the gem, not stdlib
-require 'minitest/unit'
+require 'minitest'
require 'active_support/testing/tagged_logging'
require 'active_support/testing/setup_and_teardown'
require 'active_support/testing/assertions'
require 'active_support/testing/deprecation'
-require 'active_support/testing/pending'
require 'active_support/testing/declarative'
require 'active_support/testing/isolation'
require 'active_support/testing/constant_lookup'
@@ -16,10 +15,28 @@ begin
rescue LoadError
end
+module Minitest # :nodoc:
+ class << self
+ remove_method :__run
+ end
+
+ def self.__run reporter, options # :nodoc:
+ # FIXME: MT5's runnables is not ordered. This is needed because
+ # we have have tests have cross-class order-dependent bugs.
+ suites = Runnable.runnables.sort_by { |ts| ts.name.to_s }
+
+ parallel, serial = suites.partition { |s| s.test_order == :parallel }
+
+ ParallelEach.new(parallel).map { |suite| suite.run reporter, options } +
+ serial.map { |suite| suite.run reporter, options }
+ end
+end
+
module ActiveSupport
- class TestCase < ::MiniTest::Unit::TestCase
- Assertion = MiniTest::Assertion
- alias_method :method_name, :__name__
+ class TestCase < ::Minitest::Test
+ Assertion = Minitest::Assertion
+
+ alias_method :method_name, :name
$tags = {}
def self.for_tag(tag)
@@ -36,7 +53,6 @@ module ActiveSupport
include ActiveSupport::Testing::SetupAndTeardown
include ActiveSupport::Testing::Assertions
include ActiveSupport::Testing::Deprecation
- include ActiveSupport::Testing::Pending
extend ActiveSupport::Testing::Declarative
# test/unit backwards compatibility methods
diff --git a/activesupport/lib/active_support/testing/autorun.rb b/activesupport/lib/active_support/testing/autorun.rb
index c446adc16d..5aa5f46310 100644
--- a/activesupport/lib/active_support/testing/autorun.rb
+++ b/activesupport/lib/active_support/testing/autorun.rb
@@ -1,5 +1,5 @@
gem 'minitest'
-require 'minitest/unit'
+require 'minitest'
-MiniTest::Unit.autorun
+Minitest.autorun
diff --git a/activesupport/lib/active_support/testing/constant_lookup.rb b/activesupport/lib/active_support/testing/constant_lookup.rb
index 52bfeb7179..1b2a75c35d 100644
--- a/activesupport/lib/active_support/testing/constant_lookup.rb
+++ b/activesupport/lib/active_support/testing/constant_lookup.rb
@@ -38,6 +38,8 @@ module ActiveSupport
begin
constant = names.join("::").constantize
break(constant) if yield(constant)
+ rescue NoMethodError # subclass of NameError
+ raise
rescue NameError
# Constant wasn't found, move on
ensure
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index dca91e8b75..9c52ae7768 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -1,8 +1,5 @@
require 'rbconfig'
-begin
- require 'minitest/parallel_each'
-rescue LoadError
-end
+require 'minitest/parallel_each'
module ActiveSupport
module Testing
@@ -43,44 +40,15 @@ module ActiveSupport
def method_missing(name, *args)
@calls << [name, args]
end
+
+ def info_signal
+ Signal.list['INFO']
+ end
end
module Isolation
require 'thread'
- # Recent versions of MiniTest (such as the one shipped with Ruby 2.0) already define
- # a ParallelEach class.
- unless defined? ParallelEach
- class ParallelEach
- include Enumerable
-
- # default to 2 cores
- CORES = (ENV['TEST_CORES'] || 2).to_i
-
- def initialize list
- @list = list
- @queue = SizedQueue.new CORES
- end
-
- def grep pattern
- self.class.new super
- end
-
- def each
- threads = CORES.times.map {
- Thread.new {
- while job = @queue.pop
- yield job
- end
- }
- }
- @list.each { |i| @queue << i }
- CORES.times { @queue << nil }
- threads.each(&:join)
- end
- end
- end
-
def self.included(klass) #:nodoc:
klass.extend(Module.new {
def test_methods
@@ -104,16 +72,12 @@ module ActiveSupport
end
end
- def run(runner)
- _run_class_setup
-
- serialized = run_in_isolation do |isolated_runner|
- super(isolated_runner)
+ def run
+ serialized = run_in_isolation do
+ super
end
- retval, proxy = Marshal.load(serialized)
- proxy.__replay__(runner)
- retval
+ Marshal.load(serialized)
end
module Forking
@@ -122,9 +86,8 @@ module ActiveSupport
pid = fork do
read.close
- proxy = ProxyTestResult.new
- retval = yield proxy
- write.puts [Marshal.dump([retval, proxy])].pack("m")
+ yield
+ write.puts [Marshal.dump(self.dup)].pack("m")
exit!
end
diff --git a/activesupport/lib/active_support/testing/pending.rb b/activesupport/lib/active_support/testing/pending.rb
deleted file mode 100644
index b04bbbbaea..0000000000
--- a/activesupport/lib/active_support/testing/pending.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-require 'active_support/deprecation'
-
-module ActiveSupport
- module Testing
- module Pending # :nodoc:
- unless defined?(Spec)
- def pending(description = "", &block)
- ActiveSupport::Deprecation.warn("#pending is deprecated and will be removed in Rails 4.1, please use #skip instead.")
- skip(description.blank? ? nil : description)
- end
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/testing/tagged_logging.rb b/activesupport/lib/active_support/testing/tagged_logging.rb
index 9d43eb179f..f4cee64091 100644
--- a/activesupport/lib/active_support/testing/tagged_logging.rb
+++ b/activesupport/lib/active_support/testing/tagged_logging.rb
@@ -7,7 +7,7 @@ module ActiveSupport
def before_setup
if tagged_logger
- heading = "#{self.class}: #{__name__}"
+ heading = "#{self.class}: #{name}"
divider = '-' * heading.size
tagged_logger.info divider
tagged_logger.info heading
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index 0e6d12a186..95b9b8e5ae 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -292,7 +292,7 @@ module ActiveSupport
end
end
- %w(year mon month day mday wday yday hour min sec to_date).each do |method_name|
+ %w(year mon month day mday wday yday hour min sec usec nsec to_date).each do |method_name|
class_eval <<-EOV, __FILE__, __LINE__ + 1
def #{method_name} # def month
time.#{method_name} # time.month
@@ -300,10 +300,6 @@ module ActiveSupport
EOV
end
- def usec
- time.respond_to?(:usec) ? time.usec : 0
- end
-
def to_a
[time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone]
end
@@ -317,6 +313,10 @@ module ActiveSupport
end
alias_method :tv_sec, :to_i
+ def to_r
+ utc.to_r
+ end
+
# Return an instance of Time in the system timezone.
def to_time
utc.to_time
@@ -362,6 +362,8 @@ module ActiveSupport
# TimeWithZone with the existing +time_zone+.
def method_missing(sym, *args, &block)
wrap_with_time_zone time.__send__(sym, *args, &block)
+ rescue NoMethodError => e
+ raise e, e.message.sub(time.inspect, self.inspect), e.backtrace
end
private
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index c5fbddcb5f..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").
@@ -62,6 +62,7 @@ module ActiveSupport
"Newfoundland" => "America/St_Johns",
"Brasilia" => "America/Sao_Paulo",
"Buenos Aires" => "America/Argentina/Buenos_Aires",
+ "Montevideo" => "America/Montevideo",
"Georgetown" => "America/Guyana",
"Greenland" => "America/Godthab",
"Mid-Atlantic" => "Atlantic/South_Georgia",
@@ -150,7 +151,7 @@ module ActiveSupport
"Taipei" => "Asia/Taipei",
"Perth" => "Australia/Perth",
"Irkutsk" => "Asia/Irkutsk",
- "Ulaan Bataar" => "Asia/Ulaanbaatar",
+ "Ulaanbaatar" => "Asia/Ulaanbaatar",
"Seoul" => "Asia/Seoul",
"Osaka" => "Asia/Tokyo",
"Sapporo" => "Asia/Tokyo",
@@ -176,6 +177,7 @@ module ActiveSupport
"Wellington" => "Pacific/Auckland",
"Nuku'alofa" => "Pacific/Tongatapu",
"Tokelau Is." => "Pacific/Fakaofo",
+ "Chatham Is." => "Pacific/Chatham",
"Samoa" => "Pacific/Apia"
}
@@ -238,7 +240,7 @@ module ActiveSupport
# Compare #name and TZInfo identifier to a supplied regexp, returning +true+
# if a match is found.
def =~(re)
- return true if name =~ re || MAPPING[name] =~ re
+ re === name || re === MAPPING[name]
end
# Returns a textual representation of this time zone.
diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb
index 8a8f8f946d..8762330a6e 100644
--- a/activesupport/lib/active_support/version.rb
+++ b/activesupport/lib/active_support/version.rb
@@ -1,10 +1,11 @@
module ActiveSupport
- module VERSION #:nodoc:
- MAJOR = 4
- MINOR = 0
- TINY = 0
- PRE = "beta"
+ # Returns the version of the currently loaded ActiveSupport as a Gem::Version
+ def self.version
+ Gem::Version.new "4.1.0.beta"
+ end
- STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
+ module VERSION #:nodoc:
+ MAJOR, MINOR, TINY, PRE = ActiveSupport.version.segments
+ STRING = ActiveSupport.version.to_s
end
end
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
index 4551dd2f2d..27c64c4dca 100644
--- a/activesupport/lib/active_support/xml_mini/jdom.rb
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -37,6 +37,12 @@ module ActiveSupport
{}
else
@dbf = DocumentBuilderFactory.new_instance
+ # secure processing of java xml
+ # http://www.ibm.com/developerworks/xml/library/x-tipcfsx/index.html
+ @dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
+ @dbf.setFeature("http://xml.org/sax/features/external-general-entities", false)
+ @dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false)
+ @dbf.setFeature(javax.xml.XMLConstants::FEATURE_SECURE_PROCESSING, true)
xml_string_reader = StringReader.new(data)
xml_input_source = InputSource.new(xml_string_reader)
doc = @dbf.new_document_builder.parse(xml_input_source)
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]