diff options
author | Jon Leighton <j@jonathanleighton.com> | 2011-03-02 21:24:56 +0000 |
---|---|---|
committer | Jon Leighton <j@jonathanleighton.com> | 2011-03-04 09:30:27 +0000 |
commit | 735844db712c511dd8abf36a5279318fbc0ff9d0 (patch) | |
tree | 5fbd5d224ef85d8c878bf221db98b422c9345466 /activesupport | |
parent | 9a98c766e045aebc2ef6d5b716936b73407f095d (diff) | |
parent | b171b9e73dcc6a89b1da652da61c5127fe605b51 (diff) | |
download | rails-735844db712c511dd8abf36a5279318fbc0ff9d0.tar.gz rails-735844db712c511dd8abf36a5279318fbc0ff9d0.tar.bz2 rails-735844db712c511dd8abf36a5279318fbc0ff9d0.zip |
Merge branch 'master' into nested_has_many_through
Conflicts:
activerecord/CHANGELOG
activerecord/lib/active_record/association_preload.rb
activerecord/lib/active_record/associations.rb
activerecord/lib/active_record/associations/class_methods/join_dependency.rb
activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb
activerecord/lib/active_record/associations/has_many_association.rb
activerecord/lib/active_record/associations/has_many_through_association.rb
activerecord/lib/active_record/associations/has_one_association.rb
activerecord/lib/active_record/associations/has_one_through_association.rb
activerecord/lib/active_record/associations/through_association_scope.rb
activerecord/lib/active_record/reflection.rb
activerecord/test/cases/associations/has_many_through_associations_test.rb
activerecord/test/cases/associations/has_one_through_associations_test.rb
activerecord/test/cases/reflection_test.rb
activerecord/test/cases/relations_test.rb
activerecord/test/fixtures/memberships.yml
activerecord/test/models/categorization.rb
activerecord/test/models/category.rb
activerecord/test/models/member.rb
activerecord/test/models/reference.rb
activerecord/test/models/tagging.rb
Diffstat (limited to 'activesupport')
81 files changed, 1237 insertions, 427 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 6e8cce0d27..373236ce9a 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,16 @@ *Rails 3.1.0 (unreleased)* +* LocalCache strategy is now a real middleware class, not an anonymous class +posing for pictures. + +* ActiveSupport::Dependencies::ClassCache class has been introduced for +holding references to reloadable classes. + +* ActiveSupport::Dependencies::Reference has been refactored to take direct +advantage of the new ClassCache. + +* Backports Range#cover? as an alias for Range#include? in Ruby 1.8 [Diego Carrion, fxn] + * Added weeks_ago and prev_week to Date/DateTime/Time. [Rob Zolkos, fxn] * Added before_remove_const callback to ActiveSupport::Dependencies.remove_unloadable_constants! [Andrew White] diff --git a/activesupport/MIT-LICENSE b/activesupport/MIT-LICENSE index cd928b856d..5e8b7a9450 100644 --- a/activesupport/MIT-LICENSE +++ b/activesupport/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2005-2010 David Heinemeier Hansson +Copyright (c) 2005-2011 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 6b87774978..6b662ac660 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -42,6 +42,7 @@ module ActiveSupport autoload :DescendantsTracker autoload :FileUpdateChecker + autoload :FileWatcher autoload :LogSubscriber autoload :Notifications diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb index 4dce35f1c9..0649a058aa 100644 --- a/activesupport/lib/active_support/cache/strategy/local_cache.rb +++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb @@ -50,34 +50,39 @@ module ActiveSupport end end - # 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 - klass.class_eval(<<-EOS, __FILE__, __LINE__ + 1) - class << self - def name - "ActiveSupport::Cache::Strategy::LocalCache" - end - alias :to_s :name - end + #-- + # This class wraps up local storage for middlewares. Only the middleware method should + # construct them. + class Middleware # :nodoc: + attr_reader :name, :thread_local_key - def initialize(app) - @app = app - end + def initialize(name, thread_local_key) + @name = name + @thread_local_key = thread_local_key + @app = nil + end - def call(env) - Thread.current[:#{thread_local_key}] = LocalStore.new - @app.call(env) - ensure - Thread.current[:#{thread_local_key}] = nil - end - EOS - klass + def new(app) + @app = app + self + end + + def call(env) + Thread.current[thread_local_key] = LocalStore.new + @app.call(env) + ensure + Thread.current[thread_local_key] = nil end end + # Middleware class can be inserted as a Rack handler to be local cache for the + # duration of request. + def middleware + @middleware ||= Middleware.new( + "ActiveSupport::Cache::Strategy::LocalCache", + thread_local_key) + end + def clear(options = nil) # :nodoc: local_cache.clear(options) if local_cache super diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 844237fe6a..96ce79e896 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -5,27 +5,28 @@ require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/kernel/singleton_class' module ActiveSupport - # Callbacks are hooks into the life cycle of an object that allow you to trigger logic - # before or after an alteration of the object state. + # \Callbacks are code hooks that are run at key points in an object's lifecycle. + # The typical use case is to have a base class define a set of callbacks relevant + # to the other functionality it supplies, so that subclasses can install callbacks + # that enhance or modify the base functionality without needing to override + # or redefine methods of the base class. # - # Mixing in this module allows you to define callbacks in your class. + # Mixing in this module allows you to define the events in the object's lifecycle + # that will support callbacks (via +ClassMethods.define_callbacks+), set the instance + # methods, procs, or callback objects to be called (via +ClassMethods.set_callback+), + # and run the installed callbacks at the appropriate times (via +run_callbacks+). # - # Example: - # class Storage - # include ActiveSupport::Callbacks + # Three kinds of callbacks are supported: before callbacks, run before a certain event; + # after callbacks, run after the event; and around callbacks, blocks that surround the + # event, triggering it when they yield. Callback code can be contained in instance + # methods, procs or lambdas, or callback objects that respond to certain predetermined + # methods. See +ClassMethods.set_callback+ for details. # - # define_callbacks :save - # end + # ==== Example # - # class ConfigStorage < Storage - # set_callback :save, :before, :saving_message - # def saving_message - # puts "saving..." - # end - # - # set_callback :save, :after do |object| - # puts "saved" - # end + # class Record + # include ActiveSupport::Callbacks + # define_callbacks :save # # def save # run_callbacks :save do @@ -34,29 +35,7 @@ module ActiveSupport # end # end # - # config = ConfigStorage.new - # config.save - # - # Output: - # saving... - # - save - # saved - # - # Callbacks from parent classes are inherited. - # - # Example: - # class Storage - # include ActiveSupport::Callbacks - # - # define_callbacks :save - # - # set_callback :save, :before, :prepare - # def prepare - # puts "preparing save" - # end - # end - # - # class ConfigStorage < Storage + # class PersonRecord < Record # set_callback :save, :before, :saving_message # def saving_message # puts "saving..." @@ -65,19 +44,12 @@ module ActiveSupport # set_callback :save, :after do |object| # puts "saved" # end - # - # def save - # run_callbacks :save do - # puts "- save" - # end - # end # end # - # config = ConfigStorage.new - # config.save + # person = PersonRecord.new + # person.save # # Output: - # preparing save # saving... # - save # saved @@ -89,11 +61,25 @@ module ActiveSupport extend ActiveSupport::DescendantsTracker end + # Runs the callbacks for the given event. + # + # Calls the before and around callbacks in the order they were set, yields + # the block (if given one), and then runs the after callbacks in reverse order. + # Optionally accepts a key, which will be used to compile an optimized callback + # method for each key. See +ClassMethods.define_callbacks+ for more information. + # + # If the callback chain was halted, returns +false+. Otherwise returns the result + # of the block, or +true+ if no block is given. + # + # run_callbacks :save do + # save + # end + # def run_callbacks(kind, *args, &block) send("_run_#{kind}_callbacks", *args, &block) end - class Callback + class Callback #:nodoc:# @@_callback_sequence = 0 attr_accessor :chain, :filter, :kind, :options, :per_key, :klass, :raw_filter @@ -185,7 +171,11 @@ module ActiveSupport # end filter = <<-RUBY_EVAL unless halted - result = #{@filter} + # This double assignment is to prevent warnings in 1.9.3. I would + # remove the `result` variable, but apparently some other + # generated code is depending on this variable being set sometimes + # and sometimes not. + result = result = #{@filter} halted = (#{chain.config[:terminator]}) end RUBY_EVAL @@ -328,7 +318,7 @@ module ActiveSupport end # An Array with a compile method - class CallbackChain < Array + class CallbackChain < Array #:nodoc:# attr_reader :name, :config def initialize(name, config) @@ -373,18 +363,7 @@ module ActiveSupport end module ClassMethods - # Make the run_callbacks :save method. The generated method takes - # a block that it'll yield to. It'll call the before and around filters - # in order, yield the block, and then run the after filters. - # - # run_callbacks :save do - # save - # end - # - # The run_callbacks :save method can optionally take a key, which - # will be used to compile an optimized callback method for each - # key. See #define_callbacks for more information. - # + # Generate the internal runner method called by +run_callbacks+. def __define_runner(symbol) #:nodoc: body = send("_#{symbol}_callbacks").compile @@ -440,14 +419,42 @@ module ActiveSupport end end - # Set callbacks for a previously defined callback. + # Install a callback for the given event. # - # Syntax: # set_callback :save, :before, :before_meth # set_callback :save, :after, :after_meth, :if => :condition # set_callback :save, :around, lambda { |r| stuff; yield; stuff } # - # Use skip_callback to skip any defined one. + # The second arguments indicates whether the callback is to be run +:before+, + # +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This + # means the first example above can also be written as: + # + # set_callback :save, :before_meth + # + # The callback can specified as a symbol naming an instance method; as a proc, + # lambda, or block; as a string to be instance evaluated; or as an object that + # responds to a certain method determined by the <tt>:scope</tt> argument to + # +define_callback+. + # + # If a proc, lambda, or block is given, its body is evaluated in the context + # of the current object. It can also optionally accept the current object as + # an argument. + # + # Before and around callbacks are called in the order that they are set; after + # callbacks are called in the reverse order. + # + # ===== Options + # + # * <tt>:if</tt> - A symbol naming an instance method or a proc; the callback + # will be called only when it returns a true value. + # * <tt>:unless</tt> - A symbol naming an instance method or a proc; the callback + # will be called only when it returns a false value. + # * <tt>:prepend</tt> - If true, the callback will be prepended to the existing + # chain rather than appended. + # * <tt>:per_key</tt> - A hash with <tt>:if</tt> and <tt>:unless</tt> options; + # see "Per-key conditions" below. + # + # ===== Per-key conditions # # When creating or skipping callbacks, you can specify conditions that # are always the same for a given key. For instance, in Action Pack, @@ -459,7 +466,7 @@ module ActiveSupport # # set_callback :process_action, :before, :authenticate, :per_key => {:unless => proc {|c| c.action_name == "index"}} # - # Per-Key conditions are evaluated only once per use of a given key. + # Per-key conditions are evaluated only once per use of a given key. # In the case of the above example, you would do: # # run_callbacks(:process_action, action_name) { ... dispatch stuff ... } @@ -486,7 +493,8 @@ module ActiveSupport end end - # Skip a previously defined callback. + # Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or <tt>:unless</tt> + # options may be passed in order to control when the callback is skipped. # # class Writer < Person # skip_callback :validate, :before, :check_membership, :if => lambda { self.age > 18 } @@ -509,7 +517,7 @@ module ActiveSupport end end - # Reset callbacks for a given type. + # Remove all set callbacks for the given event. # def reset_callbacks(symbol) callbacks = send("_#{symbol}_callbacks") @@ -526,68 +534,71 @@ module ActiveSupport __define_runner(symbol) end - # Defines callbacks types: + # Define sets of events in the object lifecycle that support callbacks. # # define_callbacks :validate + # define_callbacks :initialize, :save, :destroy # - # This macro accepts the following options: + # ===== Options # - # * <tt>:terminator</tt> - Indicates when a before filter is considered - # 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: + # * <tt>:terminator</tt> - Determines when a before filter will halt the callback + # chain, preventing following callbacks from being called and the event from being + # triggered. This is a string to be eval'ed. The result of the callback is available + # in the <tt>result</tt> variable. # - # define_callbacks :validate, :terminator => "result == false" + # define_callbacks :validate, :terminator => "result == false" # - # In the example above, if any before validate callbacks returns +false+, - # other callbacks are not executed. Defaults to "false", meaning no value - # halts the chain. + # In this example, if any before validate callbacks returns +false+, + # other callbacks are not executed. Defaults to "false", meaning no value + # halts the chain. # # * <tt>:rescuable</tt> - By default, after filters are not executed if - # the given block or a before filter raises an error. Set this option to - # true to change this behavior. + # the given block or a before filter raises an error. By setting this option + # to <tt>true</tt> exception raised by given block is stored and after + # executing all the after callbacks the stored exception is raised. # - # * <tt>:scope</tt> - Indicates which methods should be executed when a class - # is given as callback. Defaults to <tt>[:kind]</tt>. + # * <tt>:scope</tt> - Indicates which methods should be executed when an object + # is used as a callback. # - # class Audit - # def before(caller) - # puts 'Audit: before is called' - # end + # class Audit + # def before(caller) + # puts 'Audit: before is called' + # end # - # def before_save(caller) - # puts 'Audit: before_save is called' - # end - # end + # def before_save(caller) + # puts 'Audit: before_save is called' + # end + # end # - # class Account - # include ActiveSupport::Callbacks + # class Account + # include ActiveSupport::Callbacks # - # define_callbacks :save - # set_callback :save, :before, Audit.new + # define_callbacks :save + # set_callback :save, :before, Audit.new # - # def save - # run_callbacks :save do - # puts 'save in main' - # end - # end - # end + # def save + # run_callbacks :save do + # puts 'save in main' + # end + # end + # end # - # In the above case whenever you save an account the method <tt>Audit#before</tt> will - # be called. On the other hand + # In the above case whenever you save an account the method <tt>Audit#before</tt> will + # be called. On the other hand # - # define_callbacks :save, :scope => [:kind, :name] + # define_callbacks :save, :scope => [:kind, :name] # - # 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". 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. + # 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". 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 + # A declaration like # - # define_callbacks :save, :scope => [:name] + # define_callbacks :save, :scope => [:name] # - # would call <tt>Audit#save</tt>. + # would call <tt>Audit#save</tt>. # def define_callbacks(*callbacks) config = callbacks.last.is_a?(Hash) ? callbacks.pop : {} diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb index ac94d12e5e..81fb859334 100644 --- a/activesupport/lib/active_support/concern.rb +++ b/activesupport/lib/active_support/concern.rb @@ -3,7 +3,7 @@ module ActiveSupport # # module M # def self.included(base) - # base.extend, ClassMethods + # base.extend ClassMethods # base.send(:include, InstanceMethods) # scope :disabled, where(:disabled => true) # end diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb index 644db0b205..be19189c04 100644 --- a/activesupport/lib/active_support/configurable.rb +++ b/activesupport/lib/active_support/configurable.rb @@ -26,7 +26,7 @@ module ActiveSupport module ClassMethods def config - @_config ||= if superclass.respond_to?(:config) + @_config ||= if respond_to?(:superclass) && superclass.respond_to?(:config) superclass.config.inheritable_copy else # create a new "anonymous" class that will host the compiled reader methods diff --git a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb index f7f03f4d95..080604147d 100644 --- a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb +++ b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb @@ -1,23 +1,38 @@ require 'bigdecimal' + +begin + require 'psych' +rescue LoadError +end + require 'yaml' class BigDecimal YAML_TAG = 'tag:yaml.org,2002:float' YAML_MAPPING = { 'Infinity' => '.Inf', '-Infinity' => '-.Inf', 'NaN' => '.NaN' } - yaml_as YAML_TAG - # This emits the number without any scientific notation. # This is better than self.to_f.to_s since it doesn't lose precision. # # Note that reconstituting YAML floats to native floats may lose precision. def to_yaml(opts = {}) + return super if defined?(YAML::ENGINE) && !YAML::ENGINE.syck? + YAML.quick_emit(nil, opts) do |out| string = to_s out.scalar(YAML_TAG, YAML_MAPPING[string] || string, :plain) end end + def encode_with(coder) + string = to_s + coder.represent_scalar(nil, YAML_MAPPING[string] || string) + end + + def to_d + self + end + DEFAULT_STRING_FORMAT = 'F' def to_formatted_s(format = DEFAULT_STRING_FORMAT) _original_to_s(format) diff --git a/activesupport/lib/active_support/core_ext/class/subclasses.rb b/activesupport/lib/active_support/core_ext/class/subclasses.rb index 3e5d1a2a42..46e9daaa8f 100644 --- a/activesupport/lib/active_support/core_ext/class/subclasses.rb +++ b/activesupport/lib/active_support/core_ext/class/subclasses.rb @@ -2,49 +2,35 @@ require 'active_support/core_ext/module/anonymous' require 'active_support/core_ext/module/reachable' class Class #:nodoc: - # Rubinius - if defined?(Class.__subclasses__) - alias :subclasses :__subclasses__ + begin + ObjectSpace.each_object(Class.new) {} def descendants descendants = [] - __subclasses__.each do |k| - descendants << k - descendants.concat k.descendants + ObjectSpace.each_object(class << self; self; end) do |k| + descendants.unshift k unless k == self end descendants end - else # MRI - begin - ObjectSpace.each_object(Class.new) {} - - def descendants - descendants = [] - ObjectSpace.each_object(class << self; self; end) do |k| - descendants.unshift k unless k == self - end - descendants - end - rescue StandardError # JRuby - def descendants - descendants = [] - ObjectSpace.each_object(Class) do |k| - descendants.unshift k if k < self - end - descendants.uniq! - descendants + rescue StandardError # JRuby + def descendants + descendants = [] + ObjectSpace.each_object(Class) do |k| + descendants.unshift k if k < self end + descendants.uniq! + descendants end + end - # Returns an array with the direct children of +self+. - # - # Integer.subclasses # => [Bignum, Fixnum] - def subclasses - subclasses, chain = [], descendants - chain.each do |k| - subclasses << k unless chain.any? { |c| c > k } - end - subclasses + # Returns an array with the direct children of +self+. + # + # Integer.subclasses # => [Bignum, Fixnum] + def subclasses + subclasses, chain = [], descendants + chain.each do |k| + subclasses << k unless chain.any? { |c| c > k } end + subclasses end end diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index f34185f22c..724e076407 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -36,9 +36,9 @@ class Date ::Date.current.tomorrow end - # Returns Time.zone.today when config.time_zone is set, otherwise just returns Date.today. + # Returns Time.zone.today when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns Date.today. def current - ::Time.zone_default ? ::Time.zone.today : ::Date.today + ::Time.zone ? ::Time.zone.today : ::Date.today end end diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb index 092f936961..769ead9544 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -1,5 +1,5 @@ require 'date' -require 'active_support/inflector' +require 'active_support/inflector/methods' require 'active_support/core_ext/date/zones' class Date @@ -93,6 +93,12 @@ class Date ::DateTime.civil(year, month, day, 0, 0, 0, 0) end if RUBY_VERSION < '1.9' + def iso8601 + strftime('%F') + end if RUBY_VERSION < '1.9' + + alias_method :rfc3339, :iso8601 if RUBY_VERSION < '1.9' + def xmlschema to_time_in_current_zone.xmlschema end diff --git a/activesupport/lib/active_support/core_ext/date/zones.rb b/activesupport/lib/active_support/core_ext/date/zones.rb index 3a83af6be2..a70b47b7bc 100644 --- a/activesupport/lib/active_support/core_ext/date/zones.rb +++ b/activesupport/lib/active_support/core_ext/date/zones.rb @@ -2,10 +2,10 @@ require 'date' require 'active_support/core_ext/time/zones' class Date - # Converts Date to a TimeWithZone in the current zone if Time.zone_default is set, - # otherwise converts Date to a Time via Date#to_time + # Converts Date to a TimeWithZone in the current zone if Time.zone or Time.zone_default + # is set, otherwise converts Date to a Time via Date#to_time def to_time_in_current_zone - if ::Time.zone_default + if ::Time.zone ::Time.zone.local(year, month, day) else to_time diff --git a/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb index 1dc3933e12..8d01376f1d 100644 --- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb @@ -9,8 +9,9 @@ class DateTime ::Time.local(2007).utc_offset.to_r / 86400 end + # Returns <tt>Time.zone.now.to_datetime</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise returns <tt>Time.now.to_datetime</tt>. def current - ::Time.zone_default ? ::Time.zone.now.to_datetime : ::Time.now.to_datetime + ::Time.zone ? ::Time.zone.now.to_datetime : ::Time.now.to_datetime end end diff --git a/activesupport/lib/active_support/core_ext/date_time/conversions.rb b/activesupport/lib/active_support/core_ext/date_time/conversions.rb index 8e267c76c4..21b84b994b 100644 --- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb @@ -1,4 +1,4 @@ -require 'active_support/inflector' +require 'active_support/inflector/methods' require 'active_support/core_ext/time/conversions' require 'active_support/core_ext/date_time/calculations' require 'active_support/values/time_zone' @@ -66,7 +66,7 @@ class DateTime # Attempts to convert self to a Ruby Time object; returns self if out of range of Ruby Time class # If self has an offset other than 0, self will just be returned unaltered, since there's no clean way to map it to a Time def to_time - self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec) : self + self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec, sec_fraction * (RUBY_VERSION < '1.9' ? 86400000000 : 1000000)) : self end # To be able to keep Times, Dates and DateTimes interchangeable on conversions diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 4e8ec5a3a8..3005fef44c 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -26,10 +26,22 @@ class Hash # # * If +value+ is a callable object it must expect one or two arguments. Depending # on the arity, the callable is invoked with the +options+ hash as first argument - # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. Its - # return value becomes a new node. + # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The + # callable can add nodes by using <tt>options[:builder]</tt>. + # + # "foo".to_xml(lambda { |options, key| options[:builder].b(key) }) + # # => "<b>foo</b>" # # * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>. + # + # class Foo + # def to_xml(options) + # options[:builder].bar "fooing!" + # end + # end + # + # {:foo => Foo.new}.to_xml(:skip_instruct => true) + # # => "<hash><bar>fooing!</bar></hash>" # # * Otherwise, a node with +key+ as tag is created with a string representation of # +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added. diff --git a/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb b/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb index 0b6731883c..e3259a0a84 100644 --- a/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb +++ b/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb @@ -7,11 +7,11 @@ class Module # attr_accessor_with_default :age, 25 # end # - # some_person.age - # => 25 - # some_person.age = 26 - # some_person.age - # => 26 + # person = Person.new + # person.age # => 25 + # + # person.age = 26 + # person.age # => 26 # # To give attribute <tt>:element_name</tt> a dynamic default value, evaluated # in scope of self: diff --git a/activesupport/lib/active_support/core_ext/object/with_options.rb b/activesupport/lib/active_support/core_ext/object/with_options.rb index 3209cf7f11..c23afabfdb 100644 --- a/activesupport/lib/active_support/core_ext/object/with_options.rb +++ b/activesupport/lib/active_support/core_ext/object/with_options.rb @@ -7,13 +7,27 @@ class Object # provided. Each method called on the block variable must take an options # hash as its final argument. # - # with_options :order => 'created_at', :class_name => 'Comment' do |post| - # post.has_many :comments, :conditions => ['approved = ?', true], :dependent => :delete_all - # post.has_many :unapproved_comments, :conditions => ['approved = ?', false] - # post.has_many :all_comments + # Without with_options, this code contains duplication: + # + # class Account < ActiveRecord::Base + # has_many :customers, :dependent => :destroy + # has_many :products, :dependent => :destroy + # has_many :invoices, :dependent => :destroy + # has_many :expenses, :dependent => :destroy + # end + # + # Using with_options, we can remove the duplication: + # + # class Account < ActiveRecord::Base + # with_options :dependent => :destroy do |assoc| + # assoc.has_many :customers + # assoc.has_many :products + # assoc.has_many :invoices + # assoc.has_many :expenses + # end # end # - # Can also be used with an explicit receiver: + # It can also be used with an explicit receiver: # # map.with_options :controller => "people" do |people| # people.connect "/people", :action => "index" diff --git a/activesupport/lib/active_support/core_ext/range.rb b/activesupport/lib/active_support/core_ext/range.rb index c0736f3a44..2428a02242 100644 --- a/activesupport/lib/active_support/core_ext/range.rb +++ b/activesupport/lib/active_support/core_ext/range.rb @@ -2,3 +2,4 @@ require 'active_support/core_ext/range/blockless_step' require 'active_support/core_ext/range/conversions' require 'active_support/core_ext/range/include_range' require 'active_support/core_ext/range/overlaps' +require 'active_support/core_ext/range/cover' diff --git a/activesupport/lib/active_support/core_ext/range/cover.rb b/activesupport/lib/active_support/core_ext/range/cover.rb new file mode 100644 index 0000000000..3a182cddd2 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/range/cover.rb @@ -0,0 +1,3 @@ +class Range + alias_method(:cover?, :include?) unless instance_methods.include?(:cover?) +end diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb index 55b24b0925..2f0676f567 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -3,7 +3,7 @@ require 'active_support/inflector/inflections' require 'active_support/inflector/transliterate' # 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. +# For instance, you can figure out the name of a table from the name of a class. # # "ScaleScore".tableize # => "scale_scores" # diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index bb0f747960..c930abc003 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -101,7 +101,13 @@ module ActiveSupport #:nodoc: self end + def encode_with(coder) + coder.represent_scalar nil, to_str + end + def to_yaml(*args) + return super() if defined?(YAML::ENGINE) && !YAML::ENGINE.syck? + to_str.to_yaml(*args) end end diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index fa052fa86b..6e4b69f681 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -41,9 +41,9 @@ class Time time_with_datetime_fallback(:local, *args) end - # Returns <tt>Time.zone.now</tt> when <tt>config.time_zone</tt> is set, otherwise just returns <tt>Time.now</tt>. + # Returns <tt>Time.zone.now</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns <tt>Time.now</tt>. def current - ::Time.zone_default ? ::Time.zone.now : ::Time.now + ::Time.zone ? ::Time.zone.now : ::Time.now end end diff --git a/activesupport/lib/active_support/core_ext/time/conversions.rb b/activesupport/lib/active_support/core_ext/time/conversions.rb index d4ae3131ec..d9d5e02778 100644 --- a/activesupport/lib/active_support/core_ext/time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/time/conversions.rb @@ -1,4 +1,4 @@ -require 'active_support/inflector' +require 'active_support/inflector/methods' require 'active_support/core_ext/time/publicize_conversion_methods' require 'active_support/values/time_zone' @@ -55,31 +55,9 @@ class Time utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon) end - # Converts a Time object to a Date, dropping hour, minute, and second precision. - # - # my_time = Time.now # => Mon Nov 12 22:59:51 -0500 2007 - # my_time.to_date # => Mon, 12 Nov 2007 - # - # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009 - # your_time.to_date # => Tue, 13 Jan 2009 - def to_date - ::Date.new(year, month, day) - end unless method_defined?(:to_date) - # A method to keep Time, Date and DateTime instances interchangeable on conversions. # In this case, it simply returns +self+. def to_time self end unless method_defined?(:to_time) - - # Converts a Time instance to a Ruby DateTime instance, preserving UTC offset. - # - # my_time = Time.now # => Mon Nov 12 23:04:21 -0500 2007 - # my_time.to_datetime # => Mon, 12 Nov 2007 23:04:21 -0500 - # - # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009 - # your_time.to_datetime # => Tue, 13 Jan 2009 13:13:03 -0500 - def to_datetime - ::DateTime.civil(year, month, day, hour, min, sec, Rational(utc_offset, 86400)) - end unless method_defined?(:to_datetime) end diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb index ef401a6d18..ff90d7ca58 100644 --- a/activesupport/lib/active_support/core_ext/time/zones.rb +++ b/activesupport/lib/active_support/core_ext/time/zones.rb @@ -19,14 +19,18 @@ class Time # * A TZInfo::Timezone object. # * An identifier for a TZInfo::Timezone object (e.g., "America/New_York"). # - # Here's an example of how you might set <tt>Time.zone</tt> on a per request basis -- <tt>current_user.time_zone</tt> - # just needs to return a string identifying the user's preferred TimeZone: + # Here's an example of how you might set <tt>Time.zone</tt> on a per request basis and reset it when the request is done. + # <tt>current_user.time_zone</tt> just needs to return a string identifying the user's preferred time zone: # # class ApplicationController < ActionController::Base - # before_filter :set_time_zone + # around_filter :set_time_zone # # def set_time_zone - # Time.zone = current_user.time_zone + # old_time_zone = Time.zone + # Time.zone = current_user.time_zone if logged_in? + # yield + # ensure + # Time.zone = old_time_zone # end # end def zone=(time_zone) diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index dab6fdbac6..47596a389d 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -5,6 +5,7 @@ require 'active_support/core_ext/module/aliasing' require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/module/introspection' require 'active_support/core_ext/module/anonymous' +require 'active_support/core_ext/module/deprecation' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/load_error' require 'active_support/core_ext/name_error' @@ -47,9 +48,6 @@ module ActiveSupport #:nodoc: mattr_accessor :autoloaded_constants self.autoloaded_constants = [] - mattr_accessor :references - self.references = {} - # An array of constant names that need to be unloaded on every request. Used # to allow arbitrary constants to be marked for unloading. mattr_accessor :explicitly_unloadable_constants @@ -524,31 +522,76 @@ module ActiveSupport #:nodoc: explicitly_unloadable_constants.each { |const| remove_constant const } end - class Reference - @@constants = Hash.new { |h, k| h[k] = Inflector.constantize(k) } + class ClassCache + def initialize + @store = Hash.new { |h, k| h[k] = Inflector.constantize(k) } + end + + def empty? + @store.empty? + end + + def key?(key) + @store.key?(key) + end + + def []=(key, value) + return unless key.respond_to?(:name) + + raise(ArgumentError, 'anonymous classes cannot be cached') if key.name.blank? + + @store[key.name] = value + end + + def [](key) + key = key.name if key.respond_to?(:name) + + @store[key] + end + alias :get :[] - attr_reader :name + class Getter # :nodoc: + def initialize(name) + @name = name + end - def initialize(name) - @name = name.to_s - @@constants[@name] = name if name.respond_to?(:name) + def get + Reference.get @name + end + deprecate :get end - def get - @@constants[@name] + def new(name) + self[name] = name + Getter.new(name) end + deprecate :new - def self.clear! - @@constants.clear + def store(name) + self[name] = name + self + end + + def clear! + @store.clear end end + Reference = ClassCache.new + def ref(name) - references[name] ||= Reference.new(name) + Reference.new(name) + end + deprecate :ref + + # Store a reference to a class +klass+. + def reference(klass) + Reference.store klass end + # Get the reference for class named +name+. def constantize(name) - ref(name).get + Reference.get(name) end # Determine if the given constant has been automatically loaded. diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index e1b8211d68..ce0775a690 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -4,12 +4,12 @@ require 'active_support/deprecation/method_wrappers' require 'active_support/deprecation/proxy_wrappers' module ActiveSupport - module Deprecation #:nodoc: + module Deprecation class << self # The version the deprecated behavior will be removed, by default. attr_accessor :deprecation_horizon end - self.deprecation_horizon = '3.0' + self.deprecation_horizon = '3.1' # By default, warnings are not silenced and debugging is off. self.silenced = false diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb index f54f65dcf0..da4af339fc 100644 --- a/activesupport/lib/active_support/deprecation/behaviors.rb +++ b/activesupport/lib/active_support/deprecation/behaviors.rb @@ -7,10 +7,17 @@ module ActiveSupport # Whether to print a backtrace along with the warning. attr_accessor :debug + # Returns the set behaviour or if one isn't set, defaults to +:stderr+ def behavior @behavior ||= [DEFAULT_BEHAVIORS[:stderr]] end + # Sets the behaviour to the specified value. Can be a single value or an array. + # + # Examples + # + # ActiveSupport::Deprecation.behavior = :stderr + # ActiveSupport::Deprecation.behavior = [:stderr, :log] def behavior=(behavior) @behavior = Array.wrap(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b } end diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb index 970536a594..a65fcafb44 100644 --- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -1,4 +1,4 @@ -require 'active_support/inflector' +require 'active_support/inflector/methods' module ActiveSupport module Deprecation diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb index 6a7b11c7e0..ced08b8783 100644 --- a/activesupport/lib/active_support/deprecation/reporting.rb +++ b/activesupport/lib/active_support/deprecation/reporting.rb @@ -3,6 +3,12 @@ module ActiveSupport class << self attr_accessor :silenced + # Outputs a deprecation warning to the output configured by <tt>ActiveSupport::Deprecation.behavior</tt> + # + # Example: + # + # ActiveSupport::Deprecation.warn("something broke!") + # #=> "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" def warn(message = nil, callstack = caller) return if silenced deprecation_message(callstack, message).tap do |m| diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index de3ded1e1f..00c67a470d 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -80,6 +80,10 @@ module ActiveSupport parts.to_sentence(:locale => :en) end + def as_json(options = nil) #:nodoc: + to_i + end + protected def sum(sign, time = ::Time.current) #:nodoc: diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb index cd658fe173..a97e9d7daf 100644 --- a/activesupport/lib/active_support/file_update_checker.rb +++ b/activesupport/lib/active_support/file_update_checker.rb @@ -8,7 +8,7 @@ module ActiveSupport # I18n.reload! # end # - # ActionDispatch::Callbacks.to_prepare do + # ActionDispatch::Reloader.to_prepare do # i18n_reloader.execute_if_updated # end # diff --git a/activesupport/lib/active_support/file_watcher.rb b/activesupport/lib/active_support/file_watcher.rb new file mode 100644 index 0000000000..81e63e76a7 --- /dev/null +++ b/activesupport/lib/active_support/file_watcher.rb @@ -0,0 +1,36 @@ +module ActiveSupport + class FileWatcher + class Backend + def initialize(path, watcher) + @watcher = watcher + @path = path + end + + def trigger(files) + @watcher.trigger(files) + end + end + + def initialize + @regex_matchers = {} + end + + def watch(pattern, &block) + @regex_matchers[pattern] = block + end + + def trigger(files) + trigger_files = Hash.new { |h,k| h[k] = Hash.new { |h2,k2| h2[k2] = [] } } + + files.each do |file, state| + @regex_matchers.each do |pattern, block| + trigger_files[block][state] << file if pattern === file + end + end + + trigger_files.each do |block, payload| + block.call payload + end + end + end +end diff --git a/activesupport/lib/active_support/gzip.rb b/activesupport/lib/active_support/gzip.rb index 35a50e9a77..62f9c9aa2e 100644 --- a/activesupport/lib/active_support/gzip.rb +++ b/activesupport/lib/active_support/gzip.rb @@ -5,6 +5,10 @@ module ActiveSupport # A convenient wrapper for the zlib standard library that allows compression/decompression of strings with gzip. module Gzip class Stream < StringIO + def initialize(*) + super + set_encoding "BINARY" if "".encoding_aware? + end def close; rewind; end end @@ -22,4 +26,4 @@ module ActiveSupport output.string end end -end
\ No newline at end of file +end diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb index f8a5616a76..4a9ee5a769 100644 --- a/activesupport/lib/active_support/i18n_railtie.rb +++ b/activesupport/lib/active_support/i18n_railtie.rb @@ -19,14 +19,29 @@ module I18n # on to_prepare callbacks. This will only happen on the config.after_initialize # callback below. initializer "i18n.callbacks" do - ActionDispatch::Callbacks.to_prepare do + ActionDispatch::Reloader.to_prepare do I18n::Railtie.reloader.execute_if_updated end end - # Set the i18n configuration only after initialization since a lot of + # Set the i18n configuration after initialization since a lot of # configuration is still usually done in application initializers. config.after_initialize do |app| + I18n::Railtie.initialize_i18n(app) + end + + # Trigger i18n config before any eager loading has happened + # so it's ready if any classes require it when eager loaded + config.before_eager_load do |app| + I18n::Railtie.initialize_i18n(app) + end + + protected + + # Setup i18n configuration + def self.initialize_i18n(app) + return if @i18n_inited + fallbacks = app.config.i18n.delete(:fallbacks) app.config.i18n.each do |setting, value| @@ -44,9 +59,9 @@ module I18n reloader.paths.concat I18n.load_path reloader.execute_if_updated - end - protected + @i18n_inited = true + end def self.include_fallbacks_module I18n.backend.class.send(:include, I18n::Backend::Fallbacks) @@ -78,4 +93,4 @@ module I18n end end end -end
\ No newline at end of file +end diff --git a/activesupport/lib/active_support/inflections.rb b/activesupport/lib/active_support/inflections.rb index e7b5387ed7..06ceccdb22 100644 --- a/activesupport/lib/active_support/inflections.rb +++ b/activesupport/lib/active_support/inflections.rb @@ -4,10 +4,12 @@ module ActiveSupport inflect.plural(/s$/i, 's') inflect.plural(/(ax|test)is$/i, '\1es') inflect.plural(/(octop|vir)us$/i, '\1i') + inflect.plural(/(octop|vir)i$/i, '\1i') inflect.plural(/(alias|status)$/i, '\1es') inflect.plural(/(bu)s$/i, '\1ses') inflect.plural(/(buffal|tomat)o$/i, '\1oes') inflect.plural(/([ti])um$/i, '\1a') + inflect.plural(/([ti])a$/i, '\1a') inflect.plural(/sis$/i, 'ses') inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves') inflect.plural(/(hive)$/i, '\1s') @@ -15,7 +17,9 @@ module ActiveSupport inflect.plural(/(x|ch|ss|sh)$/i, '\1es') inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices') inflect.plural(/([m|l])ouse$/i, '\1ice') + inflect.plural(/([m|l])ice$/i, '\1ice') inflect.plural(/^(ox)$/i, '\1en') + inflect.plural(/^(oxen)$/i, '\1') inflect.plural(/(quiz)$/i, '\1zes') inflect.singular(/s$/i, '') diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb index 3caf78bc7d..e136e4c5b3 100644 --- a/activesupport/lib/active_support/inflector/inflections.rb +++ b/activesupport/lib/active_support/inflector/inflections.rb @@ -148,7 +148,7 @@ module ActiveSupport def singularize(word) result = word.to_s.dup - if inflections.uncountables.any? { |inflection| result =~ /#{inflection}\Z/i } + if inflections.uncountables.any? { |inflection| result =~ /\b(#{inflection})\Z/i } result else inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) } diff --git a/activesupport/lib/active_support/json/backends/jsongem.rb b/activesupport/lib/active_support/json/backends/jsongem.rb index cfe28d7bb9..533ba25da3 100644 --- a/activesupport/lib/active_support/json/backends/jsongem.rb +++ b/activesupport/lib/active_support/json/backends/jsongem.rb @@ -26,7 +26,11 @@ module ActiveSupport when nil nil when DATE_REGEX - DateTime.parse(data) + begin + DateTime.parse(data) + rescue ArgumentError + data + end when Array data.map! { |d| convert_dates_from(d) } when Hash diff --git a/activesupport/lib/active_support/json/backends/yajl.rb b/activesupport/lib/active_support/json/backends/yajl.rb index 64e50e0d87..58818658c7 100644 --- a/activesupport/lib/active_support/json/backends/yajl.rb +++ b/activesupport/lib/active_support/json/backends/yajl.rb @@ -23,7 +23,11 @@ module ActiveSupport when nil nil when DATE_REGEX - DateTime.parse(data) + begin + DateTime.parse(data) + rescue ArgumentError + data + end when Array data.map! { |d| convert_dates_from(d) } when Hash diff --git a/activesupport/lib/active_support/json/backends/yaml.rb b/activesupport/lib/active_support/json/backends/yaml.rb index 4cb9d01077..077eda548a 100644 --- a/activesupport/lib/active_support/json/backends/yaml.rb +++ b/activesupport/lib/active_support/json/backends/yaml.rb @@ -7,14 +7,21 @@ module ActiveSupport ParseError = ::StandardError extend self + EXCEPTIONS = [::ArgumentError] # :nodoc: + begin + require 'psych' + EXCEPTIONS << Psych::SyntaxError + rescue LoadError + end + # Parses a JSON string or IO and converts it into an object def decode(json) if json.respond_to?(:read) json = json.read end YAML.load(convert_json_to_yaml(json)) - rescue ArgumentError - raise ParseError, "Invalid JSON string" + rescue *EXCEPTIONS => e + raise ParseError, "Invalid JSON string: '%s'" % json end protected @@ -29,10 +36,10 @@ module ActiveSupport quoting = char pos = scanner.pos elsif quoting == char - if json[pos..scanner.pos-2] =~ DATE_REGEX + if valid_date?(json[pos..scanner.pos-2]) # found a date, track the exact positions of the quotes so we can # overwrite them with spaces later. - times << pos << scanner.pos + times << pos end quoting = false end @@ -47,7 +54,9 @@ module ActiveSupport json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do ustr = $1 if ustr.start_with?('u') - [ustr[1..-1].to_i(16)].pack("U") + char = [ustr[1..-1].to_i(16)].pack("U") + # "\n" needs extra escaping due to yaml formatting + char == "\n" ? "\\n" : char elsif ustr == '\\' '\\\\' else @@ -63,12 +72,14 @@ module ActiveSupport chunk = scanner.peek(right_pos[i] - scanner.pos + 1) # overwrite the quotes found around the dates with spaces while times.size > 0 && times[0] <= right_pos[i] - chunk[times.shift - scanner.pos - 1] = ' ' + chunk.insert(times.shift - scanner.pos - 1, '! ') end chunk.gsub!(/\\([\\\/]|u[[:xdigit:]]{4})/) do ustr = $1 if ustr.start_with?('u') - [ustr[1..-1].to_i(16)].pack("U") + char = [ustr[1..-1].to_i(16)].pack("U") + # "\n" needs extra escaping due to yaml formatting + char == "\n" ? "\\n" : char elsif ustr == '\\' '\\\\' else @@ -83,6 +94,16 @@ module ActiveSupport output end end + + private + def valid_date?(date_string) + begin + date_string =~ DATE_REGEX && DateTime.parse(date_string) + rescue ArgumentError + false + end + end + end end end diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index c8cac52027..82b8a7e148 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -23,7 +23,7 @@ module ActiveSupport module JSON # matches YAML-formatted dates - DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/ + DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/ # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info. def self.encode(value, options = nil) @@ -153,6 +153,12 @@ class Object end end +class Struct + def as_json(options = nil) #:nodoc: + Hash[members.zip(values)] + end +end + class TrueClass AS_JSON = ActiveSupport::JSON::Variable.new('true').freeze def as_json(options = nil) AS_JSON end #:nodoc: diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb index 5c24b9c759..52a64383a2 100644 --- a/activesupport/lib/active_support/log_subscriber/test_helper.rb +++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb @@ -38,13 +38,14 @@ module ActiveSupport ActiveSupport::LogSubscriber.colorize_logging = false + @old_notifier = ActiveSupport::Notifications.notifier set_logger(@logger) ActiveSupport::Notifications.notifier = @notifier end def teardown set_logger(nil) - ActiveSupport::Notifications.notifier = nil + ActiveSupport::Notifications.notifier = @old_notifier end class MockLogger diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb index fd79188ba4..77696eb1db 100644 --- a/activesupport/lib/active_support/notifications.rb +++ b/activesupport/lib/active_support/notifications.rb @@ -44,8 +44,11 @@ module ActiveSupport @instrumenters = Hash.new { |h,k| h[k] = notifier.listening?(k) } class << self - attr_writer :notifier - delegate :publish, :to => :notifier + attr_accessor :notifier + + def publish(name, *args) + notifier.publish(name, *args) + end def instrument(name, payload = {}) if @instrumenters[name] @@ -61,18 +64,16 @@ module ActiveSupport end end - def unsubscribe(*args) - notifier.unsubscribe(*args) + def unsubscribe(args) + notifier.unsubscribe(args) @instrumenters.clear end - def notifier - @notifier ||= Fanout.new - end - def instrumenter Thread.current[:"instrumentation_#{notifier.object_id}"] ||= Instrumenter.new(notifier) end end + + self.notifier = Fanout.new end end diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index 8e157d3af4..fbc40d1b69 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -1,3 +1,8 @@ +begin + require 'psych' +rescue LoadError +end + require 'yaml' YAML.add_builtin_type("omap") do |type, val| @@ -20,9 +25,17 @@ module ActiveSupport "!tag:yaml.org,2002:omap" end + def encode_with(coder) + coder.represent_seq '!omap', map { |k,v| { k => v } } + end + def to_yaml(opts = {}) + if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck? + return super + end + YAML.quick_emit(self, opts) do |out| - out.seq(taguri, to_yaml_style) do |seq| + out.seq(taguri) do |seq| each do |k, v| seq.add(k => v) end @@ -124,15 +137,21 @@ module ActiveSupport end def each_key + return to_enum(:each_key) unless block_given? @keys.each { |key| yield key } + self end def each_value + return to_enum(:each_value) unless block_given? @keys.each { |key| yield self[key]} + self end def each + return to_enum(:each) unless block_given? @keys.each {|key| yield [key, self[key]]} + self end alias_method :each_pair, :each diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb index 124e1a74f8..b40cbceb7e 100644 --- a/activesupport/lib/active_support/ordered_options.rb +++ b/activesupport/lib/active_support/ordered_options.rb @@ -31,7 +31,7 @@ module ActiveSupport #:nodoc: def method_missing(name, *args) if name.to_s =~ /(.*)=$/ - self[$1.to_sym] = args.first + self[$1] = args.first else self[name] end diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 64b436ba8c..8c91a061fb 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -401,7 +401,7 @@ begin Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) # Ruby 1.9 with GC::Profiler - if GC.respond_to?(:total_time) + if defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time) def measure GC::Profiler.total_time end diff --git a/activesupport/lib/active_support/testing/setup_and_teardown.rb b/activesupport/lib/active_support/testing/setup_and_teardown.rb index b2d9ddfeb7..22e41fa905 100644 --- a/activesupport/lib/active_support/testing/setup_and_teardown.rb +++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb @@ -31,14 +31,14 @@ module ActiveSupport def run(runner) result = '.' begin - _run_setup_callbacks do + run_callbacks :setup do result = super end rescue Exception => e result = runner.puke(self.class, method_name, e) ensure begin - _run_teardown_callbacks + run_callbacks :teardown rescue Exception => e result = runner.puke(self.class, method_name, e) end @@ -62,7 +62,7 @@ module ActiveSupport begin begin - _run_setup_callbacks do + run_callbacks :setup do setup __send__(@method_name) mocha_verify(mocha_counter) if mocha_counter @@ -77,7 +77,7 @@ module ActiveSupport ensure begin teardown - _run_teardown_callbacks + run_callbacks :teardown rescue Test::Unit::AssertionFailedError => e add_failure(e.message, e.backtrace) rescue Exception => e diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 6a7da8266c..3da216ac78 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -137,14 +137,20 @@ module ActiveSupport end end - def to_yaml(options = {}) - if options.kind_of?(YAML::Emitter) - utc.to_yaml(options) + def encode_with(coder) + if coder.respond_to?(:represent_object) + coder.represent_object(nil, utc) else - time.to_yaml(options).gsub('Z', formatted_offset(true, 'Z')) + coder.represent_scalar(nil, utc.strftime("%Y-%m-%d %H:%M:%S.%9NZ")) end end + def to_yaml(options = {}) + return super if defined?(YAML::ENGINE) && !YAML::ENGINE.syck? + + utc.to_yaml(options) + end + def httpdate utc.httpdate end diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb index 7fdcb11465..16570c6aea 100644 --- a/activesupport/lib/active_support/xml_mini/libxml.rb +++ b/activesupport/lib/active_support/xml_mini/libxml.rb @@ -1,5 +1,6 @@ require 'libxml' require 'active_support/core_ext/object/blank' +require 'stringio' # = XmlMini LibXML implementation module ActiveSupport diff --git a/activesupport/lib/active_support/xml_mini/libxmlsax.rb b/activesupport/lib/active_support/xml_mini/libxmlsax.rb index fe2c1b9349..2536b1f33e 100644 --- a/activesupport/lib/active_support/xml_mini/libxmlsax.rb +++ b/activesupport/lib/active_support/xml_mini/libxmlsax.rb @@ -1,5 +1,6 @@ require 'libxml' require 'active_support/core_ext/object/blank' +require 'stringio' # = XmlMini LibXML implementation using a SAX-based parser module ActiveSupport @@ -82,4 +83,4 @@ module ActiveSupport end end end -end
\ No newline at end of file +end diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb index e03a744257..04ec9e8ab8 100644 --- a/activesupport/lib/active_support/xml_mini/nokogiri.rb +++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb @@ -5,6 +5,7 @@ rescue LoadError => e raise e end require 'active_support/core_ext/object/blank' +require 'stringio' # = XmlMini Nokogiri implementation module ActiveSupport diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb index 25afbfcd1c..93fd3dfe57 100644 --- a/activesupport/lib/active_support/xml_mini/nokogirisax.rb +++ b/activesupport/lib/active_support/xml_mini/nokogirisax.rb @@ -5,6 +5,7 @@ rescue LoadError => e raise e end require 'active_support/core_ext/object/blank' +require 'stringio' # = XmlMini Nokogiri implementation using a SAX-based parser module ActiveSupport diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb index 36692af1d3..a13ad10118 100644 --- a/activesupport/lib/active_support/xml_mini/rexml.rb +++ b/activesupport/lib/active_support/xml_mini/rexml.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/object/blank' +require 'stringio' # = XmlMini ReXML implementation module ActiveSupport diff --git a/activesupport/test/buffered_logger_test.rb b/activesupport/test/buffered_logger_test.rb index 97c0ef14db..8d1b1c02c6 100644 --- a/activesupport/test/buffered_logger_test.rb +++ b/activesupport/test/buffered_logger_test.rb @@ -115,11 +115,9 @@ class BufferedLoggerTest < Test::Unit::TestCase def test_should_create_the_log_directory_if_it_doesnt_exist tmp_directory = File.join(File.dirname(__FILE__), "tmp") log_file = File.join(tmp_directory, "development.log") - assert !File.exist?(tmp_directory) + FileUtils.rm_rf(tmp_directory) @logger = Logger.new(log_file) assert File.exist?(tmp_directory) - ensure - FileUtils.rm_rf(tmp_directory) end def test_logger_should_maintain_separate_buffers_for_each_thread diff --git a/activesupport/test/callback_inheritance_test.rb b/activesupport/test/callback_inheritance_test.rb index 8caf000c5d..71249050fc 100644 --- a/activesupport/test/callback_inheritance_test.rb +++ b/activesupport/test/callback_inheritance_test.rb @@ -70,7 +70,7 @@ class EmptyParent end def dispatch - _run_dispatch_callbacks + run_callbacks :dispatch self end end diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb index 51b28b6a43..cff914f4ae 100644 --- a/activesupport/test/callbacks_test.rb +++ b/activesupport/test/callbacks_test.rb @@ -3,6 +3,27 @@ require 'test/unit' require 'active_support' module CallbacksTest + class Phone + include ActiveSupport::Callbacks + define_callbacks :save, :rescuable => true + + set_callback :save, :before, :before_save1 + set_callback :save, :after, :after_save1 + + def before_save1; self.history << :before; end + def after_save1; self.history << :after; end + + def save + run_callbacks :save do + raise 'boom' + end + end + + def history + @history ||= [] + end + end + class Record include ActiveSupport::Callbacks @@ -338,6 +359,14 @@ module CallbacksTest end class CallbacksTest < Test::Unit::TestCase + def test_save_phone + phone = Phone.new + assert_raise RuntimeError do + phone.save + end + assert_equal [:before, :after], phone.history + end + def test_save_person person = Person.new assert_equal [], person.history @@ -573,5 +602,5 @@ module CallbacksTest ], writer.history end end - + end diff --git a/activesupport/test/class_cache_test.rb b/activesupport/test/class_cache_test.rb new file mode 100644 index 0000000000..8445af8d25 --- /dev/null +++ b/activesupport/test/class_cache_test.rb @@ -0,0 +1,108 @@ +require 'abstract_unit' +require 'active_support/dependencies' + +module ActiveSupport + module Dependencies + class ClassCacheTest < ActiveSupport::TestCase + def setup + @cache = ClassCache.new + end + + def test_empty? + assert @cache.empty? + @cache[ClassCacheTest] = ClassCacheTest + assert !@cache.empty? + end + + def test_clear! + assert @cache.empty? + @cache[ClassCacheTest] = ClassCacheTest + assert !@cache.empty? + @cache.clear! + assert @cache.empty? + end + + def test_set_key + @cache[ClassCacheTest] = ClassCacheTest + assert @cache.key?(ClassCacheTest.name) + end + + def test_set_rejects_strings + @cache[ClassCacheTest.name] = ClassCacheTest + assert @cache.empty? + end + + def test_get_with_class + @cache[ClassCacheTest] = ClassCacheTest + assert_equal ClassCacheTest, @cache[ClassCacheTest] + end + + def test_get_with_name + @cache[ClassCacheTest] = ClassCacheTest + assert_equal ClassCacheTest, @cache[ClassCacheTest.name] + end + + def test_get_constantizes + assert @cache.empty? + assert_equal ClassCacheTest, @cache[ClassCacheTest.name] + end + + def test_get_is_an_alias + assert_equal @cache[ClassCacheTest], @cache.get(ClassCacheTest.name) + end + + def test_new + assert_deprecated do + @cache.new ClassCacheTest + end + assert @cache.key?(ClassCacheTest.name) + end + + def test_new_rejects_strings + assert_deprecated do + @cache.new ClassCacheTest.name + end + assert !@cache.key?(ClassCacheTest.name) + end + + def test_new_rejects_strings + @cache.store ClassCacheTest.name + assert !@cache.key?(ClassCacheTest.name) + end + + def test_store_returns_self + x = @cache.store ClassCacheTest + assert_equal @cache, x + end + + def test_new_returns_proxy + v = nil + assert_deprecated do + v = @cache.new ClassCacheTest.name + end + + assert_deprecated do + assert_equal ClassCacheTest, v.get + end + end + + def test_anonymous_class_fail + assert_raises(ArgumentError) do + assert_deprecated do + @cache.new Class.new + end + end + + assert_raises(ArgumentError) do + x = Class.new + @cache[x] = x + end + + assert_raises(ArgumentError) do + x = Class.new + @cache.store x + end + end + end + end +end diff --git a/activesupport/test/configurable_test.rb b/activesupport/test/configurable_test.rb index 9c773c1944..2b28e61815 100644 --- a/activesupport/test/configurable_test.rb +++ b/activesupport/test/configurable_test.rb @@ -21,6 +21,12 @@ class ConfigurableActiveSupport < ActiveSupport::TestCase assert_equal({ :foo => :bar }, Parent.config) end + test "adds a configuration hash to a module as well" do + mixin = Module.new { include ActiveSupport::Configurable } + mixin.config.foo = :bar + assert_equal({ :foo => :bar }, mixin.config) + end + test "configuration hash is inheritable" do assert_equal :bar, Child.config.foo assert_equal :bar, Parent.config.foo @@ -57,4 +63,4 @@ class ConfigurableActiveSupport < ActiveSupport::TestCase assert_respond_to child.config, :bar assert_respond_to child.new.config, :bar end -end
\ No newline at end of file +end diff --git a/activesupport/test/core_ext/bigdecimal.rb b/activesupport/test/core_ext/bigdecimal.rb deleted file mode 100644 index 9faad9146f..0000000000 --- a/activesupport/test/core_ext/bigdecimal.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'abstract_unit' - -class BigDecimalTest < Test::Unit::TestCase - def test_to_yaml - assert_equal("--- 100000.30020320320000000000000000000000000000001\n", BigDecimal.new('100000.30020320320000000000000000000000000000001').to_yaml) - assert_equal("--- .Inf\n", BigDecimal.new('Infinity').to_yaml) - assert_equal("--- .NaN\n", BigDecimal.new('NaN').to_yaml) - assert_equal("--- -.Inf\n", BigDecimal.new('-Infinity').to_yaml) - end -end
\ No newline at end of file diff --git a/activesupport/test/core_ext/bigdecimal_test.rb b/activesupport/test/core_ext/bigdecimal_test.rb new file mode 100644 index 0000000000..b38e08a9f4 --- /dev/null +++ b/activesupport/test/core_ext/bigdecimal_test.rb @@ -0,0 +1,17 @@ +require 'abstract_unit' +require 'bigdecimal' +require 'active_support/core_ext/big_decimal' + +class BigDecimalTest < Test::Unit::TestCase + def test_to_yaml + assert_match("--- 100000.30020320320000000000000000000000000000001\n", BigDecimal.new('100000.30020320320000000000000000000000000000001').to_yaml) + assert_match("--- .Inf\n", BigDecimal.new('Infinity').to_yaml) + assert_match("--- .NaN\n", BigDecimal.new('NaN').to_yaml) + assert_match("--- -.Inf\n", BigDecimal.new('-Infinity').to_yaml) + end + + def test_to_d + bd = BigDecimal.new '10' + assert_equal bd, bd.to_d + end +end diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index 342a31cdef..03b84ae2e5 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -259,7 +259,7 @@ class DateExtCalculationsTest < ActiveSupport::TestCase assert_equal Date.current - 1, Date.yesterday end - def test_yesterday_constructor_when_zone_default_is_not_set + def test_yesterday_constructor_when_zone_is_not_set with_env_tz 'UTC' do with_tz_default do Time.stubs(:now).returns Time.local(2000, 1, 1) @@ -268,7 +268,7 @@ class DateExtCalculationsTest < ActiveSupport::TestCase end end - def test_yesterday_constructor_when_zone_default_is_set + def test_yesterday_constructor_when_zone_is_set with_env_tz 'UTC' do with_tz_default ActiveSupport::TimeZone['Eastern Time (US & Canada)'] do # UTC -5 Time.stubs(:now).returns Time.local(2000, 1, 1) @@ -281,7 +281,7 @@ class DateExtCalculationsTest < ActiveSupport::TestCase assert_equal Date.current + 1, Date.tomorrow end - def test_tomorrow_constructor_when_zone_default_is_not_set + def test_tomorrow_constructor_when_zone_is_not_set with_env_tz 'UTC' do with_tz_default do Time.stubs(:now).returns Time.local(1999, 12, 31) @@ -290,7 +290,7 @@ class DateExtCalculationsTest < ActiveSupport::TestCase end end - def test_tomorrow_constructor_when_zone_default_is_set + def test_tomorrow_constructor_when_zone_is_set with_env_tz 'UTC' do with_tz_default ActiveSupport::TimeZone['Europe/Paris'] do # UTC +1 Time.stubs(:now).returns Time.local(1999, 12, 31, 23) @@ -303,7 +303,7 @@ class DateExtCalculationsTest < ActiveSupport::TestCase assert_equal Time.local(2005,2,21,0,0,45), Date.new(2005,2,21).since(45) end - def test_since_when_zone_default_is_set + def test_since_when_zone_is_set zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] with_env_tz 'UTC' do with_tz_default zone do @@ -317,7 +317,7 @@ class DateExtCalculationsTest < ActiveSupport::TestCase assert_equal Time.local(2005,2,20,23,59,15), Date.new(2005,2,21).ago(45) end - def test_ago_when_zone_default_is_set + def test_ago_when_zone_is_set zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] with_env_tz 'UTC' do with_tz_default zone do @@ -331,7 +331,7 @@ class DateExtCalculationsTest < ActiveSupport::TestCase assert_equal Time.local(2005,2,21,0,0,0), Date.new(2005,2,21).beginning_of_day end - def test_beginning_of_day_when_zone_default_is_set + def test_beginning_of_day_when_zone_is_set zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] with_env_tz 'UTC' do with_tz_default zone do @@ -345,7 +345,7 @@ class DateExtCalculationsTest < ActiveSupport::TestCase assert_equal Time.local(2005,2,21,23,59,59,999999.999), Date.new(2005,2,21).end_of_day end - def test_end_of_day_when_zone_default_is_set + def test_end_of_day_when_zone_is_set zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] with_env_tz 'UTC' do with_tz_default zone do @@ -367,7 +367,7 @@ class DateExtCalculationsTest < ActiveSupport::TestCase end end - def test_xmlschema_when_zone_default_is_set + def test_xmlschema_when_zone_is_set with_env_tz 'UTC' do with_tz_default ActiveSupport::TimeZone['Eastern Time (US & Canada)'] do # UTC -5 assert_match(/^1980-02-28T00:00:00-05:?00$/, Date.new(1980, 2, 28).xmlschema) @@ -376,6 +376,16 @@ class DateExtCalculationsTest < ActiveSupport::TestCase end end + if RUBY_VERSION < '1.9' + def test_rfc3339 + assert_equal('1980-02-28', Date.new(1980, 2, 28).rfc3339) + end + + def test_iso8601 + assert_equal('1980-02-28', Date.new(1980, 2, 28).iso8601) + end + end + def test_today Date.stubs(:current).returns(Date.new(2000, 1, 1)) assert_equal false, Date.new(1999, 12, 31).today? @@ -397,7 +407,7 @@ class DateExtCalculationsTest < ActiveSupport::TestCase assert_equal true, Date.new(2000,1,2).future? end - def test_current_returns_date_today_when_zone_default_not_set + def test_current_returns_date_today_when_zone_not_set with_env_tz 'US/Central' do Time.stubs(:now).returns Time.local(1999, 12, 31, 23) assert_equal Date.new(1999, 12, 31), Date.today @@ -405,15 +415,15 @@ class DateExtCalculationsTest < ActiveSupport::TestCase end end - def test_current_returns_time_zone_today_when_zone_default_set - Time.zone_default = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + def test_current_returns_time_zone_today_when_zone_is_set + Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] with_env_tz 'US/Central' do Time.stubs(:now).returns Time.local(1999, 12, 31, 23) assert_equal Date.new(1999, 12, 31), Date.today assert_equal Date.new(2000, 1, 1), Date.current end ensure - Time.zone_default = nil + Time.zone = nil end def test_date_advance_should_not_change_passed_options_hash @@ -431,11 +441,11 @@ class DateExtCalculationsTest < ActiveSupport::TestCase end def with_tz_default(tz = nil) - old_tz = Time.zone_default - Time.zone_default = tz + old_tz = Time.zone + Time.zone = tz yield ensure - Time.zone_default = old_tz + Time.zone = old_tz end end diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index 7d993d84e2..456736cbad 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -38,6 +38,8 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase assert_equal Time.utc_time(2039, 2, 21, 10, 11, 12), DateTime.new(2039, 2, 21, 10, 11, 12, 0, 0).to_time # DateTimes with offsets other than 0 are returned unaltered assert_equal DateTime.new(2005, 2, 21, 10, 11, 12, Rational(-5, 24)), DateTime.new(2005, 2, 21, 10, 11, 12, Rational(-5, 24)).to_time + # Fractional seconds are preserved + assert_equal Time.utc(2005, 2, 21, 10, 11, 12, 256), DateTime.new(2005, 2, 21, 10, 11, 12 + Rational(256, 1000000), 0).to_time end def test_civil_from_format @@ -280,21 +282,21 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase assert_equal true, DateTime.civil(2005,2,10,20,30,46).future? end - def test_current_returns_date_today_when_zone_default_not_set + def test_current_returns_date_today_when_zone_is_not_set with_env_tz 'US/Eastern' do Time.stubs(:now).returns Time.local(1999, 12, 31, 23, 59, 59) assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current end end - def test_current_returns_time_zone_today_when_zone_default_set - Time.zone_default = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + def test_current_returns_time_zone_today_when_zone_is_set + Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] with_env_tz 'US/Eastern' do Time.stubs(:now).returns Time.local(1999, 12, 31, 23, 59, 59) assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current end ensure - Time.zone_default = nil + Time.zone = nil end def test_current_without_time_zone diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index bb453b8d7f..c0b529d9f8 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'active_support/time' +require 'active_support/json' class DurationTest < ActiveSupport::TestCase def test_is_a @@ -88,8 +89,8 @@ class DurationTest < ActiveSupport::TestCase assert_in_delta((7 * 24 * 1.7).hours.ago(t), 1.7.weeks.ago(t), 1) end - def test_since_and_ago_anchored_to_time_now_when_time_zone_default_not_set - Time.zone_default = nil + def test_since_and_ago_anchored_to_time_now_when_time_zone_is_not_set + Time.zone = nil with_env_tz 'US/Eastern' do Time.stubs(:now).returns Time.local(2000) # since @@ -101,8 +102,8 @@ class DurationTest < ActiveSupport::TestCase end end - def test_since_and_ago_anchored_to_time_zone_now_when_time_zone_default_set - Time.zone_default = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + def test_since_and_ago_anchored_to_time_zone_now_when_time_zone_is_set + Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] with_env_tz 'US/Eastern' do Time.stubs(:now).returns Time.local(2000) # since @@ -115,7 +116,7 @@ class DurationTest < ActiveSupport::TestCase assert_equal 'Eastern Time (US & Canada)', 5.seconds.ago.time_zone.name end ensure - Time.zone_default = nil + Time.zone = nil end def test_adding_hours_across_dst_boundary @@ -138,6 +139,10 @@ class DurationTest < ActiveSupport::TestCase assert_equal counter, 60 end + def test_to_json + assert_equal '172800', 2.days.to_json + end + protected def with_env_tz(new_tz = 'US/Eastern') old_tz, ENV['TZ'] = ENV['TZ'], new_tz diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 74223dd7f2..a0479d45ac 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -4,6 +4,7 @@ require 'bigdecimal' require 'active_support/core_ext/string/access' require 'active_support/ordered_hash' require 'active_support/core_ext/object/conversions' +require 'active_support/inflections' class HashExtTest < Test::Unit::TestCase class IndifferentHash < HashWithIndifferentAccess diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb index 75404ec0e1..a95cf1591f 100644 --- a/activesupport/test/core_ext/module_test.rb +++ b/activesupport/test/core_ext/module_test.rb @@ -26,14 +26,10 @@ module Yz end end -class De -end - Somewhere = Struct.new(:street, :city) Someone = Struct.new(:name, :place) do delegate :street, :city, :to_f, :to => :place - delegate :state, :to => :@place delegate :upcase, :to => "place.city" end diff --git a/activesupport/test/core_ext/numeric_ext_test.rb b/activesupport/test/core_ext/numeric_ext_test.rb index 6ef4e37b26..3a2452b4b0 100644 --- a/activesupport/test/core_ext/numeric_ext_test.rb +++ b/activesupport/test/core_ext/numeric_ext_test.rb @@ -89,8 +89,8 @@ class NumericExtTimeAndDateTimeTest < Test::Unit::TestCase assert_equal DateTime.civil(2005,2,28,15,15,10), DateTime.civil(2004,2,29,15,15,10) + 1.year end - def test_since_and_ago_anchored_to_time_now_when_time_zone_default_not_set - Time.zone_default = nil + def test_since_and_ago_anchored_to_time_now_when_time_zone_is_not_set + Time.zone = nil with_env_tz 'US/Eastern' do Time.stubs(:now).returns Time.local(2000) # since @@ -102,8 +102,8 @@ class NumericExtTimeAndDateTimeTest < Test::Unit::TestCase end end - def test_since_and_ago_anchored_to_time_zone_now_when_time_zone_default_set - Time.zone_default = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + def test_since_and_ago_anchored_to_time_zone_now_when_time_zone_is_set + Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] with_env_tz 'US/Eastern' do Time.stubs(:now).returns Time.local(2000) # since @@ -116,7 +116,7 @@ class NumericExtTimeAndDateTimeTest < Test::Unit::TestCase assert_equal 'Eastern Time (US & Canada)', 5.ago.time_zone.name end ensure - Time.zone_default = nil + Time.zone = nil end protected diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb index 398e6ca9b2..5d68b198f2 100644 --- a/activesupport/test/core_ext/object_and_class_ext_test.rb +++ b/activesupport/test/core_ext/object_and_class_ext_test.rb @@ -28,18 +28,6 @@ module Nested end end -module Bar - def bar; end -end - -module Baz - def baz; end -end - -class Foo - include Bar -end - class ObjectTests < ActiveSupport::TestCase class DuckTime def acts_like_time? diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb index 5701eeef28..1424fa4aca 100644 --- a/activesupport/test/core_ext/range_ext_test.rb +++ b/activesupport/test/core_ext/range_ext_test.rb @@ -62,4 +62,16 @@ class RangeTest < Test::Unit::TestCase (1..10).step(2) {|i| array << i } assert_equal [1,3,5,7,9], array end + + if RUBY_VERSION < '1.9' + def test_cover + assert((1..3).cover?(2)) + assert !(1..3).cover?(4) + end + else + def test_cover_is_not_override + range = (1..3) + assert range.method(:include?) != range.method(:cover?) + end + end end diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index 53d497013a..891a6badac 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -533,19 +533,9 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase Time::DATE_FORMATS.delete(:custom) end - def test_to_date - assert_equal Date.new(2005, 2, 21), Time.local(2005, 2, 21, 17, 44, 30).to_date - end - - def test_to_datetime - assert_equal Time.utc(2005, 2, 21, 17, 44, 30).to_datetime, DateTime.civil(2005, 2, 21, 17, 44, 30, 0, 0) - with_env_tz 'US/Eastern' do - assert_equal Time.local(2005, 2, 21, 17, 44, 30).to_datetime, DateTime.civil(2005, 2, 21, 17, 44, 30, Rational(Time.local(2005, 2, 21, 17, 44, 30).utc_offset, 86400), 0) - end - with_env_tz 'NZ' do - assert_equal Time.local(2005, 2, 21, 17, 44, 30).to_datetime, DateTime.civil(2005, 2, 21, 17, 44, 30, Rational(Time.local(2005, 2, 21, 17, 44, 30).utc_offset, 86400), 0) - end - assert_equal ::Date::ITALY, Time.utc(2005, 2, 21, 17, 44, 30).to_datetime.start # use Ruby's default start value + def test_conversion_methods_are_publicized + assert Time.public_instance_methods.include?(:to_date) || Time.public_instance_methods.include?('to_date') + assert Time.public_instance_methods.include?(:to_datetime) || Time.public_instance_methods.include?('to_datetime') end def test_to_time diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index 5579c27215..bafa335a09 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -106,11 +106,11 @@ class TimeWithZoneTest < Test::Unit::TestCase end def test_to_yaml - assert_equal "--- 1999-12-31 19:00:00 -05:00\n", @twz.to_yaml + assert_match(/^--- 2000-01-01 00:00:00(\.0+)?\s*Z\n/, @twz.to_yaml) end def test_ruby_to_yaml - assert_equal "--- \n:twz: 2000-01-01 00:00:00 Z\n", {:twz => @twz}.to_yaml + assert_match(/---\s*\n:twz: 2000-01-01 00:00:00(\.0+)?\s*Z\n/, {:twz => @twz}.to_yaml) end def test_httpdate @@ -768,10 +768,10 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase end def test_localtime - Time.zone_default = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] assert_equal @dt.in_time_zone.localtime, @dt.in_time_zone.utc.to_time.getlocal ensure - Time.zone_default = nil + Time.zone = nil end def test_use_zone @@ -801,7 +801,7 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase assert_equal nil, Time.zone end - def test_time_zone_getter_and_setter_with_zone_default + def test_time_zone_getter_and_setter_with_zone_default_set Time.zone_default = ActiveSupport::TimeZone['Alaska'] assert_equal ActiveSupport::TimeZone['Alaska'], Time.zone Time.zone = ActiveSupport::TimeZone['Hawaii'] @@ -809,6 +809,7 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase Time.zone = nil assert_equal ActiveSupport::TimeZone['Alaska'], Time.zone ensure + Time.zone = nil Time.zone_default = nil end @@ -849,7 +850,7 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase assert_nil Time.zone end - def test_current_returns_time_now_when_zone_default_not_set + def test_current_returns_time_now_when_zone_not_set with_env_tz 'US/Eastern' do Time.stubs(:now).returns Time.local(2000) assert_equal false, Time.current.is_a?(ActiveSupport::TimeWithZone) @@ -857,8 +858,8 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase end end - def test_current_returns_time_zone_now_when_zone_default_set - Time.zone_default = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + def test_current_returns_time_zone_now_when_zone_set + Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] with_env_tz 'US/Eastern' do Time.stubs(:now).returns Time.local(2000) assert_equal true, Time.current.is_a?(ActiveSupport::TimeWithZone) @@ -866,7 +867,7 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase assert_equal Time.utc(2000), Time.current.time end ensure - Time.zone_default = nil + Time.zone = nil end protected diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index bc7f597f1d..ef017d7436 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -477,15 +477,15 @@ class DependenciesTest < Test::Unit::TestCase def test_references_should_work with_loading 'dependencies' do - c = ActiveSupport::Dependencies.ref("ServiceOne") + c = ActiveSupport::Dependencies.reference("ServiceOne") service_one_first = ServiceOne - assert_equal service_one_first, c.get + assert_equal service_one_first, c.get("ServiceOne") ActiveSupport::Dependencies.clear assert ! defined?(ServiceOne) service_one_second = ServiceOne - assert_not_equal service_one_first, c.get - assert_equal service_one_second, c.get + assert_not_equal service_one_first, c.get("ServiceOne") + assert_equal service_one_second, c.get("ServiceOne") end end diff --git a/activesupport/test/file_watcher_test.rb b/activesupport/test/file_watcher_test.rb new file mode 100644 index 0000000000..7b4d4be24f --- /dev/null +++ b/activesupport/test/file_watcher_test.rb @@ -0,0 +1,233 @@ +require 'abstract_unit' +require 'fssm' +require "fileutils" +require "timeout" + + +class FileWatcherTest < ActiveSupport::TestCase + class DumbBackend < ActiveSupport::FileWatcher::Backend + end + + def setup + @watcher = ActiveSupport::FileWatcher.new + + # In real life, the backend would take the path and use it to observe the file + # system. In our case, we will manually trigger the events for unit testing, + # so we can pass any path. + @backend = DumbBackend.new("RAILS_WOOT", @watcher) + + @payload = [] + @watcher.watch %r{^app/assets/.*\.scss$} do |pay| + pay.each do |status, files| + files.sort! + end + @payload << pay + end + end + + def test_use_triple_equals + fw = ActiveSupport::FileWatcher.new + called = [] + fw.watch("some_arbitrary_file.rb") do |file| + called << "omg" + end + fw.trigger(%w{ some_arbitrary_file.rb }) + assert_equal ['omg'], called + end + + def test_one_change + @backend.trigger("app/assets/main.scss" => :changed) + assert_equal({:changed => ["app/assets/main.scss"]}, @payload.first) + end + + def test_multiple_changes + @backend.trigger("app/assets/main.scss" => :changed, "app/assets/javascripts/foo.coffee" => :changed) + assert_equal([{:changed => ["app/assets/main.scss"]}], @payload) + end + + def test_multiple_changes_match + @backend.trigger("app/assets/main.scss" => :changed, "app/assets/print.scss" => :changed, "app/assets/javascripts/foo.coffee" => :changed) + assert_equal([{:changed => ["app/assets/main.scss", "app/assets/print.scss"]}], @payload) + end + + def test_multiple_state_changes + @backend.trigger("app/assets/main.scss" => :created, "app/assets/print.scss" => :changed) + assert_equal([{:changed => ["app/assets/print.scss"], :created => ["app/assets/main.scss"]}], @payload) + end + + def test_more_blocks + payload = [] + @watcher.watch %r{^config/routes\.rb$} do |pay| + payload << pay + end + + @backend.trigger "config/routes.rb" => :changed + assert_equal [:changed => ["config/routes.rb"]], payload + assert_equal [], @payload + end + + def test_overlapping_watchers + payload = [] + @watcher.watch %r{^app/assets/main\.scss$} do |pay| + payload << pay + end + + @backend.trigger "app/assets/print.scss" => :changed, "app/assets/main.scss" => :changed + assert_equal [:changed => ["app/assets/main.scss"]], payload + assert_equal [:changed => ["app/assets/main.scss", "app/assets/print.scss"]], @payload + end +end + +module FSSM::Backends + class Polling + def initialize_with_low_latency(options={}) + initialize_without_low_latency(options.merge(:latency => 0.1)) + end + alias_method_chain :initialize, :low_latency + end +end + +class FSSMFileWatcherTest < ActiveSupport::TestCase + class FSSMBackend < ActiveSupport::FileWatcher::Backend + def initialize(path, watcher) + super + + monitor = FSSM::Monitor.new + monitor.path(path, '**/*') do |p| + p.update { |base, relative| trigger relative => :changed } + p.delete { |base, relative| trigger relative => :deleted } + p.create { |base, relative| trigger relative => :created } + end + + @thread = Thread.new do + monitor.run + end + end + + def stop + @thread.kill + end + end + + def setup + Thread.abort_on_exception = true + + @payload = [] + @triggered = false + + @watcher = ActiveSupport::FileWatcher.new + + @path = path = File.expand_path("../tmp", __FILE__) + FileUtils.rm_rf path + + create "app/assets/main.scss", true + create "app/assets/javascripts/foo.coffee", true + create "app/assets/print.scss", true + create "app/assets/videos.scss", true + + @backend = FSSMBackend.new(path, @watcher) + + @watcher.watch %r{^app/assets/.*\.scss$} do |pay| + pay.each do |status, files| + files.sort! + end + @payload << pay + trigger + end + end + + def teardown + @backend.stop + Thread.abort_on_exception = false + end + + def create(path, past = false) + wait(past) do + path = File.join(@path, path) + FileUtils.mkdir_p(File.dirname(path)) + + FileUtils.touch(path) + File.utime(Time.now - 100, Time.now - 100, path) if past + end + end + + def change(path) + wait do + FileUtils.touch(File.join(@path, path)) + end + end + + def delete(path) + wait do + FileUtils.rm(File.join(@path, path)) + end + end + + def wait(past = false) + yield + return if past + + begin + Timeout.timeout(1) do + sleep 0.05 until @triggered + end + rescue Timeout::Error + end + + @triggered = false + end + + def trigger + @triggered = true + end + + def test_one_change + change "app/assets/main.scss" + assert_equal({:changed => ["app/assets/main.scss"]}, @payload.first) + end + + def test_multiple_changes + change "app/assets/main.scss" + change "app/assets/javascripts/foo.coffee" + assert_equal([{:changed => ["app/assets/main.scss"]}], @payload) + end + + def test_multiple_changes_match + change "app/assets/main.scss" + change "app/assets/print.scss" + change "app/assets/javascripts/foo.coffee" + assert_equal([{:changed => ["app/assets/main.scss"]}, {:changed => ["app/assets/print.scss"]}], @payload) + end + + def test_multiple_state_changes + create "app/assets/new.scss" + change "app/assets/print.scss" + delete "app/assets/videos.scss" + assert_equal([{:created => ["app/assets/new.scss"]}, {:changed => ["app/assets/print.scss"]}, {:deleted => ["app/assets/videos.scss"]}], @payload) + end + + def test_more_blocks + payload = [] + @watcher.watch %r{^config/routes\.rb$} do |pay| + payload << pay + trigger + end + + create "config/routes.rb" + assert_equal [{:created => ["config/routes.rb"]}], payload + assert_equal [], @payload + end + + def test_overlapping_watchers + payload = [] + @watcher.watch %r{^app/assets/main\.scss$} do |pay| + payload << pay + trigger + end + + change "app/assets/main.scss" + change "app/assets/print.scss" + assert_equal [{:changed => ["app/assets/main.scss"]}], payload + assert_equal [{:changed => ["app/assets/main.scss"]}, {:changed => ["app/assets/print.scss"]}], @payload + end +end diff --git a/activesupport/test/gzip_test.rb b/activesupport/test/gzip_test.rb index 2a24c0bd0d..f564e63f29 100644 --- a/activesupport/test/gzip_test.rb +++ b/activesupport/test/gzip_test.rb @@ -1,7 +1,18 @@ require 'abstract_unit' +require 'active_support/core_ext/object/blank' class GzipTest < Test::Unit::TestCase def test_compress_should_decompress_to_the_same_value assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World")) end -end
\ No newline at end of file + + def test_compress_should_return_a_binary_string + compressed = ActiveSupport::Gzip.compress('') + + if "".encoding_aware? + assert_equal Encoding.find('binary'), compressed.encoding + end + + assert !compressed.blank?, "a compressed blank string should not be blank" + end +end diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index 2990177bed..1670d9ee7d 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -22,20 +22,55 @@ class InflectorTest < Test::Unit::TestCase assert_equal "", ActiveSupport::Inflector.pluralize("") end + ActiveSupport::Inflector.inflections.uncountable.each do |word| + define_method "test_uncountability_of_#{word}" do + assert_equal word, ActiveSupport::Inflector.singularize(word) + assert_equal word, ActiveSupport::Inflector.pluralize(word) + assert_equal ActiveSupport::Inflector.pluralize(word), ActiveSupport::Inflector.singularize(word) + end + end + + def test_uncountable_word_is_not_greedy + uncountable_word = "ors" + countable_word = "sponsor" + + cached_uncountables = ActiveSupport::Inflector.inflections.uncountables + + ActiveSupport::Inflector.inflections.uncountable << uncountable_word + + assert_equal uncountable_word, ActiveSupport::Inflector.singularize(uncountable_word) + assert_equal uncountable_word, ActiveSupport::Inflector.pluralize(uncountable_word) + assert_equal ActiveSupport::Inflector.pluralize(uncountable_word), ActiveSupport::Inflector.singularize(uncountable_word) + + assert_equal "sponsor", ActiveSupport::Inflector.singularize(countable_word) + assert_equal "sponsors", ActiveSupport::Inflector.pluralize(countable_word) + assert_equal "sponsor", ActiveSupport::Inflector.singularize(ActiveSupport::Inflector.pluralize(countable_word)) + + ensure + ActiveSupport::Inflector.inflections.instance_variable_set :@uncountables, cached_uncountables + end + SingularToPlural.each do |singular, plural| - define_method "test_pluralize_#{singular}" do + define_method "test_pluralize_singular_#{singular}" do assert_equal(plural, ActiveSupport::Inflector.pluralize(singular)) assert_equal(plural.capitalize, ActiveSupport::Inflector.pluralize(singular.capitalize)) end end SingularToPlural.each do |singular, plural| - define_method "test_singularize_#{plural}" do + define_method "test_singularize_plural_#{plural}" do assert_equal(singular, ActiveSupport::Inflector.singularize(plural)) assert_equal(singular.capitalize, ActiveSupport::Inflector.singularize(plural.capitalize)) end end + SingularToPlural.each do |singular, plural| + define_method "test_pluralize_plural_#{plural}" do + assert_equal(plural, ActiveSupport::Inflector.pluralize(plural)) + assert_equal(plural.capitalize, ActiveSupport::Inflector.pluralize(plural.capitalize)) + end + end + def test_overwrite_previous_inflectors assert_equal("series", ActiveSupport::Inflector.singularize("series")) ActiveSupport::Inflector.inflections.singular "series", "serie" diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb index 59515dad32..2b144e5931 100644 --- a/activesupport/test/inflector_test_cases.rb +++ b/activesupport/test/inflector_test_cases.rb @@ -44,6 +44,7 @@ module InflectorTestCases "datum" => "data", "medium" => "media", + "stadium" => "stadia", "analysis" => "analyses", "node_child" => "node_children", diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb index d2e3efaa6b..24d9f88c09 100644 --- a/activesupport/test/json/decoding_test.rb +++ b/activesupport/test/json/decoding_test.rb @@ -19,6 +19,12 @@ class TestJSONDecoding < ActiveSupport::TestCase %({"a": "2007-01-01 01:12:34 Z"}) => {'a' => Time.utc(2007, 1, 1, 1, 12, 34)}, # no time zone %({"a": "2007-01-01 01:12:34"}) => {'a' => "2007-01-01 01:12:34"}, + # invalid date + %({"a": "1089-10-40"}) => {'a' => "1089-10-40"}, + # xmlschema date notation + %({"a": "2009-08-10T19:01:02Z"}) => {'a' => Time.utc(2009, 8, 10, 19, 1, 2)}, + %({"a": "2009-08-10T19:01:02+02:00"}) => {'a' => Time.utc(2009, 8, 10, 17, 1, 2)}, + %({"a": "2009-08-10T19:01:02-05:00"}) => {'a' => Time.utc(2009, 8, 11, 00, 1, 2)}, # needs to be *exact* %({"a": " 2007-01-01 01:12:34 Z "}) => {'a' => " 2007-01-01 01:12:34 Z "}, %({"a": "2007-01-01 : it's your birthday"}) => {'a' => "2007-01-01 : it's your birthday"}, @@ -41,7 +47,11 @@ class TestJSONDecoding < ActiveSupport::TestCase [{'d' => Date.new(1970, 1, 1), 's' => ' escape'},{'d' => Date.new(1970, 1, 1), 's' => ' escape'}], %q([{"d":"1970-01-01","s":"http:\/\/example.com"},{"d":"1970-01-01","s":"http:\/\/example.com"}]) => [{'d' => Date.new(1970, 1, 1), 's' => 'http://example.com'}, - {'d' => Date.new(1970, 1, 1), 's' => 'http://example.com'}] + {'d' => Date.new(1970, 1, 1), 's' => 'http://example.com'}], + # tests escaping of "\n" char with Yaml backend + %q({"a":"\n"}) => {"a"=>"\n"}, + %q({"a":"\u000a"}) => {"a"=>"\n"}, + %q({"a":"Line1\u000aLine2"}) => {"a"=>"Line1\nLine2"} } # load the default JSON backend @@ -57,9 +67,7 @@ class TestJSONDecoding < ActiveSupport::TestCase ActiveSupport.parse_json_times = true silence_warnings do ActiveSupport::JSON.with_backend backend do - assert_nothing_raised do - assert_equal expected, ActiveSupport::JSON.decode(json) - end + assert_equal expected, ActiveSupport::JSON.decode(json) end end end diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index e0494de6e4..d5fcbf15b7 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -1,5 +1,6 @@ # encoding: utf-8 require 'abstract_unit' +require 'active_support/core_ext/string/inflections' require 'active_support/json' class TestJSONEncoding < Test::Unit::TestCase @@ -214,6 +215,36 @@ class TestJSONEncoding < Test::Unit::TestCase assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json) end + def test_struct_encoding + Struct.new('UserNameAndEmail', :name, :email) + Struct.new('UserNameAndDate', :name, :date) + Struct.new('Custom', :name, :sub) + user_email = Struct::UserNameAndEmail.new 'David', 'sample@example.com' + user_birthday = Struct::UserNameAndDate.new 'David', Date.new(2010, 01, 01) + custom = Struct::Custom.new 'David', user_birthday + + + json_strings = "" + json_string_and_date = "" + json_custom = "" + + assert_nothing_raised do + json_strings = user_email.to_json + json_string_and_date = user_birthday.to_json + json_custom = custom.to_json + end + + assert_equal({"name" => "David", + "sub" => { + "name" => "David", + "date" => "2010/01/01" }}, JSON.parse(json_custom)) + + assert_equal({"name" => "David", "email" => "sample@example.com"}, + JSON.parse(json_strings)) + + assert_equal({"name" => "David", "date" => "2010/01/01"}, + JSON.parse(json_string_and_date)) + end protected diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index 9faa11efbc..7b48b3f85b 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -3,14 +3,19 @@ require 'abstract_unit' module Notifications class TestCase < ActiveSupport::TestCase def setup - ActiveSupport::Notifications.notifier = nil - @notifier = ActiveSupport::Notifications.notifier + @old_notifier = ActiveSupport::Notifications.notifier + @notifier = ActiveSupport::Notifications::Fanout.new + ActiveSupport::Notifications.notifier = @notifier @events = [] @named_events = [] @subscription = @notifier.subscribe { |*args| @events << event(*args) } @named_subscription = @notifier.subscribe("named.subscription") { |*args| @named_events << event(*args) } end + def teardown + ActiveSupport::Notifications.notifier = @old_notifier + end + private def event(*args) diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index 72088854fc..50168fa78f 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -78,20 +78,26 @@ class OrderedHashTest < Test::Unit::TestCase def test_each_key keys = [] - @ordered_hash.each_key { |k| keys << k } + assert_equal @ordered_hash, @ordered_hash.each_key { |k| keys << k } assert_equal @keys, keys + expected_class = RUBY_VERSION < '1.9' ? Enumerable::Enumerator : Enumerator + assert_kind_of expected_class, @ordered_hash.each_key end def test_each_value values = [] - @ordered_hash.each_value { |v| values << v } + assert_equal @ordered_hash, @ordered_hash.each_value { |v| values << v } assert_equal @values, values + expected_class = RUBY_VERSION < '1.9' ? Enumerable::Enumerator : Enumerator + assert_kind_of expected_class, @ordered_hash.each_value end def test_each values = [] - @ordered_hash.each {|key, value| values << value} + assert_equal @ordered_hash, @ordered_hash.each {|key, value| values << value} assert_equal @values, values + expected_class = RUBY_VERSION < '1.9' ? Enumerable::Enumerator : Enumerator + assert_kind_of expected_class, @ordered_hash.each end def test_each_with_index @@ -239,14 +245,14 @@ class OrderedHashTest < Test::Unit::TestCase def test_each_after_yaml_serialization values = [] - @deserialized_ordered_hash = YAML::load(YAML::dump(@ordered_hash)) + @deserialized_ordered_hash = YAML.load(YAML.dump(@ordered_hash)) @deserialized_ordered_hash.each {|key, value| values << value} assert_equal @values, values end def test_order_after_yaml_serialization - @deserialized_ordered_hash = YAML::load(YAML::dump(@ordered_hash)) + @deserialized_ordered_hash = YAML.load(YAML.dump(@ordered_hash)) assert_equal @keys, @deserialized_ordered_hash.keys assert_equal @values, @deserialized_ordered_hash.values @@ -255,12 +261,34 @@ class OrderedHashTest < Test::Unit::TestCase def test_order_after_yaml_serialization_with_nested_arrays @ordered_hash[:array] = %w(a b c) - @deserialized_ordered_hash = YAML::load(YAML::dump(@ordered_hash)) + @deserialized_ordered_hash = YAML.load(YAML.dump(@ordered_hash)) assert_equal @ordered_hash.keys, @deserialized_ordered_hash.keys assert_equal @ordered_hash.values, @deserialized_ordered_hash.values end + begin + require 'psych' + + def test_psych_serialize + @deserialized_ordered_hash = Psych.load(Psych.dump(@ordered_hash)) + + values = @deserialized_ordered_hash.map { |_, value| value } + assert_equal @values, values + end + + def test_psych_serialize_tag + yaml = Psych.dump(@ordered_hash) + assert_match '!omap', yaml + end + rescue LoadError + end + + def test_has_yaml_tag + @ordered_hash[:array] = %w(a b c) + assert_match '!omap', YAML.dump(@ordered_hash) + end + def test_update_sets_keys @updated_ordered_hash = ActiveSupport::OrderedHash.new @updated_ordered_hash.update(:name => "Bob") diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb index bf61f9e58c..a4e2acbb32 100644 --- a/activesupport/test/safe_buffer_test.rb +++ b/activesupport/test/safe_buffer_test.rb @@ -1,4 +1,10 @@ require 'abstract_unit' +begin + require 'psych' +rescue LoadError +end + +require 'yaml' class SafeBufferTest < ActiveSupport::TestCase def setup @@ -38,4 +44,20 @@ class SafeBufferTest < ActiveSupport::TestCase new_buffer = @buffer.to_s assert_equal ActiveSupport::SafeBuffer, new_buffer.class end + + def test_to_yaml + str = 'hello!' + buf = ActiveSupport::SafeBuffer.new str + yaml = buf.to_yaml + + assert_match(/^--- #{str}/, yaml) + assert_equal 'hello!', YAML.load(yaml) + end + + def test_nested + str = 'hello!' + data = { 'str' => ActiveSupport::SafeBuffer.new(str) } + yaml = YAML.dump data + assert_equal({'str' => str}, YAML.load(yaml)) + end end diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb index 7e65c63062..756d21b3e4 100644 --- a/activesupport/test/test_case_test.rb +++ b/activesupport/test/test_case_test.rb @@ -12,6 +12,10 @@ module ActiveSupport def puke(klass, name, e) @puked << [klass, name, e] end + + def options + nil + end end if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions diff --git a/activesupport/test/test_xml_mini.rb b/activesupport/test/test_xml_mini.rb index 309fa234bf..6dbcd1f40b 100644 --- a/activesupport/test/test_xml_mini.rb +++ b/activesupport/test/test_xml_mini.rb @@ -1,61 +1,100 @@ require 'abstract_unit' require 'active_support/xml_mini' +require 'active_support/builder' -class XmlMiniTest < Test::Unit::TestCase - def test_rename_key_dasherizes_by_default - assert_equal "my-key", ActiveSupport::XmlMini.rename_key("my_key") - end +module XmlMiniTest + class RenameKeyTest < 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_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_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_false - assert_equal "my_key", ActiveSupport::XmlMini.rename_key("my_key", :camelize => false) - end + def test_rename_key_camelizes_with_camelize_false + assert_equal "my_key", ActiveSupport::XmlMini.rename_key("my_key", :camelize => false) + end - def test_rename_key_camelizes_with_camelize_nil - assert_equal "my_key", ActiveSupport::XmlMini.rename_key("my_key", :camelize => nil) - end + def test_rename_key_camelizes_with_camelize_nil + assert_equal "my_key", ActiveSupport::XmlMini.rename_key("my_key", :camelize => nil) + 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_lower_camelizes_with_camelize_lower - assert_equal "myKey", ActiveSupport::XmlMini.rename_key("my_key", :camelize => :lower) - end + def test_rename_key_lower_camelizes_with_camelize_lower + assert_equal "myKey", ActiveSupport::XmlMini.rename_key("my_key", :camelize => :lower) + end - def test_rename_key_lower_camelizes_with_camelize_upper - assert_equal "MyKey", ActiveSupport::XmlMini.rename_key("my_key", :camelize => :upper) - end + def test_rename_key_lower_camelizes_with_camelize_upper + assert_equal "MyKey", ActiveSupport::XmlMini.rename_key("my_key", :camelize => :upper) + end - def test_rename_key_does_not_dasherize_leading_underscores - assert_equal "_id", ActiveSupport::XmlMini.rename_key("_id") - 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_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_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_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 - def test_rename_key_does_not_dasherize_multiple_leading_underscores - assert_equal "id__", ActiveSupport::XmlMini.rename_key("id__") + def test_rename_key_does_not_dasherize_multiple_leading_underscores + assert_equal "id__", ActiveSupport::XmlMini.rename_key("id__") + end end + class ToTagTest < ActiveSupport::TestCase + def assert_xml(xml) + assert_equal xml, @options[:builder].target! + end + + setup do + @xml = ActiveSupport::XmlMini + @options = {:skip_instruct => true, :builder => Builder::XmlMarkup.new} + end + + test "#to_tag accepts a callable object and passes options with the builder" do + @xml.to_tag(:some_tag, lambda {|o| o[:builder].br }, @options) + assert_xml "<br/>" + end + + test "#to_tag accepts a callable object and passes options and tag name" do + @xml.to_tag(:tag, lambda {|o, t| o[:builder].b(t) }, @options) + assert_xml "<b>tag</b>" + end + + test "#to_tag accepts an object responding to #to_xml and passes the options, where :root is key" do + obj = Object.new + obj.instance_eval do + def to_xml(options) options[:builder].yo(options[:root].to_s) end + end + + @xml.to_tag(:tag, obj, @options) + assert_xml "<yo>tag</yo>" + end + + test "#to_tag accepts arbitrary objects responding to #to_str" do + @xml.to_tag(:b, "Howdy", @options) + assert_xml "<b>Howdy</b>" + end + # TODO: test the remaining functions hidden in #to_tag. + end end |