diff options
Diffstat (limited to 'activesupport/lib/active_support')
13 files changed, 194 insertions, 102 deletions
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 6535cc1eb5..2bf24558a6 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -149,7 +149,7 @@ module ActiveSupport # Create a new cache. The options will be passed to any write method calls except # for :namespace which can be used to set the global namespace for the cache. - def initialize (options = nil) + def initialize(options = nil) @options = options ? options.dup : {} end @@ -538,11 +538,11 @@ module ActiveSupport # 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 = {}) + 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(:@compressed, options[:compressed]) entry.instance_variable_set(:@expires_in, options[:expires_in]) entry end @@ -573,6 +573,9 @@ module ActiveSupport # 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) end 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/concern.rb b/activesupport/lib/active_support/concern.rb index 81fb859334..af3da937c7 100644 --- a/activesupport/lib/active_support/concern.rb +++ b/activesupport/lib/active_support/concern.rb @@ -4,17 +4,12 @@ module ActiveSupport # module M # def self.included(base) # base.extend ClassMethods - # base.send(:include, InstanceMethods) # scope :disabled, where(:disabled => true) # end # # module ClassMethods # ... # end - # - # module InstanceMethods - # ... - # end # end # # By using <tt>ActiveSupport::Concern</tt> the above module could instead be written as: @@ -31,10 +26,6 @@ module ActiveSupport # module ClassMethods # ... # end - # - # module InstanceMethods - # ... - # end # end # # Moreover, it gracefully handles module dependencies. Given a +Foo+ module and a +Bar+ @@ -118,7 +109,11 @@ module ActiveSupport @_dependencies.each { |dep| base.send(:include, dep) } super base.extend const_get("ClassMethods") if const_defined?("ClassMethods") - base.send :include, const_get("InstanceMethods") if const_defined?("InstanceMethods") + if const_defined?("InstanceMethods") + base.send :include, const_get("InstanceMethods") + ActiveSupport::Deprecation.warn "The InstanceMethods module inside ActiveSupport::Concern will be " \ + "no longer included automatically. Please define instance methods directly in #{base} instead.", caller + end base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block") end end diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index 26a99658cc..c6d5f29690 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -192,13 +192,13 @@ class Date alias :sunday :end_of_week alias :at_end_of_week :end_of_week - # Returns a new Date/DateTime representing the start of the given day in the previous week (default is Monday). + # Returns a new Date/DateTime representing the start of the given day in the previous week (default is :monday). def prev_week(day = :monday) result = (self - 7).beginning_of_week + DAYS_INTO_WEEK[day] self.acts_like?(:time) ? result.change(:hour => 0) : result end - # Returns a new Date/DateTime representing the start of the given day in next week (default is Monday). + # Returns a new Date/DateTime representing the start of the given day in next week (default is :monday). def next_week(day = :monday) result = (self + 7).beginning_of_week + DAYS_INTO_WEEK[day] self.acts_like?(:time) ? result.change(:hour => 0) : result diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 7a55086452..1e15529569 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -176,12 +176,12 @@ class Time end alias :at_end_of_week :end_of_week - # Returns a new Time representing the start of the given day in the previous week (default is Monday). + # Returns a new Time representing the start of the given day in the previous week (default is :monday). def prev_week(day = :monday) ago(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0) end - # Returns a new Time representing the start of the given day in next week (default is Monday). + # Returns a new Time representing the start of the given day in next week (default is :monday). def next_week(day = :monday) since(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0) end diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index b3ac271778..db90de4682 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -428,7 +428,7 @@ module ActiveSupport #:nodoc: end # Attempt to autoload the provided module name by searching for a directory - # matching the expect path suffix. If found, the module is created and assigned + # matching the expected path suffix. If found, the module is created and assigned # to +into+'s constants with the name +const_name+. Provided that the directory # was loaded from a reloadable base path, it is added to the set of constants # that are to be unloaded. 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 diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index 0264133581..b0d4f2bd86 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -20,7 +20,7 @@ module ActiveSupport # oh.keys # => [:a, :b], this order is guaranteed # # <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts with other implementations. - class OrderedHash < ::Hash #:nodoc: + class OrderedHash < ::Hash def to_yaml_type "!tag:yaml.org,2002:omap" end |