aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/CHANGELOG.md497
-rw-r--r--activesupport/lib/active_support/cache.rb133
-rw-r--r--activesupport/lib/active_support/cache/strategy/local_cache.rb85
-rw-r--r--activesupport/lib/active_support/callbacks.rb84
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/array/grouping.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/array/uniq_by.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/array/wrap.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/benchmark.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/big_decimal/conversions.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute_accessors.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/date.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/date/conversions.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/date/infinite_comparable.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/date_and_time/calculations.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/date_time.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/conversions.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/zones.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/hash/diff.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/infinite_comparable.rb35
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb74
-rw-r--r--activesupport/lib/active_support/core_ext/module/deprecation.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/numeric.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/object/inclusion.rb28
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_param.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/string/conversions.rb14
-rw-r--r--activesupport/lib/active_support/core_ext/string/filters.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/xchar.rb18
-rw-r--r--activesupport/lib/active_support/core_ext/time.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/time/infinite_comparable.rb5
-rw-r--r--activesupport/lib/active_support/deprecation/proxy_wrappers.rb6
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb2
-rw-r--r--activesupport/lib/active_support/i18n.rb8
-rw-r--r--activesupport/lib/active_support/key_generator.rb4
-rw-r--r--activesupport/lib/active_support/lazy_load_hooks.rb2
-rw-r--r--activesupport/lib/active_support/log_subscriber.rb55
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb18
-rw-r--r--activesupport/lib/active_support/multibyte/unicode.rb58
-rw-r--r--activesupport/lib/active_support/notifications.rb23
-rw-r--r--activesupport/lib/active_support/number_helper.rb5
-rw-r--r--activesupport/lib/active_support/per_thread_registry.rb52
-rw-r--r--activesupport/lib/active_support/subscriber.rb93
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb4
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb3
-rw-r--r--activesupport/lib/active_support/version.rb2
-rw-r--r--activesupport/test/caching_test.rb53
-rw-r--r--activesupport/test/callback_inheritance_test.rb2
-rw-r--r--activesupport/test/callbacks_test.rb196
-rw-r--r--activesupport/test/core_ext/class/attribute_accessor_test.rb10
-rw-r--r--activesupport/test/core_ext/class/attribute_test.rb9
-rw-r--r--activesupport/test/core_ext/date_and_time_behavior.rb5
-rw-r--r--activesupport/test/core_ext/date_ext_test.rb5
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb7
-rw-r--r--activesupport/test/core_ext/enumerable_test.rb6
-rw-r--r--activesupport/test/core_ext/module_test.rb36
-rw-r--r--activesupport/test/core_ext/numeric_ext_test.rb54
-rw-r--r--activesupport/test/core_ext/object/inclusion_test.rb10
-rw-r--r--activesupport/test/core_ext/range_ext_test.rb26
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb120
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb7
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb10
-rw-r--r--activesupport/test/dependencies_test.rb1
-rw-r--r--activesupport/test/json/decoding_test.rb4
-rw-r--r--activesupport/test/json/encoding_test.rb15
-rw-r--r--activesupport/test/message_encryptor_test.rb7
-rw-r--r--activesupport/test/message_verifier_test.rb7
-rw-r--r--activesupport/test/notifications_test.rb2
-rw-r--r--activesupport/test/ordered_hash_test.rb6
-rw-r--r--activesupport/test/test_case_test.rb3
-rw-r--r--activesupport/test/xml_mini/nokogirisax_engine_test.rb6
76 files changed, 993 insertions, 1027 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 31395d6da2..2e04b17e78 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,493 +1,20 @@
-## Rails 4.0.0 (unreleased) ##
+* Fix skipping object callbacks using metadata fetched via callback chain
+ inspection methods (`_*_callbacks`)
-* `ActiveSupport::Notifications::Instrumenter#instrument` should yield
- its payload.
+ *Sean Walbran*
- *stopdropandrew*
+* Add a `fetch_multi` method to the cache stores. The method provides
+ an easy to use API for fetching multiple values from the cache.
-* `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
-
- *stopdropandrew*
-
-* `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.
-
- Examples:
-
- number_to_human(123, units: {}) # => 123
- number_to_human(123, units: { thousand: 'k' }) # => 123
-
- *Michael Hoffman*
-
-* 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*
-
-* Standardize 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 }
- end
- Thread.new do
- XmlMini.with_backend("LibXML") { libxml_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
- 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 behavior 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/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 6bfac15289..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.
@@ -522,7 +563,7 @@ module ActiveSupport
def handle_expired_entry(entry, key, options)
if entry && entry.expired?
race_ttl = options[:race_condition_ttl].to_i
- if race_ttl && (Time.now - entry.expires_at <= race_ttl)
+ if race_ttl && (Time.now.to_f - entry.expires_at <= race_ttl)
# When an entry has :race_condition_ttl defined, put the stale entry back into the cache
# for a brief period while the entry is begin recalculated.
entry.expires_at = Time.now + race_ttl
@@ -562,38 +603,38 @@ module ActiveSupport
# +:compress+, +:compress_threshold+, and +:expires_in+.
def initialize(value, options = {})
if should_compress?(value, options)
- @v = compress(value)
- @c = true
+ @value = compress(value)
+ @compressed = true
else
- @v = value
- end
- if expires_in = options[:expires_in]
- @x = (Time.now + expires_in).to_i
+ @value = value
end
+ @created_at = Time.now.to_f
+ @expires_in = options[:expires_in]
+ @expires_in = @expires_in.to_f if @expires_in
end
def value
- convert_version_3_entry! if defined?(@value)
- compressed? ? uncompress(@v) : @v
+ convert_version_4beta1_entry! if defined?(@v)
+ compressed? ? uncompress(@value) : @value
end
# Check if the entry is expired. The +expires_in+ parameter can override
# the value set when the entry was created.
def expired?
- convert_version_3_entry! if defined?(@value)
- if defined?(@x)
- @x && @x < Time.now.to_i
- else
- false
- end
+ convert_version_4beta1_entry! if defined?(@value)
+ @expires_in && @created_at + @expires_in <= Time.now.to_f
end
def expires_at
- Time.at(@x) if defined?(@x)
+ @expires_in ? @created_at + @expires_in : nil
end
def expires_at=(value)
- @x = value.to_i
+ if value
+ @expires_in = value.to_f - @created_at
+ else
+ @expires_in = nil
+ end
end
# Returns the size of the cached value. This could be less than
@@ -606,9 +647,9 @@ module ActiveSupport
when NilClass
0
when String
- @v.bytesize
+ @value.bytesize
else
- @s = Marshal.dump(@v).bytesize
+ @s = Marshal.dump(@value).bytesize
end
end
end
@@ -616,12 +657,12 @@ module ActiveSupport
# Duplicate the value in a class. This is used by cache implementations that don't natively
# serialize entries to protect against accidental cache modifications.
def dup_value!
- convert_version_3_entry! if defined?(@value)
- if @v && !compressed? && !(@v.is_a?(Numeric) || @v == true || @v == false)
- if @v.is_a?(String)
- @v = @v.dup
+ convert_version_4beta1_entry! if defined?(@v)
+ if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false)
+ if @value.is_a?(String)
+ @value = @value.dup
else
- @v = Marshal.load(Marshal.dump(@v))
+ @value = Marshal.load(Marshal.dump(@value))
end
end
end
@@ -637,7 +678,7 @@ module ActiveSupport
end
def compressed?
- defined?(@c) ? @c : false
+ defined?(@compressed) ? @compressed : false
end
def compress(value)
@@ -650,19 +691,19 @@ module ActiveSupport
# The internals of this method changed between Rails 3.x and 4.0. This method provides the glue
# to ensure that cache entries created under the old version still work with the new class definition.
- def convert_version_3_entry!
- if defined?(@value)
- @v = @value
- remove_instance_variable(:@value)
+ def convert_version_4beta1_entry!
+ if defined?(@v)
+ @value = @v
+ remove_instance_variable(:@v)
end
- if defined?(@compressed)
- @c = @compressed
- remove_instance_variable(:@compressed)
+ if defined?(@c)
+ @compressed = @c
+ remove_instance_variable(:@c)
end
- if defined?(@expires_in) && defined?(@created_at) && @expires_in && @created_at
- @x = (@created_at + @expires_in).to_i
- remove_instance_variable(:@created_at)
- remove_instance_variable(:@expires_in)
+ if defined?(@x) && @x
+ @created_at ||= Time.now.to_f
+ @expires_in = @x - @created_at
+ remove_instance_variable(:@x)
end
end
end
diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb
index db5f228a70..fb42c4a41e 100644
--- a/activesupport/lib/active_support/cache/strategy/local_cache.rb
+++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb
@@ -8,6 +8,23 @@ module ActiveSupport
# duration of a block. Repeated calls to the cache for the same key will hit the
# in-memory cache for faster access.
module LocalCache
+ # Class for storing and registering the local caches.
+ class LocalCacheRegistry # :nodoc:
+ extend ActiveSupport::PerThreadRegistry
+
+ def initialize
+ @registry = {}
+ end
+
+ def cache_for(local_cache_key)
+ @registry[local_cache_key]
+ end
+
+ def set_cache_for(local_cache_key, value)
+ @registry[local_cache_key] = value
+ end
+ end
+
# Simple memory backed cache. This cache is not thread safe and is intended only
# for serving as a temporary memory cache for a single thread.
class LocalStore < Store
@@ -41,24 +58,18 @@ module ActiveSupport
# Use a local cache for the duration of block.
def with_local_cache
- save_val = Thread.current[thread_local_key]
- begin
- Thread.current[thread_local_key] = LocalStore.new
- yield
- ensure
- Thread.current[thread_local_key] = save_val
- end
+ use_temporary_local_cache(LocalStore.new) { yield }
end
#--
# This class wraps up local storage for middlewares. Only the middleware method should
# construct them.
class Middleware # :nodoc:
- attr_reader :name, :thread_local_key
+ attr_reader :name, :local_cache_key
- def initialize(name, thread_local_key)
+ def initialize(name, local_cache_key)
@name = name
- @thread_local_key = thread_local_key
+ @local_cache_key = local_cache_key
@app = nil
end
@@ -68,10 +79,10 @@ module ActiveSupport
end
def call(env)
- Thread.current[thread_local_key] = LocalStore.new
+ LocalCacheRegistry.set_cache_for(local_cache_key, LocalStore.new)
@app.call(env)
ensure
- Thread.current[thread_local_key] = nil
+ LocalCacheRegistry.set_cache_for(local_cache_key, nil)
end
end
@@ -80,7 +91,7 @@ module ActiveSupport
def middleware
@middleware ||= Middleware.new(
"ActiveSupport::Cache::Strategy::LocalCache",
- thread_local_key)
+ local_cache_key)
end
def clear(options = nil) # :nodoc:
@@ -95,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
@@ -146,21 +141,37 @@ module ActiveSupport
end
private
- def thread_local_key
- @thread_local_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, '_').to_sym
+ 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
def local_cache
- Thread.current[thread_local_key]
+ LocalCacheRegistry.cache_for(local_cache_key)
end
def bypass_local_cache
- save_cache = Thread.current[thread_local_key]
+ use_temporary_local_cache(nil) { yield }
+ end
+
+ def use_temporary_local_cache(temporary_cache)
+ save_cache = LocalCacheRegistry.cache_for(local_cache_key)
begin
- Thread.current[thread_local_key] = nil
+ LocalCacheRegistry.set_cache_for(local_cache_key, temporary_cache)
yield
ensure
- Thread.current[thread_local_key] = save_cache
+ LocalCacheRegistry.set_cache_for(local_cache_key, save_cache)
end
end
end
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 6c0cae71ed..6fe7e0f4fb 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
@@ -89,7 +91,26 @@ module ActiveSupport
class Callback #:nodoc:#
@@_callback_sequence = 0
- attr_accessor :chain, :filter, :kind, :options, :klass, :raw_filter
+ class Basic < Callback
+ end
+
+ class Object < Callback
+ def duplicates?(other)
+ false
+ end
+ end
+
+ def self.build(chain, filter, kind, options, _klass)
+ klass = case filter
+ when Array, Symbol, String
+ Callback::Basic
+ else
+ Callback::Object
+ end
+ klass.new chain, filter, kind, options, _klass
+ end
+
+ attr_accessor :chain, :kind, :options, :klass, :raw_filter
def initialize(chain, filter, kind, options, klass)
@chain, @kind, @klass = chain, kind, klass
@@ -97,10 +118,15 @@ module ActiveSupport
normalize_options!(options)
@raw_filter, @options = filter, options
- @filter = _compile_filter(filter)
+ @key = compute_identifier filter
+ @source = _compile_source(filter)
recompile_options!
end
+ def filter
+ @key
+ 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."
@@ -131,10 +157,12 @@ module ActiveSupport
end
def matches?(_kind, _filter)
- @kind == _kind && @filter == _filter
+ @kind == _kind && filter == _filter
end
def duplicates?(other)
+ return false unless self.class == other.class
+
matches?(other.kind, other.filter)
end
@@ -159,7 +187,7 @@ module ActiveSupport
# 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}
+ result = result = #{@source}
halted = (#{chain.config[:terminator]})
if halted
halted_callback_hook(#{@raw_filter.inspect.inspect})
@@ -171,7 +199,7 @@ module ActiveSupport
<<-RUBY_EVAL
#{code}
if #{!chain.config[:skip_after_callbacks_if_terminated] || "!halted"} && #{@compiled_options}
- #{@filter}
+ #{@source}
end
RUBY_EVAL
when :around
@@ -187,6 +215,15 @@ module ActiveSupport
private
+ def compute_identifier(filter)
+ case filter
+ when String, ::Proc
+ filter.object_id
+ else
+ filter
+ end
+ end
+
# Compile around filters with conditions into proxy methods
# that contain the conditions.
#
@@ -206,7 +243,7 @@ module ActiveSupport
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}(halted)
if #{@compiled_options} && !halted
- #{@filter} do
+ #{@source} do
yield self
end
else
@@ -224,16 +261,26 @@ module ActiveSupport
conditions = ["true"]
unless options[:if].empty?
- conditions << Array(_compile_filter(options[:if]))
+ conditions << Array(_compile_source(options[:if]))
end
unless options[:unless].empty?
- conditions << Array(_compile_filter(options[:unless])).map {|f| "!#{f}"}
+ conditions << Array(_compile_source(options[:unless])).map {|f| "!#{f}"}
end
@compiled_options = conditions.flatten.join(" && ")
end
+ def _method_name_for_object_filter(kind, filter, append_next_id = true)
+ class_name = filter.kind_of?(Class) ? filter.to_s : filter.class.to_s
+ class_name.gsub!(/<|>|#/, '')
+ class_name.gsub!(/\/|:/, "_")
+
+ method_name = "_callback_#{kind}_#{class_name}"
+ method_name << "_#{next_id}" if append_next_id
+ method_name
+ end
+
# Filters support:
#
# Arrays:: Used in conditions. This is used to specify
@@ -254,27 +301,27 @@ module ActiveSupport
# Objects::
# a method is created that calls the before_foo method
# on the object.
- def _compile_filter(filter)
+ def _compile_source(filter)
case filter
when Array
- filter.map {|f| _compile_filter(f)}
+ filter.map {|f| _compile_source(f)}
when Symbol
filter
when String
"(#{filter})"
- when Proc
+ when ::Proc
method_name = "_callback_#{@kind}_#{next_id}"
@klass.send(:define_method, method_name, &filter)
return method_name if filter.arity <= 0
- method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ")
+ method_name << (filter.arity == 1 ? "(self)" : "(self, ::Proc.new)")
else
- method_name = "_callback_#{@kind}_#{next_id}"
+ method_name = _method_name_for_object_filter(kind, filter)
@klass.send(:define_method, "#{method_name}_object") { filter }
_normalize_legacy_filter(kind, filter)
scopes = Array(chain.config[:scope])
- method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_")
+ method_to_call = scopes.map{ |s| public_send(s) }.join("_")
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{method_name}(&blk)
@@ -319,10 +366,7 @@ module ActiveSupport
end
def compile
- method = []
- method << "value = nil"
- method << "halted = false"
-
+ method = ["value = nil", "halted = false"]
callbacks = "value = !halted && (!block_given? || yield)"
reverse_each do |callback|
callbacks = callback.apply(callbacks)
@@ -396,7 +440,7 @@ module ActiveSupport
# 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
+ type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
options = filters.last.is_a?(Hash) ? filters.pop : {}
filters.unshift(block) if block
@@ -447,7 +491,7 @@ module ActiveSupport
__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)
+ Callback.build(chain, filter, type, options.dup, self)
end
options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index 430a35fbaf..3807ee63b1 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -12,7 +12,7 @@ class Array
# pass an option key that doesn't exist in the list below, it will raise an
# <tt>ArgumentError</tt>.
#
- # Options:
+ # ==== Options
#
# * <tt>:words_connector</tt> - The sign or word used to join the elements
# in arrays with two or more elements (default: ", ").
@@ -24,6 +24,8 @@ class Array
# the connector options defined on the 'support.array' namespace in the
# corresponding dictionary file.
#
+ # ==== Examples
+ #
# [].to_sentence # => ""
# ['one'].to_sentence # => "one"
# ['one', 'two'].to_sentence # => "one and two"
@@ -38,10 +40,10 @@ class Array
# ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ')
# # => "one or two or at least three"
#
- # Examples using <tt>:locale</tt> option:
+ # Using <tt>:locale</tt> option:
#
# # Given this locale dictionary:
- # #
+ # #
# # es:
# # support:
# # array:
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/uniq_by.rb b/activesupport/lib/active_support/core_ext/array/uniq_by.rb
index ca3b7748cd..23573c97de 100644
--- a/activesupport/lib/active_support/core_ext/array/uniq_by.rb
+++ b/activesupport/lib/active_support/core_ext/array/uniq_by.rb
@@ -1,5 +1,5 @@
class Array
- # *DEPRECATED*: Use +Array#uniq+ instead.
+ # *DEPRECATED*: Use <tt>Array#uniq</tt> instead.
#
# Returns a unique array based on the criteria in the block.
#
@@ -9,7 +9,7 @@ class Array
uniq(&block)
end
- # *DEPRECATED*: Use +Array#uniq!+ instead.
+ # *DEPRECATED*: Use <tt>Array#uniq!</tt> instead.
#
# Same as +uniq_by+, but modifies +self+.
def uniq_by!(&block)
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/benchmark.rb b/activesupport/lib/active_support/core_ext/benchmark.rb
index 2d110155a5..eb25b2bc44 100644
--- a/activesupport/lib/active_support/core_ext/benchmark.rb
+++ b/activesupport/lib/active_support/core_ext/benchmark.rb
@@ -1,6 +1,13 @@
require 'benchmark'
class << Benchmark
+ # Benchmark realtime in milliseconds.
+ #
+ # Benchmark.realtime { User.all }
+ # # => 8.0e-05
+ #
+ # Benchmark.ms { User.all }
+ # # => 0.074
def ms
1000 * realtime { yield }
end
diff --git a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
index 5dc5710c53..39b8cea807 100644
--- a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
@@ -1,4 +1,5 @@
require 'bigdecimal'
+require 'bigdecimal/util'
require 'yaml'
class BigDecimal
diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb
index e51ab9ddbc..6d49b7b6e1 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute.rb
@@ -44,7 +44,8 @@ class Class
# Base.setting # => []
# Subclass.setting # => [:foo]
#
- # For convenience, a query method is defined as well:
+ # For convenience, an instance predicate method is defined as well.
+ # To skip it, pass <tt>instance_predicate: false</tt>.
#
# Subclass.setting? # => false
#
@@ -72,10 +73,11 @@ class Class
# double assignment is used to avoid "assigned but unused variable" warning
instance_reader = instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
+ instance_predicate = options.fetch(:instance_predicate, true)
attrs.each do |name|
define_singleton_method(name) { nil }
- define_singleton_method("#{name}?") { !!public_send(name) }
+ define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
ivar = "@#{name}"
@@ -109,7 +111,7 @@ class Class
self.class.public_send name
end
end
- define_method("#{name}?") { !!public_send(name) }
+ define_method("#{name}?") { !!public_send(name) } if instance_predicate
end
attr_writer name if instance_writer
diff --git a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
index fa1dbfdf06..34859617c9 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
@@ -32,7 +32,7 @@ class Class
def cattr_reader(*syms)
options = syms.extract_options!
syms.each do |sym|
- raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
+ raise NameError.new("invalid class attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
unless defined? @@#{sym}
@@#{sym} = nil
@@ -93,7 +93,7 @@ class Class
def cattr_writer(*syms)
options = syms.extract_options!
syms.each do |sym|
- raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
+ raise NameError.new("invalid class attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
unless defined? @@#{sym}
@@#{sym} = 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 1f78b9eb5a..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
@@ -93,7 +93,7 @@ module DateAndTime
# Returns a new date/time at the end of the quarter.
# Example: 31st March, 30th June, 30th September.
- # DateTIme objects will have a time set to 23:59:59.
+ # DateTime objects will have a time set to 23:59:59.
def end_of_quarter
last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month }
beginning_of_month.change(:month => last_quarter_month).end_of_month
@@ -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/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/module/deprecation.rb b/activesupport/lib/active_support/core_ext/module/deprecation.rb
index cc45cee5b8..d873de197f 100644
--- a/activesupport/lib/active_support/core_ext/module/deprecation.rb
+++ b/activesupport/lib/active_support/core_ext/module/deprecation.rb
@@ -14,8 +14,8 @@ class Module
# method where you can implement your custom warning behavior.
#
# class MyLib::Deprecator
- # def deprecation_warning(deprecated_method_name, message, caller_backtrace)
- # message = "#{method_name} is deprecated and will be removed from MyLibrary | #{message}"
+ # def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil)
+ # message = "#{deprecated_method_name} is deprecated and will be removed from MyLibrary | #{message}"
# Kernel.warn message
# end
# end
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/string.rb b/activesupport/lib/active_support/core_ext/string.rb
index 5d7cb81e38..c656db2c6c 100644
--- a/activesupport/lib/active_support/core_ext/string.rb
+++ b/activesupport/lib/active_support/core_ext/string.rb
@@ -4,7 +4,6 @@ require 'active_support/core_ext/string/multibyte'
require 'active_support/core_ext/string/starts_ends_with'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/string/access'
-require 'active_support/core_ext/string/xchar'
require 'active_support/core_ext/string/behavior'
require 'active_support/core_ext/string/output_safety'
require 'active_support/core_ext/string/exclude'
diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb
index 428fa1f826..d2a2db32bb 100644
--- a/activesupport/lib/active_support/core_ext/string/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/string/conversions.rb
@@ -20,19 +20,17 @@ class String
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/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb
index a1b3f79748..c62bb41416 100644
--- a/activesupport/lib/active_support/core_ext/string/filters.rb
+++ b/activesupport/lib/active_support/core_ext/string/filters.rb
@@ -50,6 +50,6 @@ class String
length_with_room_for_omission
end
- self[0...stop] + options[:omission]
+ "#{self[0...stop]}#{options[:omission]}"
end
end
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index 6522145572..0b506a6030 100644
--- a/activesupport/lib/active_support/core_ext/string/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/string/inflections.rb
@@ -41,7 +41,7 @@ class String
#
# If the optional parameter +locale+ is specified,
# the word will be singularized as a word of that language.
- # By default, this paramter is set to <tt>:en</tt>.
+ # By default, this parameter is set to <tt>:en</tt>.
# You must define your own inflection rules for languages other than English.
#
# 'posts'.singularize # => "post"
diff --git a/activesupport/lib/active_support/core_ext/string/xchar.rb b/activesupport/lib/active_support/core_ext/string/xchar.rb
deleted file mode 100644
index f9a5b4fb64..0000000000
--- a/activesupport/lib/active_support/core_ext/string/xchar.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-begin
- # See http://fast-xs.rubyforge.org/ by Eric Wong.
- # Also included with hpricot.
- require 'fast_xs'
-rescue LoadError
- # fast_xs extension unavailable
-else
- begin
- require 'builder'
- rescue LoadError
- # builder demands the first shot at defining String#to_xs
- end
-
- class String
- alias_method :original_xs, :to_xs if method_defined?(:to_xs)
- alias_method :to_xs, :fast_xs
- 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 837db05dcc..1b20592e4c 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/key_generator.rb b/activesupport/lib/active_support/key_generator.rb
index 71654dbb87..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 = {})
@@ -39,7 +39,7 @@ module ActiveSupport
end
end
- class DummyKeyGenerator # :nodoc:
+ class LegacyKeyGenerator # :nodoc:
SECRET_MIN_LENGTH = 30 # Characters
def initialize(secret)
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/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb
index 21a04a9152..e95dc5a866 100644
--- a/activesupport/lib/active_support/log_subscriber.rb
+++ b/activesupport/lib/active_support/log_subscriber.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/class/attribute'
+require 'active_support/subscriber'
module ActiveSupport
# ActiveSupport::LogSubscriber is an object set to consume
@@ -33,7 +34,7 @@ module ActiveSupport
# Log subscriber also has some helpers to deal with logging and automatically
# flushes all logs when the request finishes (via action_dispatch.callback
# notification) in a Rails environment.
- class LogSubscriber
+ class LogSubscriber < Subscriber
# Embed in a String to clear all previous ANSI sequences.
CLEAR = "\e[0m"
BOLD = "\e[1m"
@@ -53,26 +54,15 @@ module ActiveSupport
class << self
def logger
- if defined?(Rails) && Rails.respond_to?(:logger)
- @logger ||= Rails.logger
+ @logger ||= if defined?(Rails) && Rails.respond_to?(:logger)
+ Rails.logger
end
- @logger
end
attr_writer :logger
- def attach_to(namespace, log_subscriber=new, notifier=ActiveSupport::Notifications)
- log_subscribers << log_subscriber
-
- log_subscriber.public_methods(false).each do |event|
- next if %w{ start finish }.include?(event.to_s)
-
- notifier.subscribe("#{event}.#{namespace}", log_subscriber)
- end
- end
-
def log_subscribers
- @@log_subscribers ||= []
+ subscribers
end
# Flush all log_subscribers' logger.
@@ -81,39 +71,18 @@ module ActiveSupport
end
end
- def initialize
- @queue_key = [self.class.name, object_id].join "-"
- super
- end
-
def logger
LogSubscriber.logger
end
def start(name, id, payload)
- return unless logger
-
- e = ActiveSupport::Notifications::Event.new(name, Time.now, nil, id, payload)
- parent = event_stack.last
- parent << e if parent
-
- event_stack.push e
+ super if logger
end
def finish(name, id, payload)
- return unless logger
-
- finished = Time.now
- event = event_stack.pop
- event.end = finished
- event.payload.merge!(payload)
-
- method = name.split('.').first
- begin
- send(method, event)
- rescue Exception => e
- logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
- end
+ super if logger
+ rescue Exception => e
+ logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
end
protected
@@ -136,11 +105,5 @@ module ActiveSupport
bold = bold ? BOLD : ""
"#{bold}#{color}#{text}#{CLEAR}"
end
-
- private
-
- def event_stack
- Thread.current[@queue_key] ||= []
- end
end
end
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/notifications.rb b/activesupport/lib/active_support/notifications.rb
index 705a4693b7..c45358bba9 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -1,5 +1,6 @@
require 'active_support/notifications/instrumenter'
require 'active_support/notifications/fanout'
+require 'active_support/per_thread_registry'
module ActiveSupport
# = Notifications
@@ -177,7 +178,27 @@ module ActiveSupport
end
def instrumenter
- Thread.current[:"instrumentation_#{notifier.object_id}"] ||= Instrumenter.new(notifier)
+ InstrumentationRegistry.instrumenter_for(notifier)
+ end
+ end
+
+ # This class is a registry which holds all of the +Instrumenter+ objects
+ # in a particular thread local. To access the +Instrumenter+ object for a
+ # particular +notifier+, you can call the following method:
+ #
+ # InstrumentationRegistry.instrumenter_for(notifier)
+ #
+ # The instrumenters for multiple notifiers are held in a single instance of
+ # this class.
+ class InstrumentationRegistry # :nodoc:
+ extend ActiveSupport::PerThreadRegistry
+
+ def initialize
+ @registry = {}
+ end
+
+ def instrumenter_for(notifier)
+ @registry[notifier] ||= Instrumenter.new(notifier)
end
end
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/per_thread_registry.rb b/activesupport/lib/active_support/per_thread_registry.rb
new file mode 100644
index 0000000000..aa682fb36c
--- /dev/null
+++ b/activesupport/lib/active_support/per_thread_registry.rb
@@ -0,0 +1,52 @@
+module ActiveSupport
+ # This module is used to encapsulate access to thread local variables.
+ #
+ # Instead of polluting the thread locals namespace:
+ #
+ # Thread.current[:connection_handler]
+ #
+ # you define a class that extends this module:
+ #
+ # module ActiveRecord
+ # class RuntimeRegistry
+ # extend ActiveSupport::PerThreadRegistry
+ #
+ # attr_accessor :connection_handler
+ # end
+ # end
+ #
+ # and invoke the declared instance accessors as class methods. So
+ #
+ # ActiveRecord::RuntimeRegistry.connection_handler = connection_handler
+ #
+ # sets a connection handler local to the current thread, and
+ #
+ # ActiveRecord::RuntimeRegistry.connection_handler
+ #
+ # returns a connection handler local to the current thread.
+ #
+ # This feature is accomplished by instantiating the class and storing the
+ # instance as a thread local keyed by the class name. In the example above
+ # a key "ActiveRecord::RuntimeRegistry" is stored in <tt>Thread.current</tt>.
+ # The class methods proxy to said thread local instance.
+ #
+ # If the class has an initializer, it must accept no arguments.
+ module PerThreadRegistry
+ protected
+
+ def method_missing(name, *args, &block) # :nodoc:
+ # Caches the method definition as a singleton method of the receiver.
+ define_singleton_method(name) do |*a, &b|
+ per_thread_registry_instance.public_send(name, *a, &b)
+ end
+
+ send(name, *args, &block)
+ end
+
+ private
+
+ def per_thread_registry_instance
+ Thread.current[name] ||= new
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/subscriber.rb b/activesupport/lib/active_support/subscriber.rb
new file mode 100644
index 0000000000..34c6f900c1
--- /dev/null
+++ b/activesupport/lib/active_support/subscriber.rb
@@ -0,0 +1,93 @@
+require 'active_support/per_thread_registry'
+
+module ActiveSupport
+ # ActiveSupport::Subscriber is an object set to consume
+ # ActiveSupport::Notifications. The subscriber dispatches notifications to
+ # a registered object based on its given namespace.
+ #
+ # An example would be Active Record subscriber responsible for collecting
+ # statistics about queries:
+ #
+ # module ActiveRecord
+ # class StatsSubscriber < ActiveSupport::Subscriber
+ # def sql(event)
+ # Statsd.timing("sql.#{event.payload[:name]}", event.duration)
+ # end
+ # end
+ # end
+ #
+ # And it's finally registered as:
+ #
+ # ActiveRecord::StatsSubscriber.attach_to :active_record
+ #
+ # Since we need to know all instance methods before attaching the log
+ # subscriber, the line above should be called after your subscriber definition.
+ #
+ # After configured, whenever a "sql.active_record" notification is published,
+ # it will properly dispatch the event (ActiveSupport::Notifications::Event) to
+ # the +sql+ method.
+ class Subscriber
+ class << self
+
+ # Attach the subscriber to a namespace.
+ def attach_to(namespace, subscriber=new, notifier=ActiveSupport::Notifications)
+ subscribers << subscriber
+
+ subscriber.public_methods(false).each do |event|
+ next if %w{ start finish }.include?(event.to_s)
+
+ notifier.subscribe("#{event}.#{namespace}", subscriber)
+ end
+ end
+
+ def subscribers
+ @@subscribers ||= []
+ end
+ end
+
+ def initialize
+ @queue_key = [self.class.name, object_id].join "-"
+ super
+ end
+
+ def start(name, id, payload)
+ e = ActiveSupport::Notifications::Event.new(name, Time.now, nil, id, payload)
+ parent = event_stack.last
+ parent << e if parent
+
+ event_stack.push e
+ end
+
+ def finish(name, id, payload)
+ finished = Time.now
+ event = event_stack.pop
+ event.end = finished
+ event.payload.merge!(payload)
+
+ method = name.split('.').first
+ send(method, event)
+ end
+
+ private
+
+ 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/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index 21a0620c22..3cf82a24b9 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -5,7 +5,7 @@ module ActiveSupport
# The TimeZone class serves as a wrapper around TZInfo::Timezone instances.
# It allows us to do the following:
#
- # * Limit the set of zones provided by TZInfo to a meaningful subset of 142
+ # * Limit the set of zones provided by TZInfo to a meaningful subset of 146
# zones.
# * Retrieve and display zones with a friendlier name
# (e.g., "Eastern Time (US & Canada)" instead of "America/New_York").
@@ -177,6 +177,7 @@ module ActiveSupport
"Wellington" => "Pacific/Auckland",
"Nuku'alofa" => "Pacific/Tongatapu",
"Tokelau Is." => "Pacific/Fakaofo",
+ "Chatham Is." => "Pacific/Chatham",
"Samoa" => "Pacific/Apia"
}
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 acd320dbe0..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')
@@ -405,7 +425,7 @@ module CacheStoreBehavior
end
# https://rails.lighthouseapp.com/projects/8994/tickets/6225-memcachestore-cant-deal-with-umlauts-and-special-characters
-# The error is caused by charcter encodings that can't be compared with ASCII-8BIT regular expressions and by special
+# The error is caused by character encodings that can't be compared with ASCII-8BIT regular expressions and by special
# characters like the umlaut in UTF-8.
module EncodedKeyCacheBehavior
Encoding.list.each do |encoding|
@@ -943,29 +963,28 @@ class CacheEntryTest < ActiveSupport::TestCase
assert_equal value.bytesize, entry.size
end
- def test_restoring_version_3_entries
- version_3_entry = ActiveSupport::Cache::Entry.allocate
- version_3_entry.instance_variable_set(:@value, "hello")
- version_3_entry.instance_variable_set(:@created_at, Time.now - 60)
- entry = Marshal.load(Marshal.dump(version_3_entry))
+ def test_restoring_version_4beta1_entries
+ version_4beta1_entry = ActiveSupport::Cache::Entry.allocate
+ version_4beta1_entry.instance_variable_set(:@v, "hello")
+ version_4beta1_entry.instance_variable_set(:@x, Time.now.to_i + 60)
+ entry = Marshal.load(Marshal.dump(version_4beta1_entry))
assert_equal "hello", entry.value
assert_equal false, entry.expired?
end
- def test_restoring_compressed_version_3_entries
- version_3_entry = ActiveSupport::Cache::Entry.allocate
- version_3_entry.instance_variable_set(:@value, Zlib::Deflate.deflate(Marshal.dump("hello")))
- version_3_entry.instance_variable_set(:@compressed, true)
- entry = Marshal.load(Marshal.dump(version_3_entry))
+ def test_restoring_compressed_version_4beta1_entries
+ version_4beta1_entry = ActiveSupport::Cache::Entry.allocate
+ version_4beta1_entry.instance_variable_set(:@v, Zlib::Deflate.deflate(Marshal.dump("hello")))
+ version_4beta1_entry.instance_variable_set(:@c, true)
+ entry = Marshal.load(Marshal.dump(version_4beta1_entry))
assert_equal "hello", entry.value
end
- def test_restoring_expired_version_3_entries
- version_3_entry = ActiveSupport::Cache::Entry.allocate
- version_3_entry.instance_variable_set(:@value, "hello")
- version_3_entry.instance_variable_set(:@created_at, Time.now - 60)
- version_3_entry.instance_variable_set(:@expires_in, 58.9)
- entry = Marshal.load(Marshal.dump(version_3_entry))
+ def test_restoring_expired_version_4beta1_entries
+ version_4beta1_entry = ActiveSupport::Cache::Entry.allocate
+ version_4beta1_entry.instance_variable_set(:@v, "hello")
+ version_4beta1_entry.instance_variable_set(:@x, Time.now.to_i - 1)
+ entry = Marshal.load(Marshal.dump(version_4beta1_entry))
assert_equal "hello", entry.value
assert_equal true, entry.expired?
end
diff --git a/activesupport/test/callback_inheritance_test.rb b/activesupport/test/callback_inheritance_test.rb
index 6be8ea8b84..1adfe4edf4 100644
--- a/activesupport/test/callback_inheritance_test.rb
+++ b/activesupport/test/callback_inheritance_test.rb
@@ -109,7 +109,6 @@ class BasicCallbacksTest < ActiveSupport::TestCase
@index = GrandParent.new("index").dispatch
@update = GrandParent.new("update").dispatch
@delete = GrandParent.new("delete").dispatch
- @unknown = GrandParent.new("unknown").dispatch
end
def test_basic_conditional_callback1
@@ -130,7 +129,6 @@ class InheritedCallbacksTest < ActiveSupport::TestCase
@index = Parent.new("index").dispatch
@update = Parent.new("update").dispatch
@delete = Parent.new("delete").dispatch
- @unknown = Parent.new("unknown").dispatch
end
def test_inherited_excluded
diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb
index 13f2e3cdaf..9659f141cb 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],
@@ -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,159 @@ module CallbacksTest
assert_equal ["two", "one", "three", "yielded"], model.record
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 foo; end
+ def run; run_callbacks :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 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/class/attribute_accessor_test.rb b/activesupport/test/core_ext/class/attribute_accessor_test.rb
index 8d827f054e..0d5f39a72b 100644
--- a/activesupport/test/core_ext/class/attribute_accessor_test.rb
+++ b/activesupport/test/core_ext/class/attribute_accessor_test.rb
@@ -44,16 +44,18 @@ class ClassAttributeAccessorTest < ActiveSupport::TestCase
end
def test_should_raise_name_error_if_attribute_name_is_invalid
- assert_raises NameError do
+ exception = assert_raises NameError do
Class.new do
- cattr_reader "invalid attribute name"
+ cattr_reader "1nvalid"
end
end
+ assert_equal "invalid class attribute name: 1nvalid", exception.message
- assert_raises NameError do
+ exception = assert_raises NameError do
Class.new do
- cattr_writer "invalid attribute name"
+ cattr_writer "1nvalid"
end
end
+ assert_equal "invalid class attribute name: 1nvalid", exception.message
end
end
diff --git a/activesupport/test/core_ext/class/attribute_test.rb b/activesupport/test/core_ext/class/attribute_test.rb
index 1c3ba8a7a0..e7a1334db3 100644
--- a/activesupport/test/core_ext/class/attribute_test.rb
+++ b/activesupport/test/core_ext/class/attribute_test.rb
@@ -27,7 +27,7 @@ class ClassAttributeTest < ActiveSupport::TestCase
assert_equal 1, Class.new(@sub).setting
end
- test 'query method' do
+ test 'predicate method' do
assert_equal false, @klass.setting?
@klass.setting = 1
assert_equal true, @klass.setting?
@@ -48,7 +48,7 @@ class ClassAttributeTest < ActiveSupport::TestCase
assert_equal 1, object.setting
end
- test 'instance query' do
+ test 'instance predicate' do
object = @klass.new
assert_equal false, object.setting?
object.setting = 1
@@ -73,6 +73,11 @@ class ClassAttributeTest < ActiveSupport::TestCase
assert_raise(NoMethodError) { object.setting = 'boom' }
end
+ test 'disabling instance predicate' do
+ object = Class.new { class_attribute :setting, instance_predicate: false }.new
+ assert_raise(NoMethodError) { object.setting? }
+ end
+
test 'works well with singleton classes' do
object = @klass.new
object.singleton_class.setting = 'foo'
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/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb
index 0a1abac767..6781e3c20e 100644
--- a/activesupport/test/core_ext/enumerable_test.rb
+++ b/activesupport/test/core_ext/enumerable_test.rb
@@ -24,10 +24,8 @@ class EnumerableTests < ActiveSupport::TestCase
def test_group_by
names = %w(marcel sam david jeremy)
klass = Struct.new(:name)
- objects = (1..50).inject([]) do |people,|
- p = klass.new
- p.name = names.sort_by { rand }.first
- people << p
+ objects = (1..50).map do
+ klass.new names.sample
end
enum = GenericEnumerable.new(objects)
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..a2d1d0dc39 100644
--- a/activesupport/test/json/decoding_test.rb
+++ b/activesupport/test/json/decoding_test.rb
@@ -62,21 +62,25 @@ class TestJSONDecoding < ActiveSupport::TestCase
backends.each do |backend|
TESTS.each do |json, expected|
test "json decodes #{json} with the #{backend} backend" do
+ prev = ActiveSupport.parse_json_times
ActiveSupport.parse_json_times = true
silence_warnings do
ActiveSupport::JSON.with_backend backend do
assert_equal expected, ActiveSupport::JSON.decode(json)
end
end
+ ActiveSupport.parse_json_times = prev
end
end
test "json decodes time json with time parsing disabled with the #{backend} backend" do
+ prev = ActiveSupport.parse_json_times
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
+ ActiveSupport.parse_json_times = prev
end
end
diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index 12ce250eb3..8686dcf929 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -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/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb
index 6aea9d56f1..c3fe89de4b 100644
--- a/activesupport/test/ordered_hash_test.rb
+++ b/activesupport/test/ordered_hash_test.rb
@@ -244,11 +244,7 @@ class OrderedHashTest < ActiveSupport::TestCase
end
def test_each_after_yaml_serialization
- values = []
- @deserialized_ordered_hash = YAML.load(YAML.dump(@ordered_hash))
-
- @deserialized_ordered_hash.each {|key, value| values << value}
- assert_equal @values, values
+ assert_equal @values, YAML.load(YAML.dump(@ordered_hash)).values
end
def test_each_when_yielding_to_block_with_splat
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