Add dirty methods for store accessors
This commit is contained in:
parent
6b5130720e
commit
61a39ffcc6
@ -11,6 +11,10 @@ module ActiveRecord
|
||||
# of the model. This is very helpful for easily exposing store keys to a form or elsewhere that's
|
||||
# already built around just accessing attributes on the model.
|
||||
#
|
||||
# Every accessor comes with dirty tracking methods (+key_changed?+, +key_was+ and +key_change+).
|
||||
#
|
||||
# NOTE: There is no +key_will_change!+ method for accessors, use +store_will_change!+ instead.
|
||||
#
|
||||
# Make sure that you declare the database column used for the serialized store as a text, so there's
|
||||
# plenty of room.
|
||||
#
|
||||
@ -49,6 +53,12 @@ module ActiveRecord
|
||||
# u.settings[:country] # => 'Denmark'
|
||||
# u.settings['country'] # => 'Denmark'
|
||||
#
|
||||
# # Dirty tracking
|
||||
# u.color = 'green'
|
||||
# u.color_changed? # => true
|
||||
# u.color_was # => 'black'
|
||||
# u.color_change # => ['black', 'red']
|
||||
#
|
||||
# # Add additional accessors to an existing store through store_accessor
|
||||
# class SuperUser < User
|
||||
# store_accessor :settings, :privileges, :servants
|
||||
@ -127,6 +137,24 @@ def store_accessor(store_attribute, *keys, prefix: nil, suffix: nil)
|
||||
define_method(accessor_key) do
|
||||
read_store_attribute(store_attribute, key)
|
||||
end
|
||||
|
||||
define_method("#{accessor_key}_changed?") do
|
||||
return false unless attribute_changed?(store_attribute)
|
||||
prev_store, new_store = changes[store_attribute]
|
||||
prev_store&.dig(key) != new_store&.dig(key)
|
||||
end
|
||||
|
||||
define_method("#{accessor_key}_change") do
|
||||
return unless attribute_changed?(store_attribute)
|
||||
prev_store, new_store = changes[store_attribute]
|
||||
[prev_store&.dig(key), new_store&.dig(key)]
|
||||
end
|
||||
|
||||
define_method("#{accessor_key}_was") do
|
||||
return unless attribute_changed?(store_attribute)
|
||||
prev_store, _new_store = changes[store_attribute]
|
||||
prev_store&.dig(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -153,6 +153,22 @@ def test_yaml_round_trip_with_store_accessors
|
||||
assert_equal "GMT", y.timezone
|
||||
end
|
||||
|
||||
def test_changes_with_store_accessors
|
||||
x = Hstore.new(language: "de")
|
||||
assert x.language_changed?
|
||||
assert_nil x.language_was
|
||||
assert_equal [nil, "de"], x.language_change
|
||||
x.save!
|
||||
|
||||
assert_not x.language_changed?
|
||||
x.reload
|
||||
|
||||
x.settings = nil
|
||||
assert x.language_changed?
|
||||
assert_equal "de", x.language_was
|
||||
assert_equal ["de", nil], x.language_change
|
||||
end
|
||||
|
||||
def test_changes_in_place
|
||||
hstore = Hstore.create!(settings: { "one" => "two" })
|
||||
hstore.settings["three"] = "four"
|
||||
|
@ -79,6 +79,63 @@ class StoreTest < ActiveRecord::TestCase
|
||||
assert_not_predicate @john, :settings_changed?
|
||||
end
|
||||
|
||||
test "updating the store will mark accessor as changed" do
|
||||
@john.color = "red"
|
||||
assert @john.color_changed?
|
||||
end
|
||||
|
||||
test "new record and no accessors changes" do
|
||||
user = Admin::User.new
|
||||
assert_not user.color_changed?
|
||||
assert_nil user.color_was
|
||||
assert_nil user.color_change
|
||||
|
||||
user.color = "red"
|
||||
assert user.color_changed?
|
||||
assert_nil user.color_was
|
||||
assert_equal "red", user.color_change[1]
|
||||
end
|
||||
|
||||
test "updating the store won't mark accessor as changed if the whole store was updated" do
|
||||
@john.settings = { color: @john.color, some: "thing" }
|
||||
assert @john.settings_changed?
|
||||
assert_not @john.color_changed?
|
||||
end
|
||||
|
||||
test "updating the store populates the accessor changed array correctly" do
|
||||
@john.color = "red"
|
||||
assert_equal "black", @john.color_was
|
||||
assert_equal "black", @john.color_change[0]
|
||||
assert_equal "red", @john.color_change[1]
|
||||
end
|
||||
|
||||
test "updating the store won't mark accessor as changed if the value isn't changed" do
|
||||
@john.color = @john.color
|
||||
assert_not @john.color_changed?
|
||||
end
|
||||
|
||||
test "nullifying the store mark accessor as changed" do
|
||||
color = @john.color
|
||||
@john.settings = nil
|
||||
assert @john.color_changed?
|
||||
assert_equal color, @john.color_was
|
||||
assert_equal [color, nil], @john.color_change
|
||||
end
|
||||
|
||||
test "dirty methods for suffixed accessors" do
|
||||
@john.configs[:two_factor_auth] = true
|
||||
assert @john.two_factor_auth_configs_changed?
|
||||
assert_nil @john.two_factor_auth_configs_was
|
||||
assert_equal [nil, true], @john.two_factor_auth_configs_change
|
||||
end
|
||||
|
||||
test "dirty methods for prefixed accessors" do
|
||||
@john.spouse[:name] = "Lena"
|
||||
assert @john.partner_name_changed?
|
||||
assert_equal "Dallas", @john.partner_name_was
|
||||
assert_equal ["Dallas", "Lena"], @john.partner_name_change
|
||||
end
|
||||
|
||||
test "object initialization with not nullable column" do
|
||||
assert_equal true, @john.remember_login
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user