lib/tins/memoize.rb
require 'tins/extract_last_argument_options'
module Tins
module Memoize
module CacheMethods
# Return the cache object.
def __memoize_cache__
@__memoize_cache__ ||= {}
end
# Clear cached values for all methods/functions.
def memoize_cache_clear
__memoize_cache__.clear
self
end
def memoize_apply_visibility(id)
visibility = instance_eval do
case
when private_method_defined?(id)
:private
when protected_method_defined?(id)
:protected
end
end
yield
ensure
visibility and __send__(visibility, id)
end
end
class ::Module
# Automatically memoize calls of the the methods +method_ids+. The
# memoized results do NOT ONLY depend on the arguments, but ALSO on the
# object the method is called on.
def memoize_method(*method_ids)
method_ids.extend(ExtractLastArgumentOptions)
method_ids, opts = method_ids.extract_last_argument_options
include CacheMethods
method_ids.each do |method_id|
method_id = method_id.to_s.to_sym
memoize_apply_visibility method_id do
orig_method = instance_method(method_id)
__send__(:define_method, method_id) do |*args|
mc = __memoize_cache__
if mc.key?(method_id) and result = mc[method_id][args]
result
else
(mc[method_id] ||= {})[args] = result = orig_method.bind(self).call(*args)
$DEBUG and warn "#{self.class} cached method #{method_id}(#{args.inspect unless args.empty?}) = #{result.inspect} [#{__id__}]"
opts[:freeze] and result.freeze
end
result
end
end
end
method_ids.size == 1 ? method_ids.first : method_ids
end
include CacheMethods
# Automatically memoize calls of the functions +function_ids+. The
# memoized result does ONLY depend on the arguments given to the
# function.
def memoize_function(*function_ids)
function_ids.extend(ExtractLastArgumentOptions)
function_ids, opts = function_ids.extract_last_argument_options
mc = __memoize_cache__
function_ids.each do |function_id|
function_id = function_id.to_s.to_sym
memoize_apply_visibility function_id do
orig_function = instance_method(function_id)
__send__(:define_method, function_id) do |*args|
if mc.key?(function_id) and result = mc[function_id][args]
result
else
(mc[function_id] ||= {})[args] = result = orig_function.bind(self).call(*args)
opts[:freeze] and result.freeze
$DEBUG and warn "#{self.class} cached function #{function_id}(#{args.inspect unless args.empty?}) = #{result.inspect}"
end
result
end
end
end
function_ids.size == 1 ? function_ids.first : function_ids
end
end
end
end
require 'tins/alias'