The ability to track whether or not your active record objects have been modified or not becomes a lot easier with the new dirty object functionality of edge rails. It’s dead simple, and pretty slick, to use:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
article = Article.find(:first) article.changed? #=> false # Track changes to individual attributes with # attr_name_changed? accessor article.title #=> "Title" article.title = "New Title" article.title_changed? #=> true # Access previous value with attr_name_was accessor article.title_was #=> "Title" # See both previous and current value with attr_name_change accessor article.title_change #=> ["Title", "New Title"] |
Beyond the clever attribute based accessor methods, you can also query to object directly for its list of all changed attributes. (Continuing from the previous example):
1 2 3 4 5 |
# Get a list of changed attributes article.changed #=> ['title'] # Get the hash of changed attributes and their previous and current values article.changes #=> { 'title' => ["Title", "New Title"] } |
Note: Once you save a dirty object it clears out its changed state tracking and is once again considered unchanged.
1 2 3 |
article.changed? #=> true article.save #=> true article.changed? #=> false |
If you’re going to be modifying an attribute outside of the attr= writer, you can use attr_name_will_change! to tell the object to be aware of the change:
1 2 3 4 |
article = Article.find(:first) article.title_will_change! article.title.upcase! article.title_change #=> ['Title', 'TITLE'] |
And coming down the pipe is a feature that will make the most of this functionality – partial SQL updates that will only update attributes that have changed…
tags: ruby, rubyonrails

Partial updates are already in edge :)
What is the benefit of partial updates?
Jason: just imagine a model with large text content attributes, or god forbid, a blob.
I’m surprised that it took this long. Partial updates are much faster, cleaner, and easier to understand (let alone the most efficient way to update a table and avoid some locking mechanisms). Wether it is digging through logs, checking if you updated something that needs to flush the cache, or any other reason, this is has been a long time coming. Glad to see some much needed progress in this area finally committed.
That’s cool, though your last example is a little scary. That’s going to potentially be really hard to remember to have to flag attributes as dirty in those cases. Not that it happens a lot, but that will just make it all the more frustrating to debug…
great! :)
I think Don is right! Will I have to flag any change I made to field without the field= method?! That would cause really strange and undebuggable errors when I miss it.
On the other hand this is a cool feature. Maybe I just missed a point or it needs a little bit more tweaking? And: What happens when I do this:
article = Article.find(:first) article.title #=> “Test” article.title = “Test”
What would:
article.title_changed?
return?
AAAARG! ;)
article = Article.find(:first)
article.title #=> “Test”
article.title = “Test”
Pratik: Yes, I know partial updates are already here – I was trying to setup a little teaser for my readers whilst I labored on that article :)
Thorben: This functionality is smart enough to only mark a field as changed when its values are different. So your example will return false:
Get this functionality in your 2.0 app.
http://code.bitsweat.net/svn/dirty
But what about this?
After looking at the code in the plugin Bryan posted a link to, it’s pretty obvious that my example would return false, which is why the `will_change!` flag is necessary. Why not have the `changed?` methods and `save` methods check to see if the value is different, rather than have the setters keep track of things that changed? Then you don’t have to worry about the flag, if the value is different, no matter how it changed, `changed?` returns the right value.
Paul: You would then have to store all the original attributes twice in memory. Don’t know if that’s the reason, but it would be rather ugly.
article.title.changed? and article.title.was would have been much cleaner syntax than underscore accessor.
“would have been much cleaner syntax”
What makes it cleaner? There isn’t anything wrong with underscores. You’re asking the parent object if attribute x has changed, not asking the attribute itself if it’s changed, so your suggested syntax isn’t semantically correct.
You may now use Article.first instead of Article.find(:first) ! :)
Daniel: Yeah, I guess that’s the tradoff. I consider having to set the flag pretty ugly as well.
What would happend with partial updates at cases like Paul’s example?
@Luke… okay, I see your point, that is technically true, but Mike’s approach seems much more readable to me too, since dots are definitely always separators, while with underscores you have to stop and think about it. Perhaps just because this is new… maybe when I’m more used to it I’ll come around to seeing it your way.
That’s great, but can I selectively turn it off? What if I have an attribute which contains a large file blob? I don’t want to have it in memory twice.
I think this breaks acts_as_versioned
Agreed, this seems to break acts_as_versioned.
NoMethodError: You have a nil object when you didn’t expect it! You might have expected an instance of Array. The error occurred while evaluating nil.include? from /www/trunk/vendor/rails/activerecord/lib/active_record/dirty.rb:118:in `write_attribute’ from /www/trunk/vendor/rails/activerecord/lib/active_record/attribute_methods.rb:200:in `challenge_id=’ from /www/trunk/vendor/rails/activerecord/lib/active_record/attribute_methods.rb:229:in `send’ from /www/trunk/vendor/rails/activerecord/lib/active_record/attribute_methods.rb:229:in `method_missing’ from (irb):2
Looks great. Can’t wait to use this feature. Doing a quick threat-check for acts_as_versioned in our code and did find ... if const.respond_to? :versions #test for acts_as_versioned which I hope suggests there is a means to identify and work around acts_as_versioned issues
Some of this stuff required some ugly interceptor work in Hibernate! Especially those times when you have requirements to the effect of needing to do calculations between a user’s input value and a previous one.
Those objects look a lot cleaner than some of the objects I’ve seen (PHP). You rails guys seem to be a lot cleaner than PHP developers.
I really don’t like the will_change! kludge. It seems to go against the principles of DRY.
(Repost for better formatting)
rihad +1. title_changed_changed is awkward. Luke Redpath -1. “Changed” is flag that belong to attribute(column) here, not to whole object (record). So (IMHO) article.title.changed? is absolutely right semantic.
About will_change! – it is bad. Think about why it is there? just to cover alternative way to modify attribute. What if tomorrow it will be ANOTHER way to modify attribute, something like AOP or else. In environment, where setter is NOT in full control of attribute there is no way to reliable implement “changed?” without caching original value. (Getting it from database per demand would be another way, but it is slightly different – for example it returns changed if database was modified by another transaction).
Thus, I would be looking for the following setup switch:
Mode-1 Old-way: no original value caching – good for memory, easy to code, consistent, hard on db connection because of full save. “changed?” is optional, not reliable, not used during “save”
Mode-2 Consistent dirty check. Full caching of original values – hard on memory. “Changed?” is reliable and used for partial save – good for DB connection performance.
Mode-3 Inconsistent dirty check with flags – as implemented today. Good for memory, good for performance. Unreliable behaviour unless specially coded (“will_change!”). Unreliable behaviour in AOP future (my guess).
Sorry, I did not checked yet if there is possibilities to switch between modes. Mode-3 is not my type of doing business, but if somebody will use it with FULL understanding of its limitations – good luck.
@Josh Martin
This took so long because of the following problem: Say you have a model, which requires that field_a and field_b cannot both be true (although they can both be false, so they cannot be conflated into one single boolean). Suppose one update changes field_a to true, and another independent update changes field_b to true.
As I understand it, validations would not have caught this.
I’m not sure how they solved this for 2.1.
My App is on 1.2.6, Cannot change it to 2.1 yet. What is the best way to have similar functionality on my current version. I need to implement only ‘changed?’ method. Only solution that comes to my mind is to re-read the record in changed? method and compare. Is that is how it is implemented in 2.1 or there is any other smarter way?
@vinay – check out the dirty plugin. Not sure it can be used on Rails v1.2.x but it might be an easy tweak for you?
Nice Site! http://google.com
Wonderful, the power of simplicity. We had used the dirty plugin before, but that has the nasty habit of freezing objects at times.
Thing to note.
If you do article.title = ‘NEW TITLE’, it is considered as changed.
If you do article.title.upcase, it is considered not changed.
This is a great addition!
Hi!
We have a legacy DB with a column named “changed”. How to read this cilumn with rails 2.1? Any suggestions?