What's New in Edge Rails: Touching

Posted by ryan
at 7:51 AM on Monday, April 20, 2009

This feature was released in Rails v2.3.3

There are often times when you want an update made to one object to be reflected up the object graph as an update of an associated parent object. For instance, if a new comment is created on an article, you may very well want to mark the article as being updated. With the new touch feature of ActiveRecord, this is a whole lot easier. Using our previous example, here’s is how it works:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Article < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base

  # Make create/update/deletes of a comment mark its
  # parent article as updated
  belongs_to :article, :touch => true
end

# Adding a new comment marks the article as being updated
article.updated_at #=> "Mon Apr 20 07:42:53 -0400 2009"
article.comments.create(:body => "New comment")
article.updated_at #=> "Mon Apr 20 07:43:27 -0400 2009"

# Same for updates/deletes
article.comments.first.destroy
article.updated_at #=> "Mon Apr 20 07:45:23 -0400 2009"

This is a great way to keep tightly coupled domain models in-sync without resorting to a potential maze of callback logic.

Also, if you have a timestamp field named something other than the standard updated_at or updated_on you can explicitly specify that field as the value to the :touch option and it will get marked instead:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Article < ActiveRecord::Base
  has_many :comments
  validates_presence_of :last_updated_at  # non-standard
end

class Comment < ActiveRecord::Base
  belongs_to :article, :touch => :last_updated_at
end

# Adding a new comment marks the article as being updated
article.last_updated_at #=> "Mon Apr 20 07:42:53 -0400 2009"
article.comments.create(:body => "New comment")
article.last_updated_at #=> "Mon Apr 20 07:43:27 -0400 2009"

Also, somewhat conveniently, you can invoke touch directly on a model to update its timestamp outside any association callbacks:

1
2
3
article.updated_at #=> "Mon Apr 20 07:42:53 -0400 2009"
article.touch
article.updated_at #=> "Mon Apr 20 07:43:27 -0400 2009"

So, touch away (in a non-creepy kind of way)!

tags: ruby, rubyonrails

Comments

Leave a response

  1. vojtoApril 20, 2009 @ 08:13 AM

    Wow that sounds like a great feature :)

  2. JoshApril 20, 2009 @ 11:19 AM

    I can see this making passive cache invalidation much easier ;)

  3. Amos KingApril 20, 2009 @ 12:21 PM

    If you are working with legacy systems and have to have touch point to a column name you might want to look at http://github.com/adkron/legacy_woes

    If you are using legacy woes you can make update_at(on), :created_at(on) point to the non-standard naming convention, and then if you switch to the standard you don’t have to change all your touches.

  4. TekkubMay 01, 2009 @ 01:13 AM

    I’m running into some rather annoying issues with optimistic locking… specifically when destroying associated records that touch the parent record. For example…

    We have two models, User and Comments. Users have many comments. User has optimistic locking enabled. Comments are set to touch their user. In the user model we have…

    before_destroy {|u| u.comments.destroy_all}

    Now if we have multiple comments, StaleObjectErrors are thrown because each destroy touches the user, and thus increments it’s lock. Simplest solution would be to give us something like destroy_without_touching and maybe destroy_all_without_touching. I don’t know, but there might be other places where *_without_touching methods may be needed.

    For now I just turn off locking for a tiny spec of time while the destroy_all is run. Oh and thanks for this new feature, it rocks. It removed some horribly complex callbacks from our app… callbacks that were causing lots of these blasted stale errors. I’m very happy that the only ones I have to deal with now are on destroy, since no one uses that feature anyway!

  5. JLMay 08, 2009 @ 02:38 PM

    Touching isn’t actually in 2.3, right? Is the article just incorrectly labeled?

  6. Ryan DaigleMay 12, 2009 @ 09:35 PM

    Hey JL, I believe you’re right – this update is a post-2.3 feature. Not sure what made me think it was in 2.3 in the first place. My bad!

  7. JL SmithMay 19, 2009 @ 09:43 AM

    No problem Ryan…wish it was in 2.3 though.