class Dry::Core::Memoizable::Memoizer

def define_memoizable(method:)

Other tags:
    Api: - private
def define_memoizable(method:)
  parameters = method.parameters
  mod = self
  kernel = KERNEL
  if parameters.empty?
    key = "#{__id__}:#{method.name}".hash.abs
    define_method(method.name) do
      value = super()
      if kernel[:frozen].bind_call(self)
        # It's not possible to modify singleton classes
        # of frozen objects
        mod.remove_method(method.name)
        mod.module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
          def #{method.name}                          # def slow_calc
            cached = @__memoized__[#{key}]            #   cached = @__memoized__[12345678]
                                                      #
            if cached || @__memoized__.key?(#{key})   #   if cached || @__memoized__.key?(12345678)
              cached                                  #     cached
            else                                      #   else
              @__memoized__[#{key}] = super           #     @__memoized__[12345678] = super
            end                                       #   end
          end                                         # end
        RUBY
      else
        # We make an attr_reader for computed value.
        # Readers are "special-cased" in ruby so such
        # access will be the fastest way, faster than you'd
        # expect :)
        attr_name = :"__memozed_#{key}__"
        ivar_name = :"@#{attr_name}"
        kernel[:ivar_set].bind_call(self, ivar_name, value)
        eigenclass = kernel[:singleton].bind_call(self)
        eigenclass.attr_reader(attr_name)
        eigenclass.alias_method(method.name, attr_name)
        eigenclass.remove_method(attr_name)
      end
      value
    end
  else
    mapping = parameters.to_h { |k, v = nil| [k, v] }
    params, binds = declaration(parameters, mapping)
    last_param = parameters.last
    if last_param[0].eql?(:block) && !last_param[1].eql?(:&)
      Deprecations.warn(<<~WARN)
        Memoization for block-accepting methods isn't safe.
        Every call creates a new block instance bloating cached results.
        In the future, blocks will still be allowed but won't participate in
        cache key calculation.
      WARN
    end
    module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
      def #{method.name}(#{params.join(", ")})                 # def slow_calc(arg1, arg2, arg3)
        key = [:"#{method.name}", #{binds.join(", ")}].hash    #   key = [:slow_calc, arg1, arg2, arg3].hash
                                                               #
        if @__memoized__.key?(key)                             #   if @__memoized__.key?(key)
          @__memoized__[key]                                   #     @__memoized__[key]
        else                                                   #   else
          @__memoized__[key] = super                           #     @__memoized__[key] = super
        end                                                    #   end
      end                                                      # end
    RUBY
  end
end