Optimize no args case for Dry::Core::Memoizable
Result of three glasses of whiskey this Friday's night
This commit is contained in:
parent
0dafeab4ca
commit
d2a8d24956
|
@ -7,4 +7,4 @@ require "dry/core"
|
|||
# You can add fixtures and/or initialization code here to make experimenting
|
||||
# with your gem easier. You can also use a different console, if you like.
|
||||
|
||||
binding.irb # rubocop:disable Lint/Debugger
|
||||
binding.irb
|
||||
|
|
|
@ -80,7 +80,7 @@ module Dry
|
|||
if Undefined.equal?(value)
|
||||
if instance_variable_defined?(ivar)
|
||||
instance_variable_get(ivar)
|
||||
else # rubocop:disable Style/EmptyElse
|
||||
else
|
||||
nil
|
||||
end
|
||||
elsif type === value # rubocop:disable Style/CaseEquality
|
||||
|
|
|
@ -62,7 +62,7 @@ module Dry
|
|||
# 1 + Undefined.default(val, 2)
|
||||
# end
|
||||
#
|
||||
def undefined.default(x, y = self) # rubocop:disable Naming/MethodParameterName
|
||||
def undefined.default(x, y = self)
|
||||
if equal?(x)
|
||||
if equal?(y)
|
||||
yield
|
||||
|
|
|
@ -52,6 +52,12 @@ module Dry
|
|||
|
||||
# @api private
|
||||
class Memoizer < ::Module
|
||||
KERNEL = {
|
||||
signleton: ::Kernel.instance_method(:singleton_class),
|
||||
ivar_set: ::Kernel.instance_method(:instance_variable_set),
|
||||
frozen: ::Kernel.instance_method(:frozen?)
|
||||
}.freeze
|
||||
|
||||
# @api private
|
||||
def initialize(klass, names)
|
||||
super()
|
||||
|
@ -65,20 +71,44 @@ module Dry
|
|||
private
|
||||
|
||||
# @api private
|
||||
def define_memoizable(method:) # rubocop:disable Metrics/AbcSize
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
# rubocop:disable Metrics/PerceivedComplexity
|
||||
def define_memoizable(method:)
|
||||
parameters = method.parameters
|
||||
mod = self
|
||||
kernel = KERNEL
|
||||
|
||||
if parameters.empty?
|
||||
key = method.name.hash
|
||||
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
||||
def #{method.name} # def slow_fetch
|
||||
if @__memoized__.key?(#{key}) # if @__memoized__.key?(12345678)
|
||||
@__memoized__[#{key}] # @__memoized__[12345678]
|
||||
else # else
|
||||
@__memoized__[#{key}] = super # @__memoized__[12345678] = super
|
||||
end # end
|
||||
end # end
|
||||
RUBY
|
||||
key = method.name.hash.abs
|
||||
|
||||
define_method(method.name) do
|
||||
value = super()
|
||||
|
||||
if kernel[:frozen].bind(self).call
|
||||
mod.remove_method(method.name)
|
||||
mod.module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
||||
def #{method.name} # def slow_calc
|
||||
cached = @__memoized__[#{key}] # cached = @__memoized__[12345678]
|
||||
#
|
||||
if cached || @__memoized__.key?(#{key}) # if cached || @__memoized__.key?(12345678)
|
||||
cached # cached
|
||||
else # else
|
||||
@__memoized__[#{key}] = super # @__memoized__[12345678] = super
|
||||
end # end
|
||||
end # end
|
||||
RUBY
|
||||
else
|
||||
attr_name = :"__memozed_#{key}__"
|
||||
ivar_name = :"@#{attr_name}"
|
||||
kernel[:ivar_set].bind(self).(ivar_name, value)
|
||||
eigenclass = kernel[:signleton].bind(self).call
|
||||
eigenclass.attr_reader(attr_name)
|
||||
eigenclass.alias_method(method.name, attr_name)
|
||||
eigenclass.remove_method(attr_name)
|
||||
end
|
||||
|
||||
value
|
||||
end
|
||||
else
|
||||
mapping = parameters.to_h { |k, v = nil| [k, v] }
|
||||
params, binds = declaration(parameters, mapping)
|
||||
|
@ -112,6 +142,8 @@ module Dry
|
|||
m
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
# rubocop:enable Metrics/PerceivedComplexity
|
||||
|
||||
# @api private
|
||||
def declaration(definition, lookup)
|
||||
|
|
|
@ -75,6 +75,8 @@ RSpec.describe Dry::Core::Memoizable do
|
|||
|
||||
describe "test4" do
|
||||
it_behaves_like "a memoized method" do
|
||||
before { described_class.test4 }
|
||||
|
||||
let(:new_meth) { described_class.method(:test4) }
|
||||
|
||||
it "does not raise an error" do
|
||||
|
|
|
@ -100,7 +100,7 @@ RSpec.shared_examples "a memoizable class" do
|
|||
end
|
||||
|
||||
RSpec.shared_examples "a memoized method" do
|
||||
let(:old_meth) { new_meth.super_method }
|
||||
let(:old_meth) { described_class.class.instance_method(new_meth.name) }
|
||||
|
||||
describe "new != old" do
|
||||
subject { new_meth }
|
||||
|
|
Loading…
Reference in New Issue