aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/CHANGELOG.md500
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/filters.rb2
-rw-r--r--activesupport/lib/active_support/deprecation.rb2
-rw-r--r--activesupport/lib/active_support/gem_version.rb4
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb2
-rw-r--r--activesupport/lib/active_support/notifications.rb35
-rw-r--r--activesupport/lib/active_support/notifications/fanout.rb27
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb5
-rw-r--r--activesupport/test/hash_with_indifferent_access_test.rb28
-rw-r--r--activesupport/test/notifications_test.rb76
11 files changed, 178 insertions, 505 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 4c7b134c35..e494f66c5a 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,495 +1,23 @@
-* Introduce `ActiveSupport::ActionableError`.
-
- Actionable errors let's you dispatch actions from Rails' error pages. This
- can help you save time if you have a clear action for the resolution of
- common development errors.
-
- The de-facto example are pending migrations. Every time pending migrations
- are found, a middleware raises an error. With actionable errors, you can
- run the migrations right from the error page. Other examples include Rails
- plugins that need to run a rake task to setup themselves. They can now
- raise actionable errors to run the setup straight from the error pages.
-
- Here is how to define an actionable error:
-
- ```ruby
- class PendingMigrationError < MigrationError #:nodoc:
- include ActiveSupport::ActionableError
-
- action "Run pending migrations" do
- ActiveRecord::Tasks::DatabaseTasks.migrate
- end
- end
- ```
-
- To make an error actionable, include the `ActiveSupport::ActionableError`
- module and invoke the `action` class macro to define the action. An action
- needs a name and a procedure to execute. The name is shown as the name of a
- button on the error pages. Once clicked, it will invoke the given
- procedure.
-
- *Vipul A M*, *Yao Jie*, *Genadi Samokovarov*
-
-* Preserve `html_safe?` status on `ActiveSupport::SafeBuffer#*`.
-
- Before:
-
- ("<br />".html_safe * 2).html_safe? #=> nil
-
- After:
-
- ("<br />".html_safe * 2).html_safe? #=> true
-
- *Ryo Nakamura*
-
-* Calling test methods with `with_info_handler` method to allow minitest-hooks
- plugin to work.
-
- *Mauri Mustonen*
-
-* The Zeitwerk compatibility interface for `ActiveSupport::Dependencies` no
- longer implements `autoloaded_constants` or `autoloaded?` (undocumented,
- anyway). Experience shows introspection does not have many use cases, and
- troubleshooting is done by logging. With this design trade-off we are able
- to use even less memory in all environments.
-
- *Xavier Noria*
-
-* Depends on Zeitwerk 2, which stores less metadata if reloading is disabled
- and hence uses less memory when `config.cache_classes` is `true`, a standard
- setup in production.
-
- *Xavier Noria*
-
-* In `:zeitwerk` mode, eager load directories in engines and applications only
- if present in their respective `config.eager_load_paths`.
-
- A common use case for this is adding `lib` to `config.autoload_paths`, but
- not to `config.eager_load_paths`. In that configuration, for example, files
- in the `lib` directory should not be eager loaded.
-
- *Xavier Noria*
-
-* Fix bug in Range comparisons when comparing to an excluded-end Range
+* `truncate` would return the original string if it was too short to be truncated
+ and a frozen string if it were long enough to be truncated. Now truncate will
+ consistently return an unfrozen string regardless. This behavior is consistent
+ with `gsub` and `strip`.
Before:
- (1..10).cover?(1...11) # => false
+ 'foobar'.truncate(5).frozen?
+ # => true
+ 'foobar'.truncate(6).frozen?
+ # => false
After:
- (1..10).cover?(1...11) # => true
-
- With the same change for `Range#include?` and `Range#===`.
-
- *Owen Stephens*
-
-* Use weak references in descendants tracker to allow anonymous subclasses to
- be garbage collected.
-
- *Edgars Beigarts*
-
-* Update `ActiveSupport::Notifications::Instrumenter#instrument` to make
- passing a block optional. This will let users use
- `ActiveSupport::Notifications` messaging features outside of
- instrumentation.
-
- *Ali Ibrahim*
-
-* Fix `Time#advance` to work with dates before 1001-03-07
-
- Before:
-
- Time.utc(1001, 3, 6).advance(years: -1) # => 1000-03-05 00:00:00 UTC
-
- After
-
- Time.utc(1001, 3, 6).advance(years: -1) # => 1000-03-06 00:00:00 UTC
-
- Note that this doesn't affect `DateTime#advance` as that doesn't use a proleptic calendar.
-
- *Andrew White*
-
-* In Zeitwerk mode, engines are now managed by the `main` autoloader. Engines may reference application constants, if the application is reloaded and we do not reload engines, they won't use the reloaded application code.
-
- *Xavier Noria*
-
-* Add support for supplying `locale` to `transliterate` and `parameterize`.
-
- I18n.backend.store_translations(:de, i18n: { transliterate: { rule: { "ü" => "ue" } } })
-
- ActiveSupport::Inflector.transliterate("ü", locale: :de) # => "ue"
- "Fünf autos".parameterize(locale: :de) # => "fuenf-autos"
- ActiveSupport::Inflector.parameterize("Fünf autos", locale: :de) # => "fuenf-autos"
-
- *Kaan Ozkan*, *Sharang Dashputre*
-
-* Allow `Array#excluding` and `Enumerable#excluding` to deal with a passed array gracefully.
-
- [ 1, 2, 3, 4, 5 ].excluding([4, 5]) # => [ 1, 2, 3 ]
-
- *DHH*
-
-* Renamed `Array#without` and `Enumerable#without` to `Array#excluding` and `Enumerable#excluding`, to create parity with
- `Array#including` and `Enumerable#including`. Retained the old names as aliases.
-
- *DHH*
-
-* Added `Array#including` and `Enumerable#including` to conveniently enlarge a collection with more members using a method rather than an operator:
-
- [ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ]
- post.authors.including(Current.person) # => All the authors plus the current person!
-
- *DHH*
-
-
-## Rails 6.0.0.beta3 (March 11, 2019) ##
-
-* No changes.
-
-
-## Rails 6.0.0.beta2 (February 25, 2019) ##
-
-* New autoloading based on [Zeitwerk](https://github.com/fxn/zeitwerk).
-
- *Xavier Noria*
-
-* Revise `ActiveSupport::Notifications.unsubscribe` to correctly handle Regex or other multiple-pattern subscribers.
-
- *Zach Kemp*
-
-* Add `before_reset` callback to `CurrentAttributes` and define `after_reset` as an alias of `resets` for symmetry.
-
- *Rosa Gutierrez*
-
-* Remove the `` Kernel#` `` override that suppresses ENOENT and accidentally returns nil on Unix systems.
-
- *Akinori Musha*
-
-* Add `ActiveSupport::HashWithIndifferentAccess#assoc`.
-
- `assoc` can now be called with either a string or a symbol.
-
- *Stefan Schüßler*
-
-* Add `Hash#deep_transform_values`, and `Hash#deep_transform_values!`.
-
- *Guillermo Iguaran*
-
-
-## Rails 6.0.0.beta1 (January 18, 2019) ##
-
-* Remove deprecated `Module#reachable?` method.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `#acronym_regex` method from `Inflections`.
-
- *Rafael Mendonça França*
-
-* Fix `String#safe_constantize` throwing a `LoadError` for incorrectly cased constant references.
-
- *Keenan Brock*
-
-* Preserve key order passed to `ActiveSupport::CacheStore#fetch_multi`.
-
- `fetch_multi(*names)` now returns its results in the same order as the `*names` requested, rather than returning cache hits followed by cache misses.
-
- *Gannon McGibbon*
-
-* If the same block is `included` multiple times for a Concern, an exception is no longer raised.
-
- *Mark J. Titorenko*, *Vlad Bokov*
-
-* Fix bug where `#to_options` for `ActiveSupport::HashWithIndifferentAccess`
- would not act as alias for `#symbolize_keys`.
-
- *Nick Weiland*
-
-* Improve the logic that detects non-autoloaded constants.
-
- *Jan Habermann*, *Xavier Noria*
-
-* Deprecate `ActiveSupport::Multibyte::Unicode#pack_graphemes(array)` and `ActiveSuppport::Multibyte::Unicode#unpack_graphemes(string)`
- in favor of `array.flatten.pack("U*")` and `string.scan(/\X/).map(&:codepoints)`, respectively.
-
- *Francesco Rodríguez*
-
-* Deprecate `ActiveSupport::Multibyte::Chars.consumes?` in favor of `String#is_utf8?`.
-
- *Francesco Rodríguez*
-
-* Fix duration being rounded to a full second.
- ```
- time = DateTime.parse("2018-1-1")
- time += 0.51.seconds
- ```
- Will now correctly add 0.51 second and not 1 full second.
-
- *Edouard Chin*
-
-* Deprecate `ActiveSupport::Multibyte::Unicode#normalize` and `ActiveSuppport::Multibyte::Chars#normalize`
- in favor of `String#unicode_normalize`
-
- *Francesco Rodríguez*
-
-* Deprecate `ActiveSupport::Multibyte::Unicode#downcase/upcase/swapcase` in favor of
- `String#downcase/upcase/swapcase`.
-
- *Francesco Rodríguez*
-
-* Add `ActiveSupport::ParameterFilter`.
-
- *Yoshiyuki Kinjo*
-
-* Rename `Module#parent`, `Module#parents`, and `Module#parent_name` to
- `module_parent`, `module_parents`, and `module_parent_name`.
-
- *Gannon McGibbon*
-
-* Deprecate the use of `LoggerSilence` in favor of `ActiveSupport::LoggerSilence`
-
- *Edouard Chin*
-
-* Deprecate using negative limits in `String#first` and `String#last`.
-
- *Gannon McGibbon*, *Eric Turner*
-
-* Fix bug where `#without` for `ActiveSupport::HashWithIndifferentAccess` would fail
- with symbol arguments
-
- *Abraham Chan*
-
-* Treat `#delete_prefix`, `#delete_suffix` and `#unicode_normalize` results as non-`html_safe`.
- Ensure safety of arguments for `#insert`, `#[]=` and `#replace` calls on `html_safe` Strings.
-
- *Janosch Müller*
-
-* Changed `ActiveSupport::TaggedLogging.new` to return a new logger instance instead
- of mutating the one received as parameter.
-
- *Thierry Joyal*
-
-* Define `unfreeze_time` as an alias of `travel_back` in `ActiveSupport::Testing::TimeHelpers`.
-
- The alias is provided for symmetry with `freeze_time`.
-
- *Ryan Davidson*
-
-* Add support for tracing constant autoloads. Just throw
-
- ActiveSupport::Dependencies.logger = Rails.logger
- ActiveSupport::Dependencies.verbose = true
-
- in an initializer.
-
- *Xavier Noria*
-
-* Maintain `html_safe?` on html_safe strings when sliced.
-
- string = "<div>test</div>".html_safe
- string[-1..1].html_safe? # => true
-
- *Elom Gomez*, *Yumin Wong*
-
-* Add `Array#extract!`.
-
- The method removes and returns the elements for which the block returns a true value.
- If no block is given, an Enumerator is returned instead.
-
- numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5, 7, 9]
- numbers # => [0, 2, 4, 6, 8]
-
- *bogdanvlviv*
-
-* Support not to cache `nil` for `ActiveSupport::Cache#fetch`.
-
- cache.fetch('bar', skip_nil: true) { nil }
- cache.exist?('bar') # => false
-
- *Martin Hong*
-
-* Add "event object" support to the notification system.
- Before this change, end users were forced to create hand made artisanal
- event objects on their own, like this:
-
- 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
-
- After this change, if the block passed to `subscribe` only takes one
- parameter, the framework will yield an event object to the block. Now
- end users are no longer required to make their own:
-
- ActiveSupport::Notifications.subscribe('wait') do |event|
- @event = event
- end
-
- ActiveSupport::Notifications.instrument('wait') do
- sleep 1
- end
-
- p @event.allocations # => 7
- p @event.cpu_time # => 0.256
- p @event.idle_time # => 1003.2399
-
- Now you can enjoy event objects without making them yourself. Neat!
-
- *Aaron "t.lo" Patterson*
-
-* Add cpu_time, idle_time, and allocations to Event.
-
- *Eileen M. Uchitelle*, *Aaron Patterson*
-
-* RedisCacheStore: support key expiry in increment/decrement.
-
- Pass `:expires_in` to `#increment` and `#decrement` to set a Redis EXPIRE on the key.
-
- If the key is already set to expire, RedisCacheStore won't extend its expiry.
-
- Rails.cache.increment("some_key", 1, expires_in: 2.minutes)
-
- *Jason Lee*
-
-* Allow `Range#===` and `Range#cover?` on Range.
-
- `Range#cover?` can now accept a range argument like `Range#include?` and
- `Range#===`. `Range#===` works correctly on Ruby 2.6. `Range#include?` is moved
- into a new file, with these two methods.
-
- *Requiring active_support/core_ext/range/include_range is now deprecated.*
- *Use `require "active_support/core_ext/range/compare_range"` instead.*
-
- *utilum*
-
-* Add `index_with` to Enumerable.
-
- Allows creating a hash from an enumerable with the value from a passed block
- or a default argument.
-
- %i( title body ).index_with { |attr| post.public_send(attr) }
- # => { title: "hey", body: "what's up?" }
-
- %i( title body ).index_with(nil)
- # => { title: nil, body: nil }
-
- Closely linked with `index_by`, which creates a hash where the keys are extracted from a block.
-
- *Kasper Timm Hansen*
-
-* Fix bug where `ActiveSupport::TimeZone.all` would fail when tzinfo data for
- any timezone defined in `ActiveSupport::TimeZone::MAPPING` is missing.
-
- *Dominik Sander*
-
-* Redis cache store: `delete_matched` no longer blocks the Redis server.
- (Switches from evaled Lua to a batched SCAN + DEL loop.)
-
- *Gleb Mazovetskiy*
-
-* Fix bug where `ActiveSupport::Cache` will massively inflate the storage
- size when compression is enabled (which is true by default). This patch
- does not attempt to repair existing data: please manually flush the cache
- to clear out the problematic entries.
-
- *Godfrey Chan*
-
-* Fix bug where `URI.unescape` would fail with mixed Unicode/escaped character input:
-
- URI.unescape("\xe3\x83\x90") # => "バ"
- URI.unescape("%E3%83%90") # => "バ"
- URI.unescape("\xe3\x83\x90%E3%83%90") # => Encoding::CompatibilityError
-
- *Ashe Connor*, *Aaron Patterson*
-
-* Add `before?` and `after?` methods to `Date`, `DateTime`,
- `Time`, and `TimeWithZone`.
-
- *Nick Holden*
-
-* `ActiveSupport::Inflector#ordinal` and `ActiveSupport::Inflector#ordinalize` now support
- translations through I18n.
-
- # locale/fr.rb
-
- {
- fr: {
- number: {
- nth: {
- ordinals: lambda do |_key, number:, **_options|
- if number.to_i.abs == 1
- 'er'
- else
- 'e'
- end
- end,
-
- ordinalized: lambda do |_key, number:, **_options|
- "#{number}#{ActiveSupport::Inflector.ordinal(number)}"
- end
- }
- }
- }
- }
-
-
- *Christian Blais*
-
-* Add `:private` option to ActiveSupport's `Module#delegate`
- in order to delegate methods as private:
-
- class User < ActiveRecord::Base
- has_one :profile
- delegate :date_of_birth, to: :profile, private: true
-
- def age
- Date.today.year - date_of_birth.year
- end
- end
-
- # User.new.age # => 29
- # User.new.date_of_birth
- # => NoMethodError: private method `date_of_birth' called for #<User:0x00000008221340>
-
- *Tomas Valent*
-
-* `String#truncate_bytes` to truncate a string to a maximum bytesize without
- breaking multibyte characters or grapheme clusters like 👩‍👩‍👦‍👦.
-
- *Jeremy Daer*
-
-* `String#strip_heredoc` preserves frozenness.
-
- "foo".freeze.strip_heredoc.frozen? # => true
-
- Fixes that frozen string literals would inadvertently become unfrozen:
-
- # frozen_string_literal: true
-
- foo = <<-MSG.strip_heredoc
- la la la
- MSG
-
- foo.frozen? # => false !??
-
- *Jeremy Daer*
-
-* Rails 6 requires Ruby 2.5.0 or newer.
-
- *Jeremy Daer*, *Kasper Timm Hansen*
-
-* Adds parallel testing to Rails.
-
- Parallelize your test suite with forked processes or threads.
+ 'foobar'.truncate(5).frozen?
+ # => false
+ 'foobar'.truncate(6).frozen?
+ # => false
- *Eileen M. Uchitelle*, *Aaron Patterson*
+ *Jordan Thomas*
-Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/activesupport/CHANGELOG.md) for previous changes.
+Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/activesupport/CHANGELOG.md) for previous changes.
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index 5652f2d1cc..9fe7f8fe01 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -170,7 +170,7 @@ class Module
# The target method must be public, otherwise it will raise +NoMethodError+.
def delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil)
unless to
- raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter)."
+ raise ArgumentError, "Delegation needs a target. Supply a keyword argument 'to' (e.g. delegate :hello, to: :greeter)."
end
if prefix == true && /^[^a-z_]/.match?(to)
diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb
index df0e79afa8..7f28bd52f2 100644
--- a/activesupport/lib/active_support/core_ext/string/filters.rb
+++ b/activesupport/lib/active_support/core_ext/string/filters.rb
@@ -75,7 +75,7 @@ class String
length_with_room_for_omission
end
- "#{self[0, stop]}#{omission}"
+ +"#{self[0, stop]}#{omission}"
end
# Truncates +text+ to at most <tt>bytesize</tt> bytes in length without
diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb
index 7271ab565b..16db547016 100644
--- a/activesupport/lib/active_support/deprecation.rb
+++ b/activesupport/lib/active_support/deprecation.rb
@@ -35,7 +35,7 @@ module ActiveSupport
# and the second is a library name.
#
# ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
- def initialize(deprecation_horizon = "6.1", gem_name = "Rails")
+ def initialize(deprecation_horizon = "6.2", gem_name = "Rails")
self.gem_name = gem_name
self.deprecation_horizon = deprecation_horizon
# By default, warnings are not silenced and debugging is off.
diff --git a/activesupport/lib/active_support/gem_version.rb b/activesupport/lib/active_support/gem_version.rb
index cf17922d7d..b1ac3e7969 100644
--- a/activesupport/lib/active_support/gem_version.rb
+++ b/activesupport/lib/active_support/gem_version.rb
@@ -8,9 +8,9 @@ module ActiveSupport
module VERSION
MAJOR = 6
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ 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 42ae7e9b7b..5981763f0e 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -69,7 +69,7 @@ module ActiveSupport
super()
update(constructor)
- hash = constructor.to_hash
+ hash = constructor.is_a?(Hash) ? constructor : constructor.to_hash
self.default = hash.default if hash.default
self.default_proc = hash.default_proc if hash.default_proc
else
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index d9e93b530c..555c0ad1d3 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -38,6 +38,19 @@ module ActiveSupport
# payload # => Hash, the payload
# end
#
+ # Here, the +start+ and +finish+ values represent wall-clock time. If you are
+ # concerned about accuracy, you can register a monotonic subscriber.
+ #
+ # ActiveSupport::Notifications.monotonic_subscribe('render') do |name, start, finish, id, payload|
+ # name # => String, name of the event (such as 'render' from above)
+ # start # => Monotonic time, when the instrumented block started execution
+ # finish # => Monotonic time, when the instrumented block ended execution
+ # id # => String, unique ID for the instrumenter that fired the event
+ # payload # => Hash, the payload
+ # end
+ #
+ # The +start+ and +finish+ values above represent monotonic time.
+ #
# For instance, let's store all "render" events in an array:
#
# events = []
@@ -135,6 +148,16 @@ module ActiveSupport
# during the execution of the block. The callback is unsubscribed automatically
# after that.
#
+ # To record +started+ and +finished+ values with monotonic time,
+ # specify the optional <tt>:monotonic</tt> option to the
+ # <tt>subscribed</tt> method. The <tt>:monotonic</tt> option is set
+ # to +false+ by default.
+ #
+ # callback = lambda {|name, started, finished, unique_id, payload| ... }
+ # ActiveSupport::Notifications.subscribed(callback, "sql.active_record", monotonic: true) do
+ # ...
+ # end
+ #
# === Manual Unsubscription
#
# The +subscribe+ method returns a subscriber object:
@@ -209,11 +232,17 @@ module ActiveSupport
# @event = event
# end
def subscribe(*args, &block)
- notifier.subscribe(*args, &block)
+ pattern, callback = *args
+ notifier.subscribe(pattern, callback, false, &block)
+ end
+
+ def monotonic_subscribe(*args, &block)
+ pattern, callback = *args
+ notifier.subscribe(pattern, callback, true, &block)
end
- def subscribed(callback, *args, &block)
- subscriber = subscribe(*args, &callback)
+ def subscribed(callback, pattern, monotonic: false, &block)
+ subscriber = notifier.subscribe(pattern, callback, monotonic)
yield
ensure
unsubscribe(subscriber)
diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb
index 8812b67f63..c37bec4ee5 100644
--- a/activesupport/lib/active_support/notifications/fanout.rb
+++ b/activesupport/lib/active_support/notifications/fanout.rb
@@ -20,8 +20,8 @@ module ActiveSupport
super
end
- def subscribe(pattern = nil, callable = nil, &block)
- subscriber = Subscribers.new(pattern, callable || block)
+ def subscribe(pattern = nil, callable = nil, monotonic = false, &block)
+ subscriber = Subscribers.new(monotonic, pattern, callable || block)
synchronize do
if String === pattern
@string_subscribers[pattern] << subscriber
@@ -84,8 +84,8 @@ module ActiveSupport
end
module Subscribers # :nodoc:
- def self.new(pattern, listener)
- subscriber_class = Timed
+ def self.new(monotonic, pattern, listener)
+ subscriber_class = monotonic ? MonotonicTimed : Timed
if listener.respond_to?(:start) && listener.respond_to?(:finish)
subscriber_class = Evented
@@ -180,12 +180,29 @@ module ActiveSupport
def start(name, id, payload)
timestack = Thread.current[:_timestack] ||= []
- timestack.push Concurrent.monotonic_time
+ timestack.push Time.now
end
def finish(name, id, payload)
timestack = Thread.current[:_timestack]
started = timestack.pop
+ @delegate.call(name, started, Time.now, id, payload)
+ end
+ end
+
+ class MonotonicTimed < Evented # :nodoc:
+ def publish(name, *args)
+ @delegate.call name, *args
+ end
+
+ def start(name, id, payload)
+ timestack = Thread.current[:_timestack_monotonic] ||= []
+ timestack.push Concurrent.monotonic_time
+ end
+
+ def finish(name, id, payload)
+ timestack = Thread.current[:_timestack_monotonic]
+ started = timestack.pop
@delegate.call(name, started, Concurrent.monotonic_time, id, payload)
end
end
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index 4ffa33aa61..c5a000b67a 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -291,6 +291,11 @@ class StringInflectionsTest < ActiveSupport::TestCase
assert_equal "Hello Big[...]", "Hello Big World!".truncate(15, omission: "[...]", separator: /\s/)
end
+ def test_truncate_returns_frozen_string
+ assert_not "Hello World!".truncate(12).frozen?
+ assert_not "Hello World!!".truncate(12).frozen?
+ end
+
def test_truncate_bytes
assert_equal "👍👍👍👍", "👍👍👍👍".truncate_bytes(16)
assert_equal "👍👍👍👍", "👍👍👍👍".truncate_bytes(16, omission: nil)
diff --git a/activesupport/test/hash_with_indifferent_access_test.rb b/activesupport/test/hash_with_indifferent_access_test.rb
index 1d6a07ec5f..af96231801 100644
--- a/activesupport/test/hash_with_indifferent_access_test.rb
+++ b/activesupport/test/hash_with_indifferent_access_test.rb
@@ -836,4 +836,32 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase
assert_equal 3, hash_wia[:foo]
assert_equal 3, hash_wia[:bar]
end
+
+ def test_should_copy_the_default_when_converting_non_hash_to_hash_with_indifferent_access
+ non_hash = Object.new
+
+ def non_hash.to_hash
+ h = { foo: :bar }
+ h.default = :baz
+ h
+ end
+
+ hash_wia = HashWithIndifferentAccess.new(non_hash)
+ assert_equal :bar, hash_wia[:foo]
+ assert_equal :baz, hash_wia[:missing]
+ end
+
+ def test_should_copy_the_default_proc_when_converting_non_hash_to_hash_with_indifferent_access
+ non_hash = Object.new
+
+ def non_hash.to_hash
+ h = { foo: :bar }
+ h.default_proc = ->(hash, key) { hash[key] = :baz }
+ h
+ end
+
+ hash_wia = HashWithIndifferentAccess.new(non_hash)
+ assert_equal :bar, hash_wia[:foo]
+ assert_equal :baz, hash_wia[:missing]
+ end
end
diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb
index 0af59764b5..3b98749f1b 100644
--- a/activesupport/test/notifications_test.rb
+++ b/activesupport/test/notifications_test.rb
@@ -62,6 +62,38 @@ module Notifications
end
end
+ class TimedAndMonotonicTimedSubscriberTest < TestCase
+ def test_subscribe
+ event_name = "foo"
+ class_of_started = nil
+ class_of_finished = nil
+
+ ActiveSupport::Notifications.subscribe(event_name) do |name, started, finished, unique_id, data|
+ class_of_started = started.class
+ class_of_finished = finished.class
+ end
+
+ ActiveSupport::Notifications.instrument(event_name)
+
+ assert_equal [Time, Time], [class_of_started, class_of_finished]
+ end
+
+ def test_monotonic_subscribe
+ event_name = "foo"
+ class_of_started = nil
+ class_of_finished = nil
+
+ ActiveSupport::Notifications.monotonic_subscribe(event_name) do |name, started, finished, unique_id, data|
+ class_of_started = started.class
+ class_of_finished = finished.class
+ end
+
+ ActiveSupport::Notifications.instrument(event_name)
+
+ assert_equal [Float, Float], [class_of_started, class_of_finished]
+ end
+ end
+
class SubscribedTest < TestCase
def test_subscribed
name = "foo"
@@ -95,6 +127,42 @@ module Notifications
ensure
ActiveSupport::Notifications.notifier = old_notifier
end
+
+ def test_timed_subscribed
+ event_name = "foo"
+ class_of_started = nil
+ class_of_finished = nil
+ callback = lambda do |name, started, finished, unique_id, data|
+ class_of_started = started.class
+ class_of_finished = finished.class
+ end
+
+ ActiveSupport::Notifications.subscribed(callback, event_name) do
+ ActiveSupport::Notifications.instrument(event_name)
+ end
+
+ ActiveSupport::Notifications.instrument(event_name)
+
+ assert_equal [Time, Time], [class_of_started, class_of_finished]
+ end
+
+ def test_monotonic_timed_subscribed
+ event_name = "foo"
+ class_of_started = nil
+ class_of_finished = nil
+ callback = lambda do |name, started, finished, unique_id, data|
+ class_of_started = started.class
+ class_of_finished = finished.class
+ end
+
+ ActiveSupport::Notifications.subscribed(callback, event_name, monotonic: true) do
+ ActiveSupport::Notifications.instrument(event_name)
+ end
+
+ ActiveSupport::Notifications.instrument(event_name)
+
+ assert_equal [Float, Float], [class_of_started, class_of_finished]
+ end
end
class UnsubscribeTest < TestCase
@@ -302,7 +370,7 @@ module Notifications
class EventTest < TestCase
def test_events_are_initialized_with_details
- time = Concurrent.monotonic_time
+ time = Time.now
event = event(:foo, time, time + 0.01, random_id, {})
assert_equal :foo, event.name
@@ -310,15 +378,13 @@ module Notifications
assert_in_delta 10.0, event.duration, 0.00001
end
- def test_event_cpu_time_and_idle_time_when_start_and_finish_is_not_called
- time = Concurrent.monotonic_time
+ def test_event_cpu_time_does_not_raise_error_when_start_or_finished_not_called
+ time = Time.now
event = event(:foo, time, time + 0.01, random_id, {})
assert_equal 0, event.cpu_time
- assert_in_delta 10.0, event.idle_time, 0.00001
end
-
def test_events_consumes_information_given_as_payload
event = event(:foo, Concurrent.monotonic_time, Concurrent.monotonic_time + 1, random_id, payload: :bar)
assert_equal Hash[payload: :bar], event.payload