lib/memo_wise.rb in memo_wise-1.3.0 vs lib/memo_wise.rb in memo_wise-1.4.0
- old
+ new
@@ -167,50 +167,87 @@
HEREDOC
end
raise ArgumentError, "#{method_name.inspect} must be a Symbol" unless method_name.is_a?(Symbol)
- api = MemoWise::InternalAPI.new(klass)
- visibility = api.method_visibility(method_name)
+ visibility = MemoWise::InternalAPI.method_visibility(klass, method_name)
original_memo_wised_name = MemoWise::InternalAPI.original_memo_wised_name(method_name)
method = klass.instance_method(method_name)
klass.send(:alias_method, original_memo_wised_name, method_name)
klass.send(:private, original_memo_wised_name)
method_arguments = MemoWise::InternalAPI.method_arguments(method)
- index = MemoWise::InternalAPI.next_index!(klass, method_name)
case method_arguments
when MemoWise::InternalAPI::NONE
# Zero-arg methods can use simpler/more performant logic because the
# hash key is just the method name.
- klass.module_eval <<~HEREDOC, __FILE__, __LINE__ + 1
- def #{method_name}
- if @_memo_wise_sentinels[#{index}]
- @_memo_wise[#{index}]
- else
- ret = @_memo_wise[#{index}] = #{original_memo_wised_name}
- @_memo_wise_sentinels[#{index}] = true
- ret
+ klass.send(:define_method, method_name) do # Ruby 2.4's `define_method` is private in some cases
+ index = MemoWise::InternalAPI.index(self, method_name)
+ klass.module_eval <<~HEREDOC, __FILE__, __LINE__ + 1
+ def #{method_name}
+ if @_memo_wise_sentinels[#{index}]
+ @_memo_wise[#{index}]
+ else
+ ret = @_memo_wise[#{index}] = #{original_memo_wised_name}
+ @_memo_wise_sentinels[#{index}] = true
+ ret
+ end
end
- end
- HEREDOC
+ HEREDOC
+
+ klass.send(visibility, method_name)
+ send(method_name)
+ end
when MemoWise::InternalAPI::ONE_REQUIRED_POSITIONAL, MemoWise::InternalAPI::ONE_REQUIRED_KEYWORD
key = method.parameters.first.last
+ # NOTE: Ruby 2.6 and below, and TruffleRuby 3.0, break when we use
+ # `define_method(...) do |*args, **kwargs|`. Instead we must use the
+ # simpler `|*args|` pattern. We can't just do this always though
+ # because Ruby 2.7 and above require `|*args, **kwargs|` to work
+ # correctly.
+ # See: https://blog.saeloun.com/2019/10/07/ruby-2-7-keyword-arguments-redesign.html#ruby-26
+ # :nocov:
+ if RUBY_VERSION < "2.7" || RUBY_ENGINE == "truffleruby"
+ klass.send(:define_method, method_name) do |*args| # Ruby 2.4's `define_method` is private in some cases
+ index = MemoWise::InternalAPI.index(self, method_name)
+ klass.module_eval <<~HEREDOC, __FILE__, __LINE__ + 1
+ def #{method_name}(#{MemoWise::InternalAPI.args_str(method)})
+ _memo_wise_hash = (@_memo_wise[#{index}] ||= {})
+ _memo_wise_output = _memo_wise_hash[#{key}]
+ if _memo_wise_output || _memo_wise_hash.key?(#{key})
+ _memo_wise_output
+ else
+ _memo_wise_hash[#{key}] = #{original_memo_wised_name}(#{MemoWise::InternalAPI.call_str(method)})
+ end
+ end
+ HEREDOC
- klass.module_eval <<~HEREDOC, __FILE__, __LINE__ + 1
- def #{method_name}(#{MemoWise::InternalAPI.args_str(method)})
- _memo_wise_hash = (@_memo_wise[#{index}] ||= {})
- _memo_wise_output = _memo_wise_hash[#{key}]
- if _memo_wise_output || _memo_wise_hash.key?(#{key})
- _memo_wise_output
- else
- _memo_wise_hash[#{key}] = #{original_memo_wised_name}(#{MemoWise::InternalAPI.call_str(method)})
- end
+ klass.send(visibility, method_name)
+ send(method_name, *args)
end
- HEREDOC
+ # :nocov:
+ else
+ klass.define_method(method_name) do |*args, **kwargs|
+ index = MemoWise::InternalAPI.index(self, method_name)
+ klass.module_eval <<~HEREDOC, __FILE__, __LINE__ + 1
+ def #{method_name}(#{MemoWise::InternalAPI.args_str(method)})
+ _memo_wise_hash = (@_memo_wise[#{index}] ||= {})
+ _memo_wise_output = _memo_wise_hash[#{key}]
+ if _memo_wise_output || _memo_wise_hash.key?(#{key})
+ _memo_wise_output
+ else
+ _memo_wise_hash[#{key}] = #{original_memo_wised_name}(#{MemoWise::InternalAPI.call_str(method)})
+ end
+ end
+ HEREDOC
+
+ klass.send(visibility, method_name)
+ send(method_name, *args, **kwargs)
+ end
+ end
# MemoWise::InternalAPI::MULTIPLE_REQUIRED, MemoWise::InternalAPI::SPLAT,
# MemoWise::InternalAPI::DOUBLE_SPLAT, MemoWise::InternalAPI::SPLAT_AND_DOUBLE_SPLAT
else
# NOTE: When benchmarking this implementation against something like:
#
@@ -222,22 +259,58 @@
# is because this case uses a more complex hash key (see
# `MemoWise::InternalAPI.key_str`), and hashing that key has less
# consistent performance. In general, this should still be faster for
# truthy results because `Hash#[]` generally performs hash lookups
# faster than `Hash#fetch`.
- klass.module_eval <<~HEREDOC, __FILE__, __LINE__ + 1
- def #{method_name}(#{MemoWise::InternalAPI.args_str(method)})
- _memo_wise_hash = (@_memo_wise[#{index}] ||= {})
- _memo_wise_key = #{MemoWise::InternalAPI.key_str(method)}
- _memo_wise_output = _memo_wise_hash[_memo_wise_key]
- if _memo_wise_output || _memo_wise_hash.key?(_memo_wise_key)
- _memo_wise_output
- else
- _memo_wise_hash[_memo_wise_key] = #{original_memo_wised_name}(#{MemoWise::InternalAPI.call_str(method)})
- end
+ #
+ # NOTE: Ruby 2.6 and below, and TruffleRuby 3.0, break when we use
+ # `define_method(...) do |*args, **kwargs|`. Instead we must use the
+ # simpler `|*args|` pattern. We can't just do this always though
+ # because Ruby 2.7 and above require `|*args, **kwargs|` to work
+ # correctly.
+ # See: https://blog.saeloun.com/2019/10/07/ruby-2-7-keyword-arguments-redesign.html#ruby-26
+ # :nocov:
+ if RUBY_VERSION < "2.7" || RUBY_ENGINE == "truffleruby"
+ klass.send(:define_method, method_name) do |*args| # Ruby 2.4's `define_method` is private in some cases
+ index = MemoWise::InternalAPI.index(self, method_name)
+ klass.module_eval <<~HEREDOC, __FILE__, __LINE__ + 1
+ def #{method_name}(#{MemoWise::InternalAPI.args_str(method)})
+ _memo_wise_hash = (@_memo_wise[#{index}] ||= {})
+ _memo_wise_key = #{MemoWise::InternalAPI.key_str(method)}
+ _memo_wise_output = _memo_wise_hash[_memo_wise_key]
+ if _memo_wise_output || _memo_wise_hash.key?(_memo_wise_key)
+ _memo_wise_output
+ else
+ _memo_wise_hash[_memo_wise_key] = #{original_memo_wised_name}(#{MemoWise::InternalAPI.call_str(method)})
+ end
+ end
+ HEREDOC
+
+ klass.send(visibility, method_name)
+ send(method_name, *args)
end
- HEREDOC
+ # :nocov:
+ else # Ruby 2.7 and above break with (*args)
+ klass.define_method(method_name) do |*args, **kwargs|
+ index = MemoWise::InternalAPI.index(self, method_name)
+ klass.module_eval <<~HEREDOC, __FILE__, __LINE__ + 1
+ def #{method_name}(#{MemoWise::InternalAPI.args_str(method)})
+ _memo_wise_hash = (@_memo_wise[#{index}] ||= {})
+ _memo_wise_key = #{MemoWise::InternalAPI.key_str(method)}
+ _memo_wise_output = _memo_wise_hash[_memo_wise_key]
+ if _memo_wise_output || _memo_wise_hash.key?(_memo_wise_key)
+ _memo_wise_output
+ else
+ _memo_wise_hash[_memo_wise_key] = #{original_memo_wised_name}(#{MemoWise::InternalAPI.call_str(method)})
+ end
+ end
+ HEREDOC
+
+ klass.send(visibility, method_name)
+ send(method_name, *args, **kwargs)
+ end
+ end
end
klass.send(visibility, method_name)
end
end
@@ -438,16 +511,15 @@
# ex.method_called_times #=> nil
#
def preset_memo_wise(method_name, *args, **kwargs)
raise ArgumentError, "Pass a block as the value to preset for #{method_name}, #{args}" unless block_given?
- api = MemoWise::InternalAPI.new(self)
- api.validate_memo_wised!(method_name)
+ MemoWise::InternalAPI.validate_memo_wised!(self, method_name)
- method = method(method_name)
+ method = method(MemoWise::InternalAPI.original_memo_wised_name(method_name))
method_arguments = MemoWise::InternalAPI.method_arguments(method)
- index = api.index(method_name)
+ index = MemoWise::InternalAPI.index(self, method_name)
if method_arguments == MemoWise::InternalAPI::NONE
@_memo_wise_sentinels[index] = true
@_memo_wise[index] = yield
return
@@ -546,15 +618,14 @@
end
raise ArgumentError, "#{method_name.inspect} must be a Symbol" unless method_name.is_a?(Symbol)
raise ArgumentError, "#{method_name} is not a defined method" unless respond_to?(method_name, true)
- api = MemoWise::InternalAPI.new(self)
- api.validate_memo_wised!(method_name)
+ MemoWise::InternalAPI.validate_memo_wised!(self, method_name)
- method = method(method_name)
+ method = method(MemoWise::InternalAPI.original_memo_wised_name(method_name))
method_arguments = MemoWise::InternalAPI.method_arguments(method)
- index = api.index(method_name)
+ index = MemoWise::InternalAPI.index(self, method_name)
case method_arguments
when MemoWise::InternalAPI::NONE
@_memo_wise_sentinels[index] = nil
@_memo_wise[index] = nil