Skip to content

Commit

Permalink
Support before_reset callback in CurrentAttributes
Browse files Browse the repository at this point in the history
This is useful when we need to do some work associated to `Current.reset`
but that work depends on the values of the current attributes themselves.
This cannot be done in the supported `resets` callback because when the
block is executed, CurrentAttributes's instance has already been reset.

For symmetry, `after_reset` is defined as alias of `resets`.
  • Loading branch information
rosa committed Jan 30, 2019
1 parent 9bb07b7 commit 2ce8455
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 8 deletions.
5 changes: 4 additions & 1 deletion activesupport/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
* Add `before_reset` callback to `CurrentAttributes` and define `after_reset` as an alias of `resets` for symmetry.

*Rosa Gutierrez*

* Add `ActiveSupport::HashWithIndifferentAccess#assoc`.

`assoc` can now be called with either a string or a symbol.

*Stefan Schüßler*


## Rails 6.0.0.beta1 (January 18, 2019) ##

* Remove deprecated `Module#reachable?` method.
Expand Down
6 changes: 6 additions & 0 deletions activesupport/lib/active_support/current_attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,16 @@ def attribute(*names)
end
end

# Calls this block before #reset is called on the instance. Used for resetting external collaborators that depend on current values.
def before_reset(&block)
set_callback :reset, :before, &block
end

# Calls this block after #reset is called on the instance. Used for resetting external collaborators, like Time.zone.
def resets(&block)
set_callback :reset, :after, &block
end
alias_method :after_reset, :resets

delegate :set, :reset, to: :instance

Expand Down
37 changes: 30 additions & 7 deletions activesupport/test/current_attributes_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
require "abstract_unit"

class CurrentAttributesTest < ActiveSupport::TestCase
Person = Struct.new(:name, :time_zone)
Person = Struct.new(:id, :name, :time_zone)

class Current < ActiveSupport::CurrentAttributes
attribute :world, :account, :person, :request
delegate :time_zone, to: :person

resets { Time.zone = "UTC" }
before_reset { Session.previous = person.try(:id) }

resets do
Time.zone = "UTC"
Session.current = nil
end

def account=(account)
super
Expand All @@ -19,6 +24,7 @@ def account=(account)
def person=(person)
super
Time.zone = person.try(:time_zone)
Session.current = person.try(:id)
end

def request
Expand All @@ -30,9 +36,14 @@ def intro
end
end

class Session < ActiveSupport::CurrentAttributes
attribute :current, :previous
end

setup do
@original_time_zone = Time.zone
Current.reset
Session.reset
end

teardown do
Expand All @@ -56,16 +67,28 @@ def intro
end

test "set auxiliary class via overwritten method" do
Current.person = Person.new("David", "Central Time (US & Canada)")
Current.person = Person.new(42, "David", "Central Time (US & Canada)")
assert_equal "Central Time (US & Canada)", Time.zone.name
assert_equal 42, Session.current
end

test "resets auxiliary class via callback" do
Current.person = Person.new("David", "Central Time (US & Canada)")
test "resets auxiliary classes via callback" do
Current.person = Person.new(42, "David", "Central Time (US & Canada)")
assert_equal "Central Time (US & Canada)", Time.zone.name

Current.reset
assert_equal "UTC", Time.zone.name
assert_nil Session.current
end

test "set auxiliary class based on current attributes via before callback" do
Current.person = Person.new(42, "David", "Central Time (US & Canada)")
assert_nil Session.previous
assert_equal 42, Session.current

Current.reset
assert_equal 42, Session.previous
assert_nil Session.current
end

test "set attribute only via scope" do
Expand All @@ -92,13 +115,13 @@ def intro
end

test "delegation" do
Current.person = Person.new("David", "Central Time (US & Canada)")
Current.person = Person.new(42, "David", "Central Time (US & Canada)")
assert_equal "Central Time (US & Canada)", Current.time_zone
assert_equal "Central Time (US & Canada)", Current.instance.time_zone
end

test "all methods forward to the instance" do
Current.person = Person.new("David", "Central Time (US & Canada)")
Current.person = Person.new(42, "David", "Central Time (US & Canada)")
assert_equal "David, in Central Time (US & Canada)", Current.intro
assert_equal "David, in Central Time (US & Canada)", Current.instance.intro
end
Expand Down

0 comments on commit 2ce8455

Please sign in to comment.