Ruby – classe de proxy de memoização

Precisei criar uma classe de proxy que armazenaria em cache os resultados do método de um objeto porque não pude alterar sua implementação.

Ele tinha muitas operações caras que seriam feitas para cada chamada de método, e alguns desses métodos pesados ​​seriam chamados internamente, e é por isso que precisei vir com uma solução “intrusiva”.

É importante observar que você provavelmente deseja evitar o uso de algo assim, provavelmente sempre preferirá projetar seu código melhor do que usar este hack . Além disso, é desnecessário dizer que isso causará problemas no caso de seus métodos não serem referencialmente transparentes ou produzirem efeitos colaterais.

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