For years I have been using the cache_key_for_* method, as seen in Rails Guides and many other places. Here is an example.
1 2 3 4 5 |
def cache_key_for_products(products) count = products.count max_updated_at = products.maximum(:updated_at).try(:utc).try(:to_s, :number) "products/all-#{count}-#{max_updated_at}" end |
It works. But even this venerable snippet couldn’t escape the refactoring razor.
1 2 3 4 |
def cache_key_for_products(products) count_max = [products.size, products.maximum(:updated_at)].map(&:to_i).join('-') "products/all-#{count_max}" end |
.size vs .count
First change is using products.size instead of products.count . A minor change, but if ActiveRecord has already executed the sql query, using the .size method will prevent it from executing another query, as .count may cause.
Sean Griffin has a quick say in this battle. TL;DR Rails users should always use .size .
DateTime Number
Second, the original used a long chain of trys and then converting the DateTime of updated_at to number format. You might be breaking the law… of Demeter that is. That ‘number’ format looks like this: 20150410030422. It’s just the date and time.
Well, you can accomplish the same thing without chaining so many methods: use .to_i. Read about it at the docs. This one method uses the UTC time and gives you the number of seconds since the Unix Epoch. It looks like this: 1428635331.
Both has the same 1 second resolution, but the refactored version is shorter, which may save memcached memory if you have a lot of keys. I just think it’s cleaner, and avoids all the method chains.
Assembly
Finally, we just need to assemble it all in a string. By putting the size and time value into an array, we can use all those wonderful enumerable methods. You could just as easily use string interpolation and avoid the map and join.
Important Footnote
Rails has been using nanosecond resolution for their cache_key method for a while, assuming your backend supports factional second datetime values. My method only has 1 second resolution. If you require more granularity for your cache keys, by all means, use the original cache_keys_for_* method and change the .to_s(:number) to .to_s(:nsec).