diff options
author | Emilio Tagua <miloops@gmail.com> | 2011-02-15 12:01:04 -0300 |
---|---|---|
committer | Emilio Tagua <miloops@gmail.com> | 2011-02-15 12:01:04 -0300 |
commit | 8ee0b4414890f919594c1a388d987b5b7364a505 (patch) | |
tree | 6c6c6aa19da0eb8066d2f0b9b02b08f2cc696c29 /activesupport | |
parent | 348c0ec7c656b3691aa4e687565d28259ca0f693 (diff) | |
parent | c9f1ab5365319e087e1b010a3f90626a2b8f080b (diff) | |
download | rails-8ee0b4414890f919594c1a388d987b5b7364a505.tar.gz rails-8ee0b4414890f919594c1a388d987b5b7364a505.tar.bz2 rails-8ee0b4414890f919594c1a388d987b5b7364a505.zip |
Merge remote branch 'rails/master' into identity_map
Conflicts:
activerecord/examples/performance.rb
activerecord/lib/active_record/association_preload.rb
activerecord/lib/active_record/associations.rb
activerecord/lib/active_record/associations/association_proxy.rb
activerecord/lib/active_record/autosave_association.rb
activerecord/lib/active_record/base.rb
activerecord/lib/active_record/nested_attributes.rb
activerecord/test/cases/relations_test.rb
Diffstat (limited to 'activesupport')
54 files changed, 750 insertions, 313 deletions
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/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/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/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_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/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/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/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_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 282337d373..4a9ee5a769 100644 --- a/activesupport/lib/active_support/i18n_railtie.rb +++ b/activesupport/lib/active_support/i18n_railtie.rb @@ -24,9 +24,24 @@ module I18n 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/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/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/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/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..b4d7633e5f 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -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? diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index 7d993d84e2..8edb95b63a 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 diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index bb453b8d7f..6a01eeed6b 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 @@ -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/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/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..5c226c2d09 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 diff --git a/activesupport/test/file_watcher_test.rb b/activesupport/test/file_watcher_test.rb new file mode 100644 index 0000000000..3e577df5af --- /dev/null +++ b/activesupport/test/file_watcher_test.rb @@ -0,0 +1,75 @@ +require 'abstract_unit' + +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 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..60714a152d 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -22,6 +22,34 @@ 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 assert_equal(plural, ActiveSupport::Inflector.pluralize(singular)) 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_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 |