method

increment!

increment!(attribute)
public

Increments the attribute and saves the record.

3Notes

No concurrency

leente · Sep 13, 20102 thanks

If you want to handle concurrency, this doesn't work:

a = Article.first
b = Article.first
a.increment!(:view_count)
b.increment!(:view_count)
a.reload.view_count # -> 1
b.reload.view_count # -> 1

Instead, use SQL:

def increment_with_sql!(attribute, by = 1)
raise ArgumentError("Invalid attribute: #{attribute}") unless attribute_names.include?(attribute.to_s)
original_value_sql = "CASE WHEN `#{attribute}` IS NULL THEN 0 ELSE `#{attribute}` END"
self.class.update_all("`#{attribute}` = #{original_value_sql} + #{by.to_i}", "id = #{id}")
reload
end

increment_by_sql for PG

timdorr · Apr 4, 2013

Note, if you're using the code below for incrementing by SQL with a Postgres database, it's not going to like the backticks. Just remove them:

def increment_with_sql!(attribute, by = 1)
raise ArgumentError("Invalid attribute: #{attribute}") unless attribute_names.include?(attribute.to_s)
original_value_sql = "CASE WHEN #{attribute} IS NULL THEN 0 ELSE #{attribute} END"
self.class.update_all("#{attribute} = #{original_value_sql} + #{by.to_i}", "id = #{id}")
reload
end

SQL Injection?

pascal · Jan 5, 2015

Note that the version of leente and timdorr are probably vulnerable to SQL Injection (through attribute param).

Probably you want to look into with_lock instead of handcrafting SQL.