Skip to content

Instantly share code, notes, and snippets.

@esurdam
Last active July 26, 2016 09:42
Show Gist options
  • Save esurdam/3c6473d6700f3f15db92731d4e0c1ef9 to your computer and use it in GitHub Desktop.
Save esurdam/3c6473d6700f3f15db92731d4e0c1ef9 to your computer and use it in GitHub Desktop.
Magic Sauce Caching ;)
module CacheClassMethods
def has_serializer
if self.instance_eval "Object.const_defined? #{self.class.name}Serializer.to_s"
self.instance_eval "#{self.class.name}Serializer"
end
end
def hash_key(source = nil)
"#{self.class.name.to_s.downcase}:#{source.to_s.downcase}"
end
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
# Set model to cache itself
#
# Sane defaults set for all options
#
# @param [Hash] options additional options for caching
#
def cache_self(options = {})
prefix = options[:prefix]
key = options[:key]
use_serializer = (options[:serializer] != false)
define_method("as_serialized") {
serializer = options[:serializer] || self.has_serializer
use_serializer && (serializer != nil) ? serializer.new(self) : self
}
define_method("cache_key") {
key ? (self.send key) : id.to_s
}
define_method("cache_name") {
if prefix.is_a? Symbol
return "#{self.send prefix}"
end
"#{ self.class.name.to_s.downcase}s"
}
define_method("cache_record") {
HashCache.set "#{self.cache_name}", "#{self.cache_key}", self.as_serialized.serializable_hash.to_json
}
define_method("cached?") {
HashCache.exists("#{self.cache_name}", "#{self.cache_key}")
}
define_method("get_cached_record") {
begin
JSON.parse(HashCache.get "#{self.cache_name}", "#{self.cache_key}")
rescue
self.cache_record
return JSON.parse(HashCache.get "#{self.cache_name}", "#{self.cache_key}")
end
}
define_method("bust_cache") do
HashCache.del "#{self.cache_name}", "#{self.cache_key}"
end
# [:bust_cache, :cache_record].each { |method| self.instance_eval { private method }}
self.after_save :cache_record
self.after_destroy :bust_cached
end
# Cache a models collection
#
# Sane defaults set for all options
#
# @param [Symbol] source the method called to retrieve data
# @param [Hash] options additional options for caching
#
def cache_collection(source, options = {})
symbol = options[:as] || "#{source.to_s.pluralize.singularize}_list"
use_serializer = (options[:serializer] != false)
serializer = options[:serializer]
define_method(symbol.to_sym) {
data = HashCache.get self.hash_key(source), id
JSON.parse(data) if data
}
define_method("#{symbol.to_s}_set") do
raw = self.send source
data = []
if use_serializer && (serializer != nil)
raw.each_with_index.map{ |d,i| data[i] = serializer.new(d) }
else
data = raw
end
HashCache.set self.hash_key(source), id, data.serializable_hash.to_json
end
define_method("bust_#{symbol.to_s}_cache") do
HashCache.del self.hash_key(source), id
end
define_method("#{symbol.to_s}_count") { HashCache.count self.hash_key(source) }
end
end
end
class HashCache
## Store data as a hash map in redis
## Useful for caching assets in bucket
def self.data
@data ||= redis
end
def self.prefix
@prefix ||= "maps"
end
def self.set(hashname, key, value)
data.hset("#{prefix}:#{hashname}", "#{key}", value)
end
def self.del(hashname, key = nil)
!key ? data.del("#{prefix}:#{hashname}") : data.hdel("#{prefix}:#{hashname}", "#{key}")
end
def self.get(hashname, key = nil)
!key ? data.hgetall("#{prefix}:#{hashname}") : data.hget("#{prefix}:#{hashname}", "#{key}")
end
def self.getkeys(hashname)
data.hkeys("#{prefix}:#{hashname}")
end
def self.exists(hashname, key = nil)
!key ? data.exists("#{prefix}:#{hashname}") : data.hexists("#{prefix}:#{hashname}", "#{key}")
end
def self.count(hashname)
data.hlen("#{prefix}:#{hashname}")
end
private
def self.redis
Redis.new(host: Rails.application.secrets.redis_url, port: Rails.application.secrets.redis_port)
end
end
class Simple 
  include CacheClassMethods
  
end

# s = Simple.new
# s.cached?
# s.cache_record
# s.get_cached_record

# s.cache_key
# s.cache_name
# s.as_serialized
# s.bust_cache
class AwesomeModel < ActiveRecord::Base
  include CacheClassMethods
  cache_self prefix: :secret_hash, key: :name, serializer: AdvancedSerializer
  cache_collection :cool_things, as: :awesome_things, serializer: AwesomeThingsSerializer
  
  has_many :cool_things
  
  def name
    "Evan"
  end
  
  def secret_hash
    "youthinkyoucool"
  end
end
# a = AwesomeModel.new
## Collection
# a.awesome_things
# a.awesome_things_count

## Self cache
# s = AwesomeModel.new
# s.cached?
# s.cache_record
# s.get_cached_record

# s.cache_key
# s.cache_name
# s.as_serialized
# s.bust_cache
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment