Imagine you’re creating an online library. You have a selection of links (called “resources”) available. Each resource has a link, and a cache of the page body. Naturally, you’d like to track which are the most popular in terms of number of views–so you add a views attribute.
Now, in the Show page, you’d like to increment the number of views. You might try something obvious, like this: @resource.views += 1
This works–except the change doesn’t persist! After you refresh the page or return to the list, you’ll see the number of views are still zero! Ahhhhhh!
Ok, let’s try saving the record manually. Attempt #2 is thus:
@resource.views += 1
@resource.save
Fantastic! It works!–but, alas, it’s saving the WHOLE record back to the database, not just the views. If you’ve read our previous article on updating, you’ll know that a simpler solution exists–using the update function, like so:
Resource.update(@resource.id, {:views => @resource.views + 1})
Or, if you have a reference to the object itself, you can always do this:
@resource.update_attribute("views", @resource.views + 1) %>
Ahh, much better! But can we do better? The answer is yes–since we’re only incrementing a counter on a field, Rails provides us with an even simpler solution:
Resource.increment_counter("views", @resources.id)
A similar function, decrement_counter, exists to decrement a counter. Tada! Easy as that! Optionally, you can also use the increment! and decrement! functions.
I would recommend using the update_counters method.
In my case, my entity, Page, had an updated_at column, which Rails automatically updates anytime a record is saved. I wanted to maintain a view_count, but I didn’t want the updated_at attribute to be changed each time the view_count was incremented. I used update_counters to achieve this.
In my ‘show’ action:
Page.update_counters(params[:id], :view_count => 1)
@page = Page.find(params[:id]
The update_counters method automatically saves the record and skips the automagic timestamp updates.
This works for Rails 2.3.2
@Nathan, thanks, that’s very interesting; I haven’t really used Rails 2.3, but I’ll definitely look up this new method.