Created
March 21, 2018 20:31
-
-
Save Startouf/0f659959c1890fb2a4aa0ecb2c094ca3 to your computer and use it in GitHub Desktop.
Mongoid Database cacheable
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Concern to include in mongoid models that need database cache | |
# | |
# | |
module MongoidCacheable | |
extend ActiveSupport::Concern | |
class_methods do | |
# Create database fields to cache an attribute | |
# => Cache field, (Optional Cached_at DateTime field) | |
# | |
# == Params | |
# +attr_name+ - Name of field to be cached (eg points_for_xxx) | |
# +type+ - MongoDB Type for the cache field | |
# +recache_procedure+ - block to be executed to recompute the value. | |
# => Instance `self` sent as block param | |
# | |
# == Define methods | |
# +[attr_name]_cache+ - The last cached value for the guy | |
# +[attr_name]_recache+ - Recache the latest value (using $set) | |
# +[attr_name]_clear_cache+ - To wipe the cache clean | |
# | |
# == Options | |
# +:expires_in:+ - Expiration date for the cache. | |
# => Will add an extra database field _cached_at to store cache time | |
# | |
# == Examples | |
# | |
# cache_attribute(:points_for_contest, type: Integer, expires_in: 5.hours) do |i| | |
# MyPointService.new(i).total_value | |
# end | |
# | |
# my_model.points_for_contest_cache # => cached_value or recomputed value if expired | |
# | |
def cache_attribute(attr_name, type:, expires_in: nil, &recache_procedure) | |
cached_at_field_name = :"#{attr_name}_cached_at" | |
cache_field_name = :"#{attr_name}_cache" | |
field cache_field_name, type: type | |
field(cached_at_field_name, type: DateTime, default: nil) | |
# @Override the _cache method to read the attribute or recompute it | |
define_method(:"#{attr_name}_cache") do | |
# Avoid updates if app is in readonly mode | |
# return super() if Rails.configuration.db_readonly | |
if mongoid_cache_expired?(cached_at_field_name, expires_in: expires_in) | |
send(:"#{attr_name}_recache") | |
else | |
super() # Need to write () explicitely | |
end | |
end | |
# @Override | |
# Patch attr= method to update cached_at field at the same time | |
define_method(:"#{attr_name}_cache=") do |new_val| | |
send("#{cached_at_field_name}=", DateTime.now) | |
super(new_val) # Need to write () explicitely | |
end | |
# Recache method will call &recache_procedure | |
# ...and update the cached_at timestamp | |
# Performs a $set on the database and returns the cached value | |
define_method(:"#{attr_name}_recache") do | |
value = recache_procedure.call(self) | |
set( | |
cache_field_name => value, | |
cached_at_field_name => DateTime.now | |
) | |
value | |
end | |
# Clear the cached value and cached_at timestamp | |
define_method(:"#{attr_name}_clear_cache") do | |
set( | |
cache_field_name => nil, | |
cached_at_field_name => nil | |
) | |
end | |
end | |
end | |
included do | |
# Returns true if the mongoid cache has expired | |
# +cached_at_name+ - Mongoid field _cached_at | |
# +:expires_in:+ - Expiration time for the cache | |
def mongoid_cache_expired?(cached_at_name, expires_in: nil) | |
cached_at = send(cached_at_name) | |
return true if cached_at.blank? # Always cache at least once | |
return false if expires_in.blank? # Never auto-recache if expiration is nil | |
DateTime.now > (cached_at + expires_in) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment