aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib/active_support')
-rw-r--r--activesupport/lib/active_support/backtrace_cleaner.rb5
-rw-r--r--activesupport/lib/active_support/cache.rb148
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb4
-rw-r--r--activesupport/lib/active_support/cache/memory_store.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/hash/slice.rb6
-rw-r--r--activesupport/lib/active_support/deprecation.rb21
-rw-r--r--activesupport/lib/active_support/descendants_tracker.rb53
-rw-r--r--activesupport/lib/active_support/key_generator.rb23
-rw-r--r--activesupport/lib/active_support/queueing.rb30
-rw-r--r--activesupport/lib/active_support/testing/performance/ruby.rb2
13 files changed, 175 insertions, 126 deletions
diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb
index 53d05c3817..f1aff8a8e3 100644
--- a/activesupport/lib/active_support/backtrace_cleaner.rb
+++ b/activesupport/lib/active_support/backtrace_cleaner.rb
@@ -34,7 +34,7 @@ module ActiveSupport
# Returns the backtrace after all filters and silencers have been run
# against it. Filters run first, then silencers.
def clean(backtrace, kind = :silent)
- filtered = filter(backtrace)
+ filtered = filter_backtrace(backtrace)
case kind
when :silent
@@ -45,6 +45,7 @@ module ActiveSupport
filtered
end
end
+ alias :filter :clean
# Adds a filter from the block provided. Each line in the backtrace will be
# mapped against this filter.
@@ -76,7 +77,7 @@ module ActiveSupport
end
private
- def filter(backtrace)
+ def filter_backtrace(backtrace)
@filters.each do |f|
backtrace = backtrace.map { |line| f.call(line) }
end
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index f98ba16cdd..732ab4b7df 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -284,7 +284,9 @@ module ActiveSupport
end
if entry && entry.expired?
race_ttl = options[:race_condition_ttl].to_i
- if race_ttl and Time.now.to_f - entry.expires_at <= race_ttl
+ if race_ttl && (Time.now - 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
write_entry(key, entry, :expires_in => race_ttl * 2)
else
@@ -532,102 +534,122 @@ module ActiveSupport
end
end
- # Entry that is put into caches. It supports expiration time on entries and
- # can compress values to save space in the cache.
- class Entry
- attr_reader :created_at, :expires_in
-
+ # This class is used to represent cache entries. Cache entries have a value and an optional
+ # expiration time. The expiration time is used to support the :race_condition_ttl option
+ # on the cache.
+ #
+ # Since cache entries in most instances will be serialized, the internals of this class are highly optimized
+ # using short instance variable names that are lazily defined.
+ class Entry # :nodoc:
DEFAULT_COMPRESS_LIMIT = 16.kilobytes
- class << self
- # Create an entry with internal attributes set. This method is intended
- # to be used by implementations that store cache entries in a native
- # format instead of as serialized Ruby objects.
- def create(raw_value, created_at, options = {})
- entry = new(nil)
- entry.instance_variable_set(:@value, raw_value)
- entry.instance_variable_set(:@created_at, created_at.to_f)
- entry.instance_variable_set(:@compressed, options[:compressed])
- entry.instance_variable_set(:@expires_in, options[:expires_in])
- entry
- end
- end
-
# Create a new cache entry for the specified value. Options supported are
# +:compress+, +:compress_threshold+, and +:expires_in+.
def initialize(value, options = {})
- @compressed = false
- @expires_in = options[:expires_in]
- @expires_in = @expires_in.to_f if @expires_in
- @created_at = Time.now.to_f
- if value.nil?
- @value = nil
+ if should_compress?(value, options)
+ @v = compress(value)
+ @c = true
else
- @value = Marshal.dump(value)
- if should_compress?(@value, options)
- @value = Zlib::Deflate.deflate(@value)
- @compressed = true
- end
+ @v = value
end
- end
-
- # Get the raw value. This value may be serialized and compressed.
- def raw_value
- @value
- end
-
- # Get the value stored in the cache.
- def value
- # If the original value was exactly false @value is still true because
- # it is marshalled and eventually compressed. Both operations yield
- # strings.
- if @value
- Marshal.load(compressed? ? Zlib::Inflate.inflate(@value) : @value)
+ if expires_in = options[:expires_in]
+ @x = (Time.now + expires_in).to_i
end
end
- def compressed?
- @compressed
+ def value
+ convert_version_3_entry! if defined?(@value)
+ compressed? ? uncompress(@v) : @v
end
# Check if the entry is expired. The +expires_in+ parameter can override
# the value set when the entry was created.
def expired?
- @expires_in && @created_at + @expires_in <= Time.now.to_f
- end
-
- # Set a new time when the entry will expire.
- def expires_at=(time)
- if time
- @expires_in = time.to_f - @created_at
+ convert_version_3_entry! if defined?(@value)
+ if defined?(@x)
+ @x && @x < Time.now.to_i
else
- @expires_in = nil
+ false
end
end
- # Seconds since the epoch when the entry will expire.
def expires_at
- @expires_in ? @created_at + @expires_in : nil
+ Time.at(@x) if defined?(@x)
+ end
+
+ def expires_at=(value)
+ @x = value.to_i
end
# Returns the size of the cached value. This could be less than
# <tt>value.size</tt> if the data is compressed.
def size
- if @value.nil?
- 0
+ if defined?(@s)
+ @s
else
- @value.bytesize
+ case value
+ when NilClass
+ 0
+ when String
+ @v.bytesize
+ else
+ @s = Marshal.dump(@v).bytesize
+ end
+ end
+ end
+
+ # 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
+ else
+ @v = Marshal.load(Marshal.dump(@v))
+ end
end
end
private
- def should_compress?(serialized_value, options)
- if options[:compress]
+ def should_compress?(value, options)
+ if value && options[:compress]
compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
- return true if serialized_value.size >= compress_threshold
+ serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize
+ return true if serialized_value_size >= compress_threshold
end
false
end
+
+ def compressed?
+ defined?(@c) ? @c : false
+ end
+
+ def compress(value)
+ Zlib::Deflate.deflate(Marshal.dump(value))
+ end
+
+ def uncompress(value)
+ Marshal.load(Zlib::Inflate.inflate(value))
+ end
+
+ # 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)
+ end
+ if defined?(@compressed)
+ @c = @compressed
+ remove_instance_variable(:@compressed)
+ 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)
+ end
+ end
end
end
end
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index 5aa78cc9f3..17450fe4d0 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -132,6 +132,10 @@ module ActiveSupport
method = options && options[:unless_exist] ? :add : :set
value = options[:raw] ? entry.value.to_s : entry
expires_in = options[:expires_in].to_i
+ if expires_in > 0 && !options[:raw]
+ # Set the memcache expire a few minutes in the future to support race condition ttls on read
+ expires_in += 5.minutes
+ end
@data.send(method, escape_key(key), value, expires_in, options)
rescue Dalli::DalliError => e
logger.error("DalliError (#{e}): #{e.message}") if logger
diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb
index 7fd5e3b53d..4d26fb7e42 100644
--- a/activesupport/lib/active_support/cache/memory_store.rb
+++ b/activesupport/lib/active_support/cache/memory_store.rb
@@ -135,6 +135,7 @@ module ActiveSupport
end
def write_entry(key, entry, options) # :nodoc:
+ entry.dup_value!
synchronize do
old_entry = @data[key]
return false if @data.key?(key) && options[:unless_exist]
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index 7f37c459c1..6a0c4a015a 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -1,6 +1,8 @@
require 'active_support/xml_mini'
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/string/inflections'
+require 'active_support/core_ext/object/to_param'
+require 'active_support/core_ext/object/to_query'
class Array
# Converts the array to a comma-separated sentence where the last element is
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index a7551d9c64..02ae57b4a6 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -28,7 +28,7 @@ class Date
# Returns week start day symbol (e.g. :monday), or raises an ArgumentError for invalid day symbol.
def find_beginning_of_week!(week_start)
- raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.keys.include?(week_start)
+ raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.key?(week_start)
week_start
end
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb
index 5ba8197006..828f03ad62 100644
--- a/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -1,8 +1,10 @@
require 'active_support/xml_mini'
require 'active_support/time'
+require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/object/to_param'
+require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/hash/reverse_merge'
-require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/inflections'
class Hash
diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb
index 45fec57009..de09f26f3d 100644
--- a/activesupport/lib/active_support/core_ext/hash/slice.rb
+++ b/activesupport/lib/active_support/core_ext/hash/slice.rb
@@ -32,9 +32,9 @@ class Hash
# Removes and returns the key/value pairs matching the given keys.
#
- # { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b)
- # # => {:a => 1, :b => 2}
+ # { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => { a: 1, b: 2 }
+ # { a: 1, b: 2 }.extract!(:a, :x) # => { a: 1 }
def extract!(*keys)
- keys.each_with_object({}) { |key, result| result[key] = delete(key) }
+ keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
end
end
diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb
index b3f5fde335..6c15fffc0f 100644
--- a/activesupport/lib/active_support/deprecation.rb
+++ b/activesupport/lib/active_support/deprecation.rb
@@ -1,15 +1,24 @@
-require 'active_support/core_ext/module/deprecation'
-require 'active_support/deprecation/instance_delegator'
-require 'active_support/deprecation/behaviors'
-require 'active_support/deprecation/reporting'
-require 'active_support/deprecation/method_wrappers'
-require 'active_support/deprecation/proxy_wrappers'
require 'singleton'
module ActiveSupport
# \Deprecation specifies the API used by Rails to deprecate methods, instance
# variables, objects and constants.
class Deprecation
+ # active_support.rb sets an autoload for ActiveSupport::Deprecation.
+ #
+ # If these requires were at the top of the file the constant would not be
+ # defined by the time their files were loaded. Since some of them reopen
+ # ActiveSupport::Deprecation its autoload would be triggered, resulting in
+ # a circular require warning for active_support/deprecation.rb.
+ #
+ # So, we define the constant first, and load dependencies later.
+ require 'active_support/deprecation/instance_delegator'
+ require 'active_support/deprecation/behaviors'
+ require 'active_support/deprecation/reporting'
+ require 'active_support/deprecation/method_wrappers'
+ require 'active_support/deprecation/proxy_wrappers'
+ require 'active_support/core_ext/module/deprecation'
+
include Singleton
include InstanceDelegator
include Behavior
diff --git a/activesupport/lib/active_support/descendants_tracker.rb b/activesupport/lib/active_support/descendants_tracker.rb
index e2a8b4d4e3..27861e01d0 100644
--- a/activesupport/lib/active_support/descendants_tracker.rb
+++ b/activesupport/lib/active_support/descendants_tracker.rb
@@ -2,35 +2,50 @@ module ActiveSupport
# This module provides an internal implementation to track descendants
# which is faster than iterating through ObjectSpace.
module DescendantsTracker
- @@direct_descendants = Hash.new { |h, k| h[k] = [] }
+ @@direct_descendants = {}
- def self.direct_descendants(klass)
- @@direct_descendants[klass]
- end
+ class << self
+ def direct_descendants(klass)
+ @@direct_descendants[klass] || []
+ end
- def self.descendants(klass)
- @@direct_descendants[klass].inject([]) do |descendants, _klass|
- descendants << _klass
- descendants.concat _klass.descendants
+ def descendants(klass)
+ arr = []
+ accumulate_descendants(klass, arr)
+ arr
end
- end
- def self.clear
- if defined? ActiveSupport::Dependencies
- @@direct_descendants.each do |klass, descendants|
- if ActiveSupport::Dependencies.autoloaded?(klass)
- @@direct_descendants.delete(klass)
- else
- descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
+ def clear
+ if defined? ActiveSupport::Dependencies
+ @@direct_descendants.each do |klass, descendants|
+ if ActiveSupport::Dependencies.autoloaded?(klass)
+ @@direct_descendants.delete(klass)
+ else
+ descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
+ end
end
+ else
+ @@direct_descendants.clear
+ end
+ end
+
+ # This is the only method that is not thread safe, but is only ever called
+ # during the eager loading phase.
+ def store_inherited(klass, descendant)
+ (@@direct_descendants[klass] ||= []) << descendant
+ end
+
+ private
+ def accumulate_descendants(klass, acc)
+ if direct_descendants = @@direct_descendants[klass]
+ acc.concat(direct_descendants)
+ direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
end
- else
- @@direct_descendants.clear
end
end
def inherited(base)
- self.direct_descendants << base
+ DescendantsTracker.store_inherited(self, base)
super
end
diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb
new file mode 100644
index 0000000000..04d170f801
--- /dev/null
+++ b/activesupport/lib/active_support/key_generator.rb
@@ -0,0 +1,23 @@
+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
+ # key in multiple incompatible contexts.
+ class KeyGenerator
+ def initialize(secret, options = {})
+ @secret = secret
+ # The default iterations are higher than required for our key derivation uses
+ # on the off chance someone uses this for password storage
+ @iterations = options[:iterations] || 2**16
+ end
+
+ # Returns a derived key suitable for use. The default key_size is chosen
+ # to be compatible with the default settings of ActiveSupport::MessageVerifier.
+ # i.e. OpenSSL::Digest::SHA1#block_length
+ def generate_key(salt, key_size=64)
+ OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size)
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/queueing.rb b/activesupport/lib/active_support/queueing.rb
index 7c352886f3..c8ba28021d 100644
--- a/activesupport/lib/active_support/queueing.rb
+++ b/activesupport/lib/active_support/queueing.rb
@@ -56,32 +56,6 @@ module ActiveSupport
end
end
- # A container for multiple queues. This class delegates to a default Queue
- # so that <tt>Rails.queue.push</tt> and friends will Just Work. To use this class
- # with multiple queues:
- #
- # # In your configuration:
- # Rails.queue[:image_queue] = SomeQueue.new
- # Rails.queue[:mail_queue] = SomeQueue.new
- #
- # # In your app code:
- # Rails.queue[:mail_queue].push SomeJob.new
- #
- class QueueContainer < DelegateClass(::Queue)
- def initialize(default_queue)
- @queues = { :default => default_queue }
- super(default_queue)
- end
-
- def [](queue_name)
- @queues[queue_name]
- end
-
- def []=(queue_name, queue)
- @queues[queue_name] = queue
- end
- end
-
# The threaded consumer will run jobs in a background thread in
# development mode or in a VM where running jobs on a thread in
# production mode makes sense.
@@ -90,10 +64,6 @@ module ActiveSupport
# queue and joins the thread, which will ensure that all jobs
# are executed before the process finally dies.
class ThreadedQueueConsumer
- def self.start(*args)
- new(*args).start
- end
-
def initialize(queue, options = {})
@queue = queue
@logger = options[:logger]
diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb
index 12aef0d7fe..7c149df1e4 100644
--- a/activesupport/lib/active_support/testing/performance/ruby.rb
+++ b/activesupport/lib/active_support/testing/performance/ruby.rb
@@ -2,7 +2,7 @@ begin
require 'ruby-prof'
rescue LoadError
$stderr.puts 'Specify ruby-prof as application\'s dependency in Gemfile to run benchmarks.'
- exit
+ raise
end
module ActiveSupport