* 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 = "
test
".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 # *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.4.1 or newer. *Jeremy Daer* * Adds parallel testing to Rails. Parallelize your test suite with forked processes or threads. *Eileen M. Uchitelle*, *Aaron Patterson* Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/activesupport/CHANGELOG.md) for previous changes.