Ruby - Memoization proxy class
I've needed to create a proxy class that would cache the method results from an object because I couldn't change its implementation.
It had a lot of expensive operations that would be done for every method call, and some of these heavy methods would be called internally, and that's why I needed to come with an "intrusive" solution.
It's important to note that you probably want to avoid using something like this, you probably will always prefer to design your code better over to using this hack. Also, it's needless to say that this will give you problems in case your methods are not referentially transparent or produce side effects.
class MethodCacheProxy
instance_methods.each { |m| undef_method m unless m =~ /(^__|^send$|^object_id$)/ }
attr_reader :target, :cache, :invasive
def initialize(target, invasive = false)
@target = target
@cache = {}
@invasive = invasive
intercept_instance_methods if invasive
end
def method_missing(name, *args, &block)
cache_for(name, *args, &block)
end
def cache_for(name, *args, &block)
unless block_given?
cache[cache_key_for(name, args)] ||= target.send(name, *args)
else
target.send(name, *args, &block)
end
end
def cache_key_for(name, args)
"#{name}_#{args.hash}"
end
private
def intercept_instance_methods
instance_methods.each do |name|
override_method(name)
end
end
def instance_methods
target.class.instance_methods(false)
end
def instance_method(name)
target.class.instance_method(name)
end
def override_method(name)
cacher = self
method = instance_method(name)
target.define_singleton_method(name) do |*args, &block|
unless block.present?
cache_key = cacher.cache_key_for(name, args)
cacher.cache[cache_key] ||= method.bind(self).(*args, &block)
else
method.bind(self).(*args, &block)
end
end
end
end
Written by David Michael Nelson
Related protips
Have a fresh tip? Share with Coderwall community!
Post
Post a tip
Best
#Ruby
Authors
Sponsored by #native_company# — Learn More
#native_title#
#native_desc#