Skip to content

Commit d2a8d24

Browse files
committed
Optimize no args case for Dry::Core::Memoizable
Result of three glasses of whiskey this Friday's night
1 parent 0dafeab commit d2a8d24

File tree

6 files changed

+49
-15
lines changed

6 files changed

+49
-15
lines changed

bin/console

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ require "dry/core"
77
# You can add fixtures and/or initialization code here to make experimenting
88
# with your gem easier. You can also use a different console, if you like.
99

10-
binding.irb # rubocop:disable Lint/Debugger
10+
binding.irb

lib/dry/core/class_attributes.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def defines(*args, type: ::Object, coerce: IDENTITY) # rubocop:disable Metrics/P
8080
if Undefined.equal?(value)
8181
if instance_variable_defined?(ivar)
8282
instance_variable_get(ivar)
83-
else # rubocop:disable Style/EmptyElse
83+
else
8484
nil
8585
end
8686
elsif type === value # rubocop:disable Style/CaseEquality

lib/dry/core/constants.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def undefined.inspect
6262
# 1 + Undefined.default(val, 2)
6363
# end
6464
#
65-
def undefined.default(x, y = self) # rubocop:disable Naming/MethodParameterName
65+
def undefined.default(x, y = self)
6666
if equal?(x)
6767
if equal?(y)
6868
yield

lib/dry/core/memoizable.rb

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ def self.included(klass)
5252

5353
# @api private
5454
class Memoizer < ::Module
55+
KERNEL = {
56+
signleton: ::Kernel.instance_method(:singleton_class),
57+
ivar_set: ::Kernel.instance_method(:instance_variable_set),
58+
frozen: ::Kernel.instance_method(:frozen?)
59+
}.freeze
60+
5561
# @api private
5662
def initialize(klass, names)
5763
super()
@@ -65,20 +71,44 @@ def initialize(klass, names)
6571
private
6672

6773
# @api private
68-
def define_memoizable(method:) # rubocop:disable Metrics/AbcSize
74+
# rubocop:disable Metrics/AbcSize
75+
# rubocop:disable Metrics/PerceivedComplexity
76+
def define_memoizable(method:)
6977
parameters = method.parameters
78+
mod = self
79+
kernel = KERNEL
7080

7181
if parameters.empty?
72-
key = method.name.hash
73-
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
74-
def #{method.name} # def slow_fetch
75-
if @__memoized__.key?(#{key}) # if @__memoized__.key?(12345678)
76-
@__memoized__[#{key}] # @__memoized__[12345678]
77-
else # else
78-
@__memoized__[#{key}] = super # @__memoized__[12345678] = super
79-
end # end
80-
end # end
81-
RUBY
82+
key = method.name.hash.abs
83+
84+
define_method(method.name) do
85+
value = super()
86+
87+
if kernel[:frozen].bind(self).call
88+
mod.remove_method(method.name)
89+
mod.module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
90+
def #{method.name} # def slow_calc
91+
cached = @__memoized__[#{key}] # cached = @__memoized__[12345678]
92+
#
93+
if cached || @__memoized__.key?(#{key}) # if cached || @__memoized__.key?(12345678)
94+
cached # cached
95+
else # else
96+
@__memoized__[#{key}] = super # @__memoized__[12345678] = super
97+
end # end
98+
end # end
99+
RUBY
100+
else
101+
attr_name = :"__memozed_#{key}__"
102+
ivar_name = :"@#{attr_name}"
103+
kernel[:ivar_set].bind(self).(ivar_name, value)
104+
eigenclass = kernel[:signleton].bind(self).call
105+
eigenclass.attr_reader(attr_name)
106+
eigenclass.alias_method(method.name, attr_name)
107+
eigenclass.remove_method(attr_name)
108+
end
109+
110+
value
111+
end
82112
else
83113
mapping = parameters.to_h { |k, v = nil| [k, v] }
84114
params, binds = declaration(parameters, mapping)
@@ -112,6 +142,8 @@ def #{method.name}(#{params.join(", ")}) # def slow_calc(arg1, a
112142
m
113143
end
114144
end
145+
# rubocop:enable Metrics/AbcSize
146+
# rubocop:enable Metrics/PerceivedComplexity
115147

116148
# @api private
117149
def declaration(definition, lookup)

spec/dry/core/memoizable_spec.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@
7575

7676
describe "test4" do
7777
it_behaves_like "a memoized method" do
78+
before { described_class.test4 }
79+
7880
let(:new_meth) { described_class.method(:test4) }
7981

8082
it "does not raise an error" do

spec/support/shared_examples/memoizable.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def falsey
100100
end
101101

102102
RSpec.shared_examples "a memoized method" do
103-
let(:old_meth) { new_meth.super_method }
103+
let(:old_meth) { described_class.class.instance_method(new_meth.name) }
104104

105105
describe "new != old" do
106106
subject { new_meth }

0 commit comments

Comments
 (0)