aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib')
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb2
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb4
-rw-r--r--activesupport/lib/active_support/cache/strategy/local_cache.rb4
-rw-r--r--activesupport/lib/active_support/callbacks.rb55
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb34
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb60
-rw-r--r--activesupport/lib/active_support/notifications.rb101
7 files changed, 177 insertions, 83 deletions
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index 85e7e21624..9460532af0 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -8,7 +8,7 @@ module ActiveSupport
# A cache store implementation which stores everything on the filesystem.
#
# FileStore implements the Strategy::LocalCache strategy which implements
- # an in memory cache inside of a block.
+ # an in-memory cache inside of a block.
class FileStore < Store
attr_reader :cache_path
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index e07294178b..530839b24d 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -11,7 +11,7 @@ require 'active_support/core_ext/string/encoding'
module ActiveSupport
module Cache
# A cache store implementation which stores data in Memcached:
- # http://www.danga.com/memcached/
+ # http://memcached.org/
#
# This is currently the most popular cache store for production websites.
#
@@ -21,7 +21,7 @@ module ActiveSupport
# server goes down, then MemCacheStore will ignore it until it comes back up.
#
# MemCacheStore implements the Strategy::LocalCache strategy which implements
- # an in memory cache inside of a block.
+ # an in-memory cache inside of a block.
class MemCacheStore < Store
module Response # :nodoc:
STORED = "STORED\r\n"
diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb
index 0649a058aa..db5f228a70 100644
--- a/activesupport/lib/active_support/cache/strategy/local_cache.rb
+++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb
@@ -4,9 +4,9 @@ require 'active_support/core_ext/string/inflections'
module ActiveSupport
module Cache
module Strategy
- # Caches that implement LocalCache will be backed by an in memory cache for the
+ # Caches that implement LocalCache will be backed by an in-memory cache for the
# duration of a block. Repeated calls to the cache for the same key will hit the
- # in memory cache for faster access.
+ # in-memory cache for faster access.
module LocalCache
# 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.
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 656cba625c..ea37355fc1 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -153,7 +153,7 @@ module ActiveSupport
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def _one_time_conditions_valid_#{@callback_id}?
- true #{key_options[0]}
+ true if #{key_options}
end
RUBY_EVAL
end
@@ -171,8 +171,8 @@ module ActiveSupport
# if condition # before_save :filter_name, :if => :condition
# filter_name
# end
- filter = <<-RUBY_EVAL
- unless halted
+ <<-RUBY_EVAL
+ if !halted && #{@compiled_options}
# This double assignment is to prevent warnings in 1.9.3. I would
# remove the `result` variable, but apparently some other
# generated code is depending on this variable being set sometimes
@@ -181,8 +181,6 @@ module ActiveSupport
halted = (#{chain.config[:terminator]})
end
RUBY_EVAL
-
- [@compiled_options[0], filter, @compiled_options[1]].compact.join("\n")
when :around
# Compile around filters with conditions into proxy methods
# that contain the conditions.
@@ -202,7 +200,7 @@ module ActiveSupport
name = "_conditional_callback_#{@kind}_#{next_id}"
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}(halted)
- #{@compiled_options[0] || "if true"} && !halted
+ if #{@compiled_options} && !halted
#{@filter} do
yield self
end
@@ -222,10 +220,12 @@ module ActiveSupport
case @kind
when :after
- # if condition # after_save :filter_name, :if => :condition
- # filter_name
- # end
- [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n")
+ # after_save :filter_name, :if => :condition
+ <<-RUBY_EVAL
+ if #{@compiled_options}
+ #{@filter}
+ end
+ RUBY_EVAL
when :around
<<-RUBY_EVAL
value
@@ -240,9 +240,7 @@ module ActiveSupport
# symbols, string, procs, and objects), so compile a conditional
# expression based on the options
def _compile_options(options)
- return [] if options[:if].empty? && options[:unless].empty?
-
- conditions = []
+ conditions = ["true"]
unless options[:if].empty?
conditions << Array.wrap(_compile_filter(options[:if]))
@@ -252,7 +250,7 @@ module ActiveSupport
conditions << Array.wrap(_compile_filter(options[:unless])).map {|f| "!#{f}"}
end
- ["if #{conditions.flatten.join(" && ")}", "end"]
+ conditions.flatten.join(" && ")
end
# Filters support:
@@ -371,42 +369,37 @@ module ActiveSupport
# Generate the internal runner method called by +run_callbacks+.
def __define_runner(symbol) #:nodoc:
body = send("_#{symbol}_callbacks").compile
+ runner_method = "_run_#{symbol}_callbacks"
silence_warnings do
- undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks")
+ undef_method runner_method if method_defined?(runner_method)
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def _run_#{symbol}_callbacks(key = nil, &blk)
+ def #{runner_method}(key = nil, &blk)
if key
- name = "_run__\#{self.class.name.hash.abs}__#{symbol}__\#{key.hash.abs}__callbacks"
-
- unless respond_to?(name)
- self.class.__create_keyed_callback(name, :#{symbol}, self, &blk)
- end
-
- send(name, &blk)
+ self.class.__run_keyed_callback(key, :#{symbol}, self, &blk)
else
#{body}
end
end
- private :_run_#{symbol}_callbacks
+ private :#{runner_method}
RUBY_EVAL
end
end
- # This is called the first time a callback is called with a particular
- # key. It creates a new callback method for the key, calculating
- # which callbacks can be omitted because of per_key conditions.
+ # This method calls the callback method for the given key.
+ # If this called first time it creates a new callback method for the key,
+ # calculating which callbacks can be omitted because of per_key conditions.
#
- def __create_keyed_callback(name, kind, object, &blk) #:nodoc:
- @_keyed_callbacks ||= {}
- @_keyed_callbacks[name] ||= begin
+ def __run_keyed_callback(key, kind, object, &blk) #:nodoc:
+ name = "_run__#{self.name.hash.abs}__#{kind}__#{key.hash.abs}__callbacks"
+ unless object.respond_to?(name)
str = send("_#{kind}_callbacks").compile(name, object)
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}() #{str} end
protected :#{name}
RUBY_EVAL
- true
end
+ object.send(name, &blk)
end
# This is used internally to append, prepend and skip callbacks to the
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index e76ee60dd7..144cdd3c8f 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -21,14 +21,7 @@ module ActiveSupport
# "words".pluralize # => "words"
# "CamelOctopus".pluralize # => "CamelOctopi"
def pluralize(word)
- result = word.to_s.dup
-
- if word.empty? || inflections.uncountables.include?(result.downcase)
- result
- else
- inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
- result
- end
+ apply_inflections(word, inflections.plurals)
end
# The reverse of +pluralize+, returns the singular form of a word in a string.
@@ -40,14 +33,7 @@ module ActiveSupport
# "word".singularize # => "word"
# "CamelOctopi".singularize # => "CamelOctopus"
def singularize(word)
- result = word.to_s.dup
-
- if inflections.uncountables.any? { |inflection| result =~ /\b(#{inflection})\Z/i }
- result
- else
- inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
- result
- end
+ apply_inflections(word, inflections.singulars)
end
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
@@ -311,5 +297,21 @@ module ActiveSupport
part.empty? ? acc : "#{part}(::#{acc})?"
end
end
+
+ # Applies inflection rules for +singularize+ and +pluralize+.
+ #
+ # Examples:
+ # apply_inflections("post", inflections.plurals) # => "posts"
+ # apply_inflections("posts", inflections.singulars) # => "post"
+ def apply_inflections(word, rules)
+ result = word.to_s.dup
+
+ if word.empty? || inflections.uncountables.any? { |inflection| result =~ /\b#{inflection}\Z/i }
+ result
+ else
+ rules.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
+ result
+ end
+ end
end
end
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index e14386a85d..9ef2b29580 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -10,6 +10,16 @@ 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.
class MessageEncryptor
+ module NullSerializer #:nodoc:
+ def self.load(value)
+ value
+ end
+
+ def self.dump(value)
+ value
+ end
+ end
+
class InvalidMessage < StandardError; end
OpenSSLCipherError = OpenSSL::Cipher.const_defined?(:CipherError) ? OpenSSL::Cipher::CipherError : OpenSSL::CipherError
@@ -18,13 +28,40 @@ module ActiveSupport
ActiveSupport::Deprecation.warn "The second parameter should be an options hash. Use :cipher => 'algorithm' to specify the cipher algorithm."
options = { :cipher => options }
end
-
+
@secret = secret
@cipher = options[:cipher] || 'aes-256-cbc'
+ @verifier = MessageVerifier.new(@secret, :serializer => NullSerializer)
@serializer = options[:serializer] || Marshal
end
def encrypt(value)
+ ActiveSupport::Deprecation.warn "MessageEncryptor#encrypt is deprecated as it is not safe without a signature. " \
+ "Please use MessageEncryptor#encrypt_and_sign instead."
+ _encrypt(value)
+ end
+
+ def decrypt(value)
+ ActiveSupport::Deprecation.warn "MessageEncryptor#decrypt is deprecated as it is not safe without a signature. " \
+ "Please use MessageEncryptor#decrypt_and_verify instead."
+ _decrypt(value)
+ end
+
+ # Encrypt and sign a message. We need to sign the message in order to avoid padding attacks.
+ # Reference: http://www.limited-entropy.com/padding-oracle-attacks
+ def encrypt_and_sign(value)
+ verifier.generate(_encrypt(value))
+ end
+
+ # Decrypt and verify a message. We need to verify the message in order to avoid padding attacks.
+ # Reference: http://www.limited-entropy.com/padding-oracle-attacks
+ def decrypt_and_verify(value)
+ _decrypt(verifier.verify(value))
+ end
+
+ private
+
+ def _encrypt(value)
cipher = new_cipher
# Rely on OpenSSL for the initialization vector
iv = cipher.random_iv
@@ -39,7 +76,7 @@ module ActiveSupport
[encrypted_data, iv].map {|v| ActiveSupport::Base64.encode64s(v)}.join("--")
end
- def decrypt(encrypted_message)
+ def _decrypt(encrypted_message)
cipher = new_cipher
encrypted_data, iv = encrypted_message.split("--").map {|v| ActiveSupport::Base64.decode64(v)}
@@ -55,23 +92,12 @@ module ActiveSupport
raise InvalidMessage
end
- def encrypt_and_sign(value)
- verifier.generate(encrypt(value))
+ def new_cipher
+ OpenSSL::Cipher::Cipher.new(@cipher)
end
- def decrypt_and_verify(value)
- decrypt(verifier.verify(value))
+ def verifier
+ @verifier
end
-
-
-
- private
- def new_cipher
- OpenSSL::Cipher::Cipher.new(@cipher)
- end
-
- def verifier
- MessageVerifier.new(@secret)
- end
end
end
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index b5a70d5933..f549d2fff3 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -1,36 +1,102 @@
module ActiveSupport
- # Notifications provides an instrumentation API for Ruby. To instrument an
- # action in Ruby you just need to do:
+ # = Notifications
#
- # ActiveSupport::Notifications.instrument(:render, :extra => :information) do
+ # +ActiveSupport::Notifications+ provides an instrumentation API for Ruby.
+ #
+ # == Instrumenters
+ #
+ # To instrument an event you just need to do:
+ #
+ # ActiveSupport::Notifications.instrument("render", :extra => :information) do
# render :text => "Foo"
# end
#
+ # That executes the block first and notifies all subscribers once done.
+ #
+ # In the example above "render" is the name of the event, and the rest is called
+ # the _payload_. The payload is a mechanism that allows instrumenters to pass
+ # extra information to subscribers. Payloads consist of a hash whose contents
+ # are arbitrary and generally depend on the event.
+ #
+ # == Subscribers
+ #
# You can consume those events and the information they provide by registering
- # a log subscriber. For instance, let's store all instrumented events in an array:
+ # a subscriber. For instance, let's store all "render" events in an array:
#
- # @events = []
+ # events = []
#
- # ActiveSupport::Notifications.subscribe do |*args|
- # @events << ActiveSupport::Notifications::Event.new(*args)
+ # ActiveSupport::Notifications.subscribe("render") do |*args|
+ # events << ActiveSupport::Notifications::Event.new(*args)
# end
#
- # ActiveSupport::Notifications.instrument(:render, :extra => :information) do
+ # That code returns right away, you are just subscribing to "render" events.
+ # The block will be called asynchronously whenever someone instruments "render":
+ #
+ # ActiveSupport::Notifications.instrument("render", :extra => :information) do
# render :text => "Foo"
# end
#
- # event = @events.first
- # event.name # => :render
+ # event = events.first
+ # event.name # => "render"
# event.duration # => 10 (in milliseconds)
# event.payload # => { :extra => :information }
#
- # When subscribing to Notifications, you can pass a pattern, to only consume
- # events that match the pattern:
+ # The block in the +subscribe+ call gets the name of the event, start
+ # timestamp, end timestamp, a string with a unique identifier for that event
+ # (something like "535801666f04d0298cd6"), and a hash with the payload, in
+ # that order.
#
- # ActiveSupport::Notifications.subscribe(/render/) do |event|
- # @render_events << event
+ # If an exception happens during that particular instrumentation the payload will
+ # have a key +:exception+ with an array of two elements as value: a string with
+ # the name of the exception class, and the exception message.
+ #
+ # As the previous example depicts, the class +ActiveSupport::Notifications::Event+
+ # is able to take the arguments as they come and provide an object-oriented
+ # interface to that data.
+ #
+ # You can also subscribe to all events whose name matches a certain regexp:
+ #
+ # ActiveSupport::Notifications.subscribe(/render/) do |*args|
+ # ...
# end
#
+ # and even pass no argument to +subscribe+, in which case you are subscribing
+ # to all events.
+ #
+ # == Temporary Subscriptions
+ #
+ # Sometimes you do not want to subscribe to an event for the entire life of
+ # the application. There are two ways to unsubscribe.
+ #
+ # === Subscribe While a Block Runs
+ #
+ # You can subscribe to some event temporarily while some block runs. For
+ # example, in
+ #
+ # callback = lambda {|*args| ... }
+ # ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
+ # ...
+ # end
+ #
+ # the callback will be called for all "sql.active_record" events instrumented
+ # during the execution of the block. The callback is unsubscribed automatically
+ # after that.
+ #
+ # === Manual Unsubscription
+ #
+ # The +subscribe+ method returns a subscriber object:
+ #
+ # subscriber = ActiveSupport::Notifications.subscribe("render") do |*args|
+ # ...
+ # end
+ #
+ # To prevent that block from being called anymore, just unsubscribe passing
+ # that reference:
+ #
+ # ActiveSupport::Notifications.unsubscribe(subscriber)
+ #
+ # == 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.
#
@@ -62,6 +128,13 @@ module ActiveSupport
end
end
+ def subscribed(callback, *args, &block)
+ subscriber = subscribe(*args, &callback)
+ yield
+ ensure
+ unsubscribe(subscriber)
+ end
+
def unsubscribe(args)
notifier.unsubscribe(args)
@instrumenters.clear