lib/memo_wise.rb in memo_wise-1.6.0 vs lib/memo_wise.rb in memo_wise-1.7.0

- old
+ new

@@ -193,16 +193,47 @@ _memo_wise_hash.fetch(#{key}) do _memo_wise_hash[#{key}] = #{original_memo_wised_name}(#{MemoWise::InternalAPI.call_str(method)}) end end HEREDOC - # MemoWise::InternalAPI::MULTIPLE_REQUIRED, MemoWise::InternalAPI::SPLAT, - # MemoWise::InternalAPI::DOUBLE_SPLAT, MemoWise::InternalAPI::SPLAT_AND_DOUBLE_SPLAT - else + when MemoWise::InternalAPI::MULTIPLE_REQUIRED + # When we have multiple required params, we store the memoized values in a deeply nested hash, like: + # { method_name: { arg1 => { arg2 => { arg3 => memoized_value } } } } + last_index = method.parameters.size + layers = method.parameters.map.with_index(1) do |(_, name), index| + prev_hash = "_memo_wise_hash#{index - 1 if index > 1}" + fallback = if index == last_index + "#{original_memo_wised_name}(#{MemoWise::InternalAPI.call_str(method)})" + else + "{}" + end + "_memo_wise_hash#{index} = #{prev_hash}.fetch(#{name}) { #{prev_hash}[#{name}] = #{fallback} }" + end klass.module_eval <<~HEREDOC, __FILE__, __LINE__ + 1 def #{method_name}(#{MemoWise::InternalAPI.args_str(method)}) _memo_wise_hash = (@_memo_wise[:#{method_name}] ||= {}) + #{layers.join("\n ")} + end + HEREDOC + when MemoWise::InternalAPI::SPLAT_AND_DOUBLE_SPLAT + # When we have both *args and **kwargs, we store the memoized values in a deeply nested hash, like: + # { method_name: { args => { kwargs => memoized_value } } } + klass.module_eval <<~HEREDOC, __FILE__, __LINE__ + 1 + def #{method_name}(*args, **kwargs) + _memo_wise_hash = (@_memo_wise[:#{method_name}] ||= {}) + _memo_wise_kwargs_hash = _memo_wise_hash.fetch(args) do + _memo_wise_hash[args] = {} + end + _memo_wise_kwargs_hash.fetch(kwargs) do + _memo_wise_kwargs_hash[kwargs] = #{original_memo_wised_name}(#{MemoWise::InternalAPI.call_str(method)}) + end + end + HEREDOC + else # MemoWise::InternalAPI::SPLAT, MemoWise::InternalAPI::DOUBLE_SPLAT + klass.module_eval <<~HEREDOC, __FILE__, __LINE__ + 1 + def #{method_name}(#{MemoWise::InternalAPI.args_str(method)}) + _memo_wise_hash = (@_memo_wise[:#{method_name}] ||= {}) _memo_wise_key = #{MemoWise::InternalAPI.key_str(method)} _memo_wise_hash.fetch(_memo_wise_key) do _memo_wise_hash[_memo_wise_key] = #{original_memo_wised_name}(#{MemoWise::InternalAPI.call_str(method)}) end end @@ -428,16 +459,27 @@ when MemoWise::InternalAPI::ONE_REQUIRED_POSITIONAL then hash[args.first] = yield when MemoWise::InternalAPI::ONE_REQUIRED_KEYWORD then hash[kwargs.first.last] = yield when MemoWise::InternalAPI::SPLAT then hash[args] = yield when MemoWise::InternalAPI::DOUBLE_SPLAT then hash[kwargs] = yield when MemoWise::InternalAPI::MULTIPLE_REQUIRED - key = method.parameters.map.with_index do |(type, name), idx| - type == :req ? args[idx] : kwargs[name] + n_parameters = method.parameters.size + method.parameters.each_with_index do |(type, name), index| + val = type == :req ? args[index] : kwargs[name] + + # Walk through the layers of nested hashes. When we get to the final + # layer, yield to the block to set its value. + if index < n_parameters - 1 + hash = (hash[val] ||= {}) + else + hash[val] = yield + end end - hash[key] = yield else # MemoWise::InternalAPI::SPLAT_AND_DOUBLE_SPLAT - hash[[args, kwargs]] = yield + # When we have both *args and **kwargs, we store the memoized values like: + # { method_name: { args => { kwargs => memoized_value } } } + # so we need to initialize `hash[args]`` if it does not already exist. + (hash[args] ||= {})[kwargs] = yield end end # Resets memoized results of a given method, or all methods. # @@ -528,17 +570,28 @@ case method_arguments when MemoWise::InternalAPI::ONE_REQUIRED_POSITIONAL then method_hash&.delete(args.first) when MemoWise::InternalAPI::ONE_REQUIRED_KEYWORD then method_hash&.delete(kwargs.first.last) when MemoWise::InternalAPI::SPLAT then method_hash&.delete(args) when MemoWise::InternalAPI::DOUBLE_SPLAT then method_hash&.delete(kwargs) - else # MemoWise::InternalAPI::MULTIPLE_REQUIRED, MemoWise::InternalAPI::SPLAT_AND_DOUBLE_SPLAT - key = if method_arguments == MemoWise::InternalAPI::SPLAT_AND_DOUBLE_SPLAT - [args, kwargs] - else - method.parameters.map.with_index do |(type, name), i| - type == :req ? args[i] : kwargs[name] - end - end - method_hash&.delete(key) + when MemoWise::InternalAPI::SPLAT_AND_DOUBLE_SPLAT + # Here, memoized values are stored like: + # { method_name: { args => { kwargs => memoized_value } } } + # so we need to delete the innermost value (because the same args array + # may have multiple memoized values for different kwargs hashes). + method_hash&.[](args)&.delete(kwargs) + else # MemoWise::InternalAPI::MULTIPLE_REQUIRED + n_parameters = method.parameters.size + method.parameters.each_with_index do |(type, name), index| + val = type == :req ? args[index] : kwargs[name] + + # Walk through the layers of nested hashes. When we get to the final + # layer, delete its value. We use the safe navigation operator to + # gracefully handle any layer not yet existing. + if index < n_parameters - 1 + method_hash = method_hash&.[](val) + else + method_hash&.delete(val) + end + end end end end