aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/CHANGELOG.md447
-rw-r--r--activesupport/MIT-LICENSE2
-rw-r--r--activesupport/README.rdoc2
-rw-r--r--activesupport/Rakefile14
-rw-r--r--activesupport/activesupport.gemspec8
-rwxr-xr-xactivesupport/bin/generate_tables2
-rwxr-xr-xactivesupport/bin/test4
-rw-r--r--activesupport/lib/active_support.rb13
-rw-r--r--activesupport/lib/active_support/array_inquirer.rb44
-rw-r--r--activesupport/lib/active_support/backtrace_cleaner.rb2
-rw-r--r--activesupport/lib/active_support/cache.rb92
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb1
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb26
-rw-r--r--activesupport/lib/active_support/cache/memory_store.rb2
-rw-r--r--activesupport/lib/active_support/cache/strategy/local_cache.rb4
-rw-r--r--activesupport/lib/active_support/callbacks.rb300
-rw-r--r--activesupport/lib/active_support/concern.rb4
-rw-r--r--activesupport/lib/active_support/concurrency/latch.rb24
-rw-r--r--activesupport/lib/active_support/concurrency/share_lock.rb142
-rw-r--r--activesupport/lib/active_support/configurable.rb1
-rw-r--r--activesupport/lib/active_support/core_ext.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/array.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/array/access.rb18
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/array/inquiry.rb17
-rw-r--r--activesupport/lib/active_support/core_ext/array/wrap.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/big_decimal/conversions.rb13
-rw-r--r--activesupport/lib/active_support/core_ext/big_decimal/yaml_conversions.rb14
-rw-r--r--activesupport/lib/active_support/core_ext/class.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/class/delegating_attributes.rb45
-rw-r--r--activesupport/lib/active_support/core_ext/date/conversions.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/date_and_time/calculations.rb125
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb26
-rw-r--r--activesupport/lib/active_support/core_ext/file/atomic.rb55
-rw-r--r--activesupport/lib/active_support/core_ext/hash/compact.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb20
-rw-r--r--activesupport/lib/active_support/core_ext/hash/deep_merge.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/except.rb13
-rw-r--r--activesupport/lib/active_support/core_ext/hash/keys.rb22
-rw-r--r--activesupport/lib/active_support/core_ext/hash/slice.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/integer/time.rb15
-rw-r--r--activesupport/lib/active_support/core_ext/kernel.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/debugger.rb13
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/reporting.rb83
-rw-r--r--activesupport/lib/active_support/core_ext/load_error.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/marshal.rb14
-rw-r--r--activesupport/lib/active_support/core_ext/module/aliasing.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/module/anonymous.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/module/attr_internal.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/module/concerning.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb40
-rw-r--r--activesupport/lib/active_support/core_ext/module/method_transplanting.rb14
-rw-r--r--activesupport/lib/active_support/core_ext/module/remove_method.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/name_error.rb17
-rw-r--r--activesupport/lib/active_support/core_ext/numeric.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/bytes.rb20
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/conversions.rb28
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/inquiry.rb26
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/time.rb35
-rw-r--r--activesupport/lib/active_support/core_ext/object.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/object/deep_dup.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/object/duplicable.rb19
-rw-r--r--activesupport/lib/active_support/core_ext/object/instance_variables.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/itself.rb15
-rw-r--r--activesupport/lib/active_support/core_ext/object/json.rb16
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_query.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb115
-rw-r--r--activesupport/lib/active_support/core_ext/object/with_options.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/range/conversions.rb19
-rw-r--r--activesupport/lib/active_support/core_ext/range/each.rb34
-rw-r--r--activesupport/lib/active_support/core_ext/range/include_range.rb40
-rw-r--r--activesupport/lib/active_support/core_ext/securerandom.rb23
-rw-r--r--activesupport/lib/active_support/core_ext/string/behavior.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/filters.rb31
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/string/multibyte.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb26
-rw-r--r--activesupport/lib/active_support/core_ext/string/strip.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/struct.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/thread.rb86
-rw-r--r--activesupport/lib/active_support/core_ext/time.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb28
-rw-r--r--activesupport/lib/active_support/core_ext/time/marshal.rb31
-rw-r--r--activesupport/lib/active_support/core_ext/time/zones.rb23
-rw-r--r--activesupport/lib/active_support/core_ext/uri.rb2
-rw-r--r--activesupport/lib/active_support/dependencies.rb152
-rw-r--r--activesupport/lib/active_support/dependencies/autoload.rb2
-rw-r--r--activesupport/lib/active_support/dependencies/interlock.rb47
-rw-r--r--activesupport/lib/active_support/deprecation/behaviors.rb14
-rw-r--r--activesupport/lib/active_support/deprecation/method_wrappers.rb8
-rw-r--r--activesupport/lib/active_support/deprecation/proxy_wrappers.rb71
-rw-r--r--activesupport/lib/active_support/deprecation/reporting.rb15
-rw-r--r--activesupport/lib/active_support/duration.rb42
-rw-r--r--activesupport/lib/active_support/gem_version.rb6
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb20
-rw-r--r--activesupport/lib/active_support/i18n_railtie.rb29
-rw-r--r--activesupport/lib/active_support/inflector/inflections.rb39
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb176
-rw-r--r--activesupport/lib/active_support/inflector/transliterate.rb32
-rw-r--r--activesupport/lib/active_support/json/decoding.rb10
-rw-r--r--activesupport/lib/active_support/json/encoding.rb53
-rw-r--r--activesupport/lib/active_support/log_subscriber/test_helper.rb5
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb2
-rw-r--r--activesupport/lib/active_support/message_verifier.rb88
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb8
-rw-r--r--activesupport/lib/active_support/multibyte/unicode.rb11
-rw-r--r--activesupport/lib/active_support/notifications.rb2
-rw-r--r--activesupport/lib/active_support/notifications/fanout.rb2
-rw-r--r--activesupport/lib/active_support/notifications/instrumenter.rb12
-rw-r--r--activesupport/lib/active_support/number_helper.rb42
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_currency_converter.rb8
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb9
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_human_converter.rb4
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb6
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb2
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb53
-rw-r--r--activesupport/lib/active_support/ordered_options.rb16
-rw-r--r--activesupport/lib/active_support/rails.rb4
-rw-r--r--activesupport/lib/active_support/railtie.rb5
-rw-r--r--activesupport/lib/active_support/rescuable.rb8
-rw-r--r--activesupport/lib/active_support/security_utils.rb20
-rw-r--r--activesupport/lib/active_support/string_inquirer.rb2
-rw-r--r--activesupport/lib/active_support/subscriber.rb15
-rw-r--r--activesupport/lib/active_support/tagged_logging.rb4
-rw-r--r--activesupport/lib/active_support/test_case.rb49
-rw-r--r--activesupport/lib/active_support/testing/assertions.rb22
-rw-r--r--activesupport/lib/active_support/testing/autorun.rb9
-rw-r--r--activesupport/lib/active_support/testing/composite_filter.rb54
-rw-r--r--activesupport/lib/active_support/testing/file_fixtures.rb34
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb12
-rw-r--r--activesupport/lib/active_support/testing/method_call_assertions.rb41
-rw-r--r--activesupport/lib/active_support/testing/stream.rb42
-rw-r--r--activesupport/lib/active_support/testing/time_helpers.rb23
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb120
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb96
-rw-r--r--activesupport/lib/active_support/values/unicode_tables.datbin904640 -> 1001806 bytes
-rw-r--r--activesupport/lib/active_support/xml_mini.rb3
-rw-r--r--activesupport/lib/active_support/xml_mini/jdom.rb13
-rw-r--r--activesupport/lib/active_support/xml_mini/libxml.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogiri.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/rexml.rb11
-rw-r--r--activesupport/test/abstract_unit.rb10
-rw-r--r--activesupport/test/array_inquirer_test.rb41
-rw-r--r--activesupport/test/autoloading_fixtures/a/c/e/f.rb2
-rw-r--r--activesupport/test/autoloading_fixtures/a/c/em/f.rb2
-rw-r--r--activesupport/test/autoloading_fixtures/d.rb2
-rw-r--r--activesupport/test/autoloading_fixtures/e.rb2
-rw-r--r--activesupport/test/autoloading_fixtures/em.rb2
-rw-r--r--activesupport/test/autoloading_fixtures/typo.rb2
-rw-r--r--activesupport/test/caching_test.rb233
-rw-r--r--activesupport/test/callbacks_test.rb161
-rw-r--r--activesupport/test/concern_test.rb35
-rw-r--r--activesupport/test/configurable_test.rb8
-rw-r--r--activesupport/test/core_ext/array/access_test.rb4
-rw-r--r--activesupport/test/core_ext/array/conversions_test.rb6
-rw-r--r--activesupport/test/core_ext/big_decimal/yaml_conversions_test.rb11
-rw-r--r--activesupport/test/core_ext/class/delegating_attributes_test.rb122
-rw-r--r--activesupport/test/core_ext/date_and_time_behavior.rb70
-rw-r--r--activesupport/test/core_ext/date_ext_test.rb33
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb75
-rw-r--r--activesupport/test/core_ext/duration_test.rb56
-rw-r--r--activesupport/test/core_ext/enumerable_test.rb25
-rw-r--r--activesupport/test/core_ext/file_test.rb10
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb62
-rw-r--r--activesupport/test/core_ext/kernel_test.rb71
-rw-r--r--activesupport/test/core_ext/load_error_test.rb23
-rw-r--r--activesupport/test/core_ext/marshal_test.rb36
-rw-r--r--activesupport/test/core_ext/module/attribute_accessor_test.rb6
-rw-r--r--activesupport/test/core_ext/module_test.rb222
-rw-r--r--activesupport/test/core_ext/numeric_ext_test.rb102
-rw-r--r--activesupport/test/core_ext/object/blank_test.rb1
-rw-r--r--activesupport/test/core_ext/object/deep_dup_test.rb6
-rw-r--r--activesupport/test/core_ext/object/duplicable_test.rb14
-rw-r--r--activesupport/test/core_ext/object/itself_test.rb9
-rw-r--r--activesupport/test/core_ext/object/json_gem_encoding_test.rb66
-rw-r--r--activesupport/test/core_ext/object/try_test.rb90
-rw-r--r--activesupport/test/core_ext/range_ext_test.rb4
-rw-r--r--activesupport/test/core_ext/secure_random_test.rb20
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb230
-rw-r--r--activesupport/test/core_ext/struct_test.rb10
-rw-r--r--activesupport/test/core_ext/thread_test.rb75
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb103
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb146
-rw-r--r--activesupport/test/core_ext/uri_ext_test.rb1
-rw-r--r--activesupport/test/dependencies_test.rb303
-rw-r--r--activesupport/test/dependencies_test_helpers.rb1
-rw-r--r--activesupport/test/deprecation_test.rb35
-rw-r--r--activesupport/test/file_fixtures/sample.txt1
-rw-r--r--activesupport/test/hash_with_indifferent_access_test.rb10
-rw-r--r--activesupport/test/inflector_test.rb154
-rw-r--r--activesupport/test/inflector_test_cases.rb1
-rw-r--r--activesupport/test/json/decoding_test.rb1
-rw-r--r--activesupport/test/json/encoding_test.rb184
-rw-r--r--activesupport/test/json/encoding_test_cases.rb88
-rw-r--r--activesupport/test/log_subscriber_test.rb9
-rw-r--r--activesupport/test/message_encryptor_test.rb11
-rw-r--r--activesupport/test/message_verifier_test.rb48
-rw-r--r--activesupport/test/multibyte_chars_test.rb3
-rw-r--r--activesupport/test/multibyte_conformance_test.rb3
-rw-r--r--activesupport/test/multibyte_proxy_test.rb1
-rw-r--r--activesupport/test/multibyte_test_helpers.rb1
-rw-r--r--activesupport/test/multibyte_unicode_database_test.rb6
-rw-r--r--activesupport/test/number_helper_test.rb29
-rw-r--r--activesupport/test/ordered_options_test.rb15
-rw-r--r--activesupport/test/rescuable_test.rb33
-rw-r--r--activesupport/test/safe_buffer_test.rb12
-rw-r--r--activesupport/test/security_utils_test.rb9
-rw-r--r--activesupport/test/share_lock_test.rb333
-rw-r--r--activesupport/test/tagged_logging_test.rb13
-rw-r--r--activesupport/test/test_case_test.rb29
-rw-r--r--activesupport/test/testing/file_fixtures_test.rb28
-rw-r--r--activesupport/test/testing/method_call_assertions_test.rb123
-rw-r--r--activesupport/test/time_travel_test.rb96
-rw-r--r--activesupport/test/time_zone_test.rb114
-rw-r--r--activesupport/test/transliterate_test.rb1
-rw-r--r--activesupport/test/xml_mini/nokogiri_engine_test.rb20
-rw-r--r--activesupport/test/xml_mini/nokogirisax_engine_test.rb20
-rw-r--r--activesupport/test/xml_mini/rexml_engine_test.rb14
-rw-r--r--activesupport/test/xml_mini_test.rb4
222 files changed, 5011 insertions, 2991 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index a935d33686..aec8b1efab 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,373 +1,340 @@
-* Corrected Inflector#underscore handling of multiple successive acroynms.
+* `number_to_currency` and `number_with_delimiter` now accept custom `delimiter_pattern` option
+ to handle placement of delimiter, to support currency formats like INR
- *James Le Cuirot*
+ Example:
-* Delegation now works with ruby reserved words passed to `:to` option.
+ number_to_currency(1230000, delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/, unit: '₹', format: "%u %n")
+ # => '₹ 12,30,000.00'
- Fixes #16956.
+ *Vipul A M*
- *Agis Anastasopoulos*
+* Deprecate `:prefix` option of `number_to_human_size` with no replacement.
-* Added method `#eql?` to `ActiveSupport::Duration`, in addition to `#==`.
+ *Jean Boussier*
- Currently, the following returns `false`, contrary to expectation:
+* Fix `TimeWithZone#eql?` to properly handle `TimeWithZone` created from `DateTime`:
+ twz = DateTime.now.in_time_zone
+ twz.eql?(twz.dup) => true
- 1.minute.eql?(1.minute)
+ Fixes #14178.
- Adding method `#eql?` will make this behave like expected. Method `#eql?` is
- just a bit stricter than `#==`, as it checks whether the argument is also a duration. Their
- parts may be different though.
+ *Roque Pinel*
- 1.minute.eql?(60.seconds) # => true
- 1.minute.eql?(60) # => false
+* ActiveSupport::HashWithIndifferentAccess `select` and `reject` will now return
+ enumerator if called without block.
- *Joost Lubach*
+ Fixes #20095
-* `Time#change` can now change nanoseconds (`:nsec`) as a higher-precision
- alternative to microseconds (`:usec`).
+ *Bernard Potocki*
- *Agis Anastasooulos*
+* Removed `ActiveSupport::Concurrency::Latch`, superseded by `Concurrent::CountDownLatch`
+ from the concurrent-ruby gem.
-* `MessageVerifier.new` raises an appropriate exception if the secret is `nil`.
- This prevents `MessageVerifier#generate` from raising a cryptic error later on.
+ *Jerry D'Antonio*
- *Kostiantyn Kahanskyi*
+* Fix not calling `#default` on `HashWithIndifferentAccess#to_hash` when only
+ `default_proc` is set, which could raise.
-* Introduced new configuration option `active_support.test_order` for
- specifying the order in which test cases are executed. This option currently defaults
- to `:sorted` but will be changed to `:random` in Rails 5.0.
+ *Simon Eskildsen*
- *Akira Matsuda*, *Godfrey Chan*
+* Fix setting `default_proc` on `HashWithIndifferentAccess#dup`
-* Fixed a bug in `Inflector#underscore` where acroynms in nested constant names
- are incorrectly parsed as camelCase.
+ *Simon Eskildsen*
- Fixes #8015.
+* Fix a range of values for parameters of the Time#change
- *Fred Wu*, *Matthew Draper*
+ *Nikolay Kondratyev*
-* Make `Time#change` throw an exception if the `:usec` option is out of range and
- the time has an offset other than UTC or local.
+* Add `Enumerable#pluck` to get the same values from arrays as from ActiveRecord
+ associations.
- *Agis Anastasopoulos*
+ Fixes #20339.
-* `Method` objects now report themselves as not `duplicable?`. This allows
- hashes and arrays containing `Method` objects to be `deep_dup`ed.
+ *Kevin Deisz*
- *Peter Jaros*
+* Add a bang version to `ActiveSupport::OrderedOptions` get methods which will raise
+ an `KeyError` if the value is `.blank?`
-* `determine_constant_from_test_name` does no longer shadow `NameError`s
- which happens during constant autoloading.
-
- Fixes #9933.
-
- *Guo Xiang Tan*
-
-* Added instance_eval version to Object#try, so you can do this:
-
- person.try { name.first }
+ Before:
- instead of:
+ if (slack_url = Rails.application.secrets.slack_url).present?
+ # Do something worthwhile
+ else
+ # Raise as important secret password is not specified
+ end
- person.try { |person| person.name.first }
+ After:
- *DHH*
+ slack_url = Rails.application.secrets.slack_url!
-* Fix the `ActiveSupport::Duration#instance_of?` method to return the right
- value with the class itself since it was previously delegated to the
- internal value.
+ *Aditya Sanghi*, *Gaurish Sharma*
- *Robin Dupret*
+* Remove deprecated `Class#superclass_delegating_accessor`.
+ Use `Class#class_attribute` instead.
-* Fix rounding errors with `#travel_to` by resetting the usec on any passed time to zero, so we only travel
- with per-second precision, not anything deeper than that.
+ *Akshay Vishnoi*
- *DHH*
+* Patch `Delegator` to work with `#try`.
-* Fix DateTime comparison with `DateTime::Infinity` object.
+ Fixes #5790.
- *Rafael Mendonça França*
+ *Nate Smith*
-* Added Object#itself which returns the object itself. Useful when dealing with a chaining scenario, like Active Record scopes:
+* Add `Integer#positive?` and `Integer#negative?` query methods
+ in the vein of `Fixnum#zero?`.
- Event.public_send(state.presence_in([ :trashed, :drafted ]) || :itself).order(:created_at)
+ This makes it nicer to do things like `bunch_of_numbers.select(&:positive?)`.
*DHH*
-* `Object#with_options` executes block in merging option context when
- explicit receiver in not passed.
+* Encoding `ActiveSupport::TimeWithZone` to YAML now preserves the timezone information.
- *Pavel Pravosud*
+ Fixes #9183.
-* Fixed a compatibility issue with the `Oj` gem when cherry-picking the file
- `active_support/core_ext/object/json` without requiring `active_support/json`.
-
- Fixes #16131.
-
- *Godfrey Chan*
+ *Andrew White*
-* Make `Hash#with_indifferent_access` copy the default proc too.
+* Added `ActiveSupport::TimeZone#strptime` to allow parsing times as if
+ from a given timezone.
- *arthurnn*, *Xanders*
+ *Paul A Jungwirth*
-* Add `String#truncate_words` to truncate a string by a number of words.
+* `ActiveSupport::Callbacks#skip_callback` now raises an `ArgumentError` if
+ an unrecognized callback is removed.
- *Mohamed Osama*
+ *Iain Beeston*
-* Deprecate `capture` and `quietly`.
+* Added `ActiveSupport::ArrayInquirer` and `Array#inquiry`.
- These methods are not thread safe and may cause issues when used in threaded environments.
- To avoid problems we are deprecating them.
+ Wrapping an array in an `ArrayInquirer` gives a friendlier way to check its
+ contents:
- *Tom Meier*
+ variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet])
-* `DateTime#to_f` now preserves the fractional seconds instead of always
- rounding to `.0`.
+ variants.phone? # => true
+ variants.tablet? # => true
+ variants.desktop? # => false
- Fixes #15994.
+ variants.any?(:phone, :tablet) # => true
+ variants.any?(:phone, :desktop) # => true
+ variants.any?(:desktop, :watch) # => false
- *John Paul Ashenfelter*
+ `Array#inquiry` is a shortcut for wrapping the receiving array in an
+ `ArrayInquirer`.
-* Add `Hash#transform_values` to simplify a common pattern where the values of a
- hash must change, but the keys are left the same.
+ *George Claghorn*
- *Sean Griffin*
+* Deprecate `alias_method_chain` in favour of `Module#prepend` introduced in
+ Ruby 2.0.
-* Always instrument `ActiveSupport::Cache`.
+ *Kir Shatrov*
- Since `ActiveSupport::Notifications` only instruments items when there
- are attached subscribers, we don't need to disable instrumentation.
+* Added `#without` on `Enumerable` and `Array` to return a copy of an
+ enumerable without the specified elements.
- *Peter Wagenet*
+ *Todd Bealmear*
-* Make the `apply_inflections` method case-insensitive when checking
- whether a word is uncountable or not.
+* Fixed a problem where `String#truncate_words` would get stuck with a complex
+ string.
- *Robin Dupret*
+ *Henrik Nygren*
-* Make Dependencies pass a name to NameError error.
-
- *arthurnn*
-
-* Fixed `ActiveSupport::Cache::FileStore` exploding with long paths.
-
- *Adam Panzer / Michael Grosser*
-
-* Fixed `ActiveSupport::TimeWithZone#-` so precision is not unnecessarily lost
- when working with objects with a nanosecond component.
-
- `ActiveSupport::TimeWithZone#-` should return the same result as if we were
- using `Time#-`:
-
- Time.now.end_of_day - Time.now.beginning_of_day # => 86399.999999999
+* Fixed a roundtrip problem with `AS::SafeBuffer` where primitive-like strings
+ will be dumped as primitives:
Before:
- Time.zone.now.end_of_day.nsec # => 999999999
- Time.zone.now.end_of_day - Time.zone.now.beginning_of_day # => 86400.0
+ YAML.load ActiveSupport::SafeBuffer.new("Hello").to_yaml # => "Hello"
+ YAML.load ActiveSupport::SafeBuffer.new("true").to_yaml # => true
+ YAML.load ActiveSupport::SafeBuffer.new("false").to_yaml # => false
+ YAML.load ActiveSupport::SafeBuffer.new("1").to_yaml # => 1
+ YAML.load ActiveSupport::SafeBuffer.new("1.1").to_yaml # => 1.1
After:
- Time.zone.now.end_of_day - Time.zone.now.beginning_of_day
- # => 86399.999999999
-
- *Gordon Chan*
-
-* Fixed precision error in NumberHelper when using Rationals.
-
- Before:
-
- ActiveSupport::NumberHelper.number_to_rounded Rational(1000, 3), precision: 2
- # => "330.00"
-
- After:
-
- ActiveSupport::NumberHelper.number_to_rounded Rational(1000, 3), precision: 2
- # => "333.33"
-
- See #15379.
-
- *Juanjo Bazán*
-
-* Removed deprecated `Numeric#ago` and friends
-
- Replacements:
-
- 5.ago => 5.seconds.ago
- 5.until => 5.seconds.until
- 5.since => 5.seconds.since
- 5.from_now => 5.seconds.from_now
-
- See #12389 for the history and rationale behind this.
+ YAML.load ActiveSupport::SafeBuffer.new("Hello").to_yaml # => "Hello"
+ YAML.load ActiveSupport::SafeBuffer.new("true").to_yaml # => "true"
+ YAML.load ActiveSupport::SafeBuffer.new("false").to_yaml # => "false"
+ YAML.load ActiveSupport::SafeBuffer.new("1").to_yaml # => "1"
+ YAML.load ActiveSupport::SafeBuffer.new("1.1").to_yaml # => "1.1"
*Godfrey Chan*
-* DateTime `advance` now supports partial days.
+* Enable `number_to_percentage` to keep the number's precision by allowing
+ `:precision` to be `nil`.
- Before:
+ *Jack Xu*
- DateTime.now.advance(days: 1, hours: 12)
+* `config_accessor` became a private method, as with Ruby's `attr_accessor`.
- After:
+ *Akira Matsuda*
- DateTime.now.advance(days: 1.5)
+* `AS::Testing::TimeHelpers#travel_to` now changes `DateTime.now` as well as
+ `Time.now` and `Date.today`.
- Fixes #12005.
+ *Yuki Nishijima*
- *Shay Davidson*
+* Add `file_fixture` to `ActiveSupport::TestCase`.
+ It provides a simple mechanism to access sample files in your test cases.
-* `Hash#deep_transform_keys` and `Hash#deep_transform_keys!` now transform hashes
- in nested arrays. This change also applies to `Hash#deep_stringify_keys`,
- `Hash#deep_stringify_keys!`, `Hash#deep_symbolize_keys` and
- `Hash#deep_symbolize_keys!`.
+ By default file fixtures are stored in `test/fixtures/files`. This can be
+ configured per test-case using the `file_fixture_path` class attribute.
- *OZAWA Sakuro*
+ *Yves Senn*
-* Fixed confusing `DelegationError` in `Module#delegate`.
+* Return value of yielded block in `File.atomic_write`.
- See #15186.
+ *Ian Ker-Seymer*
- *Vladimir Yarotsky*
+* Duplicate frozen array when assigning it to a `HashWithIndifferentAccess` so
+ that it doesn't raise a `RuntimeError` when calling `map!` on it in `convert_value`.
-* Fixed `ActiveSupport::Subscriber` so that no duplicate subscriber is created
- when a subscriber method is redefined.
+ Fixes #18550.
- *Dennis Schön*
+ *Aditya Kapoor*
-* Remove deprecated string based terminators for `ActiveSupport::Callbacks`.
+* Add missing time zone definitions for Russian Federation and sync them
+ with `zone.tab` file from tzdata version 2014j (latest).
- *Eileen M. Uchitelle*
+ *Andrey Novikov*
-* Fixed an issue when using
- `ActiveSupport::NumberHelper::NumberToDelimitedConverter` to
- convert a value that is an `ActiveSupport::SafeBuffer` introduced
- in 2da9d67.
+* Add `SecureRandom.base58` for generation of random base58 strings.
- See #15064.
+ *Matthew Draper*, *Guillermo Iguaran*
- *Mark J. Titorenko*
+* Add `#prev_day` and `#next_day` counterparts to `#yesterday` and
+ `#tomorrow` for `Date`, `Time`, and `DateTime`.
-* `TimeZone#parse` defaults the day of the month to '1' if any other date
- components are specified. This is more consistent with the behavior of
- `Time#parse`.
+ *George Claghorn*
- *Ulysse Carion*
+* Add `same_time` option to `#next_week` and `#prev_week` for `Date`, `Time`,
+ and `DateTime`.
-* `humanize` strips leading underscores, if any.
+ *George Claghorn*
- Before:
+* Add `#on_weekend?`, `#next_weekday`, `#prev_weekday` methods to `Date`,
+ `Time`, and `DateTime`.
- '_id'.humanize # => ""
+ `#on_weekend?` returns `true` if the receiving date/time falls on a Saturday
+ or Sunday.
- After:
+ `#next_weekday` returns a new date/time representing the next day that does
+ not fall on a Saturday or Sunday.
+
+ `#prev_weekday` returns a new date/time representing the previous day that
+ does not fall on a Saturday or Sunday.
- '_id'.humanize # => "Id"
+ *George Claghorn*
- *Xavier Noria*
+* Change the default test order from `:sorted` to `:random`.
-* Fixed backward compatibility issues introduced in 326e652.
+ *Rafael Mendonça França*
- Empty Hash or Array should not be present in serialization result.
+* Remove deprecated `ActiveSupport::JSON::Encoding::CircularReferenceError`.
- {a: []}.to_query # => ""
- {a: {}}.to_query # => ""
+ *Rafael Mendonça França*
- For more info see #14948.
+* Remove deprecated methods `ActiveSupport::JSON::Encoding.encode_big_decimal_as_string=`
+ and `ActiveSupport::JSON::Encoding.encode_big_decimal_as_string`.
- *Bogdan Gusiev*
+ *Rafael Mendonça França*
-* Add `Digest::UUID::uuid_v3` and `Digest::UUID::uuid_v5` to support stable
- UUID fixtures on PostgreSQL.
+* Remove deprecated `ActiveSupport::SafeBuffer#prepend`.
- *Roderick van Domburg*
+ *Rafael Mendonça França*
-* Fixed `ActiveSupport::Duration#eql?` so that `1.second.eql?(1.second)` is
- true.
+* Remove deprecated methods at `Kernel`.
- This fixes the current situation of:
+ `silence_stderr`, `silence_stream`, `capture` and `quietly`.
- 1.second.eql?(1.second) # => false
+ *Rafael Mendonça França*
- `eql?` also requires that the other object is an `ActiveSupport::Duration`.
- This requirement makes `ActiveSupport::Duration`'s behavior consistent with
- the behavior of Ruby's numeric types:
+* Remove deprecated `active_support/core_ext/big_decimal/yaml_conversions`
+ file.
- 1.eql?(1.0) # => false
- 1.0.eql?(1) # => false
+ *Rafael Mendonça França*
- 1.second.eql?(1) # => false (was true)
- 1.eql?(1.second) # => false
+* Remove deprecated methods `ActiveSupport::Cache::Store.instrument` and
+ `ActiveSupport::Cache::Store.instrument=`.
- { 1 => "foo", 1.0 => "bar" }
- # => { 1 => "foo", 1.0 => "bar" }
+ *Rafael Mendonça França*
- { 1 => "foo", 1.second => "bar" }
- # now => { 1 => "foo", 1.second => "bar" }
- # was => { 1 => "bar" }
+* Change the way in which callback chains can be halted.
- And though the behavior of these hasn't changed, for reference:
+ The preferred method to halt a callback chain from now on is to explicitly
+ `throw(:abort)`.
+ In the past, returning `false` in an ActiveSupport callback had the side
+ effect of halting the callback chain. This is not recommended anymore and,
+ depending on the value of
+ `Callbacks::CallbackChain.halt_and_display_warning_on_return_false`, will
+ either not work at all or display a deprecation warning.
- 1 == 1.0 # => true
- 1.0 == 1 # => true
+* Add `Callbacks::CallbackChain.halt_and_display_warning_on_return_false`
- 1 == 1.second # => true
- 1.second == 1 # => true
+ Setting `Callbacks::CallbackChain.halt_and_display_warning_on_return_false`
+ to `true` will let an app support the deprecated way of halting callback
+ chains by returning `false`.
- *Emily Dobervich*
+ Setting the value to `false` will tell the app to ignore any `false` value
+ returned by callbacks, and only halt the chain upon `throw(:abort)`.
-* `ActiveSupport::SafeBuffer#prepend` acts like `String#prepend` and modifies
- instance in-place, returning self. `ActiveSupport::SafeBuffer#prepend!` is
- deprecated.
+ The value can also be set with the Rails configuration option
+ `config.active_support.halt_callback_chains_on_return_false`.
- *Pavel Pravosud*
+ When the configuration option is missing, its value is `true`, so older apps
+ ported to Rails 5.0 will not break (but display a deprecation warning).
+ For new Rails 5.0 apps, its value is set to `false` in an initializer, so
+ these apps will support the new behavior by default.
-* `HashWithIndifferentAccess` better respects `#to_hash` on objects it
- receives. In particular, `.new`, `#update`, `#merge`, and `#replace` accept
- objects which respond to `#to_hash`, even if those objects are not hashes
- directly.
+ *claudiob*
- *Peter Jaros*
+* Changes arguments and default value of CallbackChain's `:terminator` option
-* Deprecate `Class#superclass_delegating_accessor`, use `Class#class_attribute` instead.
+ Chains of callbacks defined without an explicit `:terminator` option will
+ now be halted as soon as a `before_` callback throws `:abort`.
- *Akshay Vishnoi*
+ Chains of callbacks defined with a `:terminator` option will maintain their
+ existing behavior of halting as soon as a `before_` callback matches the
+ terminator's expectation.
-* Ensure classes which `include Enumerable` get `#to_json` in addition to
- `#as_json`.
+ *claudiob*
- *Sammy Larbi*
+* Deprecate `MissingSourceFile` in favor of `LoadError`.
-* Change the signature of `fetch_multi` to return a hash rather than an
- array. This makes it consistent with the output of `read_multi`.
+ `MissingSourceFile` was just an alias to `LoadError` and was not being
+ raised inside the framework.
- *Parker Selbert*
+ *Rafael Mendonça França*
-* Introduce `Concern#class_methods` as a sleek alternative to clunky
- `module ClassMethods`. Add `Kernel#concern` to define at the toplevel
- without chunky `module Foo; extend ActiveSupport::Concern` boilerplate.
+* Add support for error dispatcher classes in `ActiveSupport::Rescuable`.
+ Now it acts closer to Ruby's rescue.
- # app/models/concerns/authentication.rb
- concern :Authentication do
- included do
- after_create :generate_private_key
- end
+ Example:
- class_methods do
- def authenticate(credentials)
- # ...
+ class BaseController < ApplicationController
+ module ErrorDispatcher
+ def self.===(other)
+ Exception === other && other.respond_to?(:status)
end
end
- def generate_private_key
- # ...
+ rescue_from ErrorDispatcher do |error|
+ render status: error.status, json: { error: error.to_s }
end
end
- # app/models/user.rb
- class User < ActiveRecord::Base
- include Authentication
- end
+ *Genadi Samokovarov*
+
+* Add `#verified` and `#valid_message?` methods to `ActiveSupport::MessageVerifier`
+
+ Previously, the only way to decode a message with `ActiveSupport::MessageVerifier`
+ was to use `#verify`, which would raise an exception on invalid messages. Now
+ `#verified` can also be used, which returns `nil` on messages that cannot be
+ decoded.
+
+ Previously, there was no way to check if a message's format was valid without
+ attempting to decode it. `#valid_message?` is a boolean convenience method that
+ checks whether the message is valid without actually decoding it.
- *Jeremy Kemper*
+ *Logan Leger*
-Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/activesupport/CHANGELOG.md) for previous changes.
+Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/activesupport/CHANGELOG.md) for previous changes.
diff --git a/activesupport/MIT-LICENSE b/activesupport/MIT-LICENSE
index d06d4f3b2d..7bffebb076 100644
--- a/activesupport/MIT-LICENSE
+++ b/activesupport/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2005-2014 David Heinemeier Hansson
+Copyright (c) 2005-2015 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/activesupport/README.rdoc b/activesupport/README.rdoc
index a6424a353a..cd72f53821 100644
--- a/activesupport/README.rdoc
+++ b/activesupport/README.rdoc
@@ -10,7 +10,7 @@ outside of Rails.
The latest version of Active Support can be installed with RubyGems:
- % [sudo] gem install activesupport
+ % gem install activesupport
Source code can be downloaded as part of the Rails project on GitHub:
diff --git a/activesupport/Rakefile b/activesupport/Rakefile
index 7c40df8dc8..81c242d4b1 100644
--- a/activesupport/Rakefile
+++ b/activesupport/Rakefile
@@ -1,5 +1,4 @@
require 'rake/testtask'
-require 'rubygems/package_task'
task :default => :test
Rake::TestTask.new do |t|
@@ -17,16 +16,3 @@ namespace :test do
end or raise "Failures"
end
end
-
-spec = eval(File.read('activesupport.gemspec'))
-
-Gem::PackageTask.new(spec) do |p|
- p.gem_spec = spec
-end
-
-desc "Release to rubygems"
-task :release => :package do
- require 'rake/gemcutter'
- Rake::Gemcutter::Tasks.new(spec).define
- Rake::Task['gem:push'].invoke
-end
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index 80d1d35888..67b6663915 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
s.summary = 'A toolkit of support libraries and Ruby core extensions extracted from the Rails framework.'
s.description = 'A toolkit of support libraries and Ruby core extensions extracted from the Rails framework. Rich support for multibyte strings, internationalization, time zones, and testing.'
- s.required_ruby_version = '>= 1.9.3'
+ s.required_ruby_version = '>= 2.2.2'
s.license = 'MIT'
@@ -20,9 +20,11 @@ Gem::Specification.new do |s|
s.rdoc_options.concat ['--encoding', 'UTF-8']
- s.add_dependency 'i18n', '>= 0.7.0.beta1', '< 0.8'
+ s.add_dependency 'i18n', '~> 0.7'
s.add_dependency 'json', '~> 1.7', '>= 1.7.7'
s.add_dependency 'tzinfo', '~> 1.1'
s.add_dependency 'minitest', '~> 5.1'
- s.add_dependency 'thread_safe','~> 0.1'
+ s.add_dependency 'thread_safe','~> 0.3', '>= 0.3.4'
+ s.add_dependency 'concurrent-ruby', '~> 0.9.1'
+ s.add_dependency 'method_source'
end
diff --git a/activesupport/bin/generate_tables b/activesupport/bin/generate_tables
index f39e89b7d0..71a6b78652 100755
--- a/activesupport/bin/generate_tables
+++ b/activesupport/bin/generate_tables
@@ -55,7 +55,7 @@ module ActiveSupport
codepoint.combining_class = Integer($4)
#codepoint.bidi_class = $5
codepoint.decomp_type = $7
- codepoint.decomp_mapping = ($8=='') ? nil : $8.split.collect { |element| element.hex }
+ codepoint.decomp_mapping = ($8=='') ? nil : $8.split.collect(&:hex)
#codepoint.bidi_mirrored = ($13=='Y') ? true : false
codepoint.uppercase_mapping = ($16=='') ? 0 : $16.hex
codepoint.lowercase_mapping = ($17=='') ? 0 : $17.hex
diff --git a/activesupport/bin/test b/activesupport/bin/test
new file mode 100755
index 0000000000..404cabba51
--- /dev/null
+++ b/activesupport/bin/test
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+COMPONENT_ROOT = File.expand_path("../../", __FILE__)
+require File.expand_path("../tools/test", COMPONENT_ROOT)
+exit Minitest.run(ARGV)
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index 94468240a4..588d6c49f9 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2005-2014 David Heinemeier Hansson
+# Copyright (c) 2005-2015 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -59,6 +59,7 @@ module ActiveSupport
autoload :StringInquirer
autoload :TaggedLogging
autoload :XmlMini
+ autoload :ArrayInquirer
end
autoload :Rescuable
@@ -71,14 +72,14 @@ module ActiveSupport
NumberHelper.eager_load!
end
- @@test_order = nil
+ cattr_accessor :test_order # :nodoc:
- def self.test_order=(new_order)
- @@test_order = new_order
+ def self.halt_callback_chains_on_return_false
+ Callbacks::CallbackChain.halt_and_display_warning_on_return_false
end
- def self.test_order
- @@test_order
+ def self.halt_callback_chains_on_return_false=(value)
+ Callbacks::CallbackChain.halt_and_display_warning_on_return_false = value
end
end
diff --git a/activesupport/lib/active_support/array_inquirer.rb b/activesupport/lib/active_support/array_inquirer.rb
new file mode 100644
index 0000000000..f59ddf5403
--- /dev/null
+++ b/activesupport/lib/active_support/array_inquirer.rb
@@ -0,0 +1,44 @@
+module ActiveSupport
+ # Wrapping an array in an +ArrayInquirer+ gives a friendlier way to check
+ # its string-like contents:
+ #
+ # variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet])
+ #
+ # variants.phone? # => true
+ # variants.tablet? # => true
+ # variants.desktop? # => false
+ class ArrayInquirer < Array
+ # Passes each element of +candidates+ collection to ArrayInquirer collection.
+ # The method returns true if at least one element is the same. If +candidates+
+ # collection is not given, method returns true.
+ #
+ # variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet])
+ #
+ # variants.any? # => true
+ # variants.any?(:phone, :tablet) # => true
+ # variants.any?('phone', 'desktop') # => true
+ # variants.any?(:desktop, :watch) # => false
+ def any?(*candidates, &block)
+ if candidates.none?
+ super
+ else
+ candidates.any? do |candidate|
+ include?(candidate.to_sym) || include?(candidate.to_s)
+ end
+ end
+ end
+
+ private
+ def respond_to_missing?(name, include_private = false)
+ name[-1] == '?'
+ end
+
+ def method_missing(name, *args)
+ if name[-1] == '?'
+ any?(name[0..-2])
+ else
+ super
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb
index d06f22ad5c..e161ec4cca 100644
--- a/activesupport/lib/active_support/backtrace_cleaner.rb
+++ b/activesupport/lib/active_support/backtrace_cleaner.rb
@@ -25,7 +25,7 @@ module ActiveSupport
# of the backtrace, you can call <tt>BacktraceCleaner#remove_filters!</tt>
# These two methods will give you a completely untouched backtrace.
#
- # Inspired by the Quiet Backtrace gem by Thoughtbot.
+ # Inspired by the Quiet Backtrace gem by thoughtbot.
class BacktraceCleaner
def initialize
@filters, @silencers = [], []
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index ff67a6828c..8253a76383 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -8,7 +8,6 @@ require 'active_support/core_ext/numeric/bytes'
require 'active_support/core_ext/numeric/time'
require 'active_support/core_ext/object/to_param'
require 'active_support/core_ext/string/inflections'
-require 'active_support/deprecation'
module ActiveSupport
# See ActiveSupport::Cache::Store for documentation.
@@ -27,7 +26,7 @@ module ActiveSupport
end
class << self
- # Creates a new CacheStore object according to the given options.
+ # Creates a new Store object according to the given options.
#
# If no arguments are passed to this method, then a new
# ActiveSupport::Cache::MemoryStore object will be returned.
@@ -179,18 +178,6 @@ module ActiveSupport
@silence = previous_silence
end
- # :deprecated:
- def self.instrument=(boolean)
- ActiveSupport::Deprecation.warn "ActiveSupport::Cache.instrument= is deprecated and will be removed in Rails 5. Instrumentation is now always on so you can safely stop using it."
- true
- end
-
- # :deprecated:
- def self.instrument
- ActiveSupport::Deprecation.warn "ActiveSupport::Cache.instrument is deprecated and will be removed in Rails 5. Instrumentation is now always on so you can safely stop using it."
- true
- end
-
# Fetches data from the cache, using the given key. If there is data in
# the cache with the given key, then that data is returned.
#
@@ -338,19 +325,22 @@ module ActiveSupport
def read_multi(*names)
options = names.extract_options!
options = merged_options(options)
- results = {}
- names.each do |name|
- key = namespaced_key(name, options)
- entry = read_entry(key, options)
- if entry
- if entry.expired?
- delete_entry(key, options)
- else
- results[name] = entry.value
+
+ instrument_multi(:read, names, options) do |payload|
+ results = {}
+ names.each do |name|
+ key = namespaced_key(name, options)
+ entry = read_entry(key, options)
+ if entry
+ if entry.expired?
+ delete_entry(key, options)
+ else
+ results[name] = entry.value
+ end
end
end
+ results
end
- results
end
# Fetches data from the cache, using the given keys. If there is data in
@@ -363,8 +353,11 @@ module ActiveSupport
# Returns a hash with the data for each of the names. For example:
#
# cache.write("bim", "bam")
- # cache.fetch_multi("bim", "boom") { |key| key * 2 }
- # # => { "bam" => "bam", "boom" => "boomboom" }
+ # cache.fetch_multi("bim", "unknown_key") do |key|
+ # "Fallback value for key: #{key}"
+ # end
+ # # => { "bim" => "bam",
+ # # "unknown_key" => "Fallback value for key: unknown_key" }
#
def fetch_multi(*names)
options = names.extract_options!
@@ -540,16 +533,27 @@ module ActiveSupport
end
def instrument(operation, key, options = nil)
- log(operation, key, options)
+ log { "Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}" }
payload = { :key => key }
payload.merge!(options) if options.is_a?(Hash)
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
end
- def log(operation, key, options = nil)
+ def instrument_multi(operation, keys, options = nil)
+ log do
+ formatted_keys = keys.map { |k| "- #{k}" }.join("\n")
+ "Caches multi #{operation}:\n#{formatted_keys}#{options.blank? ? "" : " (#{options.inspect})"}"
+ end
+
+ payload = { key: keys }
+ payload.merge!(options) if options.is_a?(Hash)
+ ActiveSupport::Notifications.instrument("cache_#{operation}_multi.active_support", payload) { yield(payload) }
+ end
+
+ def log
return unless logger && logger.debug? && !silence?
- logger.debug("Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}")
+ logger.debug(yield)
end
def find_cached_entry(key, name, options)
@@ -562,9 +566,9 @@ 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.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.
+ if (race_ttl > 0) && (Time.now.to_f - entry.expires_at <= race_ttl)
+ # When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
+ # for a brief period while the entry is being recalculated.
entry.expires_at = Time.now + race_ttl
write_entry(key, entry, :expires_in => race_ttl * 2)
else
@@ -615,14 +619,12 @@ module ActiveSupport
end
def value
- 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_4beta1_entry! if defined?(@value)
@expires_in && @created_at + @expires_in <= Time.now.to_f
end
@@ -658,8 +660,6 @@ 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_4beta1_entry! if defined?(@v)
-
if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false)
if @value.is_a?(String)
@value = @value.dup
@@ -692,26 +692,6 @@ module ActiveSupport
def uncompress(value)
Marshal.load(Zlib::Inflate.inflate(value))
end
-
- # 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_4beta1_entry!
- if defined?(@v)
- @value = @v
- remove_instance_variable(:@v)
- end
-
- if defined?(@c)
- @compressed = @c
- remove_instance_variable(:@c)
- end
-
- if defined?(@x) && @x
- @created_at ||= Time.now.to_f
- @expires_in = @x - @created_at
- remove_instance_variable(:@x)
- end
- end
end
end
end
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index d08ecd2f7d..e6a8b84214 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -29,6 +29,7 @@ module ActiveSupport
def clear(options = nil)
root_dirs = Dir.entries(cache_path).reject {|f| (EXCLUDED_DIRS + [".gitkeep"]).include?(f)}
FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)})
+ rescue Errno::ENOENT
end
# Preemptively iterates through all stored keys and removes the ones which have expired.
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index 61b4f0b8b0..47133bf550 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -26,7 +26,14 @@ module ActiveSupport
class MemCacheStore < Store
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
- def self.build_mem_cache(*addresses)
+ # Creates a new Dalli::Client instance with specified addresses and options.
+ # By default address is equal localhost:11211.
+ #
+ # ActiveSupport::Cache::MemCacheStore.build_mem_cache
+ # # => #<Dalli::Client:0x007f98a47d2028 @servers=["localhost:11211"], @options={}, @ring=nil>
+ # ActiveSupport::Cache::MemCacheStore.build_mem_cache('localhost:10290')
+ # # => #<Dalli::Client:0x007f98a47b3a60 @servers=["localhost:10290"], @options={}, @ring=nil>
+ def self.build_mem_cache(*addresses) # :nodoc:
addresses = addresses.flatten
options = addresses.extract_options!
addresses = ["localhost:11211"] if addresses.empty?
@@ -66,14 +73,17 @@ module ActiveSupport
def read_multi(*names)
options = names.extract_options!
options = merged_options(options)
- keys_to_names = Hash[names.map{|name| [escape_key(namespaced_key(name, options)), name]}]
- raw_values = @data.get_multi(keys_to_names.keys, :raw => true)
- values = {}
- raw_values.each do |key, value|
- entry = deserialize_entry(value)
- values[keys_to_names[key]] = entry.value unless entry.expired?
+
+ instrument_multi(:read, names, options) do
+ keys_to_names = Hash[names.map{|name| [escape_key(namespaced_key(name, options)), name]}]
+ raw_values = @data.get_multi(keys_to_names.keys, :raw => true)
+ values = {}
+ raw_values.each do |key, value|
+ entry = deserialize_entry(value)
+ values[keys_to_names[key]] = entry.value unless entry.expired?
+ end
+ values
end
- values
end
# Increment a cached value. This method uses the memcached incr atomic
diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb
index 8a0523d0e2..90bb2c38c3 100644
--- a/activesupport/lib/active_support/cache/memory_store.rb
+++ b/activesupport/lib/active_support/cache/memory_store.rb
@@ -126,7 +126,7 @@ module ActiveSupport
PER_ENTRY_OVERHEAD = 240
- def cached_size(key, entry)
+ def cached_size(key, entry) # :nodoc:
key.to_s.bytesize + entry.size + PER_ENTRY_OVERHEAD
end
diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb
index 73c6b3cb88..fe5bc82c30 100644
--- a/activesupport/lib/active_support/cache/strategy/local_cache.rb
+++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb
@@ -39,7 +39,7 @@ module ActiveSupport
@data = {}
end
- # Don't allow synchronizing since it isn't thread safe,
+ # Don't allow synchronizing since it isn't thread safe.
def synchronize # :nodoc:
yield
end
@@ -120,7 +120,7 @@ module ActiveSupport
super
end
- def set_cache_value(value, name, amount, options)
+ def set_cache_value(value, name, amount, options) # :nodoc:
if local_cache
local_cache.mute do
if value
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 45231bc101..fefba5b0fd 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -4,6 +4,8 @@ require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/kernel/singleton_class'
+require 'active_support/core_ext/string/filters'
+require 'active_support/deprecation'
require 'thread'
module ActiveSupport
@@ -78,14 +80,14 @@ module ActiveSupport
# save
# end
def run_callbacks(kind, &block)
- send "run_#{kind}_callbacks", &block
+ send "_run_#{kind}_callbacks", &block
end
private
- def _run_callbacks(callbacks, &block)
+ def __run_callbacks__(callbacks, &block)
if callbacks.empty?
- block.call if block
+ yield if block_given?
else
runner = callbacks.compile
e = Filters::Environment.new(self, false, nil, block)
@@ -121,102 +123,106 @@ module ActiveSupport
ENDING = End.new
class Before
- def self.build(next_callback, user_callback, user_conditions, chain_config, filter)
+ def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
halted_lambda = chain_config[:terminator]
if chain_config.key?(:terminator) && user_conditions.any?
- halting_and_conditional(next_callback, user_callback, user_conditions, halted_lambda, filter)
+ halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
elsif chain_config.key? :terminator
- halting(next_callback, user_callback, halted_lambda, filter)
+ halting(callback_sequence, user_callback, halted_lambda, filter)
elsif user_conditions.any?
- conditional(next_callback, user_callback, user_conditions)
+ conditional(callback_sequence, user_callback, user_conditions)
else
- simple next_callback, user_callback
+ simple callback_sequence, user_callback
end
end
- def self.halting_and_conditional(next_callback, user_callback, user_conditions, halted_lambda, filter)
- lambda { |env|
+ def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
+ callback_sequence.before do |env|
target = env.target
value = env.value
halted = env.halted
if !halted && user_conditions.all? { |c| c.call(target, value) }
- result = user_callback.call target, value
- env.halted = halted_lambda.call(target, result)
+ result_lambda = -> { user_callback.call target, value }
+ env.halted = halted_lambda.call(target, result_lambda)
if env.halted
target.send :halted_callback_hook, filter
end
end
- next_callback.call env
- }
+
+ env
+ end
end
private_class_method :halting_and_conditional
- def self.halting(next_callback, user_callback, halted_lambda, filter)
- lambda { |env|
+ def self.halting(callback_sequence, user_callback, halted_lambda, filter)
+ callback_sequence.before do |env|
target = env.target
value = env.value
halted = env.halted
unless halted
- result = user_callback.call target, value
- env.halted = halted_lambda.call(target, result)
+ result_lambda = -> { user_callback.call target, value }
+ env.halted = halted_lambda.call(target, result_lambda)
+
if env.halted
target.send :halted_callback_hook, filter
end
end
- next_callback.call env
- }
+
+ env
+ end
end
private_class_method :halting
- def self.conditional(next_callback, user_callback, user_conditions)
- lambda { |env|
+ def self.conditional(callback_sequence, user_callback, user_conditions)
+ callback_sequence.before do |env|
target = env.target
value = env.value
if user_conditions.all? { |c| c.call(target, value) }
user_callback.call target, value
end
- next_callback.call env
- }
+
+ env
+ end
end
private_class_method :conditional
- def self.simple(next_callback, user_callback)
- lambda { |env|
+ def self.simple(callback_sequence, user_callback)
+ callback_sequence.before do |env|
user_callback.call env.target, env.value
- next_callback.call env
- }
+
+ env
+ end
end
private_class_method :simple
end
class After
- def self.build(next_callback, user_callback, user_conditions, chain_config)
+ def self.build(callback_sequence, user_callback, user_conditions, chain_config)
if chain_config[:skip_after_callbacks_if_terminated]
if chain_config.key?(:terminator) && user_conditions.any?
- halting_and_conditional(next_callback, user_callback, user_conditions)
+ halting_and_conditional(callback_sequence, user_callback, user_conditions)
elsif chain_config.key?(:terminator)
- halting(next_callback, user_callback)
+ halting(callback_sequence, user_callback)
elsif user_conditions.any?
- conditional next_callback, user_callback, user_conditions
+ conditional callback_sequence, user_callback, user_conditions
else
- simple next_callback, user_callback
+ simple callback_sequence, user_callback
end
else
if user_conditions.any?
- conditional next_callback, user_callback, user_conditions
+ conditional callback_sequence, user_callback, user_conditions
else
- simple next_callback, user_callback
+ simple callback_sequence, user_callback
end
end
end
- def self.halting_and_conditional(next_callback, user_callback, user_conditions)
- lambda { |env|
- env = next_callback.call env
+ def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
+ callback_sequence.after do |env|
target = env.target
value = env.value
halted = env.halted
@@ -224,122 +230,119 @@ module ActiveSupport
if !halted && user_conditions.all? { |c| c.call(target, value) }
user_callback.call target, value
end
+
env
- }
+ end
end
private_class_method :halting_and_conditional
- def self.halting(next_callback, user_callback)
- lambda { |env|
- env = next_callback.call env
+ def self.halting(callback_sequence, user_callback)
+ callback_sequence.after do |env|
unless env.halted
user_callback.call env.target, env.value
end
+
env
- }
+ end
end
private_class_method :halting
- def self.conditional(next_callback, user_callback, user_conditions)
- lambda { |env|
- env = next_callback.call env
+ def self.conditional(callback_sequence, user_callback, user_conditions)
+ callback_sequence.after do |env|
target = env.target
value = env.value
if user_conditions.all? { |c| c.call(target, value) }
user_callback.call target, value
end
+
env
- }
+ end
end
private_class_method :conditional
- def self.simple(next_callback, user_callback)
- lambda { |env|
- env = next_callback.call env
+ def self.simple(callback_sequence, user_callback)
+ callback_sequence.after do |env|
user_callback.call env.target, env.value
+
env
- }
+ end
end
private_class_method :simple
end
class Around
- def self.build(next_callback, user_callback, user_conditions, chain_config)
+ def self.build(callback_sequence, user_callback, user_conditions, chain_config)
if chain_config.key?(:terminator) && user_conditions.any?
- halting_and_conditional(next_callback, user_callback, user_conditions)
+ halting_and_conditional(callback_sequence, user_callback, user_conditions)
elsif chain_config.key? :terminator
- halting(next_callback, user_callback)
+ halting(callback_sequence, user_callback)
elsif user_conditions.any?
- conditional(next_callback, user_callback, user_conditions)
+ conditional(callback_sequence, user_callback, user_conditions)
else
- simple(next_callback, user_callback)
+ simple(callback_sequence, user_callback)
end
end
- def self.halting_and_conditional(next_callback, user_callback, user_conditions)
- lambda { |env|
+ def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
+ callback_sequence.around do |env, &run|
target = env.target
value = env.value
halted = env.halted
if !halted && user_conditions.all? { |c| c.call(target, value) }
user_callback.call(target, value) {
- env = next_callback.call env
- env.value
+ run.call.value
}
env
else
- next_callback.call env
+ run.call
end
- }
+ end
end
private_class_method :halting_and_conditional
- def self.halting(next_callback, user_callback)
- lambda { |env|
+ def self.halting(callback_sequence, user_callback)
+ callback_sequence.around do |env, &run|
target = env.target
value = env.value
if env.halted
- next_callback.call env
+ run.call
else
user_callback.call(target, value) {
- env = next_callback.call env
- env.value
+ run.call.value
}
env
end
- }
+ end
end
private_class_method :halting
- def self.conditional(next_callback, user_callback, user_conditions)
- lambda { |env|
+ def self.conditional(callback_sequence, user_callback, user_conditions)
+ callback_sequence.around do |env, &run|
target = env.target
value = env.value
if user_conditions.all? { |c| c.call(target, value) }
user_callback.call(target, value) {
- env = next_callback.call env
- env.value
+ run.call.value
}
env
else
- next_callback.call env
+ run.call
end
- }
+ end
end
private_class_method :conditional
- def self.simple(next_callback, user_callback)
- lambda { |env|
+ def self.simple(callback_sequence, user_callback)
+ callback_sequence.around do |env, &run|
user_callback.call(env.target, env.value) {
- env = next_callback.call env
- env.value
+ run.call.value
}
env
- }
+ end
end
private_class_method :simple
end
@@ -366,14 +369,14 @@ module ActiveSupport
def filter; @key; end
def raw_filter; @filter; end
- def merge(chain, new_options)
+ def merge_conditional_options(chain, if_option:, unless_option:)
options = {
:if => @if.dup,
:unless => @unless.dup
}
- options[:if].concat Array(new_options.fetch(:unless, []))
- options[:unless].concat Array(new_options.fetch(:if, []))
+ options[:if].concat Array(unless_option)
+ options[:unless].concat Array(if_option)
self.class.build chain, @filter, @kind, options
end
@@ -392,17 +395,17 @@ module ActiveSupport
end
# Wraps code with filter
- def apply(next_callback)
+ def apply(callback_sequence)
user_conditions = conditions_lambdas
user_callback = make_lambda @filter
case kind
when :before
- Filters::Before.build(next_callback, user_callback, user_conditions, chain_config, @filter)
+ Filters::Before.build(callback_sequence, user_callback, user_conditions, chain_config, @filter)
when :after
- Filters::After.build(next_callback, user_callback, user_conditions, chain_config)
+ Filters::After.build(callback_sequence, user_callback, user_conditions, chain_config)
when :around
- Filters::Around.build(next_callback, user_callback, user_conditions, chain_config)
+ Filters::Around.build(callback_sequence, user_callback, user_conditions, chain_config)
end
end
@@ -467,16 +470,59 @@ module ActiveSupport
end
end
+ # Execute before and after filters in a sequence instead of
+ # chaining them with nested lambda calls, see:
+ # https://github.com/rails/rails/issues/18011
+ class CallbackSequence
+ def initialize(&call)
+ @call = call
+ @before = []
+ @after = []
+ end
+
+ def before(&before)
+ @before.unshift(before)
+ self
+ end
+
+ def after(&after)
+ @after.push(after)
+ self
+ end
+
+ def around(&around)
+ CallbackSequence.new do |arg|
+ around.call(arg) {
+ self.call(arg)
+ }
+ end
+ end
+
+ def call(arg)
+ @before.each { |b| b.call(arg) }
+ value = @call.call(arg)
+ @after.each { |a| a.call(arg) }
+ value
+ end
+ end
+
# An Array with a compile method.
class CallbackChain #:nodoc:#
include Enumerable
attr_reader :name, :config
+ # If true, any callback returning +false+ will halt the entire callback
+ # chain and display a deprecation message. If false, callback chains will
+ # only be halted by calling +throw :abort+. Defaults to +true+.
+ class_attribute :halt_and_display_warning_on_return_false
+ self.halt_and_display_warning_on_return_false = true
+
def initialize(name, config)
@name = name
@config = {
- :scope => [ :kind ]
+ scope: [:kind],
+ terminator: default_terminator
}.merge!(config)
@chain = []
@callbacks = nil
@@ -511,8 +557,9 @@ module ActiveSupport
def compile
@callbacks || @mutex.synchronize do
- @callbacks ||= @chain.reverse.inject(Filters::ENDING) do |chain, callback|
- callback.apply chain
+ final_sequence = CallbackSequence.new { |env| Filters::ENDING.call(env) }
+ @callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
+ callback.apply callback_sequence
end
end
end
@@ -546,6 +593,28 @@ module ActiveSupport
@callbacks = nil
@chain.delete_if { |c| callback.duplicates?(c) }
end
+
+ def default_terminator
+ Proc.new do |target, result_lambda|
+ terminate = true
+ catch(:abort) do
+ result = result_lambda.call if result_lambda.is_a?(Proc)
+ if halt_and_display_warning_on_return_false && result == false
+ display_deprecation_warning_for_false_terminator
+ else
+ terminate = false
+ end
+ end
+ terminate
+ end
+ end
+
+ def display_deprecation_warning_for_false_terminator
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Returning `false` in a callback will not implicitly halt a callback chain in the next release of Rails.
+ To explicitly halt a callback chain, please use `throw :abort` instead.
+ MSG
+ end
end
module ClassMethods
@@ -571,7 +640,7 @@ module ActiveSupport
# set_callback :save, :after, :after_meth, if: :condition
# set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }
#
- # The second arguments indicates whether the callback is to be run +:before+,
+ # The second argument indicates whether the callback is to be run +:before+,
# +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
# means the first example above can also be written as:
#
@@ -594,10 +663,12 @@ module ActiveSupport
#
# ===== Options
#
- # * <tt>:if</tt> - A symbol naming an instance method or a proc; the
- # callback will be called only when it returns a +true+ value.
- # * <tt>:unless</tt> - A symbol naming an instance method or a proc; the
- # callback will be called only when it returns a +false+ value.
+ # * <tt>:if</tt> - A symbol, a string or an array of symbols and strings,
+ # each naming an instance method or a proc; the callback will be called
+ # only when they all return a true value.
+ # * <tt>:unless</tt> - A symbol, a string or an array of symbols and
+ # strings, each naming an instance method or a proc; the callback will
+ # be called only when they all return a false value.
# * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
# existing chain rather than appended.
def set_callback(name, *filter_list, &block)
@@ -620,19 +691,27 @@ module ActiveSupport
# class Writer < Person
# skip_callback :validate, :before, :check_membership, if: -> { self.age > 18 }
# end
+ #
+ # An <tt>ArgumentError</tt> will be raised if the callback has not
+ # already been set (unless the <tt>:raise</tt> option is set to <tt>false</tt>).
def skip_callback(name, *filter_list, &block)
type, filters, options = normalize_callback_params(filter_list, block)
+ options[:raise] = true unless options.key?(:raise)
__update_callbacks(name) do |target, chain|
filters.each do |filter|
- filter = chain.find {|c| c.matches?(type, filter) }
+ callback = chain.find {|c| c.matches?(type, filter) }
+
+ if !callback && options[:raise]
+ raise ArgumentError, "#{type.to_s.capitalize} #{name} callback #{filter.inspect} has not been defined"
+ end
- if filter && options.any?
- new_filter = filter.merge(chain, options)
- chain.insert(chain.index(filter), new_filter)
+ if callback && (options.key?(:if) || options.key?(:unless))
+ new_callback = callback.merge_conditional_options(chain, if_option: options[:if], unless_option: options[:unless])
+ chain.insert(chain.index(callback), new_callback)
end
- chain.delete(filter)
+ chain.delete(callback)
end
target.set_callbacks name, chain
end
@@ -659,21 +738,22 @@ module ActiveSupport
# ===== Options
#
# * <tt>:terminator</tt> - Determines when a before filter will halt the
- # callback chain, preventing following callbacks from being called and
- # the event from being triggered. This should be a lambda to be executed.
- # The current object and the return result of the callback will be called
- # with the lambda.
+ # callback chain, preventing following before and around callbacks from
+ # being called and the event from being triggered.
+ # This should be a lambda to be executed.
+ # The current object and the result lambda of the callback will be provided
+ # to the terminator lambda.
#
- # define_callbacks :validate, terminator: ->(target, result) { result == false }
+ # define_callbacks :validate, terminator: ->(target, result_lambda) { result_lambda.call == false }
#
# In this example, if any before validate callbacks returns +false+,
- # other callbacks are not executed. Defaults to +false+, meaning no value
- # halts the chain.
+ # any successive before and around callback is not executed.
+ # Defaults to +false+, meaning no value halts the chain.
#
# * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after
# callbacks should be terminated by the <tt>:terminator</tt> option. By
- # default after callbacks executed no matter if callback chain was
- # terminated or not. Option makes sense only when <tt>:terminator</tt>
+ # default after callbacks are executed no matter if callback chain was
+ # terminated or not. This option makes sense only when <tt>:terminator</tt>
# option is specified.
#
# * <tt>:scope</tt> - Indicates which methods should be executed when an
@@ -730,8 +810,8 @@ module ActiveSupport
set_callbacks name, CallbackChain.new(name, options)
module_eval <<-RUBY, __FILE__, __LINE__ + 1
- def run_#{name}_callbacks(&block)
- _run_callbacks(_#{name}_callbacks, &block)
+ def _run_#{name}_callbacks(&block)
+ __run_callbacks__(_#{name}_callbacks, &block)
end
RUBY
end
diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb
index 342d3a9d52..0403eb70ca 100644
--- a/activesupport/lib/active_support/concern.rb
+++ b/activesupport/lib/active_support/concern.rb
@@ -114,7 +114,7 @@ module ActiveSupport
return false
else
return false if base < self
- @_dependencies.each { |dep| base.send(:include, dep) }
+ @_dependencies.each { |dep| base.include(dep) }
super
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
@@ -132,7 +132,7 @@ module ActiveSupport
end
def class_methods(&class_methods_module_definition)
- mod = const_defined?(:ClassMethods) ?
+ mod = const_defined?(:ClassMethods, false) ?
const_get(:ClassMethods) :
const_set(:ClassMethods, Module.new)
diff --git a/activesupport/lib/active_support/concurrency/latch.rb b/activesupport/lib/active_support/concurrency/latch.rb
index 1507de433e..7b8df0df04 100644
--- a/activesupport/lib/active_support/concurrency/latch.rb
+++ b/activesupport/lib/active_support/concurrency/latch.rb
@@ -1,26 +1,18 @@
-require 'thread'
-require 'monitor'
+require 'concurrent/atomics'
module ActiveSupport
module Concurrency
- class Latch
- def initialize(count = 1)
- @count = count
- @lock = Monitor.new
- @cv = @lock.new_cond
- end
+ class Latch < Concurrent::CountDownLatch
- def release
- @lock.synchronize do
- @count -= 1 if @count > 0
- @cv.broadcast if @count.zero?
- end
+ def initialize(count = 1)
+ ActiveSupport::Deprecation.warn("ActiveSupport::Concurrency::Latch is deprecated. Please use Concurrent::CountDownLatch instead.")
+ super(count)
end
+
+ alias_method :release, :count_down
def await
- @lock.synchronize do
- @cv.wait_while { @count > 0 }
- end
+ wait(nil)
end
end
end
diff --git a/activesupport/lib/active_support/concurrency/share_lock.rb b/activesupport/lib/active_support/concurrency/share_lock.rb
new file mode 100644
index 0000000000..ca48164c54
--- /dev/null
+++ b/activesupport/lib/active_support/concurrency/share_lock.rb
@@ -0,0 +1,142 @@
+require 'thread'
+require 'monitor'
+
+module ActiveSupport
+ module Concurrency
+ # A share/exclusive lock, otherwise known as a read/write lock.
+ #
+ # https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock
+ #--
+ # Note that a pending Exclusive lock attempt does not block incoming
+ # Share requests (i.e., we are "read-preferring"). That seems
+ # consistent with the behavior of "loose" upgrades, but may be the
+ # wrong choice otherwise: it nominally reduces the possibility of
+ # deadlock by risking starvation instead.
+ class ShareLock
+ include MonitorMixin
+
+ # We track Thread objects, instead of just using counters, because
+ # we need exclusive locks to be reentrant, and we need to be able
+ # to upgrade share locks to exclusive.
+
+
+ def initialize
+ super()
+
+ @cv = new_cond
+
+ @sharing = Hash.new(0)
+ @waiting = {}
+ @exclusive_thread = nil
+ @exclusive_depth = 0
+ end
+
+ # Returns false if +no_wait+ is set and the lock is not
+ # immediately available. Otherwise, returns true after the lock
+ # has been acquired.
+ #
+ # +purpose+ and +compatible+ work together; while this thread is
+ # waiting for the exclusive lock, it will yield its share (if any)
+ # to any other attempt whose +purpose+ appears in this attempt's
+ # +compatible+ list. This allows a "loose" upgrade, which, being
+ # less strict, prevents some classes of deadlocks.
+ #
+ # For many resources, loose upgrades are sufficient: if a thread
+ # is awaiting a lock, it is not running any other code. With
+ # +purpose+ matching, it is possible to yield only to other
+ # threads whose activity will not interfere.
+ def start_exclusive(purpose: nil, compatible: [], no_wait: false)
+ synchronize do
+ unless @exclusive_thread == Thread.current
+ if busy?(purpose)
+ return false if no_wait
+
+ loose_shares = @sharing.delete(Thread.current)
+ @waiting[Thread.current] = compatible if loose_shares
+
+ begin
+ @cv.wait_while { busy?(purpose) }
+ ensure
+ @waiting.delete Thread.current
+ @sharing[Thread.current] = loose_shares if loose_shares
+ end
+ end
+ @exclusive_thread = Thread.current
+ end
+ @exclusive_depth += 1
+
+ true
+ end
+ end
+
+ # Relinquish the exclusive lock. Must only be called by the thread
+ # that called start_exclusive (and currently holds the lock).
+ def stop_exclusive
+ synchronize do
+ raise "invalid unlock" if @exclusive_thread != Thread.current
+
+ @exclusive_depth -= 1
+ if @exclusive_depth == 0
+ @exclusive_thread = nil
+ @cv.broadcast
+ end
+ end
+ end
+
+ def start_sharing
+ synchronize do
+ if @exclusive_thread && @exclusive_thread != Thread.current
+ @cv.wait_while { @exclusive_thread }
+ end
+ @sharing[Thread.current] += 1
+ end
+ end
+
+ def stop_sharing
+ synchronize do
+ if @sharing[Thread.current] > 1
+ @sharing[Thread.current] -= 1
+ else
+ @sharing.delete Thread.current
+ @cv.broadcast
+ end
+ end
+ end
+
+ # Execute the supplied block while holding the Exclusive lock. If
+ # +no_wait+ is set and the lock is not immediately available,
+ # returns +nil+ without yielding. Otherwise, returns the result of
+ # the block.
+ #
+ # See +start_exclusive+ for other options.
+ def exclusive(purpose: nil, compatible: [], no_wait: false)
+ if start_exclusive(purpose: purpose, compatible: compatible, no_wait: no_wait)
+ begin
+ yield
+ ensure
+ stop_exclusive
+ end
+ end
+ end
+
+ # Execute the supplied block while holding the Share lock.
+ def sharing
+ start_sharing
+ begin
+ yield
+ ensure
+ stop_sharing
+ end
+ end
+
+ private
+
+ # Must be called within synchronize
+ def busy?(purpose)
+ (@exclusive_thread && @exclusive_thread != Thread.current) ||
+ @waiting.any? { |k, v| k != Thread.current && !v.include?(purpose) } ||
+ @sharing.size > (@sharing[Thread.current] > 0 ? 1 : 0)
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb
index 3dd44e32d8..8256c325af 100644
--- a/activesupport/lib/active_support/configurable.rb
+++ b/activesupport/lib/active_support/configurable.rb
@@ -122,6 +122,7 @@ module ActiveSupport
send("#{name}=", yield) if block_given?
end
end
+ private :config_accessor
end
# Reads and writes attributes from a configuration <tt>OrderedHash</tt>.
diff --git a/activesupport/lib/active_support/core_ext.rb b/activesupport/lib/active_support/core_ext.rb
index 199aa91020..52706c3d7a 100644
--- a/activesupport/lib/active_support/core_ext.rb
+++ b/activesupport/lib/active_support/core_ext.rb
@@ -1,3 +1,4 @@
-Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].each do |path|
+DEPRECATED_FILES = ["#{File.dirname(__FILE__)}/core_ext/struct.rb"]
+(Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"] - DEPRECATED_FILES).each do |path|
require path
end
diff --git a/activesupport/lib/active_support/core_ext/array.rb b/activesupport/lib/active_support/core_ext/array.rb
index 7d0c1e4c8d..7551551bd7 100644
--- a/activesupport/lib/active_support/core_ext/array.rb
+++ b/activesupport/lib/active_support/core_ext/array.rb
@@ -4,3 +4,4 @@ require 'active_support/core_ext/array/conversions'
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/array/grouping'
require 'active_support/core_ext/array/prepend_and_append'
+require 'active_support/core_ext/array/inquiry'
diff --git a/activesupport/lib/active_support/core_ext/array/access.rb b/activesupport/lib/active_support/core_ext/array/access.rb
index caa499dfa2..3177d8498e 100644
--- a/activesupport/lib/active_support/core_ext/array/access.rb
+++ b/activesupport/lib/active_support/core_ext/array/access.rb
@@ -20,7 +20,23 @@ class Array
# %w( a b c d ).to(-2) # => ["a", "b", "c"]
# %w( a b c ).to(-10) # => []
def to(position)
- self[0..position]
+ if position >= 0
+ take position + 1
+ else
+ self[0..position]
+ end
+ end
+
+ # Returns a copy of the Array without the specified elements.
+ #
+ # people = ["David", "Rafael", "Aaron", "Todd"]
+ # people.without "Aaron", "Todd"
+ # => ["David", "Rafael"]
+ #
+ # Note: This is an optimization of `Enumerable#without` that uses `Array#-`
+ # instead of `Array#reject` for performance reasons.
+ def without(*elements)
+ self - elements
end
# Equal to <tt>self[1]</tt>.
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index 76ffd23ed1..d80df21e7d 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -74,7 +74,7 @@ class Array
when 0
''
when 1
- self[0].to_s.dup
+ "#{self[0]}"
when 2
"#{self[0]}#{options[:two_words_connector]}#{self[1]}"
else
@@ -92,7 +92,7 @@ class Array
if empty?
'null'
else
- collect { |element| element.id }.join(',')
+ collect(&:id).join(',')
end
else
to_default_s
diff --git a/activesupport/lib/active_support/core_ext/array/inquiry.rb b/activesupport/lib/active_support/core_ext/array/inquiry.rb
new file mode 100644
index 0000000000..e8f44cc378
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/array/inquiry.rb
@@ -0,0 +1,17 @@
+require 'active_support/array_inquirer'
+
+class Array
+ # Wraps the array in an +ArrayInquirer+ object, which gives a friendlier way
+ # to check its string-like contents.
+ #
+ # pets = [:cat, :dog].inquiry
+ #
+ # pets.cat? # => true
+ # pets.ferret? # => false
+ #
+ # pets.any?(:cat, :ferret) # => true
+ # pets.any?(:ferret, :alligator) # => false
+ def inquiry
+ ActiveSupport::ArrayInquirer.new(self)
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/array/wrap.rb b/activesupport/lib/active_support/core_ext/array/wrap.rb
index 152eb02218..b611d34c27 100644
--- a/activesupport/lib/active_support/core_ext/array/wrap.rb
+++ b/activesupport/lib/active_support/core_ext/array/wrap.rb
@@ -3,7 +3,7 @@ class Array
#
# Specifically:
#
- # * If the argument is +nil+ an empty list is returned.
+ # * If the argument is +nil+ an empty array is returned.
# * Otherwise, if the argument responds to +to_ary+ it is invoked, and its result returned.
# * Otherwise, returns an array with the argument as its single element.
#
@@ -15,12 +15,13 @@ 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
- # +nil+ right away.
+ # an array with the argument as its single element 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, but returns an empty array if argument is +nil+.
+ # * It does not call +to_a+ on the argument, if the argument does not respond to +to_ary+
+ # it returns an array with the argument as its single element.
#
- # The second point is easily explained with some enumerables:
+ # The last point is easily explained with some enumerables:
#
# Array(foo: :bar) # => [[:foo, :bar]]
# Array.wrap(foo: :bar) # => [{:foo=>:bar}]
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 843c592669..234283e792 100644
--- a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
@@ -3,14 +3,13 @@ require 'bigdecimal/util'
class BigDecimal
DEFAULT_STRING_FORMAT = 'F'
- def to_formatted_s(*args)
- if args[0].is_a?(Symbol)
- super
+ alias_method :to_default_s, :to_s
+
+ def to_s(format = nil, options = nil)
+ if format.is_a?(Symbol)
+ to_formatted_s(format, options || {})
else
- format = args[0] || DEFAULT_STRING_FORMAT
- _original_to_s(format)
+ to_default_s(format || DEFAULT_STRING_FORMAT)
end
end
- alias_method :_original_to_s, :to_s
- alias_method :to_s, :to_formatted_s
end
diff --git a/activesupport/lib/active_support/core_ext/big_decimal/yaml_conversions.rb b/activesupport/lib/active_support/core_ext/big_decimal/yaml_conversions.rb
deleted file mode 100644
index 46ba93ead4..0000000000
--- a/activesupport/lib/active_support/core_ext/big_decimal/yaml_conversions.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-ActiveSupport::Deprecation.warn 'core_ext/big_decimal/yaml_conversions is deprecated and will be removed in the future.'
-
-require 'bigdecimal'
-require 'yaml'
-require 'active_support/core_ext/big_decimal/conversions'
-
-class BigDecimal
- YAML_MAPPING = { 'Infinity' => '.Inf', '-Infinity' => '-.Inf', 'NaN' => '.NaN' }
-
- def encode_with(coder)
- string = to_s
- coder.represent_scalar(nil, YAML_MAPPING[string] || string)
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/class.rb b/activesupport/lib/active_support/core_ext/class.rb
index c750a10bb2..ef903d59b5 100644
--- a/activesupport/lib/active_support/core_ext/class.rb
+++ b/activesupport/lib/active_support/core_ext/class.rb
@@ -1,3 +1,2 @@
require 'active_support/core_ext/class/attribute'
-require 'active_support/core_ext/class/delegating_attributes'
require 'active_support/core_ext/class/subclasses'
diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb
index f2a221c396..f2b7bb3ef1 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute.rb
@@ -116,12 +116,4 @@ class Class
attr_writer name if instance_writer
end
end
-
- private
-
- unless respond_to?(:singleton_class?)
- def singleton_class?
- ancestors.first != self
- end
- end
end
diff --git a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
deleted file mode 100644
index 1c305c5970..0000000000
--- a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-require 'active_support/core_ext/kernel/singleton_class'
-require 'active_support/core_ext/module/remove_method'
-require 'active_support/core_ext/module/deprecation'
-
-
-class Class
- def superclass_delegating_accessor(name, options = {})
- # Create private _name and _name= methods that can still be used if the public
- # methods are overridden.
- _superclass_delegating_accessor("_#{name}", options)
-
- # Generate the public methods name, name=, and name?.
- # These methods dispatch to the private _name, and _name= methods, making them
- # overridable.
- singleton_class.send(:define_method, name) { send("_#{name}") }
- singleton_class.send(:define_method, "#{name}?") { !!send("_#{name}") }
- singleton_class.send(:define_method, "#{name}=") { |value| send("_#{name}=", value) }
-
- # If an instance_reader is needed, generate public instance methods name and name?.
- if options[:instance_reader] != false
- define_method(name) { send("_#{name}") }
- define_method("#{name}?") { !!send("#{name}") }
- end
- end
-
- deprecate superclass_delegating_accessor: :class_attribute
-
- private
- # Take the object being set and store it in a method. This gives us automatic
- # inheritance behavior, without having to store the object in an instance
- # variable and look up the superclass chain manually.
- def _stash_object_in_method(object, method, instance_reader = true)
- singleton_class.remove_possible_method(method)
- singleton_class.send(:define_method, method) { object }
- remove_possible_method(method)
- define_method(method) { object } if instance_reader
- end
-
- def _superclass_delegating_accessor(name, options = {})
- singleton_class.send(:define_method, "#{name}=") do |value|
- _stash_object_in_method(value, name, options[:instance_reader] != false)
- end
- send("#{name}=", nil)
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb
index df419a6e63..31479a1269 100644
--- a/activesupport/lib/active_support/core_ext/date/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date/conversions.rb
@@ -35,6 +35,7 @@ class Date
# date.to_s(:db) # => "2007-11-10"
#
# date.to_formatted_s(:short) # => "10 Nov"
+ # date.to_formatted_s(:number) # => "20071110"
# date.to_formatted_s(:long) # => "November 10, 2007"
# date.to_formatted_s(:long_ordinal) # => "November 10th, 2007"
# date.to_formatted_s(:rfc822) # => "10 Nov 2007"
@@ -82,6 +83,11 @@ class Date
::Time.send(form, year, month, day)
end
+ # Returns a string which represents the time in used time zone as DateTime
+ # defined by XML Schema:
+ #
+ # date = Date.new(2015, 05, 23) # => Sat, 23 May 2015
+ # date.xmlschema # => "2015-05-23T00:00:00+04:00"
def xmlschema
in_time_zone.xmlschema
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 b85e49aca5..40811dafc0 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
@@ -9,15 +9,26 @@ module DateAndTime
:saturday => 5,
:sunday => 6
}
+ WEEKEND_DAYS = [ 6, 0 ]
# Returns a new date/time representing yesterday.
def yesterday
- advance(:days => -1)
+ advance(days: -1)
+ end
+
+ # Returns a new date/time representing the previous day.
+ def prev_day
+ advance(days: -1)
end
# Returns a new date/time representing tomorrow.
def tomorrow
- advance(:days => 1)
+ advance(days: 1)
+ end
+
+ # Returns a new date/time representing the next day.
+ def next_day
+ advance(days: 1)
end
# Returns true if the date/time is today.
@@ -35,6 +46,11 @@ module DateAndTime
self > self.class.current
end
+ # Returns true if the date/time falls on a Saturday or Sunday.
+ def on_weekend?
+ WEEKEND_DAYS.include?(wday)
+ end
+
# Returns a new date/time the specified number of days ago.
def days_ago(days)
advance(:days => -days)
@@ -76,15 +92,28 @@ module DateAndTime
end
# Returns a new date/time at the start of the month.
- # DateTime objects will have a time set to 0:00.
+ #
+ # today = Date.today # => Thu, 18 Jun 2015
+ # today.beginning_of_month # => Mon, 01 Jun 2015
+ #
+ # +DateTime+ objects will have a time set to 0:00.
+ #
+ # now = DateTime.current # => Thu, 18 Jun 2015 15:23:13 +0000
+ # now.beginning_of_month # => Mon, 01 Jun 2015 00:00:00 +0000
def beginning_of_month
first_hour(change(:day => 1))
end
alias :at_beginning_of_month :beginning_of_month
# Returns a new date/time at the start of the quarter.
- # Example: 1st January, 1st July, 1st October.
- # DateTime objects will have a time set to 0:00.
+ #
+ # today = Date.today # => Fri, 10 Jul 2015
+ # today.beginning_of_quarter # => Wed, 01 Jul 2015
+ #
+ # +DateTime+ objects will have a time set to 0:00.
+ #
+ # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
+ # now.beginning_of_quarter # => Wed, 01 Jul 2015 00:00:00 +0000
def beginning_of_quarter
first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month }
beginning_of_month.change(:month => first_quarter_month)
@@ -92,8 +121,14 @@ module DateAndTime
alias :at_beginning_of_quarter :beginning_of_quarter
# 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.
+ #
+ # today = Date.today # => Fri, 10 Jul 2015
+ # today.end_of_quarter # => Wed, 30 Sep 2015
+ #
+ # +DateTime+ objects will have a time set to 23:59:59.
+ #
+ # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
+ # now.end_of_quarter # => Wed, 30 Sep 2015 23:59:59 +0000
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
@@ -101,19 +136,47 @@ module DateAndTime
alias :at_end_of_quarter :end_of_quarter
# Return a new date/time at the beginning of the year.
- # Example: 1st January.
- # DateTime objects will have a time set to 0:00.
+ #
+ # today = Date.today # => Fri, 10 Jul 2015
+ # today.beginning_of_year # => Thu, 01 Jan 2015
+ #
+ # +DateTime+ objects will have a time set to 0:00.
+ #
+ # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
+ # now.beginning_of_year # => Thu, 01 Jan 2015 00:00:00 +0000
def beginning_of_year
change(:month => 1).beginning_of_month
end
alias :at_beginning_of_year :beginning_of_year
# Returns a new date/time representing the given day in the next week.
+ #
+ # today = Date.today # => Thu, 07 May 2015
+ # today.next_week # => Mon, 11 May 2015
+ #
# 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)))
+ # when set.
+ #
+ # today = Date.today # => Thu, 07 May 2015
+ # today.next_week(:friday) # => Fri, 15 May 2015
+ #
+ # +DateTime+ objects have their time set to 0:00 unless +same_time+ is true.
+ #
+ # now = DateTime.current # => Thu, 07 May 2015 13:31:16 +0000
+ # now.next_week # => Mon, 11 May 2015 00:00:00 +0000
+ def next_week(given_day_in_next_week = Date.beginning_of_week, same_time: false)
+ result = first_hour(weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week)))
+ same_time ? copy_time_to(result) : result
+ end
+
+ # Returns a new date/time representing the next weekday.
+ def next_weekday
+ if next_day.on_weekend?
+ next_week(:monday, same_time: true)
+ else
+ next_day
+ end
end
# Short-hand for months_since(1).
@@ -134,12 +197,23 @@ module DateAndTime
# Returns a new date/time representing the given day in the previous 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 prev_week(start_day = Date.beginning_of_week)
- first_hour(weeks_ago(1).beginning_of_week.days_since(days_span(start_day)))
+ # DateTime objects have their time set to 0:00 unless +same_time+ is true.
+ def prev_week(start_day = Date.beginning_of_week, same_time: false)
+ result = first_hour(weeks_ago(1).beginning_of_week.days_since(days_span(start_day)))
+ same_time ? copy_time_to(result) : result
end
alias_method :last_week, :prev_week
+ # Returns a new date/time representing the previous weekday.
+ def prev_weekday
+ if prev_day.on_weekend?
+ copy_time_to(beginning_of_week(:friday))
+ else
+ prev_day
+ end
+ end
+ alias_method :last_weekday, :prev_weekday
+
# Short-hand for months_ago(1).
def prev_month
months_ago(1)
@@ -235,17 +309,20 @@ module DateAndTime
end
private
+ def first_hour(date_or_time)
+ date_or_time.acts_like?(:time) ? date_or_time.beginning_of_day : date_or_time
+ end
- def first_hour(date_or_time)
- date_or_time.acts_like?(:time) ? date_or_time.beginning_of_day : date_or_time
- end
+ def last_hour(date_or_time)
+ date_or_time.acts_like?(:time) ? date_or_time.end_of_day : date_or_time
+ end
- def last_hour(date_or_time)
- date_or_time.acts_like?(:time) ? date_or_time.end_of_day : date_or_time
- end
+ def days_span(day)
+ (DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
+ end
- def days_span(day)
- (DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
- end
+ def copy_time_to(other)
+ other.change(hour: hour, min: min, sec: sec, usec: try(:usec))
+ end
end
end
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 dc4e767e9d..95617fb8c2 100644
--- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
@@ -10,7 +10,11 @@ class DateTime
end
end
- # Seconds since midnight: DateTime.now.seconds_since_midnight.
+ # Returns the number of seconds since 00:00:00.
+ #
+ # DateTime.new(2012, 8, 29, 0, 0, 0).seconds_since_midnight # => 0
+ # DateTime.new(2012, 8, 29, 12, 34, 56).seconds_since_midnight # => 45296
+ # DateTime.new(2012, 8, 29, 23, 59, 59).seconds_since_midnight # => 86399
def seconds_since_midnight
sec + (min * 60) + (hour * 3600)
end
@@ -164,7 +168,7 @@ class DateTime
if other.kind_of?(Infinity)
super
elsif other.respond_to? :to_datetime
- super other.to_datetime
+ super other.to_datetime rescue nil
else
nil
end
diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb
index 1343beb87a..fc7531d088 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -60,6 +60,32 @@ module Enumerable
def exclude?(object)
!include?(object)
end
+
+ # Returns a copy of the enumerable without the specified elements.
+ #
+ # ["David", "Rafael", "Aaron", "Todd"].without "Aaron", "Todd"
+ # => ["David", "Rafael"]
+ #
+ # {foo: 1, bar: 2, baz: 3}.without :bar
+ # => {foo: 1, baz: 3}
+ def without(*elements)
+ reject { |element| elements.include?(element) }
+ end
+
+ # Convert an enumerable to an array based on the given key.
+ #
+ # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
+ # => ["David", "Rafael", "Aaron"]
+ #
+ # [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name)
+ # => [[1, "David"], [2, "Rafael"]]
+ def pluck(*keys)
+ if keys.many?
+ map { |element| keys.map { |key| element[key] } }
+ else
+ map { |element| element[keys.first] }
+ end
+ end
end
class Range #:nodoc:
diff --git a/activesupport/lib/active_support/core_ext/file/atomic.rb b/activesupport/lib/active_support/core_ext/file/atomic.rb
index 38374af388..ab635f6db8 100644
--- a/activesupport/lib/active_support/core_ext/file/atomic.rb
+++ b/activesupport/lib/active_support/core_ext/file/atomic.rb
@@ -8,40 +8,45 @@ class File
# file.write('hello')
# end
#
- # If your temp directory is not on the same filesystem as the file you're
- # trying to write, you can provide a different temporary directory.
+ # This method needs to create a temporary file. By default it will create it
+ # in the same directory as the destination file. If you don't like this
+ # behaviour you can provide a different directory but it must be on the
+ # same physical filesystem as the the file you're trying to write.
#
# File.atomic_write('/data/something.important', '/data/tmp') do |file|
# file.write('hello')
# end
- def self.atomic_write(file_name, temp_dir = Dir.tmpdir)
+ def self.atomic_write(file_name, temp_dir = dirname(file_name))
require 'tempfile' unless defined?(Tempfile)
- require 'fileutils' unless defined?(FileUtils)
- temp_file = Tempfile.new(basename(file_name), temp_dir)
- temp_file.binmode
- yield temp_file
- temp_file.close
+ Tempfile.open(".#{basename(file_name)}", temp_dir) do |temp_file|
+ temp_file.binmode
+ return_val = yield temp_file
+ temp_file.close
- if File.exist?(file_name)
- # Get original file permissions
- old_stat = stat(file_name)
- else
- # If not possible, probe which are the default permissions in the
- # destination directory.
- old_stat = probe_stat_in(dirname(file_name))
- end
+ old_stat = if exist?(file_name)
+ # Get original file permissions
+ stat(file_name)
+ elsif temp_dir != dirname(file_name)
+ # If not possible, probe which are the default permissions in the
+ # destination directory.
+ probe_stat_in(dirname(file_name))
+ end
- # Overwrite original file with temp file
- FileUtils.mv(temp_file.path, file_name)
+ if old_stat
+ # Set correct permissions on new file
+ begin
+ chown(old_stat.uid, old_stat.gid, temp_file.path)
+ # This operation will affect filesystem ACL's
+ chmod(old_stat.mode, temp_file.path)
+ rescue Errno::EPERM, Errno::EACCES
+ # Changing file ownership failed, moving on.
+ end
+ end
- # Set correct permissions on new file
- begin
- chown(old_stat.uid, old_stat.gid, file_name)
- # This operation will affect filesystem ACL's
- chmod(old_stat.mode, file_name)
- rescue Errno::EPERM, Errno::EACCES
- # Changing file ownership failed, moving on.
+ # Overwrite original file with temp file
+ rename(temp_file.path, file_name)
+ return_val
end
end
diff --git a/activesupport/lib/active_support/core_ext/hash/compact.rb b/activesupport/lib/active_support/core_ext/hash/compact.rb
index 6566215a4d..5dc9a05ec7 100644
--- a/activesupport/lib/active_support/core_ext/hash/compact.rb
+++ b/activesupport/lib/active_support/core_ext/hash/compact.rb
@@ -1,6 +1,6 @@
class Hash
# Returns a hash with non +nil+ values.
- #
+ #
# hash = { a: true, b: false, c: nil}
# hash.compact # => { a: true, b: false}
# hash # => { a: true, b: false, c: nil}
@@ -8,9 +8,9 @@ class Hash
def compact
self.select { |_, value| !value.nil? }
end
-
+
# Replaces current hash with non +nil+ values.
- #
+ #
# hash = { a: true, b: false, c: nil}
# hash.compact! # => { a: true, b: false}
# hash # => { a: true, b: false}
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb
index 2149d4439d..8594d9bf2e 100644
--- a/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -106,7 +106,25 @@ class Hash
# # => {"hash"=>{"foo"=>1, "bar"=>2}}
#
# +DisallowedType+ is raised if the XML contains attributes with <tt>type="yaml"</tt> or
- # <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to parse this XML.
+ # <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to
+ # parse this XML.
+ #
+ # Custom +disallowed_types+ can also be passed in the form of an
+ # array.
+ #
+ # xml = <<-XML
+ # <?xml version="1.0" encoding="UTF-8"?>
+ # <hash>
+ # <foo type="integer">1</foo>
+ # <bar type="string">"David"</bar>
+ # </hash>
+ # XML
+ #
+ # hash = Hash.from_xml(xml, ['integer'])
+ # # => ActiveSupport::XMLConverter::DisallowedType: Disallowed type attribute: "integer"
+ #
+ # Note that passing custom disallowed types will override the default types,
+ # which are Symbol and YAML.
def from_xml(xml, disallowed_types = nil)
ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h
end
diff --git a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
index 763d563231..9c9faf67ea 100644
--- a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
+++ b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
@@ -4,7 +4,7 @@ class Hash
# h1 = { a: true, b: { c: [1, 2, 3] } }
# h2 = { a: false, b: { x: [3, 4, 5] } }
#
- # h1.deep_merge(h2) #=> { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
+ # h1.deep_merge(h2) # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
#
# Like with Hash#merge in the standard library, a block can be provided
# to merge values:
diff --git a/activesupport/lib/active_support/core_ext/hash/except.rb b/activesupport/lib/active_support/core_ext/hash/except.rb
index 682d089881..2f6d38c1f6 100644
--- a/activesupport/lib/active_support/core_ext/hash/except.rb
+++ b/activesupport/lib/active_support/core_ext/hash/except.rb
@@ -1,13 +1,20 @@
class Hash
- # Returns a hash that includes everything but the given keys. This is useful for
- # limiting a set of parameters to everything but a few known toggles:
+ # Returns a hash that includes everything except given keys.
+ # hash = { a: true, b: false, c: nil }
+ # hash.except(:c) # => { a: true, b: false }
+ # hash.except(:a, :b) # => { c: nil }
+ # hash # => { a: true, b: false, c: nil }
#
+ # This is useful for limiting a set of parameters to everything but a few known toggles:
# @person.update(params[:person].except(:admin))
def except(*keys)
dup.except!(*keys)
end
- # Replaces the hash without the given keys.
+ # Removes the given keys from hash and returns it.
+ # hash = { a: true, b: false, c: nil }
+ # hash.except!(:c) # => { a: true, b: false }
+ # hash # => { a: true, b: false }
def except!(*keys)
keys.each { |key| delete(key) }
self
diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb
index f4105f66b0..c30044b9ff 100644
--- a/activesupport/lib/active_support/core_ext/hash/keys.rb
+++ b/activesupport/lib/active_support/core_ext/hash/keys.rb
@@ -14,7 +14,7 @@ class Hash
result
end
- # Destructively convert all keys using the block operations.
+ # Destructively converts all keys using the block operations.
# Same as transform_keys but modifies +self+.
def transform_keys!
return enum_for(:transform_keys!) unless block_given?
@@ -31,13 +31,13 @@ class Hash
# hash.stringify_keys
# # => {"name"=>"Rob", "age"=>"28"}
def stringify_keys
- transform_keys{ |key| key.to_s }
+ transform_keys(&:to_s)
end
- # Destructively convert all keys to strings. Same as
+ # Destructively converts all keys to strings. Same as
# +stringify_keys+, but modifies +self+.
def stringify_keys!
- transform_keys!{ |key| key.to_s }
+ transform_keys!(&:to_s)
end
# Returns a new hash with all keys converted to symbols, as long as
@@ -52,14 +52,14 @@ class Hash
end
alias_method :to_options, :symbolize_keys
- # Destructively convert all keys to symbols, as long as they respond
+ # Destructively converts all keys to symbols, as long as they respond
# to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
def symbolize_keys!
transform_keys!{ |key| key.to_sym rescue key }
end
alias_method :to_options!, :symbolize_keys!
- # Validate all keys in a hash match <tt>*valid_keys</tt>, raising
+ # Validates all keys in a hash match <tt>*valid_keys</tt>, raising
# ArgumentError on a mismatch.
#
# Note that keys are treated differently than HashWithIndifferentAccess,
@@ -89,7 +89,7 @@ class Hash
_deep_transform_keys_in_object(self, &block)
end
- # Destructively convert all keys by using the block operation.
+ # Destructively converts all keys by using the block operation.
# This includes the keys from the root hash and from all
# nested hashes and arrays.
def deep_transform_keys!(&block)
@@ -105,14 +105,14 @@ class Hash
# hash.deep_stringify_keys
# # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
def deep_stringify_keys
- deep_transform_keys{ |key| key.to_s }
+ deep_transform_keys(&:to_s)
end
- # Destructively convert all keys to strings.
+ # Destructively converts all keys to strings.
# This includes the keys from the root hash and from all
# nested hashes and arrays.
def deep_stringify_keys!
- deep_transform_keys!{ |key| key.to_s }
+ deep_transform_keys!(&:to_s)
end
# Returns a new hash with all keys converted to symbols, as long as
@@ -127,7 +127,7 @@ class Hash
deep_transform_keys{ |key| key.to_sym rescue key }
end
- # Destructively convert all keys to symbols, as long as they respond
+ # Destructively converts all keys to symbols, as long as they respond
# to +to_sym+. This includes the keys from the root hash and from all
# nested hashes and arrays.
def deep_symbolize_keys!
diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb
index 8ad600b171..1d5f38231a 100644
--- a/activesupport/lib/active_support/core_ext/hash/slice.rb
+++ b/activesupport/lib/active_support/core_ext/hash/slice.rb
@@ -1,6 +1,12 @@
class Hash
- # Slice a hash to include only the given keys. This is useful for
- # limiting an options hash to valid keys before passing to a method:
+ # Slices a hash to include only the given keys. Returns a hash containing
+ # the given keys.
+ #
+ # { a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b)
+ # # => {:a=>1, :b=>2}
+ #
+ # This is useful for limiting an options hash to valid keys before
+ # passing to a method:
#
# def search(criteria = {})
# criteria.assert_valid_keys(:mass, :velocity, :time)
diff --git a/activesupport/lib/active_support/core_ext/integer/time.rb b/activesupport/lib/active_support/core_ext/integer/time.rb
index 82080ffe51..f0b7382ef3 100644
--- a/activesupport/lib/active_support/core_ext/integer/time.rb
+++ b/activesupport/lib/active_support/core_ext/integer/time.rb
@@ -17,21 +17,6 @@ class Integer
#
# # equivalent to Time.now.advance(months: 4, years: 5)
# (4.months + 5.years).from_now
- #
- # While these methods provide precise calculation when used as in the examples
- # above, care should be taken to note that this is not true if the result of
- # +months+, +years+, etc is converted before use:
- #
- # # equivalent to 30.days.to_i.from_now
- # 1.month.to_i.from_now
- #
- # # equivalent to 365.25.days.to_f.from_now
- # 1.year.to_f.from_now
- #
- # In such cases, Ruby's core
- # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
- # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
- # date and time arithmetic.
def months
ActiveSupport::Duration.new(self * 30.days, [[:months, self]])
end
diff --git a/activesupport/lib/active_support/core_ext/kernel.rb b/activesupport/lib/active_support/core_ext/kernel.rb
index 293a3b2619..364ed9d65f 100644
--- a/activesupport/lib/active_support/core_ext/kernel.rb
+++ b/activesupport/lib/active_support/core_ext/kernel.rb
@@ -1,5 +1,4 @@
require 'active_support/core_ext/kernel/agnostics'
require 'active_support/core_ext/kernel/concern'
-require 'active_support/core_ext/kernel/debugger' if RUBY_VERSION < '2.0.0'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/kernel/singleton_class'
diff --git a/activesupport/lib/active_support/core_ext/kernel/debugger.rb b/activesupport/lib/active_support/core_ext/kernel/debugger.rb
index 2073cac98d..1fde3db070 100644
--- a/activesupport/lib/active_support/core_ext/kernel/debugger.rb
+++ b/activesupport/lib/active_support/core_ext/kernel/debugger.rb
@@ -1,10 +1,3 @@
-module Kernel
- unless respond_to?(:debugger)
- # Starts a debugging session if the +debugger+ gem has been loaded (call rails server --debugger to do load it).
- def debugger
- message = "\n***** Debugger requested, but was not available (ensure the debugger gem is listed in Gemfile/installed as gem): Start server with --debugger to enable *****\n"
- defined?(Rails) ? Rails.logger.info(message) : $stderr.puts(message)
- end
- alias breakpoint debugger unless respond_to?(:breakpoint)
- end
-end
+require 'active_support/deprecation'
+
+ActiveSupport::Deprecation.warn("This file is deprecated and will be removed in Rails 5.1 with no replacement.")
diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb
index 80c531b694..8afc258df8 100644
--- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb
+++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb
@@ -1,6 +1,3 @@
-require 'rbconfig'
-require 'tempfile'
-
module Kernel
# Sets $VERBOSE to nil for the duration of the block and back to its original
# value afterwards.
@@ -29,34 +26,6 @@ module Kernel
$VERBOSE = old_verbose
end
- # For compatibility
- def silence_stderr #:nodoc:
- ActiveSupport::Deprecation.warn(
- "#silence_stderr is deprecated and will be removed in the next release"
- ) #not thread-safe
- silence_stream(STDERR) { yield }
- end
-
- # Deprecated : this method is not thread safe
- # Silences any stream for the duration of the block.
- #
- # silence_stream(STDOUT) do
- # puts 'This will never be seen'
- # end
- #
- # puts 'But this will'
- #
- # This method is not thread-safe.
- def silence_stream(stream)
- old_stream = stream.dup
- stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
- stream.sync = true
- yield
- ensure
- stream.reopen(old_stream)
- old_stream.close
- end
-
# Blocks and ignores any exception passed as argument if raised within the block.
#
# suppress(ZeroDivisionError) do
@@ -69,56 +38,4 @@ module Kernel
yield
rescue *exception_classes
end
-
- # Captures the given stream and returns it:
- #
- # stream = capture(:stdout) { puts 'notice' }
- # stream # => "notice\n"
- #
- # stream = capture(:stderr) { warn 'error' }
- # stream # => "error\n"
- #
- # even for subprocesses:
- #
- # stream = capture(:stdout) { system('echo notice') }
- # stream # => "notice\n"
- #
- # stream = capture(:stderr) { system('echo error 1>&2') }
- # stream # => "error\n"
- def capture(stream)
- ActiveSupport::Deprecation.warn(
- "#capture(stream) is deprecated and will be removed in the next release"
- ) #not thread-safe
- stream = stream.to_s
- captured_stream = Tempfile.new(stream)
- stream_io = eval("$#{stream}")
- origin_stream = stream_io.dup
- stream_io.reopen(captured_stream)
-
- yield
-
- stream_io.rewind
- return captured_stream.read
- ensure
- captured_stream.close
- captured_stream.unlink
- stream_io.reopen(origin_stream)
- end
- alias :silence :capture
-
- # Silences both STDOUT and STDERR, even for subprocesses.
- #
- # quietly { system 'bundle install' }
- #
- # This method is not thread-safe.
- def quietly
- ActiveSupport::Deprecation.warn(
- "#quietly is deprecated and will be removed in the next release"
- ) #not thread-safe
- silence_stream(STDOUT) do
- silence_stream(STDERR) do
- yield
- end
- end
- end
end
diff --git a/activesupport/lib/active_support/core_ext/load_error.rb b/activesupport/lib/active_support/core_ext/load_error.rb
index 768b980f21..60732eb41a 100644
--- a/activesupport/lib/active_support/core_ext/load_error.rb
+++ b/activesupport/lib/active_support/core_ext/load_error.rb
@@ -1,3 +1,5 @@
+require 'active_support/deprecation/proxy_wrappers'
+
class LoadError
REGEXPS = [
/^no such file to load -- (.+)$/i,
@@ -21,8 +23,8 @@ class LoadError
# Returns true if the given path name (except perhaps for the ".rb"
# extension) is the missing file which caused the exception to be raised.
def is_missing?(location)
- location.sub(/\.rb$/, '') == path.sub(/\.rb$/, '')
+ location.sub(/\.rb$/, ''.freeze) == path.sub(/\.rb$/, ''.freeze)
end
end
-MissingSourceFile = LoadError
+MissingSourceFile = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('MissingSourceFile', 'LoadError')
diff --git a/activesupport/lib/active_support/core_ext/marshal.rb b/activesupport/lib/active_support/core_ext/marshal.rb
index 56c79c04bd..20a0856e71 100644
--- a/activesupport/lib/active_support/core_ext/marshal.rb
+++ b/activesupport/lib/active_support/core_ext/marshal.rb
@@ -1,9 +1,7 @@
-require 'active_support/core_ext/module/aliasing'
-
-module Marshal
- class << self
- def load_with_autoloading(source)
- load_without_autoloading(source)
+module ActiveSupport
+ module MarshalWithAutoloading # :nodoc:
+ def load(source)
+ super(source)
rescue ArgumentError, NameError => exc
if exc.message.match(%r|undefined class/module (.+)|)
# try loading the class/module
@@ -15,7 +13,7 @@ module Marshal
raise exc
end
end
-
- alias_method_chain :load, :autoloading
end
end
+
+Marshal.singleton_class.prepend(ActiveSupport::MarshalWithAutoloading)
diff --git a/activesupport/lib/active_support/core_ext/module/aliasing.rb b/activesupport/lib/active_support/core_ext/module/aliasing.rb
index 0a6fadf928..b6934b9c54 100644
--- a/activesupport/lib/active_support/core_ext/module/aliasing.rb
+++ b/activesupport/lib/active_support/core_ext/module/aliasing.rb
@@ -1,4 +1,7 @@
class Module
+ # NOTE: This method is deprecated. Please use <tt>Module#prepend</tt> that
+ # comes with Ruby 2.0 or newer instead.
+ #
# Encapsulates the common pattern of:
#
# alias_method :foo_without_feature, :foo
@@ -21,6 +24,8 @@ class Module
#
# so you can safely chain foo, foo?, foo! and/or foo= with the same feature.
def alias_method_chain(target, feature)
+ ActiveSupport::Deprecation.warn("alias_method_chain is deprecated. Please, use Module#prepend instead. From module, you can access the original method using super.")
+
# Strip out punctuation on predicates, bang or writer methods since
# e.g. target?_without_feature is not a valid method name.
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
@@ -43,7 +48,7 @@ class Module
end
# Allows you to make aliases for attributes, which includes
- # getter, setter, and query methods.
+ # getter, setter, and a predicate.
#
# class Content < ActiveRecord::Base
# # has a title attribute
diff --git a/activesupport/lib/active_support/core_ext/module/anonymous.rb b/activesupport/lib/active_support/core_ext/module/anonymous.rb
index b0c7b021db..0ecc67a855 100644
--- a/activesupport/lib/active_support/core_ext/module/anonymous.rb
+++ b/activesupport/lib/active_support/core_ext/module/anonymous.rb
@@ -7,6 +7,13 @@ class Module
# m = Module.new
# m.name # => nil
#
+ # +anonymous?+ method returns true if module does not have a name:
+ #
+ # Module.new.anonymous? # => true
+ #
+ # module M; end
+ # M.anonymous? # => false
+ #
# A module gets a name when it is first assigned to a constant. Either
# via the +module+ or +class+ keyword or by an explicit assignment:
#
diff --git a/activesupport/lib/active_support/core_ext/module/attr_internal.rb b/activesupport/lib/active_support/core_ext/module/attr_internal.rb
index 67f0e0335d..93fb598650 100644
--- a/activesupport/lib/active_support/core_ext/module/attr_internal.rb
+++ b/activesupport/lib/active_support/core_ext/module/attr_internal.rb
@@ -27,11 +27,8 @@ class Module
def attr_internal_define(attr_name, type)
internal_name = attr_internal_ivar_name(attr_name).sub(/\A@/, '')
- # class_eval is necessary on 1.9 or else the methods are made private
- class_eval do
- # use native attr_* methods as they are faster on some Ruby implementations
- send("attr_#{type}", internal_name)
- end
+ # use native attr_* methods as they are faster on some Ruby implementations
+ send("attr_#{type}", internal_name)
attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
alias_method attr_name, internal_name
remove_method internal_name
diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
index d317df5079..a77da573fe 100644
--- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
@@ -203,10 +203,10 @@ class Module
# include HairColors
# end
#
- # Person.class_variable_get("@@hair_colors") #=> [:brown, :black, :blonde, :red]
+ # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
def mattr_accessor(*syms, &blk)
mattr_reader(*syms, &blk)
- mattr_writer(*syms, &blk)
+ mattr_writer(*syms)
end
alias :cattr_accessor :mattr_accessor
end
diff --git a/activesupport/lib/active_support/core_ext/module/concerning.rb b/activesupport/lib/active_support/core_ext/module/concerning.rb
index 07a392404e..65b88b9bbd 100644
--- a/activesupport/lib/active_support/core_ext/module/concerning.rb
+++ b/activesupport/lib/active_support/core_ext/module/concerning.rb
@@ -63,10 +63,10 @@ class Module
#
# == Mix-in noise exiled to its own file:
#
- # Once our chunk of behavior starts pushing the scroll-to-understand it's
+ # Once our chunk of behavior starts pushing the scroll-to-understand-it
# boundary, we give in and move it to a separate file. At this size, the
- # overhead feels in good proportion to the size of our extraction, despite
- # diluting our at-a-glance sense of how things really work.
+ # increased overhead can be a reasonable tradeoff even if it reduces our
+ # at-a-glance perception of how things work.
#
# class Todo
# # Other todo implementation
@@ -99,7 +99,7 @@ class Module
# end
#
# Todo.ancestors
- # # => Todo, Todo::EventTracking, Object
+ # # => [Todo, Todo::EventTracking, Object]
#
# This small step has some wonderful ripple effects. We can
# * grok the behavior of our class in one glance,
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index 570585b89a..9dc0dee1bd 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -17,7 +17,7 @@ class Module
# ==== Options
# * <tt>:to</tt> - Specifies the target object
# * <tt>:prefix</tt> - Prefixes the new method with the target name or a custom prefix
- # * <tt>:allow_nil</tt> - if set to true, prevents a +NoMethodError+ to be raised
+ # * <tt>:allow_nil</tt> - if set to true, prevents a +NoMethodError+ from being raised
#
# 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
@@ -167,7 +167,7 @@ class Module
''
end
- file, line = caller.first.split(':', 2)
+ file, line = caller(1, 1).first.split(':'.freeze, 2)
line = line.to_i
to = to.to_s
@@ -185,19 +185,31 @@ class Module
# On the other hand it could be that the target has side-effects,
# whereas conceptually, from the user point of view, the delegator should
# be doing one call.
-
- exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
-
- method_def = [
- "def #{method_prefix}#{method}(#{definition})",
- " _ = #{to}",
- " if !_.nil? || nil.respond_to?(:#{method})",
- " _.#{method}(#{definition})",
- " else",
- " #{exception unless allow_nil}",
- " end",
+ if allow_nil
+ method_def = [
+ "def #{method_prefix}#{method}(#{definition})",
+ "_ = #{to}",
+ "if !_.nil? || nil.respond_to?(:#{method})",
+ " _.#{method}(#{definition})",
+ "end",
"end"
- ].join ';'
+ ].join ';'
+ else
+ exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
+
+ method_def = [
+ "def #{method_prefix}#{method}(#{definition})",
+ " _ = #{to}",
+ " _.#{method}(#{definition})",
+ "rescue NoMethodError => e",
+ " if _.nil? && e.name == :#{method}",
+ " #{exception}",
+ " else",
+ " raise",
+ " end",
+ "end"
+ ].join ';'
+ end
module_eval(method_def, file, line)
end
diff --git a/activesupport/lib/active_support/core_ext/module/method_transplanting.rb b/activesupport/lib/active_support/core_ext/module/method_transplanting.rb
index b1097cc83b..1fde3db070 100644
--- a/activesupport/lib/active_support/core_ext/module/method_transplanting.rb
+++ b/activesupport/lib/active_support/core_ext/module/method_transplanting.rb
@@ -1,11 +1,3 @@
-class Module
- ###
- # TODO: remove this after 1.9 support is dropped
- def methods_transplantable? # :nodoc:
- x = Module.new { def foo; end }
- Module.new { define_method :bar, x.instance_method(:foo) }
- true
- rescue TypeError
- false
- end
-end
+require 'active_support/deprecation'
+
+ActiveSupport::Deprecation.warn("This file is deprecated and will be removed in Rails 5.1 with no replacement.")
diff --git a/activesupport/lib/active_support/core_ext/module/remove_method.rb b/activesupport/lib/active_support/core_ext/module/remove_method.rb
index 719071d1c2..52632d2c6b 100644
--- a/activesupport/lib/active_support/core_ext/module/remove_method.rb
+++ b/activesupport/lib/active_support/core_ext/module/remove_method.rb
@@ -1,10 +1,13 @@
class Module
+ # Removes the named method, if it exists.
def remove_possible_method(method)
if method_defined?(method) || private_method_defined?(method)
undef_method(method)
end
end
+ # Replaces the existing method definition, if there is one, with the passed
+ # block as its body.
def redefine_method(method, &block)
remove_possible_method(method)
define_method(method, &block)
diff --git a/activesupport/lib/active_support/core_ext/name_error.rb b/activesupport/lib/active_support/core_ext/name_error.rb
index e1ebd4f91c..6b447d772b 100644
--- a/activesupport/lib/active_support/core_ext/name_error.rb
+++ b/activesupport/lib/active_support/core_ext/name_error.rb
@@ -1,5 +1,12 @@
class NameError
# Extract the name of the missing constant from the exception message.
+ #
+ # begin
+ # HelloWorld
+ # rescue NameError => e
+ # e.missing_name
+ # end
+ # # => "HelloWorld"
def missing_name
if /undefined local variable or method/ !~ message
$1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
@@ -7,10 +14,16 @@ class NameError
end
# Was this exception raised because the given name was missing?
+ #
+ # begin
+ # HelloWorld
+ # rescue NameError => e
+ # e.missing_name?("HelloWorld")
+ # end
+ # # => true
def missing_name?(name)
if name.is_a? Symbol
- last_name = (missing_name || '').split('::').last
- last_name == name.to_s
+ self.name == name
else
missing_name == name.to_s
end
diff --git a/activesupport/lib/active_support/core_ext/numeric.rb b/activesupport/lib/active_support/core_ext/numeric.rb
index a6bc0624be..bcdc3eace2 100644
--- a/activesupport/lib/active_support/core_ext/numeric.rb
+++ b/activesupport/lib/active_support/core_ext/numeric.rb
@@ -1,3 +1,4 @@
require 'active_support/core_ext/numeric/bytes'
require 'active_support/core_ext/numeric/time'
+require 'active_support/core_ext/numeric/inquiry'
require 'active_support/core_ext/numeric/conversions'
diff --git a/activesupport/lib/active_support/core_ext/numeric/bytes.rb b/activesupport/lib/active_support/core_ext/numeric/bytes.rb
index deea8e9358..dfbca32474 100644
--- a/activesupport/lib/active_support/core_ext/numeric/bytes.rb
+++ b/activesupport/lib/active_support/core_ext/numeric/bytes.rb
@@ -7,36 +7,56 @@ class Numeric
EXABYTE = PETABYTE * 1024
# Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes
+ #
+ # 2.bytes # => 2
def bytes
self
end
alias :byte :bytes
+ # Returns the number of bytes equivalent to the kilobytes provided.
+ #
+ # 2.kilobytes # => 2048
def kilobytes
self * KILOBYTE
end
alias :kilobyte :kilobytes
+ # Returns the number of bytes equivalent to the megabytes provided.
+ #
+ # 2.megabytes # => 2_097_152
def megabytes
self * MEGABYTE
end
alias :megabyte :megabytes
+ # Returns the number of bytes equivalent to the gigabytes provided.
+ #
+ # 2.gigabytes # => 2_147_483_648
def gigabytes
self * GIGABYTE
end
alias :gigabyte :gigabytes
+ # Returns the number of bytes equivalent to the terabytes provided.
+ #
+ # 2.terabytes # => 2_199_023_255_552
def terabytes
self * TERABYTE
end
alias :terabyte :terabytes
+ # Returns the number of bytes equivalent to the petabytes provided.
+ #
+ # 2.petabytes # => 2_251_799_813_685_248
def petabytes
self * PETABYTE
end
alias :petabyte :petabytes
+ # Returns the number of bytes equivalent to the exabytes provided.
+ #
+ # 2.exabytes # => 2_305_843_009_213_693_952
def exabytes
self * EXABYTE
end
diff --git a/activesupport/lib/active_support/core_ext/numeric/conversions.rb b/activesupport/lib/active_support/core_ext/numeric/conversions.rb
index 6d3635c69a..0c8ff79237 100644
--- a/activesupport/lib/active_support/core_ext/numeric/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/numeric/conversions.rb
@@ -118,18 +118,28 @@ class Numeric
end
end
- [Float, Fixnum, Bignum, BigDecimal].each do |klass|
- klass.send(:alias_method, :to_default_s, :to_s)
-
- klass.send(:define_method, :to_s) do |*args|
- if args[0].is_a?(Symbol)
- format = args[0]
- options = args[1] || {}
+ [Fixnum, Bignum].each do |klass|
+ klass.class_eval do
+ alias_method :to_default_s, :to_s
+ def to_s(base_or_format = 10, options = nil)
+ if base_or_format.is_a?(Symbol)
+ to_formatted_s(base_or_format, options || {})
+ else
+ to_default_s(base_or_format)
+ end
+ end
+ end
+ end
- self.to_formatted_s(format, options)
+ Float.class_eval do
+ alias_method :to_default_s, :to_s
+ def to_s(*args)
+ if args.empty?
+ to_default_s
else
- to_default_s(*args)
+ to_formatted_s(*args)
end
end
end
+
end
diff --git a/activesupport/lib/active_support/core_ext/numeric/inquiry.rb b/activesupport/lib/active_support/core_ext/numeric/inquiry.rb
new file mode 100644
index 0000000000..7e7ac1b0b2
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/numeric/inquiry.rb
@@ -0,0 +1,26 @@
+unless 1.respond_to?(:positive?) # TODO: Remove this file when we drop support to ruby < 2.3
+class Numeric
+ # Returns true if the number is positive.
+ #
+ # 1.positive? # => true
+ # 0.positive? # => false
+ # -1.positive? # => false
+ def positive?
+ self > 0
+ end
+
+ # Returns true if the number is negative.
+ #
+ # -1.negative? # => true
+ # 0.negative? # => false
+ # 1.negative? # => false
+ def negative?
+ self < 0
+ end
+end
+
+class Complex
+ undef :positive?
+ undef :negative?
+end
+end
diff --git a/activesupport/lib/active_support/core_ext/numeric/time.rb b/activesupport/lib/active_support/core_ext/numeric/time.rb
index 689fae4830..6c4a975495 100644
--- a/activesupport/lib/active_support/core_ext/numeric/time.rb
+++ b/activesupport/lib/active_support/core_ext/numeric/time.rb
@@ -1,6 +1,8 @@
require 'active_support/duration'
require 'active_support/core_ext/time/calculations'
require 'active_support/core_ext/time/acts_like'
+require 'active_support/core_ext/date/calculations'
+require 'active_support/core_ext/date/acts_like'
class Numeric
# Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
@@ -16,53 +18,56 @@ class Numeric
#
# # equivalent to Time.current.advance(months: 4, years: 5)
# (4.months + 5.years).from_now
- #
- # While these methods provide precise calculation when used as in the examples above, care
- # should be taken to note that this is not true if the result of `months', `years', etc is
- # converted before use:
- #
- # # equivalent to 30.days.to_i.from_now
- # 1.month.to_i.from_now
- #
- # # equivalent to 365.25.days.to_f.from_now
- # 1.year.to_f.from_now
- #
- # In such cases, Ruby's core
- # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
- # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
- # date and time arithmetic.
def seconds
ActiveSupport::Duration.new(self, [[:seconds, self]])
end
alias :second :seconds
+ # Returns a Duration instance matching the number of minutes provided.
+ #
+ # 2.minutes # => 120 seconds
def minutes
ActiveSupport::Duration.new(self * 60, [[:seconds, self * 60]])
end
alias :minute :minutes
+ # Returns a Duration instance matching the number of hours provided.
+ #
+ # 2.hours # => 7_200 seconds
def hours
ActiveSupport::Duration.new(self * 3600, [[:seconds, self * 3600]])
end
alias :hour :hours
+ # Returns a Duration instance matching the number of days provided.
+ #
+ # 2.days # => 2 days
def days
ActiveSupport::Duration.new(self * 24.hours, [[:days, self]])
end
alias :day :days
+ # Returns a Duration instance matching the number of weeks provided.
+ #
+ # 2.weeks # => 14 days
def weeks
ActiveSupport::Duration.new(self * 7.days, [[:days, self * 7]])
end
alias :week :weeks
+ # Returns a Duration instance matching the number of fortnights provided.
+ #
+ # 2.fortnights # => 28 days
def fortnights
ActiveSupport::Duration.new(self * 2.weeks, [[:days, self * 14]])
end
alias :fortnight :fortnights
+ # Returns the number of milliseconds equivalent to the seconds provided.
# Used with the standard time durations, like 1.hour.in_milliseconds --
# so we can feed them to JavaScript functions like getTime().
+ #
+ # 2.in_milliseconds # => 2_000
def in_milliseconds
self * 1000
end
diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb
index f1106cca9b..f4f9152d6a 100644
--- a/activesupport/lib/active_support/core_ext/object.rb
+++ b/activesupport/lib/active_support/core_ext/object.rb
@@ -2,7 +2,6 @@ require 'active_support/core_ext/object/acts_like'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/object/deep_dup'
-require 'active_support/core_ext/object/itself'
require 'active_support/core_ext/object/try'
require 'active_support/core_ext/object/inclusion'
diff --git a/activesupport/lib/active_support/core_ext/object/deep_dup.rb b/activesupport/lib/active_support/core_ext/object/deep_dup.rb
index 2e99f4a1b8..ad5b2af161 100644
--- a/activesupport/lib/active_support/core_ext/object/deep_dup.rb
+++ b/activesupport/lib/active_support/core_ext/object/deep_dup.rb
@@ -25,7 +25,7 @@ class Array
# array[1][2] # => nil
# dup[1][2] # => 4
def deep_dup
- map { |it| it.deep_dup }
+ map(&:deep_dup)
end
end
@@ -40,6 +40,7 @@ class Hash
# dup[:a][:c] # => "c"
def deep_dup
each_with_object(dup) do |(key, value), hash|
+ hash.delete(key)
hash[key.deep_dup] = value.deep_dup
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb
index 665cb0f96d..befa5aee21 100644
--- a/activesupport/lib/active_support/core_ext/object/duplicable.rb
+++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb
@@ -19,7 +19,7 @@
class Object
# Can you safely dup this object?
#
- # False for +nil+, +false+, +true+, symbol, number and BigDecimal(in 1.9.x) objects;
+ # False for +nil+, +false+, +true+, symbol, number, method objects;
# true otherwise.
def duplicable?
true
@@ -78,17 +78,12 @@ end
require 'bigdecimal'
class BigDecimal
- # Needed to support Ruby 1.9.x, as it doesn't allow dup on BigDecimal, instead
- # raises TypeError exception. Checking here on the runtime whether BigDecimal
- # will allow dup or not.
- begin
- BigDecimal.new('4.56').dup
-
- def duplicable?
- true
- end
- rescue TypeError
- # can't dup, so use superclass implementation
+ # BigDecimals are duplicable:
+ #
+ # BigDecimal.new("1.2").duplicable? # => true
+ # BigDecimal.new("1.2").dup # => #<BigDecimal:...,'0.12E1',18(18)>
+ def duplicable?
+ true
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/instance_variables.rb b/activesupport/lib/active_support/core_ext/object/instance_variables.rb
index 755e1c6b16..593a7a4940 100644
--- a/activesupport/lib/active_support/core_ext/object/instance_variables.rb
+++ b/activesupport/lib/active_support/core_ext/object/instance_variables.rb
@@ -23,6 +23,6 @@ class Object
#
# C.new(0, 1).instance_variable_names # => ["@y", "@x"]
def instance_variable_names
- instance_variables.map { |var| var.to_s }
+ instance_variables.map(&:to_s)
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/itself.rb b/activesupport/lib/active_support/core_ext/object/itself.rb
deleted file mode 100644
index d71cea6674..0000000000
--- a/activesupport/lib/active_support/core_ext/object/itself.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-class Object
- # TODO: Remove this file when we drop support for Ruby < 2.2
- unless respond_to?(:itself)
- # Returns the object itself.
- #
- # Useful for chaining methods, such as Active Record scopes:
- #
- # Event.public_send(state.presence_in([ :trashed, :drafted ]) || :itself).order(:created_at)
- #
- # @return Object
- def itself
- self
- end
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/object/json.rb b/activesupport/lib/active_support/core_ext/object/json.rb
index 698b2d1920..0db787010c 100644
--- a/activesupport/lib/active_support/core_ext/object/json.rb
+++ b/activesupport/lib/active_support/core_ext/object/json.rb
@@ -9,7 +9,6 @@ require 'time'
require 'active_support/core_ext/time/conversions'
require 'active_support/core_ext/date_time/conversions'
require 'active_support/core_ext/date/conversions'
-require 'active_support/core_ext/module/aliasing'
# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
# their default behavior. That said, we need to define the basic to_json method in all of them,
@@ -26,22 +25,25 @@ require 'active_support/core_ext/module/aliasing'
# bypassed completely. This means that as_json won't be invoked and the JSON gem will simply
# ignore any options it does not natively understand. This also means that ::JSON.{generate,dump}
# should give exactly the same results with or without active support.
-[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass, Enumerable].each do |klass|
- klass.class_eval do
- def to_json_with_active_support_encoder(options = nil)
+
+module ActiveSupport
+ module ToJsonWithActiveSupportEncoder # :nodoc:
+ def to_json(options = nil)
if options.is_a?(::JSON::State)
# Called from JSON.{generate,dump}, forward it to JSON gem's to_json
- self.to_json_without_active_support_encoder(options)
+ super(options)
else
# to_json is being invoked directly, use ActiveSupport's encoder
ActiveSupport::JSON.encode(self, options)
end
end
-
- alias_method_chain :to_json, :active_support_encoder
end
end
+[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass, Enumerable].reverse_each do |klass|
+ klass.prepend(ActiveSupport::ToJsonWithActiveSupportEncoder)
+end
+
class Object
def as_json(options = nil) #:nodoc:
if respond_to?(:to_hash)
diff --git a/activesupport/lib/active_support/core_ext/object/to_query.rb b/activesupport/lib/active_support/core_ext/object/to_query.rb
index ccd568bbf5..ec5ace4e16 100644
--- a/activesupport/lib/active_support/core_ext/object/to_query.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_query.rb
@@ -38,7 +38,7 @@ class Array
# Calls <tt>to_param</tt> on all its elements and joins the result with
# slashes. This is used by <tt>url_for</tt> in Action Pack.
def to_param
- collect { |e| e.to_param }.join '/'
+ collect(&:to_param).join '/'
end
# Converts an array into a string suitable for use as a URL query string,
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index 31919474ed..c67eb25b68 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -1,4 +1,34 @@
+require 'delegate'
+
+module ActiveSupport
+ module Tryable #:nodoc:
+ def try(*a, &b)
+ try!(*a, &b) if a.empty? || respond_to?(a.first)
+ end
+
+ def try!(*a, &b)
+ if a.empty? && block_given?
+ if b.arity == 0
+ instance_eval(&b)
+ else
+ yield self
+ end
+ else
+ public_send(*a, &b)
+ end
+ end
+ end
+end
+
class Object
+ include ActiveSupport::Tryable
+
+ ##
+ # :method: try
+ #
+ # :call-seq:
+ # try(*a, &b)
+ #
# Invokes the public method whose name goes as first argument just like
# +public_send+ does, except that if the receiver does not respond to it the
# call returns +nil+ rather than raising an exception.
@@ -9,7 +39,23 @@ class Object
#
# instead of
#
- # @person ? @person.name : nil
+ # @person.name if @person
+ #
+ # +try+ calls can be chained:
+ #
+ # @person.try(:spouse).try(:name)
+ #
+ # instead of
+ #
+ # @person.spouse.name if @person && @person.spouse
+ #
+ # +try+ will also return +nil+ if the receiver does not respond to the method:
+ #
+ # @person.try(:non_existing_method) # => nil
+ #
+ # instead of
+ #
+ # @person.non_existing_method if @person.respond_to?(:non_existing_method) # => nil
#
# +try+ returns +nil+ when called on +nil+ regardless of whether it responds
# to the method:
@@ -24,7 +70,7 @@ class Object
#
# The number of arguments in the signature must match. If the object responds
# to the method the call is attempted and +ArgumentError+ is still raised
- # otherwise.
+ # in case of argument mismatch.
#
# If +try+ is called without arguments it yields the receiver to a given
# block unless it is +nil+:
@@ -38,42 +84,52 @@ class Object
#
# @person.try { upcase.truncate(50) }
#
- # Please also note that +try+ is defined on +Object+, therefore it won't work
+ # Please also note that +try+ is defined on +Object+. Therefore, it won't work
# with instances of classes that do not have +Object+ among their ancestors,
- # like direct subclasses of +BasicObject+. For example, using +try+ with
- # +SimpleDelegator+ will delegate +try+ to the target instead of calling it on
- # delegator itself.
- def try(*a, &b)
- if a.empty? && block_given?
- if b.arity.zero?
- instance_eval(&b)
- else
- yield self
- end
- else
- public_send(*a, &b) if respond_to?(a.first)
- end
- end
+ # like direct subclasses of +BasicObject+.
- # Same as #try, but will raise a NoMethodError exception if the receiving is not nil and
- # does not implement the tried method.
- def try!(*a, &b)
- if a.empty? && block_given?
- yield self
- else
- public_send(*a, &b)
- end
- end
+ ##
+ # :method: try!
+ #
+ # :call-seq:
+ # try!(*a, &b)
+ #
+ # Same as #try, but raises a NoMethodError exception if the receiver is
+ # not +nil+ and does not implement the tried method.
+ #
+ # "a".try!(:upcase) # => "A"
+ # nil.try!(:upcase) # => nil
+ # 123.try!(:upcase) # => NoMethodError: undefined method `upcase' for 123:Fixnum
+end
+
+class Delegator
+ include ActiveSupport::Tryable
+
+ ##
+ # :method: try
+ #
+ # :call-seq:
+ # try(a*, &b)
+ #
+ # See Object#try
+
+ ##
+ # :method: try!
+ #
+ # :call-seq:
+ # try!(a*, &b)
+ #
+ # See Object#try!
end
class NilClass
# Calling +try+ on +nil+ always returns +nil+.
- # It becomes specially helpful when navigating through associations that may return +nil+.
+ # It becomes especially helpful when navigating through associations that may return +nil+.
#
# nil.try(:name) # => nil
#
# Without +try+
- # @person && !@person.children.blank? && @person.children.first.name
+ # @person && @person.children.any? && @person.children.first.name
#
# With +try+
# @person.try(:children).try(:first).try(:name)
@@ -81,6 +137,9 @@ class NilClass
nil
end
+ # Calling +try!+ on +nil+ always returns +nil+.
+ #
+ # nil.try!(:name) # => nil
def try!(*args)
nil
end
diff --git a/activesupport/lib/active_support/core_ext/object/with_options.rb b/activesupport/lib/active_support/core_ext/object/with_options.rb
index 7d38e1d134..513c8b1d55 100644
--- a/activesupport/lib/active_support/core_ext/object/with_options.rb
+++ b/activesupport/lib/active_support/core_ext/object/with_options.rb
@@ -7,7 +7,7 @@ class Object
# provided. Each method called on the block variable must take an options
# hash as its final argument.
#
- # Without <tt>with_options></tt>, this code contains duplication:
+ # Without <tt>with_options</tt>, this code contains duplication:
#
# class Account < ActiveRecord::Base
# has_many :customers, dependent: :destroy
diff --git a/activesupport/lib/active_support/core_ext/range/conversions.rb b/activesupport/lib/active_support/core_ext/range/conversions.rb
index b1a12781f3..83eced50bf 100644
--- a/activesupport/lib/active_support/core_ext/range/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/range/conversions.rb
@@ -3,9 +3,24 @@ class Range
:db => Proc.new { |start, stop| "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'" }
}
- # Gives a human readable format of the range.
+ # Convert range to a formatted string. See RANGE_FORMATS for predefined formats.
#
- # (1..100).to_formatted_s # => "1..100"
+ # This method is aliased to <tt>to_s</tt>.
+ #
+ # range = (1..100) # => 1..100
+ #
+ # range.to_formatted_s # => "1..100"
+ # range.to_s # => "1..100"
+ #
+ # range.to_formatted_s(:db) # => "BETWEEN '1' AND '100'"
+ # range.to_s(:db) # => "BETWEEN '1' AND '100'"
+ #
+ # == Adding your own range formats to to_formatted_s
+ # You can add your own formats to the Range::RANGE_FORMATS hash.
+ # Use the format name as the hash key and a Proc instance.
+ #
+ # # config/initializers/range_formats.rb
+ # Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_s(:db)} and #{stop.to_s(:db)}" }
def to_formatted_s(format = :default)
if formatter = RANGE_FORMATS[format]
formatter.call(first, last)
diff --git a/activesupport/lib/active_support/core_ext/range/each.rb b/activesupport/lib/active_support/core_ext/range/each.rb
index ecef78f55f..dc6dad5ced 100644
--- a/activesupport/lib/active_support/core_ext/range/each.rb
+++ b/activesupport/lib/active_support/core_ext/range/each.rb
@@ -1,23 +1,21 @@
-require 'active_support/core_ext/module/aliasing'
+module ActiveSupport
+ module EachTimeWithZone #:nodoc:
+ def each(&block)
+ ensure_iteration_allowed
+ super
+ end
-class Range #:nodoc:
+ def step(n = 1, &block)
+ ensure_iteration_allowed
+ super
+ end
- def each_with_time_with_zone(&block)
- ensure_iteration_allowed
- each_without_time_with_zone(&block)
- end
- alias_method_chain :each, :time_with_zone
+ private
- def step_with_time_with_zone(n = 1, &block)
- ensure_iteration_allowed
- step_without_time_with_zone(n, &block)
- end
- alias_method_chain :step, :time_with_zone
-
- private
- def ensure_iteration_allowed
- if first.is_a?(Time)
- raise TypeError, "can't iterate from #{first.class}"
- end
+ def ensure_iteration_allowed
+ raise TypeError, "can't iterate from #{first.class}" if first.is_a?(Time)
+ end
end
end
+
+Range.prepend(ActiveSupport::EachTimeWithZone)
diff --git a/activesupport/lib/active_support/core_ext/range/include_range.rb b/activesupport/lib/active_support/core_ext/range/include_range.rb
index 3a07401c8a..c69e1e3fb9 100644
--- a/activesupport/lib/active_support/core_ext/range/include_range.rb
+++ b/activesupport/lib/active_support/core_ext/range/include_range.rb
@@ -1,23 +1,23 @@
-require 'active_support/core_ext/module/aliasing'
-
-class Range
- # Extends the default Range#include? to support range comparisons.
- # (1..5).include?(1..5) # => true
- # (1..5).include?(2..3) # => true
- # (1..5).include?(2..6) # => false
- #
- # The native Range#include? behavior is untouched.
- # ('a'..'f').include?('c') # => true
- # (5..9).include?(11) # => false
- def include_with_range?(value)
- if value.is_a?(::Range)
- # 1...10 includes 1..9 but it does not include 1..10.
- operator = exclude_end? && !value.exclude_end? ? :< : :<=
- include_without_range?(value.first) && value.last.send(operator, last)
- else
- include_without_range?(value)
+module ActiveSupport
+ module IncludeWithRange #:nodoc:
+ # Extends the default Range#include? to support range comparisons.
+ # (1..5).include?(1..5) # => true
+ # (1..5).include?(2..3) # => true
+ # (1..5).include?(2..6) # => false
+ #
+ # The native Range#include? behavior is untouched.
+ # ('a'..'f').include?('c') # => true
+ # (5..9).include?(11) # => false
+ def include?(value)
+ if value.is_a?(::Range)
+ # 1...10 includes 1..9 but it does not include 1..10.
+ operator = exclude_end? && !value.exclude_end? ? :< : :<=
+ super(value.first) && value.last.send(operator, last)
+ else
+ super
+ end
end
end
-
- alias_method_chain :include?, :range
end
+
+Range.prepend(ActiveSupport::IncludeWithRange)
diff --git a/activesupport/lib/active_support/core_ext/securerandom.rb b/activesupport/lib/active_support/core_ext/securerandom.rb
new file mode 100644
index 0000000000..6cdbea1f37
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/securerandom.rb
@@ -0,0 +1,23 @@
+require 'securerandom'
+
+module SecureRandom
+ BASE58_ALPHABET = ('0'..'9').to_a + ('A'..'Z').to_a + ('a'..'z').to_a - ['0', 'O', 'I', 'l']
+ # SecureRandom.base58 generates a random base58 string.
+ #
+ # The argument _n_ specifies the length, of the random string to be generated.
+ #
+ # If _n_ is not specified or is nil, 16 is assumed. It may be larger in the future.
+ #
+ # The result may contain alphanumeric characters except 0, O, I and l
+ #
+ # p SecureRandom.base58 #=> "4kUgL2pdQMSCQtjE"
+ # p SecureRandom.base58(24) #=> "77TMHrHJFvFDwodq8w7Ev2m7"
+ #
+ def self.base58(n = 16)
+ SecureRandom.random_bytes(n).unpack("C*").map do |byte|
+ idx = byte % 64
+ idx = SecureRandom.random_number(58) if idx >= 58
+ BASE58_ALPHABET[idx]
+ end.join
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/string/behavior.rb b/activesupport/lib/active_support/core_ext/string/behavior.rb
index 4aa960039b..710f1f4670 100644
--- a/activesupport/lib/active_support/core_ext/string/behavior.rb
+++ b/activesupport/lib/active_support/core_ext/string/behavior.rb
@@ -1,5 +1,5 @@
class String
- # Enable more predictable duck-typing on String-like classes. See <tt>Object#acts_like?</tt>.
+ # Enables more predictable duck-typing on String-like classes. See <tt>Object#acts_like?</tt>.
def acts_like_string?
true
end
diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb
index 2b1583d4ac..375ec1aef8 100644
--- a/activesupport/lib/active_support/core_ext/string/filters.rb
+++ b/activesupport/lib/active_support/core_ext/string/filters.rb
@@ -13,21 +13,34 @@ class String
end
# Performs a destructive squish. See String#squish.
+ # str = " foo bar \n \t boo"
+ # str.squish! # => "foo bar boo"
+ # str # => "foo bar boo"
def squish!
- gsub!(/\A[[:space:]]+/, '')
- gsub!(/[[:space:]]+\z/, '')
gsub!(/[[:space:]]+/, ' ')
+ strip!
self
end
- # Returns a new string with all occurrences of the pattern removed. Short-hand for String#gsub(pattern, '').
- def remove(pattern)
- gsub pattern, ''
+ # Returns a new string with all occurrences of the patterns removed.
+ # str = "foo bar test"
+ # str.remove(" test") # => "foo bar"
+ # str.remove(" test", /bar/) # => "foo "
+ # str # => "foo bar test"
+ def remove(*patterns)
+ dup.remove!(*patterns)
end
- # Alters the string by removing all occurrences of the pattern. Short-hand for String#gsub!(pattern, '').
- def remove!(pattern)
- gsub! pattern, ''
+ # Alters the string by removing all occurrences of the patterns.
+ # str = "foo bar test"
+ # str.remove!(" test", /bar/) # => "foo "
+ # str # => "foo "
+ def remove!(*patterns)
+ patterns.each do |pattern|
+ gsub! pattern, ""
+ end
+
+ self
end
# Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
@@ -80,7 +93,7 @@ class String
def truncate_words(words_count, options = {})
sep = options[:separator] || /\s+/
sep = Regexp.escape(sep.to_s) unless Regexp === sep
- if self =~ /\A((?:.+?#{sep}){#{words_count - 1}}.+?)#{sep}.*/m
+ if self =~ /\A((?>.+?#{sep}){#{words_count - 1}}.+?)#{sep}.*/m
$1 + (options[:omission] || '...')
else
dup
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index 38d567c014..b2e713077c 100644
--- a/activesupport/lib/active_support/core_ext/string/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/string/inflections.rb
@@ -164,7 +164,7 @@ class String
#
# <%= link_to(@person.name, person_path) %>
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
- def parameterize(sep = '-')
+ def parameterize(sep = '-'.freeze)
ActiveSupport::Inflector.parameterize(self, sep)
end
@@ -172,17 +172,17 @@ class String
# uses the +pluralize+ method on the last word in the string.
#
# 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
- # 'egg_and_ham'.tableize # => "egg_and_hams"
+ # 'ham_and_egg'.tableize # => "ham_and_eggs"
# 'fancyCategory'.tableize # => "fancy_categories"
def tableize
ActiveSupport::Inflector.tableize(self)
end
- # Create a class name from a plural table name like Rails does for table names to models.
+ # Creates a class name from a plural table name like Rails does for table names to models.
# Note that this returns a string and not a class. (To convert to an actual class
# follow +classify+ with +constantize+.)
#
- # 'egg_and_hams'.classify # => "EggAndHam"
+ # 'ham_and_eggs'.classify # => "HamAndEgg"
# 'posts'.classify # => "Post"
def classify
ActiveSupport::Inflector.classify(self)
diff --git a/activesupport/lib/active_support/core_ext/string/multibyte.rb b/activesupport/lib/active_support/core_ext/string/multibyte.rb
index a124202936..7055f7f699 100644
--- a/activesupport/lib/active_support/core_ext/string/multibyte.rb
+++ b/activesupport/lib/active_support/core_ext/string/multibyte.rb
@@ -1,4 +1,3 @@
-# encoding: utf-8
require 'active_support/multibyte'
class String
@@ -36,6 +35,13 @@ class String
ActiveSupport::Multibyte.proxy_class.new(self)
end
+ # Returns +true+ if string has utf_8 encoding.
+ #
+ # utf_8_str = "some string".encode "UTF-8"
+ # iso_str = "some string".encode "ISO-8859-1"
+ #
+ # utf_8_str.is_utf8? # => true
+ # iso_str.is_utf8? # => false
def is_utf8?
case encoding
when Encoding::UTF_8
diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb
index c761325108..c676b26b06 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -1,6 +1,5 @@
require 'erb'
require 'active_support/core_ext/kernel/singleton_class'
-require 'active_support/deprecation'
class ERB
module Util
@@ -14,7 +13,7 @@ class ERB
# This method is also aliased as <tt>h</tt>.
#
# In your ERB templates, use this method to escape any unsafe content. For example:
- # <%=h @person.name %>
+ # <%= h @person.name %>
#
# puts html_escape('is a > 0 & a < 10?')
# # => is a &gt; 0 &amp; a &lt; 10?
@@ -86,6 +85,11 @@ class ERB
# automatically flag the result as HTML safe, since the raw value is unsafe to
# use inside HTML attributes.
#
+ # If your JSON is being used downstream for insertion into the DOM, be aware of
+ # whether or not it is being inserted via +html()+. Most JQuery plugins do this.
+ # If that is the case, be sure to +html_escape+ or +sanitize+ any user-generated
+ # content returned by your JSON.
+ #
# If you need to output JSON elsewhere in your HTML, you can just do something
# like this, as any unsafe characters (including quotation marks) will be
# automatically escaped for you:
@@ -150,7 +154,11 @@ module ActiveSupport #:nodoc:
else
if html_safe?
new_safe_buffer = super
- new_safe_buffer.instance_eval { @html_safe = true }
+
+ if new_safe_buffer
+ new_safe_buffer.instance_variable_set :@html_safe, true
+ end
+
new_safe_buffer
else
to_str[*args]
@@ -186,11 +194,6 @@ module ActiveSupport #:nodoc:
super(html_escape_interpolated_argument(value))
end
- def prepend!(value)
- ActiveSupport::Deprecation.deprecation_warning "ActiveSupport::SafeBuffer#prepend!", :prepend
- prepend value
- end
-
def +(other)
dup.concat(other)
end
@@ -219,7 +222,7 @@ module ActiveSupport #:nodoc:
end
def encode_with(coder)
- coder.represent_scalar nil, to_str
+ coder.represent_object nil, to_str
end
UNSAFE_STRING_METHODS.each do |unsafe_method|
@@ -247,6 +250,11 @@ module ActiveSupport #:nodoc:
end
class String
+ # Marks a string as trusted safe. It will be inserted into HTML with no
+ # additional escaping performed. It is your responsibilty to ensure that the
+ # string contains no malicious content. This method is equivalent to the
+ # `raw` helper in views. It is recommended that you use `sanitize` instead of
+ # this method. It should never be called on user input.
def html_safe
ActiveSupport::SafeBuffer.new(self)
end
diff --git a/activesupport/lib/active_support/core_ext/string/strip.rb b/activesupport/lib/active_support/core_ext/string/strip.rb
index 086c610976..9fdd9d8d2e 100644
--- a/activesupport/lib/active_support/core_ext/string/strip.rb
+++ b/activesupport/lib/active_support/core_ext/string/strip.rb
@@ -17,8 +17,8 @@ class String
#
# the user would see the usage message aligned against the left margin.
#
- # Technically, it looks for the least indented line in the whole string, and removes
- # that amount of leading whitespace.
+ # Technically, it looks for the least indented non-empty line
+ # in the whole string, and removes that amount of leading whitespace.
def strip_heredoc
indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
gsub(/^[ \t]{#{indent}}/, '')
diff --git a/activesupport/lib/active_support/core_ext/struct.rb b/activesupport/lib/active_support/core_ext/struct.rb
index c2c30044f2..1fde3db070 100644
--- a/activesupport/lib/active_support/core_ext/struct.rb
+++ b/activesupport/lib/active_support/core_ext/struct.rb
@@ -1,6 +1,3 @@
-# Backport of Struct#to_h from Ruby 2.0
-class Struct # :nodoc:
- def to_h
- Hash[members.zip(values)]
- end
-end unless Struct.instance_methods.include?(:to_h)
+require 'active_support/deprecation'
+
+ActiveSupport::Deprecation.warn("This file is deprecated and will be removed in Rails 5.1 with no replacement.")
diff --git a/activesupport/lib/active_support/core_ext/thread.rb b/activesupport/lib/active_support/core_ext/thread.rb
deleted file mode 100644
index 4cd6634558..0000000000
--- a/activesupport/lib/active_support/core_ext/thread.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-class Thread
- LOCK = Mutex.new # :nodoc:
-
- # Returns the value of a thread local variable that has been set. Note that
- # these are different than fiber local values.
- #
- # Thread local values are carried along with threads, and do not respect
- # fibers. For example:
- #
- # Thread.new {
- # Thread.current.thread_variable_set("foo", "bar") # set a thread local
- # Thread.current["foo"] = "bar" # set a fiber local
- #
- # Fiber.new {
- # Fiber.yield [
- # Thread.current.thread_variable_get("foo"), # get the thread local
- # Thread.current["foo"], # get the fiber local
- # ]
- # }.resume
- # }.join.value # => ['bar', nil]
- #
- # The value <tt>"bar"</tt> is returned for the thread local, where +nil+ is returned
- # for the fiber local. The fiber is executed in the same thread, so the
- # thread local values are available.
- def thread_variable_get(key)
- _locals[key.to_sym]
- end
-
- # Sets a thread local with +key+ to +value+. Note that these are local to
- # threads, and not to fibers. Please see Thread#thread_variable_get for
- # more information.
- def thread_variable_set(key, value)
- _locals[key.to_sym] = value
- end
-
- # Returns an array of the names of the thread-local variables (as Symbols).
- #
- # thr = Thread.new do
- # Thread.current.thread_variable_set(:cat, 'meow')
- # Thread.current.thread_variable_set("dog", 'woof')
- # end
- # thr.join # => #<Thread:0x401b3f10 dead>
- # thr.thread_variables # => [:dog, :cat]
- #
- # Note that these are not fiber local variables. Please see Thread#thread_variable_get
- # for more details.
- def thread_variables
- _locals.keys
- end
-
- # Returns <tt>true</tt> if the given string (or symbol) exists as a
- # thread-local variable.
- #
- # me = Thread.current
- # me.thread_variable_set(:oliver, "a")
- # me.thread_variable?(:oliver) # => true
- # me.thread_variable?(:stanley) # => false
- #
- # Note that these are not fiber local variables. Please see Thread#thread_variable_get
- # for more details.
- def thread_variable?(key)
- _locals.has_key?(key.to_sym)
- end
-
- # Freezes the thread so that thread local variables cannot be set via
- # Thread#thread_variable_set, nor can fiber local variables be set.
- #
- # me = Thread.current
- # me.freeze
- # me.thread_variable_set(:oliver, "a") #=> RuntimeError: can't modify frozen thread locals
- # me[:oliver] = "a" #=> RuntimeError: can't modify frozen thread locals
- def freeze
- _locals.freeze
- super
- end
-
- private
-
- def _locals
- if defined?(@_locals)
- @_locals
- else
- LOCK.synchronize { @_locals ||= {} }
- end
- end
-end unless Thread.instance_methods.include?(:thread_variable_set)
diff --git a/activesupport/lib/active_support/core_ext/time.rb b/activesupport/lib/active_support/core_ext/time.rb
index 32cffe237d..72c3234630 100644
--- a/activesupport/lib/active_support/core_ext/time.rb
+++ b/activesupport/lib/active_support/core_ext/time.rb
@@ -1,5 +1,4 @@
require 'active_support/core_ext/time/acts_like'
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'
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index ab8307429a..96156deebb 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -48,7 +48,11 @@ class Time
alias_method :at, :at_with_coercion
end
- # Seconds since midnight: Time.now.seconds_since_midnight
+ # Returns the number of seconds since 00:00:00.
+ #
+ # Time.new(2012, 8, 29, 0, 0, 0).seconds_since_midnight # => 0
+ # Time.new(2012, 8, 29, 12, 34, 56).seconds_since_midnight # => 45296
+ # Time.new(2012, 8, 29, 23, 59, 59).seconds_since_midnight # => 86399
def seconds_since_midnight
to_i - change(:hour => 0).to_i + (usec / 1.0e+6)
end
@@ -69,7 +73,7 @@ class Time
# and minute is passed, then sec, usec and nsec is set to 0. The +options+
# parameter takes a hash with any of these keys: <tt>:year</tt>, <tt>:month</tt>,
# <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>, <tt>:sec</tt>, <tt>:usec</tt>
- # <tt>:nsec</tt>. Path either <tt>:usec</tt> or <tt>:nsec</tt>, not both.
+ # <tt>:nsec</tt>. Pass either <tt>:usec</tt> or <tt>:nsec</tt>, not both.
#
# Time.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => Time.new(2012, 8, 1, 22, 35, 0)
# Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => Time.new(1981, 8, 1, 22, 35, 0)
@@ -94,7 +98,7 @@ class Time
elsif zone
::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
else
- raise ArgumentError, 'argument out of range' if new_usec > 999999
+ raise ArgumentError, 'argument out of range' if new_usec >= 1000000
::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec + (new_usec.to_r / 1000000), utc_offset)
end
end
@@ -104,6 +108,12 @@ class Time
# takes a hash with any of these keys: <tt>:years</tt>, <tt>:months</tt>,
# <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>, <tt>:minutes</tt>,
# <tt>:seconds</tt>.
+ #
+ # Time.new(2015, 8, 1, 14, 35, 0).advance(seconds: 1) # => 2015-08-01 14:35:01 -0700
+ # Time.new(2015, 8, 1, 14, 35, 0).advance(minutes: 1) # => 2015-08-01 14:36:00 -0700
+ # Time.new(2015, 8, 1, 14, 35, 0).advance(hours: 1) # => 2015-08-01 15:35:00 -0700
+ # Time.new(2015, 8, 1, 14, 35, 0).advance(days: 1) # => 2015-08-02 14:35:00 -0700
+ # Time.new(2015, 8, 1, 14, 35, 0).advance(weeks: 1) # => 2015-08-08 14:35:00 -0700
def advance(options)
unless options[:weeks].nil?
options[:weeks], partial_weeks = options[:weeks].divmod(1)
@@ -162,7 +172,7 @@ class Time
alias :at_noon :middle_of_day
alias :at_middle_of_day :middle_of_day
- # Returns a new Time representing the end of the day, 23:59:59.999999 (.999999999 in ruby1.9)
+ # Returns a new Time representing the end of the day, 23:59:59.999999
def end_of_day
change(
:hour => 23,
@@ -179,7 +189,7 @@ class Time
end
alias :at_beginning_of_hour :beginning_of_hour
- # Returns a new Time representing the end of the hour, x:59:59.999999 (.999999999 in ruby1.9)
+ # Returns a new Time representing the end of the hour, x:59:59.999999
def end_of_hour
change(
:min => 59,
@@ -195,7 +205,7 @@ class Time
end
alias :at_beginning_of_minute :beginning_of_minute
- # Returns a new Time representing the end of the minute, x:xx:59.999999 (.999999999 in ruby1.9)
+ # Returns a new Time representing the end of the minute, x:xx:59.999999
def end_of_minute
change(
:sec => 59,
@@ -242,8 +252,10 @@ class Time
# Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances
# can be chronologically compared with a Time
def compare_with_coercion(other)
- # we're avoiding Time#to_datetime cause it's expensive
- if other.is_a?(Time)
+ # we're avoiding Time#to_datetime and Time#to_time because they're expensive
+ if other.class == Time
+ compare_without_coercion(other)
+ elsif other.is_a?(Time)
compare_without_coercion(other.to_time)
else
to_datetime <=> other
diff --git a/activesupport/lib/active_support/core_ext/time/marshal.rb b/activesupport/lib/active_support/core_ext/time/marshal.rb
index 497c4c3fb8..467bad1726 100644
--- a/activesupport/lib/active_support/core_ext/time/marshal.rb
+++ b/activesupport/lib/active_support/core_ext/time/marshal.rb
@@ -1,30 +1,3 @@
-# Ruby 1.9.2 adds utc_offset and zone to Time, but marshaling only
-# preserves utc_offset. Preserve zone also, even though it may not
-# work in some edge cases.
-if Time.local(2010).zone != Marshal.load(Marshal.dump(Time.local(2010))).zone
- class Time
- class << self
- alias_method :_load_without_zone, :_load
- def _load(marshaled_time)
- time = _load_without_zone(marshaled_time)
- time.instance_eval do
- if zone = defined?(@_zone) && remove_instance_variable('@_zone')
- ary = to_a
- ary[0] += subsec if ary[0] == sec
- ary[-1] = zone
- utc? ? Time.utc(*ary) : Time.local(*ary)
- else
- self
- end
- end
- end
- end
+require 'active_support/deprecation'
- alias_method :_dump_without_zone, :_dump
- def _dump(*args)
- obj = dup
- obj.instance_variable_set('@_zone', zone)
- obj.send :_dump_without_zone, *args
- end
- end
-end
+ActiveSupport::Deprecation.warn("This is deprecated and will be removed in Rails 5.1 with no replacement.")
diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb
index bbda04d60c..133d3938eb 100644
--- a/activesupport/lib/active_support/core_ext/time/zones.rb
+++ b/activesupport/lib/active_support/core_ext/time/zones.rb
@@ -1,4 +1,5 @@
require 'active_support/time_with_zone'
+require 'active_support/core_ext/time/acts_like'
require 'active_support/core_ext/date_and_time/zones'
class Time
@@ -25,7 +26,7 @@ class Time
# <tt>current_user.time_zone</tt> just needs to return a string identifying the user's preferred time zone:
#
# class ApplicationController < ActionController::Base
- # around_filter :set_time_zone
+ # around_action :set_time_zone
#
# def set_time_zone
# if logged_in?
@@ -50,12 +51,22 @@ class Time
end
end
- # Returns a TimeZone instance or nil, or raises an ArgumentError for invalid timezones.
+ # Returns a TimeZone instance matching the time zone provided.
+ # Accepts the time zone in any format supported by <tt>Time.zone=</tt>.
+ # Raises an ArgumentError for invalid time zones.
+ #
+ # Time.find_zone! "America/New_York" # => #<ActiveSupport::TimeZone @name="America/New_York" ...>
+ # Time.find_zone! "EST" # => #<ActiveSupport::TimeZone @name="EST" ...>
+ # Time.find_zone! -5.hours # => #<ActiveSupport::TimeZone @name="Bogota" ...>
+ # Time.find_zone! nil # => nil
+ # Time.find_zone! false # => false
+ # Time.find_zone! "NOT-A-TIMEZONE" # => ArgumentError: Invalid Timezone: NOT-A-TIMEZONE
def find_zone!(time_zone)
if !time_zone || time_zone.is_a?(ActiveSupport::TimeZone)
time_zone
else
- # lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone)
+ # Look up the timezone based on the identifier (unless we've been
+ # passed a TZInfo::Timezone)
unless time_zone.respond_to?(:period_for_local)
time_zone = ActiveSupport::TimeZone[time_zone] || TZInfo::Timezone.get(time_zone)
end
@@ -71,6 +82,12 @@ class Time
raise ArgumentError, "Invalid Timezone: #{time_zone}"
end
+ # Returns a TimeZone instance matching the time zone provided.
+ # Accepts the time zone in any format supported by <tt>Time.zone=</tt>.
+ # Returns +nil+ for invalid time zones.
+ #
+ # Time.find_zone "America/New_York" # => #<ActiveSupport::TimeZone @name="America/New_York" ...>
+ # Time.find_zone "NOT-A-TIMEZONE" # => nil
def find_zone(time_zone)
find_zone!(time_zone) rescue nil
end
diff --git a/activesupport/lib/active_support/core_ext/uri.rb b/activesupport/lib/active_support/core_ext/uri.rb
index bfe0832b37..0b2ff817c3 100644
--- a/activesupport/lib/active_support/core_ext/uri.rb
+++ b/activesupport/lib/active_support/core_ext/uri.rb
@@ -12,7 +12,7 @@ unless str == parser.unescape(parser.escape(str))
# YK: My initial experiments say yes, but let's be sure please
enc = str.encoding
enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
- str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc)
+ str.gsub(escaped) { |match| [match[1, 2].hex].pack('C') }.force_encoding(enc)
end
end
end
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index 93a11d4586..8215a3085e 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -12,12 +12,40 @@ require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/load_error'
require 'active_support/core_ext/name_error'
require 'active_support/core_ext/string/starts_ends_with'
+require "active_support/dependencies/interlock"
require 'active_support/inflector'
module ActiveSupport #:nodoc:
module Dependencies #:nodoc:
extend self
+ mattr_accessor :interlock
+ self.interlock = Interlock.new
+
+ # :doc:
+
+ # Execute the supplied block without interference from any
+ # concurrent loads
+ def self.run_interlock
+ Dependencies.interlock.running { yield }
+ end
+
+ # Execute the supplied block while holding an exclusive lock,
+ # preventing any other thread from being inside a #run_interlock
+ # block at the same time
+ def self.load_interlock
+ Dependencies.interlock.loading { yield }
+ end
+
+ # Execute the supplied block while holding an exclusive lock,
+ # preventing any other thread from being inside a #run_interlock
+ # block at the same time
+ def self.unload_interlock
+ Dependencies.interlock.unloading { yield }
+ end
+
+ # :nodoc:
+
# Should we turn on Ruby warnings on the first load of dependent files?
mattr_accessor :warnings_on_first_load
self.warnings_on_first_load = false
@@ -30,6 +58,10 @@ module ActiveSupport #:nodoc:
mattr_accessor :loaded
self.loaded = Set.new
+ # Stack of files being loaded.
+ mattr_accessor :loading
+ self.loading = []
+
# Should we load files or require them?
mattr_accessor :mechanism
self.mechanism = ENV['NO_RELOAD'] ? :require : :load
@@ -124,7 +156,7 @@ module ActiveSupport #:nodoc:
# Normalize the list of new constants, and add them to the list we will return
new_constants.each do |suffix|
- constants << ([namespace, suffix] - ["Object"]).join("::")
+ constants << ([namespace, suffix] - ["Object"]).join("::".freeze)
end
end
constants
@@ -201,7 +233,10 @@ module ActiveSupport #:nodoc:
# Object includes this module.
module Loadable #:nodoc:
def self.exclude_from(base)
- base.class_eval { define_method(:load, Kernel.instance_method(:load)) }
+ base.class_eval do
+ define_method(:load, Kernel.instance_method(:load))
+ private :load
+ end
end
def require_or_load(file_name)
@@ -237,18 +272,6 @@ module ActiveSupport #:nodoc:
raise
end
- def load(file, wrap = false)
- result = false
- load_dependency(file) { result = super }
- result
- end
-
- def require(file)
- result = false
- load_dependency(file) { result = super }
- result
- end
-
# Mark the given constant as unloadable. Unloadable constants are removed
# each time dependencies are cleared.
#
@@ -265,6 +288,20 @@ module ActiveSupport #:nodoc:
def unloadable(const_desc)
Dependencies.mark_for_unload const_desc
end
+
+ private
+
+ def load(file, wrap = false)
+ result = false
+ load_dependency(file) { result = super }
+ result
+ end
+
+ def require(file)
+ result = false
+ load_dependency(file) { result = super }
+ result
+ end
end
# Exception file-blaming.
@@ -316,8 +353,11 @@ module ActiveSupport #:nodoc:
def clear
log_call
- loaded.clear
- remove_unloadable_constants!
+ Dependencies.unload_interlock do
+ loaded.clear
+ loading.clear
+ remove_unloadable_constants!
+ end
end
def require_or_load(file_name, const_path = nil)
@@ -326,41 +366,49 @@ module ActiveSupport #:nodoc:
expanded = File.expand_path(file_name)
return if loaded.include?(expanded)
- # Record that we've seen this file *before* loading it to avoid an
- # infinite loop with mutual dependencies.
- loaded << expanded
-
- begin
- if load?
- log "loading #{file_name}"
+ Dependencies.load_interlock do
+ # Maybe it got loaded while we were waiting for our lock:
+ return if loaded.include?(expanded)
- # Enable warnings if this file has not been loaded before and
- # warnings_on_first_load is set.
- load_args = ["#{file_name}.rb"]
- load_args << const_path unless const_path.nil?
+ # Record that we've seen this file *before* loading it to avoid an
+ # infinite loop with mutual dependencies.
+ loaded << expanded
+ loading << expanded
- if !warnings_on_first_load or history.include?(expanded)
- result = load_file(*load_args)
+ begin
+ if load?
+ log "loading #{file_name}"
+
+ # Enable warnings if this file has not been loaded before and
+ # warnings_on_first_load is set.
+ load_args = ["#{file_name}.rb"]
+ load_args << const_path unless const_path.nil?
+
+ if !warnings_on_first_load or history.include?(expanded)
+ result = load_file(*load_args)
+ else
+ enable_warnings { result = load_file(*load_args) }
+ end
else
- enable_warnings { result = load_file(*load_args) }
+ log "requiring #{file_name}"
+ result = require file_name
end
- else
- log "requiring #{file_name}"
- result = require file_name
+ rescue Exception
+ loaded.delete expanded
+ raise
+ ensure
+ loading.pop
end
- rescue Exception
- loaded.delete expanded
- raise
- end
- # Record history *after* loading so first load gets warnings.
- history << expanded
- result
+ # Record history *after* loading so first load gets warnings.
+ history << expanded
+ result
+ end
end
# Is the provided constant path defined?
def qualified_const_defined?(path)
- Object.qualified_const_defined?(path.sub(/^::/, ''), false)
+ Object.const_defined?(path, false)
end
# Given +path+, a filesystem path to a ruby file, return an array of
@@ -373,13 +421,13 @@ module ActiveSupport #:nodoc:
bases.each do |root|
expanded_root = File.expand_path(root)
- next unless %r{\A#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path
+ next unless expanded_path.start_with?(expanded_root)
- nesting = expanded_path[(expanded_root.size)..-1]
- nesting = nesting[1..-1] if nesting && nesting[0] == ?/
- next if nesting.blank?
+ root_size = expanded_root.size
+ next if expanded_path[root_size] != ?/.freeze
- paths << nesting.camelize
+ nesting = expanded_path[(root_size + 1)..-1]
+ paths << nesting.camelize unless nesting.blank?
end
paths.uniq!
@@ -388,7 +436,7 @@ module ActiveSupport #:nodoc:
# Search for a file in autoload_paths matching the provided suffix.
def search_for_file(path_suffix)
- path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb")
+ path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb".freeze)
autoload_paths.each do |root|
path = File.join(root, path_suffix)
@@ -408,7 +456,7 @@ module ActiveSupport #:nodoc:
end
def load_once_path?(path)
- # to_s works around a ruby1.9 issue where String#starts_with?(Pathname)
+ # to_s works around a ruby issue where String#starts_with?(Pathname)
# will raise a TypeError: no implicit conversion of Pathname into String
autoload_once_paths.any? { |base| path.starts_with? base.to_s }
end
@@ -473,9 +521,9 @@ module ActiveSupport #:nodoc:
if file_path
expanded = File.expand_path(file_path)
- expanded.sub!(/\.rb\z/, '')
+ expanded.sub!(/\.rb\z/, ''.freeze)
- if loaded.include?(expanded)
+ if loading.include?(expanded)
raise "Circular dependency detected while autoloading constant #{qualified_name}"
else
require_or_load(expanded, qualified_name)
@@ -594,7 +642,7 @@ module ActiveSupport #:nodoc:
def autoloaded?(desc)
return false if desc.is_a?(Module) && desc.anonymous?
name = to_constant_name desc
- return false unless qualified_const_defined? name
+ return false unless qualified_const_defined?(name)
return autoloaded_constants.include?(name)
end
@@ -729,7 +777,7 @@ module ActiveSupport #:nodoc:
protected
def log_call(*args)
if log_activity?
- arg_str = args.collect { |arg| arg.inspect } * ', '
+ arg_str = args.collect(&:inspect) * ', '
/in `([a-z_\?\!]+)'/ =~ caller(1).first
selector = $1 || '<unknown>'
log "called #{selector}(#{arg_str})"
diff --git a/activesupport/lib/active_support/dependencies/autoload.rb b/activesupport/lib/active_support/dependencies/autoload.rb
index c0dba5f7fd..13036d521d 100644
--- a/activesupport/lib/active_support/dependencies/autoload.rb
+++ b/activesupport/lib/active_support/dependencies/autoload.rb
@@ -67,7 +67,7 @@ module ActiveSupport
end
def eager_load!
- @_autoloads.values.each { |file| require file }
+ @_autoloads.each_value { |file| require file }
end
def autoloads
diff --git a/activesupport/lib/active_support/dependencies/interlock.rb b/activesupport/lib/active_support/dependencies/interlock.rb
new file mode 100644
index 0000000000..fbeb904684
--- /dev/null
+++ b/activesupport/lib/active_support/dependencies/interlock.rb
@@ -0,0 +1,47 @@
+require 'active_support/concurrency/share_lock'
+
+module ActiveSupport #:nodoc:
+ module Dependencies #:nodoc:
+ class Interlock
+ def initialize # :nodoc:
+ @lock = ActiveSupport::Concurrency::ShareLock.new
+ end
+
+ def loading
+ @lock.exclusive(purpose: :load, compatible: [:load]) do
+ yield
+ end
+ end
+
+ def unloading
+ @lock.exclusive(purpose: :unload, compatible: [:load, :unload]) do
+ yield
+ end
+ end
+
+ # Attempt to obtain an "unloading" (exclusive) lock. If possible,
+ # execute the supplied block while holding the lock. If there is
+ # concurrent activity, return immediately (without executing the
+ # block) instead of waiting.
+ def attempt_unloading
+ @lock.exclusive(purpose: :unload, compatible: [:load, :unload], no_wait: true) do
+ yield
+ end
+ end
+
+ def start_running
+ @lock.start_sharing
+ end
+
+ def done_running
+ @lock.stop_sharing
+ end
+
+ def running
+ @lock.sharing do
+ yield
+ end
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb
index 328b8c320a..28d2d78643 100644
--- a/activesupport/lib/active_support/deprecation/behaviors.rb
+++ b/activesupport/lib/active_support/deprecation/behaviors.rb
@@ -20,7 +20,7 @@ module ActiveSupport
log: ->(message, callstack) {
logger =
- if defined?(Rails) && Rails.logger
+ if defined?(Rails.logger) && Rails.logger
Rails.logger
else
require 'active_support/logger'
@@ -38,6 +38,18 @@ module ActiveSupport
silence: ->(message, callstack) {},
}
+ # Behavior module allows to determine how to display deprecation messages.
+ # You can create a custom behavior or set any from the +DEFAULT_BEHAVIORS+
+ # constant. Available behaviors are:
+ #
+ # [+raise+] Raise <tt>ActiveSupport::DeprecationException</tt>.
+ # [+stderr+] Log all deprecation warnings to +$stderr+.
+ # [+log+] Log all deprecation warnings to +Rails.logger+.
+ # [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
+ # [+silence+] Do nothing.
+ #
+ # Setting behaviors only affects deprecations that happen after boot time.
+ # For more information you can read the documentation of the +behavior=+ method.
module Behavior
# Whether to print a backtrace along with the warning.
attr_accessor :debug
diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb
index cab8a1b14d..c74e9c40ac 100644
--- a/activesupport/lib/active_support/deprecation/method_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb
@@ -31,12 +31,14 @@ module ActiveSupport
method_names += options.keys
method_names.each do |method_name|
- target_module.alias_method_chain(method_name, :deprecation) do |target, punctuation|
- target_module.send(:define_method, "#{target}_with_deprecation#{punctuation}") do |*args, &block|
+ mod = Module.new do
+ define_method(method_name) do |*args, &block|
deprecator.deprecation_warning(method_name, options[method_name])
- send(:"#{target}_without_deprecation#{punctuation}", *args, &block)
+ super(*args, &block)
end
end
+
+ target_module.prepend(mod)
end
end
end
diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
index a03a66b96b..6572ff78a6 100644
--- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
@@ -20,20 +20,22 @@ module ActiveSupport
private
def method_missing(called, *args, &block)
- warn caller, called, args
+ warn caller_locations, called, args
target.__send__(called, *args, &block)
end
end
- # This DeprecatedObjectProxy transforms object to deprecated object.
+ # DeprecatedObjectProxy transforms an object into a deprecated one. It
+ # takes an object, a deprecation message and optionally a deprecator. The
+ # deprecator defaults to +ActiveSupport::Deprecator+ if none is specified.
#
- # @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)
+ # deprecated_object = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(Object.new, "This object is now deprecated")
+ # # => #<Object:0x007fb9b34c34b0>
#
- # When someone executes any method except +inspect+ on proxy object this will
- # trigger +warn+ method on +deprecator_instance+.
- #
- # Default deprecator is <tt>ActiveSupport::Deprecation</tt>
+ # deprecated_object.to_s
+ # DEPRECATION WARNING: This object is now deprecated.
+ # (Backtrace)
+ # # => "#<Object:0x007fb9b34c34b0>"
class DeprecatedObjectProxy < DeprecationProxy
def initialize(object, message, deprecator = ActiveSupport::Deprecation.instance)
@object = object
@@ -51,13 +53,16 @@ module ActiveSupport
end
end
- # This DeprecatedInstanceVariableProxy transforms instance variable to
- # deprecated instance variable.
+ # DeprecatedInstanceVariableProxy transforms an instance variable into a
+ # deprecated one. It takes an instance of a class, a method on that class
+ # and an instance variable. It optionally takes a deprecator as the last
+ # argument. The deprecator defaults to +ActiveSupport::Deprecator+ if none
+ # is specified.
#
# class Example
- # def initialize(deprecator)
- # @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request, deprecator)
- # @_request = :a_request
+ # def initialize
+ # @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request)
+ # @_request = :special_request
# end
#
# def request
@@ -69,12 +74,17 @@ module ActiveSupport
# end
# end
#
- # When someone execute any method on @request variable this will trigger
- # +warn+ method on +deprecator_instance+ and will fetch <tt>@_request</tt>
- # variable via +request+ method and execute the same method on non-proxy
- # instance variable.
+ # example = Example.new
+ # # => #<Example:0x007fb9b31090b8 @_request=:special_request, @request=:special_request>
+ #
+ # example.old_request.to_s
+ # # => DEPRECATION WARNING: @request is deprecated! Call request.to_s instead of
+ # @request.to_s
+ # (Bactrace information…)
+ # "special_request"
#
- # Default deprecator is <tt>ActiveSupport::Deprecation</tt>.
+ # example.request.to_s
+ # # => "special_request"
class DeprecatedInstanceVariableProxy < DeprecationProxy
def initialize(instance, method, var = "@#{method}", deprecator = ActiveSupport::Deprecation.instance)
@instance = instance
@@ -93,15 +103,23 @@ module ActiveSupport
end
end
- # This DeprecatedConstantProxy transforms constant to deprecated constant.
+ # DeprecatedConstantProxy transforms a constant into a deprecated one. It
+ # takes the names of an old (deprecated) constant and of a new constant
+ # (both in string form) and optionally a deprecator. The deprecator defaults
+ # to +ActiveSupport::Deprecator+ if none is specified. The deprecated constant
+ # now returns the value of the new one.
+ #
+ # PLANETS = %w(mercury venus earth mars jupiter saturn uranus neptune pluto)
#
- # OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST')
- # OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST', deprecator_instance)
+ # (In a later update, the orignal implementation of `PLANETS` has been removed.)
#
- # When someone use old constant this will trigger +warn+ method on
- # +deprecator_instance+.
+ # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune)
+ # PLANETS = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('PLANETS', 'PLANETS_POST_2006')
#
- # Default deprecator is <tt>ActiveSupport::Deprecation</tt>.
+ # PLANETS.map { |planet| planet.capitalize }
+ # # => DEPRECATION WARNING: PLANETS is deprecated! Use PLANETS_POST_2006 instead.
+ # (Bactrace information…)
+ # ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
class DeprecatedConstantProxy < DeprecationProxy
def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance)
@old_const = old_const
@@ -109,6 +127,11 @@ module ActiveSupport
@deprecator = deprecator
end
+ # Returns the class of the new constant.
+ #
+ # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune)
+ # PLANETS = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('PLANETS', 'PLANETS_POST_2006')
+ # PLANETS.class # => Array
def class
target.class
end
diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb
index a7d265d732..bbe25c9260 100644
--- a/activesupport/lib/active_support/deprecation/reporting.rb
+++ b/activesupport/lib/active_support/deprecation/reporting.rb
@@ -14,7 +14,7 @@ module ActiveSupport
def warn(message = nil, callstack = nil)
return if silenced
- callstack ||= caller(2)
+ callstack ||= caller_locations(2)
deprecation_message(callstack, message).tap do |m|
behavior.each { |b| b.call(m, callstack) }
end
@@ -37,7 +37,7 @@ module ActiveSupport
end
def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
- caller_backtrace ||= caller(2)
+ caller_backtrace ||= caller_locations(2)
deprecated_method_warning(deprecated_method_name, message).tap do |msg|
warn(msg, caller_backtrace)
end
@@ -79,6 +79,17 @@ module ActiveSupport
end
def extract_callstack(callstack)
+ return _extract_callstack(callstack) if callstack.first.is_a? String
+
+ rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/"
+ offending_line = callstack.find { |frame|
+ !frame.absolute_path.start_with?(rails_gem_root)
+ } || callstack.first
+ [offending_line.path, offending_line.lineno, offending_line.label]
+ end
+
+ def _extract_callstack(callstack)
+ warn "Please pass `caller_locations` to the deprecation API" if $VERBOSE
rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/"
offending_line = callstack.find { |line| !line.start_with?(rails_gem_root) } || callstack.first
if offending_line
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 584fc1e1c5..c63b61e97a 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -1,4 +1,3 @@
-require 'active_support/proxy_object'
require 'active_support/core_ext/array/conversions'
require 'active_support/core_ext/object/acts_like'
@@ -53,10 +52,38 @@ module ActiveSupport
end
end
+ # Returns the amount of seconds a duration covers as a string.
+ # For more information check to_i method.
+ #
+ # 1.day.to_s # => "86400"
def to_s
@value.to_s
end
+ # Returns the number of seconds that this Duration represents.
+ #
+ # 1.minute.to_i # => 60
+ # 1.hour.to_i # => 3600
+ # 1.day.to_i # => 86400
+ #
+ # Note that this conversion makes some assumptions about the
+ # duration of some periods, e.g. months are always 30 days
+ # and years are 365.25 days:
+ #
+ # # equivalent to 30.days.to_i
+ # 1.month.to_i # => 2592000
+ #
+ # # equivalent to 365.25.days.to_i
+ # 1.year.to_i # => 31557600
+ #
+ # In such cases, Ruby's core
+ # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
+ # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
+ # date and time arithmetic.
+ def to_i
+ @value.to_i
+ end
+
# Returns +true+ if +other+ is also a Duration instance, which has the
# same parts as this one.
def eql?(other)
@@ -92,17 +119,19 @@ module ActiveSupport
reduce(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }.
sort_by {|unit, _ | [:years, :months, :days, :minutes, :seconds].index(unit)}.
map {|unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}"}.
- to_sentence(:locale => :en)
+ to_sentence(locale: ::I18n.default_locale)
end
def as_json(options = nil) #:nodoc:
to_i
end
- def respond_to_missing?(method, include_private=false) #:nodoc
+ def respond_to_missing?(method, include_private=false) #:nodoc:
@value.respond_to?(method, include_private)
end
+ delegate :<=>, to: :value
+
protected
def sum(sign, time = ::Time.current) #:nodoc:
@@ -121,13 +150,6 @@ module ActiveSupport
private
- # We define it as a workaround to Ruby 2.0.0-p353 bug.
- # For more information, check rails/rails#13055.
- # Remove it when we drop support for 2.0.0-p353.
- def ===(other) #:nodoc:
- value === other
- end
-
def method_missing(method, *args, &block) #:nodoc:
value.send(method, *args, &block)
end
diff --git a/activesupport/lib/active_support/gem_version.rb b/activesupport/lib/active_support/gem_version.rb
index 6233c45787..7068f09d87 100644
--- a/activesupport/lib/active_support/gem_version.rb
+++ b/activesupport/lib/active_support/gem_version.rb
@@ -5,10 +5,10 @@ module ActiveSupport
end
module VERSION
- MAJOR = 4
- MINOR = 2
+ MAJOR = 5
+ MINOR = 0
TINY = 0
- PRE = "beta2"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 1468c62151..0371f760b0 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -92,7 +92,7 @@ module ActiveSupport
# hash = ActiveSupport::HashWithIndifferentAccess.new
# hash[:key] = 'value'
#
- # This value can be later fetched using either +:key+ or +'key'+.
+ # This value can be later fetched using either +:key+ or <tt>'key'</tt>.
def []=(key, value)
regular_writer(convert_key(key), convert_value(value, for: :assignment))
end
@@ -188,7 +188,7 @@ module ActiveSupport
# dup[:a][:c] # => "c"
def dup
self.class.new(self).tap do |new_hash|
- new_hash.default = default
+ set_defaults(new_hash)
end
end
@@ -238,16 +238,20 @@ module ActiveSupport
def to_options!; self end
def select(*args, &block)
+ return to_enum(:select) unless block_given?
dup.tap { |hash| hash.select!(*args, &block) }
end
def reject(*args, &block)
+ return to_enum(:reject) unless block_given?
dup.tap { |hash| hash.reject!(*args, &block) }
end
# Convert to a regular hash with string keys.
def to_hash
- _new_hash = Hash.new(default)
+ _new_hash = Hash.new
+ set_defaults(_new_hash)
+
each do |key, value|
_new_hash[key] = convert_value(value, for: :to_hash)
end
@@ -267,7 +271,7 @@ module ActiveSupport
value.nested_under_indifferent_access
end
elsif value.is_a?(Array)
- unless options[:for] == :assignment
+ if options[:for] != :assignment || value.frozen?
value = value.dup
end
value.map! { |e| convert_value(e, options) }
@@ -275,6 +279,14 @@ module ActiveSupport
value
end
end
+
+ def set_defaults(target)
+ if default_proc
+ target.default_proc = default_proc.dup
+ else
+ target.default = default
+ end
+ end
end
end
diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb
index affcfb7398..6775eec34b 100644
--- a/activesupport/lib/active_support/i18n_railtie.rb
+++ b/activesupport/lib/active_support/i18n_railtie.rb
@@ -37,10 +37,12 @@ module I18n
enforce_available_locales = I18n.enforce_available_locales if enforce_available_locales.nil?
I18n.enforce_available_locales = false
+ reloadable_paths = []
app.config.i18n.each do |setting, value|
case setting
when :railties_load_path
- app.config.i18n.load_path.unshift(*value)
+ reloadable_paths = value
+ app.config.i18n.load_path.unshift(*value.map(&:existent).flatten)
when :load_path
I18n.load_path += value
else
@@ -53,16 +55,29 @@ module I18n
# Restore available locales check so it will take place from now on.
I18n.enforce_available_locales = enforce_available_locales
- reloader = ActiveSupport::FileUpdateChecker.new(I18n.load_path.dup){ I18n.reload! }
+ directories = watched_dirs_with_extensions(reloadable_paths)
+ reloader = ActiveSupport::FileUpdateChecker.new(I18n.load_path.dup, directories) do
+ I18n.load_path.keep_if { |p| File.exist?(p) }
+ I18n.load_path |= reloadable_paths.map(&:existent).flatten
+
+ I18n.reload!
+ end
+
app.reloaders << reloader
- ActionDispatch::Reloader.to_prepare { reloader.execute_if_updated }
+ ActionDispatch::Reloader.to_prepare do
+ reloader.execute_if_updated
+ # TODO: remove the following line as soon as the return value of
+ # callbacks is ignored, that is, returning `false` does not
+ # display a deprecation warning or halts the callback chain.
+ true
+ end
reloader.execute
@i18n_inited = true
end
def self.include_fallbacks_module
- I18n.backend.class.send(:include, I18n::Backend::Fallbacks)
+ I18n.backend.class.include(I18n::Backend::Fallbacks)
end
def self.init_fallbacks(fallbacks)
@@ -90,5 +105,11 @@ module I18n
raise "Unexpected fallback type #{fallbacks.inspect}"
end
end
+
+ def self.watched_dirs_with_extensions(paths)
+ paths.each_with_object({}) do |path, result|
+ result[path.absolute_current] = path.extensions
+ end
+ end
end
end
diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb
index 97401ccec7..42560f3515 100644
--- a/activesupport/lib/active_support/inflector/inflections.rb
+++ b/activesupport/lib/active_support/inflector/inflections.rb
@@ -27,6 +27,37 @@ module ActiveSupport
class Inflections
@__instance__ = ThreadSafe::Cache.new
+ class Uncountables < Array
+ def initialize
+ @regex_array = []
+ super
+ end
+
+ def delete(entry)
+ super entry
+ @regex_array.delete(to_regex(entry))
+ end
+
+ def <<(*word)
+ add(word)
+ end
+
+ def add(words)
+ self.concat(words.flatten.map(&:downcase))
+ @regex_array += self.map {|word| to_regex(word) }
+ self
+ end
+
+ def uncountable?(str)
+ @regex_array.any? { |regex| regex === str }
+ end
+
+ private
+ def to_regex(string)
+ /\b#{::Regexp.escape(string)}\Z/i
+ end
+ end
+
def self.instance(locale = :en)
@__instance__[locale] ||= new
end
@@ -34,7 +65,7 @@ module ActiveSupport
attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
def initialize
- @plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], [], [], {}, /(?=a)b/
+ @plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], Uncountables.new, [], {}, /(?=a)b/
end
# Private, for the test suite.
@@ -154,13 +185,13 @@ module ActiveSupport
end
end
- # Add uncountable words that shouldn't be attempted inflected.
+ # Specifies words that are uncountable and should not be inflected.
#
# uncountable 'money'
# uncountable 'money', 'information'
# uncountable %w( money information rice )
def uncountable(*words)
- @uncountables += words.flatten.map(&:downcase)
+ @uncountables.add(words)
end
# Specifies a humanized form of a string by a regular expression rule or
@@ -185,7 +216,7 @@ module ActiveSupport
def clear(scope = :all)
case scope
when :all
- @plurals, @singulars, @uncountables, @humans = [], [], [], []
+ @plurals, @singulars, @uncountables, @humans = [], [], Uncountables.new, []
else
instance_variable_set "@#{scope}", []
end
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index 0e3c8517d1..ab5372ac43 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -22,58 +22,58 @@ module ActiveSupport
# pluralized using rules defined for that language. By default,
# this parameter is set to <tt>:en</tt>.
#
- # 'post'.pluralize # => "posts"
- # 'octopus'.pluralize # => "octopi"
- # 'sheep'.pluralize # => "sheep"
- # 'words'.pluralize # => "words"
- # 'CamelOctopus'.pluralize # => "CamelOctopi"
- # 'ley'.pluralize(:es) # => "leyes"
+ # pluralize('post') # => "posts"
+ # pluralize('octopus') # => "octopi"
+ # pluralize('sheep') # => "sheep"
+ # pluralize('words') # => "words"
+ # pluralize('CamelOctopus') # => "CamelOctopi"
+ # pluralize('ley', :es) # => "leyes"
def pluralize(word, locale = :en)
apply_inflections(word, inflections(locale).plurals)
end
- # The reverse of +pluralize+, returns the singular form of a word in a
+ # The reverse of #pluralize, returns the singular form of a word in a
# string.
#
# If passed an optional +locale+ parameter, the word will be
# singularized using rules defined for that language. By default,
# this parameter is set to <tt>:en</tt>.
#
- # 'posts'.singularize # => "post"
- # 'octopi'.singularize # => "octopus"
- # 'sheep'.singularize # => "sheep"
- # 'word'.singularize # => "word"
- # 'CamelOctopi'.singularize # => "CamelOctopus"
- # 'leyes'.singularize(:es) # => "ley"
+ # singularize('posts') # => "post"
+ # singularize('octopi') # => "octopus"
+ # singularize('sheep') # => "sheep"
+ # singularize('word') # => "word"
+ # singularize('CamelOctopi') # => "CamelOctopus"
+ # singularize('leyes', :es) # => "ley"
def singularize(word, locale = :en)
apply_inflections(word, inflections(locale).singulars)
end
- # By default, +camelize+ converts strings to UpperCamelCase. If the argument
- # to +camelize+ is set to <tt>:lower</tt> then +camelize+ produces
+ # Converts strings to UpperCamelCase.
+ # If the +uppercase_first_letter+ parameter is set to false, then produces
# lowerCamelCase.
#
- # +camelize+ will also convert '/' to '::' which is useful for converting
+ # Also converts '/' to '::' which is useful for converting
# paths to namespaces.
#
- # 'active_model'.camelize # => "ActiveModel"
- # 'active_model'.camelize(:lower) # => "activeModel"
- # 'active_model/errors'.camelize # => "ActiveModel::Errors"
- # 'active_model/errors'.camelize(:lower) # => "activeModel::Errors"
+ # camelize('active_model') # => "ActiveModel"
+ # camelize('active_model', false) # => "activeModel"
+ # camelize('active_model/errors') # => "ActiveModel::Errors"
+ # camelize('active_model/errors', false) # => "activeModel::Errors"
#
# As a rule of thumb you can think of +camelize+ as the inverse of
- # +underscore+, though there are cases where that does not hold:
+ # #underscore, though there are cases where that does not hold:
#
- # 'SSLError'.underscore.camelize # => "SslError"
+ # camelize(underscore('SSLError')) # => "SslError"
def camelize(term, uppercase_first_letter = true)
string = term.to_s
if uppercase_first_letter
- string = string.sub(/^[a-z\d]*/) { inflections.acronyms[$&] || $&.capitalize }
+ string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize }
else
- string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
+ string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
end
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
- string.gsub!('/', '::')
+ string.gsub!('/'.freeze, '::'.freeze)
string
end
@@ -81,34 +81,34 @@ module ActiveSupport
#
# Changes '::' to '/' to convert namespaces to paths.
#
- # 'ActiveModel'.underscore # => "active_model"
- # 'ActiveModel::Errors'.underscore # => "active_model/errors"
+ # underscore('ActiveModel') # => "active_model"
+ # underscore('ActiveModel::Errors') # => "active_model/errors"
#
# As a rule of thumb you can think of +underscore+ as the inverse of
- # +camelize+, though there are cases where that does not hold:
+ # #camelize, though there are cases where that does not hold:
#
- # 'SSLError'.underscore.camelize # => "SslError"
+ # camelize(underscore('SSLError')) # => "SslError"
def underscore(camel_cased_word)
return camel_cased_word unless camel_cased_word =~ /[A-Z-]|::/
- word = camel_cased_word.to_s.gsub('::', '/')
- word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1 && '_'}#{$2.downcase}" }
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
- word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
- word.tr!("-", "_")
+ word = camel_cased_word.to_s.gsub('::'.freeze, '/'.freeze)
+ word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1 && '_'.freeze }#{$2.downcase}" }
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
+ word.tr!("-".freeze, "_".freeze)
word.downcase!
word
end
# Tweaks an attribute name for display to end users.
#
- # Specifically, +humanize+ performs these transformations:
+ # Specifically, performs these transformations:
#
- # * Applies human inflection rules to the argument.
- # * Deletes leading underscores, if any.
- # * Removes a "_id" suffix if present.
- # * Replaces underscores with spaces, if any.
- # * Downcases all words except acronyms.
- # * Capitalizes the first word.
+ # * Applies human inflection rules to the argument.
+ # * Deletes leading underscores, if any.
+ # * Removes a "_id" suffix if present.
+ # * Replaces underscores with spaces, if any.
+ # * Downcases all words except acronyms.
+ # * Capitalizes the first word.
#
# The capitalization of the first word can be turned off by setting the
# +:capitalize+ option to false (default is true).
@@ -127,9 +127,9 @@ module ActiveSupport
inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
- result.sub!(/\A_+/, '')
- result.sub!(/_id\z/, '')
- result.tr!('_', ' ')
+ result.sub!(/\A_+/, ''.freeze)
+ result.sub!(/_id\z/, ''.freeze)
+ result.tr!('_'.freeze, ' '.freeze)
result.gsub!(/([a-z\d]*)/i) do |match|
"#{inflections.acronyms[match] || match.downcase}"
@@ -148,54 +148,54 @@ module ActiveSupport
#
# +titleize+ is also aliased as +titlecase+.
#
- # 'man from the boondocks'.titleize # => "Man From The Boondocks"
- # 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
- # 'TheManWithoutAPast'.titleize # => "The Man Without A Past"
- # 'raiders_of_the_lost_ark'.titleize # => "Raiders Of The Lost Ark"
+ # titleize('man from the boondocks') # => "Man From The Boondocks"
+ # titleize('x-men: the last stand') # => "X Men: The Last Stand"
+ # titleize('TheManWithoutAPast') # => "The Man Without A Past"
+ # titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
def titleize(word)
- humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { $&.capitalize }
+ humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { |match| match.capitalize }
end
- # Create the name of a table like Rails does for models to table names. This
- # method uses the +pluralize+ method on the last word in the string.
+ # Creates the name of a table like Rails does for models to table names.
+ # This method uses the #pluralize method on the last word in the string.
#
- # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
- # 'egg_and_ham'.tableize # => "egg_and_hams"
- # 'fancyCategory'.tableize # => "fancy_categories"
+ # tableize('RawScaledScorer') # => "raw_scaled_scorers"
+ # tableize('ham_and_egg') # => "ham_and_eggs"
+ # tableize('fancyCategory') # => "fancy_categories"
def tableize(class_name)
pluralize(underscore(class_name))
end
- # Create a class name from a plural table name like Rails does for table
+ # Creates a class name from a plural table name like Rails does for table
# names to models. Note that this returns a string and not a Class (To
- # convert to an actual class follow +classify+ with +constantize+).
+ # convert to an actual class follow +classify+ with #constantize).
#
- # 'egg_and_hams'.classify # => "EggAndHam"
- # 'posts'.classify # => "Post"
+ # classify('ham_and_eggs') # => "HamAndEgg"
+ # classify('posts') # => "Post"
#
# Singular names are not handled correctly:
#
- # 'calculus'.classify # => "Calculu"
+ # classify('calculus') # => "Calculu"
def classify(table_name)
# strip out any leading schema name
- camelize(singularize(table_name.to_s.sub(/.*\./, '')))
+ camelize(singularize(table_name.to_s.sub(/.*\./, ''.freeze)))
end
# Replaces underscores with dashes in the string.
#
- # 'puni_puni'.dasherize # => "puni-puni"
+ # dasherize('puni_puni') # => "puni-puni"
def dasherize(underscored_word)
- underscored_word.tr('_', '-')
+ underscored_word.tr('_'.freeze, '-'.freeze)
end
# Removes the module part from the expression in the string.
#
- # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
- # 'Inflections'.demodulize # => "Inflections"
- # '::Inflections'.demodulize # => "Inflections"
- # ''.demodulize # => ""
+ # demodulize('ActiveRecord::CoreExtensions::String::Inflections') # => "Inflections"
+ # demodulize('Inflections') # => "Inflections"
+ # demodulize('::Inflections') # => "Inflections"
+ # demodulize('') # => ""
#
- # See also +deconstantize+.
+ # See also #deconstantize.
def demodulize(path)
path = path.to_s
if i = path.rindex('::')
@@ -207,13 +207,13 @@ module ActiveSupport
# Removes the rightmost segment from the constant expression in the string.
#
- # 'Net::HTTP'.deconstantize # => "Net"
- # '::Net::HTTP'.deconstantize # => "::Net"
- # 'String'.deconstantize # => ""
- # '::String'.deconstantize # => ""
- # ''.deconstantize # => ""
+ # deconstantize('Net::HTTP') # => "Net"
+ # deconstantize('::Net::HTTP') # => "::Net"
+ # deconstantize('String') # => ""
+ # deconstantize('::String') # => ""
+ # deconstantize('') # => ""
#
- # See also +demodulize+.
+ # See also #demodulize.
def deconstantize(path)
path.to_s[0, path.rindex('::') || 0] # implementation based on the one in facets' Module#spacename
end
@@ -222,17 +222,17 @@ module ActiveSupport
# +separate_class_name_and_id_with_underscore+ sets whether
# the method should put '_' between the name and 'id'.
#
- # 'Message'.foreign_key # => "message_id"
- # 'Message'.foreign_key(false) # => "messageid"
- # 'Admin::Post'.foreign_key # => "post_id"
+ # foreign_key('Message') # => "message_id"
+ # foreign_key('Message', false) # => "messageid"
+ # foreign_key('Admin::Post') # => "post_id"
def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
end
# Tries to find a constant with the name specified in the argument string.
#
- # 'Module'.constantize # => Module
- # 'Test::Unit'.constantize # => Test::Unit
+ # 'Module'.constantize # => Module
+ # 'Foo::Bar'.constantize # => Foo::Bar
#
# The name is assumed to be the one of a top-level constant, no matter
# whether it starts with "::" or not. No lexical context is taken into
@@ -248,7 +248,7 @@ module ActiveSupport
# NameError is raised when the name is not in CamelCase or the constant is
# unknown.
def constantize(camel_cased_word)
- names = camel_cased_word.split('::')
+ names = camel_cased_word.split('::'.freeze)
# Trigger a built-in NameError exception including the ill-formed constant in the message.
Object.const_get(camel_cased_word) if names.empty?
@@ -280,8 +280,8 @@ module ActiveSupport
# Tries to find a constant with the name specified in the argument string.
#
- # 'Module'.safe_constantize # => Module
- # 'Test::Unit'.safe_constantize # => Test::Unit
+ # safe_constantize('Module') # => Module
+ # safe_constantize('Foo::Bar') # => Foo::Bar
#
# The name is assumed to be the one of a top-level constant, no matter
# whether it starts with "::" or not. No lexical context is taken into
@@ -290,16 +290,16 @@ module ActiveSupport
# C = 'outside'
# module M
# C = 'inside'
- # C # => 'inside'
- # 'C'.safe_constantize # => 'outside', same as ::C
+ # C # => 'inside'
+ # safe_constantize('C') # => 'outside', same as ::C
# end
#
# +nil+ is returned when the name is not in CamelCase or the constant (or
# part of it) is unknown.
#
- # 'blargle'.safe_constantize # => nil
- # 'UnknownModule'.safe_constantize # => nil
- # 'UnknownModule::Foo::Bar'.safe_constantize # => nil
+ # safe_constantize('blargle') # => nil
+ # safe_constantize('UnknownModule') # => nil
+ # safe_constantize('UnknownModule::Foo::Bar') # => nil
def safe_constantize(camel_cased_word)
constantize(camel_cased_word)
rescue NameError => e
@@ -354,7 +354,7 @@ module ActiveSupport
# const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
# const_regexp("::") # => "::"
def const_regexp(camel_cased_word) #:nodoc:
- parts = camel_cased_word.split("::")
+ parts = camel_cased_word.split("::".freeze)
return Regexp.escape(camel_cased_word) if parts.blank?
@@ -372,7 +372,7 @@ module ActiveSupport
def apply_inflections(word, rules)
result = word.to_s.dup
- if word.empty? || inflections.uncountables.include?(result.downcase[/\b\w+\Z/])
+ if word.empty? || inflections.uncountables.uncountable?(result)
result
else
rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb
index 1cde417fc5..7b28eeb6e2 100644
--- a/activesupport/lib/active_support/inflector/transliterate.rb
+++ b/activesupport/lib/active_support/inflector/transliterate.rb
@@ -58,7 +58,7 @@ module ActiveSupport
# I18n.locale = :de
# transliterate('Jürgen')
# # => "Juergen"
- def transliterate(string, replacement = "?")
+ def transliterate(string, replacement = "?".freeze)
I18n.transliterate(ActiveSupport::Multibyte::Unicode.normalize(
ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c),
:replacement => replacement)
@@ -67,31 +67,29 @@ module ActiveSupport
# Replaces special characters in a string so that it may be used as part of
# a 'pretty' URL.
#
- # class Person
- # def to_param
- # "#{id}-#{name.parameterize}"
- # end
- # end
- #
- # @person = Person.find(1)
- # # => #<Person id: 1, name: "Donald E. Knuth">
- #
- # <%= link_to(@person.name, person_path(@person)) %>
- # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
+ # parameterize("Donald E. Knuth") # => "donald-e-knuth"
+ # parameterize("^trés|Jolie-- ") # => "tres-jolie"
def parameterize(string, sep = '-')
# replace accented chars with their ascii equivalents
parameterized_string = transliterate(string)
# Turn unwanted chars into the separator
parameterized_string.gsub!(/[^a-z0-9\-_]+/i, sep)
unless sep.nil? || sep.empty?
- re_sep = Regexp.escape(sep)
+ if sep == "-".freeze
+ re_duplicate_seperator = /-{2,}/
+ re_leading_trailing_separator = /^-|-$/i
+ else
+ re_sep = Regexp.escape(sep)
+ re_duplicate_seperator = /#{re_sep}{2,}/
+ re_leading_trailing_separator = /^#{re_sep}|#{re_sep}$/i
+ end
# No more than one of the separator in a row.
- parameterized_string.gsub!(/#{re_sep}{2,}/, sep)
+ parameterized_string.gsub!(re_duplicate_seperator, sep)
# Remove leading/trailing separator.
- parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/i, '')
+ parameterized_string.gsub!(re_leading_trailing_separator, ''.freeze)
end
- parameterized_string.downcase
+ parameterized_string.downcase!
+ parameterized_string
end
-
end
end
diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb
index 35548f3f56..2932954f03 100644
--- a/activesupport/lib/active_support/json/decoding.rb
+++ b/activesupport/lib/active_support/json/decoding.rb
@@ -9,20 +9,14 @@ module ActiveSupport
module JSON
# matches YAML-formatted dates
DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/
-
+
class << self
# Parses a JSON string (JavaScript Object Notation) into a hash.
# See http://www.json.org for more info.
#
# ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
# => {"team" => "rails", "players" => "36"}
- def decode(json, options = {})
- if options.present?
- raise ArgumentError, "In Rails 4.1, ActiveSupport::JSON.decode no longer " \
- "accepts an options hash for MultiJSON. MultiJSON reached its end of life " \
- "and has been removed."
- end
-
+ def decode(json)
data = ::JSON.parse(json, quirks_mode: true)
if ActiveSupport.parse_json_times
diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb
index a14ed7ee94..031c5e9339 100644
--- a/activesupport/lib/active_support/json/encoding.rb
+++ b/activesupport/lib/active_support/json/encoding.rb
@@ -6,7 +6,6 @@ module ActiveSupport
delegate :use_standard_json_time_format, :use_standard_json_time_format=,
:time_precision, :time_precision=,
:escape_html_entities_in_json, :escape_html_entities_in_json=,
- :encode_big_decimal_as_string, :encode_big_decimal_as_string=,
:json_encoder, :json_encoder=,
:to => :'ActiveSupport::JSON::Encoding'
end
@@ -58,6 +57,10 @@ module ActiveSupport
super.gsub ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
end
end
+
+ def to_s
+ self
+ end
end
# Mark these as private so we don't leak encoding-specific constructs
@@ -113,54 +116,6 @@ module ActiveSupport
# Sets the encoder used by Rails to encode Ruby objects into JSON strings
# in +Object#to_json+ and +ActiveSupport::JSON.encode+.
attr_accessor :json_encoder
-
- def encode_big_decimal_as_string=(as_string)
- message = \
- "The JSON encoder in Rails 4.1 no longer supports encoding BigDecimals as JSON numbers. Instead, " \
- "the new encoder will always encode them as strings.\n\n" \
- "You are seeing this error because you have 'active_support.encode_big_decimal_as_string' in " \
- "your configuration file. If you have been setting this to true, you can safely remove it from " \
- "your configuration. Otherwise, you should add the 'activesupport-json_encoder' gem to your " \
- "Gemfile in order to restore this functionality."
-
- raise NotImplementedError, message
- end
-
- def encode_big_decimal_as_string
- message = \
- "The JSON encoder in Rails 4.1 no longer supports encoding BigDecimals as JSON numbers. Instead, " \
- "the new encoder will always encode them as strings.\n\n" \
- "You are seeing this error because you are trying to check the value of the related configuration, " \
- "'active_support.encode_big_decimal_as_string'. If your application depends on this option, you should " \
- "add the 'activesupport-json_encoder' gem to your Gemfile. For now, this option will always be true. " \
- "In the future, it will be removed from Rails, so you should stop checking its value."
-
- ActiveSupport::Deprecation.warn message
-
- true
- end
-
- # Deprecate CircularReferenceError
- def const_missing(name)
- if name == :CircularReferenceError
- message = "The JSON encoder in Rails 4.1 no longer offers protection from circular references. " \
- "You are seeing this warning because you are rescuing from (or otherwise referencing) " \
- "ActiveSupport::Encoding::CircularReferenceError. In the future, this error will be " \
- "removed from Rails. You should remove these rescue blocks from your code and ensure " \
- "that your data structures are free of circular references so they can be properly " \
- "serialized into JSON.\n\n" \
- "For example, the following Hash contains a circular reference to itself:\n" \
- " h = {}\n" \
- " h['circular'] = h\n" \
- "In this case, calling h.to_json would not work properly."
-
- ActiveSupport::Deprecation.warn message
-
- SystemStackError
- else
- super
- end
- end
end
self.use_standard_json_time_format = true
diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb
index 75f353f62c..cbc20c103d 100644
--- a/activesupport/lib/active_support/log_subscriber/test_helper.rb
+++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb
@@ -11,6 +11,7 @@ module ActiveSupport
# include ActiveSupport::LogSubscriber::TestHelper
#
# def setup
+ # super
# ActiveRecord::LogSubscriber.attach_to(:active_record)
# end
#
@@ -33,7 +34,7 @@ module ActiveSupport
# you can collect them doing @logger.logged(level), where level is the level
# used in logging, like info, debug, warn and so on.
module TestHelper
- def setup
+ def setup # :nodoc:
@logger = MockLogger.new
@notifier = ActiveSupport::Notifications::Fanout.new
@@ -44,7 +45,7 @@ module ActiveSupport
ActiveSupport::Notifications.notifier = @notifier
end
- def teardown
+ def teardown # :nodoc:
set_logger(nil)
ActiveSupport::Notifications.notifier = @old_notifier
end
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index 92ab6fe648..c82a13511e 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -82,7 +82,7 @@ module ActiveSupport
def _decrypt(encrypted_message)
cipher = new_cipher
- encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.strict_decode64(v)}
+ encrypted_data, iv = encrypted_message.split("--".freeze).map {|v| ::Base64.strict_decode64(v)}
cipher.decrypt
cipher.key = @secret
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index 6cb2884fb7..64c5232cf4 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -1,5 +1,6 @@
require 'base64'
require 'active_support/core_ext/object/blank'
+require 'active_support/security_utils'
module ActiveSupport
# +MessageVerifier+ makes it easy to generate and verify messages which are
@@ -33,37 +34,90 @@ module ActiveSupport
@serializer = options[:serializer] || Marshal
end
- def verify(signed_message)
- raise InvalidSignature if signed_message.blank?
+ # Checks if a signed message could have been generated by signing an object
+ # with the +MessageVerifier+'s secret.
+ #
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
+ # signed_message = verifier.generate 'a private message'
+ # verifier.valid_message?(signed_message) # => true
+ #
+ # tampered_message = signed_message.chop # editing the message invalidates the signature
+ # verifier.valid_message?(tampered_message) # => false
+ def valid_message?(signed_message)
+ return if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank?
+
+ data, digest = signed_message.split("--".freeze)
+ data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
+ end
- data, digest = signed_message.split("--")
- if data.present? && digest.present? && secure_compare(digest, generate_digest(data))
+ # Decodes the signed message using the +MessageVerifier+'s secret.
+ #
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
+ #
+ # signed_message = verifier.generate 'a private message'
+ # verifier.verified(signed_message) # => 'a private message'
+ #
+ # Returns +nil+ if the message was not signed with the same secret.
+ #
+ # other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
+ # other_verifier.verified(signed_message) # => nil
+ #
+ # Returns +nil+ if the message is not Base64-encoded.
+ #
+ # invalid_message = "f--46a0120593880c733a53b6dad75b42ddc1c8996d"
+ # verifier.verified(invalid_message) # => nil
+ #
+ # Raises any error raised while decoding the signed message.
+ #
+ # incompatible_message = "test--dad7b06c94abba8d46a15fafaef56c327665d5ff"
+ # verifier.verified(incompatible_message) # => TypeError: incompatible marshal file format
+ def verified(signed_message)
+ if valid_message?(signed_message)
begin
- @serializer.load(::Base64.strict_decode64(data))
+ data = signed_message.split("--".freeze)[0]
+ @serializer.load(decode(data))
rescue ArgumentError => argument_error
- raise InvalidSignature if argument_error.message =~ %r{invalid base64}
+ return if argument_error.message =~ %r{invalid base64}
raise
end
- else
- raise InvalidSignature
end
end
+ # Decodes the signed message using the +MessageVerifier+'s secret.
+ #
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
+ # signed_message = verifier.generate 'a private message'
+ #
+ # verifier.verify(signed_message) # => 'a private message'
+ #
+ # Raises +InvalidSignature+ if the message was not signed with the same
+ # secret or was not Base64-encoded.
+ #
+ # other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
+ # other_verifier.verify(signed_message) # => ActiveSupport::MessageVerifier::InvalidSignature
+ def verify(signed_message)
+ verified(signed_message) || raise(InvalidSignature)
+ end
+
+ # Generates a signed message for the provided value.
+ #
+ # The message is signed with the +MessageVerifier+'s secret. Without knowing
+ # the secret, the original value cannot be extracted from the message.
+ #
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
+ # verifier.generate 'a private message' # => "BAhJIhRwcml2YXRlLW1lc3NhZ2UGOgZFVA==--e2d724331ebdee96a10fb99b089508d1c72bd772"
def generate(value)
- data = ::Base64.strict_encode64(@serializer.dump(value))
+ data = encode(@serializer.dump(value))
"#{data}--#{generate_digest(data)}"
end
private
- # constant-time comparison algorithm to prevent timing attacks
- def secure_compare(a, b)
- return false unless a.bytesize == b.bytesize
-
- l = a.unpack "C#{a.bytesize}"
+ def encode(data)
+ ::Base64.strict_encode64(data)
+ end
- res = 0
- b.each_byte { |byte| res |= byte ^ l.shift }
- res == 0
+ def decode(data)
+ ::Base64.strict_decode64(data)
end
def generate_digest(data)
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index 3c0cf9f137..45cf6fc1ef 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -86,8 +86,14 @@ module ActiveSupport #:nodoc:
@wrapped_string.split(*args).map { |i| self.class.new(i) }
end
- # Works like like <tt>String#slice!</tt>, but returns an instance of
+ # Works like <tt>String#slice!</tt>, but returns an instance of
# Chars, or nil if the string was not modified.
+ #
+ # string = 'Welcome'
+ # string.mb_chars.slice!(3) # => #<ActiveSupport::Multibyte::Chars:0x000000038109b8 @wrapped_string="c">
+ # string # => 'Welome'
+ # string.mb_chars.slice!(0..3) # => #<ActiveSupport::Multibyte::Chars:0x00000002eb80a0 @wrapped_string="Welo">
+ # string # => 'me'
def slice!(*args)
chars(@wrapped_string.slice!(*args))
end
diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb
index 7d45961515..b6c991a287 100644
--- a/activesupport/lib/active_support/multibyte/unicode.rb
+++ b/activesupport/lib/active_support/multibyte/unicode.rb
@@ -11,7 +11,7 @@ module ActiveSupport
NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
# The Unicode version that is supported by the implementation
- UNICODE_VERSION = '6.3.0'
+ UNICODE_VERSION = '7.0.0'
# The default normalization used for operations that require
# normalization. It can be set to any of the normalizations
@@ -58,7 +58,7 @@ module ActiveSupport
# Returns a regular expression pattern that matches the passed Unicode
# codepoints.
def self.codepoints_to_pattern(array_of_codepoints) #:nodoc:
- array_of_codepoints.collect{ |e| [e].pack 'U*' }.join('|')
+ array_of_codepoints.collect{ |e| [e].pack 'U*'.freeze }.join('|'.freeze)
end
TRAILERS_PAT = /(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+\Z/u
LEADERS_PAT = /\A(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+/u
@@ -211,9 +211,8 @@ module ActiveSupport
codepoints
end
- # Ruby >= 2.1 has String#scrub, which is faster than the workaround used for < 2.1.
# Rubinius' String#scrub, however, doesn't support ASCII-incompatible chars.
- if '<3'.respond_to?(:scrub) && !defined?(Rubinius)
+ if !defined?(Rubinius)
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
# resulting in a valid UTF-8 string.
#
@@ -274,7 +273,7 @@ module ActiveSupport
compose(reorder_characters(decompose(:compatibility, codepoints)))
else
raise ArgumentError, "#{form} is not a valid normalization variant", caller
- end.pack('U*')
+ end.pack('U*'.freeze)
end
def downcase(string)
@@ -339,7 +338,7 @@ module ActiveSupport
end
# Redefine the === method so we can write shorter rules for grapheme cluster breaks
- @boundary.each do |k,_|
+ @boundary.each_key do |k|
@boundary[k].instance_eval do
def ===(other)
detect { |i| i === other } ? true : false
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index 325a3d75dc..b9f8e1ab2c 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -16,7 +16,7 @@ module ActiveSupport
# render text: 'Foo'
# end
#
- # That executes the block first and notifies all subscribers once done.
+ # That first executes the block and then notifies all subscribers once done.
#
# In the example above +render+ is the name of the event, and the rest is called
# the _payload_. The payload is a mechanism that allows instrumenters to pass
diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb
index 6bf8c7d5de..0131fe2572 100644
--- a/activesupport/lib/active_support/notifications/fanout.rb
+++ b/activesupport/lib/active_support/notifications/fanout.rb
@@ -111,7 +111,7 @@ module ActiveSupport
end
end
- class Timed < Evented
+ class Timed < Evented # :nodoc:
def publish(name, *args)
@delegate.call name, *args
end
diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb
index 3a244b34b5..075ddc2382 100644
--- a/activesupport/lib/active_support/notifications/instrumenter.rb
+++ b/activesupport/lib/active_support/notifications/instrumenter.rb
@@ -57,6 +57,18 @@ module ActiveSupport
@duration = nil
end
+ # Returns the difference in milliseconds between when the execution of the
+ # event started and when it ended.
+ #
+ # ActiveSupport::Notifications.subscribe('wait') do |*args|
+ # @event = ActiveSupport::Notifications::Event.new(*args)
+ # end
+ #
+ # ActiveSupport::Notifications.instrument('wait') do
+ # sleep 1
+ # end
+ #
+ # @event.duration # => 1000.138
def duration
@duration ||= 1000.0 * (self.end - time)
end
diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb
index 5ecda9593a..9762d95145 100644
--- a/activesupport/lib/active_support/number_helper.rb
+++ b/activesupport/lib/active_support/number_helper.rb
@@ -94,9 +94,9 @@ module ActiveSupport
# * <tt>:locale</tt> - Sets the locale to be used for formatting
# (defaults to current locale).
# * <tt>:precision</tt> - Sets the precision of the number
- # (defaults to 3).
- # * <tt>:significant</tt> - If +true+, precision will be the #
- # of significant_digits. If +false+, the # of fractional
+ # (defaults to 3). Keeps the number's precision if nil.
+ # * <tt>:significant</tt> - If +true+, precision will be the number
+ # of significant_digits. If +false+, the number of fractional
# digits (defaults to +false+).
# * <tt>:separator</tt> - Sets the separator between the
# fractional and integer digits (defaults to ".").
@@ -116,6 +116,7 @@ module ActiveSupport
# number_to_percentage(1000, delimiter: '.', separator: ',') # => 1.000,000%
# number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
# number_to_percentage(1000, locale: :fr) # => 1 000,000%
+ # number_to_percentage:(1000, precision: nil) # => 1000%
# number_to_percentage('98a') # => 98a%
# number_to_percentage(100, format: '%n %') # => 100 %
def number_to_percentage(number, options = {})
@@ -134,6 +135,9 @@ module ActiveSupport
# to ",").
# * <tt>:separator</tt> - Sets the separator between the
# fractional and integer digits (defaults to ".").
+ # * <tt>:delimiter_pattern</tt> - Sets a custom regular expression used for
+ # deriving the placement of delimiter. Helpful when using currency formats
+ # like INR.
#
# ==== Examples
#
@@ -146,7 +150,10 @@ module ActiveSupport
# number_to_delimited(12345678.05, locale: :fr) # => 12 345 678,05
# number_to_delimited('112a') # => 112a
# number_to_delimited(98765432.98, delimiter: ' ', separator: ',')
- # # => 98 765 432,98
+ # # => 98 765 432,98
+ # number_to_delimited("123456.78",
+ # delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/)
+ # # => 1,23,456.78
def number_to_delimited(number, options = {})
NumberToDelimitedConverter.convert(number, options)
end
@@ -161,9 +168,9 @@ module ActiveSupport
# * <tt>:locale</tt> - Sets the locale to be used for formatting
# (defaults to current locale).
# * <tt>:precision</tt> - Sets the precision of the number
- # (defaults to 3).
- # * <tt>:significant</tt> - If +true+, precision will be the #
- # of significant_digits. If +false+, the # of fractional
+ # (defaults to 3). Keeps the number's precision if nil.
+ # * <tt>:significant</tt> - If +true+, precision will be the number
+ # of significant_digits. If +false+, the number of fractional
# digits (defaults to +false+).
# * <tt>:separator</tt> - Sets the separator between the
# fractional and integer digits (defaults to ".").
@@ -182,6 +189,7 @@ module ActiveSupport
# number_to_rounded(111.2345, significant: true) # => 111
# number_to_rounded(111.2345, precision: 1, significant: true) # => 100
# number_to_rounded(13, precision: 5, significant: true) # => 13.000
+ # number_to_rounded(13, precision: nil) # => 13
# number_to_rounded(111.234, locale: :fr) # => 111,234
#
# number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
@@ -208,8 +216,8 @@ module ActiveSupport
# (defaults to current locale).
# * <tt>:precision</tt> - Sets the precision of the number
# (defaults to 3).
- # * <tt>:significant</tt> - If +true+, precision will be the #
- # of significant_digits. If +false+, the # of fractional
+ # * <tt>:significant</tt> - If +true+, precision will be the number
+ # of significant_digits. If +false+, the number of fractional
# digits (defaults to +true+)
# * <tt>:separator</tt> - Sets the separator between the
# fractional and integer digits (defaults to ".").
@@ -218,8 +226,6 @@ module ActiveSupport
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
# insignificant zeros after the decimal separator (defaults to
# +true+)
- # * <tt>:prefix</tt> - If +:si+ formats the number using the SI
- # prefix (defaults to :binary)
#
# ==== Examples
#
@@ -258,8 +264,8 @@ module ActiveSupport
# (defaults to current locale).
# * <tt>:precision</tt> - Sets the precision of the number
# (defaults to 3).
- # * <tt>:significant</tt> - If +true+, precision will be the #
- # of significant_digits. If +false+, the # of fractional
+ # * <tt>:significant</tt> - If +true+, precision will be the number
+ # of significant_digits. If +false+, the number of fractional
# digits (defaults to +true+)
# * <tt>:separator</tt> - Sets the separator between the
# fractional and integer digits (defaults to ".").
@@ -272,12 +278,12 @@ module ActiveSupport
# string containing an i18n scope where to find this hash. It
# might have the following keys:
# * *integers*: <tt>:unit</tt>, <tt>:ten</tt>,
- # *<tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>,
- # *<tt>:billion</tt>, <tt>:trillion</tt>,
- # *<tt>:quadrillion</tt>
+ # <tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>,
+ # <tt>:billion</tt>, <tt>:trillion</tt>,
+ # <tt>:quadrillion</tt>
# * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>,
- # *<tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>,
- # *<tt>:pico</tt>, <tt>:femto</tt>
+ # <tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>,
+ # <tt>:pico</tt>, <tt>:femto</tt>
# * <tt>:format</tt> - Sets the format of the output string
# (defaults to "%n %u"). The field types are:
# * %u - The quantifier (ex.: 'thousand')
diff --git a/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb b/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
index 9ae27a896a..7986eb50f0 100644
--- a/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
@@ -13,7 +13,7 @@ module ActiveSupport
end
rounded_number = NumberToRoundedConverter.convert(number, options)
- format.gsub('%n', rounded_number).gsub('%u', options[:unit])
+ format.gsub('%n'.freeze, rounded_number).gsub('%u'.freeze, options[:unit])
end
private
@@ -23,20 +23,20 @@ module ActiveSupport
end
def absolute_value(number)
- number.respond_to?("abs") ? number.abs : number.sub(/\A-/, '')
+ number.respond_to?(:abs) ? number.abs : number.sub(/\A-/, '')
end
def options
@options ||= begin
defaults = default_format_options.merge(i18n_opts)
- # Override negative format if format options is given
+ # Override negative format if format options are given
defaults[:negative_format] = "-#{opts[:format]}" if opts[:format]
defaults.merge!(opts)
end
end
def i18n_opts
- # Set International negative format if not exists
+ # Set International negative format if it does not exist
i18n = i18n_format_options
i18n[:negative_format] ||= "-#{i18n[:format]}" if i18n[:format]
i18n
diff --git a/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb b/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
index d85cc086d7..45ae8f1a93 100644
--- a/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
@@ -3,7 +3,7 @@ module ActiveSupport
class NumberToDelimitedConverter < NumberConverter #:nodoc:
self.validate_float = true
- DELIMITED_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
+ DEFAULT_DELIMITER_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
def convert
parts.join(options[:separator])
@@ -13,11 +13,16 @@ module ActiveSupport
def parts
left, right = number.to_s.split('.')
- left.gsub!(DELIMITED_REGEX) do |digit_to_delimit|
+ left.gsub!(delimiter_pattern) do |digit_to_delimit|
"#{digit_to_delimit}#{options[:delimiter]}"
end
[left, right].compact
end
+
+ def delimiter_pattern
+ options.fetch(:delimiter_pattern, DEFAULT_DELIMITER_REGEX)
+ end
+
end
end
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_human_converter.rb b/activesupport/lib/active_support/number_helper/number_to_human_converter.rb
index 9a3dc526ae..5c6fe2df83 100644
--- a/activesupport/lib/active_support/number_helper/number_to_human_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_human_converter.rb
@@ -23,7 +23,7 @@ module ActiveSupport
unit = determine_unit(units, exponent)
rounded_number = NumberToRoundedConverter.convert(number, options)
- format.gsub(/%n/, rounded_number).gsub(/%u/, unit).strip
+ format.gsub('%n'.freeze, rounded_number).gsub('%u'.freeze, unit).strip
end
private
@@ -59,7 +59,7 @@ module ActiveSupport
translate_in_locale("human.decimal_units.units", raise: true)
else
raise ArgumentError, ":units must be a Hash or String translation scope."
- end.keys.map { |e_name| INVERTED_DECIMAL_UNITS[e_name] }.sort_by { |e| -e }
+ end.keys.map { |e_name| INVERTED_DECIMAL_UNITS[e_name] }.sort_by(&:-@)
end
end
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb b/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb
index 78d2c9ae6e..a4a8690bcd 100644
--- a/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb
@@ -7,6 +7,10 @@ module ActiveSupport
self.validate_float = true
def convert
+ if opts.key?(:prefix)
+ ActiveSupport::Deprecation.warn('The :prefix option of `number_to_human_size` is deprecated and will be removed in Rails 5.1 with no replacement.')
+ end
+
@number = Float(number)
# for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
@@ -20,7 +24,7 @@ module ActiveSupport
human_size = number / (base ** exponent)
number_to_format = NumberToRoundedConverter.convert(human_size, options)
end
- conversion_format.gsub(/%n/, number_to_format).gsub(/%u/, unit)
+ conversion_format.gsub('%n'.freeze, number_to_format).gsub('%u'.freeze, unit)
end
private
diff --git a/activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb b/activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb
index eafe2844f7..4c04d40c19 100644
--- a/activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb
@@ -5,7 +5,7 @@ module ActiveSupport
def convert
rounded_number = NumberToRoundedConverter.convert(number, options)
- options[:format].gsub('%n', rounded_number)
+ options[:format].gsub('%n'.freeze, rounded_number)
end
end
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb
index dcf9a567e8..981c562551 100644
--- a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb
@@ -6,36 +6,39 @@ module ActiveSupport
def convert
precision = options.delete :precision
- significant = options.delete :significant
- case number
- when Float, String
- @number = BigDecimal(number.to_s)
- when Rational
- @number = BigDecimal(number, digit_count(number.to_i) + precision)
- else
- @number = number.to_d
- end
-
- if significant && precision > 0
- digits, rounded_number = digits_and_rounded_number(precision)
- precision -= digits
- precision = 0 if precision < 0 # don't let it be negative
- else
- rounded_number = number.round(precision)
- rounded_number = rounded_number.to_i if precision == 0
- rounded_number = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros
- end
+ if precision
+ case number
+ when Float, String
+ @number = BigDecimal(number.to_s)
+ when Rational
+ @number = BigDecimal(number, digit_count(number.to_i) + precision)
+ else
+ @number = number.to_d
+ end
- formatted_string =
- if BigDecimal === rounded_number && rounded_number.finite?
- s = rounded_number.to_s('F') + '0'*precision
- a, b = s.split('.', 2)
- a + '.' + b[0, precision]
+ if options.delete(:significant) && precision > 0
+ digits, rounded_number = digits_and_rounded_number(precision)
+ precision -= digits
+ precision = 0 if precision < 0 # don't let it be negative
else
- "%00.#{precision}f" % rounded_number
+ rounded_number = number.round(precision)
+ rounded_number = rounded_number.to_i if precision == 0 && rounded_number.finite?
+ rounded_number = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros
end
+ formatted_string =
+ if BigDecimal === rounded_number && rounded_number.finite?
+ s = rounded_number.to_s('F') + '0'*precision
+ a, b = s.split('.', 2)
+ a + '.' + b[0, precision]
+ else
+ "%00.#{precision}f" % rounded_number
+ end
+ else
+ formatted_string = number
+ end
+
delimited_number = NumberToDelimitedConverter.convert(formatted_string, options)
format_number(delimited_number)
end
diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb
index a33e2c58a9..45864990ce 100644
--- a/activesupport/lib/active_support/ordered_options.rb
+++ b/activesupport/lib/active_support/ordered_options.rb
@@ -6,6 +6,7 @@ module ActiveSupport
# h[:girl] = 'Mary'
# h[:boy] # => 'John'
# h[:girl] # => 'Mary'
+ # h[:dog] # => nil
#
# Using +OrderedOptions+, the above code could be reduced to:
#
@@ -14,6 +15,13 @@ module ActiveSupport
# h.girl = 'Mary'
# h.boy # => 'John'
# h.girl # => 'Mary'
+ # h.dog # => nil
+ #
+ # To raise an exception when the value is blank, append a
+ # bang to the key name, like:
+ #
+ # h.dog! # => raises KeyError
+ #
class OrderedOptions < Hash
alias_method :_get, :[] # preserve the original #[] method
protected :_get # make it protected
@@ -31,7 +39,13 @@ module ActiveSupport
if name_string.chomp!('=')
self[name_string] = args.first
else
- self[name]
+ bangs = name_string.chomp!('!')
+
+ if bangs
+ fetch(name_string.to_sym).presence || raise(KeyError.new("#{name_string} is blank."))
+ else
+ self[name_string]
+ end
end
end
diff --git a/activesupport/lib/active_support/rails.rb b/activesupport/lib/active_support/rails.rb
index b05c3ff126..c8e3a4bf53 100644
--- a/activesupport/lib/active_support/rails.rb
+++ b/activesupport/lib/active_support/rails.rb
@@ -1,8 +1,8 @@
# This is private interface.
#
# Rails components cherry pick from Active Support as needed, but there are a
-# few features that are used for sure some way or another and it is not worth
-# to put individual requires absolutely everywhere. Think blank? for example.
+# few features that are used for sure in some way or another and it is not worth
+# putting individual requires absolutely everywhere. Think blank? for example.
#
# This file is loaded by every Rails component except Active Support itself,
# but it does not belong to the Rails public interface. It is internal to
diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb
index 133aa6a054..cd0fb51009 100644
--- a/activesupport/lib/active_support/railtie.rb
+++ b/activesupport/lib/active_support/railtie.rb
@@ -16,6 +16,11 @@ module ActiveSupport
# Sets the default value for Time.zone
# If assigned value cannot be matched to a TimeZone, an exception will be raised.
initializer "active_support.initialize_time_zone" do |app|
+ begin
+ TZInfo::DataSource.get
+ rescue TZInfo::DataSourceNotFound => e
+ raise e.exception "tzinfo-data is not present. Please add gem 'tzinfo-data' to your Gemfile and run bundle install"
+ end
require 'active_support/core_ext/time/zones'
zone_default = Time.find_zone!(app.config.time_zone)
diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb
index a7eba91ac5..fcf5553061 100644
--- a/activesupport/lib/active_support/rescuable.rb
+++ b/activesupport/lib/active_support/rescuable.rb
@@ -60,7 +60,7 @@ module ActiveSupport
end
klasses.each do |klass|
- key = if klass.is_a?(Class) && klass <= Exception
+ key = if klass.is_a?(Module) && klass.respond_to?(:===)
klass.name
elsif klass.is_a?(String)
klass
@@ -68,7 +68,7 @@ module ActiveSupport
raise ArgumentError, "#{klass} is neither an Exception nor a String"
end
- # put the new handler at the end because the list is read in reverse
+ # Put the new handler at the end because the list is read in reverse.
self.rescue_handlers += [[key, options[:with]]]
end
end
@@ -100,8 +100,8 @@ module ActiveSupport
# a string, otherwise a NameError will be raised by the interpreter
# itself when rescue_from CONSTANT is executed.
klass = self.class.const_get(klass_name) rescue nil
- klass ||= klass_name.constantize rescue nil
- exception.is_a?(klass) if klass
+ klass ||= (klass_name.constantize rescue nil)
+ klass === exception if klass
end
case rescuer
diff --git a/activesupport/lib/active_support/security_utils.rb b/activesupport/lib/active_support/security_utils.rb
new file mode 100644
index 0000000000..64c4801179
--- /dev/null
+++ b/activesupport/lib/active_support/security_utils.rb
@@ -0,0 +1,20 @@
+module ActiveSupport
+ module SecurityUtils
+ # Constant time string comparison.
+ #
+ # The values compared should be of fixed length, such as strings
+ # that have already been processed by HMAC. This should not be used
+ # on variable length plaintext strings because it could leak length info
+ # via timing attacks.
+ def secure_compare(a, b)
+ return false unless a.bytesize == b.bytesize
+
+ l = a.unpack "C#{a.bytesize}"
+
+ res = 0
+ b.each_byte { |byte| res |= byte ^ l.shift }
+ res == 0
+ end
+ module_function :secure_compare
+ end
+end
diff --git a/activesupport/lib/active_support/string_inquirer.rb b/activesupport/lib/active_support/string_inquirer.rb
index 45271c9163..bc673150d0 100644
--- a/activesupport/lib/active_support/string_inquirer.rb
+++ b/activesupport/lib/active_support/string_inquirer.rb
@@ -1,7 +1,7 @@
module ActiveSupport
# Wrapping a string in this class gives you a prettier way to test
# for equality. The value returned by <tt>Rails.env</tt> is wrapped
- # in a StringInquirer object so instead of calling this:
+ # in a StringInquirer object, so instead of calling this:
#
# Rails.env == 'production'
#
diff --git a/activesupport/lib/active_support/subscriber.rb b/activesupport/lib/active_support/subscriber.rb
index 98be78b41b..1cd4b807ad 100644
--- a/activesupport/lib/active_support/subscriber.rb
+++ b/activesupport/lib/active_support/subscriber.rb
@@ -5,24 +5,19 @@ module ActiveSupport
# 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
+ # An example would be an Active Record subscriber responsible for collecting
# statistics about queries:
#
# module ActiveRecord
# class StatsSubscriber < ActiveSupport::Subscriber
+ # attach_to :active_record
+ #
# 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.
@@ -66,7 +61,7 @@ module ActiveSupport
pattern = "#{event}.#{namespace}"
- # don't add multiple subscribers (eg. if methods are redefined)
+ # Don't add multiple subscribers (eg. if methods are redefined).
return if subscriber.patterns.include?(pattern)
subscriber.patterns << pattern
@@ -96,7 +91,7 @@ module ActiveSupport
event.end = finished
event.payload.merge!(payload)
- method = name.split('.').first
+ method = name.split('.'.freeze).first
send(method, event)
end
diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb
index d5c2222d2e..bcd7bf74c0 100644
--- a/activesupport/lib/active_support/tagged_logging.rb
+++ b/activesupport/lib/active_support/tagged_logging.rb
@@ -43,7 +43,9 @@ module ActiveSupport
end
def current_tags
- Thread.current[:activesupport_tagged_logging_tags] ||= []
+ # We use our object ID here to avoid conflicting with other instances
+ thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}".freeze
+ Thread.current[thread_key] ||= []
end
private
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index 4c3e77b7fd..ae6f00b861 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -8,44 +8,46 @@ require 'active_support/testing/declarative'
require 'active_support/testing/isolation'
require 'active_support/testing/constant_lookup'
require 'active_support/testing/time_helpers'
+require 'active_support/testing/file_fixtures'
+require 'active_support/testing/composite_filter'
require 'active_support/core_ext/kernel/reporting'
-require 'active_support/deprecation'
module ActiveSupport
class TestCase < ::Minitest::Test
Assertion = Minitest::Assertion
class << self
+ # Sets the order in which test cases are run.
+ #
+ # ActiveSupport::TestCase.test_order = :random # => :random
+ #
+ # Valid values are:
+ # * +:random+ (to run tests in random order)
+ # * +:parallel+ (to run tests in parallel)
+ # * +:sorted+ (to run tests alphabetically by method name)
+ # * +:alpha+ (equivalent to +:sorted+)
def test_order=(new_order)
ActiveSupport.test_order = new_order
end
+ # Returns the order in which test cases are run.
+ #
+ # ActiveSupport::TestCase.test_order # => :random
+ #
+ # Possible values are +:random+, +:parallel+, +:alpha+, +:sorted+.
+ # Defaults to +:random+.
def test_order
- test_order = ActiveSupport.test_order
-
- if test_order.nil?
- ActiveSupport::Deprecation.warn "You did not specify a value for the " \
- "configuration option 'active_support.test_order'. In Rails 5.0, " \
- "the default value of this option will change from `:sorted` to " \
- "`:random`.\n" \
- "To disable this warning and keep the current behavior, you can add " \
- "the following line to your `config/environments/test.rb`:\n" \
- "\n" \
- " Rails.application.configure do\n" \
- " config.active_support.test_order = :sorted\n" \
- " end\n" \
- "\n" \
- "Alternatively, you can opt into the future behavior by setting this " \
- "option to `:random`."
+ ActiveSupport.test_order ||= :random
+ end
- test_order = :sorted
- self.test_order = test_order
+ def run(reporter, options = {})
+ if options[:patterns] && options[:patterns].any? { |p| p =~ /:\d+/ }
+ options[:filter] = \
+ Testing::CompositeFilter.new(self, options[:filter], options[:patterns])
end
- test_order
+ super
end
-
- alias :my_tests_are_order_dependent! :i_suck_and_my_tests_are_order_dependent!
end
alias_method :method_name, :name
@@ -55,6 +57,7 @@ module ActiveSupport
include ActiveSupport::Testing::Assertions
include ActiveSupport::Testing::Deprecation
include ActiveSupport::Testing::TimeHelpers
+ include ActiveSupport::Testing::FileFixtures
extend ActiveSupport::Testing::Declarative
# test/unit backwards compatibility methods
@@ -73,7 +76,7 @@ module ActiveSupport
alias :assert_not_respond_to :refute_respond_to
alias :assert_not_same :refute_same
- # Fails if the block raises an exception.
+ # Reveals the intention that the block should not raise any exception.
#
# assert_nothing_raised do
# ...
diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb
index 11cca82995..d87ce3474d 100644
--- a/activesupport/lib/active_support/testing/assertions.rb
+++ b/activesupport/lib/active_support/testing/assertions.rb
@@ -23,42 +23,42 @@ module ActiveSupport
# result of what is evaluated in the yielded block.
#
# assert_difference 'Article.count' do
- # post :create, article: {...}
+ # post :create, params: { article: {...} }
# end
#
# An arbitrary expression is passed in and evaluated.
#
- # assert_difference 'assigns(:article).comments(:reload).size' do
- # post :create, comment: {...}
+ # assert_difference 'Article.last.comments(:reload).size' do
+ # post :create, params: { comment: {...} }
# end
#
# An arbitrary positive or negative difference can be specified.
# The default is <tt>1</tt>.
#
# assert_difference 'Article.count', -1 do
- # post :delete, id: ...
+ # post :delete, params: { id: ... }
# end
#
# An array of expressions can also be passed in and evaluated.
#
# assert_difference [ 'Article.count', 'Post.count' ], 2 do
- # post :create, article: {...}
+ # post :create, params: { article: {...} }
# end
#
# A lambda or a list of lambdas can be passed in and evaluated:
#
# assert_difference ->{ Article.count }, 2 do
- # post :create, article: {...}
+ # post :create, params: { article: {...} }
# end
#
# assert_difference [->{ Article.count }, ->{ Post.count }], 2 do
- # post :create, article: {...}
+ # post :create, params: { article: {...} }
# end
#
# An error message can be specified.
#
# assert_difference 'Article.count', -1, 'An Article should be destroyed' do
- # post :delete, id: ...
+ # post :delete, params: { id: ... }
# end
def assert_difference(expression, difference = 1, message = nil, &block)
expressions = Array(expression)
@@ -66,7 +66,7 @@ module ActiveSupport
exps = expressions.map { |e|
e.respond_to?(:call) ? e : lambda { eval(e, block.binding) }
}
- before = exps.map { |e| e.call }
+ before = exps.map(&:call)
yield
@@ -81,13 +81,13 @@ module ActiveSupport
# changed before and after invoking the passed in block.
#
# assert_no_difference 'Article.count' do
- # post :create, article: invalid_attributes
+ # post :create, params: { article: invalid_attributes }
# end
#
# An error message can be specified.
#
# assert_no_difference 'Article.count', 'An Article should not be created' do
- # post :create, article: invalid_attributes
+ # post :create, params: { article: invalid_attributes }
# end
def assert_no_difference(expression, message = nil, &block)
assert_difference expression, 0, message, &block
diff --git a/activesupport/lib/active_support/testing/autorun.rb b/activesupport/lib/active_support/testing/autorun.rb
index 5aa5f46310..84c6b89340 100644
--- a/activesupport/lib/active_support/testing/autorun.rb
+++ b/activesupport/lib/active_support/testing/autorun.rb
@@ -2,4 +2,11 @@ gem 'minitest'
require 'minitest'
-Minitest.autorun
+if Minitest.respond_to?(:run_with_rails_extension)
+ unless Minitest.run_with_rails_extension
+ Minitest.run_with_autorun = true
+ Minitest.autorun
+ end
+else
+ Minitest.autorun
+end
diff --git a/activesupport/lib/active_support/testing/composite_filter.rb b/activesupport/lib/active_support/testing/composite_filter.rb
new file mode 100644
index 0000000000..bde723e30b
--- /dev/null
+++ b/activesupport/lib/active_support/testing/composite_filter.rb
@@ -0,0 +1,54 @@
+require 'method_source'
+
+module ActiveSupport
+ module Testing
+ class CompositeFilter # :nodoc:
+ def initialize(runnable, filter, patterns)
+ @runnable = runnable
+ @filters = [ derive_regexp(filter), *derive_line_filters(patterns) ].compact
+ end
+
+ def ===(method)
+ @filters.any? { |filter| filter === method }
+ end
+
+ private
+ def derive_regexp(filter)
+ filter =~ %r%/(.*)/% ? Regexp.new($1) : filter
+ end
+
+ def derive_line_filters(patterns)
+ patterns.map do |file_and_line|
+ file, line = file_and_line.split(':')
+ Filter.new(@runnable, file, line) if file
+ end
+ end
+
+ class Filter # :nodoc:
+ def initialize(runnable, file, line)
+ @runnable, @file = runnable, File.expand_path(file)
+ @line = line.to_i if line
+ end
+
+ def ===(method)
+ return unless @runnable.method_defined?(method)
+
+ if @line
+ test_file, test_range = definition_for(@runnable.instance_method(method))
+ test_file == @file && test_range.include?(@line)
+ else
+ @runnable.instance_method(method).source_location.first == @file
+ end
+ end
+
+ private
+ def definition_for(method)
+ file, start_line = method.source_location
+ end_line = method.source.count("\n") + start_line - 1
+
+ return file, start_line..end_line
+ end
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/testing/file_fixtures.rb b/activesupport/lib/active_support/testing/file_fixtures.rb
new file mode 100644
index 0000000000..4c6a0801b8
--- /dev/null
+++ b/activesupport/lib/active_support/testing/file_fixtures.rb
@@ -0,0 +1,34 @@
+module ActiveSupport
+ module Testing
+ # Adds simple access to sample files called file fixtures.
+ # File fixtures are normal files stored in
+ # <tt>ActiveSupport::TestCase.file_fixture_path</tt>.
+ #
+ # File fixtures are represented as +Pathname+ objects.
+ # This makes it easy to extract specific information:
+ #
+ # file_fixture("example.txt").read # get the file's content
+ # file_fixture("example.mp3").size # get the file size
+ module FileFixtures
+ extend ActiveSupport::Concern
+
+ included do
+ class_attribute :file_fixture_path, instance_writer: false
+ end
+
+ # Returns a +Pathname+ to the fixture file named +fixture_name+.
+ #
+ # Raises ArgumentError if +fixture_name+ can't be found.
+ def file_fixture(fixture_name)
+ path = Pathname.new(File.join(file_fixture_path, fixture_name))
+
+ if path.exist?
+ path
+ else
+ msg = "the directory '%s' does not contain a file named '%s'"
+ raise ArgumentError, msg % [file_fixture_path, fixture_name]
+ end
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index 68bda35980..1de0a19998 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -1,5 +1,3 @@
-require 'rbconfig'
-
module ActiveSupport
module Testing
module Isolation
@@ -12,7 +10,7 @@ module ActiveSupport
end
def self.forking_env?
- !ENV["NO_FORK"] && ((RbConfig::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/))
+ !ENV["NO_FORK"] && Process.respond_to?(:fork)
end
@@class_setup_mutex = Mutex.new
@@ -71,17 +69,17 @@ module ActiveSupport
else
Tempfile.open("isolation") do |tmpfile|
env = {
- ISOLATION_TEST: self.class.name,
- ISOLATION_OUTPUT: tmpfile.path
+ 'ISOLATION_TEST' => self.class.name,
+ 'ISOLATION_OUTPUT' => tmpfile.path
}
load_paths = $-I.map {|p| "-I\"#{File.expand_path(p)}\"" }.join(" ")
orig_args = ORIG_ARGV.join(" ")
test_opts = "-n#{self.class.name}##{self.name}"
- command = "#{Gem.ruby} #{load_paths} #{$0} #{orig_args} #{test_opts}"
+ command = "#{Gem.ruby} #{load_paths} #{$0} '#{orig_args}' #{test_opts}"
# IO.popen lets us pass env in a cross-platform way
- child = IO.popen([env, command])
+ child = IO.popen(env, command)
begin
Process.wait(child.pid)
diff --git a/activesupport/lib/active_support/testing/method_call_assertions.rb b/activesupport/lib/active_support/testing/method_call_assertions.rb
new file mode 100644
index 0000000000..fccaa54f40
--- /dev/null
+++ b/activesupport/lib/active_support/testing/method_call_assertions.rb
@@ -0,0 +1,41 @@
+require 'minitest/mock'
+
+module ActiveSupport
+ module Testing
+ module MethodCallAssertions # :nodoc:
+ private
+ def assert_called(object, method_name, message = nil, times: 1, returns: nil)
+ times_called = 0
+
+ object.stub(method_name, proc { times_called += 1; returns }) { yield }
+
+ error = "Expected #{method_name} to be called #{times} times, " \
+ "but was called #{times_called} times"
+ error = "#{message}.\n#{error}" if message
+ assert_equal times, times_called, error
+ end
+
+ def assert_called_with(object, method_name, args = [], returns: nil)
+ mock = Minitest::Mock.new
+
+ if args.all? { |arg| arg.is_a?(Array) }
+ args.each { |arg| mock.expect(:call, returns, arg) }
+ else
+ mock.expect(:call, returns, args)
+ end
+
+ object.stub(method_name, mock) { yield }
+
+ mock.verify
+ end
+
+ def assert_not_called(object, method_name, message = nil, &block)
+ assert_called(object, method_name, message, times: 0, &block)
+ end
+
+ def stub_any_instance(klass, instance: klass.new)
+ klass.stub(:new, instance) { yield instance }
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/testing/stream.rb b/activesupport/lib/active_support/testing/stream.rb
new file mode 100644
index 0000000000..895192ad05
--- /dev/null
+++ b/activesupport/lib/active_support/testing/stream.rb
@@ -0,0 +1,42 @@
+module ActiveSupport
+ module Testing
+ module Stream #:nodoc:
+ private
+
+ def silence_stream(stream)
+ old_stream = stream.dup
+ stream.reopen(IO::NULL)
+ stream.sync = true
+ yield
+ ensure
+ stream.reopen(old_stream)
+ old_stream.close
+ end
+
+ def quietly
+ silence_stream(STDOUT) do
+ silence_stream(STDERR) do
+ yield
+ end
+ end
+ end
+
+ def capture(stream)
+ stream = stream.to_s
+ captured_stream = Tempfile.new(stream)
+ stream_io = eval("$#{stream}")
+ origin_stream = stream_io.dup
+ stream_io.reopen(captured_stream)
+
+ yield
+
+ stream_io.rewind
+ return captured_stream.read
+ ensure
+ captured_stream.close
+ captured_stream.unlink
+ stream_io.reopen(origin_stream)
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/testing/time_helpers.rb b/activesupport/lib/active_support/testing/time_helpers.rb
index 8c63815660..fca0947c5b 100644
--- a/activesupport/lib/active_support/testing/time_helpers.rb
+++ b/activesupport/lib/active_support/testing/time_helpers.rb
@@ -39,15 +39,16 @@ module ActiveSupport
end
end
- # Containing helpers that helps you test passage of time.
+ # Contains helpers that help you test passage of time.
module TimeHelpers
# Changes current time to the time in the future or in the past by a given time difference by
- # stubbing +Time.now+ and +Date.today+.
+ # stubbing +Time.now+, +Date.today+, and +DateTime.now+.
#
- # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
# travel 1.day
- # Time.current # => Sun, 10 Nov 2013 15:34:49 EST -05:00
- # Date.current # => Sun, 10 Nov 2013
+ # Time.current # => Sun, 10 Nov 2013 15:34:49 EST -05:00
+ # Date.current # => Sun, 10 Nov 2013
+ # DateTime.current # => Sun, 10 Nov 2013 15:34:49 -0500
#
# This method also accepts a block, which will return the current time back to its original
# state at the end of the block:
@@ -61,13 +62,14 @@ module ActiveSupport
travel_to Time.now + duration, &block
end
- # Changes current time to the given time by stubbing +Time.now+ and
- # +Date.today+ to return the time or date passed into this method.
+ # Changes current time to the given time by stubbing +Time.now+,
+ # +Date.today+, and +DateTime.now+ to return the time or date passed into this method.
#
- # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
# travel_to Time.new(2004, 11, 24, 01, 04, 44)
- # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
- # Date.current # => Wed, 24 Nov 2004
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
+ # Date.current # => Wed, 24 Nov 2004
+ # DateTime.current # => Wed, 24 Nov 2004 01:04:44 -0500
#
# Dates are taken as their timestamp at the beginning of the day in the
# application time zone. <tt>Time.current</tt> returns said timestamp,
@@ -99,6 +101,7 @@ module ActiveSupport
simple_stubs.stub_object(Time, :now, now)
simple_stubs.stub_object(Date, :today, now.to_date)
+ simple_stubs.stub_object(DateTime, :now, now.to_datetime)
if block_given?
begin
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index 4a0ed356b1..817522677f 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -1,3 +1,4 @@
+require 'active_support/duration'
require 'active_support/values/time_zone'
require 'active_support/core_ext/object/acts_like'
@@ -75,8 +76,8 @@ module ActiveSupport
# Returns a <tt>Time.local()</tt> instance of the simultaneous time in your
# system's <tt>ENV['TZ']</tt> zone.
- def localtime
- utc.respond_to?(:getlocal) ? utc.getlocal : utc.to_time.getlocal
+ def localtime(utc_offset = nil)
+ utc.respond_to?(:getlocal) ? utc.getlocal(utc_offset) : utc.to_time.getlocal(utc_offset)
end
alias_method :getlocal, :localtime
@@ -121,16 +122,25 @@ module ActiveSupport
utc? && alternate_utc_string || TimeZone.seconds_to_utc_offset(utc_offset, colon)
end
- # Time uses +zone+ to display the time zone abbreviation, so we're
- # duck-typing it.
+ # Returns the time zone abbreviation.
+ #
+ # Time.zone = 'Eastern Time (US & Canada)' # => "Eastern Time (US & Canada)"
+ # Time.zone.now.zone # => "EST"
def zone
period.zone_identifier.to_s
end
+ # Returns a string of the object's date, time, zone and offset from UTC.
+ #
+ # Time.zone.now.httpdate # => "Thu, 04 Dec 2014 11:00:25 EST -05:00"
def inspect
"#{time.strftime('%a, %d %b %Y %H:%M:%S')} #{zone} #{formatted_offset}"
end
+ # Returns a string of the object's date and time in the ISO 8601 standard
+ # format.
+ #
+ # Time.zone.now.xmlschema # => "2014-12-04T11:02:37-05:00"
def xmlschema(fraction_digits = 0)
fraction = if fraction_digits.to_i > 0
(".%06i" % time.usec)[0, fraction_digits.to_i + 1]
@@ -160,12 +170,13 @@ module ActiveSupport
end
end
- def encode_with(coder)
- if coder.respond_to?(:represent_object)
- coder.represent_object(nil, utc)
- else
- coder.represent_scalar(nil, utc.strftime("%Y-%m-%d %H:%M:%S.%9NZ"))
- end
+ def init_with(coder) #:nodoc:
+ initialize(coder['utc'], coder['zone'], coder['time'])
+ end
+
+ def encode_with(coder) #:nodoc:
+ coder.tag = '!ruby/object:ActiveSupport::TimeWithZone'
+ coder.map = { 'utc' => utc, 'zone' => time_zone, 'time' => time }
end
# Returns a string of the object's date and time in the format used by
@@ -187,7 +198,7 @@ module ActiveSupport
# Returns a string of the object's date and time.
# Accepts an optional <tt>format</tt>:
- # * <tt>:default</tt> - default value, mimics Ruby 1.9 Time#to_s format.
+ # * <tt>:default</tt> - default value, mimics Ruby Time#to_s format.
# * <tt>:db</tt> - format outputs time in UTC :db time. See Time#to_formatted_s(:db).
# * Any key in <tt>Time::DATE_FORMATS</tt> can be used. See active_support/core_ext/time/conversions.rb.
def to_s(format = :default)
@@ -196,20 +207,16 @@ module ActiveSupport
elsif formatter = ::Time::DATE_FORMATS[format]
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
else
- "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby 1.9 Time#to_s format
+ "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby Time#to_s format
end
end
alias_method :to_formatted_s, :to_s
- # Replaces <tt>%Z</tt> and <tt>%z</tt> directives with +zone+ and
- # +formatted_offset+, respectively, before passing to Time#strftime, so
- # that zone information is correct
+ # Replaces <tt>%Z</tt> directive with +zone before passing to Time#strftime,
+ # so that zone information is correct.
def strftime(format)
- format = format.gsub('%Z', zone)
- .gsub('%z', formatted_offset(false))
- .gsub('%:z', formatted_offset(true))
- .gsub('%::z', formatted_offset(true) + ":00")
- time.strftime(format)
+ format = format.gsub(/((?:\A|[^%])(?:%%)*)%Z/, "\\1#{zone}")
+ getlocal(utc_offset).strftime(format)
end
# Use the time in UTC for comparisons.
@@ -239,17 +246,32 @@ module ActiveSupport
utc.future?
end
+ # Returns +true+ if +other+ is equal to current object.
def eql?(other)
- utc.eql?(other)
+ other.eql?(utc)
end
def hash
utc.hash
end
+ # Adds an interval of time to the current object's time and returns that
+ # value as a new TimeWithZone object.
+ #
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
+ # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
+ # now + 1000 # => Sun, 02 Nov 2014 01:43:08 EDT -04:00
+ #
+ # If we're adding a Duration of variable length (i.e., years, months, days),
+ # move forward from #time, otherwise move forward from #utc, for accuracy
+ # when moving across DST boundaries.
+ #
+ # For instance, a time + 24.hours will advance exactly 24 hours, while a
+ # time + 1.day will advance 23-25 hours, depending on the day.
+ #
+ # now + 24.hours # => Mon, 03 Nov 2014 00:26:28 EST -05:00
+ # now + 1.day # => Mon, 03 Nov 2014 01:26:28 EST -05:00
def +(other)
- # If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
- # otherwise move forward from #utc, for accuracy when moving across DST boundaries
if duration_of_variable_length?(other)
method_missing(:+, other)
else
@@ -257,10 +279,25 @@ module ActiveSupport
result.in_time_zone(time_zone)
end
end
+ alias_method :since, :+
+ # Returns a new TimeWithZone object that represents the difference between
+ # the current object's time and the +other+ time.
+ #
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
+ # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28 EST -05:00
+ # now - 1000 # => Sun, 02 Nov 2014 01:09:48 EST -05:00
+ #
+ # If subtracting a Duration of variable length (i.e., years, months, days),
+ # move backward from #time, otherwise move backward from #utc, for accuracy
+ # when moving across DST boundaries.
+ #
+ # For instance, a time - 24.hours will go subtract exactly 24 hours, while a
+ # time - 1.day will subtract 23-25 hours, depending on the day.
+ #
+ # now - 24.hours # => Sat, 01 Nov 2014 02:26:28 EDT -04:00
+ # now - 1.day # => Sat, 01 Nov 2014 01:26:28 EDT -04:00
def -(other)
- # If we're subtracting a Duration of variable length (i.e., years, months, days), move backwards from #time,
- # otherwise move backwards #utc, for accuracy when moving across DST boundaries
if other.acts_like?(:time)
to_time - other.to_time
elsif duration_of_variable_length?(other)
@@ -271,16 +308,6 @@ module ActiveSupport
end
end
- def since(other)
- # If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
- # otherwise move forward from #utc, for accuracy when moving across DST boundaries
- if duration_of_variable_length?(other)
- method_missing(:since, other)
- else
- utc.since(other).in_time_zone(time_zone)
- end
- end
-
def ago(other)
since(-other)
end
@@ -303,28 +330,49 @@ module ActiveSupport
EOV
end
+ # Returns Array of parts of Time in sequence of
+ # [seconds, minutes, hours, day, month, year, weekday, yearday, dst?, zone].
+ #
+ # now = Time.zone.now # => Tue, 18 Aug 2015 02:29:27 UTC +00:00
+ # now.to_a # => [27, 29, 2, 18, 8, 2015, 2, 230, false, "UTC"]
def to_a
[time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone]
end
+ # Returns the object's date and time as a floating point number of seconds
+ # since the Epoch (January 1, 1970 00:00 UTC).
+ #
+ # Time.zone.now.to_f # => 1417709320.285418
def to_f
utc.to_f
end
+ # Returns the object's date and time as an integer number of seconds
+ # since the Epoch (January 1, 1970 00:00 UTC).
+ #
+ # Time.zone.now.to_i # => 1417709320
def to_i
utc.to_i
end
alias_method :tv_sec, :to_i
+ # Returns the object's date and time as a rational number of seconds
+ # since the Epoch (January 1, 1970 00:00 UTC).
+ #
+ # Time.zone.now.to_r # => (708854548642709/500000)
def to_r
utc.to_r
end
- # Return an instance of Time in the system timezone.
+ # Returns an instance of Time in the system timezone.
def to_time
utc.to_time
end
+ # Returns an instance of DateTime with the timezone's UTC offset
+ #
+ # Time.zone.now.to_datetime # => Tue, 18 Aug 2015 02:32:20 +0000
+ # Time.current.in_time_zone('Hawaii').to_datetime # => Mon, 17 Aug 2015 16:32:20 -1000
def to_datetime
utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
end
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index 55533a5d40..2699a064d7 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -111,9 +111,11 @@ module ActiveSupport
"Jerusalem" => "Asia/Jerusalem",
"Harare" => "Africa/Harare",
"Pretoria" => "Africa/Johannesburg",
+ "Kaliningrad" => "Europe/Kaliningrad",
"Moscow" => "Europe/Moscow",
"St. Petersburg" => "Europe/Moscow",
- "Volgograd" => "Europe/Moscow",
+ "Volgograd" => "Europe/Volgograd",
+ "Samara" => "Europe/Samara",
"Kuwait" => "Asia/Kuwait",
"Riyadh" => "Asia/Riyadh",
"Nairobi" => "Africa/Nairobi",
@@ -170,6 +172,7 @@ module ActiveSupport
"Guam" => "Pacific/Guam",
"Port Moresby" => "Pacific/Port_Moresby",
"Magadan" => "Asia/Magadan",
+ "Srednekolymsk" => "Asia/Srednekolymsk",
"Solomon Is." => "Pacific/Guadalcanal",
"New Caledonia" => "Pacific/Noumea",
"Fiji" => "Pacific/Fiji",
@@ -202,7 +205,7 @@ module ActiveSupport
end
def find_tzinfo(name)
- TZInfo::TimezoneProxy.new(MAPPING[name] || name)
+ TZInfo::Timezone.new(MAPPING[name] || name)
end
alias_method :create, :new
@@ -221,13 +224,6 @@ module ActiveSupport
@zones ||= zones_map.values.sort
end
- def zones_map
- @zones_map ||= begin
- MAPPING.each_key {|place| self[place]} # load all the zones
- @lazy_zones_map
- end
- end
-
# Locate a specific time zone object. If the argument is a string, it
# is interpreted to mean the name of the timezone to locate. If it is a
# numeric value it is either the hour offset, or the second offset, of the
@@ -237,7 +233,7 @@ module ActiveSupport
case arg
when String
begin
- @lazy_zones_map[arg] ||= create(arg).tap { |tz| tz.utc_offset }
+ @lazy_zones_map[arg] ||= create(arg)
rescue TZInfo::InvalidTimezoneIdentifier
nil
end
@@ -254,6 +250,14 @@ module ActiveSupport
def us_zones
@us_zones ||= all.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ }
end
+
+ private
+ def zones_map
+ @zones_map ||= begin
+ MAPPING.each_key {|place| self[place]} # load all the zones
+ @lazy_zones_map
+ end
+ end
end
include Comparable
@@ -344,24 +348,31 @@ module ActiveSupport
#
# Time.zone.parse('Mar 2000') # => Wed, 01 Mar 2000 00:00:00 HST -10:00
def parse(str, now=now())
- parts = Date._parse(str, false)
- return if parts.empty?
-
- time = Time.new(
- parts.fetch(:year, now.year),
- parts.fetch(:mon, now.month),
- parts.fetch(:mday, parts[:year] || parts[:mon] ? 1 : now.day),
- parts.fetch(:hour, 0),
- parts.fetch(:min, 0),
- parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
- parts.fetch(:offset, 0)
- )
-
- if parts[:offset]
- TimeWithZone.new(time.utc, self)
- else
- TimeWithZone.new(nil, self, time)
- end
+ parts_to_time(Date._parse(str, false), now)
+ end
+
+ # Parses +str+ according to +format+ and returns an ActiveSupport::TimeWithZone.
+ #
+ # Assumes that +str+ is a time in the time zone +self+,
+ # unless +format+ includes an explicit time zone.
+ # (This is the same behavior as +parse+.)
+ # In either case, the returned TimeWithZone has the timezone of +self+.
+ #
+ # Time.zone = 'Hawaii' # => "Hawaii"
+ # Time.zone.strptime('1999-12-31 14:00:00', '%Y-%m-%d %H:%M:%S') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
+ #
+ # If upper components are missing from the string, they are supplied from
+ # TimeZone#now:
+ #
+ # Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00
+ # Time.zone.strptime('22:30:00', '%H:%M:%S') # => Fri, 31 Dec 1999 22:30:00 HST -10:00
+ #
+ # However, if the date component is not provided, but any other upper
+ # components are supplied, then the day of the month defaults to 1:
+ #
+ # Time.zone.strptime('Mar 2000', '%b %Y') # => Wed, 01 Mar 2000 00:00:00 HST -10:00
+ def strptime(str, format, now=now())
+ parts_to_time(DateTime._strptime(str, format), now)
end
# Returns an ActiveSupport::TimeWithZone instance representing the current
@@ -417,7 +428,36 @@ module ActiveSupport
tzinfo.periods_for_local(time)
end
+ def init_with(coder) #:nodoc:
+ initialize(coder['name'])
+ end
+
+ def encode_with(coder) #:nodoc:
+ coder.tag ="!ruby/object:#{self.class}"
+ coder.map = { 'name' => tzinfo.name }
+ end
+
private
+ def parts_to_time(parts, now)
+ return if parts.empty?
+
+ time = Time.new(
+ parts.fetch(:year, now.year),
+ parts.fetch(:mon, now.month),
+ parts.fetch(:mday, parts[:year] || parts[:mon] ? 1 : now.day),
+ parts.fetch(:hour, 0),
+ parts.fetch(:min, 0),
+ parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
+ parts.fetch(:offset, 0)
+ )
+
+ if parts[:offset]
+ TimeWithZone.new(time.utc, self)
+ else
+ TimeWithZone.new(nil, self, time)
+ end
+ end
+
def time_now
Time.now
end
diff --git a/activesupport/lib/active_support/values/unicode_tables.dat b/activesupport/lib/active_support/values/unicode_tables.dat
index 394ee95f4b..760be4c07a 100644
--- a/activesupport/lib/active_support/values/unicode_tables.dat
+++ b/activesupport/lib/active_support/values/unicode_tables.dat
Binary files differ
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index 009ee4db90..df7b081993 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -78,6 +78,9 @@ module ActiveSupport
)
end
+ attr_accessor :depth
+ self.depth = 100
+
delegate :parse, :to => :backend
def backend
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
index 27c64c4dca..94751bbc04 100644
--- a/activesupport/lib/active_support/xml_mini/jdom.rb
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -46,7 +46,7 @@ module ActiveSupport
xml_string_reader = StringReader.new(data)
xml_input_source = InputSource.new(xml_string_reader)
doc = @dbf.new_document_builder.parse(xml_input_source)
- merge_element!({CONTENT_KEY => ''}, doc.document_element)
+ merge_element!({CONTENT_KEY => ''}, doc.document_element, XmlMini.depth)
end
end
@@ -58,9 +58,10 @@ module ActiveSupport
# Hash to merge the converted element into.
# element::
# XML element to merge into hash
- def merge_element!(hash, element)
+ def merge_element!(hash, element, depth)
+ raise 'Document too deep!' if depth == 0
delete_empty(hash)
- merge!(hash, element.tag_name, collapse(element))
+ merge!(hash, element.tag_name, collapse(element, depth))
end
def delete_empty(hash)
@@ -71,14 +72,14 @@ module ActiveSupport
#
# element::
# The document element to be collapsed.
- def collapse(element)
+ def collapse(element, depth)
hash = get_attributes(element)
child_nodes = element.child_nodes
if child_nodes.length > 0
(0...child_nodes.length).each do |i|
child = child_nodes.item(i)
- merge_element!(hash, child) unless child.node_type == Node.TEXT_NODE
+ merge_element!(hash, child, depth - 1) unless child.node_type == Node.TEXT_NODE
end
merge_texts!(hash, element) unless empty_content?(element)
hash
@@ -141,7 +142,7 @@ module ActiveSupport
(0...attributes.length).each do |i|
attribute_hash[CONTENT_KEY] ||= ''
attribute_hash[attributes.item(i).name] = attributes.item(i).value
- end
+ end
attribute_hash
end
diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb
index 47a2824186..bb0ea9c582 100644
--- a/activesupport/lib/active_support/xml_mini/libxml.rb
+++ b/activesupport/lib/active_support/xml_mini/libxml.rb
@@ -75,5 +75,5 @@ module LibXML #:nodoc:
end
end
-LibXML::XML::Document.send(:include, LibXML::Conversions::Document)
-LibXML::XML::Node.send(:include, LibXML::Conversions::Node)
+LibXML::XML::Document.include(LibXML::Conversions::Document)
+LibXML::XML::Node.include(LibXML::Conversions::Node)
diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb
index 7398d4fa82..619cc7522d 100644
--- a/activesupport/lib/active_support/xml_mini/nokogiri.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb
@@ -77,7 +77,7 @@ module ActiveSupport
end
end
- Nokogiri::XML::Document.send(:include, Conversions::Document)
- Nokogiri::XML::Node.send(:include, Conversions::Node)
+ Nokogiri::XML::Document.include(Conversions::Document)
+ Nokogiri::XML::Node.include(Conversions::Node)
end
end
diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb
index 5c7c78bf70..924ed72345 100644
--- a/activesupport/lib/active_support/xml_mini/rexml.rb
+++ b/activesupport/lib/active_support/xml_mini/rexml.rb
@@ -29,7 +29,7 @@ module ActiveSupport
doc = REXML::Document.new(data)
if doc.root
- merge_element!({}, doc.root)
+ merge_element!({}, doc.root, XmlMini.depth)
else
raise REXML::ParseException,
"The document #{doc.to_s.inspect} does not have a valid root"
@@ -44,19 +44,20 @@ module ActiveSupport
# Hash to merge the converted element into.
# element::
# XML element to merge into hash
- def merge_element!(hash, element)
- merge!(hash, element.name, collapse(element))
+ def merge_element!(hash, element, depth)
+ raise REXML::ParseException, "The document is too deep" if depth == 0
+ merge!(hash, element.name, collapse(element, depth))
end
# Actually converts an XML document element into a data structure.
#
# element::
# The document element to be collapsed.
- def collapse(element)
+ def collapse(element, depth)
hash = get_attributes(element)
if element.has_elements?
- element.each_element {|child| merge_element!(hash, child) }
+ element.each_element {|child| merge_element!(hash, child, depth - 1) }
merge_texts!(hash, element) unless empty_content?(element)
hash
else
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb
index f65ec962f9..c0e23e89f7 100644
--- a/activesupport/test/abstract_unit.rb
+++ b/activesupport/test/abstract_unit.rb
@@ -15,6 +15,7 @@ silence_warnings do
end
require 'active_support/testing/autorun'
+require 'active_support/testing/method_call_assertions'
ENV['NO_RELOAD'] = '1'
require 'active_support'
@@ -37,9 +38,6 @@ def jruby_skip(message = '')
skip message if defined?(JRUBY_VERSION)
end
-require 'mocha/setup' # FIXME: stop using mocha
-
-# FIXME: we have tests that depend on run order, we should fix that and
-# remove this method call.
-require 'active_support/test_case'
-ActiveSupport::TestCase.test_order = :sorted
+class ActiveSupport::TestCase
+ include ActiveSupport::Testing::MethodCallAssertions
+end
diff --git a/activesupport/test/array_inquirer_test.rb b/activesupport/test/array_inquirer_test.rb
new file mode 100644
index 0000000000..263ab3802b
--- /dev/null
+++ b/activesupport/test/array_inquirer_test.rb
@@ -0,0 +1,41 @@
+require 'abstract_unit'
+require 'active_support/core_ext/array'
+
+class ArrayInquirerTest < ActiveSupport::TestCase
+ def setup
+ @array_inquirer = ActiveSupport::ArrayInquirer.new([:mobile, :tablet, 'api'])
+ end
+
+ def test_individual
+ assert @array_inquirer.mobile?
+ assert @array_inquirer.tablet?
+ assert_not @array_inquirer.desktop?
+ end
+
+ def test_any
+ assert @array_inquirer.any?(:mobile, :desktop)
+ assert @array_inquirer.any?(:watch, :tablet)
+ assert_not @array_inquirer.any?(:desktop, :watch)
+ end
+
+ def test_any_string_symbol_mismatch
+ assert @array_inquirer.any?('mobile')
+ assert @array_inquirer.any?(:api)
+ end
+
+ def test_any_with_block
+ assert @array_inquirer.any? { |v| v == :mobile }
+ assert_not @array_inquirer.any? { |v| v == :desktop }
+ end
+
+ def test_respond_to
+ assert_respond_to @array_inquirer, :development?
+ end
+
+ def test_inquiry
+ result = [:mobile, :tablet, 'api'].inquiry
+
+ assert_instance_of ActiveSupport::ArrayInquirer, result
+ assert_equal @array_inquirer, result
+ end
+end
diff --git a/activesupport/test/autoloading_fixtures/a/c/e/f.rb b/activesupport/test/autoloading_fixtures/a/c/e/f.rb
deleted file mode 100644
index 57dba5a307..0000000000
--- a/activesupport/test/autoloading_fixtures/a/c/e/f.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-class A::C::E::F
-end \ No newline at end of file
diff --git a/activesupport/test/autoloading_fixtures/a/c/em/f.rb b/activesupport/test/autoloading_fixtures/a/c/em/f.rb
new file mode 100644
index 0000000000..8b28e19148
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/a/c/em/f.rb
@@ -0,0 +1,2 @@
+class A::C::EM::F
+end \ No newline at end of file
diff --git a/activesupport/test/autoloading_fixtures/d.rb b/activesupport/test/autoloading_fixtures/d.rb
new file mode 100644
index 0000000000..45c794d4ca
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/d.rb
@@ -0,0 +1,2 @@
+class D
+end \ No newline at end of file
diff --git a/activesupport/test/autoloading_fixtures/e.rb b/activesupport/test/autoloading_fixtures/e.rb
deleted file mode 100644
index 2f59e4fb75..0000000000
--- a/activesupport/test/autoloading_fixtures/e.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-class E
-end \ No newline at end of file
diff --git a/activesupport/test/autoloading_fixtures/em.rb b/activesupport/test/autoloading_fixtures/em.rb
new file mode 100644
index 0000000000..16a1838667
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/em.rb
@@ -0,0 +1,2 @@
+class EM
+end \ No newline at end of file
diff --git a/activesupport/test/autoloading_fixtures/typo.rb b/activesupport/test/autoloading_fixtures/typo.rb
new file mode 100644
index 0000000000..8e047f5fd4
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/typo.rb
@@ -0,0 +1,2 @@
+TypO = 1
+
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index 5945605f7b..74ceff44f9 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -133,37 +133,42 @@ class CacheStoreSettingTest < ActiveSupport::TestCase
end
def test_mem_cache_fragment_cache_store
- Dalli::Client.expects(:new).with(%w[localhost], {})
- store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost"
- assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
+ assert_called_with(Dalli::Client, :new, [%w[localhost], {}]) do
+ store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost"
+ assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
+ end
end
def test_mem_cache_fragment_cache_store_with_given_mem_cache
mem_cache = Dalli::Client.new
- Dalli::Client.expects(:new).never
- store = ActiveSupport::Cache.lookup_store :mem_cache_store, mem_cache
- assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
+ assert_not_called(Dalli::Client, :new) do
+ store = ActiveSupport::Cache.lookup_store :mem_cache_store, mem_cache
+ assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
+ end
end
def test_mem_cache_fragment_cache_store_with_not_dalli_client
- Dalli::Client.expects(:new).never
- memcache = Object.new
- assert_raises(ArgumentError) do
- ActiveSupport::Cache.lookup_store :mem_cache_store, memcache
+ assert_not_called(Dalli::Client, :new) do
+ memcache = Object.new
+ assert_raises(ArgumentError) do
+ ActiveSupport::Cache.lookup_store :mem_cache_store, memcache
+ end
end
end
def test_mem_cache_fragment_cache_store_with_multiple_servers
- Dalli::Client.expects(:new).with(%w[localhost 192.168.1.1], {})
- store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1'
- assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
+ assert_called_with(Dalli::Client, :new, [%w[localhost 192.168.1.1], {}]) do
+ store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1'
+ assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
+ end
end
def test_mem_cache_fragment_cache_store_with_options
- Dalli::Client.expects(:new).with(%w[localhost 192.168.1.1], { :timeout => 10 })
- store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1', :namespace => 'foo', :timeout => 10
- assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
- assert_equal 'foo', store.options[:namespace]
+ assert_called_with(Dalli::Client, :new, [%w[localhost 192.168.1.1], { :timeout => 10 }]) do
+ store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1', :namespace => 'foo', :timeout => 10
+ assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
+ assert_equal 'foo', store.options[:namespace]
+ end
end
def test_object_assigned_fragment_cache_store
@@ -224,13 +229,15 @@ module CacheStoreBehavior
def test_fetch_without_cache_miss
@cache.write('foo', 'bar')
- @cache.expects(:write).never
- assert_equal 'bar', @cache.fetch('foo') { 'baz' }
+ assert_not_called(@cache, :write) do
+ assert_equal 'bar', @cache.fetch('foo') { 'baz' }
+ end
end
def test_fetch_with_cache_miss
- @cache.expects(:write).with('foo', 'baz', @cache.options)
- assert_equal 'baz', @cache.fetch('foo') { 'baz' }
+ assert_called_with(@cache, :write, ['foo', 'baz', @cache.options]) do
+ assert_equal 'baz', @cache.fetch('foo') { 'baz' }
+ end
end
def test_fetch_with_cache_miss_passes_key_to_block
@@ -245,15 +252,18 @@ module CacheStoreBehavior
def test_fetch_with_forced_cache_miss
@cache.write('foo', 'bar')
- @cache.expects(:read).never
- @cache.expects(:write).with('foo', 'bar', @cache.options.merge(:force => true))
- @cache.fetch('foo', :force => true) { 'bar' }
+ assert_not_called(@cache, :read) do
+ assert_called_with(@cache, :write, ['foo', 'bar', @cache.options.merge(:force => true)]) do
+ @cache.fetch('foo', :force => true) { 'bar' }
+ end
+ end
end
def test_fetch_with_cached_nil
@cache.write('foo', nil)
- @cache.expects(:write).never
- assert_nil @cache.fetch('foo') { 'baz' }
+ assert_not_called(@cache, :write) do
+ assert_nil @cache.fetch('foo') { 'baz' }
+ end
end
def test_should_read_and_write_hash
@@ -288,8 +298,9 @@ module CacheStoreBehavior
@cache.write('foo', 'bar', :expires_in => 10)
@cache.write('fu', 'baz')
@cache.write('fud', 'biz')
- Time.stubs(:now).returns(time + 11)
- assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
+ Time.stub(:now, time + 11) do
+ assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
+ end
end
def test_fetch_multi
@@ -303,8 +314,9 @@ module CacheStoreBehavior
end
def test_multi_with_objects
- foo = stub(:title => 'FOO!', :cache_key => 'foo')
- bar = stub(:cache_key => 'bar')
+ cache_struct = Struct.new(:cache_key, :title)
+ foo = cache_struct.new('foo', 'FOO!')
+ bar = cache_struct.new('bar')
@cache.write('bar', 'BAM!')
@@ -387,54 +399,74 @@ module CacheStoreBehavior
def test_expires_in
time = Time.local(2008, 4, 24)
- Time.stubs(:now).returns(time)
- @cache.write('foo', 'bar')
- assert_equal 'bar', @cache.read('foo')
+ Time.stub(:now, time) do
+ @cache.write('foo', 'bar')
+ assert_equal 'bar', @cache.read('foo')
+ end
- Time.stubs(:now).returns(time + 30)
- assert_equal 'bar', @cache.read('foo')
+ Time.stub(:now, time + 30) do
+ assert_equal 'bar', @cache.read('foo')
+ end
- Time.stubs(:now).returns(time + 61)
- assert_nil @cache.read('foo')
+ Time.stub(:now, time + 61) do
+ assert_nil @cache.read('foo')
+ end
end
- def test_race_condition_protection
- time = Time.now
- @cache.write('foo', 'bar', :expires_in => 60)
- Time.stubs(:now).returns(time + 61)
- result = @cache.fetch('foo', :race_condition_ttl => 10) do
- assert_equal 'bar', @cache.read('foo')
- "baz"
+ def test_race_condition_protection_skipped_if_not_defined
+ @cache.write('foo', 'bar')
+ time = @cache.send(:read_entry, 'foo', {}).expires_at
+
+ Time.stub(:now, Time.at(time)) do
+ result = @cache.fetch('foo') do
+ assert_equal nil, @cache.read('foo')
+ 'baz'
+ end
+ assert_equal 'baz', result
end
- assert_equal "baz", result
end
def test_race_condition_protection_is_limited
time = Time.now
@cache.write('foo', 'bar', :expires_in => 60)
- Time.stubs(:now).returns(time + 71)
- result = @cache.fetch('foo', :race_condition_ttl => 10) do
- assert_equal nil, @cache.read('foo')
- "baz"
+ Time.stub(:now, time + 71) do
+ result = @cache.fetch('foo', :race_condition_ttl => 10) do
+ assert_equal nil, @cache.read('foo')
+ "baz"
+ end
+ assert_equal "baz", result
end
- assert_equal "baz", result
end
def test_race_condition_protection_is_safe
time = Time.now
@cache.write('foo', 'bar', :expires_in => 60)
- Time.stubs(:now).returns(time + 61)
- begin
- @cache.fetch('foo', :race_condition_ttl => 10) do
+ Time.stub(:now, time + 61) do
+ begin
+ @cache.fetch('foo', :race_condition_ttl => 10) do
+ assert_equal 'bar', @cache.read('foo')
+ raise ArgumentError.new
+ end
+ rescue ArgumentError
+ end
+ assert_equal "bar", @cache.read('foo')
+ end
+ Time.stub(:now, time + 91) do
+ assert_nil @cache.read('foo')
+ end
+ end
+
+ def test_race_condition_protection
+ time = Time.now
+ @cache.write('foo', 'bar', :expires_in => 60)
+ Time.stub(:now, time + 61) do
+ result = @cache.fetch('foo', :race_condition_ttl => 10) do
assert_equal 'bar', @cache.read('foo')
- raise ArgumentError.new
+ "baz"
end
- rescue ArgumentError
+ assert_equal "baz", result
end
- assert_equal "bar", @cache.read('foo')
- Time.stubs(:now).returns(time + 91)
- assert_nil @cache.read('foo')
end
def test_crazy_key_characters
@@ -624,37 +656,37 @@ module AutoloadingCacheBehavior
include DependenciesTestHelpers
def test_simple_autoloading
with_autoloading_fixtures do
- @cache.write('foo', E.new)
+ @cache.write('foo', EM.new)
end
- remove_constants(:E)
+ remove_constants(:EM)
ActiveSupport::Dependencies.clear
with_autoloading_fixtures do
- assert_kind_of E, @cache.read('foo')
+ assert_kind_of EM, @cache.read('foo')
end
- remove_constants(:E)
+ remove_constants(:EM)
ActiveSupport::Dependencies.clear
end
def test_two_classes_autoloading
with_autoloading_fixtures do
- @cache.write('foo', [E.new, ClassFolder.new])
+ @cache.write('foo', [EM.new, ClassFolder.new])
end
- remove_constants(:E, :ClassFolder)
+ remove_constants(:EM, :ClassFolder)
ActiveSupport::Dependencies.clear
with_autoloading_fixtures do
loaded = @cache.read('foo')
assert_kind_of Array, loaded
assert_equal 2, loaded.size
- assert_kind_of E, loaded[0]
+ assert_kind_of EM, loaded[0]
assert_kind_of ClassFolder, loaded[1]
end
- remove_constants(:E, :ClassFolder)
+ remove_constants(:EM, :ClassFolder)
ActiveSupport::Dependencies.clear
end
end
@@ -672,6 +704,7 @@ class FileStoreTest < ActiveSupport::TestCase
def teardown
FileUtils.rm_r(cache_dir)
+ rescue Errno::ENOENT
end
def cache_dir
@@ -691,6 +724,11 @@ class FileStoreTest < ActiveSupport::TestCase
assert File.exist?(filepath)
end
+ def test_clear_without_cache_dir
+ FileUtils.rm_r(cache_dir)
+ @cache.clear
+ end
+
def test_long_keys
@cache.write("a"*10000, 1)
assert_equal 1, @cache.read("a"*10000)
@@ -747,9 +785,10 @@ class FileStoreTest < ActiveSupport::TestCase
end
def test_log_exception_when_cache_read_fails
- File.expects(:exist?).raises(StandardError, "failed")
- @cache.send(:read_entry, "winston", {})
- assert @buffer.string.present?
+ File.stub(:exist?, -> { raise StandardError.new("failed") }) do
+ @cache.send(:read_entry, "winston", {})
+ assert @buffer.string.present?
+ end
end
def test_cleanup_removes_all_expired_entries
@@ -757,11 +796,12 @@ class FileStoreTest < ActiveSupport::TestCase
@cache.write('foo', 'bar', expires_in: 10)
@cache.write('baz', 'qux')
@cache.write('quux', 'corge', expires_in: 20)
- Time.stubs(:now).returns(time + 15)
- @cache.cleanup
- assert_not @cache.exist?('foo')
- assert @cache.exist?('baz')
- assert @cache.exist?('quux')
+ Time.stub(:now, time + 15) do
+ @cache.cleanup
+ assert_not @cache.exist?('foo')
+ assert @cache.exist?('baz')
+ assert @cache.exist?('quux')
+ end
end
def test_write_with_unless_exist
@@ -998,10 +1038,6 @@ class NullStoreTest < ActiveSupport::TestCase
end
assert_nil @cache.read("name")
end
-
- def test_setting_nil_cache_store
- assert ActiveSupport::Cache.lookup_store.class.name, ActiveSupport::Cache::NullStore.name
- end
end
class CacheStoreLoggerTest < ActiveSupport::TestCase
@@ -1021,6 +1057,15 @@ class CacheStoreLoggerTest < ActiveSupport::TestCase
@cache.mute { @cache.fetch('foo') { 'bar' } }
assert @buffer.string.blank?
end
+
+ def test_multi_read_loggin
+ @cache.write 'hello', 'goodbye'
+ @cache.write 'world', 'earth'
+
+ @cache.read_multi('hello', 'world')
+
+ assert_match "Caches multi read:\n- hello\n- world", @buffer.string
+ end
end
class CacheEntryTest < ActiveSupport::TestCase
@@ -1029,9 +1074,9 @@ class CacheEntryTest < ActiveSupport::TestCase
assert !entry.expired?, 'entry not expired'
entry = ActiveSupport::Cache::Entry.new("value", :expires_in => 60)
assert !entry.expired?, 'entry not expired'
- time = Time.now + 61
- Time.stubs(:now).returns(time)
- assert entry.expired?, 'entry is expired'
+ Time.stub(:now, Time.now + 61) do
+ assert entry.expired?, 'entry is expired'
+ end
end
def test_compress_values
@@ -1047,30 +1092,4 @@ class CacheEntryTest < ActiveSupport::TestCase
assert_equal value, entry.value
assert_equal value.bytesize, entry.size
end
-
- 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_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_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
end
diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb
index 32c2dfdfc0..cda9732cae 100644
--- a/activesupport/test/callbacks_test.rb
+++ b/activesupport/test/callbacks_test.rb
@@ -49,7 +49,7 @@ module CallbacksTest
def self.before(model)
model.history << [:before_save, :class]
end
-
+
def self.after(model)
model.history << [:after_save, :class]
end
@@ -73,8 +73,8 @@ module CallbacksTest
class PersonSkipper < Person
skip_callback :save, :before, :before_save_method, :if => :yes
- skip_callback :save, :after, :before_save_method, :unless => :yes
- skip_callback :save, :after, :before_save_method, :if => :no
+ skip_callback :save, :after, :after_save_method, :unless => :yes
+ skip_callback :save, :after, :after_save_method, :if => :no
skip_callback :save, :before, :before_save_method, :unless => :no
skip_callback :save, :before, CallbackClass , :if => :yes
def yes; true; end
@@ -501,21 +501,18 @@ module CallbacksTest
end
end
- class CallbackTerminator
+ class AbstractCallbackTerminator
include ActiveSupport::Callbacks
- define_callbacks :save, :terminator => ->(_,result) { result == :halt }
-
- set_callback :save, :before, :first
- set_callback :save, :before, :second
- set_callback :save, :around, :around_it
- set_callback :save, :before, :third
- set_callback :save, :after, :first
- set_callback :save, :around, :around_it
- set_callback :save, :after, :second
- set_callback :save, :around, :around_it
- set_callback :save, :after, :third
-
+ def self.set_save_callbacks
+ set_callback :save, :before, :first
+ set_callback :save, :before, :second
+ set_callback :save, :around, :around_it
+ set_callback :save, :before, :third
+ set_callback :save, :after, :first
+ set_callback :save, :around, :around_it
+ set_callback :save, :after, :third
+ end
attr_reader :history, :saved, :halted
def initialize
@@ -552,6 +549,39 @@ module CallbacksTest
end
end
+ class CallbackTerminator < AbstractCallbackTerminator
+ define_callbacks :save, terminator: ->(_, result_lambda) { result_lambda.call == :halt }
+ set_save_callbacks
+ end
+
+ class CallbackTerminatorSkippingAfterCallbacks < AbstractCallbackTerminator
+ define_callbacks :save, terminator: ->(_, result_lambda) { result_lambda.call == :halt },
+ skip_after_callbacks_if_terminated: true
+ set_save_callbacks
+ end
+
+ class CallbackDefaultTerminator < AbstractCallbackTerminator
+ define_callbacks :save
+
+ def second
+ @history << "second"
+ throw(:abort)
+ end
+
+ set_save_callbacks
+ end
+
+ class CallbackFalseTerminator < AbstractCallbackTerminator
+ define_callbacks :save
+
+ def second
+ @history << "second"
+ false
+ end
+
+ set_save_callbacks
+ end
+
class CallbackObject
def before(caller)
caller.record << "before"
@@ -688,10 +718,10 @@ module CallbacksTest
end
class CallbackTerminatorTest < ActiveSupport::TestCase
- def test_termination
+ def test_termination_skips_following_before_and_around_callbacks
terminator = CallbackTerminator.new
terminator.save
- assert_equal ["first", "second", "third", "second", "first"], terminator.history
+ assert_equal ["first", "second", "third", "first"], terminator.history
end
def test_termination_invokes_hook
@@ -707,6 +737,73 @@ module CallbacksTest
end
end
+ class CallbackTerminatorSkippingAfterCallbacksTest < ActiveSupport::TestCase
+ def test_termination_skips_after_callbacks
+ terminator = CallbackTerminatorSkippingAfterCallbacks.new
+ terminator.save
+ assert_equal ["first", "second"], terminator.history
+ end
+ end
+
+ class CallbackDefaultTerminatorTest < ActiveSupport::TestCase
+ def test_default_termination
+ terminator = CallbackDefaultTerminator.new
+ terminator.save
+ assert_equal ["first", "second", "third", "first"], terminator.history
+ end
+
+ def test_default_termination_invokes_hook
+ terminator = CallbackDefaultTerminator.new
+ terminator.save
+ assert_equal :second, terminator.halted
+ end
+
+ def test_block_never_called_if_abort_is_thrown
+ obj = CallbackDefaultTerminator.new
+ obj.save
+ assert !obj.saved
+ end
+ end
+
+ class CallbackFalseTerminatorWithoutConfigTest < ActiveSupport::TestCase
+ def test_returning_false_halts_callback_if_config_variable_is_not_set
+ obj = CallbackFalseTerminator.new
+ assert_deprecated do
+ obj.save
+ assert_equal :second, obj.halted
+ assert !obj.saved
+ end
+ end
+ end
+
+ class CallbackFalseTerminatorWithConfigTrueTest < ActiveSupport::TestCase
+ def setup
+ ActiveSupport::Callbacks::CallbackChain.halt_and_display_warning_on_return_false = true
+ end
+
+ def test_returning_false_halts_callback_if_config_variable_is_true
+ obj = CallbackFalseTerminator.new
+ assert_deprecated do
+ obj.save
+ assert_equal :second, obj.halted
+ assert !obj.saved
+ end
+ end
+ end
+
+ class CallbackFalseTerminatorWithConfigFalseTest < ActiveSupport::TestCase
+ def setup
+ ActiveSupport::Callbacks::CallbackChain.halt_and_display_warning_on_return_false = false
+ end
+
+ def test_returning_false_does_not_halt_callback_if_config_variable_is_false
+ obj = CallbackFalseTerminator.new
+ obj.save
+ assert_equal nil, obj.halted
+ assert obj.saved
+ end
+ end
+
class HyphenatedKeyTest < ActiveSupport::TestCase
def test_save
obj = HyphenatedCallbacks.new
@@ -924,7 +1021,7 @@ module CallbacksTest
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
+ def self.skip(*things); skip_callback :foo, :before, *things; end
}
end
@@ -973,11 +1070,11 @@ module CallbacksTest
}
end
- def test_skip_lambda # removes nothing
+ def test_skip_lambda # raises error
calls = []
callback = ->(o) { calls << o }
klass = build_class(callback)
- 10.times { klass.skip callback }
+ assert_raises(ArgumentError) { klass.skip callback }
klass.new.run
assert_equal 10, calls.length
end
@@ -991,11 +1088,29 @@ module CallbacksTest
assert_equal 0, calls.length
end
- def test_skip_eval # removes nothing
+ def test_skip_string # raises error
calls = []
klass = build_class("bar")
klass.class_eval { define_method(:bar) { calls << klass } }
- klass.skip "bar"
+ assert_raises(ArgumentError) { klass.skip "bar" }
+ klass.new.run
+ assert_equal 1, calls.length
+ end
+
+ def test_skip_undefined_callback # raises error
+ calls = []
+ klass = build_class(:bar)
+ klass.class_eval { define_method(:bar) { calls << klass } }
+ assert_raises(ArgumentError) { klass.skip :qux }
+ klass.new.run
+ assert_equal 1, calls.length
+ end
+
+ def test_skip_without_raise # removes nothing
+ calls = []
+ klass = build_class(:bar)
+ klass.class_eval { define_method(:bar) { calls << klass } }
+ klass.skip :qux, raise: false
klass.new.run
assert_equal 1, calls.length
end
diff --git a/activesupport/test/concern_test.rb b/activesupport/test/concern_test.rb
index 60bd8a06aa..8ea701cfb7 100644
--- a/activesupport/test/concern_test.rb
+++ b/activesupport/test/concern_test.rb
@@ -54,29 +54,54 @@ class ConcernTest < ActiveSupport::TestCase
include Bar, Baz
end
+ module Qux
+ module ClassMethods
+ end
+ end
+
def setup
@klass = Class.new
end
def test_module_is_included_normally
- @klass.send(:include, Baz)
+ @klass.include(Baz)
assert_equal "baz", @klass.new.baz
assert @klass.included_modules.include?(ConcernTest::Baz)
end
def test_class_methods_are_extended
- @klass.send(:include, Baz)
+ @klass.include(Baz)
assert_equal "baz", @klass.baz
assert_equal ConcernTest::Baz::ClassMethods, (class << @klass; self.included_modules; end)[0]
end
+ def test_class_methods_are_extended_only_on_expected_objects
+ ::Object.__send__(:include, Qux)
+ Object.extend(Qux::ClassMethods)
+ # module needs to be created after Qux is included in Object or bug won't
+ # be triggered
+ test_module = Module.new do
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def test
+ end
+ end
+ end
+ @klass.include test_module
+ assert_equal false, Object.respond_to?(:test)
+ Qux.class_eval do
+ remove_const :ClassMethods
+ end
+ end
+
def test_included_block_is_ran
- @klass.send(:include, Baz)
+ @klass.include(Baz)
assert_equal true, @klass.included_ran
end
def test_modules_dependencies_are_met
- @klass.send(:include, Bar)
+ @klass.include(Bar)
assert_equal "bar", @klass.new.bar
assert_equal "bar+baz", @klass.new.baz
assert_equal "bar's baz + baz", @klass.baz
@@ -84,7 +109,7 @@ class ConcernTest < ActiveSupport::TestCase
end
def test_dependencies_with_multiple_modules
- @klass.send(:include, Foo)
+ @klass.include(Foo)
assert_equal [ConcernTest::Foo, ConcernTest::Bar, ConcernTest::Baz], @klass.included_modules[0..2]
end
diff --git a/activesupport/test/configurable_test.rb b/activesupport/test/configurable_test.rb
index ef847fc557..5d22ded2de 100644
--- a/activesupport/test/configurable_test.rb
+++ b/activesupport/test/configurable_test.rb
@@ -111,6 +111,14 @@ class ConfigurableActiveSupport < ActiveSupport::TestCase
end
end
+ test 'the config_accessor method should not be publicly callable' do
+ assert_raises NoMethodError do
+ Class.new {
+ include ActiveSupport::Configurable
+ }.config_accessor :foo
+ end
+ end
+
def assert_method_defined(object, method)
methods = object.public_methods.map(&:to_s)
assert methods.include?(method.to_s), "Expected #{methods.inspect} to include #{method.to_s.inspect}"
diff --git a/activesupport/test/core_ext/array/access_test.rb b/activesupport/test/core_ext/array/access_test.rb
index f14f64421d..3f1e0c4cb4 100644
--- a/activesupport/test/core_ext/array/access_test.rb
+++ b/activesupport/test/core_ext/array/access_test.rb
@@ -27,4 +27,8 @@ class AccessTest < ActiveSupport::TestCase
assert_equal array[4], array.fifth
assert_equal array[41], array.forty_two
end
+
+ def test_without
+ assert_equal [1, 2, 4], [1, 2, 3, 4, 5].without(3, 5)
+ end
end
diff --git a/activesupport/test/core_ext/array/conversions_test.rb b/activesupport/test/core_ext/array/conversions_test.rb
index 577b889410..507e13f968 100644
--- a/activesupport/test/core_ext/array/conversions_test.rb
+++ b/activesupport/test/core_ext/array/conversions_test.rb
@@ -60,6 +60,12 @@ class ToSentenceTest < ActiveSupport::TestCase
assert_equal exception.message, "Unknown key: :passing. Valid keys are: :words_connector, :two_words_connector, :last_word_connector, :locale"
end
+
+ def test_always_returns_string
+ assert_instance_of String, [ActiveSupport::SafeBuffer.new('one')].to_sentence
+ assert_instance_of String, [ActiveSupport::SafeBuffer.new('one'), 'two'].to_sentence
+ assert_instance_of String, [ActiveSupport::SafeBuffer.new('one'), 'two', 'three'].to_sentence
+ end
end
class ToSTest < ActiveSupport::TestCase
diff --git a/activesupport/test/core_ext/big_decimal/yaml_conversions_test.rb b/activesupport/test/core_ext/big_decimal/yaml_conversions_test.rb
deleted file mode 100644
index e634679d20..0000000000
--- a/activesupport/test/core_ext/big_decimal/yaml_conversions_test.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require 'abstract_unit'
-
-class BigDecimalYamlConversionsTest < ActiveSupport::TestCase
- def test_to_yaml
- assert_deprecated { require 'active_support/core_ext/big_decimal/yaml_conversions' }
- assert_match("--- 100000.30020320320000000000000000000000000000001\n", BigDecimal.new('100000.30020320320000000000000000000000000000001').to_yaml)
- assert_match("--- .Inf\n", BigDecimal.new('Infinity').to_yaml)
- assert_match("--- .NaN\n", BigDecimal.new('NaN').to_yaml)
- assert_match("--- -.Inf\n", BigDecimal.new('-Infinity').to_yaml)
- end
-end
diff --git a/activesupport/test/core_ext/class/delegating_attributes_test.rb b/activesupport/test/core_ext/class/delegating_attributes_test.rb
deleted file mode 100644
index 447b1d10ad..0000000000
--- a/activesupport/test/core_ext/class/delegating_attributes_test.rb
+++ /dev/null
@@ -1,122 +0,0 @@
-require 'abstract_unit'
-require 'active_support/core_ext/class/delegating_attributes'
-
-module DelegatingFixtures
- class Parent
- end
-
- class Child < Parent
- ActiveSupport::Deprecation.silence do
- superclass_delegating_accessor :some_attribute
- end
- end
-
- class Mokopuna < Child
- end
-
- class PercysMom
- ActiveSupport::Deprecation.silence do
- superclass_delegating_accessor :superpower
- end
- end
-
- class Percy < PercysMom
- end
-end
-
-class DelegatingAttributesTest < ActiveSupport::TestCase
- include DelegatingFixtures
- attr_reader :single_class
-
- def setup
- @single_class = Class.new(Object)
- end
-
- def test_simple_accessor_declaration
- assert_deprecated do
- single_class.superclass_delegating_accessor :both
- end
-
- # Class should have accessor and mutator
- # the instance should have an accessor only
- assert_respond_to single_class, :both
- assert_respond_to single_class, :both=
- assert single_class.public_instance_methods.map(&:to_s).include?("both")
- assert !single_class.public_instance_methods.map(&:to_s).include?("both=")
- end
-
- def test_simple_accessor_declaration_with_instance_reader_false
- _instance_methods = single_class.public_instance_methods
-
- assert_deprecated do
- single_class.superclass_delegating_accessor :no_instance_reader, :instance_reader => false
- end
-
- assert_respond_to single_class, :no_instance_reader
- assert_respond_to single_class, :no_instance_reader=
- assert !_instance_methods.include?(:no_instance_reader)
- assert !_instance_methods.include?(:no_instance_reader?)
- assert !_instance_methods.include?(:_no_instance_reader)
- end
-
- def test_working_with_simple_attributes
- assert_deprecated do
- single_class.superclass_delegating_accessor :both
- end
-
- single_class.both = "HMMM"
-
- assert_equal "HMMM", single_class.both
- assert_equal true, single_class.both?
-
- assert_equal "HMMM", single_class.new.both
- assert_equal true, single_class.new.both?
-
- single_class.both = false
- assert_equal false, single_class.both?
- end
-
- def test_child_class_delegates_to_parent_but_can_be_overridden
- parent = Class.new
-
- assert_deprecated do
- parent.superclass_delegating_accessor :both
- end
-
- child = Class.new(parent)
- parent.both = "1"
- assert_equal "1", child.both
-
- child.both = "2"
- assert_equal "1", parent.both
- assert_equal "2", child.both
-
- parent.both = "3"
- assert_equal "3", parent.both
- assert_equal "2", child.both
- end
-
- def test_delegation_stops_at_the_right_level
- assert_nil Percy.superpower
- assert_nil PercysMom.superpower
-
- PercysMom.superpower = :heatvision
- assert_equal :heatvision, Percy.superpower
- end
-
- def test_delegation_stops_for_nil
- Mokopuna.some_attribute = nil
- Child.some_attribute="1"
-
- assert_equal "1", Child.some_attribute
- assert_nil Mokopuna.some_attribute
- ensure
- Child.some_attribute=nil
- end
-
- def test_deprecation_warning
- assert_deprecated(/superclass_delegating_accessor is deprecated/) do
- single_class.superclass_delegating_accessor :test_attribute
- end
- end
-end
diff --git a/activesupport/test/core_ext/date_and_time_behavior.rb b/activesupport/test/core_ext/date_and_time_behavior.rb
index b4ef5a0597..784547bdf8 100644
--- a/activesupport/test/core_ext/date_and_time_behavior.rb
+++ b/activesupport/test/core_ext/date_and_time_behavior.rb
@@ -6,11 +6,21 @@ module DateAndTimeBehavior
assert_equal date_time_init(2005,2,28,10,10,10), date_time_init(2005,3,2,10,10,10).yesterday.yesterday
end
+ def test_prev_day
+ assert_equal date_time_init(2005,2,21,10,10,10), date_time_init(2005,2,22,10,10,10).prev_day
+ assert_equal date_time_init(2005,2,28,10,10,10), date_time_init(2005,3,2,10,10,10).prev_day.prev_day
+ end
+
def test_tomorrow
assert_equal date_time_init(2005,2,23,10,10,10), date_time_init(2005,2,22,10,10,10).tomorrow
assert_equal date_time_init(2005,3,2,10,10,10), date_time_init(2005,2,28,10,10,10).tomorrow.tomorrow
end
+ def test_next_day
+ assert_equal date_time_init(2005,2,23,10,10,10), date_time_init(2005,2,22,10,10,10).next_day
+ assert_equal date_time_init(2005,3,2,10,10,10), date_time_init(2005,2,28,10,10,10).next_day.next_day
+ end
+
def test_days_ago
assert_equal date_time_init(2005,6,4,10,10,10), date_time_init(2005,6,5,10,10,10).days_ago(1)
assert_equal date_time_init(2005,5,31,10,10,10), date_time_init(2005,6,5,10,10,10).days_ago(5)
@@ -115,6 +125,28 @@ module DateAndTimeBehavior
end
end
+ def test_next_week_at_same_time
+ assert_equal date_time_init(2005,2,28,15,15,10), date_time_init(2005,2,22,15,15,10).next_week(:monday, same_time: true)
+ assert_equal date_time_init(2005,3,4,15,15,10), date_time_init(2005,2,22,15,15,10).next_week(:friday, same_time: true)
+ assert_equal date_time_init(2006,10,30,0,0,0), date_time_init(2006,10,23,0,0,0).next_week(:monday, same_time: true)
+ assert_equal date_time_init(2006,11,1,0,0,0), date_time_init(2006,10,23,0,0,0).next_week(:wednesday, same_time: true)
+ end
+
+ def test_next_weekday_on_wednesday
+ assert_equal date_time_init(2015,1,8,0,0,0), date_time_init(2015,1,7,0,0,0).next_weekday
+ assert_equal date_time_init(2015,1,8,15,15,10), date_time_init(2015,1,7,15,15,10).next_weekday
+ end
+
+ def test_next_weekday_on_friday
+ assert_equal date_time_init(2015,1,5,0,0,0), date_time_init(2015,1,2,0,0,0).next_weekday
+ assert_equal date_time_init(2015,1,5,15,15,10), date_time_init(2015,1,2,15,15,10).next_weekday
+ end
+
+ def test_next_weekday_on_saturday
+ assert_equal date_time_init(2015,1,5,0,0,0), date_time_init(2015,1,3,0,0,0).next_weekday
+ assert_equal date_time_init(2015,1,5,15,15,10), date_time_init(2015,1,3,15,15,10).next_weekday
+ end
+
def test_next_month_on_31st
assert_equal date_time_init(2005,9,30,15,15,10), date_time_init(2005,8,31,15,15,10).next_month
end
@@ -144,6 +176,29 @@ module DateAndTimeBehavior
end
end
+ def test_prev_week_at_same_time
+ assert_equal date_time_init(2005,2,21,15,15,10), date_time_init(2005,3,1,15,15,10).prev_week(:monday, same_time: true)
+ assert_equal date_time_init(2005,2,22,15,15,10), date_time_init(2005,3,1,15,15,10).prev_week(:tuesday, same_time: true)
+ assert_equal date_time_init(2005,2,25,15,15,10), date_time_init(2005,3,1,15,15,10).prev_week(:friday, same_time: true)
+ assert_equal date_time_init(2006,10,30,0,0,0), date_time_init(2006,11,6,0,0,0).prev_week(:monday, same_time: true)
+ assert_equal date_time_init(2006,11,15,0,0,0), date_time_init(2006,11,23,0,0,0).prev_week(:wednesday, same_time: true)
+ end
+
+ def test_prev_weekday_on_wednesday
+ assert_equal date_time_init(2015,1,6,0,0,0), date_time_init(2015,1,7,0,0,0).prev_weekday
+ assert_equal date_time_init(2015,1,6,15,15,10), date_time_init(2015,1,7,15,15,10).prev_weekday
+ end
+
+ def test_prev_weekday_on_monday
+ assert_equal date_time_init(2015,1,2,0,0,0), date_time_init(2015,1,5,0,0,0).prev_weekday
+ assert_equal date_time_init(2015,1,2,15,15,10), date_time_init(2015,1,5,15,15,10).prev_weekday
+ end
+
+ def test_prev_weekday_on_sunday
+ assert_equal date_time_init(2015,1,2,0,0,0), date_time_init(2015,1,4,0,0,0).prev_weekday
+ assert_equal date_time_init(2015,1,2,15,15,10), date_time_init(2015,1,4,15,15,10).prev_weekday
+ end
+
def test_prev_month_on_31st
assert_equal date_time_init(2004,2,29,10,10,10), date_time_init(2004,3,31,10,10,10).prev_month
end
@@ -231,6 +286,21 @@ module DateAndTimeBehavior
end
end
+ def test_on_weekend_on_saturday
+ assert date_time_init(2015,1,3,0,0,0).on_weekend?
+ assert date_time_init(2015,1,3,15,15,10).on_weekend?
+ end
+
+ def test_on_weekend_on_sunday
+ assert date_time_init(2015,1,4,0,0,0).on_weekend?
+ assert date_time_init(2015,1,4,15,15,10).on_weekend?
+ end
+
+ def test_on_weekend_on_monday
+ assert_not date_time_init(2015,1,5,0,0,0).on_weekend?
+ assert_not date_time_init(2015,1,5,15,15,10).on_weekend?
+ end
+
def with_bw_default(bw = :monday)
old_bw = Date.beginning_of_week
Date.beginning_of_week = bw
diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb
index e89be25b53..0fc3f765f5 100644
--- a/activesupport/test/core_ext/date_ext_test.rb
+++ b/activesupport/test/core_ext/date_ext_test.rb
@@ -191,8 +191,9 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
def test_yesterday_constructor_when_zone_is_set
with_env_tz 'UTC' do
with_tz_default ActiveSupport::TimeZone['Eastern Time (US & Canada)'] do # UTC -5
- Time.stubs(:now).returns Time.local(2000, 1, 1)
- assert_equal Date.new(1999, 12, 30), Date.yesterday
+ Time.stub(:now, Time.local(2000, 1, 1)) do
+ assert_equal Date.new(1999, 12, 30), Date.yesterday
+ end
end
end
end
@@ -212,8 +213,9 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
def test_tomorrow_constructor_when_zone_is_set
with_env_tz 'UTC' do
with_tz_default ActiveSupport::TimeZone['Europe/Paris'] do # UTC +1
- Time.stubs(:now).returns Time.local(1999, 12, 31, 23)
- assert_equal Date.new(2000, 1, 2), Date.tomorrow
+ Time.stub(:now, Time.local(1999, 12, 31, 23)) do
+ assert_equal Date.new(2000, 1, 2), Date.tomorrow
+ end
end
end
end
@@ -317,23 +319,26 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
end
def test_past
- Date.stubs(:current).returns(Date.new(2000, 1, 1))
- assert_equal true, Date.new(1999, 12, 31).past?
- assert_equal false, Date.new(2000,1,1).past?
- assert_equal false, Date.new(2000,1,2).past?
+ Date.stub(:current, Date.new(2000, 1, 1)) do
+ assert_equal true, Date.new(1999, 12, 31).past?
+ assert_equal false, Date.new(2000,1,1).past?
+ assert_equal false, Date.new(2000,1,2).past?
+ end
end
def test_future
- Date.stubs(:current).returns(Date.new(2000, 1, 1))
- assert_equal false, Date.new(1999, 12, 31).future?
- assert_equal false, Date.new(2000,1,1).future?
- assert_equal true, Date.new(2000,1,2).future?
+ Date.stub(:current, Date.new(2000, 1, 1)) do
+ assert_equal false, Date.new(1999, 12, 31).future?
+ assert_equal false, Date.new(2000,1,1).future?
+ assert_equal true, Date.new(2000,1,2).future?
+ end
end
def test_current_returns_date_today_when_zone_not_set
with_env_tz 'US/Central' do
- Time.stubs(:now).returns Time.local(1999, 12, 31, 23)
- assert_equal Date.today, Date.current
+ Time.stub(:now, Time.local(1999, 12, 31, 23)) do
+ assert_equal Date.today, Date.current
+ end
end
end
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index 74319ecd09..6fe38c45ec 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -204,61 +204,69 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_today_with_offset
- Date.stubs(:current).returns(Date.new(2000, 1, 1))
- assert_equal false, DateTime.civil(1999,12,31,23,59,59, Rational(-18000, 86400)).today?
- assert_equal true, DateTime.civil(2000,1,1,0,0,0, Rational(-18000, 86400)).today?
- assert_equal true, DateTime.civil(2000,1,1,23,59,59, Rational(-18000, 86400)).today?
- assert_equal false, DateTime.civil(2000,1,2,0,0,0, Rational(-18000, 86400)).today?
+ Date.stub(:current, Date.new(2000, 1, 1)) do
+ assert_equal false, DateTime.civil(1999,12,31,23,59,59, Rational(-18000, 86400)).today?
+ assert_equal true, DateTime.civil(2000,1,1,0,0,0, Rational(-18000, 86400)).today?
+ assert_equal true, DateTime.civil(2000,1,1,23,59,59, Rational(-18000, 86400)).today?
+ assert_equal false, DateTime.civil(2000,1,2,0,0,0, Rational(-18000, 86400)).today?
+ end
end
def test_today_without_offset
- Date.stubs(:current).returns(Date.new(2000, 1, 1))
- assert_equal false, DateTime.civil(1999,12,31,23,59,59).today?
- assert_equal true, DateTime.civil(2000,1,1,0).today?
- assert_equal true, DateTime.civil(2000,1,1,23,59,59).today?
- assert_equal false, DateTime.civil(2000,1,2,0).today?
+ Date.stub(:current, Date.new(2000, 1, 1)) do
+ assert_equal false, DateTime.civil(1999,12,31,23,59,59).today?
+ assert_equal true, DateTime.civil(2000,1,1,0).today?
+ assert_equal true, DateTime.civil(2000,1,1,23,59,59).today?
+ assert_equal false, DateTime.civil(2000,1,2,0).today?
+ end
end
def test_past_with_offset
- DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)))
- assert_equal true, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).past?
- assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).past?
- assert_equal false, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).past?
+ DateTime.stub(:current, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) do
+ assert_equal true, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).past?
+ assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).past?
+ assert_equal false, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).past?
+ end
end
def test_past_without_offset
- DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)))
- assert_equal true, DateTime.civil(2005,2,10,20,30,44).past?
- assert_equal false, DateTime.civil(2005,2,10,20,30,45).past?
- assert_equal false, DateTime.civil(2005,2,10,20,30,46).past?
+ DateTime.stub(:current, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) do
+ assert_equal true, DateTime.civil(2005,2,10,20,30,44).past?
+ assert_equal false, DateTime.civil(2005,2,10,20,30,45).past?
+ assert_equal false, DateTime.civil(2005,2,10,20,30,46).past?
+ end
end
def test_future_with_offset
- DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)))
- assert_equal false, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).future?
- assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).future?
- assert_equal true, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).future?
+ DateTime.stub(:current, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) do
+ assert_equal false, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).future?
+ assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).future?
+ assert_equal true, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).future?
+ end
end
def test_future_without_offset
- DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)))
- assert_equal false, DateTime.civil(2005,2,10,20,30,44).future?
- assert_equal false, DateTime.civil(2005,2,10,20,30,45).future?
- assert_equal true, DateTime.civil(2005,2,10,20,30,46).future?
+ DateTime.stub(:current, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) do
+ assert_equal false, DateTime.civil(2005,2,10,20,30,44).future?
+ assert_equal false, DateTime.civil(2005,2,10,20,30,45).future?
+ assert_equal true, DateTime.civil(2005,2,10,20,30,46).future?
+ end
end
def test_current_returns_date_today_when_zone_is_not_set
with_env_tz 'US/Eastern' do
- Time.stubs(:now).returns Time.local(1999, 12, 31, 23, 59, 59)
- assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current
+ Time.stub(:now, Time.local(1999, 12, 31, 23, 59, 59)) do
+ assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current
+ end
end
end
def test_current_returns_time_zone_today_when_zone_is_set
Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
with_env_tz 'US/Eastern' do
- Time.stubs(:now).returns Time.local(1999, 12, 31, 23, 59, 59)
- assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current
+ Time.stub(:now, Time.local(1999, 12, 31, 23, 59, 59)) do
+ assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current
+ end
end
ensure
Time.zone = nil
@@ -335,6 +343,13 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal(-1, DateTime.civil(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone['UTC'] ))
end
+ def test_compare_with_string
+ assert_equal 1, DateTime.civil(2000) <=> Time.utc(1999, 12, 31, 23, 59, 59).to_s
+ assert_equal 0, DateTime.civil(2000) <=> Time.utc(2000, 1, 1, 0, 0, 0).to_s
+ assert_equal( -1, DateTime.civil(2000) <=> Time.utc(2000, 1, 1, 0, 0, 1).to_s)
+ assert_equal nil, DateTime.civil(2000) <=> "Invalid as Time"
+ end
+
def test_to_f
assert_equal 946684800.0, DateTime.civil(2000).to_f
assert_equal 946684800.0, DateTime.civil(1999,12,31,19,0,0,Rational(-5,24)).to_f
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index 5a2af7f943..9e97acaffb 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -30,7 +30,6 @@ class DurationTest < ActiveSupport::TestCase
assert ActiveSupport::Duration === 1.day
assert !(ActiveSupport::Duration === 1.day.to_i)
assert !(ActiveSupport::Duration === 'foo')
- assert !(ActiveSupport::Duration === ActiveSupport::ProxyObject.new)
end
def test_equals
@@ -71,6 +70,15 @@ class DurationTest < ActiveSupport::TestCase
assert_equal '14 days', 1.fortnight.inspect
end
+ def test_inspect_locale
+ current_locale = I18n.default_locale
+ I18n.default_locale = :de
+ I18n.backend.store_translations(:de, { support: { array: { last_word_connector: ' und ' } } })
+ assert_equal '10 years, 1 month und 1 day', (10.years + 1.month + 1.day).inspect
+ ensure
+ I18n.default_locale = current_locale
+ end
+
def test_minus_with_duration_does_not_break_subtraction_of_date_from_date
assert_nothing_raised { Date.today - Date.today }
end
@@ -132,28 +140,30 @@ class DurationTest < ActiveSupport::TestCase
def test_since_and_ago_anchored_to_time_now_when_time_zone_is_not_set
Time.zone = nil
with_env_tz 'US/Eastern' do
- Time.stubs(:now).returns Time.local(2000)
- # since
- assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.since
- assert_equal Time.local(2000,1,1,0,0,5), 5.seconds.since
- # ago
- assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago
- assert_equal Time.local(1999,12,31,23,59,55), 5.seconds.ago
+ Time.stub(:now, Time.local(2000)) do
+ # since
+ assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.since
+ assert_equal Time.local(2000,1,1,0,0,5), 5.seconds.since
+ # ago
+ assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago
+ assert_equal Time.local(1999,12,31,23,59,55), 5.seconds.ago
+ end
end
end
def test_since_and_ago_anchored_to_time_zone_now_when_time_zone_is_set
Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
with_env_tz 'US/Eastern' do
- Time.stubs(:now).returns Time.local(2000)
- # since
- assert_instance_of ActiveSupport::TimeWithZone, 5.seconds.since
- assert_equal Time.utc(2000,1,1,0,0,5), 5.seconds.since.time
- assert_equal 'Eastern Time (US & Canada)', 5.seconds.since.time_zone.name
- # ago
- assert_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago
- assert_equal Time.utc(1999,12,31,23,59,55), 5.seconds.ago.time
- assert_equal 'Eastern Time (US & Canada)', 5.seconds.ago.time_zone.name
+ Time.stub(:now, Time.local(2000)) do
+ # since
+ assert_instance_of ActiveSupport::TimeWithZone, 5.seconds.since
+ assert_equal Time.utc(2000,1,1,0,0,5), 5.seconds.since.time
+ assert_equal 'Eastern Time (US & Canada)', 5.seconds.since.time_zone.name
+ # ago
+ assert_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago
+ assert_equal Time.utc(1999,12,31,23,59,55), 5.seconds.ago.time
+ assert_equal 'Eastern Time (US & Canada)', 5.seconds.ago.time_zone.name
+ end
end
ensure
Time.zone = nil
@@ -200,4 +210,16 @@ class DurationTest < ActiveSupport::TestCase
def test_hash
assert_equal 1.minute.hash, 60.seconds.hash
end
+
+ def test_comparable
+ assert_equal(-1, (0.seconds <=> 1.second))
+ assert_equal(-1, (1.second <=> 1.minute))
+ assert_equal(-1, (1 <=> 1.minute))
+ assert_equal(0, (0.seconds <=> 0.seconds))
+ assert_equal(0, (0.seconds <=> 0.minutes))
+ assert_equal(0, (1.second <=> 1.second))
+ assert_equal(1, (1.second <=> 0.second))
+ assert_equal(1, (1.minute <=> 1.second))
+ assert_equal(1, (61 <=> 1.minute))
+ end
end
diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb
index 6fcf6e8743..f09b7d8850 100644
--- a/activesupport/test/core_ext/enumerable_test.rb
+++ b/activesupport/test/core_ext/enumerable_test.rb
@@ -3,6 +3,8 @@ require 'active_support/core_ext/array'
require 'active_support/core_ext/enumerable'
Payment = Struct.new(:price)
+ExpandedPayment = Struct.new(:dollars, :cents)
+
class SummablePayment < Payment
def +(p) self.class.new(price + p.price) end
end
@@ -71,14 +73,14 @@ class EnumerableTests < ActiveSupport::TestCase
def test_index_by
payments = GenericEnumerable.new([ Payment.new(5), Payment.new(15), Payment.new(10) ])
assert_equal({ 5 => Payment.new(5), 15 => Payment.new(15), 10 => Payment.new(10) },
- payments.index_by { |p| p.price })
+ payments.index_by(&:price))
assert_equal Enumerator, payments.index_by.class
if Enumerator.method_defined? :size
assert_equal nil, payments.index_by.size
assert_equal 42, (1..42).index_by.size
end
assert_equal({ 5 => Payment.new(5), 15 => Payment.new(15), 10 => Payment.new(10) },
- payments.index_by.each { |p| p.price })
+ payments.index_by.each(&:price))
end
def test_many
@@ -103,4 +105,23 @@ class EnumerableTests < ActiveSupport::TestCase
assert_equal true, GenericEnumerable.new([ 1 ]).exclude?(2)
assert_equal false, GenericEnumerable.new([ 1 ]).exclude?(1)
end
+
+ def test_without
+ assert_equal [1, 2, 4], GenericEnumerable.new((1..5).to_a).without(3, 5)
+ assert_equal [1, 2, 4], (1..5).to_a.without(3, 5)
+ assert_equal [1, 2, 4], (1..5).to_set.without(3, 5)
+ assert_equal({foo: 1, baz: 3}, {foo: 1, bar: 2, baz: 3}.without(:bar))
+ end
+
+ def test_pluck
+ payments = GenericEnumerable.new([ Payment.new(5), Payment.new(15), Payment.new(10) ])
+ assert_equal [5, 15, 10], payments.pluck(:price)
+
+ payments = GenericEnumerable.new([
+ ExpandedPayment.new(5, 99),
+ ExpandedPayment.new(15, 0),
+ ExpandedPayment.new(10, 50)
+ ])
+ assert_equal [[5, 99], [15, 0], [10, 50]], payments.pluck(:dollars, :cents)
+ end
end
diff --git a/activesupport/test/core_ext/file_test.rb b/activesupport/test/core_ext/file_test.rb
index 2c04e9687c..cde0132b97 100644
--- a/activesupport/test/core_ext/file_test.rb
+++ b/activesupport/test/core_ext/file_test.rb
@@ -57,6 +57,16 @@ class AtomicWriteTest < ActiveSupport::TestCase
File.unlink(file_name) rescue nil
end
+ def test_atomic_write_returns_result_from_yielded_block
+ block_return_value = File.atomic_write(file_name, Dir.pwd) do |file|
+ "Hello world!"
+ end
+
+ assert_equal "Hello world!", block_return_value
+ ensure
+ File.unlink(file_name) rescue nil
+ end
+
private
def file_name
"atomic.file"
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 5e9fdfd872..4624338df1 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -365,7 +365,7 @@ class HashExtTest < ActiveSupport::TestCase
:member? => true }
hashes.each do |name, hash|
- method_map.sort_by { |m| m.to_s }.each do |meth, expected|
+ method_map.sort_by(&:to_s).each do |meth, expected|
assert_equal(expected, hash.__send__(meth, 'a'),
"Calling #{name}.#{meth} 'a'")
assert_equal(expected, hash.__send__(meth, :a),
@@ -524,6 +524,10 @@ class HashExtTest < ActiveSupport::TestCase
end
def test_indifferent_reverse_merging
+ hash = HashWithIndifferentAccess.new key: :old_value
+ hash.reverse_merge! key: :new_value
+ assert_equal :old_value, hash[:key]
+
hash = HashWithIndifferentAccess.new('some' => 'value', 'other' => 'value')
hash.reverse_merge!(:some => 'noclobber', :another => 'clobber')
assert_equal 'value', hash[:some]
@@ -547,6 +551,11 @@ class HashExtTest < ActiveSupport::TestCase
assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash
end
+ def test_indifferent_select_returns_enumerator
+ enum = ActiveSupport::HashWithIndifferentAccess.new(@strings).select
+ assert_instance_of Enumerator, enum
+ end
+
def test_indifferent_select_returns_a_hash_when_unchanged
hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).select {|k,v| true}
@@ -568,6 +577,11 @@ class HashExtTest < ActiveSupport::TestCase
assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash
end
+ def test_indifferent_reject_returns_enumerator
+ enum = ActiveSupport::HashWithIndifferentAccess.new(@strings).reject
+ assert_instance_of Enumerator, enum
+ end
+
def test_indifferent_reject_bang
indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@strings)
indifferent_strings.reject! {|k,v| v != 1}
@@ -961,10 +975,11 @@ class HashExtTest < ActiveSupport::TestCase
assert_raise(RuntimeError) { original.except!(:a) }
end
- def test_except_with_mocha_expectation_on_original
+ def test_except_does_not_delete_values_in_original
original = { :a => 'x', :b => 'y' }
- original.expects(:delete).never
- original.except(:a)
+ assert_not_called(original, :delete) do
+ original.except(:a)
+ end
end
def test_compact
@@ -998,6 +1013,37 @@ class HashExtTest < ActiveSupport::TestCase
assert hash.key?('a')
assert_equal 1, hash[:a]
end
+
+ def test_dup_with_default_proc
+ hash = HashWithIndifferentAccess.new
+ hash.default_proc = proc { |h, v| raise "walrus" }
+ assert_nothing_raised { hash.dup }
+ end
+
+ def test_dup_with_default_proc_sets_proc
+ hash = HashWithIndifferentAccess.new
+ hash.default_proc = proc { |h, k| k + 1 }
+ new_hash = hash.dup
+
+ assert_equal 3, new_hash[2]
+
+ new_hash.default = 2
+ assert_equal 2, new_hash[:non_existant]
+ end
+
+ def test_to_hash_with_raising_default_proc
+ hash = HashWithIndifferentAccess.new
+ hash.default_proc = proc { |h, k| raise "walrus" }
+
+ assert_nothing_raised { hash.to_hash }
+ end
+
+ def test_new_from_hash_copying_default_should_not_raise_when_default_proc_does
+ hash = Hash.new
+ hash.default_proc = proc { |h, k| raise "walrus" }
+
+ assert_nothing_raised { HashWithIndifferentAccess.new_from_hash_copying_default(hash) }
+ end
end
class IWriteMyOwnXML
@@ -1549,6 +1595,14 @@ class HashToXmlTest < ActiveSupport::TestCase
assert_not_same hash_wia, hash_wia.with_indifferent_access
end
+
+ def test_allows_setting_frozen_array_values_with_indifferent_access
+ value = [1, 2, 3].freeze
+ hash = HashWithIndifferentAccess.new
+ hash[:key] = value
+ assert_equal hash[:key], value
+ end
+
def test_should_copy_the_default_value_when_converting_to_hash_with_indifferent_access
hash = Hash.new(3)
hash_wia = hash.with_indifferent_access
diff --git a/activesupport/test/core_ext/kernel_test.rb b/activesupport/test/core_ext/kernel_test.rb
index a87af0007c..503e6595cb 100644
--- a/activesupport/test/core_ext/kernel_test.rb
+++ b/activesupport/test/core_ext/kernel_test.rb
@@ -15,7 +15,6 @@ class KernelTest < ActiveSupport::TestCase
assert_equal old_verbose, $VERBOSE
end
-
def test_enable_warnings
enable_warnings { assert_equal true, $VERBOSE }
assert_equal 1234, enable_warnings { 1234 }
@@ -29,57 +28,11 @@ class KernelTest < ActiveSupport::TestCase
assert_equal old_verbose, $VERBOSE
end
-
- def test_silence_stream
- old_stream_position = STDOUT.tell
- silence_stream(STDOUT) { STDOUT.puts 'hello world' }
- assert_equal old_stream_position, STDOUT.tell
- rescue Errno::ESPIPE
- # Skip if we can't stream.tell
- end
-
- def test_silence_stream_closes_file_descriptors
- stream = StringIO.new
- dup_stream = StringIO.new
- stream.stubs(:dup).returns(dup_stream)
- dup_stream.expects(:close)
- silence_stream(stream) { stream.puts 'hello world' }
- end
-
- def test_quietly
- old_stdout_position, old_stderr_position = STDOUT.tell, STDERR.tell
- assert_deprecated do
- quietly do
- puts 'see me, feel me'
- STDERR.puts 'touch me, heal me'
- end
- end
- assert_equal old_stdout_position, STDOUT.tell
- assert_equal old_stderr_position, STDERR.tell
- rescue Errno::ESPIPE
- # Skip if we can't STDERR.tell
- end
-
def test_class_eval
o = Object.new
class << o; @x = 1; end
assert_equal 1, o.class_eval { @x }
end
-
- def test_capture
- assert_deprecated do
- assert_equal 'STDERR', capture(:stderr) { $stderr.print 'STDERR' }
- end
- assert_deprecated do
- assert_equal 'STDOUT', capture(:stdout) { print 'STDOUT' }
- end
- assert_deprecated do
- assert_equal "STDERR\n", capture(:stderr) { system('echo STDERR 1>&2') }
- end
- assert_deprecated do
- assert_equal "STDOUT\n", capture(:stdout) { system('echo STDOUT') }
- end
- end
end
class KernelSuppressTest < ActiveSupport::TestCase
@@ -112,27 +65,3 @@ class MockStdErr
puts(message)
end
end
-
-class KernelDebuggerTest < ActiveSupport::TestCase
- def test_debugger_not_available_message_to_stderr
- old_stderr = $stderr
- $stderr = MockStdErr.new
- debugger
- assert_match(/Debugger requested/, $stderr.output.first)
- ensure
- $stderr = old_stderr
- end
-
- def test_debugger_not_available_message_to_rails_logger
- rails = Class.new do
- def self.logger
- @logger ||= MockStdErr.new
- end
- end
- Object.const_set(:Rails, rails)
- debugger
- assert_match(/Debugger requested/, rails.logger.output.first)
- ensure
- Object.send(:remove_const, :Rails)
- end
-end if RUBY_VERSION < '2.0.0'
diff --git a/activesupport/test/core_ext/load_error_test.rb b/activesupport/test/core_ext/load_error_test.rb
index 5f804c749b..b2a75a2bcc 100644
--- a/activesupport/test/core_ext/load_error_test.rb
+++ b/activesupport/test/core_ext/load_error_test.rb
@@ -1,26 +1,11 @@
require 'abstract_unit'
require 'active_support/core_ext/load_error'
-class TestMissingSourceFile < ActiveSupport::TestCase
- def test_with_require
- assert_raise(MissingSourceFile) { require 'no_this_file_don\'t_exist' }
- end
- def test_with_load
- assert_raise(MissingSourceFile) { load 'nor_does_this_one' }
- end
- def test_path
- begin load 'nor/this/one.rb'
- rescue MissingSourceFile => e
- assert_equal 'nor/this/one.rb', e.path
- end
- end
- def test_is_missing
- begin load 'nor_does_this_one'
- rescue MissingSourceFile => e
- assert e.is_missing?('nor_does_this_one')
- assert e.is_missing?('nor_does_this_one.rb')
- assert_not e.is_missing?('some_other_file')
+class TestMissingSourceFile < ActiveSupport::TestCase
+ def test_it_is_deprecated
+ assert_deprecated do
+ MissingSourceFile.new
end
end
end
diff --git a/activesupport/test/core_ext/marshal_test.rb b/activesupport/test/core_ext/marshal_test.rb
index 8f3f710dfd..825df439a5 100644
--- a/activesupport/test/core_ext/marshal_test.rb
+++ b/activesupport/test/core_ext/marshal_test.rb
@@ -8,28 +8,28 @@ class MarshalTest < ActiveSupport::TestCase
def teardown
ActiveSupport::Dependencies.clear
- remove_constants(:E, :ClassFolder)
+ remove_constants(:EM, :ClassFolder)
end
test "that Marshal#load still works" do
sanity_data = ["test", [1, 2, 3], {a: [1, 2, 3]}, ActiveSupport::TestCase]
sanity_data.each do |obj|
dumped = Marshal.dump(obj)
- assert_equal Marshal.load_without_autoloading(dumped), Marshal.load(dumped)
+ assert_equal Marshal.method(:load).super_method.call(dumped), Marshal.load(dumped)
end
end
test "that a missing class is autoloaded from string" do
dumped = nil
with_autoloading_fixtures do
- dumped = Marshal.dump(E.new)
+ dumped = Marshal.dump(EM.new)
end
- remove_constants(:E)
+ remove_constants(:EM)
ActiveSupport::Dependencies.clear
with_autoloading_fixtures do
- assert_kind_of E, Marshal.load(dumped)
+ assert_kind_of EM, Marshal.load(dumped)
end
end
@@ -50,16 +50,16 @@ class MarshalTest < ActiveSupport::TestCase
test "that more than one missing class is autoloaded" do
dumped = nil
with_autoloading_fixtures do
- dumped = Marshal.dump([E.new, ClassFolder.new])
+ dumped = Marshal.dump([EM.new, ClassFolder.new])
end
- remove_constants(:E, :ClassFolder)
+ remove_constants(:EM, :ClassFolder)
ActiveSupport::Dependencies.clear
with_autoloading_fixtures do
loaded = Marshal.load(dumped)
assert_equal 2, loaded.size
- assert_kind_of E, loaded[0]
+ assert_kind_of EM, loaded[0]
assert_kind_of ClassFolder, loaded[1]
end
end
@@ -67,10 +67,10 @@ class MarshalTest < ActiveSupport::TestCase
test "that a real missing class is causing an exception" do
dumped = nil
with_autoloading_fixtures do
- dumped = Marshal.dump(E.new)
+ dumped = Marshal.dump(EM.new)
end
- remove_constants(:E)
+ remove_constants(:EM)
ActiveSupport::Dependencies.clear
assert_raise(NameError) do
@@ -84,10 +84,10 @@ class MarshalTest < ActiveSupport::TestCase
end
with_autoloading_fixtures do
- dumped = Marshal.dump([E.new, SomeClass.new])
+ dumped = Marshal.dump([EM.new, SomeClass.new])
end
- remove_constants(:E)
+ remove_constants(:EM)
self.class.send(:remove_const, :SomeClass)
ActiveSupport::Dependencies.clear
@@ -96,8 +96,8 @@ class MarshalTest < ActiveSupport::TestCase
Marshal.load(dumped)
end
- assert_nothing_raised("E failed to load while we expect only SomeClass to fail loading") do
- E.new
+ assert_nothing_raised("EM failed to load while we expect only SomeClass to fail loading") do
+ EM.new
end
assert_raise(NameError, "We expected SomeClass to not be loaded but it is!") do
@@ -109,16 +109,16 @@ class MarshalTest < ActiveSupport::TestCase
test "loading classes from files trigger autoloading" do
Tempfile.open("object_serializer_test") do |f|
with_autoloading_fixtures do
- Marshal.dump(E.new, f)
+ Marshal.dump(EM.new, f)
end
f.rewind
- remove_constants(:E)
+ remove_constants(:EM)
ActiveSupport::Dependencies.clear
with_autoloading_fixtures do
- assert_kind_of E, Marshal.load(f)
+ assert_kind_of EM, Marshal.load(f)
end
end
end
-end \ No newline at end of file
+end
diff --git a/activesupport/test/core_ext/module/attribute_accessor_test.rb b/activesupport/test/core_ext/module/attribute_accessor_test.rb
index 48f3cc579f..128c5e3d1a 100644
--- a/activesupport/test/core_ext/module/attribute_accessor_test.rb
+++ b/activesupport/test/core_ext/module/attribute_accessor_test.rb
@@ -76,4 +76,10 @@ class ModuleAttributeAccessorTest < ActiveSupport::TestCase
assert_equal 'default_reader_value', @module.defr
assert_equal 'default_writer_value', @module.class_variable_get('@@defw')
end
+
+ def test_should_not_invoke_default_value_block_multiple_times
+ count = 0
+ @module.cattr_accessor(:defcount){ count += 1 }
+ assert_equal 1, count
+ end
end
diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb
index 3c49c4d14f..bdfbadcf1d 100644
--- a/activesupport/test/core_ext/module_test.rb
+++ b/activesupport/test/core_ext/module_test.rb
@@ -78,7 +78,7 @@ Product = Struct.new(:name) do
def type
@type ||= begin
- :thing_without_same_method_name_as_delegated.name
+ nil.type_name
end
end
end
@@ -358,148 +358,178 @@ class MethodAliasingTest < ActiveSupport::TestCase
Object.instance_eval { remove_const :FooClassWithBarMethod }
end
- def test_alias_method_chain
- assert @instance.respond_to?(:bar)
- feature_aliases = [:bar_with_baz, :bar_without_baz]
+ def test_alias_method_chain_deprecated
+ assert_deprecated(/alias_method_chain/) do
+ Module.new do
+ def base
+ end
+
+ def base_with_deprecated
+ end
- feature_aliases.each do |method|
- assert !@instance.respond_to?(method)
+ alias_method_chain :base, :deprecated
+ end
end
+ end
- assert_equal 'bar', @instance.bar
+ def test_alias_method_chain
+ assert_deprecated(/alias_method_chain/) do
+ assert @instance.respond_to?(:bar)
+ feature_aliases = [:bar_with_baz, :bar_without_baz]
- FooClassWithBarMethod.class_eval { include BarMethodAliaser }
+ feature_aliases.each do |method|
+ assert !@instance.respond_to?(method)
+ end
- feature_aliases.each do |method|
- assert_respond_to @instance, method
- end
+ assert_equal 'bar', @instance.bar
+
+ FooClassWithBarMethod.class_eval { include BarMethodAliaser }
+
+ feature_aliases.each do |method|
+ assert_respond_to @instance, method
+ end
- assert_equal 'bar_with_baz', @instance.bar
- assert_equal 'bar', @instance.bar_without_baz
+ assert_equal 'bar_with_baz', @instance.bar
+ assert_equal 'bar', @instance.bar_without_baz
+ end
end
def test_alias_method_chain_with_punctuation_method
- FooClassWithBarMethod.class_eval do
- def quux!; 'quux' end
- end
+ assert_deprecated(/alias_method_chain/) do
+ FooClassWithBarMethod.class_eval do
+ def quux!; 'quux' end
+ end
- assert !@instance.respond_to?(:quux_with_baz!)
- FooClassWithBarMethod.class_eval do
- include BarMethodAliaser
- alias_method_chain :quux!, :baz
- end
- assert_respond_to @instance, :quux_with_baz!
+ assert !@instance.respond_to?(:quux_with_baz!)
+ FooClassWithBarMethod.class_eval do
+ include BarMethodAliaser
+ alias_method_chain :quux!, :baz
+ end
+ assert_respond_to @instance, :quux_with_baz!
- assert_equal 'quux_with_baz', @instance.quux!
- assert_equal 'quux', @instance.quux_without_baz!
+ assert_equal 'quux_with_baz', @instance.quux!
+ assert_equal 'quux', @instance.quux_without_baz!
+ end
end
def test_alias_method_chain_with_same_names_between_predicates_and_bang_methods
- FooClassWithBarMethod.class_eval do
- def quux!; 'quux!' end
- def quux?; true end
- def quux=(v); 'quux=' end
- end
+ assert_deprecated(/alias_method_chain/) do
+ FooClassWithBarMethod.class_eval do
+ def quux!; 'quux!' end
+ def quux?; true end
+ def quux=(v); 'quux=' end
+ end
- assert !@instance.respond_to?(:quux_with_baz!)
- assert !@instance.respond_to?(:quux_with_baz?)
- assert !@instance.respond_to?(:quux_with_baz=)
+ assert !@instance.respond_to?(:quux_with_baz!)
+ assert !@instance.respond_to?(:quux_with_baz?)
+ assert !@instance.respond_to?(:quux_with_baz=)
- FooClassWithBarMethod.class_eval { include BarMethodAliaser }
- assert_respond_to @instance, :quux_with_baz!
- assert_respond_to @instance, :quux_with_baz?
- assert_respond_to @instance, :quux_with_baz=
+ FooClassWithBarMethod.class_eval { include BarMethodAliaser }
+ assert_respond_to @instance, :quux_with_baz!
+ assert_respond_to @instance, :quux_with_baz?
+ assert_respond_to @instance, :quux_with_baz=
- FooClassWithBarMethod.alias_method_chain :quux!, :baz
- assert_equal 'quux!_with_baz', @instance.quux!
- assert_equal 'quux!', @instance.quux_without_baz!
+ FooClassWithBarMethod.alias_method_chain :quux!, :baz
+ assert_equal 'quux!_with_baz', @instance.quux!
+ assert_equal 'quux!', @instance.quux_without_baz!
- FooClassWithBarMethod.alias_method_chain :quux?, :baz
- assert_equal false, @instance.quux?
- assert_equal true, @instance.quux_without_baz?
+ FooClassWithBarMethod.alias_method_chain :quux?, :baz
+ assert_equal false, @instance.quux?
+ assert_equal true, @instance.quux_without_baz?
- FooClassWithBarMethod.alias_method_chain :quux=, :baz
- assert_equal 'quux=_with_baz', @instance.send(:quux=, 1234)
- assert_equal 'quux=', @instance.send(:quux_without_baz=, 1234)
+ FooClassWithBarMethod.alias_method_chain :quux=, :baz
+ assert_equal 'quux=_with_baz', @instance.send(:quux=, 1234)
+ assert_equal 'quux=', @instance.send(:quux_without_baz=, 1234)
+ end
end
def test_alias_method_chain_with_feature_punctuation
- FooClassWithBarMethod.class_eval do
- def quux; 'quux' end
- def quux?; 'quux?' end
- include BarMethodAliaser
- alias_method_chain :quux, :baz!
- end
+ assert_deprecated(/alias_method_chain/) do
+ FooClassWithBarMethod.class_eval do
+ def quux; 'quux' end
+ def quux?; 'quux?' end
+ include BarMethodAliaser
+ alias_method_chain :quux, :baz!
+ end
- assert_nothing_raised do
- assert_equal 'quux_with_baz', @instance.quux_with_baz!
- end
+ assert_nothing_raised do
+ assert_equal 'quux_with_baz', @instance.quux_with_baz!
+ end
- assert_raise(NameError) do
- FooClassWithBarMethod.alias_method_chain :quux?, :baz!
+ assert_raise(NameError) do
+ FooClassWithBarMethod.alias_method_chain :quux?, :baz!
+ end
end
end
def test_alias_method_chain_yields_target_and_punctuation
- args = nil
+ assert_deprecated(/alias_method_chain/) do
+ args = nil
- FooClassWithBarMethod.class_eval do
- def quux?; end
- include BarMethods
+ FooClassWithBarMethod.class_eval do
+ def quux?; end
+ include BarMethods
- FooClassWithBarMethod.alias_method_chain :quux?, :baz do |target, punctuation|
- args = [target, punctuation]
+ FooClassWithBarMethod.alias_method_chain :quux?, :baz do |target, punctuation|
+ args = [target, punctuation]
+ end
end
- end
- assert_not_nil args
- assert_equal 'quux', args[0]
- assert_equal '?', args[1]
+ assert_not_nil args
+ assert_equal 'quux', args[0]
+ assert_equal '?', args[1]
+ end
end
def test_alias_method_chain_preserves_private_method_status
- FooClassWithBarMethod.class_eval do
- def duck; 'duck' end
- include BarMethodAliaser
- private :duck
- alias_method_chain :duck, :orange
- end
+ assert_deprecated(/alias_method_chain/) do
+ FooClassWithBarMethod.class_eval do
+ def duck; 'duck' end
+ include BarMethodAliaser
+ private :duck
+ alias_method_chain :duck, :orange
+ end
- assert_raise NoMethodError do
- @instance.duck
- end
+ assert_raise NoMethodError do
+ @instance.duck
+ end
- assert_equal 'duck_with_orange', @instance.instance_eval { duck }
- assert FooClassWithBarMethod.private_method_defined?(:duck)
+ assert_equal 'duck_with_orange', @instance.instance_eval { duck }
+ assert FooClassWithBarMethod.private_method_defined?(:duck)
+ end
end
def test_alias_method_chain_preserves_protected_method_status
- FooClassWithBarMethod.class_eval do
- def duck; 'duck' end
- include BarMethodAliaser
- protected :duck
- alias_method_chain :duck, :orange
- end
+ assert_deprecated(/alias_method_chain/) do
+ FooClassWithBarMethod.class_eval do
+ def duck; 'duck' end
+ include BarMethodAliaser
+ protected :duck
+ alias_method_chain :duck, :orange
+ end
- assert_raise NoMethodError do
- @instance.duck
- end
+ assert_raise NoMethodError do
+ @instance.duck
+ end
- assert_equal 'duck_with_orange', @instance.instance_eval { duck }
- assert FooClassWithBarMethod.protected_method_defined?(:duck)
+ assert_equal 'duck_with_orange', @instance.instance_eval { duck }
+ assert FooClassWithBarMethod.protected_method_defined?(:duck)
+ end
end
def test_alias_method_chain_preserves_public_method_status
- FooClassWithBarMethod.class_eval do
- def duck; 'duck' end
- include BarMethodAliaser
- public :duck
- alias_method_chain :duck, :orange
- end
+ assert_deprecated(/alias_method_chain/) do
+ FooClassWithBarMethod.class_eval do
+ def duck; 'duck' end
+ include BarMethodAliaser
+ public :duck
+ alias_method_chain :duck, :orange
+ end
- assert_equal 'duck_with_orange', @instance.duck
- assert FooClassWithBarMethod.public_method_defined?(:duck)
+ assert_equal 'duck_with_orange', @instance.duck
+ assert FooClassWithBarMethod.public_method_defined?(:duck)
+ end
end
def test_delegate_with_case
diff --git a/activesupport/test/core_ext/numeric_ext_test.rb b/activesupport/test/core_ext/numeric_ext_test.rb
index b82448458d..0ff8f0f89b 100644
--- a/activesupport/test/core_ext/numeric_ext_test.rb
+++ b/activesupport/test/core_ext/numeric_ext_test.rb
@@ -280,14 +280,16 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
end
def test_to_s__human_size_with_si_prefix
- assert_equal '3 Bytes', 3.14159265.to_s(:human_size, :prefix => :si)
- assert_equal '123 Bytes', 123.0.to_s(:human_size, :prefix => :si)
- assert_equal '123 Bytes', 123.to_s(:human_size, :prefix => :si)
- assert_equal '1.23 KB', 1234.to_s(:human_size, :prefix => :si)
- assert_equal '12.3 KB', 12345.to_s(:human_size, :prefix => :si)
- assert_equal '1.23 MB', 1234567.to_s(:human_size, :prefix => :si)
- assert_equal '1.23 GB', 1234567890.to_s(:human_size, :prefix => :si)
- assert_equal '1.23 TB', 1234567890123.to_s(:human_size, :prefix => :si)
+ assert_deprecated do
+ assert_equal '3 Bytes', 3.14159265.to_s(:human_size, :prefix => :si)
+ assert_equal '123 Bytes', 123.0.to_s(:human_size, :prefix => :si)
+ assert_equal '123 Bytes', 123.to_s(:human_size, :prefix => :si)
+ assert_equal '1.23 KB', 1234.to_s(:human_size, :prefix => :si)
+ assert_equal '12.3 KB', 12345.to_s(:human_size, :prefix => :si)
+ assert_equal '1.23 MB', 1234567.to_s(:human_size, :prefix => :si)
+ assert_equal '1.23 GB', 1234567890.to_s(:human_size, :prefix => :si)
+ assert_equal '1.23 TB', 1234567890123.to_s(:human_size, :prefix => :si)
+ end
end
def test_to_s__human_size_with_options_hash
@@ -389,4 +391,88 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
def test_in_milliseconds
assert_equal 10_000, 10.seconds.in_milliseconds
end
+
+ # TODO: Remove positive and negative tests when we drop support to ruby < 2.3
+ b = 2**64
+ b *= b until Bignum === b
+
+ T_ZERO = b.coerce(0).first
+ T_ONE = b.coerce(1).first
+ T_MONE = b.coerce(-1).first
+
+ def test_positive
+ assert_predicate(1, :positive?)
+ assert_not_predicate(0, :positive?)
+ assert_not_predicate(-1, :positive?)
+ assert_predicate(+1.0, :positive?)
+ assert_not_predicate(+0.0, :positive?)
+ assert_not_predicate(-0.0, :positive?)
+ assert_not_predicate(-1.0, :positive?)
+ assert_predicate(+(0.0.next_float), :positive?)
+ assert_not_predicate(-(0.0.next_float), :positive?)
+ assert_predicate(Float::INFINITY, :positive?)
+ assert_not_predicate(-Float::INFINITY, :positive?)
+ assert_not_predicate(Float::NAN, :positive?)
+
+ a = Class.new(Numeric) do
+ def >(x); true; end
+ end.new
+ assert_predicate(a, :positive?)
+
+ a = Class.new(Numeric) do
+ def >(x); false; end
+ end.new
+ assert_not_predicate(a, :positive?)
+
+ assert_predicate(1/2r, :positive?)
+ assert_not_predicate(-1/2r, :positive?)
+
+ assert_predicate(T_ONE, :positive?)
+ assert_not_predicate(T_MONE, :positive?)
+ assert_not_predicate(T_ZERO, :positive?)
+
+ e = assert_raises(NoMethodError) do
+ Complex(1).positive?
+ end
+
+ assert_match(/positive\?/, e.message)
+ end
+
+ def test_negative
+ assert_predicate(-1, :negative?)
+ assert_not_predicate(0, :negative?)
+ assert_not_predicate(1, :negative?)
+ assert_predicate(-1.0, :negative?)
+ assert_not_predicate(-0.0, :negative?)
+ assert_not_predicate(+0.0, :negative?)
+ assert_not_predicate(+1.0, :negative?)
+ assert_predicate(-(0.0.next_float), :negative?)
+ assert_not_predicate(+(0.0.next_float), :negative?)
+ assert_predicate(-Float::INFINITY, :negative?)
+ assert_not_predicate(Float::INFINITY, :negative?)
+ assert_not_predicate(Float::NAN, :negative?)
+
+ a = Class.new(Numeric) do
+ def <(x); true; end
+ end.new
+ assert_predicate(a, :negative?)
+
+ a = Class.new(Numeric) do
+ def <(x); false; end
+ end.new
+ assert_not_predicate(a, :negative?)
+
+ assert_predicate(-1/2r, :negative?)
+ assert_not_predicate(1/2r, :negative?)
+
+ assert_not_predicate(T_ONE, :negative?)
+ assert_predicate(T_MONE, :negative?)
+ assert_not_predicate(T_ZERO, :negative?)
+
+ e = assert_raises(NoMethodError) do
+ Complex(1).negative?
+ end
+
+ assert_match(/negative\?/, e.message)
+ end
end
diff --git a/activesupport/test/core_ext/object/blank_test.rb b/activesupport/test/core_ext/object/blank_test.rb
index 246bc7fa61..8a5e385dd7 100644
--- a/activesupport/test/core_ext/object/blank_test.rb
+++ b/activesupport/test/core_ext/object/blank_test.rb
@@ -1,4 +1,3 @@
-# encoding: utf-8
require 'abstract_unit'
require 'active_support/core_ext/object/blank'
diff --git a/activesupport/test/core_ext/object/deep_dup_test.rb b/activesupport/test/core_ext/object/deep_dup_test.rb
index 91d558dbb5..791b5e7172 100644
--- a/activesupport/test/core_ext/object/deep_dup_test.rb
+++ b/activesupport/test/core_ext/object/deep_dup_test.rb
@@ -50,4 +50,10 @@ class DeepDupTest < ActiveSupport::TestCase
assert dup.instance_variable_defined?(:@a)
end
+ def test_deep_dup_with_hash_class_key
+ hash = { Fixnum => 1 }
+ dup = hash.deep_dup
+ assert_equal 1, dup.keys.length
+ end
+
end
diff --git a/activesupport/test/core_ext/object/duplicable_test.rb b/activesupport/test/core_ext/object/duplicable_test.rb
index 8cc39ae7b9..042f5cfb34 100644
--- a/activesupport/test/core_ext/object/duplicable_test.rb
+++ b/activesupport/test/core_ext/object/duplicable_test.rb
@@ -6,18 +6,12 @@ require 'active_support/core_ext/numeric/time'
class DuplicableTest < ActiveSupport::TestCase
RAISE_DUP = [nil, false, true, :symbol, 1, 2.3, method(:puts)]
ALLOW_DUP = ['1', Object.new, /foo/, [], {}, Time.now, Class.new, Module.new]
-
- # Needed to support Ruby 1.9.x, as it doesn't allow dup on BigDecimal, instead
- # raises TypeError exception. Checking here on the runtime whether BigDecimal
- # will allow dup or not.
- begin
- bd = BigDecimal.new('4.56')
- ALLOW_DUP << bd.dup
- rescue TypeError
- RAISE_DUP << bd
- end
+ ALLOW_DUP << BigDecimal.new('4.56')
def test_duplicable
+ rubinius_skip "* Method#dup is allowed at the moment on Rubinius\n" \
+ "* https://github.com/rubinius/rubinius/issues/3089"
+
RAISE_DUP.each do |v|
assert !v.duplicable?
assert_raises(TypeError, v.class.name) { v.dup }
diff --git a/activesupport/test/core_ext/object/itself_test.rb b/activesupport/test/core_ext/object/itself_test.rb
deleted file mode 100644
index 65db0ddf40..0000000000
--- a/activesupport/test/core_ext/object/itself_test.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'abstract_unit'
-require 'active_support/core_ext/object'
-
-class Object::ItselfTest < ActiveSupport::TestCase
- test 'itself returns self' do
- object = 'fun'
- assert_equal object, object.itself
- end
-end
diff --git a/activesupport/test/core_ext/object/json_gem_encoding_test.rb b/activesupport/test/core_ext/object/json_gem_encoding_test.rb
new file mode 100644
index 0000000000..02ab17fb64
--- /dev/null
+++ b/activesupport/test/core_ext/object/json_gem_encoding_test.rb
@@ -0,0 +1,66 @@
+require 'abstract_unit'
+require 'json'
+require 'json/encoding_test_cases'
+
+# These test cases were added to test that we do not interfere with json gem's
+# output when the AS encoder is loaded, primarily for problems reported in
+# #20775. They need to be executed in isolation to reproduce the scenario
+# correctly, because other test cases might have already loaded additional
+# dependencies.
+
+# The AS::JSON encoder requires the BigDecimal core_ext, which, unfortunately,
+# changes the BigDecimal#to_s output, and consequently the JSON gem output. So
+# we need to require this unfront to ensure we don't get a false failure, but
+# ideally we should just fix the BigDecimal core_ext to not change to_s without
+# arguments.
+require 'active_support/core_ext/big_decimal'
+
+class JsonGemEncodingTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ JSONTest::EncodingTestCases.constants.each_with_index do |name|
+ JSONTest::EncodingTestCases.const_get(name).each_with_index do |(subject, _), i|
+ test("#{name[0..-6].underscore} #{i}") do
+ assert_same_with_or_without_active_support(subject)
+ end
+ end
+ end
+
+ class CustomToJson
+ def to_json(*)
+ '"custom"'
+ end
+ end
+
+ test "custom to_json" do
+ assert_same_with_or_without_active_support(CustomToJson.new)
+ end
+
+ private
+ def require_or_skip(file)
+ require(file) || skip("'#{file}' was already loaded")
+ end
+
+ def assert_same_with_or_without_active_support(subject)
+ begin
+ expected = JSON.generate(subject, quirks_mode: true)
+ rescue JSON::GeneratorError => e
+ exception = e
+ end
+
+ require_or_skip 'active_support/core_ext/object/json'
+
+ if exception
+ assert_raises_with_message JSON::GeneratorError, e.message do
+ JSON.generate(subject, quirks_mode: true)
+ end
+ else
+ assert_equal expected, JSON.generate(subject, quirks_mode: true)
+ end
+ end
+
+ def assert_raises_with_message(exception_class, message, &block)
+ err = assert_raises(exception_class) { block.call }
+ assert_match message, err.message
+ end
+end
diff --git a/activesupport/test/core_ext/object/try_test.rb b/activesupport/test/core_ext/object/try_test.rb
index 225c20fa36..5ea0f0eca6 100644
--- a/activesupport/test/core_ext/object/try_test.rb
+++ b/activesupport/test/core_ext/object/try_test.rb
@@ -30,10 +30,6 @@ class ObjectTryTest < ActiveSupport::TestCase
assert_raise(NoMethodError) { @string.try!(method, 'llo', 'y') }
end
- def test_try_only_block_bang
- assert_equal @string.reverse, @string.try! { |s| s.reverse }
- end
-
def test_valid_method
assert_equal 5, @string.try(:size)
end
@@ -56,7 +52,11 @@ class ObjectTryTest < ActiveSupport::TestCase
end
def test_try_only_block
- assert_equal @string.reverse, @string.try { |s| s.reverse }
+ assert_equal @string.reverse, @string.try(&:reverse)
+ end
+
+ def test_try_only_block_bang
+ assert_equal @string.reverse, @string.try!(&:reverse)
end
def test_try_only_block_nil
@@ -69,13 +69,17 @@ class ObjectTryTest < ActiveSupport::TestCase
assert_equal @string.reverse, @string.try { reverse }
end
+ def test_try_with_instance_eval_block_bang
+ assert_equal @string.reverse, @string.try! { reverse }
+ end
+
def test_try_with_private_method_bang
klass = Class.new do
private
- def private_method
- 'private method'
- end
+ def private_method
+ 'private method'
+ end
end
assert_raise(NoMethodError) { klass.new.try!(:private_method) }
@@ -85,11 +89,75 @@ class ObjectTryTest < ActiveSupport::TestCase
klass = Class.new do
private
- def private_method
- 'private method'
- end
+ def private_method
+ 'private method'
+ end
end
assert_nil klass.new.try(:private_method)
end
+
+ class Decorator < SimpleDelegator
+ def delegator_method
+ 'delegator method'
+ end
+
+ def reverse
+ 'overridden reverse'
+ end
+
+ private
+
+ def private_delegator_method
+ 'private delegator method'
+ end
+ end
+
+ def test_try_with_method_on_delegator
+ assert_equal 'delegator method', Decorator.new(@string).try(:delegator_method)
+ end
+
+ def test_try_with_method_on_delegator_target
+ assert_equal 5, Decorator.new(@string).size
+ end
+
+ def test_try_with_overriden_method_on_delegator
+ assert_equal 'overridden reverse', Decorator.new(@string).reverse
+ end
+
+ def test_try_with_private_method_on_delegator
+ assert_nil Decorator.new(@string).try(:private_delegator_method)
+ end
+
+ def test_try_with_private_method_on_delegator_bang
+ assert_raise(NoMethodError) do
+ Decorator.new(@string).try!(:private_delegator_method)
+ end
+ end
+
+ def test_try_with_private_method_on_delegator_target
+ klass = Class.new do
+ private
+
+ def private_method
+ 'private method'
+ end
+ end
+
+ assert_nil Decorator.new(klass.new).try(:private_method)
+ end
+
+ def test_try_with_private_method_on_delegator_target_bang
+ klass = Class.new do
+ private
+
+ def private_method
+ 'private method'
+ end
+ end
+
+ assert_raise(NoMethodError) do
+ Decorator.new(klass.new).try!(:private_method)
+ end
+ end
end
diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb
index 98c4ec6b5e..f096328cee 100644
--- a/activesupport/test/core_ext/range_ext_test.rb
+++ b/activesupport/test/core_ext/range_ext_test.rb
@@ -115,11 +115,11 @@ class RangeTest < ActiveSupport::TestCase
def test_date_time_with_each
datetime = DateTime.now
- assert ((datetime - 1.hour)..datetime).each {}
+ assert(((datetime - 1.hour)..datetime).each {})
end
def test_date_time_with_step
datetime = DateTime.now
- assert ((datetime - 1.hour)..datetime).step(1) {}
+ assert(((datetime - 1.hour)..datetime).step(1) {})
end
end
diff --git a/activesupport/test/core_ext/secure_random_test.rb b/activesupport/test/core_ext/secure_random_test.rb
new file mode 100644
index 0000000000..dfacb7fe9f
--- /dev/null
+++ b/activesupport/test/core_ext/secure_random_test.rb
@@ -0,0 +1,20 @@
+require 'abstract_unit'
+require 'active_support/core_ext/securerandom'
+
+class SecureRandomTest < ActiveSupport::TestCase
+ def test_base58
+ s1 = SecureRandom.base58
+ s2 = SecureRandom.base58
+
+ assert_not_equal s1, s2
+ assert_equal 16, s1.length
+ end
+
+ def test_base58_with_length
+ s1 = SecureRandom.base58(24)
+ s2 = SecureRandom.base58(24)
+
+ assert_not_equal s1, s2
+ assert_equal 24, s1.length
+ end
+end
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index 2f4691817f..3a5d6df06d 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -1,4 +1,3 @@
-# encoding: utf-8
require 'date'
require 'abstract_unit'
require 'inflector_test_cases'
@@ -250,6 +249,15 @@ class StringInflectionsTest < ActiveSupport::TestCase
assert_equal "Hello<br>Big<br>World!", "Hello<br>Big<br>World!".truncate_words(3, :omission => "[...]", :separator => '<br>')
end
+ def test_truncate_words_with_complex_string
+ Timeout.timeout(10) do
+ complex_string = "aa aa aaa aa aaa aaa aaa aa aaa aaa aaa aaa aaa aaa aaa aaa aaa aaa aaaa aaaaa aaaaa aaaaaa aa aa aa aaa aa aaa aa aa aa aa a aaa aaa \n a aaa <<s"
+ assert_equal complex_string.truncate_words(80), complex_string
+ end
+ rescue Timeout::Error
+ assert false
+ end
+
def test_truncate_multibyte
assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding(Encoding::UTF_8),
"\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding(Encoding::UTF_8).truncate(10)
@@ -260,20 +268,32 @@ class StringInflectionsTest < ActiveSupport::TestCase
end
def test_remove
- assert_equal "Summer", "Fast Summer".remove(/Fast /)
- assert_equal "Summer", "Fast Summer".remove!(/Fast /)
+ original = "This is a good day to die"
+ assert_equal "This is a good day", original.remove(" to die")
+ assert_equal "This is a good day", original.remove(" to ", /die/)
+ assert_equal "This is a good day to die", original
+ end
+
+ def test_remove_for_multiple_occurrences
+ original = "This is a good day to die to die"
+ assert_equal "This is a good day", original.remove(" to die")
+ assert_equal "This is a good day to die to die", original
+ end
+
+ def test_remove!
+ original = "This is a very good day to die"
+ assert_equal "This is a good day to die", original.remove!(" very")
+ assert_equal "This is a good day to die", original
+ assert_equal "This is a good day", original.remove!(" to ", /die/)
+ assert_equal "This is a good day", original
end
def test_constantize
- run_constantize_tests_on do |string|
- string.constantize
- end
+ run_constantize_tests_on(&:constantize)
end
def test_safe_constantize
- run_safe_constantize_tests_on do |string|
- string.safe_constantize
- end
+ run_safe_constantize_tests_on(&:safe_constantize)
end
end
@@ -404,128 +424,134 @@ class StringConversionsTest < ActiveSupport::TestCase
end
def test_partial_string_to_time
- with_env_tz "Europe/Moscow" do
+ with_env_tz "Europe/Moscow" do # use timezone which does not observe DST.
now = Time.now
assert_equal Time.local(now.year, now.month, now.day, 23, 50), "23:50".to_time
assert_equal Time.utc(now.year, now.month, now.day, 23, 50), "23:50".to_time(:utc)
- assert_equal Time.local(now.year, now.month, now.day, 18, 50), "13:50 -0100".to_time
+ assert_equal Time.local(now.year, now.month, now.day, 17, 50), "13:50 -0100".to_time
assert_equal Time.utc(now.year, now.month, now.day, 23, 50), "22:50 -0100".to_time(:utc)
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)
+ Time.stub(:now, Time.local(2012, 1, 1)) do
+ 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
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)
+ Time.stub(:now, Time.local(2012, 7, 1)) do
+ 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
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)
+ Time.stub(:now, Time.local(2012, 1, 1)) do
+ 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
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)
+ Time.stub(:now, Time.local(2012, 7, 1)) do
+ 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
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)
+ Time.stub(:now, Time.local(2012, 1, 1)) do
+ 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
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)
+ Time.stub(:now, Time.local(2012, 7, 1)) do
+ 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
end
@@ -655,16 +681,6 @@ class OutputSafetyTest < ActiveSupport::TestCase
assert_equal other, "&lt;foo&gt;other"
end
- test "Deprecated #prepend! method is still present" do
- other = "other".html_safe
-
- assert_deprecated do
- other.prepend! "<foo>"
- end
-
- assert_equal other, "&lt;foo&gt;other"
- end
-
test "Concatting safe onto unsafe yields unsafe" do
@other_string = "other"
diff --git a/activesupport/test/core_ext/struct_test.rb b/activesupport/test/core_ext/struct_test.rb
deleted file mode 100644
index 0dff7b32d2..0000000000
--- a/activesupport/test/core_ext/struct_test.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require 'abstract_unit'
-require 'active_support/core_ext/struct'
-
-class StructExt < ActiveSupport::TestCase
- def test_to_h
- x = Struct.new(:foo, :bar)
- z = x.new(1, 2)
- assert_equal({ foo: 1, bar: 2 }, z.to_h)
- end
-end
diff --git a/activesupport/test/core_ext/thread_test.rb b/activesupport/test/core_ext/thread_test.rb
deleted file mode 100644
index 6a7c6e0604..0000000000
--- a/activesupport/test/core_ext/thread_test.rb
+++ /dev/null
@@ -1,75 +0,0 @@
-require 'abstract_unit'
-require 'active_support/core_ext/thread'
-
-class ThreadExt < ActiveSupport::TestCase
- def test_main_thread_variable_in_enumerator
- assert_equal Thread.main, Thread.current
-
- Thread.current.thread_variable_set :foo, "bar"
-
- thread, value = Fiber.new {
- Fiber.yield [Thread.current, Thread.current.thread_variable_get(:foo)]
- }.resume
-
- assert_equal Thread.current, thread
- assert_equal Thread.current.thread_variable_get(:foo), value
- end
-
- def test_thread_variable_in_enumerator
- Thread.new {
- Thread.current.thread_variable_set :foo, "bar"
-
- thread, value = Fiber.new {
- Fiber.yield [Thread.current, Thread.current.thread_variable_get(:foo)]
- }.resume
-
- assert_equal Thread.current, thread
- assert_equal Thread.current.thread_variable_get(:foo), value
- }.join
- end
-
- def test_thread_variables
- assert_equal [], Thread.new { Thread.current.thread_variables }.join.value
-
- t = Thread.new {
- Thread.current.thread_variable_set(:foo, "bar")
- Thread.current.thread_variables
- }
- assert_equal [:foo], t.join.value
- end
-
- def test_thread_variable?
- assert_not Thread.new { Thread.current.thread_variable?("foo") }.join.value
- t = Thread.new {
- Thread.current.thread_variable_set("foo", "bar")
- }.join
-
- assert t.thread_variable?("foo")
- assert t.thread_variable?(:foo)
- assert_not t.thread_variable?(:bar)
- end
-
- def test_thread_variable_strings_and_symbols_are_the_same_key
- t = Thread.new {}.join
- t.thread_variable_set("foo", "bar")
- assert_equal "bar", t.thread_variable_get(:foo)
- end
-
- def test_thread_variable_frozen
- t = Thread.new { }.join
- t.freeze
- assert_raises(RuntimeError) do
- t.thread_variable_set(:foo, "bar")
- end
- end
-
- def test_thread_variable_frozen_after_set
- t = Thread.new { }.join
- t.thread_variable_set :foo, "bar"
- t.freeze
- assert_raises(RuntimeError) do
- t.thread_variable_set(:baz, "qux")
- end
- end
-
-end
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index d59775001b..b14c04fba6 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -149,6 +149,9 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal Time.local(2006,3,19,23,59,59,Rational(999999999, 1000)), Time.local(2006,3,19,10,10,10).end_of_day, 'ends DST'
assert_equal Time.local(2006,10,1,23,59,59,Rational(999999999, 1000)), Time.local(2006,10,1,10,10,10).end_of_day, 'start DST'
end
+ with_env_tz 'Asia/Yekaterinburg' do
+ assert_equal Time.local(2015, 2, 8, 23, 59, 59, Rational(999999999, 1000)), Time.new(2015, 2, 8, 8, 0, 0, '+05:00').end_of_day
+ end
end
def test_end_of_hour
@@ -389,6 +392,7 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal Time.local(2005,1,2,11,22,33, 8), Time.local(2005,1,2,11,22,33,44).change(:usec => 8)
assert_equal Time.local(2005,1,2,11,22,33, 8), Time.local(2005,1,2,11,22,33,2).change(:nsec => 8000)
assert_raise(ArgumentError) { Time.local(2005,1,2,11,22,33, 8).change(:usec => 1, :nsec => 1) }
+ assert_nothing_raised(ArgumentError) { Time.new(2015, 5, 9, 10, 00, 00, '+03:00').change(nsec: 999999999) }
end
def test_utc_change
@@ -601,13 +605,15 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_days_in_month_feb_in_common_year_without_year_arg
- Time.stubs(:now).returns(Time.utc(2007))
- assert_equal 28, Time.days_in_month(2)
+ Time.stub(:now, Time.utc(2007)) do
+ assert_equal 28, Time.days_in_month(2)
+ end
end
def test_days_in_month_feb_in_leap_year_without_year_arg
- Time.stubs(:now).returns(Time.utc(2008))
- assert_equal 29, Time.days_in_month(2)
+ Time.stub(:now, Time.utc(2008)) do
+ assert_equal 29, Time.days_in_month(2)
+ end
end
def test_last_month_on_31st
@@ -619,68 +625,74 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_today_with_time_local
- Date.stubs(:current).returns(Date.new(2000, 1, 1))
- assert_equal false, Time.local(1999,12,31,23,59,59).today?
- assert_equal true, Time.local(2000,1,1,0).today?
- assert_equal true, Time.local(2000,1,1,23,59,59).today?
- assert_equal false, Time.local(2000,1,2,0).today?
+ Date.stub(:current, Date.new(2000, 1, 1)) do
+ assert_equal false, Time.local(1999,12,31,23,59,59).today?
+ assert_equal true, Time.local(2000,1,1,0).today?
+ assert_equal true, Time.local(2000,1,1,23,59,59).today?
+ assert_equal false, Time.local(2000,1,2,0).today?
+ end
end
def test_today_with_time_utc
- Date.stubs(:current).returns(Date.new(2000, 1, 1))
- assert_equal false, Time.utc(1999,12,31,23,59,59).today?
- assert_equal true, Time.utc(2000,1,1,0).today?
- assert_equal true, Time.utc(2000,1,1,23,59,59).today?
- assert_equal false, Time.utc(2000,1,2,0).today?
+ Date.stub(:current, Date.new(2000, 1, 1)) do
+ assert_equal false, Time.utc(1999,12,31,23,59,59).today?
+ assert_equal true, Time.utc(2000,1,1,0).today?
+ assert_equal true, Time.utc(2000,1,1,23,59,59).today?
+ assert_equal false, Time.utc(2000,1,2,0).today?
+ end
end
def test_past_with_time_current_as_time_local
with_env_tz 'US/Eastern' do
- Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45))
- assert_equal true, Time.local(2005,2,10,15,30,44).past?
- assert_equal false, Time.local(2005,2,10,15,30,45).past?
- assert_equal false, Time.local(2005,2,10,15,30,46).past?
- assert_equal true, Time.utc(2005,2,10,20,30,44).past?
- assert_equal false, Time.utc(2005,2,10,20,30,45).past?
- assert_equal false, Time.utc(2005,2,10,20,30,46).past?
+ Time.stub(:current, Time.local(2005,2,10,15,30,45)) do
+ assert_equal true, Time.local(2005,2,10,15,30,44).past?
+ assert_equal false, Time.local(2005,2,10,15,30,45).past?
+ assert_equal false, Time.local(2005,2,10,15,30,46).past?
+ assert_equal true, Time.utc(2005,2,10,20,30,44).past?
+ assert_equal false, Time.utc(2005,2,10,20,30,45).past?
+ assert_equal false, Time.utc(2005,2,10,20,30,46).past?
+ end
end
end
def test_past_with_time_current_as_time_with_zone
with_env_tz 'US/Eastern' do
twz = Time.utc(2005,2,10,15,30,45).in_time_zone('Central Time (US & Canada)')
- Time.stubs(:current).returns(twz)
- assert_equal true, Time.local(2005,2,10,10,30,44).past?
- assert_equal false, Time.local(2005,2,10,10,30,45).past?
- assert_equal false, Time.local(2005,2,10,10,30,46).past?
- assert_equal true, Time.utc(2005,2,10,15,30,44).past?
- assert_equal false, Time.utc(2005,2,10,15,30,45).past?
- assert_equal false, Time.utc(2005,2,10,15,30,46).past?
+ Time.stub(:current, twz) do
+ assert_equal true, Time.local(2005,2,10,10,30,44).past?
+ assert_equal false, Time.local(2005,2,10,10,30,45).past?
+ assert_equal false, Time.local(2005,2,10,10,30,46).past?
+ assert_equal true, Time.utc(2005,2,10,15,30,44).past?
+ assert_equal false, Time.utc(2005,2,10,15,30,45).past?
+ assert_equal false, Time.utc(2005,2,10,15,30,46).past?
+ end
end
end
def test_future_with_time_current_as_time_local
with_env_tz 'US/Eastern' do
- Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45))
- assert_equal false, Time.local(2005,2,10,15,30,44).future?
- assert_equal false, Time.local(2005,2,10,15,30,45).future?
- assert_equal true, Time.local(2005,2,10,15,30,46).future?
- assert_equal false, Time.utc(2005,2,10,20,30,44).future?
- assert_equal false, Time.utc(2005,2,10,20,30,45).future?
- assert_equal true, Time.utc(2005,2,10,20,30,46).future?
+ Time.stub(:current, Time.local(2005,2,10,15,30,45)) do
+ assert_equal false, Time.local(2005,2,10,15,30,44).future?
+ assert_equal false, Time.local(2005,2,10,15,30,45).future?
+ assert_equal true, Time.local(2005,2,10,15,30,46).future?
+ assert_equal false, Time.utc(2005,2,10,20,30,44).future?
+ assert_equal false, Time.utc(2005,2,10,20,30,45).future?
+ assert_equal true, Time.utc(2005,2,10,20,30,46).future?
+ end
end
end
def test_future_with_time_current_as_time_with_zone
with_env_tz 'US/Eastern' do
twz = Time.utc(2005,2,10,15,30,45).in_time_zone('Central Time (US & Canada)')
- Time.stubs(:current).returns(twz)
- assert_equal false, Time.local(2005,2,10,10,30,44).future?
- assert_equal false, Time.local(2005,2,10,10,30,45).future?
- assert_equal true, Time.local(2005,2,10,10,30,46).future?
- assert_equal false, Time.utc(2005,2,10,15,30,44).future?
- assert_equal false, Time.utc(2005,2,10,15,30,45).future?
- assert_equal true, Time.utc(2005,2,10,15,30,46).future?
+ Time.stub(:current, twz) do
+ assert_equal false, Time.local(2005,2,10,10,30,44).future?
+ assert_equal false, Time.local(2005,2,10,10,30,45).future?
+ assert_equal true, Time.local(2005,2,10,10,30,46).future?
+ assert_equal false, Time.utc(2005,2,10,15,30,44).future?
+ assert_equal false, Time.utc(2005,2,10,15,30,45).future?
+ assert_equal true, Time.utc(2005,2,10,15,30,46).future?
+ end
end
end
@@ -721,6 +733,13 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal(-1, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone['UTC'] ))
end
+ def test_compare_with_string
+ assert_equal 1, Time.utc(2000) <=> Time.utc(1999, 12, 31, 23, 59, 59, 999).to_s
+ assert_equal 0, Time.utc(2000) <=> Time.utc(2000, 1, 1, 0, 0, 0).to_s
+ assert_equal( -1, Time.utc(2000) <=> Time.utc(2000, 1, 1, 0, 0, 1, 0).to_s)
+ assert_equal nil, Time.utc(2000) <=> 'Invalid as Time'
+ end
+
def test_at_with_datetime
assert_equal Time.utc(2000, 1, 1, 0, 0, 0), Time.at(DateTime.civil(2000, 1, 1, 0, 0, 0))
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index 3000da8da4..ccb7f02331 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -1,6 +1,8 @@
require 'abstract_unit'
require 'active_support/time'
require 'time_zone_test_helpers'
+require 'active_support/core_ext/string/strip'
+require 'yaml'
class TimeWithZoneTest < ActiveSupport::TestCase
include TimeZoneTestHelpers
@@ -79,6 +81,11 @@ class TimeWithZoneTest < ActiveSupport::TestCase
assert_equal '1999-12-31 19:00:00 EST -0500', @twz.strftime('%Y-%m-%d %H:%M:%S %Z %z')
end
+ def test_strftime_with_escaping
+ assert_equal '%Z %z', @twz.strftime('%%Z %%z')
+ assert_equal '%EST %-0500', @twz.strftime('%%%Z %%%z')
+ end
+
def test_inspect
assert_equal 'Fri, 31 Dec 1999 19:00:00 EST -05:00', @twz.inspect
end
@@ -118,11 +125,53 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_to_yaml
- assert_match(/^--- 2000-01-01 00:00:00(\.0+)?\s*Z\n/, @twz.to_yaml)
+ yaml = <<-EOF.strip_heredoc
+ --- !ruby/object:ActiveSupport::TimeWithZone
+ utc: 2000-01-01 00:00:00.000000000 Z
+ zone: !ruby/object:ActiveSupport::TimeZone
+ name: America/New_York
+ time: 1999-12-31 19:00:00.000000000 Z
+ EOF
+
+ assert_equal(yaml, @twz.to_yaml)
end
def test_ruby_to_yaml
- assert_match(/---\s*\n:twz: 2000-01-01 00:00:00(\.0+)?\s*Z\n/, {:twz => @twz}.to_yaml)
+ yaml = <<-EOF.strip_heredoc
+ ---
+ twz: !ruby/object:ActiveSupport::TimeWithZone
+ utc: 2000-01-01 00:00:00.000000000 Z
+ zone: !ruby/object:ActiveSupport::TimeZone
+ name: America/New_York
+ time: 1999-12-31 19:00:00.000000000 Z
+ EOF
+
+ assert_equal(yaml, { 'twz' => @twz }.to_yaml)
+ end
+
+ def test_yaml_load
+ yaml = <<-EOF.strip_heredoc
+ --- !ruby/object:ActiveSupport::TimeWithZone
+ utc: 2000-01-01 00:00:00.000000000 Z
+ zone: !ruby/object:ActiveSupport::TimeZone
+ name: America/New_York
+ time: 1999-12-31 19:00:00.000000000 Z
+ EOF
+
+ assert_equal(@twz, YAML.load(yaml))
+ end
+
+ def test_ruby_yaml_load
+ yaml = <<-EOF.strip_heredoc
+ ---
+ twz: !ruby/object:ActiveSupport::TimeWithZone
+ utc: 2000-01-01 00:00:00.000000000 Z
+ zone: !ruby/object:ActiveSupport::TimeZone
+ name: America/New_York
+ time: 1999-12-31 19:00:00.000000000 Z
+ EOF
+
+ assert_equal({ 'twz' => @twz }, YAML.load(yaml))
end
def test_httpdate
@@ -157,52 +206,61 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_today
- Date.stubs(:current).returns(Date.new(2000, 1, 1))
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(1999,12,31,23,59,59) ).today?
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,0) ).today?
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,23,59,59) ).today?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,2,0) ).today?
+ Date.stub(:current, Date.new(2000, 1, 1)) do
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(1999,12,31,23,59,59) ).today?
+ assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,0) ).today?
+ assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,23,59,59) ).today?
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,2,0) ).today?
+ end
end
def test_past_with_time_current_as_time_local
with_env_tz 'US/Eastern' do
- Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45))
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past?
+ Time.stub(:current, Time.local(2005,2,10,15,30,45)) do
+ assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past?
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past?
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past?
+ end
end
end
def test_past_with_time_current_as_time_with_zone
twz = ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45) )
- Time.stubs(:current).returns(twz)
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past?
+ Time.stub(:current, twz) do
+ assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past?
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past?
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past?
+ end
end
def test_future_with_time_current_as_time_local
with_env_tz 'US/Eastern' do
- Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45))
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future?
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future?
+ Time.stub(:current, Time.local(2005,2,10,15,30,45)) do
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future?
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future?
+ assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future?
+ end
end
end
def test_future_with_time_current_as_time_with_zone
twz = ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45) )
- Time.stubs(:current).returns(twz)
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future?
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future?
+ Time.stub(:current, twz) do
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future?
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future?
+ assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future?
+ end
end
def test_eql?
+ assert_equal true, @twz.eql?(@twz.dup)
assert_equal true, @twz.eql?(Time.utc(2000))
assert_equal true, @twz.eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) )
assert_equal false, @twz.eql?( Time.utc(2000, 1, 1, 0, 0, 1) )
assert_equal false, @twz.eql?( DateTime.civil(1999, 12, 31, 23, 59, 59) )
+
+ other_twz = ActiveSupport::TimeWithZone.new(DateTime.now.utc, @time_zone)
+ assert_equal true, other_twz.eql?(other_twz.dup)
end
def test_hash
@@ -429,22 +487,24 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_method_missing_with_non_time_return_value
- @twz.time.expects(:foo).returns('bar')
+ time = @twz.time
+ def time.foo; 'bar'; end
assert_equal 'bar', @twz.foo
end
def test_date_part_value_methods
twz = ActiveSupport::TimeWithZone.new(Time.utc(1999,12,31,19,18,17,500), @time_zone)
- twz.expects(:method_missing).never
- assert_equal 1999, twz.year
- assert_equal 12, twz.month
- assert_equal 31, twz.day
- assert_equal 14, twz.hour
- assert_equal 18, twz.min
- assert_equal 17, twz.sec
- assert_equal 500, twz.usec
- assert_equal 5, twz.wday
- assert_equal 365, twz.yday
+ assert_not_called(twz, :method_missing) do
+ assert_equal 1999, twz.year
+ assert_equal 12, twz.month
+ assert_equal 31, twz.day
+ assert_equal 14, twz.hour
+ assert_equal 18, twz.min
+ assert_equal 17, twz.sec
+ assert_equal 500, twz.usec
+ assert_equal 5, twz.wday
+ assert_equal 365, twz.yday
+ end
end
def test_usec_returns_0_when_datetime_is_wrapped
@@ -807,6 +867,8 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_no_method_error_has_proper_context
+ rubinius_skip "Error message inconsistency"
+
e = assert_raises(NoMethodError) {
@twz.this_method_does_not_exist
}
@@ -983,19 +1045,21 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < ActiveSupport::TestCase
def test_current_returns_time_now_when_zone_not_set
with_env_tz 'US/Eastern' do
- Time.stubs(:now).returns Time.local(2000)
- assert_equal false, Time.current.is_a?(ActiveSupport::TimeWithZone)
- assert_equal Time.local(2000), Time.current
+ Time.stub(:now, Time.local(2000)) do
+ assert_equal false, Time.current.is_a?(ActiveSupport::TimeWithZone)
+ assert_equal Time.local(2000), Time.current
+ end
end
end
def test_current_returns_time_zone_now_when_zone_set
Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
with_env_tz 'US/Eastern' do
- Time.stubs(:now).returns Time.local(2000)
- assert_equal true, Time.current.is_a?(ActiveSupport::TimeWithZone)
- assert_equal 'Eastern Time (US & Canada)', Time.current.time_zone.name
- assert_equal Time.utc(2000), Time.current.time
+ Time.stub(:now, Time.local(2000)) do
+ assert_equal true, Time.current.is_a?(ActiveSupport::TimeWithZone)
+ assert_equal 'Eastern Time (US & Canada)', Time.current.time_zone.name
+ assert_equal Time.utc(2000), Time.current.time
+ end
end
end
diff --git a/activesupport/test/core_ext/uri_ext_test.rb b/activesupport/test/core_ext/uri_ext_test.rb
index 43a5997ddd..1694fe7e72 100644
--- a/activesupport/test/core_ext/uri_ext_test.rb
+++ b/activesupport/test/core_ext/uri_ext_test.rb
@@ -1,4 +1,3 @@
-# encoding: utf-8
require 'abstract_unit'
require 'uri'
require 'active_support/core_ext/uri'
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index 899bb75eae..757e600646 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -16,15 +16,18 @@ module ModuleWithConstant
end
class DependenciesTest < ActiveSupport::TestCase
- def teardown
- ActiveSupport::Dependencies.clear
+ include DependenciesTestHelpers
+
+ setup do
+ @loaded_features_copy = $LOADED_FEATURES.dup
end
- include DependenciesTestHelpers
+ teardown do
+ ActiveSupport::Dependencies.clear
+ $LOADED_FEATURES.replace(@loaded_features_copy)
+ end
def test_depend_on_path
- skip "LoadError#path does not exist" if RUBY_VERSION < '2.0.0'
-
expected = assert_raises(LoadError) do
Kernel.require 'omgwtfbbq'
end
@@ -53,8 +56,7 @@ class DependenciesTest < ActiveSupport::TestCase
assert_equal 2, ActiveSupport::Dependencies.loaded.size
end
ensure
- Object.send(:remove_const, :ServiceOne) if Object.const_defined?(:ServiceOne)
- Object.send(:remove_const, :ServiceTwo) if Object.const_defined?(:ServiceTwo)
+ remove_constants(:ServiceOne, :ServiceTwo)
end
def test_tracking_identical_loaded_files
@@ -64,11 +66,11 @@ class DependenciesTest < ActiveSupport::TestCase
assert_equal 1, ActiveSupport::Dependencies.loaded.size
end
ensure
- Object.send(:remove_const, :ServiceOne) if Object.const_defined?(:ServiceOne)
+ remove_constants(:ServiceOne)
end
def test_missing_dependency_raises_missing_source_file
- assert_raise(MissingSourceFile) { require_dependency("missing_service") }
+ assert_raise(LoadError) { require_dependency("missing_service") }
end
def test_dependency_which_raises_exception_isnt_added_to_loaded_set
@@ -84,8 +86,8 @@ class DependenciesTest < ActiveSupport::TestCase
assert_equal 'Loading me failed, so do not add to loaded or history.', e.message
assert_equal count + 1, $raises_exception_load_count
- assert !ActiveSupport::Dependencies.loaded.include?(filename)
- assert !ActiveSupport::Dependencies.history.include?(filename)
+ assert_not ActiveSupport::Dependencies.loaded.include?(filename)
+ assert_not ActiveSupport::Dependencies.history.include?(filename)
end
end
end
@@ -93,7 +95,6 @@ class DependenciesTest < ActiveSupport::TestCase
def test_dependency_which_raises_doesnt_blindly_call_blame_file!
with_loading do
filename = 'dependencies/raises_exception_without_blame_file'
-
assert_raises(Exception) { require_dependency filename }
end
end
@@ -101,13 +102,12 @@ class DependenciesTest < ActiveSupport::TestCase
def test_warnings_should_be_enabled_on_first_load
with_loading 'dependencies' do
old_warnings, ActiveSupport::Dependencies.warnings_on_first_load = ActiveSupport::Dependencies.warnings_on_first_load, true
-
filename = "check_warnings"
expanded = File.expand_path("#{File.dirname(__FILE__)}/dependencies/#{filename}")
$check_warnings_load_count = 0
- assert !ActiveSupport::Dependencies.loaded.include?(expanded)
- assert !ActiveSupport::Dependencies.history.include?(expanded)
+ assert_not ActiveSupport::Dependencies.loaded.include?(expanded)
+ assert_not ActiveSupport::Dependencies.history.include?(expanded)
silence_warnings { require_dependency filename }
assert_equal 1, $check_warnings_load_count
@@ -115,7 +115,7 @@ class DependenciesTest < ActiveSupport::TestCase
assert ActiveSupport::Dependencies.loaded.include?(expanded)
ActiveSupport::Dependencies.clear
- assert !ActiveSupport::Dependencies.loaded.include?(expanded)
+ assert_not ActiveSupport::Dependencies.loaded.include?(expanded)
assert ActiveSupport::Dependencies.history.include?(expanded)
silence_warnings { require_dependency filename }
@@ -124,7 +124,7 @@ class DependenciesTest < ActiveSupport::TestCase
assert ActiveSupport::Dependencies.loaded.include?(expanded)
ActiveSupport::Dependencies.clear
- assert !ActiveSupport::Dependencies.loaded.include?(expanded)
+ assert_not ActiveSupport::Dependencies.loaded.include?(expanded)
assert ActiveSupport::Dependencies.history.include?(expanded)
enable_warnings { require_dependency filename }
@@ -157,12 +157,37 @@ class DependenciesTest < ActiveSupport::TestCase
end
end
+ def test_ensures_the_expected_constant_is_defined
+ with_autoloading_fixtures do
+ e = assert_raise(LoadError) { Typo }
+ assert_match %r{Unable to autoload constant Typo, expected .*/test/autoloading_fixtures/typo.rb to define it}, e.message
+ end
+ end
+
+ def test_require_dependency_does_not_assume_any_particular_constant_is_defined
+ with_autoloading_fixtures do
+ require_dependency 'typo'
+ assert_equal 1, TypO
+ end
+ end
+
+ # Regression, see https://github.com/rails/rails/issues/16468.
+ def test_require_dependency_interaction_with_autoloading
+ with_autoloading_fixtures do
+ require_dependency 'typo'
+ assert_equal 1, TypO
+
+ e = assert_raise(LoadError) { Typo }
+ assert_match %r{Unable to autoload constant Typo, expected .*/test/autoloading_fixtures/typo.rb to define it}, e.message
+ end
+ end
+
def test_module_loading
with_autoloading_fixtures do
assert_kind_of Module, A
assert_kind_of Class, A::B
assert_kind_of Class, A::C::D
- assert_kind_of Class, A::C::E::F
+ assert_kind_of Class, A::C::EM::F
end
end
@@ -178,43 +203,49 @@ class DependenciesTest < ActiveSupport::TestCase
def test_directories_manifest_as_modules_unless_const_defined
with_autoloading_fixtures do
assert_kind_of Module, ModuleFolder
- Object.__send__ :remove_const, :ModuleFolder
end
+ ensure
+ remove_constants(:ModuleFolder)
end
def test_module_with_nested_class
with_autoloading_fixtures do
assert_kind_of Class, ModuleFolder::NestedClass
- Object.__send__ :remove_const, :ModuleFolder
end
+ ensure
+ remove_constants(:ModuleFolder)
end
def test_module_with_nested_inline_class
with_autoloading_fixtures do
assert_kind_of Class, ModuleFolder::InlineClass
- Object.__send__ :remove_const, :ModuleFolder
end
+ ensure
+ remove_constants(:ModuleFolder)
end
def test_directories_may_manifest_as_nested_classes
with_autoloading_fixtures do
assert_kind_of Class, ClassFolder
- Object.__send__ :remove_const, :ClassFolder
end
+ ensure
+ remove_constants(:ClassFolder)
end
def test_class_with_nested_class
with_autoloading_fixtures do
assert_kind_of Class, ClassFolder::NestedClass
- Object.__send__ :remove_const, :ClassFolder
end
+ ensure
+ remove_constants(:ClassFolder)
end
def test_class_with_nested_inline_class
with_autoloading_fixtures do
assert_kind_of Class, ClassFolder::InlineClass
- Object.__send__ :remove_const, :ClassFolder
end
+ ensure
+ remove_constants(:ClassFolder)
end
def test_class_with_nested_inline_subclass_of_parent
@@ -222,8 +253,9 @@ class DependenciesTest < ActiveSupport::TestCase
assert_kind_of Class, ClassFolder::ClassFolderSubclass
assert_kind_of Class, ClassFolder
assert_equal 'indeed', ClassFolder::ClassFolderSubclass::ConstantInClassFolder
- Object.__send__ :remove_const, :ClassFolder
end
+ ensure
+ remove_constants(:ClassFolder)
end
def test_nested_class_can_access_sibling
@@ -231,16 +263,15 @@ class DependenciesTest < ActiveSupport::TestCase
sibling = ModuleFolder::NestedClass.class_eval "NestedSibling"
assert defined?(ModuleFolder::NestedSibling)
assert_equal ModuleFolder::NestedSibling, sibling
- Object.__send__ :remove_const, :ModuleFolder
end
+ ensure
+ remove_constants(:ModuleFolder)
end
def test_doesnt_break_normal_require
path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
original_path = $:.dup
- original_features = $".dup
$:.push(path)
-
with_autoloading_fixtures do
# The _ = assignments are to prevent warnings
_ = RequiresConstant
@@ -252,15 +283,13 @@ class DependenciesTest < ActiveSupport::TestCase
assert defined?(LoadedConstant)
end
ensure
- remove_constants(:RequiresConstant, :LoadedConstant, :LoadsConstant)
- $".replace(original_features)
+ remove_constants(:RequiresConstant, :LoadedConstant)
$:.replace(original_path)
end
def test_doesnt_break_normal_require_nested
path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
original_path = $:.dup
- original_features = $".dup
$:.push(path)
with_autoloading_fixtures do
@@ -275,14 +304,12 @@ class DependenciesTest < ActiveSupport::TestCase
end
ensure
remove_constants(:RequiresConstant, :LoadedConstant, :LoadsConstant)
- $".replace(original_features)
$:.replace(original_path)
end
def test_require_returns_true_when_file_not_yet_required
path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
original_path = $:.dup
- original_features = $".dup
$:.push(path)
with_loading do
@@ -290,14 +317,12 @@ class DependenciesTest < ActiveSupport::TestCase
end
ensure
remove_constants(:LoadedConstant)
- $".replace(original_features)
$:.replace(original_path)
end
def test_require_returns_true_when_file_not_yet_required_even_when_no_new_constants_added
path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
original_path = $:.dup
- original_features = $".dup
$:.push(path)
with_loading do
@@ -306,14 +331,12 @@ class DependenciesTest < ActiveSupport::TestCase
end
ensure
remove_constants(:LoadedConstant)
- $".replace(original_features)
$:.replace(original_path)
end
def test_require_returns_false_when_file_already_required
path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
original_path = $:.dup
- original_features = $".dup
$:.push(path)
with_loading do
@@ -322,7 +345,6 @@ class DependenciesTest < ActiveSupport::TestCase
end
ensure
remove_constants(:LoadedConstant)
- $".replace(original_features)
$:.replace(original_path)
end
@@ -330,14 +352,11 @@ class DependenciesTest < ActiveSupport::TestCase
with_loading do
assert_raise(LoadError) { require 'this_file_dont_exist_dude' }
end
- ensure
- remove_constants(:LoadedConstant)
end
def test_load_returns_true_when_file_found
path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
original_path = $:.dup
- original_features = $".dup
$:.push(path)
with_loading do
@@ -346,7 +365,6 @@ class DependenciesTest < ActiveSupport::TestCase
end
ensure
remove_constants(:LoadedConstant)
- $".replace(original_features)
$:.replace(original_path)
end
@@ -354,17 +372,16 @@ class DependenciesTest < ActiveSupport::TestCase
with_loading do
assert_raise(LoadError) { load 'this_file_dont_exist_dude.rb' }
end
- ensure
- remove_constants(:LoadedConstant)
end
def failing_test_access_thru_and_upwards_fails
with_autoloading_fixtures do
- assert ! defined?(ModuleFolder)
+ assert_not defined?(ModuleFolder)
assert_raise(NameError) { ModuleFolder::Object }
assert_raise(NameError) { ModuleFolder::NestedClass::Object }
- Object.__send__ :remove_const, :ModuleFolder
end
+ ensure
+ remove_constants(:ModuleFolder)
end
def test_non_existing_const_raises_name_error_with_fully_qualified_name
@@ -377,6 +394,8 @@ class DependenciesTest < ActiveSupport::TestCase
assert_equal "uninitialized constant A::B::DoesNotExist", e.message
assert_equal :DoesNotExist, e.name
end
+ ensure
+ remove_constants(:A)
end
def test_smart_name_error_strings
@@ -466,9 +485,9 @@ class DependenciesTest < ActiveSupport::TestCase
nil_name = Module.new
def nil_name.name() nil end
assert !ActiveSupport::Dependencies.autoloaded?(nil_name)
-
- Object.class_eval { remove_const :ModuleFolder }
end
+ ensure
+ remove_constants(:ModuleFolder)
end
def test_qualified_name_for
@@ -526,37 +545,45 @@ class DependenciesTest < ActiveSupport::TestCase
assert_kind_of Module, ::ModuleWithCustomConstMissing::A
assert_kind_of String, ::ModuleWithCustomConstMissing::A::B
end
+ ensure
+ remove_constants(:ModuleWithCustomConstMissing)
end
def test_const_missing_in_anonymous_modules_loads_top_level_constants
with_autoloading_fixtures do
# class_eval STRING pushes the class to the nesting of the eval'ed code.
- klass = Class.new.class_eval "E"
- assert_equal E, klass
+ klass = Class.new.class_eval "EM"
+ assert_equal EM, klass
end
+ ensure
+ remove_constants(:EM)
end
def test_const_missing_in_anonymous_modules_raises_if_the_constant_belongs_to_Object
with_autoloading_fixtures do
- require_dependency 'e'
+ require_dependency 'em'
mod = Module.new
- e = assert_raise(NameError) { mod::E }
- assert_equal 'E cannot be autoloaded from an anonymous class or module', e.message
- assert_equal :E, e.name
+ e = assert_raise(NameError) { mod::EM }
+ assert_equal 'EM cannot be autoloaded from an anonymous class or module', e.message
+ assert_equal :EM, e.name
end
+ ensure
+ remove_constants(:EM)
end
def test_removal_from_tree_should_be_detected
with_loading 'dependencies' do
c = ServiceOne
ActiveSupport::Dependencies.clear
- assert ! defined?(ServiceOne)
+ assert_not defined?(ServiceOne)
e = assert_raise ArgumentError do
ActiveSupport::Dependencies.load_missing_constant(c, :FakeMissing)
end
assert_match %r{ServiceOne has been removed from the module tree}i, e.message
end
+ ensure
+ remove_constants(:ServiceOne)
end
def test_references_should_work
@@ -565,42 +592,46 @@ class DependenciesTest < ActiveSupport::TestCase
service_one_first = ServiceOne
assert_equal service_one_first, c.get("ServiceOne")
ActiveSupport::Dependencies.clear
- assert ! defined?(ServiceOne)
-
+ assert_not defined?(ServiceOne)
service_one_second = ServiceOne
assert_not_equal service_one_first, c.get("ServiceOne")
assert_equal service_one_second, c.get("ServiceOne")
end
+ ensure
+ remove_constants(:ServiceOne)
end
def test_constantize_shortcut_for_cached_constant_lookups
with_loading 'dependencies' do
assert_equal ServiceOne, ActiveSupport::Dependencies.constantize("ServiceOne")
end
+ ensure
+ remove_constants(:ServiceOne)
end
def test_nested_load_error_isnt_rescued
with_loading 'dependencies' do
- assert_raise(MissingSourceFile) do
+ assert_raise(LoadError) do
RequiresNonexistent1
end
end
end
def test_autoload_once_paths_do_not_add_to_autoloaded_constants
+ old_path = ActiveSupport::Dependencies.autoload_once_paths
with_autoloading_fixtures do
ActiveSupport::Dependencies.autoload_once_paths = ActiveSupport::Dependencies.autoload_paths.dup
- assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder")
- assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass")
- assert ! ActiveSupport::Dependencies.autoloaded?(ModuleFolder)
+ assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder")
+ assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass")
+ assert_not ActiveSupport::Dependencies.autoloaded?(ModuleFolder)
1 if ModuleFolder::NestedClass # 1 if to avoid warning
- assert ! ActiveSupport::Dependencies.autoloaded?(ModuleFolder::NestedClass)
+ assert_not ActiveSupport::Dependencies.autoloaded?(ModuleFolder::NestedClass)
end
ensure
- Object.class_eval { remove_const :ModuleFolder }
- ActiveSupport::Dependencies.autoload_once_paths = []
+ remove_constants(:ModuleFolder)
+ ActiveSupport::Dependencies.autoload_once_paths = old_path
end
def test_autoload_once_pathnames_do_not_add_to_autoloaded_constants
@@ -609,15 +640,15 @@ class DependenciesTest < ActiveSupport::TestCase
ActiveSupport::Dependencies.autoload_paths = pathnames
ActiveSupport::Dependencies.autoload_once_paths = pathnames
- assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder")
- assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass")
- assert ! ActiveSupport::Dependencies.autoloaded?(ModuleFolder)
+ assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder")
+ assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass")
+ assert_not ActiveSupport::Dependencies.autoloaded?(ModuleFolder)
1 if ModuleFolder::NestedClass # 1 if to avoid warning
- assert ! ActiveSupport::Dependencies.autoloaded?(ModuleFolder::NestedClass)
+ assert_not ActiveSupport::Dependencies.autoloaded?(ModuleFolder::NestedClass)
end
ensure
- Object.class_eval { remove_const :ModuleFolder }
+ remove_constants(:ModuleFolder)
ActiveSupport::Dependencies.autoload_once_paths = []
end
@@ -627,24 +658,25 @@ class DependenciesTest < ActiveSupport::TestCase
assert_equal 10, ApplicationController
assert ActiveSupport::Dependencies.autoloaded?(:ApplicationController)
end
+ ensure
+ remove_constants(:ApplicationController)
end
def test_preexisting_constants_are_not_marked_as_autoloaded
with_autoloading_fixtures do
- require_dependency 'e'
- assert ActiveSupport::Dependencies.autoloaded?(:E)
+ require_dependency 'em'
+ assert ActiveSupport::Dependencies.autoloaded?(:EM)
ActiveSupport::Dependencies.clear
end
- Object.const_set :E, Class.new
+ Object.const_set :EM, Class.new
with_autoloading_fixtures do
- require_dependency 'e'
- assert ! ActiveSupport::Dependencies.autoloaded?(:E), "E shouldn't be marked autoloaded!"
+ require_dependency 'em'
+ assert ! ActiveSupport::Dependencies.autoloaded?(:EM), "EM shouldn't be marked autoloaded!"
ActiveSupport::Dependencies.clear
end
-
ensure
- Object.class_eval { remove_const :E }
+ remove_constants(:EM)
end
def test_constants_in_capitalized_nesting_marked_as_autoloaded
@@ -653,6 +685,8 @@ class DependenciesTest < ActiveSupport::TestCase
assert ActiveSupport::Dependencies.autoloaded?("HTML::SomeClass")
end
+ ensure
+ remove_constants(:HTML)
end
def test_unloadable
@@ -683,18 +717,19 @@ class DependenciesTest < ActiveSupport::TestCase
assert_equal false, M.unloadable
end
ensure
- Object.class_eval { remove_const :M }
+ remove_constants(:M)
end
def test_unloadable_constants_should_receive_callback
- Object.const_set :C, Class.new
+ Object.const_set :C, Class.new { def self.before_remove_const; end }
C.unloadable
- C.expects(:before_remove_const).once
- assert C.respond_to?(:before_remove_const)
- ActiveSupport::Dependencies.clear
- assert !defined?(C)
+ assert_called(C, :before_remove_const, times: 1) do
+ assert C.respond_to?(:before_remove_const)
+ ActiveSupport::Dependencies.clear
+ assert !defined?(C)
+ end
ensure
- Object.class_eval { remove_const :C } if defined?(C)
+ remove_constants(:C)
end
def test_new_contants_in_without_constants
@@ -708,7 +743,7 @@ class DependenciesTest < ActiveSupport::TestCase
}.map(&:to_s)
assert ActiveSupport::Dependencies.constant_watch_stack.all? {|k,v| v.empty? }
ensure
- Object.class_eval { remove_const :Hello }
+ remove_constants(:Hello)
end
def test_new_constants_in_with_nesting
@@ -725,9 +760,7 @@ class DependenciesTest < ActiveSupport::TestCase
assert_equal ["OuterAfter", "OuterBefore"], outer.sort.map(&:to_s)
assert ActiveSupport::Dependencies.constant_watch_stack.all? {|k,v| v.empty? }
ensure
- %w(OuterBefore Inner OuterAfter).each do |name|
- Object.class_eval { remove_const name if const_defined?(name) }
- end
+ remove_constants(:OuterBefore, :Inner, :OuterAfter)
end
def test_new_constants_in_module
@@ -746,7 +779,7 @@ class DependenciesTest < ActiveSupport::TestCase
assert_equal ["M::OuterAfter", "M::OuterBefore"], outer.sort
assert ActiveSupport::Dependencies.constant_watch_stack.all? {|k,v| v.empty? }
ensure
- Object.class_eval { remove_const :M }
+ remove_constants(:M)
end
def test_new_constants_in_module_using_name
@@ -764,7 +797,7 @@ class DependenciesTest < ActiveSupport::TestCase
assert_equal ["M::OuterAfter", "M::OuterBefore"], outer.sort
assert ActiveSupport::Dependencies.constant_watch_stack.all? {|k,v| v.empty? }
ensure
- Object.class_eval { remove_const :M }
+ remove_constants(:M)
end
def test_new_constants_in_with_inherited_constants
@@ -782,26 +815,27 @@ class DependenciesTest < ActiveSupport::TestCase
def test_file_with_multiple_constants_and_require_dependency
with_autoloading_fixtures do
- assert ! defined?(MultipleConstantFile)
- assert ! defined?(SiblingConstant)
+ assert_not defined?(MultipleConstantFile)
+ assert_not defined?(SiblingConstant)
require_dependency 'multiple_constant_file'
assert defined?(MultipleConstantFile)
assert defined?(SiblingConstant)
assert ActiveSupport::Dependencies.autoloaded?(:MultipleConstantFile)
assert ActiveSupport::Dependencies.autoloaded?(:SiblingConstant)
-
ActiveSupport::Dependencies.clear
- assert ! defined?(MultipleConstantFile)
- assert ! defined?(SiblingConstant)
+ assert_not defined?(MultipleConstantFile)
+ assert_not defined?(SiblingConstant)
end
+ ensure
+ remove_constants(:MultipleConstantFile, :SiblingConstant)
end
def test_file_with_multiple_constants_and_auto_loading
with_autoloading_fixtures do
- assert ! defined?(MultipleConstantFile)
- assert ! defined?(SiblingConstant)
+ assert_not defined?(MultipleConstantFile)
+ assert_not defined?(SiblingConstant)
assert_equal 10, MultipleConstantFile
@@ -812,15 +846,17 @@ class DependenciesTest < ActiveSupport::TestCase
ActiveSupport::Dependencies.clear
- assert ! defined?(MultipleConstantFile)
- assert ! defined?(SiblingConstant)
+ assert_not defined?(MultipleConstantFile)
+ assert_not defined?(SiblingConstant)
end
+ ensure
+ remove_constants(:MultipleConstantFile, :SiblingConstant)
end
def test_nested_file_with_multiple_constants_and_require_dependency
with_autoloading_fixtures do
- assert ! defined?(ClassFolder::NestedClass)
- assert ! defined?(ClassFolder::SiblingClass)
+ assert_not defined?(ClassFolder::NestedClass)
+ assert_not defined?(ClassFolder::SiblingClass)
require_dependency 'class_folder/nested_class'
@@ -831,15 +867,17 @@ class DependenciesTest < ActiveSupport::TestCase
ActiveSupport::Dependencies.clear
- assert ! defined?(ClassFolder::NestedClass)
- assert ! defined?(ClassFolder::SiblingClass)
+ assert_not defined?(ClassFolder::NestedClass)
+ assert_not defined?(ClassFolder::SiblingClass)
end
+ ensure
+ remove_constants(:ClassFolder)
end
def test_nested_file_with_multiple_constants_and_auto_loading
with_autoloading_fixtures do
- assert ! defined?(ClassFolder::NestedClass)
- assert ! defined?(ClassFolder::SiblingClass)
+ assert_not defined?(ClassFolder::NestedClass)
+ assert_not defined?(ClassFolder::SiblingClass)
assert_kind_of Class, ClassFolder::NestedClass
@@ -850,9 +888,11 @@ class DependenciesTest < ActiveSupport::TestCase
ActiveSupport::Dependencies.clear
- assert ! defined?(ClassFolder::NestedClass)
- assert ! defined?(ClassFolder::SiblingClass)
+ assert_not defined?(ClassFolder::NestedClass)
+ assert_not defined?(ClassFolder::SiblingClass)
end
+ ensure
+ remove_constants(:ClassFolder)
end
def test_autoload_doesnt_shadow_no_method_error_with_relative_constant
@@ -863,9 +903,8 @@ class DependenciesTest < ActiveSupport::TestCase
assert !defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it should have failed!"
end
end
-
ensure
- Object.class_eval { remove_const :RaisesNoMethodError if const_defined?(:RaisesNoMethodError) }
+ remove_constants(:RaisesNoMethodError)
end
def test_autoload_doesnt_shadow_no_method_error_with_absolute_constant
@@ -876,9 +915,8 @@ class DependenciesTest < ActiveSupport::TestCase
assert !defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it should have failed!"
end
end
-
ensure
- Object.class_eval { remove_const :RaisesNoMethodError if const_defined?(:RaisesNoMethodError) }
+ remove_constants(:RaisesNoMethodError)
end
def test_autoload_doesnt_shadow_error_when_mechanism_not_set_to_load
@@ -894,7 +932,6 @@ class DependenciesTest < ActiveSupport::TestCase
def test_autoload_doesnt_shadow_name_error
with_autoloading_fixtures do
- Object.send(:remove_const, :RaisesNameError) if defined?(::RaisesNameError)
2.times do
e = assert_raise NameError do
::RaisesNameError::FooBarBaz.object_id
@@ -909,19 +946,20 @@ class DependenciesTest < ActiveSupport::TestCase
assert !defined?(::RaisesNameError), "::RaisesNameError is defined but it should have failed!"
end
end
-
ensure
- Object.class_eval { remove_const :RaisesNoMethodError if const_defined?(:RaisesNoMethodError) }
+ remove_constants(:RaisesNameError)
end
def test_remove_constant_handles_double_colon_at_start
Object.const_set 'DeleteMe', Module.new
DeleteMe.const_set 'OrMe', Module.new
ActiveSupport::Dependencies.remove_constant "::DeleteMe::OrMe"
- assert ! defined?(DeleteMe::OrMe)
+ assert_not defined?(DeleteMe::OrMe)
assert defined?(DeleteMe)
ActiveSupport::Dependencies.remove_constant "::DeleteMe"
- assert ! defined?(DeleteMe)
+ assert_not defined?(DeleteMe)
+ ensure
+ remove_constants(:DeleteMe)
end
def test_remove_constant_does_not_trigger_loading_autoloads
@@ -931,7 +969,9 @@ class DependenciesTest < ActiveSupport::TestCase
end
assert_nil ActiveSupport::Dependencies.remove_constant(constant), "Kernel#autoload has been triggered by remove_constant"
- assert !defined?(ShouldNotBeAutoloaded)
+ assert_not defined?(ShouldNotBeAutoloaded)
+ ensure
+ remove_constants(constant)
end
def test_remove_constant_does_not_autoload_already_removed_parents_as_a_side_effect
@@ -940,11 +980,14 @@ class DependenciesTest < ActiveSupport::TestCase
_ = ::A::B # assignment to silence parse-time warning "possibly useless use of :: in void context"
ActiveSupport::Dependencies.remove_constant('A')
ActiveSupport::Dependencies.remove_constant('A::B')
- assert !defined?(A)
+ assert_not defined?(A)
end
+ ensure
+ remove_constants(:A)
end
def test_load_once_constants_should_not_be_unloaded
+ old_path = ActiveSupport::Dependencies.autoload_once_paths
with_autoloading_fixtures do
ActiveSupport::Dependencies.autoload_once_paths = ActiveSupport::Dependencies.autoload_paths
_ = ::A # assignment to silence parse-time warning "possibly useless use of :: in void context"
@@ -953,8 +996,8 @@ class DependenciesTest < ActiveSupport::TestCase
assert defined?(A)
end
ensure
- ActiveSupport::Dependencies.autoload_once_paths = []
- Object.class_eval { remove_const :A if const_defined?(:A) }
+ ActiveSupport::Dependencies.autoload_once_paths = old_path
+ remove_constants(:A)
end
def test_access_unloaded_constants_for_reload
@@ -966,29 +1009,45 @@ class DependenciesTest < ActiveSupport::TestCase
A::B # Make sure no circular dependency error
end
+ ensure
+ remove_constants(:A)
end
def test_autoload_once_paths_should_behave_when_recursively_loading
+ old_path = ActiveSupport::Dependencies.autoload_once_paths
with_loading 'dependencies', 'autoloading_fixtures' do
ActiveSupport::Dependencies.autoload_once_paths = [ActiveSupport::Dependencies.autoload_paths.last]
- assert !defined?(CrossSiteDependency)
+ assert_not defined?(CrossSiteDependency)
assert_nothing_raised { CrossSiteDepender.nil? }
assert defined?(CrossSiteDependency)
- assert !ActiveSupport::Dependencies.autoloaded?(CrossSiteDependency),
+ assert_not ActiveSupport::Dependencies.autoloaded?(CrossSiteDependency),
"CrossSiteDependency shouldn't be marked as autoloaded!"
ActiveSupport::Dependencies.clear
assert defined?(CrossSiteDependency),
"CrossSiteDependency shouldn't have been unloaded!"
end
ensure
- ActiveSupport::Dependencies.autoload_once_paths = []
+ ActiveSupport::Dependencies.autoload_once_paths = old_path
+ remove_constants(:CrossSiteDependency)
end
def test_hook_called_multiple_times
assert_nothing_raised { ActiveSupport::Dependencies.hook! }
end
+ def test_load_and_require_stay_private
+ assert Object.private_methods.include?(:load)
+ assert Object.private_methods.include?(:require)
+
+ ActiveSupport::Dependencies.unhook!
+
+ assert Object.private_methods.include?(:load)
+ assert Object.private_methods.include?(:require)
+ ensure
+ ActiveSupport::Dependencies.hook!
+ end
+
def test_unhook
ActiveSupport::Dependencies.unhook!
assert !Module.new.respond_to?(:const_missing_without_dependencies)
diff --git a/activesupport/test/dependencies_test_helpers.rb b/activesupport/test/dependencies_test_helpers.rb
index 9268512a97..e4d5197112 100644
--- a/activesupport/test/dependencies_test_helpers.rb
+++ b/activesupport/test/dependencies_test_helpers.rb
@@ -13,6 +13,7 @@ module DependenciesTestHelpers
ActiveSupport::Dependencies.autoload_paths = prior_autoload_paths
ActiveSupport::Dependencies.mechanism = old_mechanism
ActiveSupport::Dependencies.explicitly_unloadable_constants = []
+ ActiveSupport::Dependencies.clear
end
def with_autoloading_fixtures(&block)
diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb
index 7aff56cbad..7e8844b301 100644
--- a/activesupport/test/deprecation_test.rb
+++ b/activesupport/test/deprecation_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/testing/stream'
class Deprecatee
def initialize
@@ -36,6 +37,8 @@ end
class DeprecationTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Stream
+
def setup
# Track the last warning.
@old_behavior = ActiveSupport::Deprecation.behavior
@@ -253,17 +256,17 @@ class DeprecationTest < ActiveSupport::TestCase
end
def test_deprecate_with_custom_deprecator
- custom_deprecator = mock('Deprecator') do
- expects(:deprecation_warning)
- end
+ custom_deprecator = Struct.new(:deprecation_warning).new
- klass = Class.new do
- def method
+ assert_called_with(custom_deprecator, :deprecation_warning, [:method, nil]) do
+ klass = Class.new do
+ def method
+ end
+ deprecate :method, deprecator: custom_deprecator
end
- deprecate :method, deprecator: custom_deprecator
- end
- klass.new.method
+ klass.new.method
+ end
end
def test_deprecated_constant_with_deprecator_given
@@ -356,20 +359,4 @@ class DeprecationTest < ActiveSupport::TestCase
deprecator
end
- def capture(stream)
- stream = stream.to_s
- captured_stream = Tempfile.new(stream)
- stream_io = eval("$#{stream}")
- origin_stream = stream_io.dup
- stream_io.reopen(captured_stream)
-
- yield
-
- stream_io.rewind
- return captured_stream.read
- ensure
- captured_stream.close
- captured_stream.unlink
- stream_io.reopen(origin_stream)
- end
end
diff --git a/activesupport/test/file_fixtures/sample.txt b/activesupport/test/file_fixtures/sample.txt
new file mode 100644
index 0000000000..0fa80e7383
--- /dev/null
+++ b/activesupport/test/file_fixtures/sample.txt
@@ -0,0 +1 @@
+sample file fixture
diff --git a/activesupport/test/hash_with_indifferent_access_test.rb b/activesupport/test/hash_with_indifferent_access_test.rb
deleted file mode 100644
index 843994147b..0000000000
--- a/activesupport/test/hash_with_indifferent_access_test.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require 'abstract_unit'
-require 'active_support/hash_with_indifferent_access'
-
-class HashWithIndifferentAccessTest < ActiveSupport::TestCase
- def test_reverse_merge
- hash = HashWithIndifferentAccess.new key: :old_value
- hash.reverse_merge! key: :new_value
- assert_equal :old_value, hash[:key]
- end
-end \ No newline at end of file
diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb
index be68bb2e2e..a0764f6d6b 100644
--- a/activesupport/test/inflector_test.rb
+++ b/activesupport/test/inflector_test.rb
@@ -8,6 +8,20 @@ class InflectorTest < ActiveSupport::TestCase
include InflectorTestCases
include ConstantizeTestCases
+ def setup
+ # Dups the singleton before each test, restoring the original inflections later.
+ #
+ # This helper is implemented by setting @__instance__ because in some tests
+ # there are module functions that access ActiveSupport::Inflector.inflections,
+ # so we need to replace the singleton itself.
+ @original_inflections = ActiveSupport::Inflector::Inflections.instance_variable_get(:@__instance__)[:en]
+ ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: @original_inflections.dup)
+ end
+
+ def teardown
+ ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: @original_inflections)
+ end
+
def test_pluralize_plurals
assert_equal "plurals", ActiveSupport::Inflector.pluralize("plurals")
assert_equal "Plurals", ActiveSupport::Inflector.pluralize("Plurals")
@@ -26,20 +40,18 @@ class InflectorTest < ActiveSupport::TestCase
end
def test_uncountable_word_is_not_greedy
- with_dup do
- uncountable_word = "ors"
- countable_word = "sponsor"
+ uncountable_word = "ors"
+ countable_word = "sponsor"
- ActiveSupport::Inflector.inflections.uncountable << uncountable_word
+ ActiveSupport::Inflector.inflections.uncountable << uncountable_word
- assert_equal uncountable_word, ActiveSupport::Inflector.singularize(uncountable_word)
- assert_equal uncountable_word, ActiveSupport::Inflector.pluralize(uncountable_word)
- assert_equal ActiveSupport::Inflector.pluralize(uncountable_word), ActiveSupport::Inflector.singularize(uncountable_word)
+ assert_equal uncountable_word, ActiveSupport::Inflector.singularize(uncountable_word)
+ assert_equal uncountable_word, ActiveSupport::Inflector.pluralize(uncountable_word)
+ assert_equal ActiveSupport::Inflector.pluralize(uncountable_word), ActiveSupport::Inflector.singularize(uncountable_word)
- assert_equal "sponsor", ActiveSupport::Inflector.singularize(countable_word)
- assert_equal "sponsors", ActiveSupport::Inflector.pluralize(countable_word)
- assert_equal "sponsor", ActiveSupport::Inflector.singularize(ActiveSupport::Inflector.pluralize(countable_word))
- end
+ assert_equal "sponsor", ActiveSupport::Inflector.singularize(countable_word)
+ assert_equal "sponsors", ActiveSupport::Inflector.pluralize(countable_word)
+ assert_equal "sponsor", ActiveSupport::Inflector.singularize(ActiveSupport::Inflector.pluralize(countable_word))
end
SingularToPlural.each do |singular, plural|
@@ -70,11 +82,9 @@ class InflectorTest < ActiveSupport::TestCase
def test_overwrite_previous_inflectors
- with_dup do
- assert_equal("series", ActiveSupport::Inflector.singularize("series"))
- ActiveSupport::Inflector.inflections.singular "series", "serie"
- assert_equal("serie", ActiveSupport::Inflector.singularize("series"))
- end
+ assert_equal("series", ActiveSupport::Inflector.singularize("series"))
+ ActiveSupport::Inflector.inflections.singular "series", "serie"
+ assert_equal("serie", ActiveSupport::Inflector.singularize("series"))
end
MixtureToTitleCase.each_with_index do |(before, titleized), index|
@@ -367,10 +377,8 @@ class InflectorTest < ActiveSupport::TestCase
%w{plurals singulars uncountables humans}.each do |inflection_type|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def test_clear_#{inflection_type}
- with_dup do
- ActiveSupport::Inflector.inflections.clear :#{inflection_type}
- assert ActiveSupport::Inflector.inflections.#{inflection_type}.empty?, \"#{inflection_type} inflections should be empty after clear :#{inflection_type}\"
- end
+ ActiveSupport::Inflector.inflections.clear :#{inflection_type}
+ assert ActiveSupport::Inflector.inflections.#{inflection_type}.empty?, \"#{inflection_type} inflections should be empty after clear :#{inflection_type}\"
end
RUBY
end
@@ -405,73 +413,63 @@ class InflectorTest < ActiveSupport::TestCase
end
def test_clear_all
- with_dup do
- ActiveSupport::Inflector.inflections do |inflect|
- # ensure any data is present
- inflect.plural(/(quiz)$/i, '\1zes')
- inflect.singular(/(database)s$/i, '\1')
- inflect.uncountable('series')
- inflect.human("col_rpted_bugs", "Reported bugs")
-
- inflect.clear :all
-
- assert inflect.plurals.empty?
- assert inflect.singulars.empty?
- assert inflect.uncountables.empty?
- assert inflect.humans.empty?
- end
+ ActiveSupport::Inflector.inflections do |inflect|
+ # ensure any data is present
+ inflect.plural(/(quiz)$/i, '\1zes')
+ inflect.singular(/(database)s$/i, '\1')
+ inflect.uncountable('series')
+ inflect.human("col_rpted_bugs", "Reported bugs")
+
+ inflect.clear :all
+
+ assert inflect.plurals.empty?
+ assert inflect.singulars.empty?
+ assert inflect.uncountables.empty?
+ assert inflect.humans.empty?
end
end
def test_clear_with_default
- with_dup do
- ActiveSupport::Inflector.inflections do |inflect|
- # ensure any data is present
- inflect.plural(/(quiz)$/i, '\1zes')
- inflect.singular(/(database)s$/i, '\1')
- inflect.uncountable('series')
- inflect.human("col_rpted_bugs", "Reported bugs")
-
- inflect.clear
-
- assert inflect.plurals.empty?
- assert inflect.singulars.empty?
- assert inflect.uncountables.empty?
- assert inflect.humans.empty?
- end
+ ActiveSupport::Inflector.inflections do |inflect|
+ # ensure any data is present
+ inflect.plural(/(quiz)$/i, '\1zes')
+ inflect.singular(/(database)s$/i, '\1')
+ inflect.uncountable('series')
+ inflect.human("col_rpted_bugs", "Reported bugs")
+
+ inflect.clear
+
+ assert inflect.plurals.empty?
+ assert inflect.singulars.empty?
+ assert inflect.uncountables.empty?
+ assert inflect.humans.empty?
end
end
Irregularities.each do |singular, plural|
define_method("test_irregularity_between_#{singular}_and_#{plural}") do
- with_dup do
- ActiveSupport::Inflector.inflections do |inflect|
- inflect.irregular(singular, plural)
- assert_equal singular, ActiveSupport::Inflector.singularize(plural)
- assert_equal plural, ActiveSupport::Inflector.pluralize(singular)
- end
+ ActiveSupport::Inflector.inflections do |inflect|
+ inflect.irregular(singular, plural)
+ assert_equal singular, ActiveSupport::Inflector.singularize(plural)
+ assert_equal plural, ActiveSupport::Inflector.pluralize(singular)
end
end
end
Irregularities.each do |singular, plural|
define_method("test_pluralize_of_irregularity_#{plural}_should_be_the_same") do
- with_dup do
- ActiveSupport::Inflector.inflections do |inflect|
- inflect.irregular(singular, plural)
- assert_equal plural, ActiveSupport::Inflector.pluralize(plural)
- end
+ ActiveSupport::Inflector.inflections do |inflect|
+ inflect.irregular(singular, plural)
+ assert_equal plural, ActiveSupport::Inflector.pluralize(plural)
end
end
end
Irregularities.each do |singular, plural|
define_method("test_singularize_of_irregularity_#{singular}_should_be_the_same") do
- with_dup do
- ActiveSupport::Inflector.inflections do |inflect|
- inflect.irregular(singular, plural)
- assert_equal singular, ActiveSupport::Inflector.singularize(singular)
- end
+ ActiveSupport::Inflector.inflections do |inflect|
+ inflect.irregular(singular, plural)
+ assert_equal singular, ActiveSupport::Inflector.singularize(singular)
end
end
end
@@ -503,12 +501,10 @@ class InflectorTest < ActiveSupport::TestCase
%w(plurals singulars uncountables humans acronyms).each do |scope|
define_method("test_clear_inflections_with_#{scope}") do
- with_dup do
- # clear the inflections
- ActiveSupport::Inflector.inflections do |inflect|
- inflect.clear(scope)
- assert_equal [], inflect.send(scope)
- end
+ # clear the inflections
+ ActiveSupport::Inflector.inflections do |inflect|
+ inflect.clear(scope)
+ assert_equal [], inflect.send(scope)
end
end
end
@@ -520,18 +516,4 @@ class InflectorTest < ActiveSupport::TestCase
assert_equal "HTTP", ActiveSupport::Inflector.pluralize("HTTP")
end
-
- # Dups the singleton and yields, restoring the original inflections later.
- # Use this in tests what modify the state of the singleton.
- #
- # This helper is implemented by setting @__instance__ because in some tests
- # there are module functions that access ActiveSupport::Inflector.inflections,
- # so we need to replace the singleton itself.
- def with_dup
- original = ActiveSupport::Inflector::Inflections.instance_variable_get(:@__instance__)[:en]
- ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: original.dup)
- yield
- ensure
- ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: original)
- end
end
diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb
index 3770f00fe3..18a8b92eb9 100644
--- a/activesupport/test/inflector_test_cases.rb
+++ b/activesupport/test/inflector_test_cases.rb
@@ -1,4 +1,3 @@
-# encoding: utf-8
module InflectorTestCases
SingularToPlural = {
diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb
index 80bf255080..f2fc456f4b 100644
--- a/activesupport/test/json/decoding_test.rb
+++ b/activesupport/test/json/decoding_test.rb
@@ -1,4 +1,3 @@
-# encoding: utf-8
require 'abstract_unit'
require 'active_support/json'
require 'active_support/time'
diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index ad358ad21d..9f4b62fd8b 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -1,126 +1,27 @@
-# encoding: utf-8
require 'securerandom'
require 'abstract_unit'
require 'active_support/core_ext/string/inflections'
require 'active_support/json'
require 'active_support/time'
require 'time_zone_test_helpers'
+require 'json/encoding_test_cases'
class TestJSONEncoding < ActiveSupport::TestCase
include TimeZoneTestHelpers
- class Foo
- def initialize(a, b)
- @a, @b = a, b
- end
- end
-
- class Hashlike
- def to_hash
- { :foo => "hello", :bar => "world" }
- end
- end
-
- class Custom
- def initialize(serialized)
- @serialized = serialized
- end
-
- def as_json(options = nil)
- @serialized
- end
- end
-
- class CustomWithOptions
- attr_accessor :foo, :bar
-
- def as_json(options={})
- options[:only] = %w(foo bar)
- super(options)
- end
- end
-
- class OptionsTest
- def as_json(options = :default)
- options
- end
- end
-
- class HashWithAsJson < Hash
- attr_accessor :as_json_called
-
- def initialize(*)
- super
- end
-
- def as_json(options={})
- @as_json_called = true
- super
- end
- end
-
- TrueTests = [[ true, %(true) ]]
- FalseTests = [[ false, %(false) ]]
- NilTests = [[ nil, %(null) ]]
- NumericTests = [[ 1, %(1) ],
- [ 2.5, %(2.5) ],
- [ 0.0/0.0, %(null) ],
- [ 1.0/0.0, %(null) ],
- [ -1.0/0.0, %(null) ],
- [ BigDecimal('0.0')/BigDecimal('0.0'), %(null) ],
- [ BigDecimal('2.5'), %("#{BigDecimal('2.5').to_s}") ]]
-
- StringTests = [[ 'this is the <string>', %("this is the \\u003cstring\\u003e")],
- [ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ],
- [ 'http://test.host/posts/1', %("http://test.host/posts/1")],
- [ "Control characters: \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\u2028\u2029",
- %("Control characters: \\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f\\u2028\\u2029") ]]
-
- ArrayTests = [[ ['a', 'b', 'c'], %([\"a\",\"b\",\"c\"]) ],
- [ [1, 'a', :b, nil, false], %([1,\"a\",\"b\",null,false]) ]]
-
- RangeTests = [[ 1..2, %("1..2")],
- [ 1...2, %("1...2")],
- [ 1.5..2.5, %("1.5..2.5")]]
-
- SymbolTests = [[ :a, %("a") ],
- [ :this, %("this") ],
- [ :"a b", %("a b") ]]
-
- ObjectTests = [[ Foo.new(1, 2), %({\"a\":1,\"b\":2}) ]]
- HashlikeTests = [[ Hashlike.new, %({\"bar\":\"world\",\"foo\":\"hello\"}) ]]
- CustomTests = [[ Custom.new("custom"), '"custom"' ],
- [ Custom.new(nil), 'null' ],
- [ Custom.new(:a), '"a"' ],
- [ Custom.new([ :foo, "bar" ]), '["foo","bar"]' ],
- [ Custom.new({ :foo => "hello", :bar => "world" }), '{"bar":"world","foo":"hello"}' ],
- [ Custom.new(Hashlike.new), '{"bar":"world","foo":"hello"}' ],
- [ Custom.new(Custom.new(Custom.new(:a))), '"a"' ]]
-
- RegexpTests = [[ /^a/, '"(?-mix:^a)"' ], [/^\w{1,2}[a-z]+/ix, '"(?ix-m:^\\\\w{1,2}[a-z]+)"']]
-
- DateTests = [[ Date.new(2005,2,1), %("2005/02/01") ]]
- TimeTests = [[ Time.utc(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]]
- DateTimeTests = [[ DateTime.civil(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]]
-
- StandardDateTests = [[ Date.new(2005,2,1), %("2005-02-01") ]]
- StandardTimeTests = [[ Time.utc(2005,2,1,15,15,10), %("2005-02-01T15:15:10.000Z") ]]
- StandardDateTimeTests = [[ DateTime.civil(2005,2,1,15,15,10), %("2005-02-01T15:15:10.000+00:00") ]]
- StandardStringTests = [[ 'this is the <string>', %("this is the <string>")]]
-
def sorted_json(json)
return json unless json =~ /^\{.*\}$/
'{' + json[1..-2].split(',').sort.join(',') + '}'
end
- constants.grep(/Tests$/).each do |class_tests|
+ JSONTest::EncodingTestCases.constants.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|
+ JSONTest::EncodingTestCases.const_get(class_tests).each do |pair|
assert_equal pair.last, sorted_json(ActiveSupport::JSON.encode(pair.first))
end
ensure
@@ -131,6 +32,8 @@ class TestJSONEncoding < ActiveSupport::TestCase
end
def test_process_status
+ rubinius_skip "https://github.com/rubinius/rubinius/issues/3334"
+
# There doesn't seem to be a good way to get a handle on a Process::Status object without actually
# creating a child process, hence this to populate $?
system("not_a_real_program_#{SecureRandom.hex}")
@@ -146,6 +49,13 @@ class TestJSONEncoding < ActiveSupport::TestCase
assert_equal %({\"a\":\"b\",\"c\":\"d\"}), sorted_json(ActiveSupport::JSON.encode(:a => :b, :c => :d))
end
+ def test_hash_keys_encoding
+ ActiveSupport.escape_html_entities_in_json = true
+ assert_equal "{\"\\u003c\\u003e\":\"\\u003c\\u003e\"}", ActiveSupport::JSON.encode("<>" => "<>")
+ ensure
+ ActiveSupport.escape_html_entities_in_json = false
+ end
+
def test_utf8_string_encoded_properly
result = ActiveSupport::JSON.encode('€2.99')
assert_equal '"€2.99"', result
@@ -176,46 +86,6 @@ class TestJSONEncoding < ActiveSupport::TestCase
assert_equal "𐒑", decoded_hash['string']
end
- def test_reading_encode_big_decimal_as_string_option
- assert_deprecated do
- assert ActiveSupport.encode_big_decimal_as_string
- end
- end
-
- def test_setting_deprecated_encode_big_decimal_as_string_option
- assert_raise(NotImplementedError) do
- ActiveSupport.encode_big_decimal_as_string = true
- end
-
- assert_raise(NotImplementedError) do
- ActiveSupport.encode_big_decimal_as_string = false
- end
- end
-
- def test_exception_raised_when_encoding_circular_reference_in_array
- a = [1]
- a << a
- assert_deprecated do
- assert_raise(ActiveSupport::JSON::Encoding::CircularReferenceError) { ActiveSupport::JSON.encode(a) }
- end
- end
-
- def test_exception_raised_when_encoding_circular_reference_in_hash
- a = { :name => 'foo' }
- a[:next] = a
- assert_deprecated do
- assert_raise(ActiveSupport::JSON::Encoding::CircularReferenceError) { ActiveSupport::JSON.encode(a) }
- end
- end
-
- def test_exception_raised_when_encoding_circular_reference_in_hash_inside_array
- a = { :name => 'foo', :sub => [] }
- a[:sub] << a
- assert_deprecated do
- assert_raise(ActiveSupport::JSON::Encoding::CircularReferenceError) { ActiveSupport::JSON.encode(a) }
- end
- end
-
def test_hash_key_identifiers_are_always_quoted
values = {0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B"}
assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(ActiveSupport::JSON.encode(values))
@@ -256,7 +126,7 @@ class TestJSONEncoding < ActiveSupport::TestCase
end
def test_hash_like_with_options
- h = Hashlike.new
+ h = JSONTest::Hashlike.new
json = h.to_json :only => [:foo]
assert_equal({"foo"=>"hello"}, JSON.parse(json))
@@ -377,6 +247,15 @@ class TestJSONEncoding < ActiveSupport::TestCase
assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json)
end
+ class CustomWithOptions
+ attr_accessor :foo, :bar
+
+ def as_json(options={})
+ options[:only] = %w(foo bar)
+ super(options)
+ end
+ end
+
def test_hash_to_json_should_not_keep_options_around
f = CustomWithOptions.new
f.foo = "hello"
@@ -397,6 +276,12 @@ class TestJSONEncoding < ActiveSupport::TestCase
{"foo"=>"other_foo","test"=>"other_test"}], ActiveSupport::JSON.decode(array.to_json))
end
+ class OptionsTest
+ def as_json(options = :default)
+ options
+ end
+ end
+
def test_hash_as_json_without_options
json = { foo: OptionsTest.new }.as_json
assert_equal({"foo" => :default}, json)
@@ -444,6 +329,19 @@ class TestJSONEncoding < ActiveSupport::TestCase
assert_equal false, false.as_json
end
+ class HashWithAsJson < Hash
+ attr_accessor :as_json_called
+
+ def initialize(*)
+ super
+ end
+
+ def as_json(options={})
+ @as_json_called = true
+ super
+ end
+ end
+
def test_json_gem_dump_by_passing_active_support_encoder
h = HashWithAsJson.new
h[:foo] = "hello"
diff --git a/activesupport/test/json/encoding_test_cases.rb b/activesupport/test/json/encoding_test_cases.rb
new file mode 100644
index 0000000000..0159ba8606
--- /dev/null
+++ b/activesupport/test/json/encoding_test_cases.rb
@@ -0,0 +1,88 @@
+require 'bigdecimal'
+
+module JSONTest
+ class Foo
+ def initialize(a, b)
+ @a, @b = a, b
+ end
+ end
+
+ class Hashlike
+ def to_hash
+ { :foo => "hello", :bar => "world" }
+ end
+ end
+
+ class Custom
+ def initialize(serialized)
+ @serialized = serialized
+ end
+
+ def as_json(options = nil)
+ @serialized
+ end
+ end
+
+ class MyStruct < Struct.new(:name, :value)
+ def initialize(*)
+ @unused = "unused instance variable"
+ super
+ end
+ end
+
+ module EncodingTestCases
+ TrueTests = [[ true, %(true) ]]
+ FalseTests = [[ false, %(false) ]]
+ NilTests = [[ nil, %(null) ]]
+ NumericTests = [[ 1, %(1) ],
+ [ 2.5, %(2.5) ],
+ [ 0.0/0.0, %(null) ],
+ [ 1.0/0.0, %(null) ],
+ [ -1.0/0.0, %(null) ],
+ [ BigDecimal('0.0')/BigDecimal('0.0'), %(null) ],
+ [ BigDecimal('2.5'), %("#{BigDecimal('2.5')}") ]]
+
+ StringTests = [[ 'this is the <string>', %("this is the \\u003cstring\\u003e")],
+ [ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ],
+ [ 'http://test.host/posts/1', %("http://test.host/posts/1")],
+ [ "Control characters: \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\u2028\u2029",
+ %("Control characters: \\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f\\u2028\\u2029") ]]
+
+ ArrayTests = [[ ['a', 'b', 'c'], %([\"a\",\"b\",\"c\"]) ],
+ [ [1, 'a', :b, nil, false], %([1,\"a\",\"b\",null,false]) ]]
+
+ HashTests = [[ {foo: "bar"}, %({\"foo\":\"bar\"}) ],
+ [ {1 => 1, 2 => 'a', 3 => :b, 4 => nil, 5 => false}, %({\"1\":1,\"2\":\"a\",\"3\":\"b\",\"4\":null,\"5\":false}) ]]
+
+ RangeTests = [[ 1..2, %("1..2")],
+ [ 1...2, %("1...2")],
+ [ 1.5..2.5, %("1.5..2.5")]]
+
+ SymbolTests = [[ :a, %("a") ],
+ [ :this, %("this") ],
+ [ :"a b", %("a b") ]]
+
+ ObjectTests = [[ Foo.new(1, 2), %({\"a\":1,\"b\":2}) ]]
+ HashlikeTests = [[ Hashlike.new, %({\"bar\":\"world\",\"foo\":\"hello\"}) ]]
+ StructTests = [[ MyStruct.new(:foo, "bar"), %({\"name\":\"foo\",\"value\":\"bar\"}) ],
+ [ MyStruct.new(nil, nil), %({\"name\":null,\"value\":null}) ]]
+ CustomTests = [[ Custom.new("custom"), '"custom"' ],
+ [ Custom.new(nil), 'null' ],
+ [ Custom.new(:a), '"a"' ],
+ [ Custom.new([ :foo, "bar" ]), '["foo","bar"]' ],
+ [ Custom.new({ :foo => "hello", :bar => "world" }), '{"bar":"world","foo":"hello"}' ],
+ [ Custom.new(Hashlike.new), '{"bar":"world","foo":"hello"}' ],
+ [ Custom.new(Custom.new(Custom.new(:a))), '"a"' ]]
+
+ RegexpTests = [[ /^a/, '"(?-mix:^a)"' ], [/^\w{1,2}[a-z]+/ix, '"(?ix-m:^\\\\w{1,2}[a-z]+)"']]
+
+ DateTests = [[ Date.new(2005,2,1), %("2005/02/01") ]]
+ TimeTests = [[ Time.utc(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]]
+ DateTimeTests = [[ DateTime.civil(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]]
+
+ StandardDateTests = [[ Date.new(2005,2,1), %("2005-02-01") ]]
+ StandardTimeTests = [[ Time.utc(2005,2,1,15,15,10), %("2005-02-01T15:15:10.000Z") ]]
+ StandardDateTimeTests = [[ DateTime.civil(2005,2,1,15,15,10), %("2005-02-01T15:15:10.000+00:00") ]]
+ StandardStringTests = [[ 'this is the <string>', %("this is the <string>")]]
+ end
+end
diff --git a/activesupport/test/log_subscriber_test.rb b/activesupport/test/log_subscriber_test.rb
index 2a0e8d20ed..998a6887c5 100644
--- a/activesupport/test/log_subscriber_test.rb
+++ b/activesupport/test/log_subscriber_test.rb
@@ -82,10 +82,11 @@ class SyncLogSubscriberTest < ActiveSupport::TestCase
def test_does_not_send_the_event_if_logger_is_nil
ActiveSupport::LogSubscriber.logger = nil
- @log_subscriber.expects(:some_event).never
- ActiveSupport::LogSubscriber.attach_to :my_log_subscriber, @log_subscriber
- instrument "some_event.my_log_subscriber"
- wait
+ assert_not_called(@log_subscriber, :some_event) do
+ ActiveSupport::LogSubscriber.attach_to :my_log_subscriber, @log_subscriber
+ instrument "some_event.my_log_subscriber"
+ wait
+ end
end
def test_does_not_fail_with_non_namespaced_events
diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb
index b6c0a08b05..eb71369397 100644
--- a/activesupport/test/message_encryptor_test.rb
+++ b/activesupport/test/message_encryptor_test.rb
@@ -1,12 +1,5 @@
require 'abstract_unit'
-
-begin
- require 'openssl'
- OpenSSL::Digest::SHA1
-rescue LoadError, NameError
- $stderr.puts "Skipping MessageEncryptor test: broken OpenSSL install"
-else
-
+require 'openssl'
require 'active_support/time'
require 'active_support/json'
@@ -97,5 +90,3 @@ class MessageEncryptorTest < ActiveSupport::TestCase
::Base64.strict_encode64(bits)
end
end
-
-end
diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb
index 28035bc428..668d78492e 100644
--- a/activesupport/test/message_verifier_test.rb
+++ b/activesupport/test/message_verifier_test.rb
@@ -1,12 +1,5 @@
require 'abstract_unit'
-
-begin
- require 'openssl'
- OpenSSL::Digest::SHA1
-rescue LoadError, NameError
- $stderr.puts "Skipping MessageVerifier test: broken OpenSSL install"
-else
-
+require 'openssl'
require 'active_support/time'
require 'active_support/json'
@@ -27,21 +20,30 @@ class MessageVerifierTest < ActiveSupport::TestCase
@data = { :some => "data", :now => Time.local(2010) }
end
+ def test_valid_message
+ data, hash = @verifier.generate(@data).split("--")
+ assert !@verifier.valid_message?(nil)
+ assert !@verifier.valid_message?("")
+ assert !@verifier.valid_message?("\xff") # invalid encoding
+ assert !@verifier.valid_message?("#{data.reverse}--#{hash}")
+ assert !@verifier.valid_message?("#{data}--#{hash.reverse}")
+ assert !@verifier.valid_message?("purejunk")
+ end
+
def test_simple_round_tripping
message = @verifier.generate(@data)
+ assert_equal @data, @verifier.verified(message)
assert_equal @data, @verifier.verify(message)
end
- def test_missing_signature_raises
- assert_not_verified(nil)
- assert_not_verified("")
+ def test_verified_returns_false_on_invalid_message
+ assert !@verifier.verified("purejunk")
end
- def test_tampered_data_raises
- data, hash = @verifier.generate(@data).split("--")
- assert_not_verified("#{data.reverse}--#{hash}")
- assert_not_verified("#{data}--#{hash.reverse}")
- assert_not_verified("purejunk")
+ def test_verify_exception_on_invalid_message
+ assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do
+ @verifier.verify("purejunk")
+ end
end
def test_alternative_serialization_method
@@ -50,6 +52,7 @@ class MessageVerifierTest < ActiveSupport::TestCase
verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!", :serializer => JSONSerializer.new)
message = verifier.generate({ :foo => 123, 'bar' => Time.utc(2010) })
exp = { "foo" => 123, "bar" => "2010-01-01T00:00:00.000Z" }
+ assert_equal exp, verifier.verified(message)
assert_equal exp, verifier.verify(message)
ensure
ActiveSupport.use_standard_json_time_format = prev
@@ -63,6 +66,11 @@ class MessageVerifierTest < ActiveSupport::TestCase
#
valid_message = "BAh7BjoIZm9vbzonTWVzc2FnZVZlcmlmaWVyVGVzdDo6QXV0b2xvYWRDbGFzcwY6CUBmb29JIghmb28GOgZFVA==--f3ef39a5241c365083770566dc7a9eb5d6ace914"
exception = assert_raise(ArgumentError, NameError) do
+ @verifier.verified(valid_message)
+ end
+ assert_includes ["uninitialized constant MessageVerifierTest::AutoloadClass",
+ "undefined class/module MessageVerifierTest::AutoloadClass"], exception.message
+ exception = assert_raise(ArgumentError, NameError) do
@verifier.verify(valid_message)
end
assert_includes ["uninitialized constant MessageVerifierTest::AutoloadClass",
@@ -75,12 +83,4 @@ class MessageVerifierTest < ActiveSupport::TestCase
end
assert_equal exception.message, 'Secret should not be nil.'
end
-
- def assert_not_verified(message)
- assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do
- @verifier.verify(message)
- end
- end
-end
-
end
diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb
index feca013675..e1c4b705f8 100644
--- a/activesupport/test/multibyte_chars_test.rb
+++ b/activesupport/test/multibyte_chars_test.rb
@@ -1,4 +1,3 @@
-# encoding: utf-8
require 'abstract_unit'
require 'multibyte_test_helpers'
require 'active_support/core_ext/string/multibyte'
@@ -182,7 +181,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
end
def test_sortability
- words = %w(builder armor zebra).sort_by { |s| s.mb_chars }
+ words = %w(builder armor zebra).sort_by(&:mb_chars)
assert_equal %w(armor builder zebra), words
end
diff --git a/activesupport/test/multibyte_conformance_test.rb b/activesupport/test/multibyte_conformance_test.rb
index aba81b8248..d8704716e7 100644
--- a/activesupport/test/multibyte_conformance_test.rb
+++ b/activesupport/test/multibyte_conformance_test.rb
@@ -1,4 +1,3 @@
-# encoding: utf-8
require 'abstract_unit'
require 'multibyte_test_helpers'
@@ -115,7 +114,7 @@ class MultibyteConformanceTest < ActiveSupport::TestCase
next if (line.empty? || line =~ /^\#/)
cols, comment = line.split("#")
- cols = cols.split(";").map{|e| e.strip}.reject{|e| e.empty? }
+ cols = cols.split(";").map(&:strip).reject(&:empty?)
next unless cols.length == 5
# codepoints are in hex in the test suite, pack wants them as integers
diff --git a/activesupport/test/multibyte_proxy_test.rb b/activesupport/test/multibyte_proxy_test.rb
index d8ffd7ca9c..11f5374017 100644
--- a/activesupport/test/multibyte_proxy_test.rb
+++ b/activesupport/test/multibyte_proxy_test.rb
@@ -1,4 +1,3 @@
-# encoding: utf-8
require 'abstract_unit'
diff --git a/activesupport/test/multibyte_test_helpers.rb b/activesupport/test/multibyte_test_helpers.rb
index 90af2dadd4..2e4b5cc873 100644
--- a/activesupport/test/multibyte_test_helpers.rb
+++ b/activesupport/test/multibyte_test_helpers.rb
@@ -1,4 +1,3 @@
-# encoding: utf-8
module MultibyteTestHelpers
UNICODE_STRING = 'こにちわ'.freeze
diff --git a/activesupport/test/multibyte_unicode_database_test.rb b/activesupport/test/multibyte_unicode_database_test.rb
index bec65daf50..dd33641ec2 100644
--- a/activesupport/test/multibyte_unicode_database_test.rb
+++ b/activesupport/test/multibyte_unicode_database_test.rb
@@ -1,4 +1,3 @@
-# encoding: utf-8
require 'abstract_unit'
@@ -12,8 +11,9 @@ class MultibyteUnicodeDatabaseTest < ActiveSupport::TestCase
UnicodeDatabase::ATTRIBUTES.each do |attribute|
define_method "test_lazy_loading_on_attribute_access_of_#{attribute}" do
- @ucd.expects(:load)
- @ucd.send(attribute)
+ assert_called(@ucd, :load) do
+ @ucd.send(attribute)
+ end
end
end
diff --git a/activesupport/test/number_helper_test.rb b/activesupport/test/number_helper_test.rb
index 50d84a9470..944bce1b41 100644
--- a/activesupport/test/number_helper_test.rb
+++ b/activesupport/test/number_helper_test.rb
@@ -83,6 +83,14 @@ module ActiveSupport
assert_equal("98a%", number_helper.number_to_percentage("98a"))
assert_equal("NaN%", number_helper.number_to_percentage(Float::NAN))
assert_equal("Inf%", number_helper.number_to_percentage(Float::INFINITY))
+ assert_equal("NaN%", number_helper.number_to_percentage(Float::NAN, precision: 0))
+ assert_equal("Inf%", number_helper.number_to_percentage(Float::INFINITY, precision: 0))
+ assert_equal("NaN%", number_helper.number_to_percentage(Float::NAN, precision: 1))
+ assert_equal("Inf%", number_helper.number_to_percentage(Float::INFINITY, precision: 1))
+ assert_equal("1000%", number_helper.number_to_percentage(1000, precision: nil))
+ assert_equal("1000%", number_helper.number_to_percentage(1000, precision: nil))
+ assert_equal("1000.1%", number_helper.number_to_percentage(1000.1, precision: nil))
+ assert_equal("-0.13 %", number_helper.number_to_percentage("-0.13", precision: nil, format: "%n %"))
end
end
@@ -98,6 +106,7 @@ module ActiveSupport
assert_equal("123,456,789.78901", number_helper.number_to_delimited(123456789.78901))
assert_equal("0.78901", number_helper.number_to_delimited(0.78901))
assert_equal("123,456.78", number_helper.number_to_delimited("123456.78"))
+ assert_equal("1,23,456.78", number_helper.number_to_delimited("123456.78", delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/))
assert_equal("123,456.78", number_helper.number_to_delimited("123456.78".html_safe))
end
end
@@ -226,15 +235,17 @@ module ActiveSupport
end
def test_number_to_human_size_with_si_prefix
- [@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper|
- assert_equal '3 Bytes', number_helper.number_to_human_size(3.14159265, :prefix => :si)
- assert_equal '123 Bytes', number_helper.number_to_human_size(123.0, :prefix => :si)
- assert_equal '123 Bytes', number_helper.number_to_human_size(123, :prefix => :si)
- assert_equal '1.23 KB', number_helper.number_to_human_size(1234, :prefix => :si)
- assert_equal '12.3 KB', number_helper.number_to_human_size(12345, :prefix => :si)
- assert_equal '1.23 MB', number_helper.number_to_human_size(1234567, :prefix => :si)
- assert_equal '1.23 GB', number_helper.number_to_human_size(1234567890, :prefix => :si)
- assert_equal '1.23 TB', number_helper.number_to_human_size(1234567890123, :prefix => :si)
+ assert_deprecated do
+ [@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper|
+ assert_equal '3 Bytes', number_helper.number_to_human_size(3.14159265, :prefix => :si)
+ assert_equal '123 Bytes', number_helper.number_to_human_size(123.0, :prefix => :si)
+ assert_equal '123 Bytes', number_helper.number_to_human_size(123, :prefix => :si)
+ assert_equal '1.23 KB', number_helper.number_to_human_size(1234, :prefix => :si)
+ assert_equal '12.3 KB', number_helper.number_to_human_size(12345, :prefix => :si)
+ assert_equal '1.23 MB', number_helper.number_to_human_size(1234567, :prefix => :si)
+ assert_equal '1.23 GB', number_helper.number_to_human_size(1234567890, :prefix => :si)
+ assert_equal '1.23 TB', number_helper.number_to_human_size(1234567890123, :prefix => :si)
+ end
end
end
diff --git a/activesupport/test/ordered_options_test.rb b/activesupport/test/ordered_options_test.rb
index fdc745b23b..18767a3536 100644
--- a/activesupport/test/ordered_options_test.rb
+++ b/activesupport/test/ordered_options_test.rb
@@ -85,4 +85,19 @@ class OrderedOptionsTest < ActiveSupport::TestCase
assert_equal 42, a.method(:blah=).call(42)
assert_equal 42, a.method(:blah).call
end
+
+ def test_raises_with_bang
+ a = ActiveSupport::OrderedOptions.new
+ a[:foo] = :bar
+ assert a.respond_to?(:foo!)
+
+ assert_nothing_raised { a.foo! }
+ assert_equal a.foo, a.foo!
+
+ assert_raises(KeyError) do
+ a.foo = nil
+ a.foo!
+ end
+ assert_raises(KeyError) { a.non_existing_key! }
+ end
end
diff --git a/activesupport/test/rescuable_test.rb b/activesupport/test/rescuable_test.rb
index ec9d231125..bd43ad0797 100644
--- a/activesupport/test/rescuable_test.rb
+++ b/activesupport/test/rescuable_test.rb
@@ -12,6 +12,12 @@ end
class CoolError < StandardError
end
+module WeirdError
+ def self.===(other)
+ Exception === other && other.respond_to?(:weird?)
+ end
+end
+
class Stargate
attr_accessor :result
@@ -29,6 +35,10 @@ class Stargate
@result = e.message
end
+ rescue_from WeirdError do
+ @result = 'weird'
+ end
+
def dispatch(method)
send(method)
rescue Exception => e
@@ -47,6 +57,16 @@ class Stargate
raise MadRonon.new("dex")
end
+ def weird
+ StandardError.new.tap do |exc|
+ def exc.weird?
+ true
+ end
+
+ raise exc
+ end
+ end
+
def sos
@result = 'killed'
end
@@ -91,15 +111,20 @@ class RescuableTest < ActiveSupport::TestCase
assert_equal 'dex', @stargate.result
end
+ def test_rescue_from_error_dispatchers_with_case_operator
+ @stargate.dispatch :weird
+ assert_equal 'weird', @stargate.result
+ end
+
def test_rescues_defined_later_are_added_at_end_of_the_rescue_handlers_array
- expected = ["WraithAttack", "WraithAttack", "NuclearExplosion", "MadRonon"]
- result = @stargate.send(:rescue_handlers).collect {|e| e.first}
+ expected = ["WraithAttack", "WraithAttack", "NuclearExplosion", "MadRonon", "WeirdError"]
+ result = @stargate.send(:rescue_handlers).collect(&:first)
assert_equal expected, result
end
def test_children_should_inherit_rescue_definitions_from_parents_and_child_rescue_should_be_appended
- expected = ["WraithAttack", "WraithAttack", "NuclearExplosion", "MadRonon", "CoolError"]
- result = @cool_stargate.send(:rescue_handlers).collect {|e| e.first}
+ expected = ["WraithAttack", "WraithAttack", "NuclearExplosion", "MadRonon", "WeirdError", "CoolError"]
+ result = @cool_stargate.send(:rescue_handlers).collect(&:first)
assert_equal expected, result
end
end
diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb
index efa9d5e61f..18fb6d2fbf 100644
--- a/activesupport/test/safe_buffer_test.rb
+++ b/activesupport/test/safe_buffer_test.rb
@@ -61,6 +61,13 @@ class SafeBufferTest < ActiveSupport::TestCase
assert_equal({'str' => str}, YAML.load(yaml))
end
+ test "Should work with primitive-like-strings in to_yaml conversion" do
+ assert_equal 'true', YAML.load(ActiveSupport::SafeBuffer.new('true').to_yaml)
+ assert_equal 'false', YAML.load(ActiveSupport::SafeBuffer.new('false').to_yaml)
+ assert_equal '1', YAML.load(ActiveSupport::SafeBuffer.new('1').to_yaml)
+ assert_equal '1.1', YAML.load(ActiveSupport::SafeBuffer.new('1.1').to_yaml)
+ end
+
test "Should work with underscore" do
str = "MyTest".html_safe.underscore
assert_equal "my_test", str
@@ -165,4 +172,9 @@ class SafeBufferTest < ActiveSupport::TestCase
x = 'foo %{x} bar'.html_safe % { x: 'qux' }
assert x.html_safe?, 'should be safe'
end
+
+ test 'Should not affect frozen objects when accessing characters' do
+ x = 'Hello'.html_safe
+ assert_equal x[/a/, 1], nil
+ end
end
diff --git a/activesupport/test/security_utils_test.rb b/activesupport/test/security_utils_test.rb
new file mode 100644
index 0000000000..08d2e3baa6
--- /dev/null
+++ b/activesupport/test/security_utils_test.rb
@@ -0,0 +1,9 @@
+require 'abstract_unit'
+require 'active_support/security_utils'
+
+class SecurityUtilsTest < ActiveSupport::TestCase
+ def test_secure_compare_should_perform_string_comparison
+ assert ActiveSupport::SecurityUtils.secure_compare('a', 'a')
+ assert !ActiveSupport::SecurityUtils.secure_compare('a', 'b')
+ end
+end
diff --git a/activesupport/test/share_lock_test.rb b/activesupport/test/share_lock_test.rb
new file mode 100644
index 0000000000..ad41db608b
--- /dev/null
+++ b/activesupport/test/share_lock_test.rb
@@ -0,0 +1,333 @@
+require 'abstract_unit'
+require 'concurrent/atomics'
+require 'active_support/concurrency/share_lock'
+
+class ShareLockTest < ActiveSupport::TestCase
+ def setup
+ @lock = ActiveSupport::Concurrency::ShareLock.new
+ end
+
+ def test_reentrancy
+ thread = Thread.new do
+ @lock.sharing { @lock.sharing {} }
+ @lock.exclusive { @lock.exclusive {} }
+ end
+ assert_threads_not_stuck thread
+ end
+
+ def test_sharing_doesnt_block
+ with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_latch|
+ assert_threads_not_stuck(Thread.new {@lock.sharing {} })
+ end
+ end
+
+ def test_sharing_blocks_exclusive
+ with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
+ @lock.exclusive(no_wait: true) { flunk } # polling should fail
+ exclusive_thread = Thread.new { @lock.exclusive {} }
+ assert_threads_stuck_but_releasable_by_latch exclusive_thread, sharing_thread_release_latch
+ end
+ end
+
+ def test_exclusive_blocks_sharing
+ with_thread_waiting_in_lock_section(:exclusive) do |exclusive_thread_release_latch|
+ sharing_thread = Thread.new { @lock.sharing {} }
+ assert_threads_stuck_but_releasable_by_latch sharing_thread, exclusive_thread_release_latch
+ end
+ end
+
+ def test_multiple_exlusives_are_able_to_progress
+ with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
+ exclusive_threads = (1..2).map do
+ Thread.new do
+ @lock.exclusive {}
+ end
+ end
+
+ assert_threads_stuck_but_releasable_by_latch exclusive_threads, sharing_thread_release_latch
+ end
+ end
+
+ def test_sharing_is_upgradeable_to_exclusive
+ upgrading_thread = Thread.new do
+ @lock.sharing do
+ @lock.exclusive {}
+ end
+ end
+ assert_threads_not_stuck upgrading_thread
+ end
+
+ def test_exclusive_upgrade_waits_for_other_sharers_to_leave
+ with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
+ in_sharing = Concurrent::CountDownLatch.new
+
+ upgrading_thread = Thread.new do
+ @lock.sharing do
+ in_sharing.count_down
+ @lock.exclusive {}
+ end
+ end
+
+ in_sharing.wait
+ assert_threads_stuck_but_releasable_by_latch upgrading_thread, sharing_thread_release_latch
+ end
+ end
+
+ def test_exclusive_matching_purpose
+ [true, false].each do |use_upgrading|
+ with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
+ exclusive_threads = (1..2).map do
+ Thread.new do
+ @lock.send(use_upgrading ? :sharing : :tap) do
+ @lock.exclusive(purpose: :load, compatible: [:load, :unload]) {}
+ end
+ end
+ end
+
+ assert_threads_stuck_but_releasable_by_latch exclusive_threads, sharing_thread_release_latch
+ end
+ end
+ end
+
+ def test_killed_thread_loses_lock
+ with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
+ thread = Thread.new do
+ @lock.sharing do
+ @lock.exclusive {}
+ end
+ end
+
+ assert_threads_stuck thread
+ thread.kill
+
+ sharing_thread_release_latch.count_down
+
+ thread = Thread.new do
+ @lock.exclusive {}
+ end
+
+ assert_threads_not_stuck thread
+ end
+ end
+
+ def test_exclusive_conflicting_purpose
+ [true, false].each do |use_upgrading|
+ with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
+ begin
+ conflicting_exclusive_threads = [
+ Thread.new do
+ @lock.send(use_upgrading ? :sharing : :tap) do
+ @lock.exclusive(purpose: :red, compatible: [:green, :purple]) {}
+ end
+ end,
+ Thread.new do
+ @lock.send(use_upgrading ? :sharing : :tap) do
+ @lock.exclusive(purpose: :blue, compatible: [:green]) {}
+ end
+ end
+ ]
+
+ assert_threads_stuck conflicting_exclusive_threads # wait for threads to get into their respective `exclusive {}` blocks
+
+ # This thread will be stuck as long as any other thread is in
+ # a sharing block. While it's blocked, it holds no lock, so it
+ # doesn't interfere with any other attempts.
+ no_purpose_thread = Thread.new do
+ @lock.exclusive {}
+ end
+ assert_threads_stuck no_purpose_thread
+
+ # This thread is compatible with both of the "primary"
+ # attempts above. It's initially stuck on the outer share
+ # lock, but as soon as that's released, it can run --
+ # regardless of whether those threads hold share locks.
+ compatible_thread = Thread.new do
+ @lock.exclusive(purpose: :green, compatible: []) {}
+ end
+ assert_threads_stuck compatible_thread
+
+ assert_threads_stuck conflicting_exclusive_threads
+
+ sharing_thread_release_latch.count_down
+
+ assert_threads_not_stuck compatible_thread # compatible thread is now able to squeak through
+
+ if use_upgrading
+ # The "primary" threads both each hold a share lock, and are
+ # mutually incompatible; they're still stuck.
+ assert_threads_stuck conflicting_exclusive_threads
+
+ # The thread without a specified purpose is also stuck; it's
+ # not compatible with anything.
+ assert_threads_stuck no_purpose_thread
+ else
+ # As the primaries didn't hold a share lock, as soon as the
+ # outer one was released, all the exclusive locks are free
+ # to be acquired in turn.
+
+ assert_threads_not_stuck conflicting_exclusive_threads
+ assert_threads_not_stuck no_purpose_thread
+ end
+ ensure
+ conflicting_exclusive_threads.each(&:kill)
+ no_purpose_thread.kill
+ end
+ end
+ end
+ end
+
+ def test_exclusive_ordering
+ scratch_pad = []
+ scratch_pad_mutex = Mutex.new
+
+ load_params = [:load, [:load]]
+ unload_params = [:unload, [:unload, :load]]
+
+ [load_params, load_params, unload_params, unload_params].permutation do |thread_params|
+ with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
+ threads = thread_params.map do |purpose, compatible|
+ Thread.new do
+ @lock.sharing do
+ @lock.exclusive(purpose: purpose, compatible: compatible) do
+ scratch_pad_mutex.synchronize { scratch_pad << purpose }
+ end
+ end
+ end
+ end
+
+ sleep(0.01)
+ scratch_pad_mutex.synchronize { assert_empty scratch_pad }
+
+ sharing_thread_release_latch.count_down
+
+ assert_threads_not_stuck threads
+ scratch_pad_mutex.synchronize do
+ assert_equal [:load, :load, :unload, :unload], scratch_pad
+ scratch_pad.clear
+ end
+ end
+ end
+ end
+
+ def test_in_shared_section_incompatible_non_upgrading_threads_cannot_preempt_upgrading_threads
+ scratch_pad = []
+ scratch_pad_mutex = Mutex.new
+
+ upgrading_load_params = [:load, [:load], true]
+ non_upgrading_unload_params = [:unload, [:load, :unload], false]
+
+ [upgrading_load_params, non_upgrading_unload_params].permutation do |thread_params|
+ with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
+ threads = thread_params.map do |purpose, compatible, use_upgrading|
+ Thread.new do
+ @lock.send(use_upgrading ? :sharing : :tap) do
+ @lock.exclusive(purpose: purpose, compatible: compatible) do
+ scratch_pad_mutex.synchronize { scratch_pad << purpose }
+ end
+ end
+ end
+ end
+
+ assert_threads_stuck threads
+ scratch_pad_mutex.synchronize { assert_empty scratch_pad }
+
+ sharing_thread_release_latch.count_down
+
+ assert_threads_not_stuck threads
+ scratch_pad_mutex.synchronize do
+ assert_equal [:load, :unload], scratch_pad
+ scratch_pad.clear
+ end
+ end
+ end
+ end
+
+ private
+
+ module CustomAssertions
+ SUFFICIENT_TIMEOUT = 0.2
+
+ private
+
+ def assert_threads_stuck_but_releasable_by_latch(threads, latch)
+ assert_threads_stuck threads
+ latch.count_down
+ assert_threads_not_stuck threads
+ end
+
+ def assert_threads_stuck(threads)
+ sleep(SUFFICIENT_TIMEOUT) # give threads time to do their business
+ assert(Array(threads).all? { |t| t.join(0.001).nil? })
+ end
+
+ def assert_threads_not_stuck(threads)
+ assert(Array(threads).all? { |t| t.join(SUFFICIENT_TIMEOUT) })
+ end
+ end
+
+ class CustomAssertionsTest < ActiveSupport::TestCase
+ include CustomAssertions
+
+ def setup
+ @latch = Concurrent::CountDownLatch.new
+ @thread = Thread.new { @latch.wait }
+ end
+
+ def teardown
+ @latch.count_down
+ @thread.join
+ end
+
+ def test_happy_path
+ assert_threads_stuck_but_releasable_by_latch @thread, @latch
+ end
+
+ def test_detects_stuck_thread
+ assert_raises(Minitest::Assertion) do
+ assert_threads_not_stuck @thread
+ end
+ end
+
+ def test_detects_free_thread
+ @latch.count_down
+ assert_raises(Minitest::Assertion) do
+ assert_threads_stuck @thread
+ end
+ end
+
+ def test_detects_already_released
+ @latch.count_down
+ assert_raises(Minitest::Assertion) do
+ assert_threads_stuck_but_releasable_by_latch @thread, @latch
+ end
+ end
+
+ def test_detects_remains_latched
+ another_latch = Concurrent::CountDownLatch.new
+ assert_raises(Minitest::Assertion) do
+ assert_threads_stuck_but_releasable_by_latch @thread, another_latch
+ end
+ end
+ end
+
+ include CustomAssertions
+
+ def with_thread_waiting_in_lock_section(lock_section)
+ in_section = Concurrent::CountDownLatch.new
+ section_release = Concurrent::CountDownLatch.new
+
+ stuck_thread = Thread.new do
+ @lock.send(lock_section) do
+ in_section.count_down
+ section_release.wait
+ end
+ end
+
+ in_section.wait
+
+ yield section_release
+ ensure
+ section_release.count_down
+ stuck_thread.join # clean up
+ end
+end
diff --git a/activesupport/test/tagged_logging_test.rb b/activesupport/test/tagged_logging_test.rb
index 27f629474e..03a63e94e8 100644
--- a/activesupport/test/tagged_logging_test.rb
+++ b/activesupport/test/tagged_logging_test.rb
@@ -79,6 +79,19 @@ class TaggedLoggingTest < ActiveSupport::TestCase
assert_equal "[OMG] Cool story bro\n[BCX] Funky time\n", @output.string
end
+ test "keeps each tag in their own instance" do
+ @other_output = StringIO.new
+ @other_logger = ActiveSupport::TaggedLogging.new(MyLogger.new(@other_output))
+ @logger.tagged("OMG") do
+ @other_logger.tagged("BCX") do
+ @logger.info "Cool story bro"
+ @other_logger.info "Funky time"
+ end
+ end
+ assert_equal "[OMG] Cool story bro\n", @output.string
+ assert_equal "[BCX] Funky time\n", @other_output.string
+ end
+
test "cleans up the taggings on flush" do
@logger.tagged("BCX") do
Thread.new do
diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb
index 5e852c8050..9e6d1a91d0 100644
--- a/activesupport/test/test_case_test.rb
+++ b/activesupport/test/test_case_test.rb
@@ -182,40 +182,27 @@ class TestOrderTest < ActiveSupport::TestCase
ActiveSupport::TestCase.test_order = @original_test_order
end
- def test_defaults_to_sorted_with_warning
+ def test_defaults_to_random
ActiveSupport::TestCase.test_order = nil
- assert_equal :sorted, assert_deprecated { ActiveSupport::TestCase.test_order }
+ assert_equal :random, ActiveSupport::TestCase.test_order
- # It should only produce a deprecation warning the first time this is accessed
- assert_equal :sorted, assert_not_deprecated { ActiveSupport::TestCase.test_order }
- assert_equal :sorted, assert_not_deprecated { ActiveSupport.test_order }
+ assert_equal :random, ActiveSupport.test_order
end
def test_test_order_is_global
- ActiveSupport::TestCase.test_order = :random
-
- assert_equal :random, ActiveSupport.test_order
- assert_equal :random, ActiveSupport::TestCase.test_order
- assert_equal :random, self.class.test_order
- assert_equal :random, Class.new(ActiveSupport::TestCase).test_order
-
- ActiveSupport.test_order = :sorted
+ ActiveSupport::TestCase.test_order = :sorted
assert_equal :sorted, ActiveSupport.test_order
assert_equal :sorted, ActiveSupport::TestCase.test_order
assert_equal :sorted, self.class.test_order
assert_equal :sorted, Class.new(ActiveSupport::TestCase).test_order
- end
- def test_i_suck_and_my_tests_are_order_dependent!
- ActiveSupport::TestCase.test_order = :random
-
- klass = Class.new(ActiveSupport::TestCase) do
- i_suck_and_my_tests_are_order_dependent!
- end
+ ActiveSupport.test_order = :random
- assert_equal :alpha, klass.test_order
+ assert_equal :random, ActiveSupport.test_order
assert_equal :random, ActiveSupport::TestCase.test_order
+ assert_equal :random, self.class.test_order
+ assert_equal :random, Class.new(ActiveSupport::TestCase).test_order
end
end
diff --git a/activesupport/test/testing/file_fixtures_test.rb b/activesupport/test/testing/file_fixtures_test.rb
new file mode 100644
index 0000000000..91b8a9071c
--- /dev/null
+++ b/activesupport/test/testing/file_fixtures_test.rb
@@ -0,0 +1,28 @@
+require 'abstract_unit'
+
+class FileFixturesTest < ActiveSupport::TestCase
+ self.file_fixture_path = File.expand_path("../../file_fixtures", __FILE__)
+
+ test "#file_fixture returns Pathname to file fixture" do
+ path = file_fixture("sample.txt")
+ assert_kind_of Pathname, path
+ assert_match %r{activesupport/test/file_fixtures/sample.txt$}, path.to_s
+ end
+
+ test "raises an exception when the fixture file does not exist" do
+ e = assert_raises(ArgumentError) do
+ file_fixture("nope")
+ end
+ assert_match(/^the directory '[^']+test\/file_fixtures' does not contain a file named 'nope'$/, e.message)
+ end
+end
+
+class FileFixturesPathnameDirectoryTest < ActiveSupport::TestCase
+ self.file_fixture_path = Pathname.new(File.expand_path("../../file_fixtures", __FILE__))
+
+ test "#file_fixture_path returns Pathname to file fixture" do
+ path = file_fixture("sample.txt")
+ assert_kind_of Pathname, path
+ assert_match %r{activesupport/test/file_fixtures/sample.txt$}, path.to_s
+ end
+end
diff --git a/activesupport/test/testing/method_call_assertions_test.rb b/activesupport/test/testing/method_call_assertions_test.rb
new file mode 100644
index 0000000000..3e5ba7c079
--- /dev/null
+++ b/activesupport/test/testing/method_call_assertions_test.rb
@@ -0,0 +1,123 @@
+require 'abstract_unit'
+require 'active_support/testing/method_call_assertions'
+
+class MethodCallAssertionsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::MethodCallAssertions
+
+ class Level
+ def increment; 1; end
+ def decrement; end
+ def <<(arg); end
+ end
+
+ setup do
+ @object = Level.new
+ end
+
+ def test_assert_called_with_defaults_to_expect_once
+ assert_called @object, :increment do
+ @object.increment
+ end
+ end
+
+ def test_assert_called_more_than_once
+ assert_called(@object, :increment, times: 2) do
+ @object.increment
+ @object.increment
+ end
+ end
+
+ def test_assert_called_method_with_arguments
+ assert_called(@object, :<<) do
+ @object << 2
+ end
+ end
+
+ def test_assert_called_returns
+ assert_called(@object, :increment, returns: 10) do
+ assert_equal 10, @object.increment
+ end
+ end
+
+ def test_assert_called_failure
+ error = assert_raises(Minitest::Assertion) do
+ assert_called(@object, :increment) do
+ # Call nothing...
+ end
+ end
+
+ assert_equal "Expected increment to be called 1 times, but was called 0 times.\nExpected: 1\n Actual: 0", error.message
+ end
+
+ def test_assert_called_with_message
+ error = assert_raises(Minitest::Assertion) do
+ assert_called(@object, :increment, 'dang it') do
+ # Call nothing...
+ end
+ end
+
+ assert_match(/dang it.\nExpected increment/, error.message)
+ end
+
+ def test_assert_called_with
+ assert_called_with(@object, :increment) do
+ @object.increment
+ end
+ end
+
+ def test_assert_called_with_arguments
+ assert_called_with(@object, :<<, [ 2 ]) do
+ @object << 2
+ end
+ end
+
+ def test_assert_called_with_failure
+ assert_raises(MockExpectationError) do
+ assert_called_with(@object, :<<, [ 4567 ]) do
+ @object << 2
+ end
+ end
+ end
+
+ def test_assert_called_with_returns
+ assert_called_with(@object, :increment, returns: 1) do
+ @object.increment
+ end
+ end
+
+ def test_assert_called_with_multiple_expected_arguments
+ assert_called_with(@object, :<<, [ [ 1 ], [ 2 ] ]) do
+ @object << 1
+ @object << 2
+ end
+ end
+
+ def test_assert_not_called
+ assert_not_called(@object, :decrement) do
+ @object.increment
+ end
+ end
+
+ def test_assert_not_called_failure
+ error = assert_raises(Minitest::Assertion) do
+ assert_not_called(@object, :increment) do
+ @object.increment
+ end
+ end
+
+ assert_equal "Expected increment to be called 0 times, but was called 1 times.\nExpected: 0\n Actual: 1", error.message
+ end
+
+ def test_stub_any_instance
+ stub_any_instance(Level) do |instance|
+ assert_equal instance, Level.new
+ end
+ end
+
+ def test_stub_any_instance_with_instance
+ stub_any_instance(Level, instance: @object) do |instance|
+ assert_equal @object, instance
+ assert_equal instance, Level.new
+ end
+ end
+end
diff --git a/activesupport/test/time_travel_test.rb b/activesupport/test/time_travel_test.rb
index 065539671d..59c3e52c2f 100644
--- a/activesupport/test/time_travel_test.rb
+++ b/activesupport/test/time_travel_test.rb
@@ -1,72 +1,90 @@
require 'abstract_unit'
-require 'active_support/core_ext/date'
+require 'active_support/core_ext/date_time'
require 'active_support/core_ext/numeric/time'
class TimeTravelTest < ActiveSupport::TestCase
- setup do
- Time.stubs now: Time.now
- end
-
teardown do
travel_back
end
def test_time_helper_travel
- expected_time = Time.now + 1.day
- travel 1.day
-
- assert_equal expected_time.to_s(:db), Time.now.to_s(:db)
- assert_equal expected_time.to_date, Date.today
- end
-
- def test_time_helper_travel_with_block
- expected_time = Time.now + 1.day
+ Time.stub(:now, Time.now) do
+ expected_time = Time.now + 1.day
+ travel 1.day
- travel 1.day do
assert_equal expected_time.to_s(:db), Time.now.to_s(:db)
assert_equal expected_time.to_date, Date.today
+ assert_equal expected_time.to_datetime.to_s(:db), DateTime.now.to_s(:db)
end
-
- assert_not_equal expected_time.to_s(:db), Time.now.to_s(:db)
- assert_not_equal expected_time.to_date, Date.today
end
- def test_time_helper_travel_to
- expected_time = Time.new(2004, 11, 24, 01, 04, 44)
- travel_to expected_time
-
- assert_equal expected_time, Time.now
- assert_equal Date.new(2004, 11, 24), Date.today
+ def test_time_helper_travel_with_block
+ Time.stub(:now, Time.now) do
+ expected_time = Time.now + 1.day
+
+ travel 1.day do
+ assert_equal expected_time.to_s(:db), Time.now.to_s(:db)
+ assert_equal expected_time.to_date, Date.today
+ assert_equal expected_time.to_datetime.to_s(:db), DateTime.now.to_s(:db)
+ end
+
+ assert_not_equal expected_time.to_s(:db), Time.now.to_s(:db)
+ assert_not_equal expected_time.to_date, Date.today
+ assert_not_equal expected_time.to_datetime.to_s(:db), DateTime.now.to_s(:db)
+ end
end
- def test_time_helper_travel_to_with_block
- expected_time = Time.new(2004, 11, 24, 01, 04, 44)
+ def test_time_helper_travel_to
+ Time.stub(:now, Time.now) do
+ expected_time = Time.new(2004, 11, 24, 01, 04, 44)
+ travel_to expected_time
- travel_to expected_time do
assert_equal expected_time, Time.now
assert_equal Date.new(2004, 11, 24), Date.today
+ assert_equal expected_time.to_datetime, DateTime.now
end
+ end
- assert_not_equal expected_time, Time.now
- assert_not_equal Date.new(2004, 11, 24), Date.today
+ def test_time_helper_travel_to_with_block
+ Time.stub(:now, Time.now) do
+ expected_time = Time.new(2004, 11, 24, 01, 04, 44)
+
+ travel_to expected_time do
+ assert_equal expected_time, Time.now
+ assert_equal Date.new(2004, 11, 24), Date.today
+ assert_equal expected_time.to_datetime, DateTime.now
+ end
+
+ assert_not_equal expected_time, Time.now
+ assert_not_equal Date.new(2004, 11, 24), Date.today
+ assert_not_equal expected_time.to_datetime, DateTime.now
+ end
end
def test_time_helper_travel_back
- expected_time = Time.new(2004, 11, 24, 01, 04, 44)
+ Time.stub(:now, Time.now) do
+ expected_time = Time.new(2004, 11, 24, 01, 04, 44)
- travel_to expected_time
- assert_equal expected_time, Time.now
- assert_equal Date.new(2004, 11, 24), Date.today
- travel_back
+ travel_to expected_time
+ assert_equal expected_time, Time.now
+ assert_equal Date.new(2004, 11, 24), Date.today
+ assert_equal expected_time.to_datetime, DateTime.now
+ travel_back
- assert_not_equal expected_time, Time.now
- assert_not_equal Date.new(2004, 11, 24), Date.today
+ assert_not_equal expected_time, Time.now
+ assert_not_equal Date.new(2004, 11, 24), Date.today
+ assert_not_equal expected_time.to_datetime, DateTime.now
+ end
end
def test_travel_to_will_reset_the_usec_to_avoid_mysql_rouding
- travel_to Time.utc(2014, 10, 10, 10, 10, 50, 999999) do
- assert_equal 50, Time.now.sec
- assert_equal 0, Time.now.usec
+ Time.stub(:now, Time.now) do
+ travel_to Time.utc(2014, 10, 10, 10, 10, 50, 999999) do
+ assert_equal 50, Time.now.sec
+ assert_equal 0, Time.now.usec
+ assert_equal 50, DateTime.now.sec
+ assert_equal 0, DateTime.now.usec
+ end
end
end
end
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 3e6d9652bb..00d40c4497 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -1,6 +1,7 @@
require 'abstract_unit'
require 'active_support/time'
require 'time_zone_test_helpers'
+require 'yaml'
class TimeZoneTest < ActiveSupport::TestCase
include TimeZoneTestHelpers
@@ -252,9 +253,10 @@ class TimeZoneTest < ActiveSupport::TestCase
def test_parse_with_incomplete_date
zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
- zone.stubs(:now).returns zone.local(1999,12,31)
- twz = zone.parse('19:00:00')
- assert_equal Time.utc(1999,12,31,19), twz.time
+ zone.stub(:now, zone.local(1999,12,31)) do
+ twz = zone.parse('19:00:00')
+ assert_equal Time.utc(1999,12,31,19), twz.time
+ end
end
def test_parse_with_day_omitted
@@ -284,9 +286,10 @@ class TimeZoneTest < ActiveSupport::TestCase
def test_parse_with_missing_time_components
zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
- zone.stubs(:now).returns zone.local(1999, 12, 31, 12, 59, 59)
- twz = zone.parse('2012-12-01')
- assert_equal Time.utc(2012, 12, 1), twz.time
+ zone.stub(:now, zone.local(1999, 12, 31, 12, 59, 59)) do
+ twz = zone.parse('2012-12-01')
+ assert_equal Time.utc(2012, 12, 1), twz.time
+ end
end
def test_parse_with_javascript_date
@@ -311,6 +314,80 @@ class TimeZoneTest < ActiveSupport::TestCase
end
end
+ def test_strptime
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.strptime('1999-12-31 12:00:00', '%Y-%m-%d %H:%M:%S')
+ assert_equal Time.utc(1999,12,31,17), twz
+ assert_equal Time.utc(1999,12,31,12), twz.time
+ assert_equal Time.utc(1999,12,31,17), twz.utc
+ assert_equal zone, twz.time_zone
+ end
+
+ def test_strptime_with_nondefault_time_zone
+ with_tz_default ActiveSupport::TimeZone['Pacific Time (US & Canada)'] do
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.strptime('1999-12-31 12:00:00', '%Y-%m-%d %H:%M:%S')
+ assert_equal Time.utc(1999,12,31,17), twz
+ assert_equal Time.utc(1999,12,31,12), twz.time
+ assert_equal Time.utc(1999,12,31,17), twz.utc
+ assert_equal zone, twz.time_zone
+ end
+ end
+
+ def test_strptime_with_explicit_time_zone_as_abbrev
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.strptime('1999-12-31 12:00:00 PST', '%Y-%m-%d %H:%M:%S %Z')
+ assert_equal Time.utc(1999,12,31,20), twz
+ assert_equal Time.utc(1999,12,31,15), twz.time
+ assert_equal Time.utc(1999,12,31,20), twz.utc
+ assert_equal zone, twz.time_zone
+ end
+
+ def test_strptime_with_explicit_time_zone_as_h_offset
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.strptime('1999-12-31 12:00:00 -08', '%Y-%m-%d %H:%M:%S %:::z')
+ assert_equal Time.utc(1999,12,31,20), twz
+ assert_equal Time.utc(1999,12,31,15), twz.time
+ assert_equal Time.utc(1999,12,31,20), twz.utc
+ assert_equal zone, twz.time_zone
+ end
+
+ def test_strptime_with_explicit_time_zone_as_hm_offset
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.strptime('1999-12-31 12:00:00 -08:00', '%Y-%m-%d %H:%M:%S %:z')
+ assert_equal Time.utc(1999,12,31,20), twz
+ assert_equal Time.utc(1999,12,31,15), twz.time
+ assert_equal Time.utc(1999,12,31,20), twz.utc
+ assert_equal zone, twz.time_zone
+ end
+
+ def test_strptime_with_explicit_time_zone_as_hms_offset
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.strptime('1999-12-31 12:00:00 -08:00:00', '%Y-%m-%d %H:%M:%S %::z')
+ assert_equal Time.utc(1999,12,31,20), twz
+ assert_equal Time.utc(1999,12,31,15), twz.time
+ assert_equal Time.utc(1999,12,31,20), twz.utc
+ assert_equal zone, twz.time_zone
+ end
+
+ def test_strptime_with_almost_explicit_time_zone
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.strptime('1999-12-31 12:00:00 %Z', '%Y-%m-%d %H:%M:%S %%Z')
+ assert_equal Time.utc(1999,12,31,17), twz
+ assert_equal Time.utc(1999,12,31,12), twz.time
+ assert_equal Time.utc(1999,12,31,17), twz.utc
+ assert_equal zone, twz.time_zone
+ end
+
+ def test_strptime_with_day_omitted
+ with_env_tz 'US/Eastern' do
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ assert_equal Time.local(2000, 2, 1), zone.strptime('Feb', '%b', Time.local(2000, 1, 1))
+ assert_equal Time.local(2005, 2, 1), zone.strptime('Feb 2005', '%b %Y', Time.local(2000, 1, 1))
+ assert_equal Time.local(2005, 2, 2), zone.strptime('2 Feb 2005', '%e %b %Y', Time.local(2000, 1, 1))
+ end
+ end
+
def test_utc_offset_lazy_loaded_from_tzinfo_when_not_passed_in_to_initialize
tzinfo = TZInfo::Timezone.get('America/New_York')
zone = ActiveSupport::TimeZone.create(tzinfo.name, nil, tzinfo)
@@ -395,20 +472,14 @@ class TimeZoneTest < ActiveSupport::TestCase
assert_raise(ArgumentError) { ActiveSupport::TimeZone[false] }
end
- def test_unknown_zone_should_have_tzinfo_but_exception_on_utc_offset
- zone = ActiveSupport::TimeZone.create("bogus")
- assert_instance_of TZInfo::TimezoneProxy, zone.tzinfo
- assert_raise(TZInfo::InvalidTimezoneIdentifier) { zone.utc_offset }
- end
-
- def test_unknown_zone_with_utc_offset
- zone = ActiveSupport::TimeZone.create("bogus", -21_600)
- assert_equal(-21_600, zone.utc_offset)
+ def test_unknown_zone_raises_exception
+ assert_raise TZInfo::InvalidTimezoneIdentifier do
+ ActiveSupport::TimeZone.create("bogus")
+ end
end
def test_unknown_zones_dont_store_mapping_keys
- ActiveSupport::TimeZone["bogus"]
- assert !ActiveSupport::TimeZone.zones_map.key?("bogus")
+ assert_nil ActiveSupport::TimeZone["bogus"]
end
def test_new
@@ -419,4 +490,13 @@ class TimeZoneTest < ActiveSupport::TestCase
assert ActiveSupport::TimeZone.us_zones.include?(ActiveSupport::TimeZone["Hawaii"])
assert !ActiveSupport::TimeZone.us_zones.include?(ActiveSupport::TimeZone["Kuala Lumpur"])
end
+
+ def test_to_yaml
+ assert_equal("--- !ruby/object:ActiveSupport::TimeZone\nname: Pacific/Honolulu\n", ActiveSupport::TimeZone["Hawaii"].to_yaml)
+ assert_equal("--- !ruby/object:ActiveSupport::TimeZone\nname: Europe/London\n", ActiveSupport::TimeZone["Europe/London"].to_yaml)
+ end
+
+ def test_yaml_load
+ assert_equal(ActiveSupport::TimeZone["Pacific/Honolulu"], YAML.load("--- !ruby/object:ActiveSupport::TimeZone\nname: Pacific/Honolulu\n"))
+ end
end
diff --git a/activesupport/test/transliterate_test.rb b/activesupport/test/transliterate_test.rb
index 6833ae68a7..378421fedd 100644
--- a/activesupport/test/transliterate_test.rb
+++ b/activesupport/test/transliterate_test.rb
@@ -1,4 +1,3 @@
-# encoding: utf-8
require 'abstract_unit'
require 'active_support/inflector/transliterate'
diff --git a/activesupport/test/xml_mini/nokogiri_engine_test.rb b/activesupport/test/xml_mini/nokogiri_engine_test.rb
index 2e962576b5..1314c9065a 100644
--- a/activesupport/test/xml_mini/nokogiri_engine_test.rb
+++ b/activesupport/test/xml_mini/nokogiri_engine_test.rb
@@ -8,15 +8,13 @@ require 'active_support/xml_mini'
require 'active_support/core_ext/hash/conversions'
class NokogiriEngineTest < ActiveSupport::TestCase
- include ActiveSupport
-
def setup
- @default_backend = XmlMini.backend
- XmlMini.backend = 'Nokogiri'
+ @default_backend = ActiveSupport::XmlMini.backend
+ ActiveSupport::XmlMini.backend = 'Nokogiri'
end
def teardown
- XmlMini.backend = @default_backend
+ ActiveSupport::XmlMini.backend = @default_backend
end
def test_file_from_xml
@@ -56,13 +54,13 @@ class NokogiriEngineTest < ActiveSupport::TestCase
end
def test_setting_nokogiri_as_backend
- XmlMini.backend = 'Nokogiri'
- assert_equal XmlMini_Nokogiri, XmlMini.backend
+ ActiveSupport::XmlMini.backend = 'Nokogiri'
+ assert_equal ActiveSupport::XmlMini_Nokogiri, ActiveSupport::XmlMini.backend
end
def test_blank_returns_empty_hash
- assert_equal({}, XmlMini.parse(nil))
- assert_equal({}, XmlMini.parse(''))
+ assert_equal({}, ActiveSupport::XmlMini.parse(nil))
+ assert_equal({}, ActiveSupport::XmlMini.parse(''))
end
def test_array_type_makes_an_array
@@ -207,9 +205,9 @@ class NokogiriEngineTest < ActiveSupport::TestCase
private
def assert_equal_rexml(xml)
- parsed_xml = XmlMini.parse(xml)
+ parsed_xml = ActiveSupport::XmlMini.parse(xml)
xml.rewind if xml.respond_to?(:rewind)
- hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) }
+ hash = ActiveSupport::XmlMini.with_backend('REXML') { ActiveSupport::XmlMini.parse(xml) }
assert_equal(hash, parsed_xml)
end
end
diff --git a/activesupport/test/xml_mini/nokogirisax_engine_test.rb b/activesupport/test/xml_mini/nokogirisax_engine_test.rb
index 4f078f31e0..7978a50921 100644
--- a/activesupport/test/xml_mini/nokogirisax_engine_test.rb
+++ b/activesupport/test/xml_mini/nokogirisax_engine_test.rb
@@ -8,15 +8,13 @@ require 'active_support/xml_mini'
require 'active_support/core_ext/hash/conversions'
class NokogiriSAXEngineTest < ActiveSupport::TestCase
- include ActiveSupport
-
def setup
- @default_backend = XmlMini.backend
- XmlMini.backend = 'NokogiriSAX'
+ @default_backend = ActiveSupport::XmlMini.backend
+ ActiveSupport::XmlMini.backend = 'NokogiriSAX'
end
def teardown
- XmlMini.backend = @default_backend
+ ActiveSupport::XmlMini.backend = @default_backend
end
def test_file_from_xml
@@ -57,13 +55,13 @@ class NokogiriSAXEngineTest < ActiveSupport::TestCase
end
def test_setting_nokogirisax_as_backend
- XmlMini.backend = 'NokogiriSAX'
- assert_equal XmlMini_NokogiriSAX, XmlMini.backend
+ ActiveSupport::XmlMini.backend = 'NokogiriSAX'
+ assert_equal ActiveSupport::XmlMini_NokogiriSAX, ActiveSupport::XmlMini.backend
end
def test_blank_returns_empty_hash
- assert_equal({}, XmlMini.parse(nil))
- assert_equal({}, XmlMini.parse(''))
+ assert_equal({}, ActiveSupport::XmlMini.parse(nil))
+ assert_equal({}, ActiveSupport::XmlMini.parse(''))
end
def test_array_type_makes_an_array
@@ -208,9 +206,9 @@ class NokogiriSAXEngineTest < ActiveSupport::TestCase
private
def assert_equal_rexml(xml)
- parsed_xml = XmlMini.parse(xml)
+ parsed_xml = ActiveSupport::XmlMini.parse(xml)
xml.rewind if xml.respond_to?(:rewind)
- hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) }
+ hash = ActiveSupport::XmlMini.with_backend('REXML') { ActiveSupport::XmlMini.parse(xml) }
assert_equal(hash, parsed_xml)
end
end
diff --git a/activesupport/test/xml_mini/rexml_engine_test.rb b/activesupport/test/xml_mini/rexml_engine_test.rb
index 0c1f11803c..f0067ca656 100644
--- a/activesupport/test/xml_mini/rexml_engine_test.rb
+++ b/activesupport/test/xml_mini/rexml_engine_test.rb
@@ -2,19 +2,17 @@ require 'abstract_unit'
require 'active_support/xml_mini'
class REXMLEngineTest < ActiveSupport::TestCase
- include ActiveSupport
-
def test_default_is_rexml
- assert_equal XmlMini_REXML, XmlMini.backend
+ assert_equal ActiveSupport::XmlMini_REXML, ActiveSupport::XmlMini.backend
end
def test_set_rexml_as_backend
- XmlMini.backend = 'REXML'
- assert_equal XmlMini_REXML, XmlMini.backend
+ ActiveSupport::XmlMini.backend = 'REXML'
+ assert_equal ActiveSupport::XmlMini_REXML, ActiveSupport::XmlMini.backend
end
def test_parse_from_io
- XmlMini.backend = 'REXML'
+ ActiveSupport::XmlMini.backend = 'REXML'
io = StringIO.new(<<-eoxml)
<root>
good
@@ -29,9 +27,9 @@ class REXMLEngineTest < ActiveSupport::TestCase
private
def assert_equal_rexml(xml)
- parsed_xml = XmlMini.parse(xml)
+ parsed_xml = ActiveSupport::XmlMini.parse(xml)
xml.rewind if xml.respond_to?(:rewind)
- hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) }
+ hash = ActiveSupport::XmlMini.with_backend('REXML') { ActiveSupport::XmlMini.parse(xml) }
assert_equal(hash, parsed_xml)
end
end
diff --git a/activesupport/test/xml_mini_test.rb b/activesupport/test/xml_mini_test.rb
index f49431cbbf..55e8181b54 100644
--- a/activesupport/test/xml_mini_test.rb
+++ b/activesupport/test/xml_mini_test.rb
@@ -1,9 +1,9 @@
require 'abstract_unit'
require 'active_support/xml_mini'
require 'active_support/builder'
-require 'active_support/core_ext/array'
require 'active_support/core_ext/hash'
require 'active_support/core_ext/big_decimal'
+require 'yaml'
module XmlMiniTest
class RenameKeyTest < ActiveSupport::TestCase
@@ -11,7 +11,7 @@ module XmlMiniTest
assert_equal "my-key", ActiveSupport::XmlMini.rename_key("my_key")
end
- def test_rename_key_does_nothing_with_dasherize_true
+ def test_rename_key_dasherizes_with_dasherize_true
assert_equal "my-key", ActiveSupport::XmlMini.rename_key("my_key", :dasherize => true)
end