diff options
Diffstat (limited to 'activesupport')
64 files changed, 1191 insertions, 1126 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 84a03825dc..493c169803 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,507 +1,33 @@ -## Rails 4.0.0 (unreleased) ## +* Replace `multi_json` with `json`. -* An `ActiveSupport::Subscriber` class has been extracted from - `ActiveSupport::LogSubscriber`, allowing you to use the event attachment - API for other kinds of subscribers. + Since Rails requires Ruby 1.9 and since Ruby 1.9 includes `json` in the standard library, + `multi_json` is no longer necessary. - *Daniel Schierbeck* - -* `Class#class_attribute` accepts an `instance_predicate` option which - defaults to `true`. If set to `false` the predicate method will not - be defined. - - *Agis Anastasopoulos* - -* `fast_xs` support has been removed. Use `String#encode(xml: :attr)`. - -* `ActiveSupport::Notifications::Instrumenter#instrument` should - yield its payload. - - *stopdropandrew* - -* `ActiveSupport::TimeWithZone` raises `NoMethodError` in proper context. - Fixes #9772. - - *Yves Senn* - -* Fix deletion of empty directories in `ActiveSupport::Cache::FileStore`. - - *Charles Jones* - - -## Rails 4.0.0.beta1 (February 25, 2013) ## - -* Improve singularizing a singular for multiple cases. - Fixes #2608 #1825 #2395. - - Example: - - # Before - 'address'.singularize # => 'addres' - - # After - 'address'.singularize # => 'address' - - *Mark McSpadden* - -* Prevent `DateTime#change` from truncating the second fraction, when seconds - do not need to be changed. - - *Chris Baynes* - -* Added `ActiveSupport::TimeWithZone#to_r` for `Time#at` compatibility. - - Before this change: - - Time.zone = 'Tokyo' - time = Time.zone.now - time == Time.at(time) # => false - - After the change: - - Time.zone = 'Tokyo' - time = Time.zone.now - time == Time.at(time) # => true + *Erik Michaels-Ober* - *stopdropandrew* +* Added escaping of U+2028 and U+2029 inside the json encoder. + These characters are legal in JSON but break the Javascript interpreter. + After escaping them, the JSON is still legal and can be parsed by Javascript. -* `ActiveSupport::NumberHelper#number_to_human` returns the number unaltered when - the given units hash does not contain the needed key, e.g. when the number provided - is less than the largest key provided. - Fixes #9269. + *Mario Caropreso + Viktor Kelemen + zackham* - Examples: +* Fix skipping object callbacks using metadata fetched via callback chain + inspection methods (`_*_callbacks`) - number_to_human(123, units: {}) # => 123 - number_to_human(123, units: { thousand: 'k' }) # => 123 + *Sean Walbran* - *Michael Hoffman* +* Add a `fetch_multi` method to the cache stores. The method provides + an easy to use API for fetching multiple values from the cache. -* Added `beginning_of_minute` support to core ext calculations for `Time` and `DateTime`. - - *Gagan Awhad* - -* Add `:nsec` date format. - - *Jamie Gaskins* - -* `ActiveSupport::Gzip.compress` allows two optional arguments for compression - level and strategy. - - *Beyond* - -* Modify `TimeWithZone#as_json` to include 3 decimal places of sub-second accuracy - by default, which is optional as per the ISO8601 spec, but extremely useful. Also - the default behaviour of `Date#toJSON()` in recent versions of Chrome, Safari and - Firefox. - - *James Harton* - -* Improve `String#squish` to handle Unicode whitespace. *Antoine Lyset* - -* Standardise on `to_time` returning an instance of `Time` in the local system timezone - across `String`, `Time`, `Date`, `DateTime` and `ActiveSupport::TimeWithZone`. - - *Andrew White* - -* Extract `ActiveSupport::Testing::Performance` into https://github.com/rails/rails-perftest - You can add the gem to your `Gemfile` to keep using performance tests. - - gem 'rails-perftest' - - *Yves Senn* - -* `Hash.from_xml` raises when it encounters `type="symbol"` or `type="yaml"`. - Use `Hash.from_trusted_xml` to parse this XML. - - CVE-2013-0156 - - *Jeremy Kemper* - -* Deprecate `assert_present` and `assert_blank` in favor of - `assert object.blank?` and `assert object.present?` - - *Yves Senn* - -* Change `String#to_date` to use `Date.parse`. This gives more consistent error - messages and allows the use of partial dates. - - "gibberish".to_date => Argument Error: invalid date - "3rd Feb".to_date => Sun, 03 Feb 2013 - - *Kelly Stannard* - -* It's now possible to compare `Date`, `DateTime`, `Time` and `TimeWithZone` - with `Float::INFINITY`. This allows to create date/time ranges with one infinite bound. Example: - range = Range.new(Date.today, Float::INFINITY) - - Also it's possible to check inclusion of date/time in range with conversion. - - range.include?(Time.now + 1.year) # => true - range.include?(DateTime.now + 1.year) # => true - - *Alexander Grebennik* - -* Remove meaningless `ActiveSupport::FrozenObjectError`, which was just an alias of `RuntimeError`. - - *Akira Matsuda* - -* Introduce `assert_not` to replace warty `assert !foo`. *Jeremy Kemper* - -* Prevent `Callbacks#set_callback` from setting the same callback twice. - - before_save :foo, :bar, :foo - - will at first call `bar`, then `foo`. `foo` will no more be called - twice. - - *Dmitriy Kiriyenko* - -* Add `ActiveSupport::Logger#silence` that works the same as the old `Logger#silence` extension. - - *DHH* - -* Remove surrogate unicode character encoding from `ActiveSupport::JSON.encode` - The encoding scheme was broken for unicode characters outside the basic multilingual plane; - since json is assumed to be UTF-8, and we already force the encoding to UTF-8, - simply pass through the un-encoded characters. - - *Brett Carter* - -* Deprecate `Time.time_with_date_fallback`, `Time.utc_time` and `Time.local_time`. - These methods were added to handle the limited range of Ruby's native `Time` - implementation. Those limitations no longer apply so we are deprecating them in 4.0 - and they will be removed in 4.1. - - *Andrew White* - -* Deprecate `Date#to_time_in_current_zone` and add `Date#in_time_zone`. *Andrew White* - -* Add `String#in_time_zone` method to convert a string to an `ActiveSupport::TimeWithZone`. *Andrew White* - -* Deprecate `ActiveSupport::BasicObject` in favor of `ActiveSupport::ProxyObject`. - This class is used for proxy classes. It avoids confusion with Ruby's `BasicObject` - class. - - *Francesco Rodriguez* - -* Patched `Marshal#load` to work with constant autoloading. Fixes autoloading - with cache stores that rely on `Marshal` (`MemCacheStore` and `FileStore`). - Fixes #8167. - - *Uriel Katz* - -* Make `Time.zone.parse` to work with JavaScript format date strings. *Andrew White* - -* Add `DateTime#seconds_until_end_of_day` and `Time#seconds_until_end_of_day` - as a complement for `seconds_from_midnight`; useful when setting expiration - times for caches, e.g.: - - <% cache('dashboard', expires_in: Date.current.seconds_until_end_of_day) do %> - ... - - *Olek Janiszewski* - -* No longer proxy `ActiveSupport::Multibyte#class`. *Steve Klabnik* - -* Deprecate `ActiveSupport::TestCase#pending` method, use `skip` from minitest instead. *Carlos Antonio da Silva* - -* `XmlMini.with_backend` now may be safely used with threads: - - Thread.new do - XmlMini.with_backend("REXML") { rexml_power } + # Calculating scores is expensive, so we only do it for posts + # that have been updated. Cache keys are automatically extracted + # from objects that define a #cache_key method. + scores = Rails.cache.fetch_multi(*posts) do |post| + calculate_score(post) end - Thread.new do - XmlMini.with_backend("LibXML") { libxml_power } - end - - Each thread will use it's own backend. - - *Nikita Afanasenko* - -* Dependencies no longer trigger `Kernel#autoload` in `remove_constant`. Fixes #8213. *Xavier Noria* - -* Simplify `mocha` integration and remove monkey-patches, bumping `mocha` to 0.13.0. *James Mead* - -* `#as_json` isolates options when encoding a hash. Fixes #8182. - - *Yves Senn* - -* Deprecate `Hash#diff` in favor of minitest's #diff. *Steve Klabnik* - -* `Kernel#capture` can catch output from subprocesses. *Dmitry Vorotilin* - -* `to_xml` conversions now use builder's `tag!` method instead of explicit invocation of `method_missing`. - - *Nikita Afanasenko* - -* Fixed timezone mapping of the Solomon Islands. *Steve Klabnik* - -* Make callstack attribute optional in `ActiveSupport::Deprecation::Reporting` - methods `warn` and `deprecation_warning`. - - *Alexey Gaziev* - -* Implement `HashWithIndifferentAccess#replace` so `key?` works correctly. *David Graham* - -* Handle the possible permission denied errors `atomic.rb` might trigger due to its `chown` - and `chmod` calls. - - *Daniele Sluijters* - -* `Hash#extract!` returns only those keys that present in the receiver. - - {a: 1, b: 2}.extract!(:a, :x) # => {:a => 1} - - *Mikhail Dieterle* - -* `Hash#extract!` returns the same subclass, that the receiver is. I.e. - `HashWithIndifferentAccess#extract!` returns a `HashWithIndifferentAccess` instance. - - *Mikhail Dieterle* - -* Optimize `ActiveSupport::Cache::Entry` to reduce memory and processing overhead. *Brian Durand* -* Tests tag the Rails log with the current test class and test case: - - [SessionsControllerTest] [test_0002_sign in] Processing by SessionsController#create as HTML - [SessionsControllerTest] [test_0002_sign in] ... - - *Jeremy Kemper* - -* Add `logger.push_tags` and `.pop_tags` to complement `logger.tagged`: - - class Job - def before - Rails.logger.push_tags :jobs, self.class.name - end - - def after - Rails.logger.pop_tags 2 - end - end - - *Jeremy Kemper* - -* Allow delegation to the class using the `:class` keyword, replacing - `self.class` usage: - - class User - def self.hello - "world" - end - - delegate :hello, to: :class - end - - *Marc-Andre Lafortune* - -* `Date.beginning_of_week` thread local and `beginning_of_week` application - config option added (default is Monday). - - *Innokenty Mikhailov* - -* An optional block can be passed to `config_accessor` to set its default value - - class User - include ActiveSupport::Configurable - - config_accessor :hair_colors do - [:brown, :black, :blonde, :red] - end - end - - User.hair_colors # => [:brown, :black, :blonde, :red] - - *Larry Lv* - -* `ActiveSupport::Benchmarkable#silence` has been deprecated due to its lack of - thread safety. It will be removed without replacement in Rails 4.1. - - *Steve Klabnik* - -* An optional block can be passed to `Hash#deep_merge`. The block will be invoked - for each duplicated key and used to resolve the conflict. - - *Pranas Kiziela* - -* `ActiveSupport::Deprecation` is now a class. It is possible to create an instance - of deprecator. Backwards compatibility has been preserved. - - You can choose which instance of the deprecator will be used. - - deprecate :method_name, deprecator: deprecator_instance - - You can use `ActiveSupport::Deprecation` in your gem. - - require 'active_support/deprecation' - require 'active_support/core_ext/module/deprecation' - - class MyGem - def self.deprecator - ActiveSupport::Deprecation.new('2.0', 'MyGem') - end - - def old_method - end - - def new_method - end - - deprecate old_method: :new_method, deprecator: deprecator - end - - MyGem.new.old_method - # => DEPRECATION WARNING: old_method is deprecated and will be removed from MyGem 2.0 (use new_method instead). (called from <main> at file.rb:18) - - *Piotr Niełacny & Robert Pankowecki* - -* `ERB::Util.html_escape` encodes single quote as `#39`. Decimal form has better support in old browsers. *Kalys Osmonov* - -* `ActiveSupport::Callbacks`: deprecate monkey patch of object callbacks. - Using the `filter` method like this: - - before_filter MyFilter.new - - class MyFilter - def filter(controller) - end - end - - Is now deprecated with recommendation to use the corresponding filter type - (`#before`, `#after` or `#around`): - - before_filter MyFilter.new - - class MyFilter - def before(controller) - end - end - - *Bogdan Gusiev* - -* An optional block can be passed to `HashWithIndifferentAccess#update` and `#merge`. - The block will be invoked for each duplicated key, and used to resolve the conflict, - thus replicating the behaviour of the corresponding methods on the `Hash` class. - - *Leo Cassarani* - -* Remove `j` alias for `ERB::Util#json_escape`. - The `j` alias is already used for `ActionView::Helpers::JavaScriptHelper#escape_javascript` - and both modules are included in the view context that would confuse the developers. - - *Akira Matsuda* - -* Replace deprecated `memcache-client` gem with `dalli` in `ActiveSupport::Cache::MemCacheStore`. - - *Guillermo Iguaran* - -* Add default values to all `ActiveSupport::NumberHelper` methods, to avoid - errors with empty locales or missing values. - - *Carlos Antonio da Silva* - -* `ActiveSupport::JSON::Variable` is deprecated. Define your own `#as_json` and - `#encode_json` methods for custom JSON string literals. - - *Erich Menge* - -* Add `String#indent`. *fxn & Ace Suares* - -* Inflections can now be defined per locale. `singularize` and `pluralize` - accept locale as an extra argument. - - *David Celis* - -* `Object#try` will now return `nil` instead of raise a `NoMethodError` if the - receiving object does not implement the method, but you can still get the - old behavior by using the new `Object#try!`. - - *DHH* - -* `ERB::Util.html_escape` now escapes single quotes. *Santiago Pastorino* - -* `Time#change` now works with time values with offsets other than UTC or the local time zone. *Andrew White* - -* `ActiveSupport::Callbacks`: deprecate usage of filter object with `#before` and `#after` methods as `around` callback. *Bogdan Gusiev* - -* Add `Time#prev_quarter` and `Time#next_quarter` short-hands for `months_ago(3)` and `months_since(3)`. *SungHee Kang* - -* Remove obsolete and unused `require_association` method from dependencies. *fxn* - -* Add `:instance_accessor` option for `config_accessor`. - - class User - include ActiveSupport::Configurable - config_accessor :allowed_access, instance_accessor: false - end - - User.new.allowed_access = true # => NoMethodError - User.new.allowed_access # => NoMethodError - - *Francesco Rodriguez* - -* `ActionView::Helpers::NumberHelper` methods have been moved to `ActiveSupport::NumberHelper` and are now available via - `Numeric#to_s`. `Numeric#to_s` now accepts the formatting options `:phone`, `:currency`, `:percentage`, `:delimited`, - `:rounded`, `:human`, and `:human_size`. - - *Andrew Mutz* - -* Add `Hash#transform_keys`, `Hash#transform_keys!`, `Hash#deep_transform_keys`, and `Hash#deep_transform_keys!`. *Mark McSpadden* - -* Changed XML type `datetime` to `dateTime` (with upper case letter `T`). *Angelo Capilleri* - -* Add `:instance_accessor` option for `class_attribute`. *Alexey Vakhov* - -* `constantize` now looks in the ancestor chain. *Marc-Andre Lafortune & Andrew White* - -* Adds `Hash#deep_stringify_keys` and `Hash#deep_stringify_keys!` to convert all keys from a `Hash` instance into strings. *Lucas Húngaro* - -* Adds `Hash#deep_symbolize_keys` and `Hash#deep_symbolize_keys!` to convert all keys from a `Hash` instance into symbols. *Lucas Húngaro* - -* `Object#try` can't call private methods. *Vasiliy Ermolovich* - -* `AS::Callbacks#run_callbacks` remove `key` argument. *Francesco Rodriguez* - -* `deep_dup` works more expectedly now and duplicates also values in `Hash` instances and elements in `Array` instances. *Alexey Gaziev* - -* Inflector no longer applies ice -> ouse to words like "slice", "police", etc. *Wes Morgan* - -* Add `ActiveSupport::Deprecations.behavior = :silence` to completely ignore Rails runtime deprecations. *twinturbo* - -* Make `Module#delegate` stop using `send` - can no longer delegate to private methods. *dasch* - -* `ActiveSupport::Callbacks`: deprecate `:rescuable` option. *Bogdan Gusiev* - -* Adds `Integer#ordinal` to get the ordinal suffix string of an integer. *Tim Gildea* - -* `ActiveSupport::Callbacks`: `:per_key` option is no longer supported. *Bogdan Gusiev* - -* `ActiveSupport::Callbacks#define_callbacks`: add `:skip_after_callbacks_if_terminated` option. *Bogdan Gusiev* - -* Add `html_escape_once` to `ERB::Util`, and delegate the `escape_once` tag helper to it. *Carlos Antonio da Silva* - -* Deprecates the compatibility method `Module#local_constant_names`, - use `Module#local_constants` instead (which returns symbols). *Xavier Noria* - -* Deletes the compatibility method `Module#method_names`, - use `Module#methods` from now on (which returns symbols). *Xavier Noria* - -* Deletes the compatibility method `Module#instance_method_names`, - use `Module#instance_methods` from now on (which returns symbols). *Xavier Noria* - -* `BufferedLogger` is deprecated. Use `ActiveSupport::Logger`, or the logger - from the Ruby standard library. - - *Aaron Patterson* - -* Unicode database updated to 6.1.0. *Norman Clarke* - -* Adds `encode_big_decimal_as_string` option to force JSON serialization of `BigDecimal` as numeric instead - of wrapping them in strings for safety. - -* Optimize log subscribers to check log level before doing any processing. *Brian Durand* + *Daniel Schierbeck* -Please check [3-2-stable](https://github.com/rails/rails/blob/3-2-stable/activesupport/CHANGELOG.md) for previous changes. +Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/activesupport/CHANGELOG.md) for previous changes. diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec index b46a331f6a..08d503ae30 100644 --- a/activesupport/activesupport.gemspec +++ b/activesupport/activesupport.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |s| s.rdoc_options.concat ['--encoding', 'UTF-8'] s.add_dependency('i18n', '~> 0.6', '>= 0.6.4') - s.add_dependency 'multi_json', '~> 1.3' + s.add_dependency 'json', '~> 1.7' s.add_dependency 'tzinfo', '~> 0.3.37' s.add_dependency 'minitest', '~> 4.2' s.add_dependency 'thread_safe','~> 0.1' diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 2368e5ebd4..b1ab5570a8 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -56,16 +56,7 @@ module ActiveSupport case store when Symbol - store_class_name = store.to_s.camelize - store_class = - begin - require "active_support/cache/#{store}" - rescue LoadError => e - raise "Could not find cache store adapter for #{store} (#{e})" - else - ActiveSupport::Cache.const_get(store_class_name) - end - store_class.new(*parameters) + retrieve_store_class(store).new(*parameters) when nil ActiveSupport::Cache::MemoryStore.new else @@ -73,6 +64,18 @@ module ActiveSupport end end + # Expands out the +key+ argument into a key that can be used for the + # cache store. Optionally accepts a namespace, and all keys will be + # scoped within that namespace. + # + # If the +key+ argument provided is an array, or responds to +to_a+, then + # each of elements in the array will be turned into parameters/keys and + # concatenated into a single key. For example: + # + # expand_cache_key([:foo, :bar]) # => "foo/bar" + # expand_cache_key([:foo, :bar], "namespace") # => "namespace/foo/bar" + # + # The +key+ argument can also respond to +cache_key+ or +to_param+. def expand_cache_key(key, namespace = nil) expanded_cache_key = namespace ? "#{namespace}/" : "" @@ -94,6 +97,16 @@ module ActiveSupport else key.to_param end.to_s end + + # Obtains the specified cache store class, given the name of the +store+. + # Raises an error when the store class cannot be found. + def retrieve_store_class(store) + require "active_support/cache/#{store}" + rescue LoadError => e + raise "Could not find cache store adapter for #{store} (#{e})" + else + ActiveSupport::Cache.const_get(store.to_s.camelize) + end end # An abstract cache store class. There are multiple cache store @@ -339,6 +352,34 @@ module ActiveSupport results end + # Fetches data from the cache, using the given keys. If there is data in + # the cache with the given keys, then that data is returned. Otherwise, + # the supplied block is called for each key for which there was no data, + # and the result will be written to the cache and returned. + # + # Options are passed to the underlying cache implementation. + # + # Returns an array with the data for each of the names. For example: + # + # cache.write("bim", "bam") + # cache.fetch_multi("bim", "boom") {|key| key * 2 } + # #=> ["bam", "boomboom"] + # + def fetch_multi(*names) + options = names.extract_options! + options = merged_options(options) + + results = read_multi(*names, options) + + names.map do |name| + results.fetch(name) do + value = yield name + write(name, value, options) + value + end + end + end + # Writes the value to the cache, with the key. # # Options are passed to the underlying cache implementation. diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb index c62afe836c..fb42c4a41e 100644 --- a/activesupport/lib/active_support/cache/strategy/local_cache.rb +++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb @@ -9,7 +9,7 @@ module ActiveSupport # in-memory cache for faster access. module LocalCache # Class for storing and registering the local caches. - class LocalCacheRegistry + class LocalCacheRegistry # :nodoc: extend ActiveSupport::PerThreadRegistry def initialize @@ -58,13 +58,7 @@ module ActiveSupport # Use a local cache for the duration of block. def with_local_cache - save_val = LocalCacheRegistry.cache_for(local_cache_key) - begin - LocalCacheRegistry.set_cache_for(local_cache_key, LocalStore.new) - yield - ensure - LocalCacheRegistry.set_cache_for(local_cache_key, save_val) - end + use_temporary_local_cache(LocalStore.new) { yield } end #-- @@ -112,29 +106,13 @@ module ActiveSupport def increment(name, amount = 1, options = nil) # :nodoc: value = bypass_local_cache{super} - if local_cache - local_cache.mute do - if value - local_cache.write(name, value, options) - else - local_cache.delete(name, options) - end - end - end + increment_or_decrement(value, name, amount, options) value end def decrement(name, amount = 1, options = nil) # :nodoc: value = bypass_local_cache{super} - if local_cache - local_cache.mute do - if value - local_cache.write(name, value, options) - else - local_cache.delete(name, options) - end - end - end + increment_or_decrement(value, name, amount, options) value end @@ -163,6 +141,18 @@ module ActiveSupport end private + def increment_or_decrement(value, name, amount, options) + if local_cache + local_cache.mute do + if value + local_cache.write(name, value, options) + else + local_cache.delete(name, options) + end + end + end + end + def local_cache_key @local_cache_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, '_').to_sym end @@ -172,9 +162,13 @@ module ActiveSupport end def bypass_local_cache + use_temporary_local_cache(nil) { yield } + end + + def use_temporary_local_cache(temporary_cache) save_cache = LocalCacheRegistry.cache_for(local_cache_key) begin - LocalCacheRegistry.set_cache_for(local_cache_key, nil) + LocalCacheRegistry.set_cache_for(local_cache_key, temporary_cache) yield ensure LocalCacheRegistry.set_cache_for(local_cache_key, save_cache) diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 6c0cae71ed..ab3bb23e88 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -61,6 +61,8 @@ module ActiveSupport extend ActiveSupport::DescendantsTracker end + CALLBACK_FILTER_TYPES = [:before, :after, :around] + # Runs the callbacks for the given event. # # Calls the before and around callbacks in the order they were set, yields @@ -74,8 +76,14 @@ module ActiveSupport # save # end def run_callbacks(kind, &block) - runner_name = self.class.__define_callbacks(kind, self) - send(runner_name, &block) + cbs = send("_#{kind}_callbacks") + if cbs.empty? + yield if block_given? + else + runner = cbs.compile + e = Filters::Environment.new(self, false, nil, block) + runner.call(e).value + end end private @@ -86,35 +94,278 @@ module ActiveSupport def halted_callback_hook(filter) end + module Filters + Environment = Struct.new(:target, :halted, :value, :run_block) + + class End + def call(env) + block = env.run_block + env.value = !env.halted && (!block || block.call) + env + end + end + ENDING = End.new + + class Before + def self.build(next_callback, user_callback, user_conditions, chain_config, filter) + if chain_config.key?(:terminator) && user_conditions.any? + halted_lambda = eval "lambda { |result| #{chain_config[:terminator]} }" + halting_and_conditional(next_callback, user_callback, user_conditions, halted_lambda, filter) + elsif chain_config.key? :terminator + halted_lambda = eval "lambda { |result| #{chain_config[:terminator]} }" + halting(next_callback, user_callback, halted_lambda, filter) + elsif user_conditions.any? + conditional(next_callback, user_callback, user_conditions) + else + simple next_callback, user_callback + end + end + + private + + def self.halting_and_conditional(next_callback, user_callback, user_conditions, halted_lambda, filter) + lambda { |env| + target = env.target + value = env.value + halted = env.halted + + if !halted && user_conditions.all? { |c| c.call(target, value) } + result = user_callback.call target, value + env.halted = halted_lambda.call result + if env.halted + target.send :halted_callback_hook, filter + end + end + next_callback.call env + } + end + + def self.halting(next_callback, user_callback, halted_lambda, filter) + lambda { |env| + target = env.target + value = env.value + halted = env.halted + + if !halted + result = user_callback.call target, value + env.halted = halted_lambda.call result + if env.halted + target.send :halted_callback_hook, filter + end + end + next_callback.call env + } + end + + def self.conditional(next_callback, user_callback, user_conditions) + lambda { |env| + target = env.target + value = env.value + + if user_conditions.all? { |c| c.call(target, value) } + user_callback.call target, value + end + next_callback.call env + } + end + + def self.simple(next_callback, user_callback) + lambda { |env| + user_callback.call env.target, env.value + next_callback.call env + } + end + end + + class After + def self.build(next_callback, user_callback, user_conditions, chain_config) + if chain_config[:skip_after_callbacks_if_terminated] + if chain_config.key?(:terminator) && user_conditions.any? + halting_and_conditional(next_callback, user_callback, user_conditions) + elsif chain_config.key?(:terminator) + halting(next_callback, user_callback) + elsif user_conditions.any? + conditional next_callback, user_callback, user_conditions + else + simple next_callback, user_callback + end + else + if user_conditions.any? + conditional next_callback, user_callback, user_conditions + else + simple next_callback, user_callback + end + end + end + + private + + def self.halting_and_conditional(next_callback, user_callback, user_conditions) + lambda { |env| + env = next_callback.call env + target = env.target + value = env.value + halted = env.halted + + if !halted && user_conditions.all? { |c| c.call(target, value) } + user_callback.call target, value + end + env + } + end + + def self.halting(next_callback, user_callback) + lambda { |env| + env = next_callback.call env + if !env.halted + user_callback.call env.target, env.value + end + env + } + end + + def self.conditional(next_callback, user_callback, user_conditions) + lambda { |env| + env = next_callback.call env + target = env.target + value = env.value + + if user_conditions.all? { |c| c.call(target, value) } + user_callback.call target, value + end + env + } + end + + def self.simple(next_callback, user_callback) + lambda { |env| + env = next_callback.call env + user_callback.call env.target, env.value + env + } + end + end + + class Around + def self.build(next_callback, user_callback, user_conditions, chain_config) + if chain_config.key?(:terminator) && user_conditions.any? + halting_and_conditional(next_callback, user_callback, user_conditions) + elsif chain_config.key? :terminator + halting(next_callback, user_callback) + elsif user_conditions.any? + conditional(next_callback, user_callback, user_conditions) + else + simple(next_callback, user_callback) + end + end + + private + + def self.halting_and_conditional(next_callback, user_callback, user_conditions) + lambda { |env| + target = env.target + value = env.value + halted = env.halted + + if !halted && user_conditions.all? { |c| c.call(target, value) } + user_callback.call(target, value) { + env = next_callback.call env + env.value + } + env + else + next_callback.call env + end + } + end + + def self.halting(next_callback, user_callback) + lambda { |env| + target = env.target + value = env.value + + if !env.halted + user_callback.call(target, value) { + env = next_callback.call env + env.value + } + env + else + next_callback.call env + end + } + end + + def self.conditional(next_callback, user_callback, user_conditions) + lambda { |env| + target = env.target + value = env.value + + if user_conditions.all? { |c| c.call(target, value) } + user_callback.call(target, value) { + env = next_callback.call env + env.value + } + env + else + next_callback.call env + end + } + end + + def self.simple(next_callback, user_callback) + lambda { |env| + user_callback.call(env.target, env.value) { + env = next_callback.call env + env.value + } + env + } + end + end + end + class Callback #:nodoc:# - @@_callback_sequence = 0 + def self.build(chain, filter, kind, options) + new chain.name, filter, kind, options, chain.config + end - attr_accessor :chain, :filter, :kind, :options, :klass, :raw_filter + attr_accessor :kind, :options, :name + attr_reader :chain_config + + def initialize(name, filter, kind, options, chain_config) + @chain_config = chain_config + @name = name + @kind = kind + @filter = filter + @options = options + @key = compute_identifier filter - def initialize(chain, filter, kind, options, klass) - @chain, @kind, @klass = chain, kind, klass deprecate_per_key_option(options) normalize_options!(options) - - @raw_filter, @options = filter, options - @filter = _compile_filter(filter) - recompile_options! end + def filter; @key; end + def raw_filter; @filter; end + def deprecate_per_key_option(options) if options[:per_key] raise NotImplementedError, ":per_key option is no longer supported. Use generic :if and :unless options instead." end end - def clone(chain, klass) - obj = super() - obj.chain = chain - obj.klass = klass - obj.options = @options.dup - obj.options[:if] = @options[:if].dup - obj.options[:unless] = @options[:unless].dup - obj + def merge(chain, new_options) + _options = { + :if => @options[:if].dup, + :unless => @options[:unless].dup + } + + deprecate_per_key_option new_options + + _options[:if].concat Array(new_options.fetch(:unless, [])) + _options[:unless].concat Array(new_options.fetch(:if, [])) + + self.class.build chain, @filter, @kind, _options end def normalize_options!(options) @@ -122,123 +373,42 @@ module ActiveSupport options[:unless] = Array(options[:unless]) end - def name - chain.name - end - - def next_id - @@_callback_sequence += 1 - end - def matches?(_kind, _filter) - @kind == _kind && @filter == _filter + @kind == _kind && filter == _filter end def duplicates?(other) - matches?(other.kind, other.filter) - end - - def _update_filter(filter_options, new_options) - filter_options[:if].concat(Array(new_options[:unless])) if new_options.key?(:unless) - filter_options[:unless].concat(Array(new_options[:if])) if new_options.key?(:if) - end - - def recompile!(_options) - deprecate_per_key_option(_options) - _update_filter(self.options, _options) - - recompile_options! + case @filter + when Symbol, String + matches?(other.kind, other.filter) + else + false + end end # Wraps code with filter - def apply(code) - case @kind + def apply(next_callback) + user_conditions = conditions_lambdas + user_callback = make_lambda @filter + + case kind when :before - <<-RUBY_EVAL - if !halted && #{@compiled_options} - # This double assignment is to prevent warnings in 1.9.3 as - # the `result` variable is not always used except if the - # terminator code refers to it. - result = result = #{@filter} - halted = (#{chain.config[:terminator]}) - if halted - halted_callback_hook(#{@raw_filter.inspect.inspect}) - end - end - #{code} - RUBY_EVAL + Filters::Before.build(next_callback, user_callback, user_conditions, chain_config, @filter) when :after - <<-RUBY_EVAL - #{code} - if #{!chain.config[:skip_after_callbacks_if_terminated] || "!halted"} && #{@compiled_options} - #{@filter} - end - RUBY_EVAL + Filters::After.build(next_callback, user_callback, user_conditions, chain_config) when :around - name = define_conditional_callback - <<-RUBY_EVAL - #{name}(halted) do - #{code} - value - end - RUBY_EVAL + Filters::Around.build(next_callback, user_callback, user_conditions, chain_config) end end private - # Compile around filters with conditions into proxy methods - # that contain the conditions. - # - # For `set_callback :save, :around, :filter_name, if: :condition': - # - # def _conditional_callback_save_17 - # if condition - # filter_name do - # yield self - # end - # else - # yield self - # end - # end - def define_conditional_callback - name = "_conditional_callback_#{@kind}_#{next_id}" - @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def #{name}(halted) - if #{@compiled_options} && !halted - #{@filter} do - yield self - end - else - yield self - end - end - RUBY_EVAL - name - end - - # Options support the same options as filters themselves (and support - # symbols, string, procs, and objects), so compile a conditional - # expression based on the options. - def recompile_options! - conditions = ["true"] - - unless options[:if].empty? - conditions << Array(_compile_filter(options[:if])) - end - - unless options[:unless].empty? - conditions << Array(_compile_filter(options[:unless])).map {|f| "!#{f}"} - end - - @compiled_options = conditions.flatten.join(" && ") + def invert_lambda(l) + lambda { |*args, &blk| !l.call(*args, &blk) } end # Filters support: # - # Arrays:: Used in conditions. This is used to specify - # multiple conditions. Used internally to - # merge conditions from skip_* filters. # Symbols:: A method to call. # Strings:: Some content to evaluate. # Procs:: A proc to call with the object. @@ -247,43 +417,59 @@ module ActiveSupport # All of these objects are compiled into methods and handled # the same after this point: # - # Arrays:: Merged together into a single filter. # Symbols:: Already methods. # Strings:: class_eval'ed into methods. # Procs:: define_method'ed into methods. # Objects:: # a method is created that calls the before_foo method # on the object. - def _compile_filter(filter) + def make_lambda(filter) case filter - when Array - filter.map {|f| _compile_filter(f)} when Symbol - filter + lambda { |target, _, &blk| target.send filter, &blk } when String - "(#{filter})" - when Proc - method_name = "_callback_#{@kind}_#{next_id}" - @klass.send(:define_method, method_name, &filter) - return method_name if filter.arity <= 0 + l = eval "lambda { |value| #{filter} }" + lambda { |target, value| target.instance_exec(value, &l) } + when ::Proc + raise ArgumentError if filter.arity > 1 + + if filter.arity <= 0 + lambda { |target, _| target.instance_exec(&filter) } + else + lambda { |target, _| target.instance_exec(target, &filter) } + end + else + scopes = Array(chain_config[:scope]) + method_to_call = scopes.map{ |s| public_send(s) }.join("_") + + lambda { |target, _, &blk| + filter.public_send method_to_call, target, &blk + } + end + end - method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ") + def compute_identifier(filter) + case filter + when String, ::Proc + filter.object_id else - method_name = "_callback_#{@kind}_#{next_id}" - @klass.send(:define_method, "#{method_name}_object") { filter } + filter + end + end - _normalize_legacy_filter(kind, filter) - scopes = Array(chain.config[:scope]) - method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_") + def conditions_lambdas + conditions = [] - @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def #{method_name}(&blk) - #{method_name}_object.send(:#{method_to_call}, self, &blk) - end - RUBY_EVAL + unless options[:if].empty? + lambdas = Array(options[:if]).map { |c| make_lambda c } + conditions.concat lambdas + end - method_name + unless options[:unless].empty? + lambdas = Array(options[:unless]).map { |c| make_lambda c } + conditions.concat lambdas.map { |l| invert_lambda l } end + conditions end def _normalize_legacy_filter(kind, filter) @@ -307,30 +493,53 @@ module ActiveSupport end # An Array with a compile method. - class CallbackChain < Array #:nodoc:# + class CallbackChain #:nodoc:# + include Enumerable + attr_reader :name, :config def initialize(name, config) @name = name @config = { - :terminator => "false", :scope => [ :kind ] }.merge!(config) + @chain = [] + @callbacks = nil + end + + def each(&block); @chain.each(&block); end + def index(o); @chain.index(o); end + def empty?; @chain.empty?; end + + def insert(index, o) + @callbacks = nil + @chain.insert(index, o) + end + + def delete(o) + @callbacks = nil + @chain.delete(o) + end + + def clear + @callbacks = nil + @chain.clear + self + end + + def initialize_copy(other) + @callbacks = nil + @chain = other.chain.dup end def compile - method = [] - method << "value = nil" - method << "halted = false" + return @callbacks if @callbacks - callbacks = "value = !halted && (!block_given? || yield)" - reverse_each do |callback| - callbacks = callback.apply(callbacks) + @callbacks = Filters::ENDING + @chain.reverse_each do |callback| + @callbacks = callback.apply(@callbacks) end - method << callbacks - - method << "value" - method.join("\n") + @callbacks end def append(*callbacks) @@ -341,69 +550,45 @@ module ActiveSupport callbacks.each { |c| prepend_one(c) } end + protected + def chain; @chain; end + private def append_one(callback) + @callbacks = nil remove_duplicates(callback) - push(callback) + @chain.push(callback) end def prepend_one(callback) + @callbacks = nil remove_duplicates(callback) - unshift(callback) + @chain.unshift(callback) end def remove_duplicates(callback) - delete_if { |c| callback.duplicates?(c) } + @callbacks = nil + @chain.delete_if { |c| callback.duplicates?(c) } end end module ClassMethods - # This method defines callback chain method for the given kind - # if it was not yet defined. - # This generated method plays caching role. - def __define_callbacks(kind, object) #:nodoc: - name = __callback_runner_name(kind) - unless object.respond_to?(name, true) - str = object.send("_#{kind}_callbacks").compile - class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def #{name}() #{str} end - protected :#{name} - RUBY_EVAL - end - name - end - - def __reset_runner(symbol) - name = __callback_runner_name(symbol) - undef_method(name) if method_defined?(name) - end - - def __callback_runner_name_cache - @__callback_runner_name_cache ||= ThreadSafe::Cache.new {|cache, kind| cache[kind] = __generate_callback_runner_name(kind) } - end - - def __generate_callback_runner_name(kind) - "_run__#{self.name.hash.abs}__#{kind}__callbacks" - end - - def __callback_runner_name(kind) - __callback_runner_name_cache[kind] + def normalize_callback_params(name, filters, block) # :nodoc: + type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before + options = filters.last.is_a?(Hash) ? filters.pop : {} + filters.unshift(block) if block + [type, filters, options] end # This is used internally to append, prepend and skip callbacks to the # CallbackChain. - def __update_callbacks(name, filters = [], block = nil) #:nodoc: - type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before - options = filters.last.is_a?(Hash) ? filters.pop : {} - filters.unshift(block) if block - + def __update_callbacks(name) #:nodoc: ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target| - chain = target.send("_#{name}_callbacks") - yield target, chain.dup, type, filters, options - target.__reset_runner(name) + chain = target.get_callbacks name + yield target, chain.dup end end @@ -443,16 +628,15 @@ module ActiveSupport # * <tt>:prepend</tt> - If +true+, the callback will be prepended to the # existing chain rather than appended. def set_callback(name, *filter_list, &block) - mapped = nil - - __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options| - mapped ||= filters.map do |filter| - Callback.new(chain, filter, type, options.dup, self) - end + type, filters, options = normalize_callback_params(name, filter_list, block) + self_chain = get_callbacks name + mapped = filters.map do |filter| + Callback.build(self_chain, filter, type, options.dup) + end + __update_callbacks(name) do |target, chain| options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped) - - target.send("_#{name}_callbacks=", chain) + target.set_callbacks name, chain end end @@ -464,36 +648,34 @@ module ActiveSupport # skip_callback :validate, :before, :check_membership, if: -> { self.age > 18 } # end def skip_callback(name, *filter_list, &block) - __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options| + type, filters, options = normalize_callback_params(name, filter_list, block) + + __update_callbacks(name) do |target, chain| filters.each do |filter| filter = chain.find {|c| c.matches?(type, filter) } if filter && options.any? - new_filter = filter.clone(chain, self) + new_filter = filter.merge(chain, options) chain.insert(chain.index(filter), new_filter) - new_filter.recompile!(options) end chain.delete(filter) end - target.send("_#{name}_callbacks=", chain) + target.set_callbacks name, chain end end # Remove all set callbacks for the given event. def reset_callbacks(symbol) - callbacks = send("_#{symbol}_callbacks") + callbacks = get_callbacks symbol ActiveSupport::DescendantsTracker.descendants(self).each do |target| - chain = target.send("_#{symbol}_callbacks").dup + chain = target.get_callbacks(symbol).dup callbacks.each { |c| chain.delete(c) } - target.send("_#{symbol}_callbacks=", chain) - target.__reset_runner(symbol) + target.set_callbacks symbol, chain end - self.send("_#{symbol}_callbacks=", callbacks.dup.clear) - - __reset_runner(symbol) + self.set_callbacks symbol, callbacks.dup.clear end # Define sets of events in the object lifecycle that support callbacks. @@ -567,9 +749,19 @@ module ActiveSupport config = callbacks.last.is_a?(Hash) ? callbacks.pop : {} callbacks.each do |callback| class_attribute "_#{callback}_callbacks" - send("_#{callback}_callbacks=", CallbackChain.new(callback, config)) + set_callbacks callback, CallbackChain.new(callback, config) end end + + protected + + def get_callbacks(name) + send "_#{name}_callbacks" + end + + def set_callbacks(name, callbacks) + send "_#{name}_callbacks=", callbacks + end end end end diff --git a/activesupport/lib/active_support/core_ext/array/grouping.rb b/activesupport/lib/active_support/core_ext/array/grouping.rb index 640e6e9328..dbddc7a7b4 100644 --- a/activesupport/lib/active_support/core_ext/array/grouping.rb +++ b/activesupport/lib/active_support/core_ext/array/grouping.rb @@ -25,7 +25,7 @@ class Array # subtracting from number gives how many to add; # modulo number ensures we don't add group of just fill. padding = (number - size % number) % number - collection = dup.concat([fill_with] * padding) + collection = dup.concat(Array.new(padding, fill_with)) end if block_given? diff --git a/activesupport/lib/active_support/core_ext/array/wrap.rb b/activesupport/lib/active_support/core_ext/array/wrap.rb index 1245768870..152eb02218 100644 --- a/activesupport/lib/active_support/core_ext/array/wrap.rb +++ b/activesupport/lib/active_support/core_ext/array/wrap.rb @@ -15,12 +15,12 @@ class Array # # * If the argument responds to +to_ary+ the method is invoked. <tt>Kernel#Array</tt> # moves on to try +to_a+ if the returned value is +nil+, but <tt>Array.wrap</tt> returns - # such a +nil+ right away. + # +nil+ right away. # * If the returned value from +to_ary+ is neither +nil+ nor an +Array+ object, <tt>Kernel#Array</tt> # raises an exception, while <tt>Array.wrap</tt> does not, it just returns the value. - # * It does not call +to_a+ on the argument, though special-cases +nil+ to return an empty array. + # * It does not call +to_a+ on the argument, but returns an empty array if argument is +nil+. # - # The last point is particularly worth comparing for some enumerables: + # The second point is easily explained with some enumerables: # # Array(foo: :bar) # => [[:foo, :bar]] # Array.wrap(foo: :bar) # => [{:foo=>:bar}] @@ -29,10 +29,10 @@ class Array # # [*object] # - # which for +nil+ returns <tt>[]</tt>, and calls to <tt>Array(object)</tt> otherwise. + # which returns <tt>[]</tt> for +nil+, but calls to <tt>Array(object)</tt> otherwise. # - # Thus, in this case the behavior may be different for +nil+, and the differences with - # <tt>Kernel#Array</tt> explained above apply to the rest of <tt>object</tt>s. + # The differences with <tt>Kernel#Array</tt> explained above + # apply to the rest of <tt>object</tt>s. def self.wrap(object) if object.nil? [] diff --git a/activesupport/lib/active_support/core_ext/date.rb b/activesupport/lib/active_support/core_ext/date.rb index 5f13f5f70f..465fedda80 100644 --- a/activesupport/lib/active_support/core_ext/date.rb +++ b/activesupport/lib/active_support/core_ext/date.rb @@ -2,5 +2,4 @@ require 'active_support/core_ext/date/acts_like' require 'active_support/core_ext/date/calculations' require 'active_support/core_ext/date/conversions' require 'active_support/core_ext/date/zones' -require 'active_support/core_ext/date/infinite_comparable' diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb index cdf606f28c..0637fe4929 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -1,7 +1,6 @@ require 'date' require 'active_support/inflector/methods' require 'active_support/core_ext/date/zones' -require 'active_support/core_ext/module/remove_method' class Date DATE_FORMATS = { diff --git a/activesupport/lib/active_support/core_ext/date/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/date/infinite_comparable.rb deleted file mode 100644 index ca5d793942..0000000000 --- a/activesupport/lib/active_support/core_ext/date/infinite_comparable.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'active_support/core_ext/infinite_comparable' - -class Date - include InfiniteComparable -end diff --git a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb index 5b89ace66b..0d14cba7cc 100644 --- a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb @@ -109,11 +109,11 @@ module DateAndTime alias :at_beginning_of_year :beginning_of_year # Returns a new date/time representing the given day in the next week. - # Week is assumed to start on +start_day+, default is - # +Date.beginning_of_week+ or +config.beginning_of_week+ when set. - # DateTime objects have their time set to 0:00. - def next_week(start_day = Date.beginning_of_week) - first_hour{ weeks_since(1).beginning_of_week.days_since(days_span(start_day)) } + # The +given_day_in_next_week+ defaults to the beginning of the week + # which is determined by +Date.beginning_of_week+ or +config.beginning_of_week+ + # when set. +DateTime+ objects have their time set to 0:00. + def next_week(given_day_in_next_week = Date.beginning_of_week) + first_hour{ weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week)) } end # Short-hand for months_since(1). diff --git a/activesupport/lib/active_support/core_ext/date_time.rb b/activesupport/lib/active_support/core_ext/date_time.rb index 024af91738..e8a27b9f38 100644 --- a/activesupport/lib/active_support/core_ext/date_time.rb +++ b/activesupport/lib/active_support/core_ext/date_time.rb @@ -2,4 +2,3 @@ require 'active_support/core_ext/date_time/acts_like' require 'active_support/core_ext/date_time/calculations' require 'active_support/core_ext/date_time/conversions' require 'active_support/core_ext/date_time/zones' -require 'active_support/core_ext/date_time/infinite_comparable' 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 9f0864d9bb..937567440b 100644 --- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb @@ -1,3 +1,4 @@ +require 'date' require 'active_support/deprecation' class DateTime @@ -154,4 +155,11 @@ class DateTime def utc_offset (offset * 86400).to_i end + + # Layers additional behavior on DateTime#<=> so that Time and + # ActiveSupport::TimeWithZone instances can be compared with a DateTime. + def <=>(other) + super other.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 b7d8414a9d..df07917d19 100644 --- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb @@ -1,3 +1,4 @@ +require 'date' require 'active_support/inflector/methods' require 'active_support/core_ext/time/conversions' require 'active_support/core_ext/date_time/calculations' diff --git a/activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb deleted file mode 100644 index 8a282b19f2..0000000000 --- a/activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'active_support/core_ext/infinite_comparable' - -class DateTime - include InfiniteComparable -end diff --git a/activesupport/lib/active_support/core_ext/date_time/zones.rb b/activesupport/lib/active_support/core_ext/date_time/zones.rb index 6457ffbaf6..01a627f8af 100644 --- a/activesupport/lib/active_support/core_ext/date_time/zones.rb +++ b/activesupport/lib/active_support/core_ext/date_time/zones.rb @@ -1,3 +1,4 @@ +require 'date' require 'active_support/core_ext/time/zones' class DateTime diff --git a/activesupport/lib/active_support/core_ext/hash/diff.rb b/activesupport/lib/active_support/core_ext/hash/diff.rb index 5f3868b5b0..4359213380 100644 --- a/activesupport/lib/active_support/core_ext/hash/diff.rb +++ b/activesupport/lib/active_support/core_ext/hash/diff.rb @@ -1,3 +1,5 @@ +require 'active_support/deprecation' + class Hash # Returns a hash that represents the difference between two hashes. # diff --git a/activesupport/lib/active_support/core_ext/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/infinite_comparable.rb deleted file mode 100644 index b78b2deaad..0000000000 --- a/activesupport/lib/active_support/core_ext/infinite_comparable.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'active_support/concern' -require 'active_support/core_ext/module/aliasing' -require 'active_support/core_ext/object/try' - -module InfiniteComparable - extend ActiveSupport::Concern - - included do - alias_method_chain :<=>, :infinity - end - - define_method :'<=>_with_infinity' do |other| - if other.class == self.class - public_send :'<=>_without_infinity', other - else - infinite = try(:infinite?) - other_infinite = other.try(:infinite?) - - # inf <=> inf - if infinite && other_infinite - infinite <=> other_infinite - # not_inf <=> inf - elsif other_infinite - -other_infinite - # inf <=> not_inf - elsif infinite - infinite - else - conversion = "to_#{self.class.name.downcase}" - other = other.public_send(conversion) if other.respond_to?(conversion) - public_send :'<=>_without_infinity', other - end - end - end -end diff --git a/activesupport/lib/active_support/core_ext/marshal.rb b/activesupport/lib/active_support/core_ext/marshal.rb index c7a8348b1d..56c79c04bd 100644 --- a/activesupport/lib/active_support/core_ext/marshal.rb +++ b/activesupport/lib/active_support/core_ext/marshal.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/module/aliasing' + module Marshal class << self def load_with_autoloading(source) diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb index e608eeaf42..6d42667e97 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -1,8 +1,10 @@ class Module - # Provides a delegate class method to easily expose contained objects' public methods - # as your own. Pass one or more methods (specified as symbols or strings) - # and the name of the target object via the <tt>:to</tt> option (also a symbol - # or string). At least one method and the <tt>:to</tt> option are required. + # Provides a +delegate+ class method to easily expose contained objects' + # public methods as your own. + # + # The macro receives one or more method names (specified as symbols or + # strings) and the name of the target object via the <tt>:to</tt> option + # (also a symbol or string). # # Delegation is particularly useful with Active Record associations: # @@ -89,29 +91,44 @@ class Module # invoice.customer_name # => 'John Doe' # invoice.customer_address # => 'Vimmersvej 13' # - # If the delegate object is +nil+ an exception is raised, and that happens - # no matter whether +nil+ responds to the delegated method. You can get a - # +nil+ instead with the +:allow_nil+ option. + # If the target is +nil+ and does not respond to the delegated method a + # +NoMethodError+ is raised, as with any other value. Sometimes, however, it + # makes sense to be robust to that situation and that is the purpose of the + # <tt>:allow_nil</tt> option: If the target is not +nil+, or it is and + # responds to the method, everything works as usual. But if it is +nil+ and + # does not respond to the delegated method, +nil+ is returned. # - # class Foo - # attr_accessor :bar - # def initialize(bar = nil) - # @bar = bar - # end - # delegate :zoo, to: :bar + # class User < ActiveRecord::Base + # has_one :profile + # delegate :age, to: :profile # end # - # Foo.new.zoo # raises NoMethodError exception (you called nil.zoo) + # User.new.age # raises NoMethodError: undefined method `age' + # + # But if not having a profile yet is fine and should not be an error + # condition: + # + # class User < ActiveRecord::Base + # has_one :profile + # delegate :age, to: :profile, allow_nil: true + # end + # + # User.new.age # nil + # + # Note that if the target is not +nil+ then the call is attempted regardless of the + # <tt>:allow_nil</tt> option, and thus an exception is still raised if said object + # does not respond to the method: # # class Foo - # attr_accessor :bar - # def initialize(bar = nil) + # def initialize(bar) # @bar = bar # end - # delegate :zoo, to: :bar, allow_nil: true + # + # delegate :name, to: :@bar, allow_nil: true # end # - # Foo.new.zoo # returns nil + # Foo.new("Bar").name # raises NoMethodError: undefined method `name' + # def delegate(*methods) options = methods.pop unless options.is_a?(Hash) && to = options[:to] @@ -142,22 +159,31 @@ class Module # methods still accept two arguments. definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block' + # The following generated methods call the target exactly once, storing + # the returned value in a dummy variable. + # + # Reason is twofold: On one hand doing less calls is in general better. + # On the other hand it could be that the target has side-effects, + # whereas conceptualy, from the user point of view, the delegator should + # be doing one call. if allow_nil - module_eval(<<-EOS, file, line - 2) + module_eval(<<-EOS, file, line - 3) def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block) - if #{to} || #{to}.respond_to?(:#{method}) # if client || client.respond_to?(:name) - #{to}.#{method}(#{definition}) # client.name(*args, &block) + _ = #{to} # _ = client + if !_.nil? || nil.respond_to?(:#{method}) # if !_.nil? || nil.respond_to?(:name) + _.#{method}(#{definition}) # _.name(*args, &block) end # end end # end EOS else exception = %(raise "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}") - module_eval(<<-EOS, file, line - 1) + module_eval(<<-EOS, file, line - 2) def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block) - #{to}.#{method}(#{definition}) # client.name(*args, &block) + _ = #{to} # _ = client + _.#{method}(#{definition}) # _.name(*args, &block) rescue NoMethodError # rescue NoMethodError - if #{to}.nil? # if client.nil? + if _.nil? # if _.nil? #{exception} # # add helpful message to the exception else # else raise # raise diff --git a/activesupport/lib/active_support/core_ext/numeric.rb b/activesupport/lib/active_support/core_ext/numeric.rb index d5cfc2ece4..a6bc0624be 100644 --- a/activesupport/lib/active_support/core_ext/numeric.rb +++ b/activesupport/lib/active_support/core_ext/numeric.rb @@ -1,4 +1,3 @@ require 'active_support/core_ext/numeric/bytes' require 'active_support/core_ext/numeric/time' require 'active_support/core_ext/numeric/conversions' -require 'active_support/core_ext/numeric/infinite_comparable' diff --git a/activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb deleted file mode 100644 index b5f1b0487b..0000000000 --- a/activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'active_support/core_ext/infinite_comparable' - -class Float - include InfiniteComparable -end - -class BigDecimal - include InfiniteComparable -end diff --git a/activesupport/lib/active_support/core_ext/object/inclusion.rb b/activesupport/lib/active_support/core_ext/object/inclusion.rb index 3fec465ec0..b5671f66d0 100644 --- a/activesupport/lib/active_support/core_ext/object/inclusion.rb +++ b/activesupport/lib/active_support/core_ext/object/inclusion.rb @@ -1,25 +1,15 @@ class Object - # Returns true if this object is included in the argument(s). Argument must be - # any object which responds to +#include?+ or optionally, multiple arguments can be passed in. Usage: + # Returns true if this object is included in the argument. Argument must be + # any object which responds to +#include?+. Usage: # - # characters = ['Konata', 'Kagami', 'Tsukasa'] - # 'Konata'.in?(characters) # => true + # characters = ["Konata", "Kagami", "Tsukasa"] + # "Konata".in?(characters) # => true # - # character = 'Konata' - # character.in?('Konata', 'Kagami', 'Tsukasa') # => true - # - # This will throw an ArgumentError if a single argument is passed in and it doesn't respond + # This will throw an ArgumentError if the argument doesn't respond # to +#include?+. - def in?(*args) - if args.length > 1 - args.include? self - else - another_object = args.first - if another_object.respond_to? :include? - another_object.include? self - else - raise ArgumentError.new 'The single parameter passed to #in? must respond to #include?' - end - end + def in?(another_object) + another_object.include?(self) + rescue NoMethodError + raise ArgumentError.new("The parameter passed to #in? must respond to #include?") end end diff --git a/activesupport/lib/active_support/core_ext/object/to_param.rb b/activesupport/lib/active_support/core_ext/object/to_param.rb index 0d5f3501e5..3b137ce6ae 100644 --- a/activesupport/lib/active_support/core_ext/object/to_param.rb +++ b/activesupport/lib/active_support/core_ext/object/to_param.rb @@ -53,6 +53,6 @@ class Hash def to_param(namespace = nil) collect do |key, value| value.to_query(namespace ? "#{namespace}[#{key}]" : key) - end.sort * '&' + end.sort! * '&' end end diff --git a/activesupport/lib/active_support/core_ext/range/include_range.rb b/activesupport/lib/active_support/core_ext/range/include_range.rb index 3af66aaf2f..3a07401c8a 100644 --- a/activesupport/lib/active_support/core_ext/range/include_range.rb +++ b/activesupport/lib/active_support/core_ext/range/include_range.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/module/aliasing' + class Range # Extends the default Range#include? to support range comparisons. # (1..5).include?(1..5) # => true diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb index 428fa1f826..6691fc0995 100644 --- a/activesupport/lib/active_support/core_ext/string/conversions.rb +++ b/activesupport/lib/active_support/core_ext/string/conversions.rb @@ -15,24 +15,23 @@ class String # "2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 +0100 # "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0100 # "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 05:12:00 UTC + # "12/13/2012".to_time # => ArgumentError: argument out of range def to_time(form = :local) parts = Date._parse(self, false) return if parts.empty? now = Time.now - offset = parts[:offset] - utc_offset = form == :utc ? 0 : now.utc_offset - adjustment = offset ? offset - utc_offset : 0 - - Time.send( - form, + time = Time.new( parts.fetch(:year, now.year), parts.fetch(:mon, now.month), parts.fetch(:mday, now.day), parts.fetch(:hour, 0), parts.fetch(:min, 0), - parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0) - ) - adjustment + parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0), + parts.fetch(:offset, form == :utc ? 0 : nil) + ) + + form == :utc ? time.utc : time.getlocal end # Converts a string to a Date value. diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb index 0b506a6030..56e8a5f98d 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -185,7 +185,7 @@ class String # # Singular names are not handled correctly. # - # 'business'.classify # => "Busines" + # 'business'.classify # => "Business" def classify ActiveSupport::Inflector.classify(self) end diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index dc033ed11b..7c21abea72 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -171,17 +171,15 @@ module ActiveSupport #:nodoc: end UNSAFE_STRING_METHODS.each do |unsafe_method| - if 'String'.respond_to?(unsafe_method) - class_eval <<-EOT, __FILE__, __LINE__ + 1 - def #{unsafe_method}(*args, &block) # def capitalize(*args, &block) - to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block) - end # end - - def #{unsafe_method}!(*args) # def capitalize!(*args) - @html_safe = false # @html_safe = false - super # super - end # end - EOT + if String.new.respond_to?(unsafe_method) + define_method(unsafe_method) do |*args, &block| + to_str.send(unsafe_method, *args, &block) + end + + define_method("#{unsafe_method}!") do |*args| + @html_safe = false + super(*args) + end end end end diff --git a/activesupport/lib/active_support/core_ext/time.rb b/activesupport/lib/active_support/core_ext/time.rb index af6b589b71..32cffe237d 100644 --- a/activesupport/lib/active_support/core_ext/time.rb +++ b/activesupport/lib/active_support/core_ext/time.rb @@ -3,4 +3,3 @@ require 'active_support/core_ext/time/calculations' require 'active_support/core_ext/time/conversions' require 'active_support/core_ext/time/marshal' require 'active_support/core_ext/time/zones' -require 'active_support/core_ext/time/infinite_comparable' diff --git a/activesupport/lib/active_support/core_ext/time/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/time/infinite_comparable.rb deleted file mode 100644 index 63795885f5..0000000000 --- a/activesupport/lib/active_support/core_ext/time/infinite_comparable.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'active_support/core_ext/infinite_comparable' - -class Time - include InfiniteComparable -end diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb index 485dc91063..a03a66b96b 100644 --- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -25,7 +25,7 @@ module ActiveSupport end end - # This DeprecatedObjectProxy transforms object to depracated object. + # This DeprecatedObjectProxy transforms object to deprecated object. # # @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!") # @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!", deprecator_instance) @@ -52,7 +52,7 @@ module ActiveSupport end # This DeprecatedInstanceVariableProxy transforms instance variable to - # depracated instance variable. + # deprecated instance variable. # # class Example # def initialize(deprecator) @@ -93,7 +93,7 @@ module ActiveSupport end end - # This DeprecatedConstantProxy transforms constant to depracated constant. + # This DeprecatedConstantProxy transforms constant to deprecated constant. # # OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST') # OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST', deprecator_instance) diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index b6c18fa54b..9a9ed02bd9 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -223,7 +223,7 @@ module ActiveSupport def deep_stringify_keys; dup end undef :symbolize_keys! undef :deep_symbolize_keys! - def symbolize_keys; to_hash.symbolize_keys end + def symbolize_keys; to_hash.symbolize_keys! end def deep_symbolize_keys; to_hash.deep_symbolize_keys end def to_options!; self end diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb index 22521a8e93..6cc98191d4 100644 --- a/activesupport/lib/active_support/i18n.rb +++ b/activesupport/lib/active_support/i18n.rb @@ -1,13 +1,13 @@ +require 'active_support/core_ext/hash/deep_merge' +require 'active_support/core_ext/hash/except' +require 'active_support/core_ext/hash/slice' begin - require 'active_support/core_ext/hash/deep_merge' - require 'active_support/core_ext/hash/except' - require 'active_support/core_ext/hash/slice' require 'i18n' - require 'active_support/lazy_load_hooks' rescue LoadError => e $stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install" raise e end +require 'active_support/lazy_load_hooks' ActiveSupport.run_load_hooks(:i18n) I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml" diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb index a4a32b2ad0..30833a4cb1 100644 --- a/activesupport/lib/active_support/json/decoding.rb +++ b/activesupport/lib/active_support/json/decoding.rb @@ -1,6 +1,6 @@ require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/module/delegation' -require 'multi_json' +require 'json' module ActiveSupport # Look for and parse json strings that look like ISO 8601 times. @@ -13,8 +13,8 @@ module ActiveSupport # # ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}") # => {"team" => "rails", "players" => "36"} - def decode(json, options ={}) - data = MultiJson.load(json, options) + def decode(json, proc = nil, options = {}) + data = ::JSON.load(json, proc, options) if ActiveSupport.parse_json_times convert_dates_from(data) else @@ -22,23 +22,6 @@ module ActiveSupport end end - def engine - MultiJson.adapter - end - alias :backend :engine - - def engine=(name) - MultiJson.use(name) - end - alias :backend= :engine= - - def with_backend(name) - old_backend, self.backend = backend, name - yield - ensure - self.backend = old_backend - end - # Returns the class of the error that will be raised when there is an # error in decoding JSON. Using this method means you won't directly # depend on the ActiveSupport's JSON implementation, in case it changes @@ -50,7 +33,7 @@ module ActiveSupport # Rails.logger.warn("Attempted to decode invalid JSON: #{some_string}") # end def parse_error - MultiJson::DecodeError + ::JSON::ParserError end private diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 9bf1ea35b3..7f8b41d218 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -1,3 +1,5 @@ +#encoding: us-ascii + require 'active_support/core_ext/object/to_json' require 'active_support/core_ext/module/delegation' require 'active_support/json/variable' @@ -98,13 +100,18 @@ module ActiveSupport "\010" => '\b', "\f" => '\f', "\n" => '\n', + "\xe2\x80\xa8" => '\u2028', + "\xe2\x80\xa9" => '\u2029', "\r" => '\r', "\t" => '\t', '"' => '\"', '\\' => '\\\\', '>' => '\u003E', '<' => '\u003C', - '&' => '\u0026' } + '&' => '\u0026', + "#{0xe2.chr}#{0x80.chr}#{0xa8.chr}" => '\u2028', + "#{0xe2.chr}#{0x80.chr}#{0xa9.chr}" => '\u2029', + } class << self # If true, use ISO 8601 format for dates and times. Otherwise, fall back @@ -121,9 +128,9 @@ module ActiveSupport def escape_html_entities_in_json=(value) self.escape_regex = \ if @escape_html_entities_in_json = value - /[\x00-\x1F"\\><&]/ + /\xe2\x80\xa8|\xe2\x80\xa9|[\x00-\x1F"\\><&]/ else - /[\x00-\x1F"\\]/ + /\xe2\x80\xa8|\xe2\x80\xa9|[\x00-\x1F"\\]/ end end diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb index 37124fb7ae..598c46bce5 100644 --- a/activesupport/lib/active_support/key_generator.rb +++ b/activesupport/lib/active_support/key_generator.rb @@ -4,7 +4,7 @@ require 'openssl' module ActiveSupport # KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2 # It can be used to derive a number of keys for various purposes from a given secret. - # This lets rails applications have a single secure secret, but avoid reusing that + # This lets Rails applications have a single secure secret, but avoid reusing that # key in multiple incompatible contexts. class KeyGenerator def initialize(secret, options = {}) diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb index e489512531..e2b8f0f648 100644 --- a/activesupport/lib/active_support/lazy_load_hooks.rb +++ b/activesupport/lib/active_support/lazy_load_hooks.rb @@ -1,5 +1,5 @@ module ActiveSupport - # lazy_load_hooks allows rails to lazily load a lot of components and thus + # lazy_load_hooks allows Rails to lazily load a lot of components and thus # making the app boot faster. Because of this feature now there is no need to # require <tt>ActiveRecord::Base</tt> at boot time purely to apply # configuration. Instead a hook is registered that applies configuration once diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index ce40a7d689..bffdfc6201 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -12,10 +12,11 @@ module ActiveSupport # This can be used in situations similar to the <tt>MessageVerifier</tt>, but # where you don't want users to be able to determine the value of the payload. # - # key = OpenSSL::Digest::SHA256.new('password').digest # => "\x89\xE0\x156\xAC..." - # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...> - # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..." - # crypt.decrypt_and_verify(encrypted_data) # => "my secret data" + # salt = SecureRandom.random_bytes(64) + # key = ActiveSupport::KeyGenerator.new('password').generate_key(salt) # => "\x89\xE0\x156\xAC..." + # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...> + # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..." + # crypt.decrypt_and_verify(encrypted_data) # => "my secret data" class MessageEncryptor module NullSerializer #:nodoc: def self.load(value) @@ -28,7 +29,7 @@ module ActiveSupport end class InvalidMessage < StandardError; end - OpenSSLCipherError = OpenSSL::Cipher.const_defined?(:CipherError) ? OpenSSL::Cipher::CipherError : OpenSSL::CipherError + OpenSSLCipherError = OpenSSL::Cipher::CipherError # Initialize a new MessageEncryptor. +secret+ must be at least as long as # the cipher key size. For the default 'aes-256-cbc' cipher, this is 256 @@ -66,12 +67,11 @@ module ActiveSupport def _encrypt(value) cipher = new_cipher - # Rely on OpenSSL for the initialization vector - iv = cipher.random_iv - cipher.encrypt cipher.key = @secret - cipher.iv = iv + + # Rely on OpenSSL for the initialization vector + iv = cipher.random_iv encrypted_data = cipher.update(@serializer.dump(value)) encrypted_data << cipher.final diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb index cbc1608349..f1dfff738c 100644 --- a/activesupport/lib/active_support/multibyte/unicode.rb +++ b/activesupport/lib/active_support/multibyte/unicode.rb @@ -218,51 +218,31 @@ module ActiveSupport # Passing +true+ will forcibly tidy all bytes, assuming that the string's # encoding is entirely CP1252 or ISO-8859-1. def tidy_bytes(string, force = false) + return string if string.empty? + if force - return string.unpack("C*").map do |b| - tidy_byte(b) - end.flatten.compact.pack("C*").unpack("U*").pack("U*") + return string.encode(Encoding::UTF_8, Encoding::Windows_1252, invalid: :replace, undef: :replace) end - bytes = string.unpack("C*") - conts_expected = 0 - last_lead = 0 - - bytes.each_index do |i| + # We can't transcode to the same format, so we choose a nearly-identical encoding. + # We're going to 'transcode' bytes from UTF-8 when possible, then fall back to + # CP1252 when we get errors. The final string will be 'converted' back to UTF-8 + # before returning. + reader = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_8_MAC) - byte = bytes[i] - is_cont = byte > 127 && byte < 192 - is_lead = byte > 191 && byte < 245 - is_unused = byte > 240 - is_restricted = byte > 244 + source = string.dup + out = ''.force_encoding(Encoding::UTF_8_MAC) - # Impossible or highly unlikely byte? Clean it. - if is_unused || is_restricted - bytes[i] = tidy_byte(byte) - elsif is_cont - # Not expecting continuation byte? Clean up. Otherwise, now expect one less. - conts_expected == 0 ? bytes[i] = tidy_byte(byte) : conts_expected -= 1 - else - if conts_expected > 0 - # Expected continuation, but got ASCII or leading? Clean backwards up to - # the leading byte. - (1..(i - last_lead)).each {|j| bytes[i - j] = tidy_byte(bytes[i - j])} - conts_expected = 0 - end - if is_lead - # Final byte is leading? Clean it. - if i == bytes.length - 1 - bytes[i] = tidy_byte(bytes.last) - else - # Valid leading byte? Expect continuations determined by position of - # first zero bit, with max of 3. - conts_expected = byte < 224 ? 1 : byte < 240 ? 2 : 3 - last_lead = i - end - end - end + loop do + reader.primitive_convert(source, out) + _, _, _, error_bytes, _ = reader.primitive_errinfo + break if error_bytes.nil? + out << error_bytes.encode(Encoding::UTF_8_MAC, Encoding::Windows_1252, invalid: :replace, undef: :replace) end - bytes.empty? ? "" : bytes.flatten.compact.pack("C*").unpack("U*").pack("U*") + + reader.finish + + out.encode!(Encoding::UTF_8) end # Returns the KC normalization of the string by default. NFKC is diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb index cc935e6cb9..414960d2b1 100644 --- a/activesupport/lib/active_support/number_helper.rb +++ b/activesupport/lib/active_support/number_helper.rb @@ -295,7 +295,7 @@ module ActiveSupport options = format_options(options[:locale]).merge!(options) - parts = number.to_s.to_str.split('.') + parts = number.to_s.split('.') parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}") parts.join(options[:separator]) end @@ -356,7 +356,8 @@ module ActiveSupport digits, rounded_number = 1, 0 else digits = (Math.log10(number.abs) + 1).floor - rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new((10 ** (digits - precision)).to_f.to_s)).round.to_f * 10 ** (digits - precision) + multiplier = 10 ** (digits - precision) + rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new(multiplier.to_f.to_s)).round.to_f * multiplier digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed end precision -= digits diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb index c9518bda79..e03bb4ca0f 100644 --- a/activesupport/lib/active_support/ordered_options.rb +++ b/activesupport/lib/active_support/ordered_options.rb @@ -40,6 +40,14 @@ module ActiveSupport end end + # +InheritableOptions+ provides a constructor to build an +OrderedOptions+ + # hash inherited from the another hash. + # + # Use this if you already have some hash and you want to create a new one based on it. + # + # h = ActiveSupport::InheritableOptions.new({ girl: 'Mary', boy: 'John' }) + # h.girl # => 'Mary' + # h.boy # => 'John' class InheritableOptions < OrderedOptions def initialize(parent = nil) if parent.kind_of?(OrderedOptions) diff --git a/activesupport/lib/active_support/subscriber.rb b/activesupport/lib/active_support/subscriber.rb index ab76cf5c85..34c6f900c1 100644 --- a/activesupport/lib/active_support/subscriber.rb +++ b/activesupport/lib/active_support/subscriber.rb @@ -1,3 +1,5 @@ +require 'active_support/per_thread_registry' + module ActiveSupport # ActiveSupport::Subscriber is an object set to consume # ActiveSupport::Notifications. The subscriber dispatches notifications to @@ -68,8 +70,24 @@ module ActiveSupport private - def event_stack - Thread.current[@queue_key] ||= [] + def event_stack + SubscriberQueueRegistry.get_queue(@queue_key) + end + end + + # This is a registry for all the event stacks kept for subscribers. + # + # See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt> + # for further details. + class SubscriberQueueRegistry # :nodoc: + extend PerThreadRegistry + + def initialize + @registry = {} + end + + def get_queue(queue_key) + @registry[queue_key] ||= [] end end end diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb index d70d971538..e16b73a036 100644 --- a/activesupport/lib/active_support/testing/isolation.rb +++ b/activesupport/lib/active_support/testing/isolation.rb @@ -40,6 +40,10 @@ module ActiveSupport def method_missing(name, *args) @calls << [name, args] end + + def info_signal + Signal.list['INFO'] + end end module Isolation diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index ca23057189..8762330a6e 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -1,7 +1,7 @@ module ActiveSupport # Returns the version of the currently loaded ActiveSupport as a Gem::Version def self.version - Gem::Version.new "4.0.0.beta1" + Gem::Version.new "4.1.0.beta" end module VERSION #:nodoc: diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index bcc200cf33..ae6eaa4b60 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -257,6 +257,26 @@ module CacheStoreBehavior assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu')) end + def test_fetch_multi + @cache.write('foo', 'bar') + @cache.write('fud', 'biz') + + values = @cache.fetch_multi('foo', 'fu', 'fud') {|value| value * 2 } + + assert_equal(["bar", "fufu", "biz"], values) + assert_equal("fufu", @cache.read('fu')) + end + + def test_multi_with_objects + foo = stub(:title => "FOO!", :cache_key => "foo") + bar = stub(:cache_key => "bar") + + @cache.write('bar', "BAM!") + + values = @cache.fetch_multi(foo, bar) {|object| object.title } + assert_equal(["FOO!", "BAM!"], values) + end + def test_read_and_write_compressed_small_data @cache.write('foo', 'bar', :compress => true) assert_equal 'bar', @cache.read('foo') diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb index 13f2e3cdaf..86057f7cbb 100644 --- a/activesupport/test/callbacks_test.rb +++ b/activesupport/test/callbacks_test.rb @@ -66,6 +66,16 @@ module CallbacksTest end end + class CallbackClass + def self.before(model) + model.history << [:before_save, :class] + end + + def self.after(model) + model.history << [:after_save, :class] + end + end + class Person < Record [:before_save, :after_save].each do |callback_method| callback_method_sym = callback_method.to_sym @@ -73,6 +83,7 @@ module CallbacksTest send(callback_method, callback_string(callback_method_sym)) send(callback_method, callback_proc(callback_method_sym)) send(callback_method, callback_object(callback_method_sym.to_s.gsub(/_save/, ''))) + send(callback_method, CallbackClass) send(callback_method) { |model| model.history << [callback_method_sym, :block] } end @@ -86,10 +97,14 @@ module CallbacksTest skip_callback :save, :after, :before_save_method, :unless => :yes skip_callback :save, :after, :before_save_method, :if => :no skip_callback :save, :before, :before_save_method, :unless => :no + skip_callback :save, :before, CallbackClass , :if => :yes def yes; true; end def no; false; end end + class PersonForProgrammaticSkipping < Person + end + class ParentController include ActiveSupport::Callbacks @@ -430,6 +445,26 @@ module CallbacksTest [:before_save, :object], [:before_save, :block], [:after_save, :block], + [:after_save, :class], + [:after_save, :object], + [:after_save, :proc], + [:after_save, :string], + [:after_save, :symbol] + ], person.history + end + + def test_skip_person_programmatically + PersonForProgrammaticSkipping._save_callbacks.each do |save_callback| + if "before" == save_callback.kind.to_s + PersonForProgrammaticSkipping.skip_callback("save", save_callback.kind, save_callback.filter) + end + end + person = PersonForProgrammaticSkipping.new + assert_equal [], person.history + person.save + assert_equal [ + [:after_save, :block], + [:after_save, :class], [:after_save, :object], [:after_save, :proc], [:after_save, :string], @@ -449,8 +484,10 @@ module CallbacksTest [:before_save, :string], [:before_save, :proc], [:before_save, :object], + [:before_save, :class], [:before_save, :block], [:after_save, :block], + [:after_save, :class], [:after_save, :object], [:after_save, :proc], [:after_save, :string], @@ -681,7 +718,7 @@ module CallbacksTest def test_termination_invokes_hook terminator = CallbackTerminator.new terminator.save - assert_equal ":second", terminator.halted + assert_equal :second, terminator.halted end def test_block_never_called_if_terminated @@ -715,8 +752,10 @@ module CallbacksTest [:before_save, :string], [:before_save, :proc], [:before_save, :object], + [:before_save, :class], [:before_save, :block], [:after_save, :block], + [:after_save, :class], [:after_save, :object], [:after_save, :proc], [:after_save, :string], @@ -735,7 +774,7 @@ module CallbacksTest class PerKeyOptionDeprecationTest < ActiveSupport::TestCase - def test_per_key_option_deprecaton + def test_per_key_option_deprecation assert_raise NotImplementedError do Phone.class_eval do set_callback :save, :before, :before_save1, :per_key => {:if => "true"} @@ -762,4 +801,240 @@ module CallbacksTest assert_equal ["two", "one", "three", "yielded"], model.record end end + + class CallbackProcTest < ActiveSupport::TestCase + def build_class(callback) + Class.new { + include ActiveSupport::Callbacks + define_callbacks :foo + set_callback :foo, :before, callback + def run; run_callbacks :foo; end + } + end + + def test_proc_arity_0 + calls = [] + klass = build_class(->() { calls << :foo }) + klass.new.run + assert_equal [:foo], calls + end + + def test_proc_arity_1 + calls = [] + klass = build_class(->(o) { calls << o }) + instance = klass.new + instance.run + assert_equal [instance], calls + end + + def test_proc_arity_2 + assert_raises(ArgumentError) do + klass = build_class(->(x,y) { }) + klass.new.run + end + end + + def test_proc_negative_called_with_empty_list + calls = [] + klass = build_class(->(*args) { calls << args }) + klass.new.run + assert_equal [[]], calls + end + end + + class ConditionalTests < ActiveSupport::TestCase + def build_class(callback) + Class.new { + include ActiveSupport::Callbacks + define_callbacks :foo + set_callback :foo, :before, :foo, :if => callback + def foo; end + def run; run_callbacks :foo; end + } + end + + # FIXME: do we really want to support classes as conditionals? There were + # no tests for it previous to this. + def test_class_conditional_with_scope + z = [] + callback = Class.new { + define_singleton_method(:foo) { |o| z << o } + } + klass = Class.new { + include ActiveSupport::Callbacks + define_callbacks :foo, :scope => [:name] + set_callback :foo, :before, :foo, :if => callback + def run; run_callbacks :foo; end + private + def foo; end + } + object = klass.new + object.run + assert_equal [object], z + end + + # FIXME: do we really want to support classes as conditionals? There were + # no tests for it previous to this. + def test_class + z = [] + klass = build_class Class.new { + define_singleton_method(:before) { |o| z << o } + } + object = klass.new + object.run + assert_equal [object], z + end + + def test_proc_negative_arity # passes an empty list if *args + z = [] + object = build_class(->(*args) { z << args }).new + object.run + assert_equal [], z.flatten + end + + def test_proc_arity0 + z = [] + object = build_class(->() { z << 0 }).new + object.run + assert_equal [0], z + end + + def test_proc_arity1 + z = [] + object = build_class(->(x) { z << x }).new + object.run + assert_equal [object], z + end + + def test_proc_arity2 + assert_raises(ArgumentError) do + object = build_class(->(a,b) { }).new + object.run + end + end + end + + class ResetCallbackTest < ActiveSupport::TestCase + def build_class(memo) + klass = Class.new { + include ActiveSupport::Callbacks + define_callbacks :foo + set_callback :foo, :before, :hello + def run; run_callbacks :foo; end + } + klass.class_eval { + define_method(:hello) { memo << :hi } + } + klass + end + + def test_reset_callbacks + events = [] + klass = build_class events + klass.new.run + assert_equal 1, events.length + + klass.reset_callbacks :foo + klass.new.run + assert_equal 1, events.length + end + + def test_reset_impacts_subclasses + events = [] + klass = build_class events + subclass = Class.new(klass) { set_callback :foo, :before, :world } + subclass.class_eval { define_method(:world) { events << :world } } + + subclass.new.run + assert_equal 2, events.length + + klass.reset_callbacks :foo + subclass.new.run + assert_equal 3, events.length + end + end + + class CallbackTypeTest < ActiveSupport::TestCase + def build_class(callback, n = 10) + Class.new { + include ActiveSupport::Callbacks + define_callbacks :foo + n.times { set_callback :foo, :before, callback } + def run; run_callbacks :foo; end + def self.skip(thing); skip_callback :foo, :before, thing; end + } + end + + def test_add_class + calls = [] + callback = Class.new { + define_singleton_method(:before) { |o| calls << o } + } + build_class(callback).new.run + assert_equal 10, calls.length + end + + def test_add_lambda + calls = [] + build_class(->(o) { calls << o }).new.run + assert_equal 10, calls.length + end + + def test_add_symbol + calls = [] + klass = build_class(:bar) + klass.class_eval { define_method(:bar) { calls << klass } } + klass.new.run + assert_equal 1, calls.length + end + + def test_add_eval + calls = [] + klass = build_class("bar") + klass.class_eval { define_method(:bar) { calls << klass } } + klass.new.run + assert_equal 1, calls.length + end + + def test_skip_class # removes one at a time + calls = [] + callback = Class.new { + define_singleton_method(:before) { |o| calls << o } + } + klass = build_class(callback) + 9.downto(0) { |i| + klass.skip callback + klass.new.run + assert_equal i, calls.length + calls.clear + } + end + + def test_skip_lambda # removes nothing + calls = [] + callback = ->(o) { calls << o } + klass = build_class(callback) + 10.times { klass.skip callback } + klass.new.run + assert_equal 10, calls.length + end + + def test_skip_symbol # removes all + calls = [] + klass = build_class(:bar) + klass.class_eval { define_method(:bar) { calls << klass } } + klass.skip :bar + klass.new.run + assert_equal 0, calls.length + end + + def test_skip_eval # removes nothing + calls = [] + klass = build_class("bar") + klass.class_eval { define_method(:bar) { calls << klass } } + klass.skip "bar" + klass.new.run + assert_equal 1, calls.length + end + end end diff --git a/activesupport/test/core_ext/date_and_time_behavior.rb b/activesupport/test/core_ext/date_and_time_behavior.rb index 9927856aa2..b4ef5a0597 100644 --- a/activesupport/test/core_ext/date_and_time_behavior.rb +++ b/activesupport/test/core_ext/date_and_time_behavior.rb @@ -95,6 +95,11 @@ module DateAndTimeBehavior end def test_next_week + # M | T | W | T | F | S | S # M | T | W | T | F | S | S # + # | 22/2 | | | | | # 28/2 | | | | | | # monday in next week `next_week` + # | 22/2 | | | | | # | | | | 4/3 | | # friday in next week `next_week(:friday)` + # 23/10 | | | | | | # 30/10 | | | | | | # monday in next week `next_week` + # 23/10 | | | | | | # | | 1/11 | | | | # wednesday in next week `next_week(:wednesday)` assert_equal date_time_init(2005,2,28,0,0,0), date_time_init(2005,2,22,15,15,10).next_week assert_equal date_time_init(2005,3,4,0,0,0), date_time_init(2005,2,22,15,15,10).next_week(:friday) assert_equal date_time_init(2006,10,30,0,0,0), date_time_init(2006,10,23,0,0,0).next_week diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index f3fa96ec6f..b1adf6d396 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -357,11 +357,6 @@ class DateExtBehaviorTest < ActiveSupport::TestCase Date.today.freeze.freeze end end - - def test_compare_with_infinity - assert_equal(-1, Date.today <=> Float::INFINITY) - assert_equal(1, Date.today <=> -Float::INFINITY) - end end class DateExtConversionsTest < ActiveSupport::TestCase diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index 7be578599b..3e76b8a9b1 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -335,10 +335,3 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') end end - -class DateTimeExtBehaviorTest < ActiveSupport::TestCase - def test_compare_with_infinity - assert_equal(-1, DateTime.now <=> Float::INFINITY) - assert_equal(1, DateTime.now <=> -Float::INFINITY) - end -end diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb index 82249ddd1b..8872611fb1 100644 --- a/activesupport/test/core_ext/module_test.rb +++ b/activesupport/test/core_ext/module_test.rb @@ -82,6 +82,21 @@ class Name end end +class SideEffect + attr_reader :ints + + delegate :to_i, :to => :shift, :allow_nil => true + delegate :to_s, :to => :shift + + def initialize + @ints = [1, 2, 3] + end + + def shift + @ints.shift + end +end + class ModuleTest < ActiveSupport::TestCase def setup @david = Someone.new("David", Somewhere.new("Paulina", "Chicago")) @@ -171,6 +186,17 @@ class ModuleTest < ActiveSupport::TestCase assert_nil rails.name end + # Ensures with check for nil, not for a falseish target. + def test_delegation_with_allow_nil_and_false_value + project = Project.new(false, false) + assert_raise(NoMethodError) { project.name } + end + + def test_delegation_with_allow_nil_and_invalid_value + rails = Project.new("Rails", "David") + assert_raise(NoMethodError) { rails.name } + end + def test_delegation_with_allow_nil_and_nil_value_and_prefix Project.class_eval do delegate :name, :to => :person, :allow_nil => true, :prefix => true @@ -228,6 +254,16 @@ class ModuleTest < ActiveSupport::TestCase "[#{e.backtrace.inspect}] did not include [#{file_and_line}]" end + def test_delegation_invokes_the_target_exactly_once + se = SideEffect.new + + assert_equal 1, se.to_i + assert_equal [2, 3], se.ints + + assert_equal '2', se.to_s + assert_equal [3], se.ints + end + def test_parent assert_equal Yz::Zy, Yz::Zy::Cd.parent assert_equal Yz, Yz::Zy.parent diff --git a/activesupport/test/core_ext/numeric_ext_test.rb b/activesupport/test/core_ext/numeric_ext_test.rb index 3744d50864..f22ae3ccac 100644 --- a/activesupport/test/core_ext/numeric_ext_test.rb +++ b/activesupport/test/core_ext/numeric_ext_test.rb @@ -441,57 +441,3 @@ class NumericExtFormattingTest < ActiveSupport::TestCase assert_equal '1 Million', BigDecimal("1000010").to_s(:human) end end - -class NumericExtBehaviorTest < ActiveSupport::TestCase - def setup - @inf = BigDecimal.new('Infinity') - end - - def test_compare_infinity_with_date - assert_equal(-1, -Float::INFINITY <=> Date.today) - assert_equal(1, Float::INFINITY <=> Date.today) - assert_equal(-1, -@inf <=> Date.today) - assert_equal(1, @inf <=> Date.today) - end - - def test_compare_infinty_with_infinty - assert_equal(-1, -Float::INFINITY <=> Float::INFINITY) - assert_equal(1, Float::INFINITY <=> -Float::INFINITY) - assert_equal(0, Float::INFINITY <=> Float::INFINITY) - assert_equal(0, -Float::INFINITY <=> -Float::INFINITY) - - assert_equal(-1, -Float::INFINITY <=> BigDecimal::INFINITY) - assert_equal(1, Float::INFINITY <=> -BigDecimal::INFINITY) - assert_equal(0, Float::INFINITY <=> BigDecimal::INFINITY) - assert_equal(0, -Float::INFINITY <=> -BigDecimal::INFINITY) - - assert_equal(-1, -BigDecimal::INFINITY <=> Float::INFINITY) - assert_equal(1, BigDecimal::INFINITY <=> -Float::INFINITY) - assert_equal(0, BigDecimal::INFINITY <=> Float::INFINITY) - assert_equal(0, -BigDecimal::INFINITY <=> -Float::INFINITY) - end - - def test_compare_infinity_with_time - assert_equal(-1, -Float::INFINITY <=> Time.now) - assert_equal(1, Float::INFINITY <=> Time.now) - assert_equal(-1, -@inf <=> Time.now) - assert_equal(1, @inf <=> Time.now) - end - - def test_compare_infinity_with_datetime - assert_equal(-1, -Float::INFINITY <=> DateTime.now) - assert_equal(1, Float::INFINITY <=> DateTime.now) - assert_equal(-1, -@inf <=> DateTime.now) - assert_equal(1, @inf <=> DateTime.now) - end - - def test_compare_infinity_with_twz - time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] - twz = ActiveSupport::TimeWithZone.new(Time.now, time_zone) - - assert_equal(-1, -Float::INFINITY <=> twz) - assert_equal(1, Float::INFINITY <=> twz) - assert_equal(-1, -@inf <=> twz) - assert_equal(1, @inf <=> twz) - end -end diff --git a/activesupport/test/core_ext/object/inclusion_test.rb b/activesupport/test/core_ext/object/inclusion_test.rb index 22888333f5..478706eeae 100644 --- a/activesupport/test/core_ext/object/inclusion_test.rb +++ b/activesupport/test/core_ext/object/inclusion_test.rb @@ -2,16 +2,6 @@ require 'abstract_unit' require 'active_support/core_ext/object/inclusion' class InTest < ActiveSupport::TestCase - def test_in_multiple_args - assert :b.in?(:a,:b) - assert !:c.in?(:a,:b) - end - - def test_in_multiple_arrays - assert [1,2].in?([1,2],[2,3]) - assert ![1,2].in?([1,3],[2,1]) - end - def test_in_array assert 1.in?([1,2]) assert !3.in?([1,2]) diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb index 6e94d5e10d..3e2355ae23 100644 --- a/activesupport/test/core_ext/range_ext_test.rb +++ b/activesupport/test/core_ext/range_ext_test.rb @@ -1,7 +1,6 @@ require 'abstract_unit' require 'active_support/time' require 'active_support/core_ext/range' -require 'active_support/core_ext/numeric' class RangeTest < ActiveSupport::TestCase def test_to_s_from_dates @@ -17,7 +16,6 @@ class RangeTest < ActiveSupport::TestCase def test_date_range assert_instance_of Range, DateTime.new..DateTime.new assert_instance_of Range, DateTime::Infinity.new..DateTime::Infinity.new - assert_instance_of Range, DateTime.new..DateTime::Infinity.new end def test_overlaps_last_inclusive @@ -92,28 +90,4 @@ class RangeTest < ActiveSupport::TestCase time_range_2 = Time.utc(2005, 12, 10, 17, 31)..Time.utc(2005, 12, 10, 18, 00) assert !time_range_1.overlaps?(time_range_2) end - - def test_infinite_bounds - time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] - - time = Time.now - date = Date.today - datetime = DateTime.now - twz = ActiveSupport::TimeWithZone.new(time, time_zone) - - infinity1 = Float::INFINITY - infinity2 = BigDecimal.new('Infinity') - - [infinity1, infinity2].each do |infinity| - [time, date, datetime, twz].each do |bound| - [time, date, datetime, twz].each do |value| - assert Range.new(bound, infinity).include?(value + 10.years) - assert Range.new(-infinity, bound).include?(value - 10.years) - - assert !Range.new(bound, infinity).include?(value - 10.years) - assert !Range.new(-infinity, bound).include?(value + 10.years) - end - end - end - end end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index 62c5741ffb..8f0ebc13ea 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -300,9 +300,9 @@ class StringConversionsTest < ActiveSupport::TestCase assert_equal Time.local(2005, 2, 27, 23, 50, 19, 275038), "2005-02-27T23:50:19.275038".to_time assert_equal Time.utc(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_time(:utc) assert_equal Time.local(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_time - assert_equal Time.local(2011, 2, 27, 18, 50), "2011-02-27 13:50 -0100".to_time + assert_equal Time.local(2011, 2, 27, 17, 50), "2011-02-27 13:50 -0100".to_time assert_equal Time.utc(2011, 2, 27, 23, 50), "2011-02-27 22:50 -0100".to_time(:utc) - assert_equal Time.local(2005, 2, 27, 23, 50), "2005-02-27 14:50 -0500".to_time + assert_equal Time.local(2005, 2, 27, 22, 50), "2005-02-27 14:50 -0500".to_time assert_nil "".to_time end end @@ -326,6 +326,122 @@ class StringConversionsTest < ActiveSupport::TestCase end end + def test_standard_time_string_to_time_when_current_time_is_standard_time + with_env_tz "US/Eastern" do + Time.stubs(:now).returns(Time.local(2012, 1, 1)) + assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time + assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 -0800".to_time + assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 -0800".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 -0500".to_time + assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 -0500".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 5, 0), "2012-01-01 10:00 UTC".to_time + assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00 UTC".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 PST".to_time + assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 PST".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 EST".to_time + assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 EST".to_time(:utc) + end + end + + def test_standard_time_string_to_time_when_current_time_is_daylight_savings + with_env_tz "US/Eastern" do + Time.stubs(:now).returns(Time.local(2012, 7, 1)) + assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time + assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 -0800".to_time + assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 -0800".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 -0500".to_time + assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 -0500".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 5, 0), "2012-01-01 10:00 UTC".to_time + assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00 UTC".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 PST".to_time + assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 PST".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 EST".to_time + assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 EST".to_time(:utc) + end + end + + def test_daylight_savings_string_to_time_when_current_time_is_standard_time + with_env_tz "US/Eastern" do + Time.stubs(:now).returns(Time.local(2012, 1, 1)) + assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time + assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 -0700".to_time + assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 -0700".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 -0400".to_time + assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 -0400".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 6, 0), "2012-07-01 10:00 UTC".to_time + assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00 UTC".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 PDT".to_time + assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 PDT".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 EDT".to_time + assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 EDT".to_time(:utc) + end + end + + def test_daylight_savings_string_to_time_when_current_time_is_daylight_savings + with_env_tz "US/Eastern" do + Time.stubs(:now).returns(Time.local(2012, 7, 1)) + assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time + assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 -0700".to_time + assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 -0700".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 -0400".to_time + assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 -0400".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 6, 0), "2012-07-01 10:00 UTC".to_time + assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00 UTC".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 PDT".to_time + assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 PDT".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 EDT".to_time + assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 EDT".to_time(:utc) + end + end + + def test_partial_string_to_time_when_current_time_is_standard_time + with_env_tz "US/Eastern" do + Time.stubs(:now).returns(Time.local(2012, 1, 1)) + assert_equal Time.local(2012, 1, 1, 10, 0), "10:00".to_time + assert_equal Time.utc(2012, 1, 1, 10, 0), "10:00".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 6, 0), "10:00 -0100".to_time + assert_equal Time.utc(2012, 1, 1, 11, 0), "10:00 -0100".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 10, 0), "10:00 -0500".to_time + assert_equal Time.utc(2012, 1, 1, 15, 0), "10:00 -0500".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 5, 0), "10:00 UTC".to_time + assert_equal Time.utc(2012, 1, 1, 10, 0), "10:00 UTC".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 13, 0), "10:00 PST".to_time + assert_equal Time.utc(2012, 1, 1, 18, 0), "10:00 PST".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 12, 0), "10:00 PDT".to_time + assert_equal Time.utc(2012, 1, 1, 17, 0), "10:00 PDT".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 10, 0), "10:00 EST".to_time + assert_equal Time.utc(2012, 1, 1, 15, 0), "10:00 EST".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 9, 0), "10:00 EDT".to_time + assert_equal Time.utc(2012, 1, 1, 14, 0), "10:00 EDT".to_time(:utc) + end + end + + def test_partial_string_to_time_when_current_time_is_daylight_savings + with_env_tz "US/Eastern" do + Time.stubs(:now).returns(Time.local(2012, 7, 1)) + assert_equal Time.local(2012, 7, 1, 10, 0), "10:00".to_time + assert_equal Time.utc(2012, 7, 1, 10, 0), "10:00".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 7, 0), "10:00 -0100".to_time + assert_equal Time.utc(2012, 7, 1, 11, 0), "10:00 -0100".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 11, 0), "10:00 -0500".to_time + assert_equal Time.utc(2012, 7, 1, 15, 0), "10:00 -0500".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 6, 0), "10:00 UTC".to_time + assert_equal Time.utc(2012, 7, 1, 10, 0), "10:00 UTC".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 14, 0), "10:00 PST".to_time + assert_equal Time.utc(2012, 7, 1, 18, 0), "10:00 PST".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 13, 0), "10:00 PDT".to_time + assert_equal Time.utc(2012, 7, 1, 17, 0), "10:00 PDT".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 11, 0), "10:00 EST".to_time + assert_equal Time.utc(2012, 7, 1, 15, 0), "10:00 EST".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 10, 0), "10:00 EDT".to_time + assert_equal Time.utc(2012, 7, 1, 14, 0), "10:00 EDT".to_time(:utc) + end + end + def test_string_to_datetime assert_equal DateTime.civil(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_datetime assert_equal 0, "2039-02-27 23:50".to_datetime.offset # use UTC offset diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index 2864d7a57f..4e53aff00b 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -855,10 +855,3 @@ class TimeExtMarshalingTest < ActiveSupport::TestCase assert_equal Time.local(2004, 2, 29), Time.local(2004, 5, 31).last_quarter end end - -class TimeExtBehaviorTest < ActiveSupport::TestCase - def test_compare_with_infinity - assert_equal(-1, Time.now <=> Float::INFINITY) - assert_equal(1, Time.now <=> -Float::INFINITY) - end -end diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index 98a87ab9e6..3ce3297874 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -1118,13 +1118,3 @@ class TimeWithZoneMethodsForString < ActiveSupport::TestCase Time.zone = old_tz end end - -class TimeWithZoneExtBehaviorTest < ActiveSupport::TestCase - def test_compare_with_infinity - time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] - twz = ActiveSupport::TimeWithZone.new(Time.now, time_zone) - - assert_equal(-1, twz <=> Float::INFINITY) - assert_equal(1, twz <=> -Float::INFINITY) - end -end diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index 115a4e894d..4b1426bb2e 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -526,7 +526,6 @@ class DependenciesTest < ActiveSupport::TestCase m = Module.new m.module_eval "def a() CountingLoader; end" extend m - kls = nil with_autoloading_fixtures do kls = nil assert_nothing_raised { kls = a } diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb index d1454902e5..34ed866848 100644 --- a/activesupport/test/json/decoding_test.rb +++ b/activesupport/test/json/decoding_test.rb @@ -55,29 +55,23 @@ class TestJSONDecoding < ActiveSupport::TestCase %q({"a":"Line1\u000aLine2"}) => {"a"=>"Line1\nLine2"} } - backends = [:ok_json] - backends << :json_gem if defined?(::JSON) - backends << :yajl if defined?(::Yajl) - - backends.each do |backend| - TESTS.each do |json, expected| - test "json decodes #{json} with the #{backend} backend" do - ActiveSupport.parse_json_times = true - silence_warnings do - ActiveSupport::JSON.with_backend backend do - assert_equal expected, ActiveSupport::JSON.decode(json) - end - end + TESTS.each do |json, expected| + test "json decodes #{json}" do + prev = ActiveSupport.parse_json_times + ActiveSupport.parse_json_times = true + silence_warnings do + assert_equal expected, ActiveSupport::JSON.decode(json) end + ActiveSupport.parse_json_times = prev end + end - test "json decodes time json with time parsing disabled with the #{backend} backend" do - ActiveSupport.parse_json_times = false - expected = {"a" => "2007-01-01 01:12:34 Z"} - ActiveSupport::JSON.with_backend backend do - assert_equal expected, ActiveSupport::JSON.decode(%({"a": "2007-01-01 01:12:34 Z"})) - end - end + test "json decodes time json with time parsing disabled" do + prev = ActiveSupport.parse_json_times + ActiveSupport.parse_json_times = false + expected = {"a" => "2007-01-01 01:12:34 Z"} + assert_equal expected, ActiveSupport::JSON.decode(%({"a": "2007-01-01 01:12:34 Z"})) + ActiveSupport.parse_json_times = prev end def test_failed_json_decoding diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index 12ce250eb3..106a7fb522 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -45,8 +45,8 @@ class TestJSONEncoding < ActiveSupport::TestCase StringTests = [[ 'this is the <string>', %("this is the \\u003Cstring\\u003E")], [ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ], [ 'http://test.host/posts/1', %("http://test.host/posts/1")], - [ "Control characters: \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - %("Control characters: \\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000B\\f\\r\\u000E\\u000F\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F") ]] + [ "Control characters: \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\342\200\250\342\200\251", + %("Control characters: \\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000B\\f\\r\\u000E\\u000F\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F\\u2028\\u2029") ]] ArrayTests = [[ ['a', 'b', 'c'], %([\"a\",\"b\",\"c\"]) ], [ [1, 'a', :b, nil, false], %([1,\"a\",\"b\",null,false]) ]] @@ -82,6 +82,8 @@ class TestJSONEncoding < ActiveSupport::TestCase constants.grep(/Tests$/).each do |class_tests| define_method("test_#{class_tests[0..-6].underscore}") do begin + prev = ActiveSupport.use_standard_json_time_format + ActiveSupport.escape_html_entities_in_json = class_tests !~ /^Standard/ ActiveSupport.use_standard_json_time_format = class_tests =~ /^Standard/ self.class.const_get(class_tests).each do |pair| @@ -89,7 +91,7 @@ class TestJSONEncoding < ActiveSupport::TestCase end ensure ActiveSupport.escape_html_entities_in_json = false - ActiveSupport.use_standard_json_time_format = false + ActiveSupport.use_standard_json_time_format = prev end end end @@ -172,16 +174,21 @@ class TestJSONEncoding < ActiveSupport::TestCase end def test_time_to_json_includes_local_offset + prev = ActiveSupport.use_standard_json_time_format ActiveSupport.use_standard_json_time_format = true with_env_tz 'US/Eastern' do assert_equal %("2005-02-01T15:15:10-05:00"), ActiveSupport::JSON.encode(Time.local(2005,2,1,15,15,10)) end ensure - ActiveSupport.use_standard_json_time_format = false + ActiveSupport.use_standard_json_time_format = prev end def test_hash_with_time_to_json + prev = ActiveSupport.use_standard_json_time_format + ActiveSupport.use_standard_json_time_format = false assert_equal '{"time":"2009/01/01 00:00:00 +0000"}', { :time => Time.utc(2009) }.to_json + ensure + ActiveSupport.use_standard_json_time_format = prev end def test_nested_hash_with_float @@ -302,12 +309,12 @@ class TestJSONEncoding < ActiveSupport::TestCase assert_equal({"name" => "David", "sub" => { "name" => "David", - "date" => "2010/01/01" }}, JSON.parse(json_custom)) + "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"}, + assert_equal({"name" => "David", "date" => "2010-01-01"}, JSON.parse(json_string_and_date)) end diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb index 06c7e8a1a7..509c453b5c 100644 --- a/activesupport/test/message_encryptor_test.rb +++ b/activesupport/test/message_encryptor_test.rb @@ -56,9 +56,14 @@ class MessageEncryptorTest < ActiveSupport::TestCase end def test_alternative_serialization_method + prev = ActiveSupport.use_standard_json_time_format + ActiveSupport.use_standard_json_time_format = true encryptor = ActiveSupport::MessageEncryptor.new(SecureRandom.hex(64), SecureRandom.hex(64), :serializer => JSONSerializer.new) message = encryptor.encrypt_and_sign({ :foo => 123, 'bar' => Time.utc(2010) }) - assert_equal encryptor.decrypt_and_verify(message), { "foo" => 123, "bar" => "2010-01-01T00:00:00Z" } + exp = { "foo" => 123, "bar" => "2010-01-01T00:00:00Z" } + assert_equal exp, encryptor.decrypt_and_verify(message) + ensure + ActiveSupport.use_standard_json_time_format = prev end private diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb index 5adff41653..a8633f7299 100644 --- a/activesupport/test/message_verifier_test.rb +++ b/activesupport/test/message_verifier_test.rb @@ -45,9 +45,14 @@ class MessageVerifierTest < ActiveSupport::TestCase end def test_alternative_serialization_method + prev = ActiveSupport.use_standard_json_time_format + ActiveSupport.use_standard_json_time_format = true verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!", :serializer => JSONSerializer.new) message = verifier.generate({ :foo => 123, 'bar' => Time.utc(2010) }) - assert_equal verifier.verify(message), { "foo" => 123, "bar" => "2010-01-01T00:00:00Z" } + exp = { "foo" => 123, "bar" => "2010-01-01T00:00:00Z" } + assert_equal exp, verifier.verify(message) + ensure + ActiveSupport.use_standard_json_time_format = prev end def assert_not_verified(message) diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index d63c59883a..33627a4e74 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -99,7 +99,7 @@ module Notifications @notifier.publish :foo @notifier.publish :foo - @notifier.subscribe("not_existant") do |*args| + @notifier.subscribe("not_existent") do |*args| @events << ActiveSupport::Notifications::Event.new(*args) end diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb index dfe9f3c11c..a1bdcf2396 100644 --- a/activesupport/test/test_case_test.rb +++ b/activesupport/test/test_case_test.rb @@ -19,6 +19,9 @@ module ActiveSupport def record(*args) end + + def info_signal + end end def test_standard_error_raised_within_setup_callback_is_puked diff --git a/activesupport/test/xml_mini/nokogirisax_engine_test.rb b/activesupport/test/xml_mini/nokogirisax_engine_test.rb index d4f63f6bd0..84a5c44a87 100644 --- a/activesupport/test/xml_mini/nokogirisax_engine_test.rb +++ b/activesupport/test/xml_mini/nokogirisax_engine_test.rb @@ -56,9 +56,9 @@ class NokogiriSAXEngineTest < ActiveSupport::TestCase end end - def test_setting_nokogiri_as_backend - XmlMini.backend = 'Nokogiri' - assert_equal XmlMini_Nokogiri, XmlMini.backend + def test_setting_nokogirisax_as_backend + XmlMini.backend = 'NokogiriSAX' + assert_equal XmlMini_NokogiriSAX, XmlMini.backend end def test_blank_returns_empty_hash |