diff options
author | Dmitry Polushkin <dmitry.polushkin@gmail.com> | 2011-12-31 01:10:42 +0000 |
---|---|---|
committer | Dmitry Polushkin <dmitry.polushkin@gmail.com> | 2011-12-31 01:10:42 +0000 |
commit | 04bc40ff501b1bf81bec7ce3937cb06c896ffc69 (patch) | |
tree | 88a33663195900df8a7307aefa2c9aaa561c3973 /activesupport/lib/active_support | |
parent | 84eece0a823e9c601ea99a8709f24605a19bcbfd (diff) | |
parent | ed17983ec56dec689a0311c7f8fcbeba9874e5a4 (diff) | |
download | rails-04bc40ff501b1bf81bec7ce3937cb06c896ffc69.tar.gz rails-04bc40ff501b1bf81bec7ce3937cb06c896ffc69.tar.bz2 rails-04bc40ff501b1bf81bec7ce3937cb06c896ffc69.zip |
Merge branch 'master' of git://github.com/rails/rails
Diffstat (limited to 'activesupport/lib/active_support')
90 files changed, 985 insertions, 1998 deletions
diff --git a/activesupport/lib/active_support/base64.rb b/activesupport/lib/active_support/base64.rb index 35014cb3d5..41a1a3469d 100644 --- a/activesupport/lib/active_support/base64.rb +++ b/activesupport/lib/active_support/base64.rb @@ -1,42 +1,18 @@ -begin - require 'base64' -rescue LoadError -end +require 'base64' module ActiveSupport - if defined? ::Base64 - Base64 = ::Base64 - else - # Base64 provides utility methods for encoding and de-coding binary data - # using a base 64 representation. A base 64 representation of binary data - # consists entirely of printable US-ASCII characters. The Base64 module - # is included in Ruby 1.8, but has been removed in Ruby 1.9. - module Base64 - # Encodes a string to its base 64 representation. Each 60 characters of - # output is separated by a newline character. - # - # ActiveSupport::Base64.encode64("Original unencoded string") - # # => "T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==\n" - def self.encode64(data) - [data].pack("m") - end - - # Decodes a base 64 encoded string to its original representation. - # - # ActiveSupport::Base64.decode64("T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==") - # # => "Original unencoded string" - def self.decode64(data) - data.unpack("m").first - end - end - end + Base64 = ::Base64 + # *DEPRECATED*: Use +Base64.strict_encode64+ instead. + # # Encodes the value as base64 without the newline breaks. This makes the base64 encoding readily usable as URL parameters # or memcache keys without further processing. # # ActiveSupport::Base64.encode64s("Original unencoded string") # # => "T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==" def Base64.encode64s(value) - encode64(value).gsub(/\n/, '') + ActiveSupport::Deprecation.warn "encode64s " \ + "is deprecated. Use Base64.strict_encode64 instead", caller + strict_encode64(value) end end diff --git a/activesupport/lib/active_support/basic_object.rb b/activesupport/lib/active_support/basic_object.rb index 3b5277c205..c3c7ab0112 100644 --- a/activesupport/lib/active_support/basic_object.rb +++ b/activesupport/lib/active_support/basic_object.rb @@ -1,21 +1,14 @@ module ActiveSupport - if defined? ::BasicObject - # A class with no predefined methods that behaves similarly to Builder's - # BlankSlate. Used for proxy classes. - class BasicObject < ::BasicObject - undef_method :== - undef_method :equal? + # A class with no predefined methods that behaves similarly to Builder's + # BlankSlate. Used for proxy classes. + class BasicObject < ::BasicObject + undef_method :== + undef_method :equal? - # Let ActiveSupport::BasicObject at least raise exceptions. - def raise(*args) - ::Object.send(:raise, *args) - end - end - else - class BasicObject #:nodoc: - instance_methods.each do |m| - undef_method(m) if m.to_s !~ /(?:^__|^nil\?$|^send$|^object_id$)/ - end + # Let ActiveSupport::BasicObject at least raise exceptions. + def raise(*args) + ::Object.send(:raise, *args) end end + end diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb index 26412cd7f4..36e29644c6 100644 --- a/activesupport/lib/active_support/buffered_logger.rb +++ b/activesupport/lib/active_support/buffered_logger.rb @@ -1,137 +1,10 @@ require 'thread' require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/deprecation' +require 'active_support/logger' +require 'fileutils' module ActiveSupport - # Inspired by the buffered logger idea by Ezra - class BufferedLogger - module Severity - DEBUG = 0 - INFO = 1 - WARN = 2 - ERROR = 3 - FATAL = 4 - UNKNOWN = 5 - end - include Severity - - MAX_BUFFER_SIZE = 1000 - - ## - # :singleton-method: - # Set to false to disable the silencer - cattr_accessor :silencer - self.silencer = true - - # Silences the logger for the duration of the block. - def silence(temporary_level = ERROR) - if silencer - begin - old_logger_level, self.level = level, temporary_level - yield self - ensure - self.level = old_logger_level - end - else - yield self - end - end - - attr_accessor :level - attr_reader :auto_flushing - - def initialize(log, level = DEBUG) - @level = level - @buffer = Hash.new { |h,k| h[k] = [] } - @auto_flushing = 1 - @guard = Mutex.new - - if log.respond_to?(:write) - @log = log - elsif File.exist?(log) - @log = open_log(log, (File::WRONLY | File::APPEND)) - else - FileUtils.mkdir_p(File.dirname(log)) - @log = open_log(log, (File::WRONLY | File::APPEND | File::CREAT)) - end - end - - def open_log(log, mode) - open(log, mode).tap do |open_log| - open_log.set_encoding(Encoding::BINARY) if open_log.respond_to?(:set_encoding) - open_log.sync = true - end - end - - def add(severity, message = nil, progname = nil, &block) - return if @level > severity - message = (message || (block && block.call) || progname).to_s - # If a newline is necessary then create a new message ending with a newline. - # Ensures that the original message is not mutated. - message = "#{message}\n" unless message[-1] == ?\n - buffer << message - auto_flush - message - end - - # Dynamically add methods such as: - # def info - # def warn - # def debug - Severity.constants.each do |severity| - class_eval <<-EOT, __FILE__, __LINE__ + 1 - def #{severity.downcase}(message = nil, progname = nil, &block) # def debug(message = nil, progname = nil, &block) - add(#{severity}, message, progname, &block) # add(DEBUG, message, progname, &block) - end # end - - def #{severity.downcase}? # def debug? - #{severity} >= @level # DEBUG >= @level - end # end - EOT - end - - # Set the auto-flush period. Set to true to flush after every log message, - # to an integer to flush every N messages, or to false, nil, or zero to - # never auto-flush. If you turn auto-flushing off, be sure to regularly - # flush the log yourself -- it will eat up memory until you do. - def auto_flushing=(period) - @auto_flushing = - case period - when true; 1 - when false, nil, 0; MAX_BUFFER_SIZE - when Integer; period - else raise ArgumentError, "Unrecognized auto_flushing period: #{period.inspect}" - end - end - - def flush - @guard.synchronize do - buffer.each do |content| - @log.write(content) - end - - # Important to do this even if buffer was empty or else @buffer will - # accumulate empty arrays for each request where nothing was logged. - clear_buffer - end - end - - def close - flush - @log.close if @log.respond_to?(:close) - @log = nil - end - - protected - def auto_flush - flush if buffer.size >= @auto_flushing - end - - def buffer - @buffer[Thread.current] - end - - def clear_buffer - @buffer.delete(Thread.current) - end - end + BufferedLogger = ActiveSupport::Deprecation::DeprecatedConstantProxy.new( + 'BufferedLogger', '::ActiveSupport::Logger') end diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 95d936b32f..7d032ca984 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -16,8 +16,7 @@ module ActiveSupport autoload :FileStore, 'active_support/cache/file_store' autoload :MemoryStore, 'active_support/cache/memory_store' autoload :MemCacheStore, 'active_support/cache/mem_cache_store' - autoload :SynchronizedMemoryStore, 'active_support/cache/synchronized_memory_store' - autoload :CompressedMemCacheStore, 'active_support/cache/compressed_mem_cache_store' + autoload :NullStore, 'active_support/cache/null_store' # These options mean something to all cache implementations. Individual cache # implementations may support additional options. @@ -27,75 +26,75 @@ module ActiveSupport autoload :LocalCache, 'active_support/cache/strategy/local_cache' end - # Creates a new CacheStore object according to the given options. - # - # If no arguments are passed to this method, then a new - # ActiveSupport::Cache::MemoryStore object will be returned. - # - # If you pass a Symbol as the first argument, then a corresponding cache - # store class under the ActiveSupport::Cache namespace will be created. - # For example: - # - # ActiveSupport::Cache.lookup_store(:memory_store) - # # => returns a new ActiveSupport::Cache::MemoryStore object - # - # ActiveSupport::Cache.lookup_store(:mem_cache_store) - # # => returns a new ActiveSupport::Cache::MemCacheStore object - # - # Any additional arguments will be passed to the corresponding cache store - # class's constructor: - # - # ActiveSupport::Cache.lookup_store(:file_store, "/tmp/cache") - # # => same as: ActiveSupport::Cache::FileStore.new("/tmp/cache") - # - # If the first argument is not a Symbol, then it will simply be returned: - # - # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new) - # # => returns MyOwnCacheStore.new - def self.lookup_store(*store_option) - store, *parameters = *Array.wrap(store_option).flatten - - 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) - when nil - ActiveSupport::Cache::MemoryStore.new - else - store + class << self + # Creates a new CacheStore object according to the given options. + # + # If no arguments are passed to this method, then a new + # ActiveSupport::Cache::MemoryStore object will be returned. + # + # If you pass a Symbol as the first argument, then a corresponding cache + # store class under the ActiveSupport::Cache namespace will be created. + # For example: + # + # ActiveSupport::Cache.lookup_store(:memory_store) + # # => returns a new ActiveSupport::Cache::MemoryStore object + # + # ActiveSupport::Cache.lookup_store(:mem_cache_store) + # # => returns a new ActiveSupport::Cache::MemCacheStore object + # + # Any additional arguments will be passed to the corresponding cache store + # class's constructor: + # + # ActiveSupport::Cache.lookup_store(:file_store, "/tmp/cache") + # # => same as: ActiveSupport::Cache::FileStore.new("/tmp/cache") + # + # If the first argument is not a Symbol, then it will simply be returned: + # + # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new) + # # => returns MyOwnCacheStore.new + def lookup_store(*store_option) + store, *parameters = *Array.wrap(store_option).flatten + + 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) + when nil + ActiveSupport::Cache::MemoryStore.new + else + store + end end - end - def self.expand_cache_key(key, namespace = nil) - expanded_cache_key = namespace ? "#{namespace}/" : "" + def expand_cache_key(key, namespace = nil) + expanded_cache_key = namespace ? "#{namespace}/" : "" + + prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"] + if prefix + expanded_cache_key << "#{prefix}/" + end - prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"] - if prefix - expanded_cache_key << "#{prefix}/" + expanded_cache_key << retrieve_cache_key(key) + expanded_cache_key end - expanded_cache_key << - if key.respond_to?(:cache_key) - key.cache_key - elsif key.is_a?(Array) - if key.size > 1 - key.collect { |element| expand_cache_key(element) }.to_param - else - key.first.to_param - end - elsif key - key.to_param - end.to_s + private - expanded_cache_key + def retrieve_cache_key(key) + case + when key.respond_to?(:cache_key) then key.cache_key + when key.is_a?(Array) then ['Array', *key.map { |element| retrieve_cache_key(element) }].to_param + else key.to_param + end.to_s + end end # An abstract cache store class. There are multiple cache store @@ -141,7 +140,7 @@ module ActiveSupport # large enough to warrant compression. To turn on compression either pass # <tt>:compress => true</tt> in the initializer or as an option to +fetch+ # or +write+. To specify the threshold at which to compress values, set the - # <tt>:compress_threshold</tt> option. The default threshold is 32K. + # <tt>:compress_threshold</tt> option. The default threshold is 16K. class Store cattr_accessor :logger, :instance_writer => true @@ -151,7 +150,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 @@ -232,7 +231,7 @@ module ActiveSupport # <tt>:race_condition_ttl</tt> does not play any role. # # # Set all values to expire after one minute. - # cache = ActiveSupport::Cache::MemoryCache.new(:expires_in => 1.minute) + # cache = ActiveSupport::Cache::MemoryStore.new(:expires_in => 1.minute) # # cache.write("foo", "original value") # val_1 = nil @@ -540,11 +539,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 @@ -561,7 +560,7 @@ module ActiveSupport @value = nil else @value = Marshal.dump(value) - if should_compress?(value, options) + if should_compress?(@value, options) @value = Zlib::Deflate.deflate(@value) @compressed = true end @@ -575,6 +574,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 @@ -615,13 +617,10 @@ module ActiveSupport end private - def should_compress?(value, options) - if options[:compress] && value - unless value.is_a?(Numeric) - compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT - serialized_value = value.is_a?(String) ? value : Marshal.dump(value) - return true if serialized_value.size >= compress_threshold - end + def should_compress?(serialized_value, options) + if options[:compress] + compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT + return true if serialized_value.size >= compress_threshold end false end diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index b431041b76..9460532af0 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -8,12 +8,13 @@ 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 DIR_FORMATTER = "%03X" FILENAME_MAX_SIZE = 230 # max filename size on file system is 255, minus room for timestamp and random characters appended by Tempfile (used by atomic write) + EXCLUDED_DIRS = ['.', '..'].freeze def initialize(cache_path, options = nil) super(options) @@ -22,7 +23,7 @@ module ActiveSupport end def clear(options = nil) - root_dirs = Dir.entries(cache_path).reject{|f| f.in?(['.', '..'])} + root_dirs = Dir.entries(cache_path).reject{|f| f.in?(EXCLUDED_DIRS)} FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)}) end @@ -149,7 +150,7 @@ module ActiveSupport # Delete empty directories in the cache. def delete_empty_directories(dir) return if dir == cache_path - if Dir.entries(dir).reject{|f| f.in?(['.', '..'])}.empty? + if Dir.entries(dir).reject{|f| f.in?(EXCLUDED_DIRS)}.empty? File.delete(dir) rescue nil delete_empty_directories(File.dirname(dir)) end @@ -163,7 +164,7 @@ module ActiveSupport def search_dir(dir, &callback) return if !File.exist?(dir) Dir.foreach(dir) do |d| - next if d == "." || d == ".." + next if d.in?(EXCLUDED_DIRS) name = File.join(dir, d) if File.directory?(name) search_dir(name, &callback) diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index e07294178b..e38a8387b4 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" @@ -165,7 +165,7 @@ module ActiveSupport # characters properly. def escape_key(key) key = key.to_s.dup - key = key.force_encoding("BINARY") if key.encoding_aware? + key = key.force_encoding("BINARY") key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" } key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250 key diff --git a/activesupport/lib/active_support/cache/null_store.rb b/activesupport/lib/active_support/cache/null_store.rb new file mode 100644 index 0000000000..4427eaafcd --- /dev/null +++ b/activesupport/lib/active_support/cache/null_store.rb @@ -0,0 +1,44 @@ +module ActiveSupport + module Cache + # A cache store implementation which doesn't actually store anything. Useful in + # development and test environments where you don't want caching turned on but + # need to go through the caching interface. + # + # This cache does implement the local cache strategy, so values will actually + # be cached inside blocks that utilize this strategy. See + # ActiveSupport::Cache::Strategy::LocalCache for more details. + class NullStore < Store + def initialize(options = nil) + super(options) + extend Strategy::LocalCache + end + + def clear(options = nil) + end + + def cleanup(options = nil) + end + + def increment(name, amount = 1, options = nil) + end + + def decrement(name, amount = 1, options = nil) + end + + def delete_matched(matcher, options = nil) + end + + protected + def read_entry(key, options) # :nodoc: + end + + def write_entry(key, entry, options) # :nodoc: + true + end + + def delete_entry(key, options) # :nodoc: + false + end + end + end +end diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb index 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..df3aeb6b8a 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -77,8 +77,16 @@ module ActiveSupport # save # end # - def run_callbacks(kind, *args, &block) - send("_run_#{kind}_callbacks", *args, &block) + def run_callbacks(kind, key = nil, &block) + self.class.__run_callbacks(key, kind, self, &block) + end + + private + + # A hook invoked everytime a before callback is halted. + # This can be overriden in AS::Callback implementors in order + # to provide better debugging/logging. + def halted_callback_hook(filter) end class Callback #:nodoc:# @@ -153,7 +161,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,18 +179,18 @@ module ActiveSupport # if condition # before_save :filter_name, :if => :condition # filter_name # end - filter = <<-RUBY_EVAL - unless halted - # 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 - # and sometimes not. + <<-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 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 +210,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 +230,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 +250,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 +260,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: @@ -368,45 +376,30 @@ module ActiveSupport end module ClassMethods - # Generate the internal runner method called by +run_callbacks+. - def __define_runner(symbol) #:nodoc: - body = send("_#{symbol}_callbacks").compile - - silence_warnings do - undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks") - class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def _run_#{symbol}_callbacks(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) - else - #{body} - end - end - private :_run_#{symbol}_callbacks - 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 - str = send("_#{kind}_callbacks").compile(name, object) + def __run_callbacks(key, kind, object, &blk) #:nodoc: + name = __callback_runner_name(key, kind) + unless object.respond_to?(name) + str = send("_#{kind}_callbacks").compile(key, object) class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def #{name}() #{str} end protected :#{name} RUBY_EVAL - true end + object.send(name, &blk) + end + + def __reset_runner(symbol) + name = __callback_runner_name(nil, symbol) + undef_method(name) if method_defined?(name) + end + + def __callback_runner_name(key, kind) + "_run__#{self.name.hash.abs}__#{kind}__#{key.hash.abs}__callbacks" end # This is used internally to append, prepend and skip callbacks to the @@ -420,7 +413,7 @@ module ActiveSupport ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target| chain = target.send("_#{name}_callbacks") yield target, chain.dup, type, filters, options - target.__define_runner(name) + target.__reset_runner(name) end end @@ -534,12 +527,12 @@ module ActiveSupport chain = target.send("_#{symbol}_callbacks").dup callbacks.each { |c| chain.delete(c) } target.send("_#{symbol}_callbacks=", chain) - target.__define_runner(symbol) + target.__reset_runner(symbol) end self.send("_#{symbol}_callbacks=", callbacks.dup.clear) - __define_runner(symbol) + __reset_runner(symbol) end # Define sets of events in the object lifecycle that support callbacks. @@ -613,7 +606,6 @@ module ActiveSupport callbacks.each do |callback| class_attribute "_#{callback}_callbacks" send("_#{callback}_callbacks=", CallbackChain.new(callback, config)) - __define_runner(callback) end end end diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb index 81fb859334..c94a8d99f4 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,6 @@ 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") base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block") end end diff --git a/activesupport/lib/active_support/core_ext.rb b/activesupport/lib/active_support/core_ext.rb index 46a8609dd7..b48bdf08e8 100644 --- a/activesupport/lib/active_support/core_ext.rb +++ b/activesupport/lib/active_support/core_ext.rb @@ -1,3 +1,4 @@ Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].sort.each do |path| + next if File.basename(path, '.rb') == 'logger' require "active_support/core_ext/#{File.basename(path, '.rb')}" end diff --git a/activesupport/lib/active_support/core_ext/array.rb b/activesupport/lib/active_support/core_ext/array.rb index 268c9bed4c..79ba79192a 100644 --- a/activesupport/lib/active_support/core_ext/array.rb +++ b/activesupport/lib/active_support/core_ext/array.rb @@ -4,5 +4,4 @@ require 'active_support/core_ext/array/uniq_by' require 'active_support/core_ext/array/conversions' require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/array/grouping' -require 'active_support/core_ext/array/random_access' require 'active_support/core_ext/array/prepend_and_append' diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 3b22e8b4f9..f3d06ecb2f 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -39,10 +39,10 @@ class Array # # Blog.all.to_formatted_s # => "First PostSecond PostThird Post" # - # Adding in the <tt>:db</tt> argument as the format yields a prettier - # output: + # Adding in the <tt>:db</tt> argument as the format yields a comma separated + # id list: # - # Blog.all.to_formatted_s(:db) # => "First Post,Second Post,Third Post" + # Blog.all.to_formatted_s(:db) # => "1,2,3" def to_formatted_s(format = :default) case format when :db diff --git a/activesupport/lib/active_support/core_ext/array/grouping.rb b/activesupport/lib/active_support/core_ext/array/grouping.rb index 4cd9bfadac..2b3f639cb1 100644 --- a/activesupport/lib/active_support/core_ext/array/grouping.rb +++ b/activesupport/lib/active_support/core_ext/array/grouping.rb @@ -1,5 +1,3 @@ -require 'enumerator' - class Array # Splits or iterates over the array in groups of size +number+, # padding any remaining slots with +fill_with+ unless it is +false+. diff --git a/activesupport/lib/active_support/core_ext/array/random_access.rb b/activesupport/lib/active_support/core_ext/array/random_access.rb deleted file mode 100644 index bb1807a68a..0000000000 --- a/activesupport/lib/active_support/core_ext/array/random_access.rb +++ /dev/null @@ -1,30 +0,0 @@ -class Array - # Backport of Array#sample based on Marc-Andre Lafortune's https://github.com/marcandre/backports/ - # Returns a random element or +n+ random elements from the array. - # If the array is empty and +n+ is nil, returns <tt>nil</tt>. - # If +n+ is passed and its value is less than 0, it raises an +ArgumentError+ exception. - # If the value of +n+ is equal or greater than 0 it returns <tt>[]</tt>. - # - # [1,2,3,4,5,6].sample # => 4 - # [1,2,3,4,5,6].sample(3) # => [2, 4, 5] - # [1,2,3,4,5,6].sample(-3) # => ArgumentError: negative array size - # [].sample # => nil - # [].sample(3) # => [] - def sample(n=nil) - return self[Kernel.rand(size)] if n.nil? - n = n.to_int - rescue Exception => e - raise TypeError, "Coercion error: #{n.inspect}.to_int => Integer failed:\n(#{e.message})" - else - raise TypeError, "Coercion error: obj.to_int did NOT return an Integer (was #{n.class})" unless n.kind_of? Integer - raise ArgumentError, "negative array size" if n < 0 - n = size if n > size - result = Array.new(self) - n.times do |i| - r = i + Kernel.rand(size - i) - result[i], result[r] = result[r], result[i] - end - result[n..size] = [] - result - end unless method_defined? :sample -end 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 9c5f97b0e9..ac3dedc0e3 100644 --- a/activesupport/lib/active_support/core_ext/array/uniq_by.rb +++ b/activesupport/lib/active_support/core_ext/array/uniq_by.rb @@ -1,16 +1,22 @@ class Array - # Returns an unique array based on the criteria given as a +Proc+. + # *DEPRECATED*: Use +Array#uniq+ instead. + # + # Returns a unique array based on the criteria in the block. # # [1, 2, 3, 4].uniq_by { |i| i.odd? } # => [1, 2] # - def uniq_by - hash, array = {}, [] - each { |i| hash[yield(i)] ||= (array << i) } - array + def uniq_by(&block) + ActiveSupport::Deprecation.warn "uniq_by " \ + "is deprecated. Use Array#uniq instead", caller + uniq(&block) end - # Same as uniq_by, but modifies self. - def uniq_by! - replace(uniq_by{ |i| yield(i) }) + # *DEPRECATED*: Use +Array#uniq!+ instead. + # + # Same as +uniq_by+, but modifies +self+. + def uniq_by!(&block) + ActiveSupport::Deprecation.warn "uniq_by! " \ + "is deprecated. Use Array#uniq! instead", caller + uniq!(&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..af78226c21 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -7,24 +7,6 @@ require 'active_support/core_ext/time/zones' class Date DAYS_INTO_WEEK = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6 } - if RUBY_VERSION < '1.9' - undef :>> - - # Backported from 1.9. The one in 1.8 leads to incorrect next_month and - # friends for dates where the calendar reform is involved. It additionally - # prevents an infinite loop fixed in r27013. - def >>(n) - y, m = (year * 12 + (mon - 1) + n).divmod(12) - m, = (m + 1) .divmod(1) - d = mday - until jd2 = self.class.valid_civil?(y, m, d, start) - d -= 1 - raise ArgumentError, 'invalid date' unless d > 0 - end - self + (jd2 - jd) - end - end - class << self # Returns a new Date representing the date 1 day ago (i.e. yesterday's date). def yesterday @@ -154,51 +136,54 @@ class Date advance(:years => years) end - # Shorthand for years_ago(1) - def prev_year - years_ago(1) - end unless method_defined?(:prev_year) - - # Shorthand for years_since(1) - def next_year - years_since(1) - end unless method_defined?(:next_year) - - # Shorthand for months_ago(1) - def prev_month - months_ago(1) - end unless method_defined?(:prev_month) - - # Shorthand for months_since(1) - def next_month - months_since(1) - end unless method_defined?(:next_month) + # Returns number of days to start of this week. Week is assumed to start on + # +start_day+, default is +:monday+. + def days_to_week_start(start_day = :monday) + start_day_number = DAYS_INTO_WEEK[start_day] + current_day_number = wday != 0 ? wday - 1 : 6 + (current_day_number - start_day_number) % 7 + end - # Returns a new Date/DateTime representing the "start" of this week (i.e, Monday; DateTime objects will have time set to 0:00). - def beginning_of_week - days_to_monday = self.wday!=0 ? self.wday-1 : 6 - result = self - days_to_monday - self.acts_like?(:time) ? result.midnight : result + # Returns a new +Date+/+DateTime+ representing the start of this week. Week is + # assumed to start on +start_day+, default is +:monday+. +DateTime+ objects + # have their time set to 0:00. + def beginning_of_week(start_day = :monday) + days_to_start = days_to_week_start(start_day) + result = self - days_to_start + acts_like?(:time) ? result.midnight : result end - alias :monday :beginning_of_week alias :at_beginning_of_week :beginning_of_week - # Returns a new Date/DateTime representing the end of this week (Sunday, DateTime objects will have time set to 23:59:59). - def end_of_week - days_to_sunday = self.wday!=0 ? 7-self.wday : 0 - result = self + days_to_sunday.days + # Returns a new +Date+/+DateTime+ representing the start of this week. Week is + # assumed to start on a Monday. +DateTime+ objects have their time set to 0:00. + def monday + beginning_of_week + end + + # Returns a new +Date+/+DateTime+ representing the end of this week. Week is + # assumed to start on +start_day+, default is +:monday+. +DateTime+ objects + # have their time set to 23:59:59. + def end_of_week(start_day = :monday) + days_to_end = 6 - days_to_week_start(start_day) + result = self + days_to_end.days self.acts_like?(:time) ? result.end_of_day : result end - 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 end of this week. Week is + # assumed to start on a Monday. +DateTime+ objects have their time set to 23:59:59. + def sunday + end_of_week + end + + # Returns a new +Date+/+DateTime+ representing the given +day+ in the previous + # week. Default is +:monday+. +DateTime+ objects have their time set to 0:00. 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/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb index 338104fd05..3262c254f7 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -63,12 +63,6 @@ class Date alias_method :default_inspect, :inspect alias_method :inspect, :readable_inspect - # A method to keep Time, Date and DateTime instances interchangeable on conversions. - # In this case, it simply returns +self+. - def to_date - self - end if RUBY_VERSION < '1.9' - # Converts a Date instance to a Time, where the time is set to the beginning of the day. # The timezone can be either :local or :utc (default :local). # @@ -83,23 +77,6 @@ class Date ::Time.send("#{form}_time", year, month, day) end - # Converts a Date instance to a DateTime, where the time is set to the beginning of the day - # and UTC offset is set to 0. - # - # ==== Examples - # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007 - # - # date.to_datetime # => Sat, 10 Nov 2007 00:00:00 0000 - def to_datetime - ::DateTime.civil(year, month, day, 0, 0, 0, 0) - end if RUBY_VERSION < '1.9' - - def iso8601 - strftime('%F') - end if RUBY_VERSION < '1.9' - - alias_method :rfc3339, :iso8601 if RUBY_VERSION < '1.9' - def xmlschema to_time_in_current_zone.xmlschema end diff --git a/activesupport/lib/active_support/core_ext/date/freeze.rb b/activesupport/lib/active_support/core_ext/date/freeze.rb deleted file mode 100644 index a731f8345e..0000000000 --- a/activesupport/lib/active_support/core_ext/date/freeze.rb +++ /dev/null @@ -1,33 +0,0 @@ -# Date memoizes some instance methods using metaprogramming to wrap -# the methods with one that caches the result in an instance variable. -# -# If a Date is frozen but the memoized method hasn't been called, the -# first call will result in a frozen object error since the memo -# instance variable is uninitialized. -# -# Work around by eagerly memoizing before the first freeze. -# -# Ruby 1.9 uses a preinitialized instance variable so it's unaffected. -# This hack is as close as we can get to feature detection: -if RUBY_VERSION < '1.9' - require 'date' - begin - ::Date.today.freeze.jd - rescue => frozen_object_error - if frozen_object_error.message =~ /frozen/ - class Date #:nodoc: - def freeze - unless frozen? - self.class.private_instance_methods(false).each do |m| - if m.to_s =~ /\A__\d+__\Z/ - instance_variable_set(:"@#{m}", [send(m)]) - end - end - end - - super - end - end - end - end -end 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 48cf1a435d..1a3cf66a1b 100644 --- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb @@ -1,5 +1,3 @@ -require 'rational' unless RUBY_VERSION >= '1.9.2' - class DateTime class << self # DateTimes aren't aware of DST rules, so use a consistent non-DST offset when creating a DateTime with an offset in the local zone 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 ca899c714c..d7b3ad7d8d 100644 --- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb @@ -6,7 +6,7 @@ require 'active_support/values/time_zone' class DateTime # Ruby 1.9 has DateTime#to_time which internally relies on Time. We define our own #to_time which allows # DateTimes outside the range of what can be created with Time. - remove_method :to_time if instance_methods.include?(:to_time) + remove_method :to_time # Convert to a formatted string. See Time::DATE_FORMATS for predefined formats. # @@ -66,7 +66,7 @@ class DateTime # Attempts to convert self to a Ruby Time object; returns self if out of range of Ruby Time class. # If self has an offset other than 0, self will just be returned unaltered, since there's no clean way to map it to a Time. def to_time - self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec, sec_fraction * (RUBY_VERSION < '1.9' ? 86400000000 : 1000000)) : self + self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec, sec_fraction * 1000000) : self end # To be able to keep Times, Dates and DateTimes interchangeable on conversions. diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb index 9343bb7106..77a5087981 100644 --- a/activesupport/lib/active_support/core_ext/enumerable.rb +++ b/activesupport/lib/active_support/core_ext/enumerable.rb @@ -1,41 +1,4 @@ -require 'active_support/ordered_hash' - module Enumerable - # Ruby 1.8.7 introduces group_by, but the result isn't ordered. Override it. - remove_method(:group_by) if [].respond_to?(:group_by) && RUBY_VERSION < '1.9' - - # Collect an enumerable into sets, grouped by the result of a block. Useful, - # for example, for grouping records by date. - # - # Example: - # - # latest_transcripts.group_by(&:day).each do |day, transcripts| - # p "#{day} -> #{transcripts.map(&:class).join(', ')}" - # end - # "2006-03-01 -> Transcript" - # "2006-02-28 -> Transcript" - # "2006-02-27 -> Transcript, Transcript" - # "2006-02-26 -> Transcript, Transcript" - # "2006-02-25 -> Transcript" - # "2006-02-24 -> Transcript, Transcript" - # "2006-02-23 -> Transcript" - def group_by - return to_enum :group_by unless block_given? - assoc = ActiveSupport::OrderedHash.new - - each do |element| - key = yield(element) - - if assoc.has_key?(key) - assoc[key] << element - else - assoc[key] = [element] - end - end - - assoc - end unless [].respond_to?(:group_by) - # Calculates a sum from the elements. Examples: # # payments.sum { |p| p.price * p.tax_rate } @@ -63,27 +26,6 @@ module Enumerable end end - # Iterates over a collection, passing the current element *and* the - # +memo+ to the block. Handy for building up hashes or - # reducing collections down to one object. Examples: - # - # %w(foo bar).each_with_object({}) { |str, hsh| hsh[str] = str.upcase } - # # => {'foo' => 'FOO', 'bar' => 'BAR'} - # - # *Note* that you can't use immutable objects like numbers, true or false as - # the memo. You would think the following returns 120, but since the memo is - # never changed, it does not. - # - # (1..5).each_with_object(1) { |value, memo| memo *= value } # => 1 - # - def each_with_object(memo) - return to_enum :each_with_object, memo unless block_given? - each do |element| - yield element, memo - end - memo - end unless [].respond_to?(:each_with_object) - # Convert an enumerable to a hash. Examples: # # people.index_by(&:login) diff --git a/activesupport/lib/active_support/core_ext/exception.rb b/activesupport/lib/active_support/core_ext/exception.rb index ef801e713d..ba7757ea07 100644 --- a/activesupport/lib/active_support/core_ext/exception.rb +++ b/activesupport/lib/active_support/core_ext/exception.rb @@ -1,3 +1,3 @@ module ActiveSupport - FrozenObjectError = RUBY_VERSION < '1.9' ? TypeError : RuntimeError + FrozenObjectError = RuntimeError end diff --git a/activesupport/lib/active_support/core_ext/file.rb b/activesupport/lib/active_support/core_ext/file.rb index a763447566..dc24afbe7f 100644 --- a/activesupport/lib/active_support/core_ext/file.rb +++ b/activesupport/lib/active_support/core_ext/file.rb @@ -1,2 +1 @@ require 'active_support/core_ext/file/atomic' -require 'active_support/core_ext/file/path' diff --git a/activesupport/lib/active_support/core_ext/file/path.rb b/activesupport/lib/active_support/core_ext/file/path.rb deleted file mode 100644 index b5feab80ae..0000000000 --- a/activesupport/lib/active_support/core_ext/file/path.rb +++ /dev/null @@ -1,5 +0,0 @@ -class File - unless File.allocate.respond_to?(:to_path) - alias to_path path - end -end
\ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/float.rb b/activesupport/lib/active_support/core_ext/float.rb deleted file mode 100644 index 7570471b95..0000000000 --- a/activesupport/lib/active_support/core_ext/float.rb +++ /dev/null @@ -1 +0,0 @@ -require 'active_support/core_ext/float/rounding' diff --git a/activesupport/lib/active_support/core_ext/float/rounding.rb b/activesupport/lib/active_support/core_ext/float/rounding.rb deleted file mode 100644 index 0d4fb87665..0000000000 --- a/activesupport/lib/active_support/core_ext/float/rounding.rb +++ /dev/null @@ -1,19 +0,0 @@ -class Float - alias precisionless_round round - private :precisionless_round - - # Rounds the float with the specified precision. - # - # x = 1.337 - # x.round # => 1 - # x.round(1) # => 1.3 - # x.round(2) # => 1.34 - def round(precision = nil) - if precision - magnitude = 10.0 ** precision - (self * magnitude).round / magnitude - else - precisionless_round - end - end -end if RUBY_VERSION < '1.9' diff --git a/activesupport/lib/active_support/core_ext/io.rb b/activesupport/lib/active_support/core_ext/io.rb deleted file mode 100644 index 75f1055191..0000000000 --- a/activesupport/lib/active_support/core_ext/io.rb +++ /dev/null @@ -1,15 +0,0 @@ -if RUBY_VERSION < '1.9.2' - -# :stopdoc: -class IO - def self.binread(name, length = nil, offset = nil) - return File.read name unless length || offset - File.open(name, 'rb') { |f| - f.seek offset if offset - f.read length - } - end -end -# :startdoc: - -end diff --git a/activesupport/lib/active_support/core_ext/kernel/singleton_class.rb b/activesupport/lib/active_support/core_ext/kernel/singleton_class.rb index 33612155fb..9bbf1bbd73 100644 --- a/activesupport/lib/active_support/core_ext/kernel/singleton_class.rb +++ b/activesupport/lib/active_support/core_ext/kernel/singleton_class.rb @@ -1,11 +1,4 @@ module Kernel - # Returns the object's singleton class. - def singleton_class - class << self - self - end - end unless respond_to?(:singleton_class) # exists in 1.9.2 - # class_eval on an object acts like singleton_class.class_eval. def class_eval(*args, &block) singleton_class.class_eval(*args, &block) diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb index e63a0a9ed9..a51818d2b2 100644 --- a/activesupport/lib/active_support/core_ext/logger.rb +++ b/activesupport/lib/active_support/core_ext/logger.rb @@ -1,4 +1,7 @@ require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/deprecation' + +ActiveSupport::Deprecation.warn 'this file is deprecated and will be removed' # Adds the 'around_level' method to Logger. class Logger #:nodoc: diff --git a/activesupport/lib/active_support/core_ext/module.rb b/activesupport/lib/active_support/core_ext/module.rb index 9fed346b7c..f2d4887df6 100644 --- a/activesupport/lib/active_support/core_ext/module.rb +++ b/activesupport/lib/active_support/core_ext/module.rb @@ -5,7 +5,6 @@ require 'active_support/core_ext/module/reachable' require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/module/attr_internal' require 'active_support/core_ext/module/delegation' -require 'active_support/core_ext/module/synchronization' require 'active_support/core_ext/module/deprecation' require 'active_support/core_ext/module/remove_method' -require 'active_support/core_ext/module/method_names'
\ No newline at end of file +require 'active_support/core_ext/module/qualified_const' diff --git a/activesupport/lib/active_support/core_ext/module/anonymous.rb b/activesupport/lib/active_support/core_ext/module/anonymous.rb index 3982c9c586..0a9e791030 100644 --- a/activesupport/lib/active_support/core_ext/module/anonymous.rb +++ b/activesupport/lib/active_support/core_ext/module/anonymous.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/object/blank' - class Module # A module may or may not have a name. # @@ -7,7 +5,7 @@ class Module # M.name # => "M" # # m = Module.new - # m.name # => "" + # m.name # => nil # # A module gets a name when it is first assigned to a constant. Either # via the +module+ or +class+ keyword or by an explicit assignment: @@ -17,8 +15,6 @@ class Module # m.name # => "M" # def anonymous? - # Uses blank? because the name of an anonymous class is an empty - # string in 1.8, and nil in 1.9. - name.blank? + name.nil? end end diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb index c08ad251dd..1893a9cfa6 100644 --- a/activesupport/lib/active_support/core_ext/module/introspection.rb +++ b/activesupport/lib/active_support/core_ext/module/introspection.rb @@ -57,27 +57,8 @@ class Module parents end - if RUBY_VERSION < '1.9' - # Returns the constants that have been defined locally by this object and - # not in an ancestor. This method is exact if running under Ruby 1.9. In - # previous versions it may miss some constants if their definition in some - # ancestor is identical to their definition in the receiver. - def local_constants - inherited = {} - - ancestors.each do |anc| - next if anc == self - anc.constants.each { |const| inherited[const] = anc.const_get(const) } - end - - constants.select do |const| - !inherited.key?(const) || inherited[const].object_id != const_get(const).object_id - end - end - else - def local_constants #:nodoc: - constants(false) - end + def local_constants #:nodoc: + constants(false) end # Returns the names of the constants defined locally rather than the diff --git a/activesupport/lib/active_support/core_ext/module/method_names.rb b/activesupport/lib/active_support/core_ext/module/method_names.rb deleted file mode 100644 index 2eb40a83ab..0000000000 --- a/activesupport/lib/active_support/core_ext/module/method_names.rb +++ /dev/null @@ -1,14 +0,0 @@ -class Module - if instance_methods[0].is_a?(Symbol) - def instance_method_names(*args) - instance_methods(*args).map(&:to_s) - end - - def method_names(*args) - methods(*args).map(&:to_s) - end - else - alias_method :instance_method_names, :instance_methods - alias_method :method_names, :methods - end -end
\ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/module/qualified_const.rb b/activesupport/lib/active_support/core_ext/module/qualified_const.rb new file mode 100644 index 0000000000..8adf050b6b --- /dev/null +++ b/activesupport/lib/active_support/core_ext/module/qualified_const.rb @@ -0,0 +1,52 @@ +require 'active_support/core_ext/string/inflections' + +#-- +# Allows code reuse in the methods below without polluting Module. +#++ +module QualifiedConstUtils + def self.raise_if_absolute(path) + raise NameError, "wrong constant name #$&" if path =~ /\A::[^:]+/ + end + + def self.names(path) + path.split('::') + end +end + +## +# Extends the API for constants to be able to deal with qualified names. Arguments +# are assumed to be relative to the receiver. +# +#-- +# Qualified names are required to be relative because we are extending existing +# methods that expect constant names, ie, relative paths of length 1. For example, +# Object.const_get("::String") raises NameError and so does qualified_const_get. +#++ +class Module + def qualified_const_defined?(path, search_parents=true) + QualifiedConstUtils.raise_if_absolute(path) + + QualifiedConstUtils.names(path).inject(self) do |mod, name| + return unless mod.const_defined?(name, search_parents) + mod.const_get(name) + end + return true + end + + def qualified_const_get(path) + QualifiedConstUtils.raise_if_absolute(path) + + QualifiedConstUtils.names(path).inject(self) do |mod, name| + mod.const_get(name) + end + end + + def qualified_const_set(path, value) + QualifiedConstUtils.raise_if_absolute(path) + + const_name = path.demodulize + mod_name = path.deconstantize + mod = mod_name.empty? ? self : qualified_const_get(mod_name) + mod.const_set(const_name, value) + end +end diff --git a/activesupport/lib/active_support/core_ext/module/synchronization.rb b/activesupport/lib/active_support/core_ext/module/synchronization.rb deleted file mode 100644 index ed16c2f71b..0000000000 --- a/activesupport/lib/active_support/core_ext/module/synchronization.rb +++ /dev/null @@ -1,43 +0,0 @@ -require 'thread' -require 'active_support/core_ext/module/aliasing' -require 'active_support/core_ext/array/extract_options' - -class Module - # Synchronize access around a method, delegating synchronization to a - # particular mutex. A mutex (either a Mutex, or any object that responds to - # #synchronize and yields to a block) must be provided as a final :with option. - # The :with option should be a symbol or string, and can represent a method, - # constant, or instance or class variable. - # Example: - # class SharedCache - # @@lock = Mutex.new - # def expire - # ... - # end - # synchronize :expire, :with => :@@lock - # end - def synchronize(*methods) - options = methods.extract_options! - unless options.is_a?(Hash) && with = options[:with] - raise ArgumentError, "Synchronization needs a mutex. Supply an options hash with a :with key as the last argument (e.g. synchronize :hello, :with => :@mutex)." - end - - methods.each do |method| - aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1 - - if method_defined?("#{aliased_method}_without_synchronization#{punctuation}") - raise ArgumentError, "#{method} is already synchronized. Double synchronization is not currently supported." - end - - module_eval(<<-EOS, __FILE__, __LINE__ + 1) - def #{aliased_method}_with_synchronization#{punctuation}(*args, &block) # def expire_with_synchronization(*args, &block) - #{with}.synchronize do # @@lock.synchronize do - #{aliased_method}_without_synchronization#{punctuation}(*args, &block) # expire_without_synchronization(*args, &block) - end # end - end # end - EOS - - alias_method_chain method, :synchronization - end - end -end diff --git a/activesupport/lib/active_support/core_ext/object/blank.rb b/activesupport/lib/active_support/core_ext/object/blank.rb index fe27f45295..7271671908 100644 --- a/activesupport/lib/active_support/core_ext/object/blank.rb +++ b/activesupport/lib/active_support/core_ext/object/blank.rb @@ -89,9 +89,6 @@ class Hash end class String - # 0x3000: fullwidth whitespace - NON_WHITESPACE_REGEXP = %r![^\s#{[0x3000].pack("U")}]! - # A string is blank if it's empty or contains whitespaces only: # # "".blank? # => true @@ -100,12 +97,7 @@ class String # " something here ".blank? # => false # def blank? - # 1.8 does not takes [:space:] properly - if encoding_aware? - self !~ /[^[:space:]]/ - else - self !~ NON_WHITESPACE_REGEXP - end + self !~ /[^[:space:]]/ end end diff --git a/activesupport/lib/active_support/core_ext/object/inclusion.rb b/activesupport/lib/active_support/core_ext/object/inclusion.rb index b5671f66d0..f611cdd606 100644 --- a/activesupport/lib/active_support/core_ext/object/inclusion.rb +++ b/activesupport/lib/active_support/core_ext/object/inclusion.rb @@ -1,15 +1,25 @@ class Object - # Returns true if this object is included in the argument. Argument must be - # any object which responds to +#include?+. Usage: + # 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: # # characters = ["Konata", "Kagami", "Tsukasa"] # "Konata".in?(characters) # => true + # + # character = "Konata" + # character.in?("Konata", "Kagami", "Tsukasa") # => true # - # This will throw an ArgumentError if the argument doesn't respond + # This will throw an ArgumentError if a single argument is passed in and it doesn't respond # to +#include?+. - def in?(another_object) - another_object.include?(self) - rescue NoMethodError - raise ArgumentError.new("The parameter passed to #in? must 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 end end diff --git a/activesupport/lib/active_support/core_ext/object/instance_variables.rb b/activesupport/lib/active_support/core_ext/object/instance_variables.rb index eda9694614..66caf9bec8 100644 --- a/activesupport/lib/active_support/core_ext/object/instance_variables.rb +++ b/activesupport/lib/active_support/core_ext/object/instance_variables.rb @@ -23,11 +23,7 @@ class Object # end # # C.new(0, 1).instance_variable_names # => ["@y", "@x"] - if RUBY_VERSION >= '1.9' - def instance_variable_names - instance_variables.map { |var| var.to_s } - end - else - alias_method :instance_variable_names, :instance_variables + def instance_variable_names + instance_variables.map { |var| var.to_s } end end diff --git a/activesupport/lib/active_support/core_ext/object/to_json.rb b/activesupport/lib/active_support/core_ext/object/to_json.rb index 14ef27340e..e7dc60a612 100644 --- a/activesupport/lib/active_support/core_ext/object/to_json.rb +++ b/activesupport/lib/active_support/core_ext/object/to_json.rb @@ -10,10 +10,10 @@ end # several cases (for instance, the JSON implementation for Hash does not work) with inheritance # and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json. [Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass| - klass.class_eval <<-RUBY, __FILE__, __LINE__ + klass.class_eval do # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info. def to_json(options = nil) ActiveSupport::JSON.encode(self, options) end - RUBY + end end diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb index 4797c93e63..e77a9da0ec 100644 --- a/activesupport/lib/active_support/core_ext/object/try.rb +++ b/activesupport/lib/active_support/core_ext/object/try.rb @@ -28,8 +28,6 @@ class Object def try(*a, &b) if a.empty? && block_given? yield self - elsif !a.empty? && !respond_to?(a.first) - nil else __send__(*a, &b) end diff --git a/activesupport/lib/active_support/core_ext/process.rb b/activesupport/lib/active_support/core_ext/process.rb deleted file mode 100644 index 0b0bc6dc69..0000000000 --- a/activesupport/lib/active_support/core_ext/process.rb +++ /dev/null @@ -1 +0,0 @@ -require 'active_support/core_ext/process/daemon' diff --git a/activesupport/lib/active_support/core_ext/process/daemon.rb b/activesupport/lib/active_support/core_ext/process/daemon.rb deleted file mode 100644 index f5202ddee4..0000000000 --- a/activesupport/lib/active_support/core_ext/process/daemon.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Process - def self.daemon(nochdir = nil, noclose = nil) - exit if fork # Parent exits, child continues. - Process.setsid # Become session leader. - exit if fork # Zap session leader. See [1]. - - unless nochdir - Dir.chdir "/" # Release old working directory. - end - - File.umask 0000 # Ensure sensible umask. Adjust as needed. - - unless noclose - STDIN.reopen "/dev/null" # Free file descriptors and - STDOUT.reopen "/dev/null", "a" # point them somewhere sensible. - STDERR.reopen '/dev/null', 'a' - end - - trap("TERM") { exit } - - return 0 - end unless respond_to?(:daemon) -end diff --git a/activesupport/lib/active_support/core_ext/range.rb b/activesupport/lib/active_support/core_ext/range.rb index 2428a02242..c0736f3a44 100644 --- a/activesupport/lib/active_support/core_ext/range.rb +++ b/activesupport/lib/active_support/core_ext/range.rb @@ -2,4 +2,3 @@ require 'active_support/core_ext/range/blockless_step' require 'active_support/core_ext/range/conversions' require 'active_support/core_ext/range/include_range' require 'active_support/core_ext/range/overlaps' -require 'active_support/core_ext/range/cover' diff --git a/activesupport/lib/active_support/core_ext/range/blockless_step.rb b/activesupport/lib/active_support/core_ext/range/blockless_step.rb index db42ef5c47..f687287f0d 100644 --- a/activesupport/lib/active_support/core_ext/range/blockless_step.rb +++ b/activesupport/lib/active_support/core_ext/range/blockless_step.rb @@ -1,27 +1,11 @@ require 'active_support/core_ext/module/aliasing' class Range - begin - (1..2).step - # Range#step doesn't return an Enumerator - rescue LocalJumpError - # Return an array when step is called without a block. - def step_with_blockless(*args, &block) - if block_given? - step_without_blockless(*args, &block) - else - array = [] - step_without_blockless(*args) { |step| array << step } - array - end - end - else - def step_with_blockless(*args, &block) #:nodoc: - if block_given? - step_without_blockless(*args, &block) - else - step_without_blockless(*args).to_a - end + def step_with_blockless(*args, &block) #:nodoc: + if block_given? + step_without_blockless(*args, &block) + else + step_without_blockless(*args).to_a end end diff --git a/activesupport/lib/active_support/core_ext/range/cover.rb b/activesupport/lib/active_support/core_ext/range/cover.rb deleted file mode 100644 index 3a182cddd2..0000000000 --- a/activesupport/lib/active_support/core_ext/range/cover.rb +++ /dev/null @@ -1,3 +0,0 @@ -class Range - alias_method(:cover?, :include?) unless instance_methods.include?(:cover?) -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 0246627467..684b7cbc4a 100644 --- a/activesupport/lib/active_support/core_ext/range/include_range.rb +++ b/activesupport/lib/active_support/core_ext/range/include_range.rb @@ -9,9 +9,9 @@ class Range # (5..9).include?(11) # => false def include_with_range?(value) if value.is_a?(::Range) - operator = exclude_end? ? :< : :<= - end_value = value.exclude_end? ? last.succ : last - include_without_range?(value.first) && (value.last <=> end_value).send(operator, 0) + # 1...10 includes 1..9 but it does not include 1..10. + operator = exclude_end? && !value.exclude_end? ? :< : :<= + include_without_range?(value.first) && value.last.send(operator, last) else include_without_range?(value) end diff --git a/activesupport/lib/active_support/core_ext/rexml.rb b/activesupport/lib/active_support/core_ext/rexml.rb deleted file mode 100644 index 0419ebc84b..0000000000 --- a/activesupport/lib/active_support/core_ext/rexml.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'active_support/core_ext/kernel/reporting' - -# Fixes the rexml vulnerability disclosed at: -# http://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/ -# This fix is identical to rexml-expansion-fix version 1.0.1. -# -# We still need to distribute this fix because albeit the REXML -# in recent 1.8.7s is patched, it wasn't in early patchlevels. -require 'rexml/rexml' - -# Earlier versions of rexml defined REXML::Version, newer ones REXML::VERSION -unless (defined?(REXML::VERSION) ? REXML::VERSION : REXML::Version) > "3.1.7.2" - silence_warnings { require 'rexml/document' } - - # REXML in 1.8.7 has the patch but early patchlevels didn't update Version from 3.1.7.2. - unless REXML::Document.respond_to?(:entity_expansion_limit=) - silence_warnings { require 'rexml/entity' } - - module REXML #:nodoc: - class Entity < Child #:nodoc: - undef_method :unnormalized - def unnormalized - document.record_entity_expansion! if document - v = value() - return nil if v.nil? - @unnormalized = Text::unnormalize(v, parent) - @unnormalized - end - end - class Document < Element #:nodoc: - @@entity_expansion_limit = 10_000 - def self.entity_expansion_limit= val - @@entity_expansion_limit = val - end - - def record_entity_expansion! - @number_of_expansions ||= 0 - @number_of_expansions += 1 - if @number_of_expansions > @@entity_expansion_limit - raise "Number of entity expansions exceeded, processing aborted." - end - end - end - end - end -end diff --git a/activesupport/lib/active_support/core_ext/string/access.rb b/activesupport/lib/active_support/core_ext/string/access.rb index c0d5cdf2d5..9b5266c58c 100644 --- a/activesupport/lib/active_support/core_ext/string/access.rb +++ b/activesupport/lib/active_support/core_ext/string/access.rb @@ -1,99 +1,35 @@ require "active_support/multibyte" class String - unless '1.9'.respond_to?(:force_encoding) - # Returns the character at the +position+ treating the string as an array (where 0 is the first character). - # - # Examples: - # "hello".at(0) # => "h" - # "hello".at(4) # => "o" - # "hello".at(10) # => ERROR if < 1.9, nil in 1.9 - def at(position) - mb_chars[position, 1].to_s - end - - # Returns the remaining of the string from the +position+ treating the string as an array (where 0 is the first character). - # - # Examples: - # "hello".from(0) # => "hello" - # "hello".from(2) # => "llo" - # "hello".from(10) # => "" if < 1.9, nil in 1.9 - def from(position) - mb_chars[position..-1].to_s - end - - # Returns the beginning of the string up to the +position+ treating the string as an array (where 0 is the first character). - # - # Examples: - # "hello".to(0) # => "h" - # "hello".to(2) # => "hel" - # "hello".to(10) # => "hello" - def to(position) - mb_chars[0..position].to_s - end - - # Returns the first character of the string or the first +limit+ characters. - # - # Examples: - # "hello".first # => "h" - # "hello".first(2) # => "he" - # "hello".first(10) # => "hello" - def first(limit = 1) - if limit == 0 - '' - elsif limit >= size - self - else - mb_chars[0...limit].to_s - end - end - - # Returns the last character of the string or the last +limit+ characters. - # - # Examples: - # "hello".last # => "o" - # "hello".last(2) # => "lo" - # "hello".last(10) # => "hello" - def last(limit = 1) - if limit == 0 - '' - elsif limit >= size - self - else - mb_chars[(-limit)..-1].to_s - end - end - else - def at(position) - self[position] - end + def at(position) + self[position] + end - def from(position) - self[position..-1] - end + def from(position) + self[position..-1] + end - def to(position) - self[0..position] - end + def to(position) + self[0..position] + end - def first(limit = 1) - if limit == 0 - '' - elsif limit >= size - self - else - to(limit - 1) - end + def first(limit = 1) + if limit == 0 + '' + elsif limit >= size + self + else + to(limit - 1) end + end - def last(limit = 1) - if limit == 0 - '' - elsif limit >= size - self - else - from(-limit) - end + def last(limit = 1) + if limit == 0 + '' + elsif limit >= size + self + else + from(-limit) end end end diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb index 0f8933b658..541f969faa 100644 --- a/activesupport/lib/active_support/core_ext/string/conversions.rb +++ b/activesupport/lib/active_support/core_ext/string/conversions.rb @@ -1,37 +1,7 @@ -# encoding: utf-8 require 'date' -require 'active_support/core_ext/time/publicize_conversion_methods' require 'active_support/core_ext/time/calculations' class String - # Returns the codepoint of the first character of the string, assuming a - # single-byte character encoding: - # - # "a".ord # => 97 - # "à".ord # => 224, in ISO-8859-1 - # - # This method is defined in Ruby 1.8 for Ruby 1.9 forward compatibility on - # these character encodings. - # - # <tt>ActiveSupport::Multibyte::Chars#ord</tt> is forward compatible with - # Ruby 1.9 on UTF8 strings: - # - # "a".mb_chars.ord # => 97 - # "à".mb_chars.ord # => 224, in UTF8 - # - # Note that the 224 is different in both examples. In ISO-8859-1 "à" is - # represented as a single byte, 224. In UTF8 it is represented with two - # bytes, namely 195 and 160, but its Unicode codepoint is 224. If we - # call +ord+ on the UTF8 string "à" the return value will be 195. That is - # not an error, because UTF8 is unsupported, the call itself would be - # bogus. - def ord - self[0] - end unless method_defined?(:ord) - - # +getbyte+ backport from Ruby 1.9 - alias_method :getbyte, :[] unless method_defined?(:getbyte) - # Form can be either :utc (default) or :local. def to_time(form = :utc) return nil if self.blank? diff --git a/activesupport/lib/active_support/core_ext/string/encoding.rb b/activesupport/lib/active_support/core_ext/string/encoding.rb index d4781bfe0c..dc635ed6a5 100644 --- a/activesupport/lib/active_support/core_ext/string/encoding.rb +++ b/activesupport/lib/active_support/core_ext/string/encoding.rb @@ -1,11 +1,8 @@ +require 'active_support/deprecation' + class String - if defined?(Encoding) && "".respond_to?(:encode) - def encoding_aware? - true - end - else - def encoding_aware? - false - end + def encoding_aware? + ActiveSupport::Deprecation.warn 'String#encoding_aware? is deprecated', caller + true end -end
\ No newline at end of file +end diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb index c7ceeb9de4..1e57b586d9 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -9,14 +9,25 @@ require 'active_support/inflector/transliterate' class String # Returns the plural form of the word in the string. # + # If the optional parameter +count+ is specified, + # the singular form will be returned if <tt>count == 1</tt>. + # For any other value of +count+ the plural will be returned. + # + # ==== Examples # "post".pluralize # => "posts" # "octopus".pluralize # => "octopi" # "sheep".pluralize # => "sheep" # "words".pluralize # => "words" # "the blue mailman".pluralize # => "the blue mailmen" # "CamelOctopus".pluralize # => "CamelOctopi" - def pluralize - ActiveSupport::Inflector.pluralize(self) + # "apple".pluralize(1) # => "apple" + # "apple".pluralize(2) # => "apples" + def pluralize(count = nil) + if count == 1 + self + else + ActiveSupport::Inflector.pluralize(self) + end end # The reverse of +pluralize+, returns the singular form of a word in a string. @@ -42,7 +53,7 @@ class String def constantize ActiveSupport::Inflector.constantize(self) end - + # +safe_constantize+ tries to find a declared constant with the name specified # in the string. It returns nil when the name is not in CamelCase # or is not initialized. See ActiveSupport::Inflector.safe_constantize @@ -106,10 +117,25 @@ class String # # "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections" # "Inflections".demodulize # => "Inflections" + # + # See also +deconstantize+. def demodulize ActiveSupport::Inflector.demodulize(self) end + # Removes the rightmost segment from the constant expression in the string. + # + # "Net::HTTP".deconstantize # => "Net" + # "::Net::HTTP".deconstantize # => "::Net" + # "String".deconstantize # => "" + # "::String".deconstantize # => "" + # "".deconstantize # => "" + # + # See also +demodulize+. + def deconstantize + ActiveSupport::Inflector.deconstantize(self) + end + # Replaces special characters in a string so that it may be used as part of a 'pretty' URL. # # ==== Examples diff --git a/activesupport/lib/active_support/core_ext/string/multibyte.rb b/activesupport/lib/active_support/core_ext/string/multibyte.rb index aae1cfccf2..4e7824ad74 100644 --- a/activesupport/lib/active_support/core_ext/string/multibyte.rb +++ b/activesupport/lib/active_support/core_ext/string/multibyte.rb @@ -2,71 +2,55 @@ require 'active_support/multibyte' class String - if RUBY_VERSION >= "1.9" - # == Multibyte proxy - # - # +mb_chars+ is a multibyte safe proxy for string methods. - # - # In Ruby 1.8 and older it creates and returns an instance of the ActiveSupport::Multibyte::Chars class which - # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy - # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string. - # - # name = 'Claus Müller' - # name.reverse # => "rell??M sualC" - # name.length # => 13 - # - # name.mb_chars.reverse.to_s # => "rellüM sualC" - # name.mb_chars.length # => 12 - # - # In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that - # it becomes easy to run one version of your code on multiple Ruby versions. - # - # == Method chaining - # - # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows - # method chaining on the result of any of these methods. - # - # name.mb_chars.reverse.length # => 12 - # - # == Interoperability and configuration - # - # The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between - # String and Char work like expected. The bang! methods change the internal string representation in the Chars - # object. Interoperability problems can be resolved easily with a +to_s+ call. - # - # For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For - # information about how to change the default Multibyte behavior see ActiveSupport::Multibyte. - def mb_chars - if ActiveSupport::Multibyte.proxy_class.consumes?(self) - ActiveSupport::Multibyte.proxy_class.new(self) - else - self - end - end - - def is_utf8? #:nodoc - case encoding - when Encoding::UTF_8 - valid_encoding? - when Encoding::ASCII_8BIT, Encoding::US_ASCII - dup.force_encoding(Encoding::UTF_8).valid_encoding? - else - false - end - end - else - def mb_chars - if ActiveSupport::Multibyte.proxy_class.wants?(self) - ActiveSupport::Multibyte.proxy_class.new(self) - else - self - end + # == Multibyte proxy + # + # +mb_chars+ is a multibyte safe proxy for string methods. + # + # In Ruby 1.8 and older it creates and returns an instance of the ActiveSupport::Multibyte::Chars class which + # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy + # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string. + # + # name = 'Claus Müller' + # name.reverse # => "rell??M sualC" + # name.length # => 13 + # + # name.mb_chars.reverse.to_s # => "rellüM sualC" + # name.mb_chars.length # => 12 + # + # In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that + # it becomes easy to run one version of your code on multiple Ruby versions. + # + # == Method chaining + # + # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows + # method chaining on the result of any of these methods. + # + # name.mb_chars.reverse.length # => 12 + # + # == Interoperability and configuration + # + # The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between + # String and Char work like expected. The bang! methods change the internal string representation in the Chars + # object. Interoperability problems can be resolved easily with a +to_s+ call. + # + # For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For + # information about how to change the default Multibyte behavior see ActiveSupport::Multibyte. + def mb_chars + if ActiveSupport::Multibyte.proxy_class.consumes?(self) + ActiveSupport::Multibyte.proxy_class.new(self) + else + self end + end - # Returns true if the string has UTF-8 semantics (a String used for purely byte resources is unlikely to have - # them), returns false otherwise. - def is_utf8? - ActiveSupport::Multibyte::Chars.consumes?(self) + def is_utf8? + case encoding + when Encoding::UTF_8 + valid_encoding? + when Encoding::ASCII_8BIT, Encoding::US_ASCII + dup.force_encoding(Encoding::UTF_8).valid_encoding? + else + false end end 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 5d7f74bb65..6cb2ea68b3 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -20,7 +20,7 @@ class ERB if s.html_safe? s else - s.gsub(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/</, "<").html_safe + s.encode(s.encoding, :xml => :attr)[1...-1].html_safe end end diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 372dd69212..f3235d11bb 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -161,27 +161,46 @@ class Time months_since(1) end - # Returns a new Time representing the "start" of this week (Monday, 0:00) - def beginning_of_week - days_to_monday = wday!=0 ? wday-1 : 6 - (self - days_to_monday.days).midnight + # Returns number of days to start of this week, week starts on start_day (default is :monday). + def days_to_week_start(start_day = :monday) + start_day_number = DAYS_INTO_WEEK[start_day] + current_day_number = wday != 0 ? wday - 1 : 6 + days_span = current_day_number - start_day_number + days_span >= 0 ? days_span : 7 + days_span + end + + # Returns a new Time representing the "start" of this week, week starts on start_day (default is :monday, i.e. Monday, 0:00). + def beginning_of_week(start_day = :monday) + days_to_start = days_to_week_start(start_day) + (self - days_to_start.days).midnight end - alias :monday :beginning_of_week alias :at_beginning_of_week :beginning_of_week - # Returns a new Time representing the end of this week, (end of Sunday) - def end_of_week - days_to_sunday = wday!=0 ? 7-wday : 0 - (self + days_to_sunday.days).end_of_day + # Returns a new +Date+/+DateTime+ representing the start of this week. Week is + # assumed to start on a Monday. +DateTime+ objects have their time set to 0:00. + def monday + beginning_of_week + end + + # Returns a new Time representing the end of this week, week starts on start_day (default is :monday, i.e. end of Sunday). + def end_of_week(start_day = :monday) + days_to_end = 6 - days_to_week_start(start_day) + (self + days_to_end.days).end_of_day 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 +Date+/+DateTime+ representing the end of this week. Week is + # assumed to start on a Monday. +DateTime+ objects have their time set to 23:59:59. + def sunday + end_of_week + end + + # 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 @@ -312,4 +331,14 @@ class Time end alias_method :compare_without_coercion, :<=> alias_method :<=>, :compare_with_coercion + + # Layers additional behavior on Time#eql? so that ActiveSupport::TimeWithZone instances + # can be eql? to an equivalent Time + def eql_with_coercion(other) + # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do eql? comparison + other = other.comparable_time if other.respond_to?(:comparable_time) + eql_without_coercion(other) + end + alias_method :eql_without_coercion, :eql? + alias_method :eql?, :eql_with_coercion end diff --git a/activesupport/lib/active_support/core_ext/time/conversions.rb b/activesupport/lib/active_support/core_ext/time/conversions.rb index 49ac18d245..5a17fc1afa 100644 --- a/activesupport/lib/active_support/core_ext/time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/time/conversions.rb @@ -1,5 +1,4 @@ require 'active_support/inflector/methods' -require 'active_support/core_ext/time/publicize_conversion_methods' require 'active_support/values/time_zone' class Time diff --git a/activesupport/lib/active_support/core_ext/time/marshal.rb b/activesupport/lib/active_support/core_ext/time/marshal.rb index 457d3f5b62..1bf622d6a6 100644 --- a/activesupport/lib/active_support/core_ext/time/marshal.rb +++ b/activesupport/lib/active_support/core_ext/time/marshal.rb @@ -1,30 +1,3 @@ -# Pre-1.9 versions of Ruby have a bug with marshaling Time instances, where utc instances are -# unmarshalled in the local zone, instead of utc. We're layering behavior on the _dump and _load -# methods so that utc instances can be flagged on dump, and coerced back to utc on load. -if !Marshal.load(Marshal.dump(Time.now.utc)).utc? - class Time - class << self - alias_method :_load_without_utc_flag, :_load - def _load(marshaled_time) - time = _load_without_utc_flag(marshaled_time) - time.instance_eval do - if defined?(@marshal_with_utc_coercion) - val = remove_instance_variable("@marshal_with_utc_coercion") - end - val ? utc : self - end - end - end - - alias_method :_dump_without_utc_flag, :_dump - def _dump(*args) - obj = dup - obj.instance_variable_set('@marshal_with_utc_coercion', utc?) - obj._dump_without_utc_flag(*args) - end - end -end - # Ruby 1.9.2 adds utc_offset and zone to Time, but marshaling only # preserves utc_offset. Preserve zone also, even though it may not # work in some edge cases. diff --git a/activesupport/lib/active_support/core_ext/time/publicize_conversion_methods.rb b/activesupport/lib/active_support/core_ext/time/publicize_conversion_methods.rb deleted file mode 100644 index e1878d3c20..0000000000 --- a/activesupport/lib/active_support/core_ext/time/publicize_conversion_methods.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'date' - -class Time - # Ruby 1.8-cvs and early 1.9 series define private Time#to_date - %w(to_date to_datetime).each do |method| - if private_instance_methods.include?(method) || private_instance_methods.include?(method.to_sym) - public method - end - end -end diff --git a/activesupport/lib/active_support/core_ext/uri.rb b/activesupport/lib/active_support/core_ext/uri.rb index ee991e3439..0b219ce44a 100644 --- a/activesupport/lib/active_support/core_ext/uri.rb +++ b/activesupport/lib/active_support/core_ext/uri.rb @@ -1,22 +1,18 @@ # encoding: utf-8 -if RUBY_VERSION >= '1.9' - require 'uri' +require 'uri' +str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese. +parser = URI::Parser.new - str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese. - - parser = URI::Parser.new - - unless str == parser.unescape(parser.escape(str)) - URI::Parser.class_eval do - remove_method :unescape - def unescape(str, escaped = /%[a-fA-F\d]{2}/) - # TODO: Are we actually sure that ASCII == UTF-8? - # YK: My initial experiments say yes, but let's be sure please - enc = str.encoding - enc = Encoding::UTF_8 if enc == Encoding::US_ASCII - str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc) - end +unless str == parser.unescape(parser.escape(str)) + URI::Parser.class_eval do + remove_method :unescape + def unescape(str, escaped = /%[a-fA-F\d]{2}/) + # TODO: Are we actually sure that ASCII == UTF-8? + # YK: My initial experiments say yes, but let's be sure please + enc = str.encoding + enc = Encoding::UTF_8 if enc == Encoding::US_ASCII + str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc) end end end diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index c072a8e646..e121e452a3 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -5,6 +5,7 @@ require 'active_support/core_ext/module/aliasing' require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/module/introspection' require 'active_support/core_ext/module/anonymous' +require 'active_support/core_ext/module/qualified_const' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/load_error' require 'active_support/core_ext/name_error' @@ -70,14 +71,24 @@ module ActiveSupport #:nodoc: # # This is handled by walking back up the watch stack and adding the constants # found by child.rb to the list of original constants in parent.rb - class WatchStack < Hash + class WatchStack + include Enumerable + # @watching is a stack of lists of constants being watched. For instance, # if parent.rb is autoloaded, the stack will look like [[Object]]. If parent.rb # then requires namespace/child.rb, the stack will look like [[Object], [Namespace]]. def initialize @watching = [] - super { |h,k| h[k] = [] } + @stack = Hash.new { |h,k| h[k] = [] } + end + + def each(&block) + @stack.each(&block) + end + + def watching? + !@watching.empty? end # return a list of new constants found since the last call to watch_namespaces @@ -88,7 +99,7 @@ module ActiveSupport #:nodoc: @watching.last.each do |namespace| # Retrieve the constants that were present under the namespace when watch_namespaces # was originally called - original_constants = self[namespace].last + original_constants = @stack[namespace].last mod = Inflector.constantize(namespace) if Dependencies.qualified_const_defined?(namespace) next unless mod.is_a?(Module) @@ -101,7 +112,7 @@ module ActiveSupport #:nodoc: # element of self[Object] will be an Array of the constants that were present # before parent.rb was required. The second element will be an Array of the # constants that were present before child.rb was required. - self[namespace].each do |namespace_constants| + @stack[namespace].each do |namespace_constants| namespace_constants.concat(new_constants) end @@ -125,13 +136,14 @@ module ActiveSupport #:nodoc: Inflector.constantize(module_name).local_constant_names : [] watching << module_name - self[module_name] << original_constants + @stack[module_name] << original_constants end @watching << watching end + private def pop_modules(modules) - modules.each { |mod| self[mod].pop } + modules.each { |mod| @stack[mod].pop } end end @@ -218,8 +230,8 @@ module ActiveSupport #:nodoc: end def load_dependency(file) - if Dependencies.load? - Dependencies.new_constants_in(Object) { yield }.presence + if Dependencies.load? && ActiveSupport::Dependencies.constant_watch_stack.watching? + Dependencies.new_constants_in(Object) { yield } else yield end @@ -228,13 +240,13 @@ module ActiveSupport #:nodoc: raise end - def load(file, *) + def load(file, wrap = false) result = false load_dependency(file) { result = super } result end - def require(file, *) + def require(file) result = false load_dependency(file) { result = super } result @@ -358,24 +370,11 @@ module ActiveSupport #:nodoc: # Is the provided constant path defined? def qualified_const_defined?(path) - names = path.sub(/^::/, '').to_s.split('::') - - names.inject(Object) do |mod, name| - return false unless local_const_defined?(mod, name) - mod.const_get name - end + Object.qualified_const_defined?(path.sub(/^::/, ''), false) end - if Module.method(:const_defined?).arity == 1 - # Does this module define this constant? - # Wrapper to accommodate changing Module#const_defined? in Ruby 1.9 - def local_const_defined?(mod, const) - mod.const_defined?(const) - end - else - def local_const_defined?(mod, const) #:nodoc: - mod.const_defined?(const, false) - end + def local_const_defined?(mod, const) #:nodoc: + mod.const_defined?(const, false) end # Given +path+, a filesystem path to a ruby file, return an array of constant @@ -426,7 +425,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. @@ -525,7 +524,7 @@ module ActiveSupport #:nodoc: class ClassCache def initialize - @store = Hash.new { |h, k| h[k] = Inflector.constantize(k) } + @store = Hash.new end def empty? @@ -536,23 +535,24 @@ module ActiveSupport #:nodoc: @store.key?(key) end - def []=(key, value) - return unless key.respond_to?(:name) - - raise(ArgumentError, 'anonymous classes cannot be cached') if key.name.blank? - - @store[key.name] = value + def get(key) + key = key.name if key.respond_to?(:name) + @store[key] ||= Inflector.constantize(key) end + alias :[] :get - def [](key) + def safe_get(key) key = key.name if key.respond_to?(:name) - - @store[key] + @store[key] || begin + klass = Inflector.safe_constantize(key) + @store[key] = klass + end end - alias :get :[] - def store(name) - self[name] = name + def store(klass) + return self unless klass.respond_to?(:name) + raise(ArgumentError, 'anonymous classes cannot be cached') if klass.name.empty? + @store[klass.name] = klass self end @@ -569,10 +569,17 @@ module ActiveSupport #:nodoc: end # Get the reference for class named +name+. + # Raises an exception if referenced class does not exist. def constantize(name) Reference.get(name) end + # Get the reference for class named +name+ if one exists. + # Otherwise returns nil. + def safe_constantize(name) + Reference.safe_get(name) + end + # Determine if the given constant has been automatically loaded. def autoloaded?(desc) # No name => anonymous module. diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index 89b0923882..00c67a470d 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -10,7 +10,6 @@ module ActiveSupport # 1.month.ago # equivalent to Time.now.advance(:months => -1) class Duration < BasicObject attr_accessor :value, :parts - delegate :duplicable?, :to => :value # required when using ActiveSupport's BasicObject on 1.8 def initialize(value, parts) #:nodoc: @value, @parts = value, parts diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb index f76ddff038..a4ad2da137 100644 --- a/activesupport/lib/active_support/file_update_checker.rb +++ b/activesupport/lib/active_support/file_update_checker.rb @@ -1,8 +1,28 @@ +require "active_support/core_ext/array/wrap" +require "active_support/core_ext/array/extract_options" + module ActiveSupport - # This class is responsible to track files and invoke the given block - # whenever one of these files are changed. For example, this class - # is used by Rails to reload the I18n framework whenever they are - # changed upon a new request. + # \FileUpdateChecker specifies the API used by Rails to watch files + # and control reloading. The API depends on four methods: + # + # * +initialize+ which expects two parameters and one block as + # described below; + # + # * +updated?+ which returns a boolean if there were updates in + # the filesystem or not; + # + # * +execute+ which executes the given block on initialization + # and updates the counter to the latest timestamp; + # + # * +execute_if_updated+ which just executes the block if it was updated; + # + # After initialization, a call to +execute_if_updated+ must execute + # the block only if there was really a change in the filesystem. + # + # == Examples + # + # This class is used by Rails to reload the I18n framework whenever + # they are changed upon a new request. # # i18n_reloader = ActiveSupport::FileUpdateChecker.new(paths) do # I18n.reload! @@ -13,24 +33,89 @@ module ActiveSupport # end # class FileUpdateChecker - attr_reader :paths, :last_update_at - - def initialize(paths, calculate=false, &block) - @paths = paths + # It accepts two parameters on initialization. The first is an array + # of files and the second is an optional hash of directories. The hash must + # have directories as keys and the value is an array of extensions to be + # watched under that directory. + # + # This method must also receive a block that will be called once a path changes. + # + # == Implementation details + # + # This particular implementation checks for added and updated files, + # but not removed files. Directories lookup are compiled to a glob for + # performance. Therefore, while someone can add new files to the +files+ + # array after initialization (and parts of Rails do depend on this feature), + # adding new directories after initialization is not allowed. + # + # Notice that other objects that implements FileUpdateChecker API may + # not even allow new files to be added after initialization. If this + # is the case, we recommend freezing the +files+ after initialization to + # avoid changes that won't make effect. + def initialize(files, dirs={}, &block) + @files = files + @glob = compile_glob(dirs) @block = block - @last_update_at = calculate ? updated_at : nil + @updated_at = nil + @last_update_at = updated_at + end + + # Check if any of the entries were updated. If so, the updated_at + # value is cached until the block is executed via +execute+ or +execute_if_updated+ + def updated? + current_updated_at = updated_at + if @last_update_at < current_updated_at + @updated_at = updated_at + true + else + false + end end - def updated_at - paths.map { |path| File.mtime(path) }.max + # Executes the given block and updates the counter to latest timestamp. + def execute + @last_update_at = updated_at + @block.call + ensure + @updated_at = nil end + # Execute the block given if updated. def execute_if_updated - current_update_at = self.updated_at - if @last_update_at != current_update_at - @last_update_at = current_update_at - @block.call + if updated? + execute + true + else + false + end + end + + private + + def updated_at #:nodoc: + @updated_at || begin + all = [] + all.concat @files.select { |f| File.exists?(f) } + all.concat Dir[@glob] if @glob + all.map { |path| File.mtime(path) }.max || Time.at(0) end end + + def compile_glob(hash) #:nodoc: + hash.freeze # Freeze so changes aren't accidently pushed + return if hash.empty? + + globs = [] + hash.each do |key, value| + globs << "#{key}/**/*#{compile_ext(value)}" + end + "{#{globs.join(",")}}" + end + + def compile_ext(array) #:nodoc: + array = Array.wrap(array) + return if array.empty? + ".{#{array.join(",")}}" + end end end diff --git a/activesupport/lib/active_support/gzip.rb b/activesupport/lib/active_support/gzip.rb index 9651f02c73..f7036315d6 100644 --- a/activesupport/lib/active_support/gzip.rb +++ b/activesupport/lib/active_support/gzip.rb @@ -8,7 +8,7 @@ module ActiveSupport class Stream < StringIO def initialize(*) super - set_encoding "BINARY" if "".encoding_aware? + set_encoding "BINARY" end def close; rewind; end end diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index 636f019cd5..674e4acfd6 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -16,6 +16,10 @@ module ActiveSupport dup end + def nested_under_indifferent_access + self + end + def initialize(constructor = {}) if constructor.is_a?(Hash) super() diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb index 4c59fe9ac9..bbeb8d82c6 100644 --- a/activesupport/lib/active_support/i18n_railtie.rb +++ b/activesupport/lib/active_support/i18n_railtie.rb @@ -10,14 +10,19 @@ module I18n config.i18n.fallbacks = ActiveSupport::OrderedOptions.new def self.reloader - @reloader ||= ActiveSupport::FileUpdateChecker.new([]){ I18n.reload! } + @reloader ||= ActiveSupport::FileUpdateChecker.new(reloader_paths){ I18n.reload! } + end + + def self.reloader_paths + @reloader_paths ||= [] end # Add <tt>I18n::Railtie.reloader</tt> to ActionDispatch callbacks. Since, at this # point, no path was added to the reloader, I18n.reload! is not triggered # on to_prepare callbacks. This will only happen on the config.after_initialize # callback below. - initializer "i18n.callbacks" do + initializer "i18n.callbacks" do |app| + app.reloaders << I18n::Railtie.reloader ActionDispatch::Reloader.to_prepare do I18n::Railtie.reloader.execute_if_updated end @@ -58,8 +63,8 @@ module I18n init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks) - reloader.paths.concat I18n.load_path - reloader.execute_if_updated + reloader_paths.concat I18n.load_path + reloader.execute @i18n_inited = true end diff --git a/activesupport/lib/active_support/inflections.rb b/activesupport/lib/active_support/inflections.rb index daf2a1e1d9..527cce2594 100644 --- a/activesupport/lib/active_support/inflections.rb +++ b/activesupport/lib/active_support/inflections.rb @@ -16,8 +16,8 @@ module ActiveSupport inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies') inflect.plural(/(x|ch|ss|sh)$/i, '\1es') inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices') - inflect.plural(/([m|l])ouse$/i, '\1ice') - inflect.plural(/([m|l])ice$/i, '\1ice') + inflect.plural(/(m|l)ouse$/i, '\1ice') + inflect.plural(/(m|l)ice$/i, '\1ice') inflect.plural(/^(ox)$/i, '\1en') inflect.plural(/^(oxen)$/i, '\1') inflect.plural(/(quiz)$/i, '\1zes') @@ -35,7 +35,7 @@ module ActiveSupport inflect.singular(/(s)eries$/i, '\1eries') inflect.singular(/(m)ovies$/i, '\1ovie') inflect.singular(/(x|ch|ss|sh)es$/i, '\1') - inflect.singular(/([m|l])ice$/i, '\1ouse') + inflect.singular(/(m|l)ice$/i, '\1ouse') inflect.singular(/(bus)es$/i, '\1') inflect.singular(/(o)es$/i, '\1') inflect.singular(/(shoe)s$/i, '\1') diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index 934529d496..7f325aee94 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+ @@ -160,13 +146,32 @@ module ActiveSupport underscored_word.gsub(/_/, '-') end - # Removes the module part from the expression in the string. + # Removes the module part from the expression in the string: # - # Examples: # "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections" # "Inflections".demodulize # => "Inflections" - def demodulize(class_name_in_module) - class_name_in_module.to_s.gsub(/^.*::/, '') + # + # See also +deconstantize+. + def demodulize(path) + path = path.to_s + if i = path.rindex('::') + path[(i+2)..-1] + else + path + end + end + + # Removes the rightmost segment from the constant expression in the string: + # + # "Net::HTTP".deconstantize # => "Net" + # "::Net::HTTP".deconstantize # => "::Net" + # "String".deconstantize # => "" + # "::String".deconstantize # => "" + # "".deconstantize # => "" + # + # See also +demodulize+. + def deconstantize(path) + path.to_s[0...(path.rindex('::') || 0)] # implementation based on the one in facets' Module#spacename end # Creates a foreign key name from a class name. @@ -181,47 +186,32 @@ module ActiveSupport underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id") end - # Ruby 1.9 introduces an inherit argument for Module#const_get and - # #const_defined? and changes their default behavior. - if Module.method(:const_get).arity == 1 - # Tries to find a constant with the name specified in the argument string: - # - # "Module".constantize # => Module - # "Test::Unit".constantize # => Test::Unit - # - # The name is assumed to be the one of a top-level constant, no matter whether - # it starts with "::" or not. No lexical context is taken into account: - # - # C = 'outside' - # module M - # C = 'inside' - # C # => 'inside' - # "C".constantize # => 'outside', same as ::C - # end - # - # NameError is raised when the name is not in CamelCase or the constant is - # unknown. - def constantize(camel_cased_word) - names = camel_cased_word.split('::') - names.shift if names.empty? || names.first.empty? - - constant = Object - names.each do |name| - constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name) - end - constant - end - else - def constantize(camel_cased_word) #:nodoc: - names = camel_cased_word.split('::') - names.shift if names.empty? || names.first.empty? + # Tries to find a constant with the name specified in the argument string: + # + # "Module".constantize # => Module + # "Test::Unit".constantize # => Test::Unit + # + # The name is assumed to be the one of a top-level constant, no matter whether + # it starts with "::" or not. No lexical context is taken into account: + # + # C = 'outside' + # module M + # C = 'inside' + # C # => 'inside' + # "C".constantize # => 'outside', same as ::C + # end + # + # NameError is raised when the name is not in CamelCase or the constant is + # unknown. + def constantize(camel_cased_word) #:nodoc: + names = camel_cased_word.split('::') + names.shift if names.empty? || names.first.empty? - constant = Object - names.each do |name| - constant = constant.const_defined?(name, false) ? constant.const_get(name) : constant.const_missing(name) - end - constant + constant = Object + names.each do |name| + constant = constant.const_defined?(name, false) ? constant.const_get(name) : constant.const_missing(name) end + constant end # Tries to find a constant with the name specified in the argument string: @@ -292,5 +282,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/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 469ae69258..d7181035d3 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -50,9 +50,9 @@ module ActiveSupport end # like encode, but only calls as_json, without encoding to string - def as_json(value) + def as_json(value, use_options = true) check_for_circular_references(value) do - value.as_json(options_for(value)) + use_options ? value.as_json(options_for(value)) : value.as_json end end @@ -119,9 +119,7 @@ module ActiveSupport end def escape(string) - if string.respond_to?(:force_encoding) - string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY) - end + string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY) json = string. gsub(escape_regex) { |s| ESCAPED_CHARS[s] }. gsub(/([\xC0-\xDF][\x80-\xBF]| @@ -130,7 +128,7 @@ module ActiveSupport s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/n, '\\\\u\&') } json = %("#{json}") - json.force_encoding(::Encoding::UTF_8) if json.respond_to?(:force_encoding) + json.force_encoding(::Encoding::UTF_8) json end end @@ -212,7 +210,7 @@ class Array def as_json(options = nil) #:nodoc: # use encoder as a proxy to call as_json on all elements, to protect from circular references encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options) - map { |v| encoder.as_json(v) } + map { |v| encoder.as_json(v, options) } end def encode_json(encoder) #:nodoc: @@ -239,7 +237,7 @@ class Hash # use encoder as a proxy to call as_json on all values in the subset, to protect from circular references encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options) result = self.is_a?(ActiveSupport::OrderedHash) ? ActiveSupport::OrderedHash : Hash - result[subset.map { |k, v| [k.to_s, encoder.as_json(v)] }] + result[subset.map { |k, v| [k.to_s, encoder.as_json(v, options)] }] end def encode_json(encoder) diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb index dcfcf0b63c..7b7fc81e6c 100644 --- a/activesupport/lib/active_support/log_subscriber/test_helper.rb +++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb @@ -50,7 +50,7 @@ module ActiveSupport end class MockLogger - include ActiveSupport::BufferedLogger::Severity + include ActiveSupport::Logger::Severity attr_reader :flush_count attr_accessor :level @@ -73,7 +73,7 @@ module ActiveSupport @flush_count += 1 end - ActiveSupport::BufferedLogger::Severity.constants.each do |severity| + ActiveSupport::Logger::Severity.constants.each do |severity| class_eval <<-EOT, __FILE__, __LINE__ + 1 def #{severity.downcase}? #{severity} >= @level diff --git a/activesupport/lib/active_support/logger.rb b/activesupport/lib/active_support/logger.rb new file mode 100644 index 0000000000..66e8fcadb4 --- /dev/null +++ b/activesupport/lib/active_support/logger.rb @@ -0,0 +1,18 @@ +require 'logger' + +module ActiveSupport + class Logger < ::Logger + def initialize(*args) + super + @formatter = SimpleFormatter.new + end + + # Simple formatter which only displays the message. + class SimpleFormatter < ::Logger::Formatter + # This method is invoked when a log event occurs + def call(severity, timestamp, progname, msg) + "#{String === msg ? msg : msg.inspect}\n" + end + end + end +end diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb deleted file mode 100644 index 4c67676ad5..0000000000 --- a/activesupport/lib/active_support/memoizable.rb +++ /dev/null @@ -1,116 +0,0 @@ -require 'active_support/core_ext/kernel/singleton_class' -require 'active_support/core_ext/module/aliasing' -require 'active_support/deprecation' - -module ActiveSupport - module Memoizable - def self.extended(base) - ActiveSupport::Deprecation.warn "ActiveSupport::Memoizable is deprecated and will be removed in future releases," \ - "simply use Ruby memoization pattern instead.", caller - super - end - - def self.memoized_ivar_for(symbol) - "@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}".to_sym - end - - module InstanceMethods - def self.included(base) - base.class_eval do - unless base.method_defined?(:freeze_without_memoizable) - alias_method_chain :freeze, :memoizable - end - end - end - - def freeze_with_memoizable - memoize_all unless frozen? - freeze_without_memoizable - end - - def memoize_all - prime_cache ".*" - end - - def unmemoize_all - flush_cache ".*" - end - - def prime_cache(*syms) - syms.each do |sym| - methods.each do |m| - if m.to_s =~ /^_unmemoized_(#{sym})/ - if method(m).arity == 0 - __send__($1) - else - ivar = ActiveSupport::Memoizable.memoized_ivar_for($1) - instance_variable_set(ivar, {}) - end - end - end - end - end - - def flush_cache(*syms) - syms.each do |sym| - (methods + private_methods + protected_methods).each do |m| - if m.to_s =~ /^_unmemoized_(#{sym.to_s.gsub(/\?\Z/, '\?')})/ - ivar = ActiveSupport::Memoizable.memoized_ivar_for($1) - instance_variable_get(ivar).clear if instance_variable_defined?(ivar) - end - end - end - end - end - - def memoize(*symbols) - symbols.each do |symbol| - original_method = :"_unmemoized_#{symbol}" - memoized_ivar = ActiveSupport::Memoizable.memoized_ivar_for(symbol) - - class_eval <<-EOS, __FILE__, __LINE__ + 1 - include InstanceMethods # include InstanceMethods - # - if method_defined?(:#{original_method}) # if method_defined?(:_unmemoized_mime_type) - raise "Already memoized #{symbol}" # raise "Already memoized mime_type" - end # end - alias #{original_method} #{symbol} # alias _unmemoized_mime_type mime_type - # - if instance_method(:#{symbol}).arity == 0 # if instance_method(:mime_type).arity == 0 - def #{symbol}(reload = false) # def mime_type(reload = false) - if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty? # if reload || !defined?(@_memoized_mime_type) || @_memoized_mime_type.empty? - #{memoized_ivar} = [#{original_method}] # @_memoized_mime_type = [_unmemoized_mime_type] - end # end - #{memoized_ivar}[0] # @_memoized_mime_type[0] - end # end - else # else - def #{symbol}(*args) # def mime_type(*args) - #{memoized_ivar} ||= {} unless frozen? # @_memoized_mime_type ||= {} unless frozen? - args_length = method(:#{original_method}).arity # args_length = method(:_unmemoized_mime_type).arity - if args.length == args_length + 1 && # if args.length == args_length + 1 && - (args.last == true || args.last == :reload) # (args.last == true || args.last == :reload) - reload = args.pop # reload = args.pop - end # end - # - if defined?(#{memoized_ivar}) && #{memoized_ivar} # if defined?(@_memoized_mime_type) && @_memoized_mime_type - if !reload && #{memoized_ivar}.has_key?(args) # if !reload && @_memoized_mime_type.has_key?(args) - #{memoized_ivar}[args] # @_memoized_mime_type[args] - elsif #{memoized_ivar} # elsif @_memoized_mime_type - #{memoized_ivar}[args] = #{original_method}(*args) # @_memoized_mime_type[args] = _unmemoized_mime_type(*args) - end # end - else # else - #{original_method}(*args) # _unmemoized_mime_type(*args) - end # end - end # end - end # end - # - if private_method_defined?(#{original_method.inspect}) # if private_method_defined?(:_unmemoized_mime_type) - private #{symbol.inspect} # private :mime_type - elsif protected_method_defined?(#{original_method.inspect}) # elsif protected_method_defined?(:_unmemoized_mime_type) - protected #{symbol.inspect} # protected :mime_type - end # end - EOS - end - end - end -end diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index e14386a85d..476ba0b3d1 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -10,21 +10,41 @@ 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 def initialize(secret, options = {}) - unless options.is_a?(Hash) - 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) + # 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 @@ -36,10 +56,10 @@ module ActiveSupport encrypted_data = cipher.update(@serializer.dump(value)) encrypted_data << cipher.final - [encrypted_data, iv].map {|v| ActiveSupport::Base64.encode64s(v)}.join("--") + [encrypted_data, iv].map {|v| ActiveSupport::Base64.strict_encode64(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 +75,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/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index 9d7c81142a..7cb5b1e82d 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -18,7 +18,7 @@ module ActiveSupport # self.current_user = User.find(id) # end # - # By default it uses Marshal to serialize the message. If you want to use another + # 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.: # @@ -27,11 +27,6 @@ module ActiveSupport class InvalidSignature < StandardError; end def initialize(secret, options = {}) - unless options.is_a?(Hash) - ActiveSupport::Deprecation.warn "The second parameter should be an options hash. Use :digest => 'algorithm' to specify the digest algorithm." - options = { :digest => options } - end - @secret = secret @digest = options[:digest] || 'SHA1' @serializer = options[:serializer] || Marshal @@ -49,7 +44,7 @@ module ActiveSupport end def generate(value) - data = ActiveSupport::Base64.encode64s(@serializer.dump(value)) + data = ActiveSupport::Base64.strict_encode64(@serializer.dump(value)) "#{data}--#{generate_digest(data)}" end diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index b78d92f599..dcc176e93f 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -38,16 +38,10 @@ module ActiveSupport #:nodoc: alias to_s wrapped_string alias to_str wrapped_string - if RUBY_VERSION >= "1.9" - # Creates a new Chars instance by wrapping _string_. - def initialize(string) - @wrapped_string = string - @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen? - end - else - def initialize(string) #:nodoc: - @wrapped_string = string - end + # Creates a new Chars instance by wrapping _string_. + def initialize(string) + @wrapped_string = string + @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen? end # Forward all undefined methods to the wrapped string. @@ -94,151 +88,8 @@ module ActiveSupport #:nodoc: @wrapped_string <=> other.to_s end - if RUBY_VERSION < "1.9" - # Returns +true+ if the Chars class can and should act as a proxy for the string _string_. Returns - # +false+ otherwise. - def self.wants?(string) - $KCODE == 'UTF8' && consumes?(string) - end - - # Returns a new Chars object containing the _other_ object concatenated to the string. - # - # Example: - # ('Café'.mb_chars + ' périferôl').to_s # => "Café périferôl" - def +(other) - chars(@wrapped_string + other) - end - - # Like <tt>String#=~</tt> only it returns the character offset (in codepoints) instead of the byte offset. - # - # Example: - # 'Café périferôl'.mb_chars =~ /ô/ # => 12 - def =~(other) - translate_offset(@wrapped_string =~ other) - end - - # Inserts the passed string at specified codepoint offsets. - # - # Example: - # 'Café'.mb_chars.insert(4, ' périferôl').to_s # => "Café périferôl" - def insert(offset, fragment) - unpacked = Unicode.u_unpack(@wrapped_string) - unless offset > unpacked.length - @wrapped_string.replace( - Unicode.u_unpack(@wrapped_string).insert(offset, *Unicode.u_unpack(fragment)).pack('U*') - ) - else - raise IndexError, "index #{offset} out of string" - end - self - end - - # Returns +true+ if contained string contains _other_. Returns +false+ otherwise. - # - # Example: - # 'Café'.mb_chars.include?('é') # => true - def include?(other) - # We have to redefine this method because Enumerable defines it. - @wrapped_string.include?(other) - end - - # Returns the position _needle_ in the string, counting in codepoints. Returns +nil+ if _needle_ isn't found. - # - # Example: - # 'Café périferôl'.mb_chars.index('ô') # => 12 - # 'Café périferôl'.mb_chars.index(/\w/u) # => 0 - def index(needle, offset=0) - wrapped_offset = first(offset).wrapped_string.length - index = @wrapped_string.index(needle, wrapped_offset) - index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil - end - - # Returns the position _needle_ in the string, counting in - # codepoints, searching backward from _offset_ or the end of the - # string. Returns +nil+ if _needle_ isn't found. - # - # Example: - # 'Café périferôl'.mb_chars.rindex('é') # => 6 - # 'Café périferôl'.mb_chars.rindex(/\w/u) # => 13 - def rindex(needle, offset=nil) - offset ||= length - wrapped_offset = first(offset).wrapped_string.length - index = @wrapped_string.rindex(needle, wrapped_offset) - index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil - end - - # Returns the number of codepoints in the string - def size - Unicode.u_unpack(@wrapped_string).size - end - alias_method :length, :size - - # Strips entire range of Unicode whitespace from the right of the string. - def rstrip - chars(@wrapped_string.gsub(Unicode::TRAILERS_PAT, '')) - end - - # Strips entire range of Unicode whitespace from the left of the string. - def lstrip - chars(@wrapped_string.gsub(Unicode::LEADERS_PAT, '')) - end - - # Strips entire range of Unicode whitespace from the right and left of the string. - def strip - rstrip.lstrip - end - - # Returns the codepoint of the first character in the string. - # - # Example: - # 'こんにちは'.mb_chars.ord # => 12371 - def ord - Unicode.u_unpack(@wrapped_string)[0] - end - - # Works just like <tt>String#rjust</tt>, only integer specifies characters instead of bytes. - # - # Example: - # - # "¾ cup".mb_chars.rjust(8).to_s - # # => " ¾ cup" - # - # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace - # # => " ¾ cup" - def rjust(integer, padstr=' ') - justify(integer, :right, padstr) - end - - # Works just like <tt>String#ljust</tt>, only integer specifies characters instead of bytes. - # - # Example: - # - # "¾ cup".mb_chars.rjust(8).to_s - # # => "¾ cup " - # - # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace - # # => "¾ cup " - def ljust(integer, padstr=' ') - justify(integer, :left, padstr) - end - - # Works just like <tt>String#center</tt>, only integer specifies characters instead of bytes. - # - # Example: - # - # "¾ cup".mb_chars.center(8).to_s - # # => " ¾ cup " - # - # "¾ cup".mb_chars.center(8, " ").to_s # Use non-breaking whitespace - # # => " ¾ cup " - def center(integer, padstr=' ') - justify(integer, :center, padstr) - end - - else - def =~(other) - @wrapped_string =~ other - end + def =~(other) + @wrapped_string =~ other end # Works just like <tt>String#split</tt>, with the exception that the items in the resulting list are Chars @@ -431,9 +282,7 @@ module ActiveSupport #:nodoc: return nil if byte_offset.nil? return 0 if @wrapped_string == '' - if @wrapped_string.respond_to?(:force_encoding) - @wrapped_string = @wrapped_string.dup.force_encoding(Encoding::ASCII_8BIT) - end + @wrapped_string = @wrapped_string.dup.force_encoding(Encoding::ASCII_8BIT) begin @wrapped_string[0...byte_offset].unpack('U*').length diff --git a/activesupport/lib/active_support/multibyte/utils.rb b/activesupport/lib/active_support/multibyte/utils.rb index 94b393cee2..bd6d4bad41 100644 --- a/activesupport/lib/active_support/multibyte/utils.rb +++ b/activesupport/lib/active_support/multibyte/utils.rb @@ -2,36 +2,14 @@ module ActiveSupport #:nodoc: module Multibyte #:nodoc: - if Kernel.const_defined?(:Encoding) - # Returns a regular expression that matches valid characters in the current encoding - def self.valid_character - VALID_CHARACTER[Encoding.default_external.to_s] - end - else - def self.valid_character - case $KCODE - when 'UTF8' - VALID_CHARACTER['UTF-8'] - when 'SJIS' - VALID_CHARACTER['Shift_JIS'] - end - end + # Returns a regular expression that matches valid characters in the current encoding + def self.valid_character + VALID_CHARACTER[Encoding.default_external.to_s] end - if 'string'.respond_to?(:valid_encoding?) - # Verifies the encoding of a string - def self.verify(string) - string.valid_encoding? - end - else - def self.verify(string) - if expression = valid_character - # Splits the string on character boundaries, which are determined based on $KCODE. - string.split(//).all? { |c| expression =~ c } - else - true - end - end + # Verifies the encoding of a string + def self.verify(string) + string.valid_encoding? end # Verifies the encoding of the string and raises an exception when it's not valid @@ -39,22 +17,11 @@ module ActiveSupport #:nodoc: raise EncodingError.new("Found characters with invalid encoding") unless verify(string) end - if 'string'.respond_to?(:force_encoding) - # Removes all invalid characters from the string. - # - # Note: this method is a no-op in Ruby 1.9 - def self.clean(string) - string - end - else - def self.clean(string) - if expression = valid_character - # Splits the string on character boundaries, which are determined based on $KCODE. - string.split(//).grep(expression).join - else - string - end - end + # Removes all invalid characters from the string. + # + # Note: this method is a no-op in Ruby 1.9 + def self.clean(string) + string 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..d4f309fbd7 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 @@ -51,172 +51,5 @@ module ActiveSupport def extractable_options? true end - - # Hash is ordered in Ruby 1.9! - if RUBY_VERSION < '1.9' - - # In MRI the Hash class is core and written in C. In particular, methods are - # programmed with explicit C function calls and polymorphism is not honored. - # - # For example, []= is crucial in this implementation to maintain the @keys - # array but hash.c invokes rb_hash_aset() originally. This prevents method - # reuse through inheritance and forces us to reimplement stuff. - # - # For instance, we cannot use the inherited #merge! because albeit the algorithm - # itself would work, our []= is not being called at all by the C code. - - def initialize(*args, &block) - super - @keys = [] - end - - def self.[](*args) - ordered_hash = new - - if (args.length == 1 && args.first.is_a?(Array)) - args.first.each do |key_value_pair| - next unless (key_value_pair.is_a?(Array)) - ordered_hash[key_value_pair[0]] = key_value_pair[1] - end - - return ordered_hash - end - - unless (args.size % 2 == 0) - raise ArgumentError.new("odd number of arguments for Hash") - end - - args.each_with_index do |val, ind| - next if (ind % 2 != 0) - ordered_hash[val] = args[ind + 1] - end - - ordered_hash - end - - def initialize_copy(other) - super - # make a deep copy of keys - @keys = other.keys - end - - def []=(key, value) - @keys << key unless has_key?(key) - super - end - - def delete(key) - if has_key? key - index = @keys.index(key) - @keys.delete_at index - end - super - end - - def delete_if - super - sync_keys! - self - end - - def reject! - super - sync_keys! - self - end - - def reject(&block) - dup.reject!(&block) - end - - def keys - @keys.dup - end - - def values - @keys.collect { |key| self[key] } - end - - def to_hash - self - end - - def to_a - @keys.map { |key| [ key, self[key] ] } - end - - def each_key - return to_enum(:each_key) unless block_given? - @keys.each { |key| yield key } - self - end - - def each_value - return to_enum(:each_value) unless block_given? - @keys.each { |key| yield self[key]} - self - end - - def each - return to_enum(:each) unless block_given? - @keys.each {|key| yield [key, self[key]]} - self - end - - def each_pair - return to_enum(:each_pair) unless block_given? - @keys.each {|key| yield key, self[key]} - self - end - - alias_method :select, :find_all - - def clear - super - @keys.clear - self - end - - def shift - k = @keys.first - v = delete(k) - [k, v] - end - - def merge!(other_hash) - if block_given? - other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v } - else - other_hash.each { |k, v| self[k] = v } - end - self - end - - alias_method :update, :merge! - - def merge(other_hash, &block) - dup.merge!(other_hash, &block) - end - - # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not. - def replace(other) - super - @keys = other.keys - self - end - - def invert - OrderedHash[self.to_a.map!{|key_value_pair| key_value_pair.reverse}] - end - - def inspect - "#<OrderedHash #{super}>" - end - - private - def sync_keys! - @keys.delete_if {|k| !has_key?(k)} - end - end end end diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb index 1638512af0..f696716cc8 100644 --- a/activesupport/lib/active_support/railtie.rb +++ b/activesupport/lib/active_support/railtie.rb @@ -5,12 +5,6 @@ module ActiveSupport class Railtie < Rails::Railtie config.active_support = ActiveSupport::OrderedOptions.new - # Loads support for "whiny nil" (noisy warnings when methods are invoked - # on +nil+ values) if Configuration#whiny_nils is true. - initializer "active_support.initialize_whiny_nils" do |app| - require 'active_support/whiny_nil' if app.config.whiny_nils - end - initializer "active_support.deprecation_behavior" do |app| if deprecation = app.config.active_support.deprecation ActiveSupport::Deprecation.behavior = deprecation diff --git a/activesupport/lib/active_support/ruby/shim.rb b/activesupport/lib/active_support/ruby/shim.rb index 608b3fe4b9..41fd866481 100644 --- a/activesupport/lib/active_support/ruby/shim.rb +++ b/activesupport/lib/active_support/ruby/shim.rb @@ -3,20 +3,14 @@ # # Date next_year, next_month # DateTime to_date, to_datetime, xmlschema -# Enumerable group_by, each_with_object, none? -# Process Process.daemon -# REXML security fix +# Enumerable group_by, none? # String ord # Time to_date, to_time, to_datetime require 'active_support' require 'active_support/core_ext/date/calculations' require 'active_support/core_ext/date_time/conversions' require 'active_support/core_ext/enumerable' -require 'active_support/core_ext/process/daemon' require 'active_support/core_ext/string/conversions' require 'active_support/core_ext/string/interpolation' require 'active_support/core_ext/string/encoding' -require 'active_support/core_ext/rexml' require 'active_support/core_ext/time/conversions' -require 'active_support/core_ext/file/path' -require 'active_support/core_ext/module/method_names'
\ No newline at end of file diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb new file mode 100644 index 0000000000..8eae43188d --- /dev/null +++ b/activesupport/lib/active_support/tagged_logging.rb @@ -0,0 +1,63 @@ +require 'active_support/core_ext/object/blank' +require 'logger' + +module ActiveSupport + # Wraps any standard Logger class to provide tagging capabilities. Examples: + # + # Logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) + # Logger.tagged("BCX") { Logger.info "Stuff" } # Logs "[BCX] Stuff" + # Logger.tagged("BCX", "Jason") { Logger.info "Stuff" } # Logs "[BCX] [Jason] Stuff" + # Logger.tagged("BCX") { Logger.tagged("Jason") { Logger.info "Stuff" } } # Logs "[BCX] [Jason] Stuff" + # + # This is used by the default Rails.logger as configured by Railties to make it easy to stamp log lines + # with subdomains, request ids, and anything else to aid debugging of multi-user production applications. + class TaggedLogging + def initialize(logger) + @logger = logger + @tags = Hash.new { |h,k| h[k] = [] } + end + + def tagged(*new_tags) + tags = current_tags + new_tags = Array.wrap(new_tags).flatten.reject(&:blank?) + tags.concat new_tags + yield + ensure + new_tags.size.times { tags.pop } + end + + def add(severity, message = nil, progname = nil, &block) + @logger.add(severity, "#{tags_text}#{message}", progname, &block) + end + + %w( fatal error warn info debug unknown ).each do |severity| + eval <<-EOM, nil, __FILE__, __LINE__ + 1 + def #{severity}(progname = nil, &block) # def warn(progname = nil, &block) + add(Logger::#{severity.upcase}, progname, &block) # add(Logger::WARN, progname, &block) + end # end + EOM + end + + def flush + @tags.delete(Thread.current) + @logger.flush if @logger.respond_to?(:flush) + end + + def method_missing(method, *args) + @logger.send(method, *args) + end + + protected + + def tags_text + tags = current_tags + if tags.any? + tags.collect { |tag| "[#{tag}]" }.join(" ") + " " + end + end + + def current_tags + @tags[Thread.current] + end + end +end diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb index f3629ada5b..4e1a58a801 100644 --- a/activesupport/lib/active_support/testing/assertions.rb +++ b/activesupport/lib/active_support/testing/assertions.rb @@ -87,7 +87,7 @@ module ActiveSupport # Test if an expression is not blank. Passes if object.present? is true. # - # assert_present {:data => 'x' } # => true + # assert_present({:data => 'x' }) # => true def assert_present(object, message=nil) message ||= "#{object.inspect} is blank" assert object.present?, message diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index dd23f8d82d..209bfac19f 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -13,12 +13,7 @@ module ActiveSupport included do superclass_delegating_accessor :profile_options self.profile_options = {} - - if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions - include ForMiniTest - else - include ForClassicTestUnit - end + include ForMiniTest end # each implementation should define metrics and freeze the defaults @@ -77,48 +72,6 @@ module ActiveSupport end end - module ForClassicTestUnit - def run(result) - return if method_name =~ /^default_test$/ - - yield(self.class::STARTED, name) - @_result = result - - run_warmup - if full_profile_options && metrics = full_profile_options[:metrics] - metrics.each do |metric_name| - if klass = Metrics[metric_name.to_sym] - run_profile(klass.new) - result.add_run - else - puts '%20s: unsupported' % metric_name - end - end - end - - yield(self.class::FINISHED, name) - end - - def run_test(metric, mode) - run_callbacks :setup - setup - metric.send(mode) { __send__ @method_name } - rescue ::Test::Unit::AssertionFailedError => e - add_failure(e.message, e.backtrace) - rescue StandardError, ScriptError => e - add_error(e) - ensure - begin - teardown - run_callbacks :teardown, :enumerator => :reverse_each - rescue ::Test::Unit::AssertionFailedError => e - add_failure(e.message, e.backtrace) - rescue StandardError, ScriptError => e - add_error(e) - end - end - end - protected # overridden by each implementation def run_gc; end @@ -208,8 +161,7 @@ module ActiveSupport end end - ruby = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' - ruby += "-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}" + ruby = "#{RUBY_ENGINE}-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}" @env = [app, rails, ruby, RUBY_PLATFORM] * ',' end @@ -306,7 +258,6 @@ module ActiveSupport end end -RUBY_ENGINE = 'ruby' unless defined?(RUBY_ENGINE) # mri 1.8 case RUBY_ENGINE when 'ruby' then require 'active_support/testing/performance/ruby' when 'rbx' then require 'active_support/testing/performance/rubinius' diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index 7d6d047ef6..26731c6bd7 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -144,8 +144,6 @@ end if RUBY_VERSION.between?('1.9.2', '2.0') require 'active_support/testing/performance/ruby/yarv' -elsif RUBY_VERSION.between?('1.8.6', '1.9') - require 'active_support/testing/performance/ruby/mri' else $stderr.puts 'Update your ruby interpreter to be able to run benchmarks.' exit diff --git a/activesupport/lib/active_support/testing/performance/ruby/mri.rb b/activesupport/lib/active_support/testing/performance/ruby/mri.rb deleted file mode 100644 index 142279dd6e..0000000000 --- a/activesupport/lib/active_support/testing/performance/ruby/mri.rb +++ /dev/null @@ -1,57 +0,0 @@ -module ActiveSupport - module Testing - module Performance - module Metrics - class Base - protected - # Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker) - if GC.respond_to?(:enable_stats) - def with_gc_stats - GC.enable_stats - GC.start - yield - ensure - GC.disable_stats - end - end - end - - class Memory < DigitalInformationUnit - # Ruby 1.8 + ruby-prof wrapper - if RubyProf.respond_to?(:measure_memory) - def measure - RubyProf.measure_memory - end - end - end - - class Objects < Amount - # Ruby 1.8 + ruby-prof wrapper - if RubyProf.respond_to?(:measure_allocations) - def measure - RubyProf.measure_allocations - end - end - end - - class GcRuns < Amount - # Ruby 1.8 + ruby-prof wrapper - if RubyProf.respond_to?(:measure_gc_runs) - def measure - RubyProf.measure_gc_runs - end - end - end - - class GcTime < Time - # Ruby 1.8 + ruby-prof wrapper - if RubyProf.respond_to?(:measure_gc_time) - def measure - RubyProf.measure_gc_time / 1000.0 / 1000.0 - end - end - end - end - end - end -end diff --git a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb index 7873262331..c34d31bf10 100644 --- a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb +++ b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb @@ -4,15 +4,12 @@ module ActiveSupport module Metrics class Base protected - # Ruby 1.9 with GC::Profiler - if defined?(GC::Profiler) - def with_gc_stats - GC::Profiler.enable - GC.start - yield - ensure - GC::Profiler.disable - end + def with_gc_stats + GC::Profiler.enable + GC.start + yield + ensure + GC::Profiler.disable end end @@ -35,20 +32,14 @@ module ActiveSupport end class GcRuns < Amount - # Ruby 1.9 - if GC.respond_to?(:count) - def measure - GC.count - end + def measure + GC.count end end class GcTime < Time - # Ruby 1.9 with GC::Profiler - if defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time) - def measure - GC::Profiler.total_time - end + def measure + GC::Profiler.total_time end end end diff --git a/activesupport/lib/active_support/testing/setup_and_teardown.rb b/activesupport/lib/active_support/testing/setup_and_teardown.rb index 22e41fa905..40b781485e 100644 --- a/activesupport/lib/active_support/testing/setup_and_teardown.rb +++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb @@ -10,11 +10,7 @@ module ActiveSupport include ActiveSupport::Callbacks define_callbacks :setup, :teardown - if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions - include ForMiniTest - else - include ForClassicTestUnit - end + include ForMiniTest end module ClassMethods @@ -47,65 +43,6 @@ module ActiveSupport end end - module ForClassicTestUnit - # For compatibility with Ruby < 1.8.6 - PASSTHROUGH_EXCEPTIONS = Test::Unit::TestCase::PASSTHROUGH_EXCEPTIONS rescue [NoMemoryError, SignalException, Interrupt, SystemExit] - - # This redefinition is unfortunate but test/unit shows us no alternative. - # Doubly unfortunate: hax to support Mocha's hax. - def run(result) - return if @method_name.to_s == "default_test" - - mocha_counter = retrieve_mocha_counter(result) - yield(Test::Unit::TestCase::STARTED, name) - @_result = result - - begin - begin - run_callbacks :setup do - setup - __send__(@method_name) - mocha_verify(mocha_counter) if mocha_counter - end - rescue Mocha::ExpectationError => e - add_failure(e.message, e.backtrace) - rescue Test::Unit::AssertionFailedError => e - add_failure(e.message, e.backtrace) - rescue Exception => e - raise if PASSTHROUGH_EXCEPTIONS.include?(e.class) - add_error(e) - ensure - begin - teardown - run_callbacks :teardown - rescue Test::Unit::AssertionFailedError => e - add_failure(e.message, e.backtrace) - rescue Exception => e - raise if PASSTHROUGH_EXCEPTIONS.include?(e.class) - add_error(e) - end - end - ensure - mocha_teardown if mocha_counter - end - - result.add_run - yield(Test::Unit::TestCase::FINISHED, name) - end - - protected - - def retrieve_mocha_counter(result) #:nodoc: - if respond_to?(:mocha_verify) # using mocha - if defined?(Mocha::TestCaseAdapter::AssertionCounter) - Mocha::TestCaseAdapter::AssertionCounter.new(result) - else - Mocha::Integration::TestUnit::AssertionCounter.new(result) - end - end - end - end - end end end diff --git a/activesupport/lib/active_support/time.rb b/activesupport/lib/active_support/time.rb index 86f057d676..9634b52ecf 100644 --- a/activesupport/lib/active_support/time.rb +++ b/activesupport/lib/active_support/time.rb @@ -13,7 +13,6 @@ end require 'date' require 'time' -require 'active_support/core_ext/time/publicize_conversion_methods' require 'active_support/core_ext/time/marshal' require 'active_support/core_ext/time/acts_like' require 'active_support/core_ext/time/calculations' @@ -21,7 +20,6 @@ require 'active_support/core_ext/time/conversions' require 'active_support/core_ext/time/zones' require 'active_support/core_ext/date/acts_like' -require 'active_support/core_ext/date/freeze' require 'active_support/core_ext/date/calculations' require 'active_support/core_ext/date/conversions' require 'active_support/core_ext/date/zones' diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 63279d0e6d..d3adf671a0 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -203,7 +203,11 @@ module ActiveSupport end def eql?(other) - utc == other + utc.eql?(other) + end + + def hash + utc.hash end def +(other) @@ -277,7 +281,6 @@ module ActiveSupport def to_i utc.to_i end - alias_method :hash, :to_i alias_method :tv_sec, :to_i # A TimeWithZone acts like a Time, so just return +self+. diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index bd8e7f907a..8a8f8f946d 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -1,7 +1,7 @@ module ActiveSupport module VERSION #:nodoc: - MAJOR = 3 - MINOR = 2 + MAJOR = 4 + MINOR = 0 TINY = 0 PRE = "beta" diff --git a/activesupport/lib/active_support/whiny_nil.rb b/activesupport/lib/active_support/whiny_nil.rb deleted file mode 100644 index 577db5018e..0000000000 --- a/activesupport/lib/active_support/whiny_nil.rb +++ /dev/null @@ -1,60 +0,0 @@ -# Extensions to +nil+ which allow for more helpful error messages for people who -# are new to Rails. -# -# Ruby raises NoMethodError if you invoke a method on an object that does not -# respond to it: -# -# $ ruby -e nil.destroy -# -e:1: undefined method `destroy' for nil:NilClass (NoMethodError) -# -# With these extensions, if the method belongs to the public interface of the -# classes in NilClass::WHINERS the error message suggests which could be the -# actual intended class: -# -# $ rails runner nil.destroy -# ... -# You might have expected an instance of ActiveRecord::Base. -# ... -# -# NilClass#id exists in Ruby 1.8 (though it is deprecated). Since +id+ is a fundamental -# method of Active Record models NilClass#id is redefined as well to raise a RuntimeError -# and warn the user. She probably wanted a model database identifier and the 4 -# returned by the original method could result in obscure bugs. -# -# The flag <tt>config.whiny_nils</tt> determines whether this feature is enabled. -# By default it is on in development and test modes, and it is off in production -# mode. -class NilClass - METHOD_CLASS_MAP = Hash.new - - def self.add_whiner(klass) - methods = klass.public_instance_methods - public_instance_methods - class_name = klass.name - methods.each { |method| METHOD_CLASS_MAP[method.to_sym] = class_name } - end - - add_whiner ::Array - - # Raises a RuntimeError when you attempt to call +id+ on +nil+. - def id - raise RuntimeError, "Called id for nil, which would mistakenly be #{object_id} -- if you really wanted the id of nil, use object_id", caller - end - - private - def method_missing(method, *args) - if klass = METHOD_CLASS_MAP[method] - raise_nil_warning_for klass, method, caller - else - super - end - end - - # Raises a NoMethodError when you attempt to call a method on +nil+. - def raise_nil_warning_for(class_name = nil, selector = nil, with_caller = nil) - message = "You have a nil object when you didn't expect it!" - message << "\nYou might have expected an instance of #{class_name}." if class_name - message << "\nThe error occurred while evaluating nil.#{selector}" if selector - - raise NoMethodError, message, with_caller || caller - end -end |