In some part of my code I ended writing the following:

  self.count_processed ||= 0
  self.count_processed += 1

where self is some ActiveRecord model, and count_processed is an attribute of that model (and stored in the database).

What am i trying to achieve (if it is not blatantly obvious):

  • if count_processed is not initialised, make it zero
  • increment count_processed

Imho this code is clear and readable, but I had the feeling it could be more concise/prettier. So I asked that question on our campfire, to see if we could come up with something shorter.

Very nice to be working in a team where you can just throw up questions like these and a very useful, educational discussion unfolds. In short we came up with the following solutions.

Solution 1: to_i

self.count_processed = self.count_processed.to_i + 1

Nifty! Isn’t it? Use to_i because it will handle the nil correctly.

But for me this looked wrong. If I would return to this code after a few weeks, months, I would wonder why I did this way, and not just wrote self.count_processed += 1.

So while the code is correct, the intent of the code is not clear.

Solution 2: concise!

self.count_processed = (self.count_processed || 0) + 1

This is very beautiful, and the intent is also very clear. If it is not initialised, use the zero, else just use the value and add 1. Awesome.

Solution 3: change the getter

And alternative solution would be to overwrite the getter, like this

def count_processed
  self[:count_processed] ||= 0

Note the notation we used: we use self[:count_processed] because this will fetch the value from the database column. If this was a normal getter, we would write @count_processed (but that does not work for an ActiveRecord model).

After redefining the getter, we can just write:

self.count_processed += 1

While this will work always, does it express its intent more clearly or not? Actually you no longer have to worry about the initialisation, because it is handled, and we can focus on what we really want: increment the counter.

I opted for this solution.

What about you?

Which version do you prefer? Do you have any alternative suggestions?