Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 202 lines (184 sloc) 5.903 kb
c3cea9b @jeremy Fix unstated dep on HWIA
jeremy authored
1 require 'active_support/hash_with_indifferent_access'
3b1c69d @fxn adds a few requires in active_model/dirty.rb
fxn authored
2 require 'active_support/core_ext/object/duplicable'
c3cea9b @jeremy Fix unstated dep on HWIA
jeremy authored
3
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
4 module ActiveModel
c9a88a2 @frodsan minor edits in AM documentation [ci skip]
frodsan authored
5 # == Active \Model \Dirty
716c243 @rizwanreza Minor changes to active_model/callbacks.rb and dirty.rb
rizwanreza authored
6 #
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -exec ...
spastorino authored
7 # Provides a way to track changes in your object in the same way as
716c243 @rizwanreza Minor changes to active_model/callbacks.rb and dirty.rb
rizwanreza authored
8 # Active Record does.
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -exec ...
spastorino authored
9 #
a43abfa Rewording
Oge Nnadi authored
10 # The requirements for implementing ActiveModel::Dirty are:
dba196c @lifo Merge docrails
lifo authored
11 #
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
12 # * <tt>include ActiveModel::Dirty</tt> in your object.
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -exec ...
spastorino authored
13 # * Call <tt>define_attribute_methods</tt> passing each method you want to
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
14 # track.
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -exec ...
spastorino authored
15 # * Call <tt>attr_name_will_change!</tt> before each change to the tracked
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
16 # attribute.
9aa1a3d @rafaelfranca Merge pull request #10816 from bogdan/less-dirty-dirty
rafaelfranca authored
17 # * Call <tt>changes_applied</tt> after the changes are persisted.
089e1b6 @rafaelfranca Document reset_changes since it is part of public API
rafaelfranca authored
18 # * Call <tt>reset_changes</tt> when you want to reset the changes
19 # information.
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -exec ...
spastorino authored
20 #
dba196c @lifo Merge docrails
lifo authored
21 # A minimal implementation could be:
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -exec ...
spastorino authored
22 #
dba196c @lifo Merge docrails
lifo authored
23 # class Person
24 # include ActiveModel::Dirty
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -exec ...
spastorino authored
25 #
00c94d7 @frodsan updating define_attribute_methods documentation
frodsan authored
26 # define_attribute_methods :name
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -exec ...
spastorino authored
27 #
dba196c @lifo Merge docrails
lifo authored
28 # def name
29 # @name
30 # end
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -exec ...
spastorino authored
31 #
dba196c @lifo Merge docrails
lifo authored
32 # def name=(val)
2c8a4a5 @toretore Remove or fix non-working examples and add a few tests to Dirty [#5185 s...
toretore authored
33 # name_will_change! unless val == @name
dba196c @lifo Merge docrails
lifo authored
34 # @name = val
35 # end
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -exec ...
spastorino authored
36 #
dba196c @lifo Merge docrails
lifo authored
37 # def save
9aa1a3d @rafaelfranca Merge pull request #10816 from bogdan/less-dirty-dirty
rafaelfranca authored
38 # # do persistence work
39 # changes_applied
dba196c @lifo Merge docrails
lifo authored
40 # end
089e1b6 @rafaelfranca Document reset_changes since it is part of public API
rafaelfranca authored
41 #
42 # def reload!
43 # reset_changes
44 # end
dba196c @lifo Merge docrails
lifo authored
45 # end
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -exec ...
spastorino authored
46 #
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
47 # A newly instantiated object is unchanged:
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
48 #
04cda18 @rubys Prefer find_by over dynamic finders in rdoc
rubys authored
49 # person = Person.find_by(name: 'Uncle Bob')
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
50 # person.changed? # => false
51 #
52 # Change the name:
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
53 #
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
54 # person.name = 'Bob'
55 # person.changed? # => true
56 # person.name_changed? # => true
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
57 # person.name_was # => "Uncle Bob"
58 # person.name_change # => ["Uncle Bob", "Bob"]
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
59 # person.name = 'Bill'
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
60 # person.name_change # => ["Uncle Bob", "Bill"]
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
61 #
62 # Save the changes:
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
63 #
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
64 # person.save
65 # person.changed? # => false
66 # person.name_changed? # => false
67 #
089e1b6 @rafaelfranca Document reset_changes since it is part of public API
rafaelfranca authored
68 # Reset the changes:
69 #
70 # person.previous_changes # => {"name" => ["Uncle Bob", "Bill"]}
ed0b080 @rafaelfranca Fix the documentation method.
rafaelfranca authored
71 # person.reload!
089e1b6 @rafaelfranca Document reset_changes since it is part of public API
rafaelfranca authored
72 # person.previous_changes # => {}
73 #
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
74 # Assigning the same value leaves the attribute unchanged:
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
75 #
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
76 # person.name = 'Bill'
77 # person.name_changed? # => false
78 # person.name_change # => nil
79 #
80 # Which attributes have changed?
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
81 #
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
82 # person.name = 'Bob'
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
83 # person.changed # => ["name"]
84 # person.changes # => {"name" => ["Bill", "Bob"]}
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
85 #
1a6d762 @neerajdotname expanding on the will_change! method in documentation
neerajdotname authored
86 # If an attribute is modified in-place then make use of <tt>[attribute_name]_will_change!</tt>
bc818e4 @frodsan update ActiveModel::Errors documentation and minor fixes [ci skip]
frodsan authored
87 # to mark that the attribute is changing. Otherwise ActiveModel can't track
88 # changes to in-place attributes.
1a6d762 @neerajdotname expanding on the will_change! method in documentation
neerajdotname authored
89 #
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
90 # person.name_will_change!
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
91 # person.name_change # => ["Bill", "Bill"]
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
92 # person.name << 'y'
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
93 # person.name_change # => ["Bill", "Billy"]
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
94 module Dirty
95 extend ActiveSupport::Concern
96 include ActiveModel::AttributeMethods
97
98 included do
99 attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
eebb9dd @patricksrobertson Convert ActiveModel to 1.9 hash syntax.
patricksrobertson authored
100 attribute_method_affix prefix: 'reset_', suffix: '!'
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
101 end
102
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
103 # Returns +true+ if any attribute have unsaved changes, +false+ otherwise.
104 #
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
105 # person.changed? # => false
106 # person.name = 'bob'
107 # person.changed? # => true
108 def changed?
5e70522 Changing active model dirty module helper method to more appropriate met...
Prasath Venkatraman authored
109 changed_attributes.present?
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
110 end
111
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
112 # Returns an array with the name of the attributes with unsaved changes.
113 #
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
114 # person.changed # => []
115 # person.name = 'bob'
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
116 # person.changed # => ["name"]
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
117 def changed
3adaef8 @spohlenz Restore changed_attributes method in ActiveModel::Dirty and loosen expec...
spohlenz authored
118 changed_attributes.keys
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
119 end
120
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
121 # Returns a hash of changed attributes indicating their original
bc818e4 @frodsan update ActiveModel::Errors documentation and minor fixes [ci skip]
frodsan authored
122 # and new values like <tt>attr => [original value, new value]</tt>.
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
123 #
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
124 # person.changes # => {}
125 # person.name = 'bob'
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
126 # person.changes # => { "name" => ["bill", "bob"] }
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
127 def changes
c8e632b @amatsuda Namespace HashWithIndifferentAccess
amatsuda authored
128 ActiveSupport::HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
129 end
130
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
131 # Returns a hash of attributes that were changed before the model was saved.
132 #
133 # person.name # => "bob"
8098943 @crankharder I added this feature so that a Map of changed fields could be retrieved
crankharder authored
134 # person.name = 'robert'
135 # person.save
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
136 # person.previous_changes # => {"name" => ["bob", "robert"]}
8098943 @crankharder I added this feature so that a Map of changed fields could be retrieved
crankharder authored
137 def previous_changes
9aa1a3d @rafaelfranca Merge pull request #10816 from bogdan/less-dirty-dirty
rafaelfranca authored
138 @previously_changed ||= {}
8098943 @crankharder I added this feature so that a Map of changed fields could be retrieved
crankharder authored
139 end
140
f87820d @frodsan update ActiveModel::Dirty documentation
frodsan authored
141 # Returns a hash of the attributes with unsaved changes indicating their original
142 # values like <tt>attr => original value</tt>.
143 #
144 # person.name # => "bob"
145 # person.name = 'robert'
146 # person.changed_attributes # => {"name" => "bob"}
6b4e0cc @fbrubacher a cloned object no longer mimics changed flags from creator , plus a tes...
fbrubacher authored
147 def changed_attributes
0e65587 @griffinmyers DirtyModel uses a hash to keep track of any changes made to attributes
griffinmyers authored
148 @changed_attributes ||= ActiveSupport::HashWithIndifferentAccess.new
6b4e0cc @fbrubacher a cloned object no longer mimics changed flags from creator , plus a tes...
fbrubacher authored
149 end
150
47617ec @tenderlove expose a few attribute changed methods
tenderlove authored
151 # Handle <tt>*_changed?</tt> for +method_missing+.
152 def attribute_changed?(attr)
153 changed_attributes.include?(attr)
154 end
3adaef8 @spohlenz Restore changed_attributes method in ActiveModel::Dirty and loosen expec...
spohlenz authored
155
47617ec @tenderlove expose a few attribute changed methods
tenderlove authored
156 # Handle <tt>*_was</tt> for +method_missing+.
157 def attribute_was(attr)
158 attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
159 end
160
161 private
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
162
9aa1a3d @rafaelfranca Merge pull request #10816 from bogdan/less-dirty-dirty
rafaelfranca authored
163 # Removes current changes and makes them accessible through +previous_changes+.
164 def changes_applied
165 @previously_changed = changes
166 @changed_attributes = {}
167 end
168
169 # Removes all dirty data: current changes and previous changes
170 def reset_changes
171 @previously_changed = {}
172 @changed_attributes = {}
173 end
174
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
175 # Handle <tt>*_change</tt> for +method_missing+.
176 def attribute_change(attr)
3adaef8 @spohlenz Restore changed_attributes method in ActiveModel::Dirty and loosen expec...
spohlenz authored
177 [changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
178 end
179
180 # Handle <tt>*_will_change!</tt> for +method_missing+.
181 def attribute_will_change!(attr)
9d1f6ed @carlosantoniodasilva Return earlier if attribute already changed in *_will_change! methods
carlosantoniodasilva authored
182 return if attribute_changed?(attr)
183
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
184 begin
185 value = __send__(attr)
186 value = value.duplicable? ? value.clone : value
187 rescue TypeError, NoMethodError
188 end
189
9d1f6ed @carlosantoniodasilva Return earlier if attribute already changed in *_will_change! methods
carlosantoniodasilva authored
190 changed_attributes[attr] = value
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
191 end
192
193 # Handle <tt>reset_*!</tt> for +method_missing+.
194 def reset_attribute!(attr)
cf7ab60 @rmascarenhas Reset attributes should not report changes.
rmascarenhas authored
195 if attribute_changed?(attr)
196 __send__("#{attr}=", changed_attributes[attr])
197 changed_attributes.delete(attr)
198 end
f97dae5 @josh Extract common dirty tracking methods in AMo
josh authored
199 end
200 end
201 end
Something went wrong with that request. Please try again.