diff options
Diffstat (limited to 'activesupport')
-rw-r--r-- | activesupport/CHANGELOG | 7 | ||||
-rw-r--r-- | activesupport/lib/active_support/cache/file_store.rb | 1 | ||||
-rw-r--r-- | activesupport/lib/active_support/callbacks.rb | 138 | ||||
-rw-r--r-- | activesupport/lib/active_support/core_ext/date/calculations.rb | 8 | ||||
-rw-r--r-- | activesupport/lib/active_support/core_ext/date/conversions.rb | 12 | ||||
-rw-r--r-- | activesupport/lib/active_support/core_ext/hash/except.rb | 1 | ||||
-rw-r--r-- | activesupport/lib/active_support/dependencies.rb | 2 | ||||
-rw-r--r-- | activesupport/lib/active_support/json/decoding.rb | 2 | ||||
-rw-r--r-- | activesupport/lib/active_support/multibyte/chars.rb | 7 | ||||
-rw-r--r-- | activesupport/lib/active_support/ordered_hash.rb | 21 | ||||
-rw-r--r-- | activesupport/lib/active_support/xml_mini/rexml.rb | 2 | ||||
-rw-r--r-- | activesupport/test/core_ext/date_ext_test.rb | 51 | ||||
-rw-r--r-- | activesupport/test/core_ext/hash_ext_test.rb | 15 | ||||
-rw-r--r-- | activesupport/test/ordered_hash_test.rb | 30 |
14 files changed, 194 insertions, 103 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 6566d3fa06..13ec3c3775 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,3 +1,10 @@ +*Rails 3.0.0 [Release Candidate] (unreleased)* + +* ActiveSupport::OrderedHash#merge and #merge! accept a block. #4838 [Paul Mucur, fxn] + +* Date#since, #ago, #beginning_of_day, #end_of_day, and #xmlschema honor now the user time zone if set. [Geoff Buesing] + + *Rails 3.0.0 [beta 4] (June 8th, 2010)* * Extracted String#truncate from TextHelper#truncate [DHH] diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index fc225e77a2..84f6f29572 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/file/atomic' +require 'active_support/core_ext/string/conversions' module ActiveSupport module Cache diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index ba15043bde..3ff33eea72 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/class/inheritable_attributes' +require 'active_support/core_ext/class/subclasses' require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/kernel/singleton_class' @@ -383,21 +384,12 @@ module ActiveSupport # key. See #define_callbacks for more information. # def __define_runner(symbol) #:nodoc: - send("_update_#{symbol}_superclass_callbacks") body = send("_#{symbol}_callbacks").compile(nil) silence_warnings do undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks") class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def _run_#{symbol}_callbacks(key = nil, &blk) - @_initialized_#{symbol}_callbacks ||= begin - if self.class.send("_update_#{symbol}_superclass_callbacks") - self.class.__define_runner(#{symbol.inspect}) - return _run_#{symbol}_callbacks(key, &blk) - end - true - end - if key name = "_run__\#{self.class.name.hash.abs}__#{symbol}__\#{key.hash.abs}__callbacks" @@ -432,16 +424,15 @@ module ActiveSupport # CallbackChain. # def __update_callbacks(name, filters = [], block = nil) #:nodoc: - send("_update_#{name}_superclass_callbacks") - type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before options = filters.last.is_a?(Hash) ? filters.pop : {} filters.unshift(block) if block - chain = send("_#{name}_callbacks") - yield chain, type, filters, options if block_given? - - __define_runner(name) + ([self] + self.descendents).each do |target| + chain = target.send("_#{name}_callbacks") + yield chain, type, filters, options + target.__define_runner(name) + end end # Set callbacks for a previously defined callback. @@ -454,11 +445,13 @@ module ActiveSupport # Use skip_callback to skip any defined one. # # When creating or skipping callbacks, you can specify conditions that - # are always the same for a given key. For instance, in ActionPack, + # are always the same for a given key. For instance, in Action Pack, # we convert :only and :except conditions into per-key conditions. # # before_filter :authenticate, :except => "index" + # # becomes + # # dispatch_callback :before, :authenticate, :per_key => {:unless => proc {|c| c.action_name == "index"}} # # Per-Key conditions are evaluated only once per use of a given key. @@ -471,14 +464,18 @@ module ActiveSupport # is a speed improvement for ActionPack. # def set_callback(name, *filter_list, &block) + mapped = nil + __update_callbacks(name, filter_list, block) do |chain, type, filters, options| - filters.map! do |filter| - removed = chain.delete_if {|c| c.matches?(type, filter) } - send("_removed_#{name}_callbacks").push(*removed) + mapped ||= filters.map do |filter| Callback.new(chain, filter, type, options.dup, self) end - options[:prepend] ? chain.unshift(*filters) : chain.push(*filters) + filters.each do |filter| + chain.delete_if {|c| c.matches?(type, filter) } + end + + options[:prepend] ? chain.unshift(*mapped) : chain.push(*mapped) end end @@ -496,7 +493,6 @@ module ActiveSupport end chain.delete(filter) - send("_removed_#{name}_callbacks") << filter end end end @@ -505,53 +501,76 @@ module ActiveSupport # def reset_callbacks(symbol) callbacks = send("_#{symbol}_callbacks") + + self.descendents.each do |target| + chain = target.send("_#{symbol}_callbacks") + callbacks.each { |c| chain.delete(c) } + target.__define_runner(symbol) + end + callbacks.clear - send("_removed_#{symbol}_callbacks").concat(callbacks) __define_runner(symbol) end - # Define callbacks types. - # - # ==== Example + # Defines callbacks types: # # define_callbacks :validate # - # ==== Options + # This macro accepts the following options: # # * <tt>:terminator</tt> - Indicates when a before filter is considered # to be halted. # # 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". + # 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. # # * <tt>:rescuable</tt> - By default, after filters are not executed if - # the given block or an before_filter raises an error. Supply :rescuable => true - # to change this behavior. + # the given block or a before filter raises an error. Set this option to + # true to change this behavior. # - # * <tt>:scope</tt> - Show which methods should be executed when a class - # is given as callback: + # * <tt>:scope</tt> - Indicates which methods should be executed when a class + # is given as callback. Defaults to <tt>[:kind]</tt>. # - # define_callbacks :filters, :scope => [ :kind ] + # class Audit + # def before(caller) + # puts 'Audit: before is called' + # end # - # When a class is given: + # def before_save(caller) + # puts 'Audit: before_save is called' + # end + # end # - # before_filter MyFilter + # class Account + # include ActiveSupport::Callbacks # - # It will call the type of the filter in the given class, which in this - # case, is "before". + # define_callbacks :save + # set_callback :save, :before, Audit.new # - # If, for instance, you supply the given scope: + # def save + # run_callbacks :save do + # puts 'save in main' + # end + # end + # end # - # define_callbacks :validate, :scope => [ :kind, :name ] + # In the above case whenever you save an account the method <tt>Audit#before</tt> will + # be called. On the other hand # - # It will call "#{kind}_#{name}" in the given class. So "before_validate" - # will be called in the class below: + # define_callbacks :save, :scope => [:kind, :name] # - # before_validate MyValidation + # 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". # - # Defaults to :kind. + # A declaration like + # + # define_callbacks :save, :scope => [:name] + # + # would call <tt>Audit#save</tt>. # def define_callbacks(*callbacks) config = callbacks.last.is_a?(Hash) ? callbacks.pop : {} @@ -559,39 +578,6 @@ module ActiveSupport extlib_inheritable_reader("_#{callback}_callbacks") do CallbackChain.new(callback, config) end - - extlib_inheritable_reader("_removed_#{callback}_callbacks") do - [] - end - - class_eval <<-METHOD, __FILE__, __LINE__ + 1 - def self._#{callback}_superclass_callbacks - if superclass.respond_to?(:_#{callback}_callbacks) - superclass._#{callback}_callbacks + superclass._#{callback}_superclass_callbacks - else - [] - end - end - - def self._update_#{callback}_superclass_callbacks - changed, index = false, 0 - - callbacks = (_#{callback}_superclass_callbacks - - _#{callback}_callbacks) - _removed_#{callback}_callbacks - - callbacks.each do |callback| - if new_index = _#{callback}_callbacks.index(callback) - index = new_index + 1 - else - changed = true - _#{callback}_callbacks.insert(index, callback) - index = index + 1 - end - end - changed - end - METHOD - __define_runner(callback) 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 579079d4f1..28fec5394f 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -57,19 +57,19 @@ class Date # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00) # and then subtracts the specified number of seconds def ago(seconds) - to_time.since(-seconds) + to_time_in_current_zone.since(-seconds) end # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00) # and then adds the specified number of seconds def since(seconds) - to_time.since(seconds) + to_time_in_current_zone.since(seconds) end alias :in :since # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00) def beginning_of_day - to_time + to_time_in_current_zone end alias :midnight :beginning_of_day alias :at_midnight :beginning_of_day @@ -77,7 +77,7 @@ class Date # Converts Date to a Time (or DateTime if necessary) with the time portion set to the end of the day (23:59:59) def end_of_day - to_time.end_of_day + to_time_in_current_zone.end_of_day end def plus_with_duration(other) #:nodoc: diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb index 13ef703f49..ba17b0a06b 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -81,6 +81,16 @@ class Date def to_time(form = :local) ::Time.send("#{form}_time", year, month, day) end + + # 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 + def to_time_in_current_zone + if ::Time.zone_default + ::Time.zone.local(year, month, day) + else + to_time + end + end # Converts a Date instance to a DateTime, where the time is set to the beginning of the day # and UTC offset is set to 0. @@ -94,6 +104,6 @@ class Date end if RUBY_VERSION < '1.9' def xmlschema - to_time.xmlschema + to_time_in_current_zone.xmlschema end end diff --git a/activesupport/lib/active_support/core_ext/hash/except.rb b/activesupport/lib/active_support/core_ext/hash/except.rb index 207801d3a7..89729df258 100644 --- a/activesupport/lib/active_support/core_ext/hash/except.rb +++ b/activesupport/lib/active_support/core_ext/hash/except.rb @@ -17,7 +17,6 @@ class Hash # Replaces the hash without the given keys. def except!(*keys) - keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) keys.each { |key| delete(key) } self end diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index d48da11bf3..e8210dfe37 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -153,7 +153,7 @@ module ActiveSupport #:nodoc: nesting.each do |namespace| begin - return Dependencies.load_missing_constant namespace.constantize, const_name + return Dependencies.load_missing_constant Inflector.constantize(namespace), const_name rescue NoMethodError then raise rescue NameError => e error ||= e diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb index 04ff316a44..c1f6330c6c 100644 --- a/activesupport/lib/active_support/json/decoding.rb +++ b/activesupport/lib/active_support/json/decoding.rb @@ -22,7 +22,7 @@ module ActiveSupport if name.is_a?(Module) @backend = name else - require "active_support/json/backends/#{name.to_s.downcase}.rb" + require "active_support/json/backends/#{name.to_s.downcase}" @backend = ActiveSupport::JSON::Backends::const_get(name) end @parse_error = @backend::ParseError diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 1eb6e8748b..d6ccb4bac1 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -34,7 +34,6 @@ module ActiveSupport #:nodoc: # # ActiveSupport::Multibyte.proxy_class = CharsForUTF32 class Chars - attr_reader :wrapped_string alias to_s wrapped_string alias to_str wrapped_string @@ -45,14 +44,16 @@ module ActiveSupport #:nodoc: @wrapped_string = string @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen? end - - undef <=> else def initialize(string) #:nodoc: @wrapped_string = string end end + def <=>(other) + @wrapped_string <=> other + end + # Forward all undefined methods to the wrapped string. def method_missing(method, *args, &block) if method.to_s =~ /!$/ diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index e1a2866863..91de722748 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -23,6 +23,17 @@ module ActiveSupport # Hash is ordered in Ruby 1.9! if RUBY_VERSION < '1.9' + + # In MRI the Hash class is core and written in C. In particular, methods are + # programmed with explicit C function calls and polymorphism is not honored. + # + # For example, []= is crucial in this implementation to maintain the @keys + # array but hash.c invokes rb_hash_aset() originally. This prevents method + # reuse through inheritance and forces us to reimplement stuff. + # + # For instance, we cannot use the inherited #merge! because albeit the algorithm + # itself would work, our []= is not being called at all by the C code. + def initialize(*args, &block) super @keys = [] @@ -130,12 +141,16 @@ module ActiveSupport end def merge!(other_hash) - other_hash.each {|k,v| self[k] = v } + if block_given? + other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v } + else + other_hash.each { |k, v| self[k] = v } + end self end - def merge(other_hash) - dup.merge!(other_hash) + def merge(other_hash, &block) + dup.merge!(other_hash, &block) end # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not. diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb index 863d66a91d..a58f22ee5d 100644 --- a/activesupport/lib/active_support/xml_mini/rexml.rb +++ b/activesupport/lib/active_support/xml_mini/rexml.rb @@ -11,7 +11,7 @@ module ActiveSupport # Parse an XML Document string or IO into a simple hash # # Same as XmlSimple::xml_in but doesn't shoot itself in the foot, - # and uses the defaults from ActiveSupport + # and uses the defaults from Active Support. # # data:: # XML Document string or IO to parse diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index 3e003b3ed4..59c168d33d 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -286,19 +286,59 @@ class DateExtCalculationsTest < ActiveSupport::TestCase def test_since 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 + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + with_env_tz 'UTC' do + with_tz_default zone do + assert_equal zone.local(2005,2,21,0,0,45), Date.new(2005,2,21).since(45) + assert_equal zone, Date.new(2005,2,21).since(45).time_zone + end + end + end def test_ago 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 + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + with_env_tz 'UTC' do + with_tz_default zone do + assert_equal zone.local(2005,2,20,23,59,15), Date.new(2005,2,21).ago(45) + assert_equal zone, Date.new(2005,2,21).ago(45).time_zone + end + end + end def test_beginning_of_day 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 + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + with_env_tz 'UTC' do + with_tz_default zone do + assert_equal zone.local(2005,2,21,0,0,0), Date.new(2005,2,21).beginning_of_day + assert_equal zone, Date.new(2005,2,21).beginning_of_day.time_zone + end + end + end def test_end_of_day 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 + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + with_env_tz 'UTC' do + with_tz_default zone do + assert_equal zone.local(2005,2,21,23,59,59,999999.999), Date.new(2005,2,21).end_of_day + assert_equal zone, Date.new(2005,2,21).end_of_day.time_zone + end + end + end + def test_xmlschema with_env_tz 'US/Eastern' do assert_match(/^1980-02-28T00:00:00-05:?00$/, Date.new(1980, 2, 28).xmlschema) @@ -310,6 +350,15 @@ class DateExtCalculationsTest < ActiveSupport::TestCase end end end + + def test_xmlschema_when_zone_default_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) + assert_match(/^1980-06-28T00:00:00-04:?00$/, Date.new(1980, 6, 28).xmlschema) + end + end + end def test_today Date.stubs(:current).returns(Date.new(2000, 1, 1)) diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index b2a9731578..7b2c10908f 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -940,13 +940,14 @@ class HashToXmlTest < Test::Unit::TestCase end def test_expansion_count_is_limited - expected = { - 'ActiveSupport::XmlMini_REXML' => 'RuntimeError', - 'ActiveSupport::XmlMini_Nokogiri' => 'Nokogiri::XML::SyntaxError', - 'ActiveSupport::XmlMini_NokogiriSAX' => 'RuntimeError', - 'ActiveSupport::XmlMini_LibXML' => 'LibXML::XML::Error', - 'ActiveSupport::XmlMini_LibXMLSAX' => 'LibXML::XML::Error', - }[ActiveSupport::XmlMini.backend.name].constantize + expected = + case ActiveSupport::XmlMini.backend.name + when 'ActiveSupport::XmlMini_REXML'; RuntimeError + when 'ActiveSupport::XmlMini_Nokogiri'; Nokogiri::XML::SyntaxError + when 'ActiveSupport::XmlMini_NokogiriSAX'; RuntimeError + when 'ActiveSupport::XmlMini_LibXML'; LibXML::XML::Error + when 'ActiveSupport::XmlMini_LibXMLSAX'; LibXML::XML::Error + end assert_raise expected do attack_xml = <<-EOT diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index d070206d44..0f36f5204d 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -141,10 +141,32 @@ class OrderedHashTest < Test::Unit::TestCase merged = @ordered_hash.merge other_hash assert_equal merged.length, @ordered_hash.length + other_hash.length assert_equal @keys + ['purple', 'violet'], merged.keys + end + + def test_merge_with_block + hash = ActiveSupport::OrderedHash.new + hash[:a] = 0 + hash[:b] = 0 + merged = hash.merge(:b => 2, :c => 7) do |key, old_value, new_value| + new_value + 1 + end + + assert_equal 0, merged[:a] + assert_equal 3, merged[:b] + assert_equal 7, merged[:c] + end + + def test_merge_bang_with_block + hash = ActiveSupport::OrderedHash.new + hash[:a] = 0 + hash[:b] = 0 + hash.merge!(:a => 1, :c => 7) do |key, old_value, new_value| + new_value + 3 + end - @ordered_hash.merge! other_hash - assert_equal @ordered_hash, merged - assert_equal @ordered_hash.keys, merged.keys + assert_equal 4, hash[:a] + assert_equal 0, hash[:b] + assert_equal 7, hash[:c] end def test_shift @@ -152,7 +174,7 @@ class OrderedHashTest < Test::Unit::TestCase assert_equal [@keys.first, @values.first], pair assert !@ordered_hash.keys.include?(pair.first) end - + def test_keys original = @ordered_hash.keys.dup @ordered_hash.keys.pop |