Class: MemoWise::InternalAPI
- Inherits:
-
Object
- Object
- MemoWise::InternalAPI
- Defined in:
- lib/memo_wise/internal_api.rb
Constant Summary collapse
- NONE =
:none
- ONE_REQUIRED_POSITIONAL =
:one_required_positional
- ONE_REQUIRED_KEYWORD =
:one_required_keyword
- MULTIPLE_REQUIRED =
:multiple_required
- SPLAT =
:splat
- DOUBLE_SPLAT =
:double_splat
- SPLAT_AND_DOUBLE_SPLAT =
:splat_and_double_splat
Class Method Summary collapse
-
.args_str(method) ⇒ String
The arguments string to use when defining our new memoized version of the method.
-
.call_str(method) ⇒ String
The arguments string to use when calling the original method in our new memoized version of the method, i.e.
-
.create_memo_wise_state!(obj) ⇒ Object
Create initial mutable state to store memoized values if it doesn’t already exist.
-
.key_str(method) ⇒ String
The string to use as a hash key when looking up a memoized value, based on the method’s arguments.
-
.method_arguments(method) ⇒ Symbol
One of: - :none (example: ‘def foo`) - :one_required_positional (example: `def foo(a)`) - :one_required_keyword (example: `def foo(a:)`) - :multiple_required (examples: `def foo(a, b)`, `def foo(a:, b:)`, `def foo(a, b:)`) - :splat (examples: `def foo(a=1)`, `def foo(a, *b)`) - :double_splat (examples: `def foo(a: 1)`, `def foo(a:, **b)`) - :splat_and_double_splat (examples: `def foo(a=1, b: 2)`, `def foo(a=1, **b)`, `def foo(*a, **b)`).
-
.method_visibility(target, method_name) ⇒ :private, ...
Returns visibility of an instance method defined on class ‘target`.
-
.original_class_from_singleton(klass) ⇒ Object
Find the original class for which the given class is the corresponding “singleton class”.
-
.original_memo_wised_name(method_name) ⇒ Symbol
Convention we use for renaming the original method when we replace with the memoized version in memo_wise.
-
.validate_memo_wised!(target, method_name) ⇒ Object
Validates that memo_wise has already been called on ‘method_name`.
Class Method Details
.args_str(method) ⇒ String
Returns the arguments string to use when defining our new memoized version of the method.
69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/memo_wise/internal_api.rb', line 69 def self.args_str(method) case method_arguments(method) when SPLAT then "*args" when DOUBLE_SPLAT then "**kwargs" when ONE_REQUIRED_POSITIONAL, ONE_REQUIRED_KEYWORD, MULTIPLE_REQUIRED method.parameters.map do |type, name| "#{name}#{':' if type == :keyreq}" end.join(", ") else raise ArgumentError, "Unexpected arguments for #{method.name}" end end |
.call_str(method) ⇒ String
Returns the arguments string to use when calling the original method in our new memoized version of the method, i.e. when setting a memoized value.
86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/memo_wise/internal_api.rb', line 86 def self.call_str(method) case method_arguments(method) when SPLAT then "*args" when DOUBLE_SPLAT then "**kwargs" when SPLAT_AND_DOUBLE_SPLAT then "*args, **kwargs" when ONE_REQUIRED_POSITIONAL, ONE_REQUIRED_KEYWORD, MULTIPLE_REQUIRED method.parameters.map do |type, name| type == :req ? name : "#{name}: #{name}" end.join(", ") else raise ArgumentError, "Unexpected arguments for #{method.name}" end end |
.create_memo_wise_state!(obj) ⇒ Object
Create initial mutable state to store memoized values if it doesn’t already exist
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/memo_wise/internal_api.rb', line 12 def self.create_memo_wise_state!(obj) # `@_memo_wise` stores memoized results of method calls in a hash keyed on # method name. The structure is slightly different for different types of # methods. It looks like: # { # zero_arg_method_name: :memoized_result, # single_arg_method_name: { arg1 => :memoized_result, ... }, # # # This is faster than a single top-level hash key of: [:multi_arg_method_name, arg1, arg2] # multi_arg_method_name: { arg1 => { arg2 => :memoized_result, ... }, ... } # } obj.instance_variable_set(:@_memo_wise, {}) unless obj.instance_variable_defined?(:@_memo_wise) obj end |
.key_str(method) ⇒ String
Returns the string to use as a hash key when looking up a memoized value, based on the method’s arguments.
103 104 105 106 107 108 109 110 |
# File 'lib/memo_wise/internal_api.rb', line 103 def self.key_str(method) case method_arguments(method) when SPLAT then "args" when DOUBLE_SPLAT then "kwargs" else raise ArgumentError, "Unexpected arguments for #{method.name}" end end |
.method_arguments(method) ⇒ Symbol
Returns one of:
-
:none (example: ‘def foo`)
-
:one_required_positional (example: ‘def foo(a)`)
-
:one_required_keyword (example: ‘def foo(a:)`)
-
:multiple_required (examples: ‘def foo(a, b)`, `def foo(a:, b:)`, `def foo(a, b:)`)
-
:splat (examples: ‘def foo(a=1)`, `def foo(a, *b)`)
-
:double_splat (examples: ‘def foo(a: 1)`, `def foo(a:, **b)`)
-
:splat_and_double_splat (examples: ‘def foo(a=1, b: 2)`, `def foo(a=1, **b)`, `def foo(*a, **b)`).
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/memo_wise/internal_api.rb', line 46 def self.method_arguments(method) return NONE if method.arity.zero? parameters = method.parameters.map(&:first) if parameters == [:req] ONE_REQUIRED_POSITIONAL elsif parameters == [:keyreq] ONE_REQUIRED_KEYWORD elsif parameters.all? { |type| type == :req || type == :keyreq } MULTIPLE_REQUIRED elsif parameters & %i[req opt rest] == parameters.uniq SPLAT elsif parameters & %i[keyreq key keyrest] == parameters.uniq DOUBLE_SPLAT else SPLAT_AND_DOUBLE_SPLAT end end |
.method_visibility(target, method_name) ⇒ :private, ...
Returns visibility of an instance method defined on class ‘target`.
160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/memo_wise/internal_api.rb', line 160 def self.method_visibility(target, method_name) if target.private_method_defined?(method_name) :private elsif target.protected_method_defined?(method_name) :protected elsif target.public_method_defined?(method_name) :public else raise ArgumentError, "#{method_name.inspect} must be a method on #{target}" end end |
.original_class_from_singleton(klass) ⇒ Object
Find the original class for which the given class is the corresponding “singleton class”.
See stackoverflow.com/questions/54531270/retrieve-a-ruby-object-from-its-singleton-class
126 127 128 129 130 |
# File 'lib/memo_wise/internal_api.rb', line 126 def self.original_class_from_singleton(klass) raise ArgumentError, "Must be a singleton class: #{klass.inspect}" unless klass.singleton_class? find_attached_object(klass) end |
.original_memo_wised_name(method_name) ⇒ Symbol
Convention we use for renaming the original method when we replace with the memoized version in MemoWise.memo_wise.
141 142 143 |
# File 'lib/memo_wise/internal_api.rb', line 141 def self.original_memo_wised_name(method_name) :"_memo_wise_original_#{method_name}" end |
.validate_memo_wised!(target, method_name) ⇒ Object
Validates that MemoWise.memo_wise has already been called on ‘method_name`.
179 180 181 182 183 184 185 |
# File 'lib/memo_wise/internal_api.rb', line 179 def self.validate_memo_wised!(target, method_name) original_name = original_memo_wised_name(method_name) unless target_class(target).private_method_defined?(original_name) raise ArgumentError, "#{method_name} is not a memo_wised method" end end |