diff --git a/lib/memoist.rb b/lib/memoist.rb index c49e102..5a83e92 100644 --- a/lib/memoist.rb +++ b/lib/memoist.rb @@ -13,6 +13,16 @@ def self.memoized_methods end end + def self.extended(extender) + Memoist.memoist_eval(extender) do + unless singleton_class.method_defined?(:memoized_methods) + def self.memoized_methods + @_memoized_methods ||= [] + end + end + end + end + def self.memoized_ivar_for(method_name, identifier = nil) "@#{memoized_prefix(identifier)}_#{escape_punctuation(method_name)}" end @@ -59,7 +69,7 @@ def self.memoist_eval(klass, *args, &block) end def self.extract_reload!(method, args) - if args.length == method.arity.abs + 1 && (args.last == true || args.last == :reload) + if args.length == method.arity.abs + 2 && (args.last == true || args.last == :reload) reload = args.pop end reload @@ -123,6 +133,9 @@ def clear_structs def memoize(*method_names) identifier = method_names.pop[:identifier] if method_names.last.is_a?(Hash) + if method_names.last.is_a?(Hash) + identifier = method_names.pop[:identifier] + end method_names.each do |method_name| unmemoized_method = Memoist.unmemoized_method_for(method_name, identifier) @@ -203,14 +216,17 @@ def #{method_name}(reload = false) # end module_eval <<-EOS, __FILE__, __LINE__ + 1 - def #{method_name}(*args) + def #{method_name}(*positioned_args, **named_args) + args = [named_args, *positioned_args] reload = Memoist.extract_reload!(method(#{unmemoized_method.inspect}), args) skip_cache = reload || !(instance_variable_defined?(#{memoized_ivar.inspect}) && #{memoized_ivar} && #{memoized_ivar}.has_key?(args)) set_cache = skip_cache && !frozen? if skip_cache - value = #{unmemoized_method}(*args) + named_args, *positioned_args = args + positioned_args.pop if !reload.nil? && [true, :reload].include?(positioned_args.last) + value = #{unmemoized_method}(*positioned_args, **named_args) else value = #{memoized_ivar}[args] end diff --git a/test/memoist/args_test.rb b/test/memoist/args_test.rb new file mode 100644 index 0000000..c0c33c4 --- /dev/null +++ b/test/memoist/args_test.rb @@ -0,0 +1,39 @@ +require 'pry-rails' +require 'test_helper' +require_relative '../support/named_args_helper' + +class MemoistNamedArgsTest < Minitest::Test + def test_named_args_class_initialization + instance1 = ::NamedArgsHelper.new(a1: 11) + instance2 = ::NamedArgsHelper.new(a1: 11) + + refute_same instance1, instance2 + end + + def test_memoized_object_is_properly_caching_with_named_args + instance = ::NamedArgsHelper.new(a1: 11) + result1 = instance.calc_with_named_args(a2: 12, a3: 13) + _result2 = instance.calc_with_named_args(a2: 13, a3: 13) + result3 = instance.calc_with_named_args(a2: 12, a3: 13) + + assert_same result1, result3 + end + + def test_memoized_object_is_properly_caching_with_positioned_args + instance = ::NamedArgsHelper.new(a1: 11) + result1 = instance.calc_with_positioned_args(12, 13) + _result2 = instance.calc_with_positioned_args(13, 13) + result3 = instance.calc_with_positioned_args(12, 13) + + assert_same result1, result3 + end + + def test_memoized_object_is_properly_caching_with_mixed_args + instance = ::NamedArgsHelper.new(a1: 11) + result1 = instance.calc_with_mixed_args(12, a3: 13) + _result2 = instance.calc_with_mixed_args(13, a3: 13) + result3 = instance.calc_with_mixed_args(12, a3: 13) + + assert_same result1, result3 + end +end diff --git a/test/support/named_args_helper.rb b/test/support/named_args_helper.rb new file mode 100644 index 0000000..aab29f0 --- /dev/null +++ b/test/support/named_args_helper.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'memoist' + +class NamedArgsHelper + extend Memoist + + def initialize(a1:) + @a1 = a1 + end + + def calc_with_named_args(a2:, a3: nil) + create_object(a1: a1, a2: a2, a3: a3) + end + memoize :calc_with_named_args + + def calc_with_positioned_args(a2, a3 = nil) + create_object(a1: a1, a2: a2, a3: a3) + end + memoize :calc_with_positioned_args + + def calc_with_mixed_args(a2, a3: nil) + create_object(a1: a1, a2: a2, a3: a3) + end + memoize :calc_with_mixed_args + + private + + attr_reader :a1 + + def create_object(**named_args) + OpenStruct.new(**named_args) + end +end