diff options
Diffstat (limited to 'activesupport')
82 files changed, 884 insertions, 467 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 60586cf99a..8485e7d46b 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,4 +1,6 @@ -*Rails 3.0.0 [Release Candidate] (unreleased)* +*Rails 3.0.0 [release candidate] (July 26th, 2010)* + +* Removed Object#returning, Object#tap should be used instead. [Santiago Pastorino] * Deprecation behavior is no longer hardcoded to the name of the environment. Instead, it is set via config.active_support.deprecation and can be one diff --git a/activesupport/README b/activesupport/README deleted file mode 100644 index 9fb9a80cbe..0000000000 --- a/activesupport/README +++ /dev/null @@ -1,43 +0,0 @@ -= Active Support -- Utility classes and standard library extensions from Rails - -Active Support is a collection of various utility classes and standard library extensions that were found useful -for Rails. All these additions have hence been collected in this bundle as way to gather all that sugar that makes -Ruby sweeter. - - -== Download - -The latest version of Active Support can be found at - -* http://rubyforge.org/project/showfiles.php?group_id=182 - -Documentation can be found at - -* http://as.rubyonrails.com - - -== Installation - -The preferred method of installing Active Support is through its GEM file. You'll need to have -RubyGems[http://rubygems.rubyforge.org/wiki/wiki.pl] installed for that, though. If you have it, -then use: - - % [sudo] gem install activesupport-1.0.0.gem - - -== License - -Active Support is released under the MIT license. - - -== Support - -The Active Support homepage is http://www.rubyonrails.com. You can find the Active Support -RubyForge page at http://rubyforge.org/projects/activesupport. And as Jim from Rake says: - - Feel free to submit commits or feature requests. If you send a patch, - remember to update the corresponding unit tests. If fact, I prefer - new feature to be submitted in the form of new unit tests. - -For other information, feel free to ask on the ruby-talk mailing list -(which is mirrored to comp.lang.ruby) or contact mailto:david@loudthinking.com. diff --git a/activesupport/README.rdoc b/activesupport/README.rdoc new file mode 100644 index 0000000000..77b8a64304 --- /dev/null +++ b/activesupport/README.rdoc @@ -0,0 +1,33 @@ += Active Support -- Utility classes and Ruby extensions from Rails + +Active Support is a collection of utility classes and standard library +extensions that were found useful for the Rails framework. These additions +reside in this package so they can be loaded as needed in Ruby projects +outside of Rails. + + +== Download and installation + +The latest version of Active Support can be installed with Rubygems: + + % [sudo] gem install activesupport + +Source code can be downloaded as part of the Rails project on GitHub + +* http://github.com/rails/rails/tree/master/activesupport/ + + +== License + +Active Support is released under the MIT license. + + +== Support + +API documentation is at + +* http://api.rubyonrails.com + +Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here: + +* https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets diff --git a/activesupport/Rakefile b/activesupport/Rakefile index 2aebe05de2..8e2683ef89 100644 --- a/activesupport/Rakefile +++ b/activesupport/Rakefile @@ -1,7 +1,7 @@ -gem 'rdoc', '= 2.2' +gem 'rdoc', '>= 2.5.9' require 'rdoc' require 'rake/testtask' -require 'rake/rdoctask' +require 'rdoc/task' require 'rake/gempackagetask' task :default => :test @@ -22,13 +22,13 @@ dist_dirs = [ "lib", "test"] # Genereate the RDoc documentation -Rake::RDocTask.new { |rdoc| +RDoc::Task.new { |rdoc| rdoc.rdoc_dir = 'doc' rdoc.title = "Active Support -- Utility classes and standard library extensions from Rails" - rdoc.options << '--line-numbers' << '--inline-source' + rdoc.options << '-f' << 'horo' + rdoc.options << '--main' << 'README.rdoc' rdoc.options << '--charset' << 'utf-8' - rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo' - rdoc.rdoc_files.include('README', 'CHANGELOG') + rdoc.rdoc_files.include('README.rdoc', 'CHANGELOG') rdoc.rdoc_files.include('lib/active_support.rb') rdoc.rdoc_files.include('lib/active_support/**/*.rb') } @@ -45,9 +45,3 @@ task :release => :package do Rake::Gemcutter::Tasks.new(spec).define Rake::Task['gem:push'].invoke end - -desc "Publish the API documentation" -task :pdoc => [:rdoc] do - require 'rake/contrib/sshpublisher' - Rake::SshDirPublisher.new("rails@api.rubyonrails.org", "public_html/as", "doc").upload -end diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec index 8611a1e5fa..df7f68fecf 100644 --- a/activesupport/activesupport.gemspec +++ b/activesupport/activesupport.gemspec @@ -14,7 +14,7 @@ Gem::Specification.new do |s| s.homepage = 'http://www.rubyonrails.org' s.rubyforge_project = 'activesupport' - s.files = Dir['CHANGELOG', 'README', 'lib/**/*'] + s.files = Dir['CHANGELOG', 'README.rdoc', 'lib/**/*'] s.require_path = 'lib' s.has_rdoc = true diff --git a/activesupport/install.rb b/activesupport/install.rb deleted file mode 100644 index 9c54d8c1a9..0000000000 --- a/activesupport/install.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'rbconfig' -require 'find' -require 'ftools' - -include Config - -# this was adapted from rdoc's install.rb by ways of Log4r - -$sitedir = CONFIG["sitelibdir"] -unless $sitedir - version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"] - $libdir = File.join(CONFIG["libdir"], "ruby", version) - $sitedir = $:.find {|x| x =~ /site_ruby/ } - if !$sitedir - $sitedir = File.join($libdir, "site_ruby") - elsif $sitedir !~ Regexp.quote(version) - $sitedir = File.join($sitedir, version) - end -end - -# the actual gruntwork -Dir.chdir("lib") - -Find.find("active_support", "active_support.rb") { |f| - if f[-3..-1] == ".rb" - File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true) - else - File::makedirs(File.join($sitedir, *f.split(/\//))) - end -} diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb index 29c3843d16..b861a6f62a 100644 --- a/activesupport/lib/active_support/buffered_logger.rb +++ b/activesupport/lib/active_support/buffered_logger.rb @@ -101,7 +101,11 @@ module ActiveSupport @guard.synchronize do unless buffer.empty? old_buffer = buffer - @log.write(old_buffer.join) + all_content = StringIO.new + old_buffer.each do |content| + all_content << content + end + @log.write(all_content.string) end # Important to do this even if buffer was empty or else @buffer will diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index bef9c98ecf..30195bdea5 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -19,8 +19,6 @@ module ActiveSupport autoload :SynchronizedMemoryStore, 'active_support/cache/synchronized_memory_store' autoload :CompressedMemCacheStore, 'active_support/cache/compressed_mem_cache_store' - EMPTY_OPTIONS = {}.freeze - # These options mean something to all cache implementations. Individual cache # implementations may support additional options. UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :race_condition_ttl] @@ -129,13 +127,6 @@ module ActiveSupport # cache.namespace = lambda { @last_mod_time } # Set the namespace to a variable # @last_mod_time = Time.now # Invalidate the entire cache by changing namespace # - # All caches support auto expiring content after a specified number of seconds. - # To set the cache entry time to live, you can either specify +:expires_in+ as - # an option to the constructor to have it affect all entries or to the +fetch+ - # or +write+ methods for just one entry. - # - # cache = ActiveSupport::Cache::MemoryStore.new(:expire_in => 5.minutes) - # cache.write(key, value, :expire_in => 1.minute) # Set a lower value for one entry # # Caches can also store values in a compressed format to save space and reduce # time spent sending data. Since there is some overhead, values must be large @@ -147,7 +138,7 @@ module ActiveSupport cattr_accessor :logger, :instance_writer => true - attr_reader :silence + attr_reader :silence, :options alias :silence? :silence # Create a new cache. The options will be passed to any write method calls except @@ -156,11 +147,6 @@ module ActiveSupport @options = options ? options.dup : {} end - # Get the default options set when the cache was created. - def options - @options ||= {} - end - # Silence the logger. def silence! @silence = true @@ -175,7 +161,7 @@ module ActiveSupport @silence = previous_silence end - # Set to true if cache stores should be instrumented. By default is false. + # Set to true if cache stores should be instrumented. Default is false. def self.instrument=(boolean) Thread.current[:instrument_cache_store] = boolean end @@ -187,7 +173,7 @@ module ActiveSupport # Fetches data from the cache, using the given key. If there is data in # the cache with the given key, then that data is returned. # - # If there is no such data in the cache (a cache miss occurred), then + # If there is no such data in the cache (a cache miss occurred), # then nil will be returned. However, if a block has been passed, then # that block will be run in the event of a cache miss. The return value # of the block will be written to the cache under the given cache key, @@ -211,23 +197,30 @@ module ActiveSupport # Setting <tt>:compress</tt> will store a large cache entry set by the call # in a compressed format. # - # Setting <tt>:expires_in</tt> will set an expiration time on the cache - # entry if it is set by call. # - # Setting <tt>:race_condition_ttl</tt> will invoke logic on entries set with - # an <tt>:expires_in</tt> option. If an entry is found in the cache that is - # expired and it has been expired for less than the number of seconds specified - # by this option and a block was passed to the method call, then the expiration - # future time of the entry in the cache will be updated to that many seconds - # in the and the block will be evaluated and written to the cache. + # Setting <tt>:expires_in</tt> will set an expiration time on the cache. All caches + # support auto expiring content after a specified number of seconds. This value can + # be specified as an option to the construction in which call all entries will be + # affected. Or it can be supplied to the +fetch+ or +write+ method for just one entry. # - # This is very useful in situations where a cache entry is used very frequently - # under heavy load. The first process to find an expired cache entry will then - # become responsible for regenerating that entry while other processes continue - # to use the slightly out of date entry. This can prevent race conditions where - # too many processes are trying to regenerate the entry all at once. If the - # process regenerating the entry errors out, the entry will be regenerated - # after the specified number of seconds. + # cache = ActiveSupport::Cache::MemoryStore.new(:expire_in => 5.minutes) + # cache.write(key, value, :expire_in => 1.minute) # Set a lower value for one entry + # + # Setting <tt>:race_condition_ttl</tt> is very useful in situations where a cache entry + # is used very frequently unver heavy load. If a cache expires and due to heavy load + # seven different processes will try to read data natively and then they all will try to + # write to cache. To avoid that case the first process to find an expired cache entry will + # bump the cache expiration time by the value set in <tt>:race_condition_ttl</tt>. Yes + # this process is extending the time for a stale value by another few seconds. Because + # of extended life of the previous cache, other processes will continue to use slightly + # stale data for a just a big longer. In the meantime that first process will go ahead + # and will write into cache the new value. After that all the processes will start + # getting new value. The key is to keep <tt>:race_condition_ttl</tt> small. + # + # If the process regenerating the entry errors out, the entry will be regenerated + # after the specified number of seconds. Also note that the life of stale cache is + # extended only if it expired recently. Otherwise a new value is generated and + # <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) @@ -252,6 +245,7 @@ module ActiveSupport # # # val_1 => "new value 1" # # val_2 => "original value" + # # sleep 10 # First thread extend the life of cache by another 10 seconds # # cache.fetch("foo") => "new value 1" # # Other options will be handled by the specific cache store implementation. @@ -353,11 +347,9 @@ module ActiveSupport results end - # Writes the given value to the cache, with the given key. + # Writes the value to the cache, with the key. # - # You may also specify additional options via the +options+ argument. - # The specific cache store implementation will decide what to do with - # +options+. + # Options are passed to the underlying cache implementation. def write(name, value, options = nil) options = merged_options(options) instrument(:write, name, options) do |payload| @@ -366,7 +358,7 @@ module ActiveSupport end end - # Delete an entry in the cache. Returns +true+ if there was an entry to delete. + # Deletes an entry in the cache. Returns +true+ if an entry is deleted. # # Options are passed to the underlying cache implementation. def delete(name, options = nil) @@ -376,7 +368,7 @@ module ActiveSupport end end - # Return true if the cache contains an entry with this name. + # Return true if the cache contains an entry for the given key. # # Options are passed to the underlying cache implementation. def exist?(name, options = nil) @@ -391,11 +383,11 @@ module ActiveSupport end end - # Delete all entries whose keys match a pattern. + # Delete all entries with keys matching the pattern. # # Options are passed to the underlying cache implementation. # - # Not all implementations may support +delete_matched+. + # All implementations may not support this method. def delete_matched(matcher, options = nil) raise NotImplementedError.new("#{self.class.name} does not support delete_matched") end @@ -404,7 +396,7 @@ module ActiveSupport # # Options are passed to the underlying cache implementation. # - # Not all implementations may support +delete_matched+. + # All implementations may not support this method. def increment(name, amount = 1, options = nil) raise NotImplementedError.new("#{self.class.name} does not support increment") end @@ -413,28 +405,26 @@ module ActiveSupport # # Options are passed to the underlying cache implementation. # - # Not all implementations may support +delete_matched+. + # All implementations may not support this method. def decrement(name, amount = 1, options = nil) raise NotImplementedError.new("#{self.class.name} does not support decrement") end - # Cleanup the cache by removing expired entries. Not all cache implementations may - # support this method. + # Cleanup the cache by removing expired entries. # # Options are passed to the underlying cache implementation. # - # Not all implementations may support +delete_matched+. + # All implementations may not support this method. def cleanup(options = nil) raise NotImplementedError.new("#{self.class.name} does not support cleanup") end - # Clear the entire cache. Not all cache implementations may support this method. - # You should be careful with this method since it could affect other processes - # if you are using a shared cache. + # Clear the entire cache. Be careful with this method since it could + # affect other processes if shared cache is being used. # # Options are passed to the underlying cache implementation. # - # Not all implementations may support +delete_matched+. + # All implementations may not support this method. def clear(options = nil) raise NotImplementedError.new("#{self.class.name} does not support clear") end @@ -483,9 +473,9 @@ module ActiveSupport end end - # Expand a key to be a consistent string value. If the object responds to +cache_key+, - # it will be called. Otherwise, the to_param method will be called. If the key is a - # Hash, the keys will be sorted alphabetically. + # Expand key to be a consistent string value. Invoke +cache_key+ if + # object responds to +cache_key+. Otherwise, to_param method will be + # called. If the key is a Hash, then keys will be sorted alphabetically. def expanded_key(key) # :nodoc: if key.respond_to?(:cache_key) key = key.cache_key.to_s @@ -502,7 +492,7 @@ module ActiveSupport end end - # Prefix a key with the namespace. The two values will be delimited with a colon. + # Prefix a key with the namespace. Namespace and key will be delimited with a colon. def namespaced_key(key, options) key = expanded_key(key) namespace = options[:namespace] if options @@ -599,7 +589,7 @@ module ActiveSupport end end - # Set a new time to live on the entry so it expires at the given time. + # Set a new time when the entry will expire. def expires_at=(time) if time @expires_in = time.to_f - @created_at @@ -608,12 +598,12 @@ module ActiveSupport end end - # Seconds since the epoch when the cache entry will expire. + # Seconds since the epoch when the entry will expire. def expires_at @expires_in ? @created_at + @expires_in : nil end - # Get the size of the cached value. This could be less than value.size + # Returns the size of the cached value. This could be less than value.size # if the data is compressed. def size if @value.nil? diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index 852defeae8..f32b562368 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -16,8 +16,7 @@ module ActiveSupport # Special features: # - Clustering and load balancing. One can specify multiple memcached servers, # and MemCacheStore will load balance between all available servers. If a - # server goes down, then MemCacheStore will ignore it until it goes back - # online. + # 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. @@ -69,7 +68,7 @@ module ActiveSupport extend LocalCacheWithRaw end - # Reads multiple keys from the cache using a single call to the + # Reads multiple values from the cache using a single call to the # servers for all keys. Options can be passed in the last argument. def read_multi(*names) options = names.extract_options! @@ -113,7 +112,7 @@ module ActiveSupport end # Clear the entire cache on all memcached servers. This method should - # be used with care when using a shared cache. + # be used with care when shared cache is being used. def clear(options = nil) @data.flush_all end diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb index f5c2b8af8b..b15bb42c88 100644 --- a/activesupport/lib/active_support/cache/memory_store.rb +++ b/activesupport/lib/active_support/cache/memory_store.rb @@ -5,9 +5,9 @@ module ActiveSupport # A cache store implementation which stores everything into memory in the # same process. If you're running multiple Ruby on Rails server processes # (which is the case if you're using mongrel_cluster or Phusion Passenger), - # then this means that your Rails server process instances won't be able + # then this means that Rails server process instances won't be able # to share cache data with each other and this may not be the most - # appropriate cache for you. + # appropriate cache in that scenario. # # This cache has a bounded size specified by the :size options to the # initializer (default is 32Mb). When the cache exceeds the allotted size, @@ -47,8 +47,8 @@ module ActiveSupport end end - # Prune the cache down so the entries fit within the specified memory size by removing - # the least recently accessed entries. + # To ensure entries fit within the specified memory prune the cache by removing the least + # recently accessed entries. def prune(target_size, max_time = nil) return if pruning? @pruning = true @@ -67,7 +67,7 @@ module ActiveSupport end end - # Return true if the cache is currently be pruned to remove older entries. + # Returns true if the cache is currently being pruned. def pruning? @pruning end diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb index efb5ad26ab..3edba52fc4 100644 --- a/activesupport/lib/active_support/cache/strategy/local_cache.rb +++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb @@ -8,7 +8,7 @@ module ActiveSupport # duration of a block. Repeated calls to the cache for the same key will hit the # in memory cache for faster access. module LocalCache - # Simple memory backed cache. This cache is not thread safe but is intended only + # Simple memory backed cache. This cache is not thread safe and is intended only # for serving as a temporary memory cache for a single thread. class LocalStore < Store def initialize @@ -16,7 +16,7 @@ module ActiveSupport @data = {} end - # Since it isn't thread safe, don't allow synchronizing. + # Don't allow synchronizing since it isn't thread safe, def synchronize # :nodoc: yield end @@ -39,7 +39,7 @@ module ActiveSupport end end - # Use a local cache to front for the cache for the duration of a block. + # Use a local cache for the duration of block. def with_local_cache save_val = Thread.current[thread_local_key] begin @@ -50,8 +50,8 @@ module ActiveSupport end end - # Middleware class can be inserted as a Rack handler to use a local cache for the - # duration of a request. + # Middleware class can be inserted as a Rack handler to be local cache for the + # duration of request. def middleware @middleware ||= begin klass = Class.new diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 1c7802f7de..24e407c253 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -419,7 +419,10 @@ module ActiveSupport @_keyed_callbacks ||= {} @_keyed_callbacks[name] ||= begin str = send("_#{kind}_callbacks").compile(name, object) - class_eval "def #{name}() #{str} end", __FILE__, __LINE__ + class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + def #{name}() #{str} end + protected :#{name} + RUBY_EVAL true end end @@ -483,7 +486,11 @@ module ActiveSupport end end - # Skip a previously defined callback for a given type. + # Skip a previously defined callback. + # + # class Writer < Person + # skip_callback :validate, :before, :check_membership, :if => lambda { self.age > 18 } + # end # def skip_callback(name, *filter_list, &block) __update_callbacks(name, filter_list, block) do |chain, type, filters, options| @@ -523,7 +530,8 @@ module ActiveSupport # This macro accepts the following options: # # * <tt>:terminator</tt> - Indicates when a before filter is considered - # to be halted. + # to halted. This is a string to be eval'ed and has the result of the + # very filter available in the <tt>result</tt> variable: # # define_callbacks :validate, :terminator => "result == false" # @@ -568,7 +576,9 @@ module ActiveSupport # # would trigger <tt>Audit#before_save</tt> instead. That's constructed by calling # <tt>"#{kind}_#{name}"</tt> on the given instance. In this case "kind" is "before" and - # "name" is "save". + # "name" is "save". In this context ":kind" and ":name" have special meanings: ":kind" + # refers to the kind of callback (before/after/around) and ":name" refers to the + # method on which callbacks are being defined. # # A declaration like # diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb index eb31f7cad4..2d87e8d0e5 100644 --- a/activesupport/lib/active_support/concern.rb +++ b/activesupport/lib/active_support/concern.rb @@ -1,3 +1,38 @@ +# A typical module looks like this +# +# module M +# def self.included(base) +# base.send(:extend, ClassMethods) +# base.send(:include, InstanceMethods) +# scope :foo, :conditions => { :created_at => nil } +# end +# +# module ClassMethods +# def cm; puts 'I am a class method'; end +# end +# +# module InstanceMethods +# def im; puts 'I am an instance method'; end +# end +# end +# +# By using <tt>ActiveSupport::Concern</tt> the above module could instead be written as: +# +# module M +# extend ActiveSupport::Concern +# +# included do +# scope :foo, :conditions => { :created_at => nil } +# end +# +# module ClassMethods +# def cm; puts 'I am a class method'; end +# end +# +# module InstanceMethods +# def im; puts 'I am an instance method'; end +# end +# end module ActiveSupport module Concern def self.extended(base) diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 79e3828817..7585137aca 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -58,12 +58,12 @@ class Array alias_method :to_default_s, :to_s alias_method :to_s, :to_formatted_s - # Returns a string that represents this array in XML by sending +to_xml+ - # to each element. Active Record collections delegate their representation + # Returns a string that represents the array in XML by invoking +to_xml+ + # on each element. Active Record collections delegate their representation # in XML to this method. # # All elements are expected to respond to +to_xml+, if any of them does - # not an exception is raised. + # not then an exception is raised. # # The root node reflects the class name of the first element in plural # if all elements belong to the same type and that's not Hash: @@ -115,8 +115,8 @@ class Array # <?xml version="1.0" encoding="UTF-8"?> # <projects type="array"/> # - # By default root children have as node name the one of the root - # singularized. You can change it with the <tt>:children</tt> option. + # By default name of the node for the children of root is <tt>root.singularize</tt>. + # You can change it with the <tt>:children</tt> option. # # The +options+ hash is passed downwards: # 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 a09b2302fd..bd5c7a187f 100644 --- a/activesupport/lib/active_support/core_ext/array/uniq_by.rb +++ b/activesupport/lib/active_support/core_ext/array/uniq_by.rb @@ -2,7 +2,7 @@ class Array # Return an unique array based on the criteria given as a proc. # # [1, 2, 3, 4].uniq_by { |i| i.odd? } - # #=> [1, 2] + # # => [1, 2] # def uniq_by hash, array = {}, [] diff --git a/activesupport/lib/active_support/core_ext/array/wrap.rb b/activesupport/lib/active_support/core_ext/array/wrap.rb index e211bdeeca..06b2acd662 100644 --- a/activesupport/lib/active_support/core_ext/array/wrap.rb +++ b/activesupport/lib/active_support/core_ext/array/wrap.rb @@ -1,15 +1,41 @@ class Array - # Wraps the object in an Array unless it's an Array. Converts the - # object to an Array using #to_ary if it implements that. + # Wraps its argument in an array unless it is already an array (or array-like). # - # It differs with Array() in that it does not call +to_a+ on - # the argument: + # Specifically: + # + # * If the argument is +nil+ an empty list is returned. + # * Otherwise, if the argument responds to +to_ary+ it is invoked, and its result returned. + # * Otherwise, returns an array with the argument as its single element. + # + # Array.wrap(nil) # => [] + # Array.wrap([1, 2, 3]) # => [1, 2, 3] + # Array.wrap(0) # => [0] + # + # This method is similar in purpose to <tt>Kernel#Array</tt>, but there are some differences: + # + # * If the argument responds to +to_ary+ the method is invoked. <tt>Kernel#Array</tt> + # moves on to try +to_a+ if the returned value is +nil+, but <tt>Arraw.wrap</tt> returns + # such a +nil+ right away. + # * If the returned value from +to_ary+ is neither +nil+ nor an +Array+ object, <tt>Kernel#Array</tt> + # raises an exception, while <tt>Array.wrap</tt> does not, it just returns the value. + # * It does not call +to_a+ on the argument, though special-cases +nil+ to return an empty array. + # + # The last point is particularly worth comparing for some enumerables: # # Array(:foo => :bar) # => [[:foo, :bar]] # Array.wrap(:foo => :bar) # => [{:foo => :bar}] # # Array("foo\nbar") # => ["foo\n", "bar"], in Ruby 1.8 # Array.wrap("foo\nbar") # => ["foo\nbar"] + # + # There's also a related idiom that uses the splat operator: + # + # [*object] + # + # which returns <tt>[nil]</tt> for +nil+, and calls to <tt>Array(object)</tt> otherwise. + # + # Thus, in this case the behavior is different for +nil+, and the differences with + # <tt>Kernel#Array</tt> explained above apply to the rest of +object+s. def self.wrap(object) if object.nil? [] diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb index 576366e496..bfa57fe1f7 100644 --- a/activesupport/lib/active_support/core_ext/class/attribute.rb +++ b/activesupport/lib/active_support/core_ext/class/attribute.rb @@ -2,8 +2,8 @@ require 'active_support/core_ext/kernel/singleton_class' require 'active_support/core_ext/module/remove_method' class Class - # Declare a class-level attribute whose value is inheritable and - # overwritable by subclasses: + # Declare a class-level attribute whose value is inheritable by subclasses. + # Subclasses can change their own value and it will not impact parent class. # # class Base # class_attribute :setting @@ -18,12 +18,34 @@ class Class # Subclass.setting # => false # Base.setting # => true # + # In the above case as long as Subclass does not assign a value to setting + # by performing <tt>Subclass.setting = _something_ </tt>, <tt>Subclass.setting</tt> + # would read value assigned to parent class. Once Subclass assigns a value then + # the value assigned by Subclass would be returned. + # # This matches normal Ruby method inheritance: think of writing an attribute - # on a subclass as overriding the reader method. + # on a subclass as overriding the reader method. However, you need to be aware + # when using +class_attribute+ with mutable structures as +Array+ or +Hash+. + # In such cases, you don't want to do changes in places but use setters: + # + # Base.setting = [] + # Base.setting #=> [] + # Subclass.setting #=> [] + # + # # Appending in child changes both parent and child because it is the same object: + # Subclass.setting << :foo + # Base.setting #=> [:foo] + # Subclass.setting #=> [:foo] + # + # # Use setters to not propagate changes: + # Base.setting = [] + # Subclass.setting += [:foo] + # Base.setting #=> [] + # Subclass.setting #=> [:foo] # # For convenience, a query method is defined as well: # - # Subclass.setting? # => false + # Subclass.setting? # => false # # Instances may overwrite the class value in the same way: # diff --git a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb index feef5d2d57..4e35b1b488 100644 --- a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb @@ -3,11 +3,27 @@ require 'active_support/core_ext/array/extract_options' # Extends the class object with class and instance accessors for class attributes, # just like the native attr* accessors for instance attributes. # +# Note that unlike +class_attribute+, if a subclass changes the value then that would +# also change the value for parent class. Similarly if parent class changes the value +# then that would change the value of subclasses too. +# # class Person # cattr_accessor :hair_colors # end # # Person.hair_colors = [:brown, :black, :blonde, :red] +# Person.hair_colors #=> [:brown, :black, :blonde, :red] +# Person.new.hair_colors #=> [:brown, :black, :blonde, :red] +# +# To opt out of the instance writer method, pass :instance_writer => false. +# To opt out of the instance reader method, pass :instance_reader => false. +# +# class Person +# cattr_accessor :hair_colors, :instance_writer => false, :instance_reader => false +# end +# +# Person.new.hair_colors = [:brown] # => NoMethodError +# Person.new.hair_colors # => NoMethodError class Class def cattr_reader(*syms) options = syms.extract_options! diff --git a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb index 7aff05dcdf..e844cf50d1 100644 --- a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb +++ b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb @@ -1,17 +1,39 @@ require 'active_support/core_ext/object/duplicable' require 'active_support/core_ext/array/extract_options' -# Retain for backward compatibility. Methods are now included in Class. +# Retained for backward compatibility. Methods are now included in Class. module ClassInheritableAttributes # :nodoc: end -# Allows attributes to be shared within an inheritance hierarchy, but where each descendant gets a copy of +# It is recommend to use <tt>class_attribute</tt> over methods defined in this file. Please +# refer to documentation for <tt>class_attribute</tt> for more information. Officially it is not +# deprected but <tt>class_attribute</tt> is faster. +# +# Allows attributes to be shared within an inheritance hierarchy. Each descendant gets a copy of # their parents' attributes, instead of just a pointer to the same. This means that the child can add elements # to, for example, an array without those additions being shared with either their parent, siblings, or -# children, which is unlike the regular class-level attributes that are shared across the entire hierarchy. +# children. This is unlike the regular class-level attributes that are shared across the entire hierarchy. # # The copies of inheritable parent attributes are added to subclasses when they are created, via the # +inherited+ hook. +# +# class Person +# class_inheritable_accessor :hair_colors +# end +# +# Person.hair_colors = [:brown, :black, :blonde, :red] +# Person.hair_colors #=> [:brown, :black, :blonde, :red] +# Person.new.hair_colors #=> [:brown, :black, :blonde, :red] +# +# To opt out of the instance writer method, pass :instance_writer => false. +# To opt out of the instance reader method, pass :instance_reader => false. +# +# class Person +# class_inheritable_accessor :hair_colors :instance_writer => false, :instance_reader => false +# end +# +# Person.new.hair_colors = [:brown] # => NoMethodError +# Person.new.hair_colors # => NoMethodError class Class # :nodoc: def class_inheritable_reader(*syms) options = syms.extract_options! diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index e6a213625c..c5b54318ce 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -40,23 +40,23 @@ class Date end end - # Tells whether the Date object's date lies in the past + # Returns true if the Date object's date lies in the past. Otherwise returns false. def past? self < ::Date.current end - # Tells whether the Date object's date is today + # Returns true if the Date object's date is today. def today? self.to_date == ::Date.current # we need the to_date because of DateTime end - # Tells whether the Date object's date lies in the future + # Returns true if the Date object's date lies in the future. def future? self > ::Date.current end # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00) - # and then subtracts the specified number of seconds + # and then subtracts the specified number of seconds. def ago(seconds) to_time_in_current_zone.since(-seconds) end @@ -127,22 +127,22 @@ class Date ) end - # Returns a new Date/DateTime representing the time a number of specified months ago + # Returns a new Date/DateTime representing the time a number of specified months ago. def months_ago(months) advance(:months => -months) end - # Returns a new Date/DateTime representing the time a number of specified months in the future + # Returns a new Date/DateTime representing the time a number of specified months in the future. def months_since(months) advance(:months => months) end - # Returns a new Date/DateTime representing the time a number of specified years ago + # Returns a new Date/DateTime representing the time a number of specified years ago. def years_ago(years) advance(:years => -years) end - # Returns a new Date/DateTime representing the time a number of specified years in the future + # Returns a new Date/DateTime representing the time a number of specified years in the future. def years_since(years) advance(:years => years) end @@ -152,22 +152,22 @@ class Date years_ago(1) end unless method_defined?(:prev_year) - # Short-hand for years_since(1) + # Shorthand for years_since(1) def next_year years_since(1) end unless method_defined?(:next_year) - # Short-hand for months_ago(1) + # Shorthand for months_ago(1) def prev_month months_ago(1) end unless method_defined?(:prev_month) - # Short-hand for months_since(1) + # Shorthand for months_since(1) def next_month months_since(1) end unless method_defined?(:next_month) - # Returns a new Date/DateTime representing the "start" of this week (i.e, Monday; DateTime objects will have time set to 0:00) + # 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 @@ -176,7 +176,7 @@ class Date 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) + # 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 diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb index d0821a7c68..f76ed401cd 100644 --- a/activesupport/lib/active_support/core_ext/enumerable.rb +++ b/activesupport/lib/active_support/core_ext/enumerable.rb @@ -66,7 +66,8 @@ module Enumerable # +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'} + # %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 diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 565c9af7fb..2763af6121 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -83,7 +83,7 @@ class Hash case value.class.to_s when 'Hash' if value['type'] == 'array' - child_key, entries = Array.wrap(value.detect { |k,v| k != 'type' }) # child_key is throwaway + _, entries = Array.wrap(value.detect { |k,v| k != 'type' }) if entries.nil? || (c = value['__content__'] && c.blank?) [] else diff --git a/activesupport/lib/active_support/core_ext/kernel/requires.rb b/activesupport/lib/active_support/core_ext/kernel/requires.rb index d2238898d6..3bf46271d7 100644 --- a/activesupport/lib/active_support/core_ext/kernel/requires.rb +++ b/activesupport/lib/active_support/core_ext/kernel/requires.rb @@ -11,13 +11,13 @@ module Kernel # 1. Requiring the module is unsuccessful, maybe it's a gem and nobody required rubygems yet. Try. begin require 'rubygems' - rescue LoadError => rubygems_not_installed + rescue LoadError # => rubygems_not_installed raise cannot_require end # 2. Rubygems is installed and loaded. Try to load the library again begin require library_name - rescue LoadError => gem_not_installed + rescue LoadError # => gem_not_installed raise cannot_require end end diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb index 9c4d5fae26..2d88cb57e5 100644 --- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb @@ -5,9 +5,7 @@ class Module options = syms.extract_options! syms.each do |sym| class_eval(<<-EOS, __FILE__, __LINE__ + 1) - unless defined? @@#{sym} - @@#{sym} = nil - end + @@#{sym} = nil unless defined? @@#{sym} def self.#{sym} @@#{sym} @@ -28,10 +26,6 @@ class Module options = syms.extract_options! syms.each do |sym| class_eval(<<-EOS, __FILE__, __LINE__ + 1) - unless defined? @@#{sym} - @@#{sym} = nil - end - def self.#{sym}=(obj) @@#{sym} = obj end diff --git a/activesupport/lib/active_support/core_ext/module/remove_method.rb b/activesupport/lib/active_support/core_ext/module/remove_method.rb index 2714a46b28..b8c01aca0e 100644 --- a/activesupport/lib/active_support/core_ext/module/remove_method.rb +++ b/activesupport/lib/active_support/core_ext/module/remove_method.rb @@ -3,4 +3,9 @@ class Module remove_method(method) rescue NameError end + + def redefine_method(method, &block) + remove_possible_method(method) + define_method(method, &block) + end end
\ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb index 27618b55c6..d671da6711 100644 --- a/activesupport/lib/active_support/core_ext/object.rb +++ b/activesupport/lib/active_support/core_ext/object.rb @@ -2,12 +2,11 @@ require 'active_support/core_ext/object/acts_like' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/duplicable' require 'active_support/core_ext/object/try' +require 'active_support/core_ext/object/returning' require 'active_support/core_ext/object/conversions' require 'active_support/core_ext/object/instance_variables' -require 'active_support/core_ext/object/misc' -require 'active_support/core_ext/object/returning' require 'active_support/core_ext/object/to_json' require 'active_support/core_ext/object/to_param' require 'active_support/core_ext/object/to_query' diff --git a/activesupport/lib/active_support/core_ext/object/misc.rb b/activesupport/lib/active_support/core_ext/object/misc.rb deleted file mode 100644 index 3e3af03cc5..0000000000 --- a/activesupport/lib/active_support/core_ext/object/misc.rb +++ /dev/null @@ -1,2 +0,0 @@ -require 'active_support/core_ext/object/returning' -require 'active_support/core_ext/object/with_options' diff --git a/activesupport/lib/active_support/core_ext/object/returning.rb b/activesupport/lib/active_support/core_ext/object/returning.rb index 0dc2e1266a..07250b2a27 100644 --- a/activesupport/lib/active_support/core_ext/object/returning.rb +++ b/activesupport/lib/active_support/core_ext/object/returning.rb @@ -25,7 +25,7 @@ class Object # end # # foo # => ['bar', 'baz'] - # + # # # returning with a block argument # def foo # returning [] do |values| @@ -33,10 +33,11 @@ class Object # values << 'baz' # end # end - # + # # foo # => ['bar', 'baz'] def returning(value) + ActiveSupport::Deprecation.warn('Object#returning has been deprecated in favor of Object#tap.', caller) yield(value) value end -end +end
\ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/object/to_param.rb b/activesupport/lib/active_support/core_ext/object/to_param.rb index 06f077e920..f2e7c2351e 100644 --- a/activesupport/lib/active_support/core_ext/object/to_param.rb +++ b/activesupport/lib/active_support/core_ext/object/to_param.rb @@ -44,6 +44,6 @@ class Hash def to_param(namespace = nil) collect do |key, value| value.to_query(namespace ? "#{namespace}[#{key}]" : key) - end.sort * '&' + end * '&' end end diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb index 66c4034781..32913a06ad 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -1,3 +1,5 @@ +require 'active_support/inflector/methods' +require 'active_support/inflector/inflections' # String inflections define new methods on the String class to transform names for different purposes. # For instance, you can figure out the name of a database from the name of a class. # diff --git a/activesupport/lib/active_support/core_ext/string/multibyte.rb b/activesupport/lib/active_support/core_ext/string/multibyte.rb index 16ccd36458..0b974f5e0a 100644 --- a/activesupport/lib/active_support/core_ext/string/multibyte.rb +++ b/activesupport/lib/active_support/core_ext/string/multibyte.rb @@ -12,11 +12,11 @@ class String # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsuled string. # # name = 'Claus Müller' - # name.reverse #=> "rell??M sualC" - # name.length #=> 13 + # name.reverse # => "rell??M sualC" + # name.length # => 13 # - # name.mb_chars.reverse.to_s #=> "rellüM sualC" - # name.mb_chars.length #=> 12 + # 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. @@ -26,7 +26,7 @@ class String # 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 + # name.mb_chars.reverse.length # => 12 # # == Interoperability and configuration # diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 7d5143ba37..2b80bd214f 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -72,10 +72,6 @@ module ActiveSupport #:nodoc: methods.each { |m| class_eval "def #{m}(*) lock { super } end", __FILE__, __LINE__ } end - def get(key) - (val = assoc(key)) ? val[1] : [] - end - locked :concat, :each, :delete_if, :<< def new_constants_for(frames) @@ -85,7 +81,18 @@ module ActiveSupport #:nodoc: next unless mod.is_a?(Module) new_constants = mod.local_constant_names - prior_constants - get(mod_name).concat(new_constants) + + # If we are checking for constants under, say, :Object, nested under something + # else that is checking for constants also under :Object, make sure the + # parent knows that we have found, and taken care of, the constant. + # + # In particular, this means that since Kernel.require discards the constants + # it finds, parents will be notified that about those constants, and not + # consider them "new". As a result, they will not be added to the + # autoloaded_constants list. + each do |key, value| + value.concat(new_constants) if key == mod_name + end new_constants.each do |suffix| constants << ([mod_name, suffix] - ["Object"]).join("::") @@ -592,7 +599,7 @@ module ActiveSupport #:nodoc: # Convert the provided const desc to a qualified constant name (as a string). # A module, class, symbol, or string may be provided. def to_constant_name(desc) #:nodoc: - name = case desc + case desc when String then desc.sub(/^::/, '') when Symbol then desc.to_s when Module diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb index dec56715be..deb33ab702 100644 --- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -3,6 +3,13 @@ require 'active_support/inflector' module ActiveSupport module Deprecation class DeprecationProxy #:nodoc: + def self.new(*args, &block) + object = args.first + + return object unless object + super + end + instance_methods.each { |m| undef_method m unless m =~ /^__|^object_id$/ } # Don't give a deprecation warning on inspect since test/unit and error diff --git a/activesupport/lib/active_support/descendants_tracker.rb b/activesupport/lib/active_support/descendants_tracker.rb index 6cba84d79e..4d1cfacc95 100644 --- a/activesupport/lib/active_support/descendants_tracker.rb +++ b/activesupport/lib/active_support/descendants_tracker.rb @@ -11,9 +11,9 @@ module ActiveSupport end def self.descendants(klass) - @@direct_descendants[klass].inject([]) do |descendants, klass| - descendants << klass - descendants.concat klass.descendants + @@direct_descendants[klass].inject([]) do |descendants, _klass| + descendants << _klass + descendants.concat _klass.descendants end end @@ -40,4 +40,4 @@ module ActiveSupport DescendantsTracker.descendants(self) end end -end
\ No newline at end of file +end diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index cd0d66a482..7da357730b 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -4,8 +4,8 @@ require 'active_support/core_ext/object/acts_like' module ActiveSupport # Provides accurate date and time measurements using Date#advance and - # Time#advance, respectively. It mainly supports the methods on Numeric, - # such as in this example: + # Time#advance, respectively. It mainly supports the methods on Numeric. + # Example: # # 1.month.ago # equivalent to Time.now.advance(:months => -1) class Duration < BasicObject diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index f64f0f44cc..eec5d4cf47 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/hash/keys' # This class has dubious semantics and we only have it so that diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index b3dc5b2f3a..de49750083 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -20,6 +20,11 @@ module ActiveSupport # "active_record".camelize(:lower) # => "activeRecord" # "active_record/errors".camelize # => "ActiveRecord::Errors" # "active_record/errors".camelize(:lower) # => "activeRecord::Errors" + # + # As a rule of thumb you can think of +camelize+ as the inverse of +underscore+, + # though there are cases where that does not hold: + # + # "SSLError".underscore.camelize # => "SslError" def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true) if first_letter_in_uppercase lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase } @@ -28,13 +33,18 @@ module ActiveSupport end end - # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string. + # Makes an underscored, lowercase form from the expression in the string. # # Changes '::' to '/' to convert namespaces to paths. # # Examples: # "ActiveRecord".underscore # => "active_record" # "ActiveRecord::Errors".underscore # => active_record/errors + # + # As a rule of thumb you can think of +underscore+ as the inverse of +camelize+, + # though there are cases where that does not hold: + # + # "SSLError".underscore.camelize # => "SslError" def underscore(camel_cased_word) word = camel_cased_word.to_s.dup word.gsub!(/::/, '/') diff --git a/activesupport/lib/active_support/json/backends/yaml.rb b/activesupport/lib/active_support/json/backends/yaml.rb index 215b3d6f90..4cb9d01077 100644 --- a/activesupport/lib/active_support/json/backends/yaml.rb +++ b/activesupport/lib/active_support/json/backends/yaml.rb @@ -13,7 +13,7 @@ module ActiveSupport json = json.read end YAML.load(convert_json_to_yaml(json)) - rescue ArgumentError => e + rescue ArgumentError raise ParseError, "Invalid JSON string" end diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb index 3664431a28..ef43fc0431 100644 --- a/activesupport/lib/active_support/lazy_load_hooks.rb +++ b/activesupport/lib/active_support/lazy_load_hooks.rb @@ -1,3 +1,22 @@ +# lazy_load_hooks allows rails to lazily load a lot of components and thus making the app boot faster. Because of +# this feature now there is no need to require <tt>ActiveRecord::Base</tt> at boot time purely to apply configuration. Instead +# a hook is registered that applies configuration once <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is used +# as example but this feature can be applied elsewhere too. +# +# Here is an example where +on_load+ method is called to register a hook. +# +# initializer "active_record.initialize_timezone" do +# ActiveSupport.on_load(:active_record) do +# self.time_zone_aware_attributes = true +# self.default_timezone = :utc +# end +# end +# +# When the entirety of +activerecord/lib/active_record/base.rb+ has been evaluated then +run_load_hooks+ is invoked. +# The very last line of +activerecord/lib/active_record/base.rb+ is: +# +# ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base) +# module ActiveSupport @load_hooks = Hash.new {|h,k| h[k] = [] } @loaded = {} @@ -24,4 +43,4 @@ module ActiveSupport execute_hook(base, options, hook) end end -end
\ No newline at end of file +end diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb index 891d718af3..83930b3f0d 100644 --- a/activesupport/lib/active_support/log_subscriber.rb +++ b/activesupport/lib/active_support/log_subscriber.rb @@ -21,7 +21,7 @@ module ActiveSupport # ActiveRecord::LogSubscriber.attach_to :active_record # # Since we need to know all instance methods before attaching the log subscriber, - # the line above shuold be called after your ActiveRecord::LogSubscriber definition. + # the line above should be called after your ActiveRecord::LogSubscriber definition. # # After configured, whenever a "sql.active_record" notification is published, # it will properly dispatch the event (ActiveSupport::Notifications::Event) to @@ -63,15 +63,9 @@ module ActiveSupport @@flushable_loggers = nil log_subscriber.public_methods(false).each do |event| - notifier.subscribe("#{event}.#{namespace}") do |*args| - next if log_subscriber.logger.nil? - - begin - log_subscriber.send(event, ActiveSupport::Notifications::Event.new(*args)) - rescue Exception => e - log_subscriber.logger.error "Could not log #{args[0].inspect} event. #{e.class}: #{e.message}" - end - end + next if 'call' == event.to_s + + notifier.subscribe("#{event}.#{namespace}", log_subscriber) end end @@ -92,6 +86,17 @@ module ActiveSupport flushable_loggers.each(&:flush) end + def call(message, *args) + return unless logger + + method = message.split('.').first + begin + send(method, ActiveSupport::Notifications::Event.new(message, *args)) + rescue Exception => e + logger.error "Could not log #{message.inspect} event. #{e.class}: #{e.message}" + end + end + protected %w(info debug warn error fatal unknown).each do |level| diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb index 96506a4b2b..9e52cb97a9 100644 --- a/activesupport/lib/active_support/log_subscriber/test_helper.rb +++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb @@ -1,4 +1,5 @@ require 'active_support/log_subscriber' +require 'active_support/buffered_logger' module ActiveSupport class LogSubscriber @@ -33,7 +34,7 @@ module ActiveSupport module TestHelper def setup @logger = MockLogger.new - @notifier = ActiveSupport::Notifications::Notifier.new(queue) + @notifier = ActiveSupport::Notifications::Fanout.new ActiveSupport::LogSubscriber.colorize_logging = false @@ -47,10 +48,14 @@ module ActiveSupport end class MockLogger + include ActiveSupport::BufferedLogger::Severity + attr_reader :flush_count + attr_accessor :level - def initialize + def initialize(level = DEBUG) @flush_count = 0 + @level = level @logged = Hash.new { |h,k| h[k] = [] } end @@ -65,6 +70,14 @@ module ActiveSupport def flush @flush_count += 1 end + + ActiveSupport::BufferedLogger::Severity.constants.each do |severity| + class_eval <<-EOT, __FILE__, __LINE__ + 1 + def #{severity.downcase}? + #{severity} >= @level + end + EOT + end end # Wait notifications to be published. @@ -81,10 +94,6 @@ module ActiveSupport def set_logger(logger) ActiveSupport::LogSubscriber.logger = logger end - - def queue - ActiveSupport::Notifications::Fanout.new - end end end -end
\ No newline at end of file +end diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index 1031662293..6c46b68eaf 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -47,11 +47,11 @@ module ActiveSupport def secure_compare(a, b) return false unless a.bytesize == b.bytesize - l = a.unpack "C*" + l = a.unpack "C#{a.bytesize}" - res = true - b.each_byte { |byte| res = (byte == l.shift) && res } - res + res = 0 + b.each_byte { |byte| res |= byte ^ l.shift } + res == 0 end def generate_digest(data) diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 51c2a0edac..019fb2df06 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -11,7 +11,7 @@ module ActiveSupport #:nodoc: # String methods are proxied through the Chars object, and can be accessed through the +mb_chars+ method. Methods # which would normally return a String object now return a Chars object so methods can be chained. # - # "The Perfect String ".mb_chars.downcase.strip.normalize #=> "the perfect string" + # "The Perfect String ".mb_chars.downcase.strip.normalize # => "the perfect string" # # Chars objects are perfectly interchangeable with String objects as long as no explicit class checks are made. # If certain methods do explicitly check the class, call +to_s+ before you pass chars objects to them. @@ -83,12 +83,13 @@ module ActiveSupport #:nodoc: include Comparable - # Returns <tt>-1</tt>, <tt>0</tt> or <tt>+1</tt> depending on whether the Chars object is to be sorted before, - # equal or after the object on the right side of the operation. It accepts any object that implements +to_s+. - # See <tt>String#<=></tt> for more details. + # Returns -1, 0, or 1, depending on whether the Chars object is to be sorted before, + # equal or after the object on the right side of the operation. It accepts any object + # that implements +to_s+: # - # Example: - # 'é'.mb_chars <=> 'ü'.mb_chars #=> -1 + # 'é'.mb_chars <=> 'ü'.mb_chars # => -1 + # + # See <tt>String#<=></tt> for more details. def <=>(other) @wrapped_string <=> other.to_s end @@ -103,7 +104,7 @@ module ActiveSupport #:nodoc: # 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" + # ('Café'.mb_chars + ' périferôl').to_s # => "Café périferôl" def +(other) chars(@wrapped_string + other) end @@ -111,7 +112,7 @@ module ActiveSupport #:nodoc: # 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 + # 'Café périferôl'.mb_chars =~ /ô/ # => 12 def =~(other) translate_offset(@wrapped_string =~ other) end @@ -119,7 +120,7 @@ module ActiveSupport #:nodoc: # Inserts the passed string at specified codepoint offsets. # # Example: - # 'Café'.mb_chars.insert(4, ' périferôl').to_s #=> "Café périferôl" + # '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 @@ -135,7 +136,7 @@ module ActiveSupport #:nodoc: # Returns +true+ if contained string contains _other_. Returns +false+ otherwise. # # Example: - # 'Café'.mb_chars.include?('é') #=> true + # 'Café'.mb_chars.include?('é') # => true def include?(other) # We have to redefine this method because Enumerable defines it. @wrapped_string.include?(other) @@ -144,8 +145,8 @@ module ActiveSupport #:nodoc: # 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 + # '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) @@ -157,8 +158,8 @@ module ActiveSupport #:nodoc: # 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 + # '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 @@ -190,7 +191,7 @@ module ActiveSupport #:nodoc: # Returns the codepoint of the first character in the string. # # Example: - # 'こんにちは'.mb_chars.ord #=> 12371 + # 'こんにちは'.mb_chars.ord # => 12371 def ord Unicode.u_unpack(@wrapped_string)[0] end @@ -200,10 +201,10 @@ module ActiveSupport #:nodoc: # Example: # # "¾ cup".mb_chars.rjust(8).to_s - # #=> " ¾ cup" + # # => " ¾ cup" # # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace - # #=> " ¾ cup" + # # => " ¾ cup" def rjust(integer, padstr=' ') justify(integer, :right, padstr) end @@ -213,10 +214,10 @@ module ActiveSupport #:nodoc: # Example: # # "¾ cup".mb_chars.rjust(8).to_s - # #=> "¾ cup " + # # => "¾ cup " # # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace - # #=> "¾ cup " + # # => "¾ cup " def ljust(integer, padstr=' ') justify(integer, :left, padstr) end @@ -226,10 +227,10 @@ module ActiveSupport #:nodoc: # Example: # # "¾ cup".mb_chars.center(8).to_s - # #=> " ¾ cup " + # # => " ¾ cup " # # "¾ cup".mb_chars.center(8, " ").to_s # Use non-breaking whitespace - # #=> " ¾ cup " + # # => " ¾ cup " def center(integer, padstr=' ') justify(integer, :center, padstr) end @@ -244,7 +245,7 @@ module ActiveSupport #:nodoc: # instances instead of String. This makes chaining methods easier. # # Example: - # 'Café périferôl'.mb_chars.split(/é/).map { |part| part.upcase.to_s } #=> ["CAF", " P", "RIFERÔL"] + # 'Café périferôl'.mb_chars.split(/é/).map { |part| part.upcase.to_s } # => ["CAF", " P", "RIFERÔL"] def split(*args) @wrapped_string.split(*args).map { |i| i.mb_chars } end @@ -256,12 +257,12 @@ module ActiveSupport #:nodoc: # s = "Müller" # s.mb_chars[2] = "e" # Replace character with offset 2 # s - # #=> "Müeler" + # # => "Müeler" # # s = "Müller" # s.mb_chars[1, 2] = "ö" # Replace 2 characters at character offset 1 # s - # #=> "Möler" + # # => "Möler" def []=(*args) replace_by = args.pop # Indexed replace with regular expressions already works @@ -292,7 +293,7 @@ module ActiveSupport #:nodoc: # Reverses all characters in the string. # # Example: - # 'Café'.mb_chars.reverse.to_s #=> 'éfaC' + # 'Café'.mb_chars.reverse.to_s # => 'éfaC' def reverse chars(Unicode.g_unpack(@wrapped_string).reverse.flatten.pack('U*')) end @@ -301,7 +302,7 @@ module ActiveSupport #:nodoc: # character. # # Example: - # 'こんにちは'.mb_chars.slice(2..3).to_s #=> "にち" + # 'こんにちは'.mb_chars.slice(2..3).to_s # => "にち" def slice(*args) if args.size > 2 raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" # Do as if we were native @@ -330,7 +331,7 @@ module ActiveSupport #:nodoc: # # Example: # s = 'こんにちは' - # s.mb_chars.limit(7) #=> "こに" + # s.mb_chars.limit(7) # => "こに" def limit(limit) slice(0...translate_offset(limit)) end @@ -338,7 +339,7 @@ module ActiveSupport #:nodoc: # Convert characters in the string to uppercase. # # Example: - # 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s #=> "LAURENT, OÙ SONT LES TESTS ?" + # 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?" def upcase chars(Unicode.apply_mapping @wrapped_string, :uppercase_mapping) end @@ -346,7 +347,7 @@ module ActiveSupport #:nodoc: # Convert characters in the string to lowercase. # # Example: - # 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s #=> "věda a výzkum" + # 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s # => "věda a výzkum" def downcase chars(Unicode.apply_mapping @wrapped_string, :lowercase_mapping) end @@ -354,7 +355,7 @@ module ActiveSupport #:nodoc: # Converts the first character to uppercase and the remainder to lowercase. # # Example: - # 'über'.mb_chars.capitalize.to_s #=> "Über" + # 'über'.mb_chars.capitalize.to_s # => "Über" def capitalize (slice(0) || chars('')).upcase + (slice(1..-1) || chars('')).downcase end @@ -382,8 +383,8 @@ module ActiveSupport #:nodoc: # Performs canonical decomposition on all the characters. # # Example: - # 'é'.length #=> 2 - # 'é'.mb_chars.decompose.to_s.length #=> 3 + # 'é'.length # => 2 + # 'é'.mb_chars.decompose.to_s.length # => 3 def decompose chars(Unicode.decompose_codepoints(:canonical, Unicode.u_unpack(@wrapped_string)).pack('U*')) end @@ -391,8 +392,8 @@ module ActiveSupport #:nodoc: # Performs composition on all the characters. # # Example: - # 'é'.length #=> 3 - # 'é'.mb_chars.compose.to_s.length #=> 2 + # 'é'.length # => 3 + # 'é'.mb_chars.compose.to_s.length # => 2 def compose chars(Unicode.compose_codepoints(Unicode.u_unpack(@wrapped_string)).pack('U*')) end @@ -400,8 +401,8 @@ module ActiveSupport #:nodoc: # Returns the number of grapheme clusters in the string. # # Example: - # 'क्षि'.mb_chars.length #=> 4 - # 'क्षि'.mb_chars.g_length #=> 3 + # 'क्षि'.mb_chars.length # => 4 + # 'क्षि'.mb_chars.g_length # => 3 def g_length Unicode.g_unpack(@wrapped_string).length end diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb index 11c72d873b..1139783b65 100644 --- a/activesupport/lib/active_support/multibyte/unicode.rb +++ b/activesupport/lib/active_support/multibyte/unicode.rb @@ -64,7 +64,7 @@ module ActiveSupport # valid UTF-8. # # Example: - # Unicode.u_unpack('Café') #=> [67, 97, 102, 233] + # Unicode.u_unpack('Café') # => [67, 97, 102, 233] def u_unpack(string) begin string.unpack 'U*' @@ -85,8 +85,8 @@ module ActiveSupport # Unpack the string at grapheme boundaries. Returns a list of character lists. # # Example: - # Unicode.g_unpack('क्षि') #=> [[2325, 2381], [2359], [2367]] - # Unicode.g_unpack('Café') #=> [[67], [97], [102], [233]] + # Unicode.g_unpack('क्षि') # => [[2325, 2381], [2359], [2367]] + # Unicode.g_unpack('Café') # => [[67], [97], [102], [233]] def g_unpack(string) codepoints = u_unpack(string) unpacked = [] @@ -99,15 +99,15 @@ module ActiveSupport current = codepoints[pos] if ( # CR X LF - one = ( previous == database.boundary[:cr] and current == database.boundary[:lf] ) or + ( previous == database.boundary[:cr] and current == database.boundary[:lf] ) or # L X (L|V|LV|LVT) - two = ( database.boundary[:l] === previous and in_char_class?(current, [:l,:v,:lv,:lvt]) ) or + ( database.boundary[:l] === previous and in_char_class?(current, [:l,:v,:lv,:lvt]) ) or # (LV|V) X (V|T) - three = ( in_char_class?(previous, [:lv,:v]) and in_char_class?(current, [:v,:t]) ) or + ( in_char_class?(previous, [:lv,:v]) and in_char_class?(current, [:v,:t]) ) or # (LVT|T) X (T) - four = ( in_char_class?(previous, [:lvt,:t]) and database.boundary[:t] === current ) or + ( in_char_class?(previous, [:lvt,:t]) and database.boundary[:t] === current ) or # X Extend - five = (database.boundary[:extend] === current) + (database.boundary[:extend] === current) ) else unpacked << codepoints[marker..pos-1] @@ -120,7 +120,7 @@ module ActiveSupport # Reverse operation of g_unpack. # # Example: - # Unicode.g_pack(Unicode.g_unpack('क्षि')) #=> 'क्षि' + # Unicode.g_pack(Unicode.g_unpack('क्षि')) # => 'क्षि' def g_pack(unpacked) (unpacked.flatten).pack('U*') end @@ -238,7 +238,6 @@ module ActiveSupport bytes.each_index do |i| byte = bytes[i] - is_ascii = byte < 128 is_cont = byte > 127 && byte < 192 is_lead = byte > 191 && byte < 245 is_unused = byte > 240 diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb index 1444fc1609..fd79188ba4 100644 --- a/activesupport/lib/active_support/notifications.rb +++ b/activesupport/lib/active_support/notifications.rb @@ -22,9 +22,9 @@ module ActiveSupport # end # # event = @events.first - # event.name #=> :render - # event.duration #=> 10 (in milliseconds) - # event.payload #=> { :extra => :information } + # 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: @@ -41,39 +41,37 @@ module ActiveSupport autoload :Event, 'active_support/notifications/instrumenter' autoload :Fanout, 'active_support/notifications/fanout' + @instrumenters = Hash.new { |h,k| h[k] = notifier.listening?(k) } + class << self attr_writer :notifier - delegate :publish, :subscribe, :unsubscribe, :to => :notifier - delegate :instrument, :to => :instrumenter - - def notifier - @notifier ||= Notifier.new - end - - def instrumenter - Thread.current[:"instrumentation_#{notifier.object_id}"] ||= Instrumenter.new(notifier) - end - end + delegate :publish, :to => :notifier - class Notifier - def initialize(queue = Fanout.new) - @queue = queue + def instrument(name, payload = {}) + if @instrumenters[name] + instrumenter.instrument(name, payload) { yield payload if block_given? } + else + yield payload if block_given? + end end - def publish(*args) - @queue.publish(*args) + def subscribe(*args, &block) + notifier.subscribe(*args, &block).tap do + @instrumenters.clear + end end - def subscribe(pattern = nil, &block) - @queue.bind(pattern).subscribe(&block) + def unsubscribe(*args) + notifier.unsubscribe(*args) + @instrumenters.clear end - def unsubscribe(subscriber) - @queue.unsubscribe(subscriber) + def notifier + @notifier ||= Fanout.new end - def wait - @queue.wait + def instrumenter + Thread.current[:"instrumentation_#{notifier.object_id}"] ||= Instrumenter.new(notifier) end end end diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index 300ec842a9..adc34f3286 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -8,85 +8,53 @@ module ActiveSupport @listeners_for = {} end - def bind(pattern) - Binding.new(self, pattern) - end - - def subscribe(pattern = nil, &block) + def subscribe(pattern = nil, block = Proc.new) + subscriber = Subscriber.new(pattern, block).tap do |s| + @subscribers << s + end @listeners_for.clear - @subscribers << Subscriber.new(pattern, &block) - @subscribers.last + subscriber end def unsubscribe(subscriber) - @listeners_for.clear @subscribers.reject! {|s| s.matches?(subscriber)} + @listeners_for.clear end def publish(name, *args) - if listeners = @listeners_for[name] - listeners.each { |s| s.publish(name, *args) } - else - @listeners_for[name] = @subscribers.select { |s| s.publish(name, *args) } - end + listeners_for(name).each { |s| s.publish(name, *args) } end - # This is a sync queue, so there is not waiting. - def wait + def listeners_for(name) + @listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) } end - # Used for internal implementation only. - class Binding #:nodoc: - def initialize(queue, pattern) - @queue = queue - @pattern = - case pattern - when Regexp, NilClass - pattern - else - /^#{Regexp.escape(pattern.to_s)}$/ - end - end + def listening?(name) + listeners_for(name).any? + end - def subscribe(&block) - @queue.subscribe(@pattern, &block) - end + # This is a sync queue, so there is not waiting. + def wait end class Subscriber #:nodoc: - def initialize(pattern, &block) + def initialize(pattern, delegate) @pattern = pattern - @block = block + @delegate = delegate end - def publish(*args) - return unless subscribed_to?(args.first) - push(*args) - true - end - - def drained? - true + def publish(message, *args) + @delegate.call(message, *args) end def subscribed_to?(name) - !@pattern || @pattern =~ name.to_s + !@pattern || @pattern === name.to_s end def matches?(subscriber_or_name) - case subscriber_or_name - when String - @pattern && @pattern =~ subscriber_or_name - when self - true - end + self === subscriber_or_name || + @pattern && @pattern === subscriber_or_name end - - private - - def push(*args) - @block.call(*args) - end end end end diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb index 7e89402822..441fefb491 100644 --- a/activesupport/lib/active_support/notifications/instrumenter.rb +++ b/activesupport/lib/active_support/notifications/instrumenter.rb @@ -15,14 +15,15 @@ module ActiveSupport # and publish it. Notice that events get sent even if an error occurs # in the passed-in block def instrument(name, payload={}) - time = Time.now + started = Time.now + begin - yield(payload) if block_given? + yield rescue Exception => e payload[:exception] = [e.class.name, e.message] raise e ensure - @notifier.publish(name, time, Time.now, @id, payload) + @notifier.publish(name, started, Time.now, @id, payload) end end @@ -33,7 +34,7 @@ module ActiveSupport end class Event - attr_reader :name, :time, :end, :transaction_id, :payload + attr_reader :name, :time, :end, :transaction_id, :payload, :duration def initialize(name, start, ending, transaction_id, payload) @name = name @@ -41,14 +42,11 @@ module ActiveSupport @time = start @transaction_id = transaction_id @end = ending - end - - def duration - @duration ||= 1000.0 * (@end - @time) + @duration = 1000.0 * (@end - @time) end def parent_of?(event) - start = (self.time - event.time) * 1000 + start = (time - event.time) * 1000 start <= 0 && (start + duration >= event.duration) end end diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index 6b563b9063..2e8d538d0b 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -4,8 +4,17 @@ YAML.add_builtin_type("omap") do |type, val| ActiveSupport::OrderedHash[val.map(&:to_a).map(&:first)] end -# OrderedHash is namespaced to prevent conflicts with other implementations module ActiveSupport + # The order of iteration over hashes in Ruby 1.8 is undefined. For example, you do not know the + # order in which +keys+ will return keys, or +each+ yield pairs. <tt>ActiveSupport::OrderedHash</tt> + # implements a hash that preserves insertion order, as in Ruby 1.9: + # + # oh = ActiveSupport::OrderedHash.new + # oh[:a] = 1 + # oh[:b] = 2 + # oh.keys # => [:a, :b], this order is guaranteed + # + # <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts with other implementations. class OrderedHash < ::Hash #:nodoc: def to_yaml_type "!tag:yaml.org,2002:omap" diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb index 61ccb79211..7fc2b45b51 100644 --- a/activesupport/lib/active_support/ordered_options.rb +++ b/activesupport/lib/active_support/ordered_options.rb @@ -1,5 +1,21 @@ require 'active_support/ordered_hash' +# Usually key value pairs are handled something like this: +# +# h = ActiveSupport::OrderedOptions.new +# h[:boy] = 'John' +# h[:girl] = 'Mary' +# h[:boy] # => 'John' +# h[:girl] # => 'Mary' +# +# Using <tt>OrderedOptions</tt> above code could be reduced to: +# +# h = ActiveSupport::OrderedOptions.new +# h.boy = 'John' +# h.girl = 'Mary' +# h.boy # => 'John' +# h.girl # => 'Mary' +# module ActiveSupport #:nodoc: class OrderedOptions < OrderedHash def []=(key, value) diff --git a/activesupport/lib/active_support/secure_random.rb b/activesupport/lib/active_support/secure_random.rb index cfbce4d754..73344498cb 100644 --- a/activesupport/lib/active_support/secure_random.rb +++ b/activesupport/lib/active_support/secure_random.rb @@ -26,25 +26,25 @@ module ActiveSupport # == Example # # # random hexadecimal string. - # p SecureRandom.hex(10) #=> "52750b30ffbc7de3b362" - # p SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559" - # p SecureRandom.hex(11) #=> "6aca1b5c58e4863e6b81b8" - # p SecureRandom.hex(12) #=> "94b2fff3e7fd9b9c391a2306" - # p SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23" + # p SecureRandom.hex(10) # => "52750b30ffbc7de3b362" + # p SecureRandom.hex(10) # => "92b15d6c8dc4beb5f559" + # p SecureRandom.hex(11) # => "6aca1b5c58e4863e6b81b8" + # p SecureRandom.hex(12) # => "94b2fff3e7fd9b9c391a2306" + # p SecureRandom.hex(13) # => "39b290146bea6ce975c37cfc23" # ... # # # random base64 string. - # p SecureRandom.base64(10) #=> "EcmTPZwWRAozdA==" - # p SecureRandom.base64(10) #=> "9b0nsevdwNuM/w==" - # p SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg==" - # p SecureRandom.base64(11) #=> "l7XEiFja+8EKEtY=" - # p SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8" - # p SecureRandom.base64(13) #=> "vKLJ0tXBHqQOuIcSIg==" + # p SecureRandom.base64(10) # => "EcmTPZwWRAozdA==" + # p SecureRandom.base64(10) # => "9b0nsevdwNuM/w==" + # p SecureRandom.base64(10) # => "KO1nIU+p9DKxGg==" + # p SecureRandom.base64(11) # => "l7XEiFja+8EKEtY=" + # p SecureRandom.base64(12) # => "7kJSM/MzBJI+75j8" + # p SecureRandom.base64(13) # => "vKLJ0tXBHqQOuIcSIg==" # ... # # # random binary string. - # p SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301" - # p SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337" + # p SecureRandom.random_bytes(10) # => "\016\t{\370g\310pbr\301" + # p SecureRandom.random_bytes(10) # => "\323U\030TO\234\357\020\a\337" # ... module SecureRandom # SecureRandom.random_bytes generates a random binary string. diff --git a/activesupport/lib/active_support/testing/setup_and_teardown.rb b/activesupport/lib/active_support/testing/setup_and_teardown.rb index d8942c3974..b2d9ddfeb7 100644 --- a/activesupport/lib/active_support/testing/setup_and_teardown.rb +++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb @@ -96,7 +96,7 @@ module ActiveSupport protected def retrieve_mocha_counter(result) #:nodoc: - if using_mocha = respond_to?(:mocha_verify) + if respond_to?(:mocha_verify) # using mocha if defined?(Mocha::TestCaseAdapter::AssertionCounter) Mocha::TestCaseAdapter::AssertionCounter.new(result) else diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 62d02bdeb6..ad6c3de1f5 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -5,9 +5,9 @@ module ActiveSupport # A Time-like class that can represent a time in any time zone. Necessary because standard Ruby Time instances are # limited to UTC and the system's <tt>ENV['TZ']</tt> zone. # - # You shouldn't ever need to create a TimeWithZone instance directly via <tt>new</tt> -- instead, Rails provides the methods - # +local+, +parse+, +at+ and +now+ on TimeZone instances, and +in_time_zone+ on Time and DateTime instances, for a more - # user-friendly syntax. Examples: + # You shouldn't ever need to create a TimeWithZone instance directly via <tt>new</tt> . Instead use methods + # +local+, +parse+, +at+ and +now+ on TimeZone instances, and +in_time_zone+ on Time and DateTime instances. + # Examples: # # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)' # Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45 EST -05:00 @@ -18,7 +18,8 @@ module ActiveSupport # # See Time and TimeZone for further documentation of these methods. # - # TimeWithZone instances implement the same API as Ruby Time instances, so that Time and TimeWithZone instances are interchangeable. Examples: + # TimeWithZone instances implement the same API as Ruby Time instances, so that Time and TimeWithZone instances are interchangeable. + # Examples: # # t = Time.zone.now # => Sun, 18 May 2008 13:27:25 EDT -04:00 # t.hour # => 13 @@ -113,8 +114,8 @@ module ActiveSupport end alias_method :iso8601, :xmlschema - # Coerces the date to a string for JSON encoding. The default format is ISO 8601. You can get - # %Y/%m/%d %H:%M:%S +offset style by setting ActiveSupport::JSON::Encoding.use_standard_json_time_format + # Coerces time to a string for JSON encoding. The default format is ISO 8601. You can get + # %Y/%m/%d %H:%M:%S +offset style by setting <tt>ActiveSupport::JSON::Encoding.use_standard_json_time_format</tt> # to false. # # ==== Examples diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 49dd8a1b99..abd585b64f 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -8,10 +8,10 @@ require 'active_support/core_ext/object/try' # * Lazily load TZInfo::Timezone instances only when they're needed. # * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+, +parse+, +at+ and +now+ methods. # -# If you set <tt>config.time_zone</tt> in the Rails Initializer, you can access this TimeZone object via <tt>Time.zone</tt>: +# If you set <tt>config.time_zone</tt> in the Rails Application, you can access this TimeZone object via <tt>Time.zone</tt>: # -# # environment.rb: -# Rails::Initializer.run do |config| +# # application.rb: +# class Application < Rails::Application # config.time_zone = "Eastern Time (US & Canada)" # end # diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index 52612c27cb..9d2cf13260 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -3,7 +3,7 @@ module ActiveSupport MAJOR = 3 MINOR = 0 TINY = 0 - BUILD = "beta4" + BUILD = "rc" STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb index 7594d7b68b..352172027b 100644 --- a/activesupport/lib/active_support/xml_mini.rb +++ b/activesupport/lib/active_support/xml_mini.rb @@ -9,7 +9,7 @@ module ActiveSupport module XmlMini extend self - # This module exists to decorate files deserialized using Hash.from_xml with + # This module decorates files deserialized using Hash.from_xml with # the <tt>original_filename</tt> and <tt>content_type</tt> methods. module FileLike #:nodoc: attr_writer :original_filename, :content_type @@ -129,12 +129,16 @@ module ActiveSupport camelize = options.has_key?(:camelize) && options[:camelize] dasherize = !options.has_key?(:dasherize) || options[:dasherize] key = key.camelize if camelize - key = key.dasherize if dasherize + key = _dasherize(key) if dasherize key end protected + def _dasherize(key) + key.gsub(/(?!^[_]*)_(?![_]*$)/, '-') + end + # TODO: Add support for other encodings def _parse_binary(bin, entity) #:nodoc: case entity['encoding'] diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb index 9cf187302f..7fdcb11465 100644 --- a/activesupport/lib/active_support/xml_mini/libxml.rb +++ b/activesupport/lib/active_support/xml_mini/libxml.rb @@ -1,5 +1,4 @@ require 'libxml' -require 'active_support/core_ext/object/returning' require 'active_support/core_ext/object/blank' # = XmlMini LibXML implementation diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb index eb61a7fc22..e03a744257 100644 --- a/activesupport/lib/active_support/xml_mini/nokogiri.rb +++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb @@ -1,4 +1,9 @@ -require 'nokogiri' +begin + require 'nokogiri' +rescue LoadError => e + $stderr.puts "You don't have nokogiri installed in your application. Please add it to your Gemfile and run bundle install" + raise e +end require 'active_support/core_ext/object/blank' # = XmlMini Nokogiri implementation diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb index 8af7b5e565..38c8685390 100644 --- a/activesupport/lib/active_support/xml_mini/nokogirisax.rb +++ b/activesupport/lib/active_support/xml_mini/nokogirisax.rb @@ -1,4 +1,9 @@ -require 'nokogiri' +begin + require 'nokogiri' +rescue LoadError => e + $stderr.puts "You don't have nokogiri installed in your application. Please add it to your Gemfile and run bundle install" + raise e +end require 'active_support/core_ext/object/blank' # = XmlMini Nokogiri implementation using a SAX-based parser @@ -80,4 +85,4 @@ module ActiveSupport end end end -end
\ No newline at end of file +end diff --git a/activesupport/test/autoloading_fixtures/load_path/loaded_constant.rb b/activesupport/test/autoloading_fixtures/load_path/loaded_constant.rb new file mode 100644 index 0000000000..e3d1218c96 --- /dev/null +++ b/activesupport/test/autoloading_fixtures/load_path/loaded_constant.rb @@ -0,0 +1,3 @@ +module LoadedConstant +end + diff --git a/activesupport/test/autoloading_fixtures/loads_constant.rb b/activesupport/test/autoloading_fixtures/loads_constant.rb new file mode 100644 index 0000000000..0b30dc8bca --- /dev/null +++ b/activesupport/test/autoloading_fixtures/loads_constant.rb @@ -0,0 +1,5 @@ +module LoadsConstant +end + +# The _ = assignment is to prevent warnings +_ = RequiresConstant diff --git a/activesupport/test/autoloading_fixtures/requires_constant.rb b/activesupport/test/autoloading_fixtures/requires_constant.rb new file mode 100644 index 0000000000..14804a0de0 --- /dev/null +++ b/activesupport/test/autoloading_fixtures/requires_constant.rb @@ -0,0 +1,5 @@ +require "loaded_constant" + +module RequiresConstant +end + diff --git a/activesupport/test/buffered_logger_test.rb b/activesupport/test/buffered_logger_test.rb index 850febb959..97c0ef14db 100644 --- a/activesupport/test/buffered_logger_test.rb +++ b/activesupport/test/buffered_logger_test.rb @@ -1,9 +1,12 @@ require 'abstract_unit' +require 'multibyte_test_helpers' require 'stringio' require 'fileutils' require 'active_support/buffered_logger' class BufferedLoggerTest < Test::Unit::TestCase + include MultibyteTestHelpers + Logger = ActiveSupport::BufferedLogger def setup @@ -146,4 +149,16 @@ class BufferedLoggerTest < Test::Unit::TestCase @logger.expects :clear_buffer @logger.flush end + + def test_buffer_multibyte + @logger.auto_flushing = 2 + @logger.info(UNICODE_STRING) + @logger.info(BYTE_STRING) + assert @output.string.include?(UNICODE_STRING) + byte_string = @output.string.dup + if byte_string.respond_to?(:force_encoding) + byte_string.force_encoding("ASCII-8BIT") + end + assert byte_string.include?(BYTE_STRING) + end end diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 3e14c754b7..b79a7bbaec 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -7,6 +7,49 @@ class CacheKeyTest < ActiveSupport::TestCase assert_equal '1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true]) assert_equal 'name/1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true], :name) end + + def test_expand_cache_key_with_rails_cache_id + begin + ENV['RAILS_CACHE_ID'] = 'c99' + assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key(:foo) + assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key([:foo]) + assert_equal 'c99/c99/foo/c99/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar]) + assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key(:foo, :nm) + assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key([:foo], :nm) + assert_equal 'nm/c99/c99/foo/c99/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar], :nm) + ensure + ENV['RAILS_CACHE_ID'] = nil + end + end + + def test_expand_cache_key_with_rails_app_version + begin + ENV['RAILS_APP_VERSION'] = 'rails3' + assert_equal 'rails3/foo', ActiveSupport::Cache.expand_cache_key(:foo) + ensure + ENV['RAILS_APP_VERSION'] = nil + end + end + + def test_expand_cache_key_rails_cache_id_should_win_over_rails_app_version + begin + ENV['RAILS_CACHE_ID'] = 'c99' + ENV['RAILS_APP_VERSION'] = 'rails3' + assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key(:foo) + ensure + ENV['RAILS_CACHE_ID'] = nil + ENV['RAILS_APP_VERSION'] = nil + end + end + + def test_respond_to_cache_key + key = 'foo' + def key.cache_key + :foo_key + end + assert_equal 'foo_key', ActiveSupport::Cache.expand_cache_key(key) + end + end class CacheStoreSettingTest < ActiveSupport::TestCase @@ -279,7 +322,7 @@ module CacheStoreBehavior assert_equal 'bar', @cache.read('foo') raise ArgumentError.new end - rescue ArgumentError => e + rescue ArgumentError end assert_equal "bar", @cache.read('foo') Time.stubs(:now).returns(time + 71) diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb index 54376deee5..009a254c64 100644 --- a/activesupport/test/core_ext/array_ext_test.rb +++ b/activesupport/test/core_ext/array_ext_test.rb @@ -398,6 +398,18 @@ class ArrayWrapperTests < Test::Unit::TestCase def method_missing(*a) @target.send(*a) end end + class DoubtfulToAry + def to_ary + :not_an_array + end + end + + class NilToAry + def to_ary + nil + end + end + def test_array ary = %w(foo bar) assert_same ary, Array.wrap(ary) @@ -438,4 +450,12 @@ class ArrayWrapperTests < Test::Unit::TestCase o = Struct.new(:foo).new(123) assert_equal [o], Array.wrap(o) end + + def test_wrap_returns_nil_if_to_ary_returns_nil + assert_nil Array.wrap(NilToAry.new) + end + + def test_wrap_does_not_complain_if_to_ary_does_not_return_an_array + assert_equal DoubtfulToAry.new.to_ary, Array.wrap(DoubtfulToAry.new) + end end diff --git a/activesupport/test/core_ext/class_test.rb b/activesupport/test/core_ext/class_test.rb index 08bb13dd35..60ba3b8f88 100644 --- a/activesupport/test/core_ext/class_test.rb +++ b/activesupport/test/core_ext/class_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'active_support/core_ext/class' +require 'set' class ClassTest < Test::Unit::TestCase class Parent; end @@ -12,16 +13,16 @@ class ClassTest < Test::Unit::TestCase class C < B; end def test_descendants - assert_equal [Foo, Bar, Baz, A, B, C], Parent.descendants - assert_equal [Bar, Baz], Foo.descendants + assert_equal [Foo, Bar, Baz, A, B, C].to_set, Parent.descendants.to_set + assert_equal [Bar, Baz].to_set, Foo.descendants.to_set assert_equal [Baz], Bar.descendants assert_equal [], Baz.descendants end def test_subclasses - assert_equal [Foo, A], Parent.subclasses + assert_equal [Foo, A].to_set, Parent.subclasses.to_set assert_equal [Bar], Foo.subclasses assert_equal [Baz], Bar.subclasses assert_equal [], Baz.subclasses end -end
\ No newline at end of file +end diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index 19d7935211..e8506f5222 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -171,22 +171,29 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase end def test_advance - assert_equal DateTime.civil(2006,2,28,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:years => 1) - assert_equal DateTime.civil(2005,6,28,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:months => 4) - assert_equal DateTime.civil(2005,3,21,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:weeks => 3) - assert_equal DateTime.civil(2005,3,5,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:days => 5) - assert_equal DateTime.civil(2012,9,28,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:years => 7, :months => 7) - assert_equal DateTime.civil(2013,10,3,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :days => 5) + assert_equal DateTime.civil(2006,2,28,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:years => 1) + assert_equal DateTime.civil(2005,6,28,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:months => 4) + assert_equal DateTime.civil(2005,3,21,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:weeks => 3) + assert_equal DateTime.civil(2005,3,5,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:days => 5) + assert_equal DateTime.civil(2012,9,28,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:years => 7, :months => 7) + assert_equal DateTime.civil(2013,10,3,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :days => 5) assert_equal DateTime.civil(2013,10,17,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5) assert_equal DateTime.civil(2001,12,27,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:years => -3, :months => -2, :days => -1) - assert_equal DateTime.civil(2005,2,28,15,15,10), DateTime.civil(2004,2,29,15,15,10).advance(:years => 1) #leap day plus one year - assert_equal DateTime.civil(2005,2,28,20,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:hours => 5) - assert_equal DateTime.civil(2005,2,28,15,22,10), DateTime.civil(2005,2,28,15,15,10).advance(:minutes => 7) - assert_equal DateTime.civil(2005,2,28,15,15,19), DateTime.civil(2005,2,28,15,15,10).advance(:seconds => 9) - assert_equal DateTime.civil(2005,2,28,20,22,19), DateTime.civil(2005,2,28,15,15,10).advance(:hours => 5, :minutes => 7, :seconds => 9) - assert_equal DateTime.civil(2005,2,28,10,8,1), DateTime.civil(2005,2,28,15,15,10).advance(:hours => -5, :minutes => -7, :seconds => -9) + assert_equal DateTime.civil(2005,2,28,15,15,10), DateTime.civil(2004,2,29,15,15,10).advance(:years => 1) #leap day plus one year + assert_equal DateTime.civil(2005,2,28,20,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:hours => 5) + assert_equal DateTime.civil(2005,2,28,15,22,10), DateTime.civil(2005,2,28,15,15,10).advance(:minutes => 7) + assert_equal DateTime.civil(2005,2,28,15,15,19), DateTime.civil(2005,2,28,15,15,10).advance(:seconds => 9) + assert_equal DateTime.civil(2005,2,28,20,22,19), DateTime.civil(2005,2,28,15,15,10).advance(:hours => 5, :minutes => 7, :seconds => 9) + assert_equal DateTime.civil(2005,2,28,10,8,1), DateTime.civil(2005,2,28,15,15,10).advance(:hours => -5, :minutes => -7, :seconds => -9) assert_equal DateTime.civil(2013,10,17,20,22,19), DateTime.civil(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5, :hours => 5, :minutes => 7, :seconds => 9) + end + def test_advanced_processes_first_the_date_deltas_and_then_the_time_deltas + # If the time deltas were processed first, the following datetimes would be advanced to 2010/04/01 instead. + assert_equal DateTime.civil(2010, 3, 29), DateTime.civil(2010, 2, 28, 23, 59, 59).advance(:months => 1, :seconds => 1) + assert_equal DateTime.civil(2010, 3, 29), DateTime.civil(2010, 2, 28, 23, 59).advance(:months => 1, :minutes => 1) + assert_equal DateTime.civil(2010, 3, 29), DateTime.civil(2010, 2, 28, 23).advance(:months => 1, :hours => 1) + assert_equal DateTime.civil(2010, 3, 29), DateTime.civil(2010, 2, 28, 22, 58, 59).advance(:months => 1, :hours => 1, :minutes => 1, :seconds => 1) end def test_next_week diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 7b2c10908f..5d9846a216 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -2,6 +2,8 @@ require 'abstract_unit' require 'active_support/core_ext/hash' require 'bigdecimal' require 'active_support/core_ext/string/access' +require 'active_support/ordered_hash' +require 'active_support/core_ext/object/conversions' class HashExtTest < Test::Unit::TestCase def setup @@ -449,6 +451,33 @@ class IWriteMyOwnXML end end +class HashExtToParamTests < Test::Unit::TestCase + class ToParam < String + def to_param + "#{self}-1" + end + end + + def test_string_hash + assert_equal '', {}.to_param + assert_equal 'hello=world', { :hello => "world" }.to_param + assert_equal 'hello=10', { "hello" => 10 }.to_param + assert_equal 'hello=world&say_bye=true', ActiveSupport::OrderedHash[:hello, "world", "say_bye", true].to_param + end + + def test_number_hash + assert_equal '10=20&30=40&50=60', ActiveSupport::OrderedHash[10, 20, 30, 40, 50, 60].to_param + end + + def test_to_param_hash + assert_equal 'custom=param-1&custom2=param2-1', ActiveSupport::OrderedHash[ToParam.new('custom'), ToParam.new('param'), ToParam.new('custom2'), ToParam.new('param2')].to_param + end + + def test_to_param_hash_escapes_its_keys_and_values + assert_equal 'param+1=A+string+with+%2F+characters+%26+that+should+be+%3F+escaped', { 'param 1' => 'A string with / characters & that should be ? escaped' }.to_param + end +end + class HashToXmlTest < Test::Unit::TestCase def setup @xml_options = { :root => :person, :skip_instruct => true, :indent => 0 } diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb index 39ee0ac748..5d9cdf22c2 100644 --- a/activesupport/test/core_ext/module_test.rb +++ b/activesupport/test/core_ext/module_test.rb @@ -147,7 +147,7 @@ class ModuleTest < Test::Unit::TestCase end assert_nothing_raised do - child = Class.new(parent) do + Class.new(parent) do class << self delegate :parent_method, :to => :superclass end diff --git a/activesupport/test/core_ext/object/to_query_test.rb b/activesupport/test/core_ext/object/to_query_test.rb index 4d655913cc..e28b4cd493 100644 --- a/activesupport/test/core_ext/object/to_query_test.rb +++ b/activesupport/test/core_ext/object/to_query_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/ordered_hash' require 'active_support/core_ext/object/to_query' class ToQueryTest < Test::Unit::TestCase @@ -18,12 +19,12 @@ class ToQueryTest < Test::Unit::TestCase def test_nested_conversion assert_query_equal 'person[login]=seckar&person[name]=Nicholas', - :person => {:name => 'Nicholas', :login => 'seckar'} + :person => ActiveSupport::OrderedHash[:login, 'seckar', :name, 'Nicholas'] end def test_multiple_nested assert_query_equal 'account[person][id]=20&person[id]=10', - :person => {:id => 10}, :account => {:person => {:id => 20}} + ActiveSupport::OrderedHash[:account, {:person => {:id => 20}}, :person, {:id => 10}] end def test_array_values diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index c7088638c7..f98d823f00 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -117,6 +117,7 @@ class DependenciesTest < Test::Unit::TestCase assert_equal true, $checked_verbose, 'After first load warnings should be left alone.' assert ActiveSupport::Dependencies.loaded.include?(expanded) + ActiveSupport::Dependencies.warnings_on_first_load = old_warnings end end @@ -212,6 +213,50 @@ class DependenciesTest < Test::Unit::TestCase end end + def test_doesnt_break_normal_require + path = File.expand_path("../autoloading_fixtures/load_path", __FILE__) + original_path = $:.dup + original_features = $".dup + $:.push(path) + + with_autoloading_fixtures do + # The _ = assignments are to prevent warnings + _ = RequiresConstant + assert defined?(RequiresConstant) + assert defined?(LoadedConstant) + ActiveSupport::Dependencies.clear + _ = RequiresConstant + assert defined?(RequiresConstant) + assert defined?(LoadedConstant) + end + ensure + remove_constants(:RequiresConstant, :LoadedConstant, :LoadsConstant) + $".replace(original_features) + $:.replace(original_path) + end + + def test_doesnt_break_normal_require_nested + path = File.expand_path("../autoloading_fixtures/load_path", __FILE__) + original_path = $:.dup + original_features = $".dup + $:.push(path) + + with_autoloading_fixtures do + # The _ = assignments are to prevent warnings + _ = LoadsConstant + assert defined?(LoadsConstant) + assert defined?(LoadedConstant) + ActiveSupport::Dependencies.clear + _ = LoadsConstant + assert defined?(LoadsConstant) + assert defined?(LoadedConstant) + end + ensure + remove_constants(:RequiresConstant, :LoadedConstant, :LoadsConstant) + $".replace(original_features) + $:.replace(original_path) + end + def failing_test_access_thru_and_upwards_fails with_autoloading_fixtures do assert ! defined?(ModuleFolder) @@ -418,7 +463,6 @@ class DependenciesTest < Test::Unit::TestCase def test_removal_from_tree_should_be_detected with_loading 'dependencies' do - root = ActiveSupport::Dependencies.autoload_paths.first c = ServiceOne ActiveSupport::Dependencies.clear assert ! defined?(ServiceOne) @@ -433,7 +477,6 @@ class DependenciesTest < Test::Unit::TestCase def test_references_should_work with_loading 'dependencies' do - root = ActiveSupport::Dependencies.autoload_paths.first c = ActiveSupport::Dependencies.ref("ServiceOne") service_one_first = ServiceOne assert_equal service_one_first, c.get @@ -798,4 +841,11 @@ class DependenciesTest < Test::Unit::TestCase ensure ActiveSupport::Dependencies.hook! end + +private + def remove_constants(*constants) + constants.each do |constant| + Object.send(:remove_const, constant) if Object.const_defined?(constant) + end + end end diff --git a/activesupport/test/deprecation/proxy_wrappers_test.rb b/activesupport/test/deprecation/proxy_wrappers_test.rb new file mode 100644 index 0000000000..aa887f274d --- /dev/null +++ b/activesupport/test/deprecation/proxy_wrappers_test.rb @@ -0,0 +1,22 @@ +require 'abstract_unit' +require 'active_support/deprecation' + +class ProxyWrappersTest < Test::Unit::TestCase + Waffles = false + NewWaffles = :hamburgers + + def test_deprecated_object_proxy_doesnt_wrap_falsy_objects + proxy = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(nil, "message") + assert !proxy + end + + def test_deprecated_instance_variable_proxy_doesnt_wrap_falsy_objects + proxy = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(nil, :waffles) + assert !proxy + end + + def test_deprecated_constant_proxy_doesnt_wrap_falsy_objects + proxy = ActiveSupport::Deprecation::DeprecatedConstantProxy.new(Waffles, NewWaffles) + assert !proxy + end +end diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index a679efb41e..1527d02d16 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -148,7 +148,7 @@ class TestJSONEncoding < Test::Unit::TestCase :latitude => 123.234 } } - result = ActiveSupport::JSON.encode(hash) + ActiveSupport::JSON.encode(hash) end end diff --git a/activesupport/test/load_paths_test.rb b/activesupport/test/load_paths_test.rb index 9c83d6f061..36e3726a02 100644 --- a/activesupport/test/load_paths_test.rb +++ b/activesupport/test/load_paths_test.rb @@ -8,8 +8,8 @@ class LoadPathsTest < Test::Unit::TestCase paths[expanded_path] += 1 paths } + load_paths_count[File.expand_path('../../lib', __FILE__)] -= 1 - # CI has a bunch of duplicate load paths - # assert_equal [], load_paths_count.select { |k, v| v > 1 }, $LOAD_PATH.inspect + assert load_paths_count.select { |k, v| v > 1 }.empty?, $LOAD_PATH.inspect end end diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb index 195e3eaa42..b11fa8d346 100644 --- a/activesupport/test/memoizable_test.rb +++ b/activesupport/test/memoizable_test.rb @@ -134,7 +134,6 @@ class MemoizableTest < ActiveSupport::TestCase end def test_reloadable - counter = @calculator.counter assert_equal 1, @calculator.counter assert_equal 2, @calculator.counter(:reload) assert_equal 2, @calculator.counter diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb index 78232d8eb5..6ebbfdf334 100644 --- a/activesupport/test/multibyte_chars_test.rb +++ b/activesupport/test/multibyte_chars_test.rb @@ -234,7 +234,7 @@ class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase def test_include_raises_when_nil_is_passed @chars.include?(nil) flunk "Expected chars.include?(nil) to raise TypeError or NoMethodError" - rescue Exception => e + rescue Exception end def test_index_should_return_character_offset diff --git a/activesupport/test/multibyte_test_helpers.rb b/activesupport/test/multibyte_test_helpers.rb index 597f949059..8839b75601 100644 --- a/activesupport/test/multibyte_test_helpers.rb +++ b/activesupport/test/multibyte_test_helpers.rb @@ -4,6 +4,9 @@ module MultibyteTestHelpers UNICODE_STRING = 'こにちわ' ASCII_STRING = 'ohayo' BYTE_STRING = "\270\236\010\210\245" + if BYTE_STRING.respond_to?(:force_encoding) + BYTE_STRING.force_encoding("ASCII-8BIT") + end def chars(str) ActiveSupport::Multibyte::Chars.new(str) @@ -16,4 +19,4 @@ module MultibyteTestHelpers def assert_equal_codepoints(expected, actual, message=nil) assert_equal(inspect_codepoints(expected), inspect_codepoints(actual), message) end -end
\ No newline at end of file +end diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index e11de5f67a..9faa11efbc 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -11,14 +11,11 @@ module Notifications @named_subscription = @notifier.subscribe("named.subscription") { |*args| @named_events << event(*args) } end - private - def event(*args) - ActiveSupport::Notifications::Event.new(*args) - end + private - def drain - @notifier.wait - end + def event(*args) + ActiveSupport::Notifications::Event.new(*args) + end end class UnsubscribeTest < TestCase @@ -132,13 +129,10 @@ module Notifications def test_instrument_returns_block_result assert_equal 2, instrument(:awesome) { 1 + 1 } - drain end def test_instrument_yields_the_paylod_for_further_modification assert_equal 2, instrument(:awesome) { |p| p[:result] = 1 + 1 } - drain - assert_equal 1, @events.size assert_equal :awesome, @events.first.name assert_equal Hash[:result => 2], @events.first.payload @@ -154,15 +148,11 @@ module Notifications 1 + 1 end - drain - assert_equal 1, @events.size assert_equal :wot, @events.first.name assert_equal Hash[:payload => "child"], @events.first.payload end - drain - assert_equal 2, @events.size assert_equal :awesome, @events.last.name assert_equal Hash[:payload => "notifications"], @events.last.payload @@ -177,7 +167,6 @@ module Notifications assert_equal "FAIL", e.message end - drain assert_equal 1, @events.size assert_equal Hash[:payload => "notifications", :exception => ["RuntimeError", "FAIL"]], @events.last.payload @@ -185,8 +174,6 @@ module Notifications def test_event_is_pushed_even_without_block instrument(:awesome, :payload => "notifications") - drain - assert_equal 1, @events.size assert_equal :awesome, @events.last.name assert_equal Hash[:payload => "notifications"], @events.last.payload @@ -198,9 +185,9 @@ module Notifications time = Time.now event = event(:foo, time, time + 0.01, random_id, {}) - assert_equal :foo, event.name - assert_equal time, event.time - assert_equal 10.0, event.duration + assert_equal :foo, event.name + assert_equal time, event.time + assert_in_delta 10.0, event.duration, 0.00001 end def test_events_consumes_information_given_as_payload diff --git a/activesupport/test/option_merger_test.rb b/activesupport/test/option_merger_test.rb index b898292c9c..33e3e69666 100644 --- a/activesupport/test/option_merger_test.rb +++ b/activesupport/test/option_merger_test.rb @@ -1,5 +1,5 @@ require 'abstract_unit' -require 'active_support/core_ext/object/misc' +require 'active_support/core_ext/object/with_options' class OptionMergerTest < Test::Unit::TestCase def setup diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index d340bed444..f47e896487 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -214,7 +214,7 @@ class OrderedHashTest < Test::Unit::TestCase def test_alternate_initialization_raises_exception_on_odd_length_args begin - alternate = ActiveSupport::OrderedHash[1,2,3,4,5] + ActiveSupport::OrderedHash[1,2,3,4,5] flunk "Hash::[] should have raised an exception on initialization " + "with an odd number of parameters" rescue diff --git a/activesupport/test/rescuable_test.rb b/activesupport/test/rescuable_test.rb index ff77e16edd..1c74ce8b2a 100644 --- a/activesupport/test/rescuable_test.rb +++ b/activesupport/test/rescuable_test.rb @@ -9,11 +9,16 @@ end class MadRonon < StandardError end +class CoolError < StandardError +end + class Stargate attr_accessor :result include ActiveSupport::Rescuable + rescue_from WraithAttack, :with => :sos_first + rescue_from WraithAttack, :with => :sos rescue_from NuclearExplosion do @@ -45,11 +50,30 @@ class Stargate def sos @result = 'killed' end + + def sos_first + @result = 'sos_first' + end + +end + +class CoolStargate < Stargate + attr_accessor :result + + include ActiveSupport::Rescuable + + rescue_from CoolError, :with => :sos_cool_error + + def sos_cool_error + @result = 'sos_cool_error' + end end + class RescueableTest < Test::Unit::TestCase def setup @stargate = Stargate.new + @cool_stargate = CoolStargate.new end def test_rescue_from_with_method @@ -66,4 +90,17 @@ class RescueableTest < Test::Unit::TestCase @stargate.dispatch :ronanize assert_equal 'dex', @stargate.result end + + def test_rescues_defined_later_are_added_at_end_of_the_rescue_handlers_array + expected = ["WraithAttack", "WraithAttack", "NuclearExplosion", "MadRonon"] + result = @stargate.send(:rescue_handlers).collect {|e| e.first} + assert_equal expected, result + end + + def test_children_should_inherit_rescue_defintions_from_parents_and_child_rescue_should_be_appended + expected = ["WraithAttack", "WraithAttack", "NuclearExplosion", "MadRonon", "CoolError"] + result = @cool_stargate.send(:rescue_handlers).collect {|e| e.first} + assert_equal expected, result + end + end diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb index 3092fe01ae..633d3b212b 100644 --- a/activesupport/test/test_test.rb +++ b/activesupport/test/test_test.rb @@ -49,8 +49,8 @@ class AssertDifferenceTest < ActiveSupport::TestCase end def test_expression_is_evaluated_in_the_appropriate_scope - local_scope = 'foo' silence_warnings do + local_scope = 'foo' assert_difference('local_scope; @object.num') { @object.increment } end end diff --git a/activesupport/test/test_xml_mini.rb b/activesupport/test/test_xml_mini.rb new file mode 100644 index 0000000000..585eb15c6e --- /dev/null +++ b/activesupport/test/test_xml_mini.rb @@ -0,0 +1,49 @@ +require 'abstract_unit' +require 'active_support/xml_mini' + +class XmlMiniTest < Test::Unit::TestCase + def test_rename_key_dasherizes_by_default + assert_equal "my-key", ActiveSupport::XmlMini.rename_key("my_key") + end + + def test_rename_key_does_nothing_with_dasherize_true + assert_equal "my-key", ActiveSupport::XmlMini.rename_key("my_key", :dasherize => true) + end + + def test_rename_key_does_nothing_with_dasherize_false + assert_equal "my_key", ActiveSupport::XmlMini.rename_key("my_key", :dasherize => false) + end + + def test_rename_key_camelizes_with_camelize_true + assert_equal "MyKey", ActiveSupport::XmlMini.rename_key("my_key", :camelize => true) + end + + def test_rename_key_camelizes_with_camelize_true + assert_equal "MyKey", ActiveSupport::XmlMini.rename_key("my_key", :camelize => true) + end + + def test_rename_key_does_not_dasherize_leading_underscores + assert_equal "_id", ActiveSupport::XmlMini.rename_key("_id") + end + + def test_rename_key_with_leading_underscore_dasherizes_interior_underscores + assert_equal "_my-key", ActiveSupport::XmlMini.rename_key("_my_key") + end + + def test_rename_key_does_not_dasherize_trailing_underscores + assert_equal "id_", ActiveSupport::XmlMini.rename_key("id_") + end + + def test_rename_key_with_trailing_underscore_dasherizes_interior_underscores + assert_equal "my-key_", ActiveSupport::XmlMini.rename_key("my_key_") + end + + def test_rename_key_does_not_dasherize_multiple_leading_underscores + assert_equal "__id", ActiveSupport::XmlMini.rename_key("__id") + end + + def test_rename_key_does_not_dasherize_multiple_leading_underscores + assert_equal "id__", ActiveSupport::XmlMini.rename_key("id__") + end + +end |