lib/memo_wise.rb in memo_wise-0.3.0 vs lib/memo_wise.rb in memo_wise-0.4.0

- old
+ new

@@ -37,15 +37,45 @@ # This approach supports memoization on frozen (immutable) objects -- for # example, classes created by the # [Values](https://github.com/tcrayford/Values) # [gem](https://rubygems.org/gems/values). # - def initialize(*) - MemoWise.create_memo_wise_state!(self) - super - end + # To support syntax differences with keyword and positional arguments starting + # with ruby 2.7, we have to set up the initializer with some slightly + # different syntax for the different versions. This variance in syntax is not + # included in coverage reports since the branch chosen will never differ + # within a single ruby version. This means it is impossible for us to get + # 100% coverage of this line within a single CI run. + # + # See + # [this article](https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/) + # for more information. + # + # :nocov: + all_args = RUBY_VERSION < "2.7" ? "*" : "..." + # :nocov: + class_eval <<-END_OF_METHOD, __FILE__, __LINE__ + 1 + # On Ruby 2.7 or greater: + # + # def initialize(...) + # MemoWise.create_memo_wise_state!(self) + # super + # end + # + # On Ruby 2.6 or lower: + # + # def initialize(*) + # MemoWise.create_memo_wise_state!(self) + # super + # end + def initialize(#{all_args}) + MemoWise.create_memo_wise_state!(self) + super + end + END_OF_METHOD + # @private # # Determine whether `method` takes any *positional* args. # # These are the types of positional args: @@ -180,14 +210,11 @@ # Object in which to create mutable state to store future memoized values # # @return [Object] the passed-in obj def self.create_memo_wise_state!(obj) unless obj.instance_variables.include?(:@_memo_wise) - obj.instance_variable_set( - :@_memo_wise, - Hash.new { |h, k| h[k] = {} } - ) + obj.instance_variable_set(:@_memo_wise, {}) end obj end @@ -267,11 +294,11 @@ # Zero-arg methods can use simpler/more performant logic because the # hash key is just the method name. if method.arity.zero? klass.module_eval <<-END_OF_METHOD, __FILE__, __LINE__ + 1 # def foo - # @_memo_wise.fetch(:foo}) do + # @_memo_wise.fetch(:foo) do # @_memo_wise[:foo] = _memo_wise_original_foo # end # end def #{method_name} @@ -301,18 +328,22 @@ # Note that we don't need to freeze args before using it as a hash key # because Ruby always copies argument arrays when splatted. klass.module_eval <<-END_OF_METHOD, __FILE__, __LINE__ + 1 # def foo(*args, **kwargs) - # hash = @_memo_wise[:foo] + # hash = @_memo_wise.fetch(:foo) do + # @_memo_wise[:foo] = {} + # end # hash.fetch([args, kwargs].freeze) do # hash[[args, kwargs].freeze] = _memo_wise_original_foo(*args, **kwargs) # end # end def #{method_name}#{args_str} - hash = @_memo_wise[:#{method_name}] + hash = @_memo_wise.fetch(:#{method_name}) do + @_memo_wise[:#{method_name}] = {} + end hash.fetch(#{fetch_key}) do hash[#{fetch_key}] = #{original_memo_wised_name}#{args_str} end end END_OF_METHOD @@ -371,11 +402,11 @@ # # NOTE: Currently, no attempt is made to validate that the given arguments are # valid for the given method. # # @param method_name [Symbol] - # Name of a method previously setup with `#memo_wise`. + # Name of a method previously set up with `#memo_wise`. # # @param args [Array] # (Optional) If the method takes positional args, these are the values of # position args for which the given block's result will be preset as the # memoized result. @@ -422,11 +453,14 @@ validate_params!(method_name, args) if method(method_name).arity.zero? @_memo_wise[method_name] = yield else - @_memo_wise[method_name][fetch_key(method_name, *args, **kwargs)] = yield + hash = @_memo_wise.fetch(method_name) do + @_memo_wise[method_name] = {} + end + hash[fetch_key(method_name, *args, **kwargs)] = yield end end # Resets memoized results of a given method, or all methods. # @@ -447,11 +481,11 @@ # # - If *not* given `method_name`: # - Resets all memoized results of calling *all methods*. # # @param method_name [Symbol, nil] - # (Optional) Name of a method previously setup with `#memo_wise`. If not + # (Optional) Name of a method previously set up with `#memo_wise`. If not # given, will reset *all* memoized results for *all* methods. # # @param args [Array] # (Optional) If the method takes positional args, these are the values of # position args for which the memoized result will be reset. @@ -519,10 +553,10 @@ validate_memo_wised!(method_name) if args.empty? && kwargs.empty? @_memo_wise.delete(method_name) else - @_memo_wise[method_name].delete(fetch_key(method_name, *args, **kwargs)) + @_memo_wise[method_name]&.delete(fetch_key(method_name, *args, **kwargs)) end end private