Module: Farce::Memoize
- Defined in:
- lib/farce/memoize.rb
Overview
Ractor-friendly memoization module.
Replaces the following pattern:
class MyClass
def time
@time ||= Time.now
end
end
Using Farce::Memoize instead gives you:
- Allows memoization after freezing
- Is Ractor-safe (with optional scoping or transfer modes) and thread-safe
- Adds one instance variable per memoized method (avoids hash lookup and doesn't bust Ruby's data structures)
- Is Marshal-dump/load friendly
- Allows weak memoization, where the memoized value is kept until it is garbage collected
- Does not need to run code on the main Ractor for memoization
Methods with arguments cannot be memoized. Explicitly use a map instead.
Defined Under Namespace
Classes: Mixin
Class Method Summary collapse
-
.[](*methods, scope: :global, mode: :smart, weak: false, visibility: nil) ⇒ Mixin
Creates or retrieves the memoization mixin for the given methods, scope, and mode.
Instance Method Summary collapse
-
#memoize(*methods, scope: :global, mode: :smart, weak: false, visibility: nil) ⇒ Array<Symbol>
Creates a memoization mixin for the given arguments and adds it to the current class.
Class Method Details
Instance Method Details
#memoize(*methods, scope: :global, mode: :smart, weak: false, visibility: nil) ⇒ Array<Symbol>
Creates a memoization mixin for the given arguments and adds it to the current class.
Can be used as a prefix to def:
memoize def some_method = expensive_computation
Or called separately with the method names, useful for passing additional options:
def some_method = expensive_computation
# memoize some_method per thread
memoize :some_method, scope: :thread
Visibility of Memoized Methods
If no visibility is provided, it will determine the visibility based on the existing method definitions.
If none exist, it will default to public. This happens when memoize is called.
Make sure you set the visibility before calling this method.
This works:
memoize private def some_method = expensive_computation
As does this:
private
memoize def some_method = expensive_computation
Or this:
private def some_method = expensive_computation
memoize :some_method
But this doesn't, as the mixin's method will end up public:
private memoize def some_method = expensive_computation
Same for this one, as the mixin's method will also end up public:
memoize :some_method
private def some_method = expensive_computation
You can of course set visibility explicitly when calling memoize:
memoize :some_method, visibility: :private
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/farce/memoize.rb', line 293 def memoize(*methods, visibility: nil, warn: true, **) if visibility.nil? groups = methods.group_by do |method| next :protected if protected_method_defined?(method) next :private if private_method_defined?(method) :public end return groups.flat_map { memoize(*_2, visibility: _1, warn:, **) } end if warn methods.each do |name| method = instance_method(name) next if method.parameters.empty? warn "#{method.owner}##{name} expects parameters and may be unsuitable for memoization" rescue NameError => e raise e unless e.name == name.to_sym end end mixin = Farce::Memoize[*methods, visibility:, **] prepend mixin mixin.memoized_methods end |