Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/tycooon/memery
A gem for memoization in Ruby
https://github.com/tycooon/memery
memoization ruby
Last synced: 8 days ago
JSON representation
A gem for memoization in Ruby
- Host: GitHub
- URL: https://github.com/tycooon/memery
- Owner: tycooon
- License: mit
- Created: 2017-12-16T11:50:15.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2024-09-13T20:41:06.000Z (about 2 months ago)
- Last Synced: 2024-10-14T00:25:18.309Z (24 days ago)
- Topics: memoization, ruby
- Language: Ruby
- Homepage:
- Size: 68.4 KB
- Stars: 174
- Watchers: 7
- Forks: 13
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# Memery [![Gem Version](https://badge.fury.io/rb/memery.svg)](https://badge.fury.io/rb/memery) ![Build Status](https://github.com/tycooon/memery/actions/workflows/ci.yml/badge.svg) [![Coverage Status](https://coveralls.io/repos/github/tycooon/memery/badge.svg?branch=master)](https://coveralls.io/github/tycooon/memery?branch=master)
Memery is a Ruby gem that simplifies memoization of method return values. In Ruby, memoization typically looks like this:
```ruby
def user
@user ||= User.find(some_id)
end
```However, this approach fails if the calculated result can be `nil` or `false`, or if the method uses arguments. Additionally, multi-line methods require extra `begin`/`end` blocks:
```ruby
def user
@user ||= begin
some_id = calculate_id
klass = calculate_klass
klass.find(some_id)
end
end
```To handle these situations, memoization gems like Memery exist. The example above can be rewritten using Memery as follows:
```ruby
memoize def user
some_id = calculate_id
klass = calculate_klass
klass.find(some_id)
end
```## Installation
Add `gem "memery"` to your Gemfile.
## Usage
```ruby
class A
include Memerymemoize def call
puts "calculating"
42
end# Alternatively:
# def call
# ...
# end
# memoize :call
enda = A.new
a.call # => 42
a.call # => 42
a.call # => 42
# "calculating" will only be printed once.a.call { 1 } # => 42
# "calculating" will be printed again because passing a block disables memoization.
```Memoization works with methods that take arguments. The memoization is based on these arguments using an internal hash, so the following will work as expected:
```ruby
class A
include Memerymemoize def call(arg1, arg2)
puts "calculating"
arg1 + arg2
end
enda = A.new
a.call(1, 5) # => 6
a.call(2, 15) # => 17
a.call(1, 5) # => 6
# "calculating" will be printed twice, once for each unique argument list.
```For class methods:
```ruby
class B
class << self
include Memerymemoize def call
puts "calculating"
42
end
end
endB.call # => 42
B.call # => 42
B.call # => 42
# "calculating" will only be printed once.
```### Conditional Memoization
```ruby
class A
include Memeryattr_accessor :environment
def call
puts "calculating"
42
endmemoize :call, condition: -> { environment == 'production' }
enda = A.new
a.environment = 'development'
a.call # => 42
# calculating
a.call # => 42
# calculating
a.call # => 42
# calculating
# Text will be printed every time because result of condition block is `false`.a.environment = 'production'
a.call # => 42
# calculating
a.call # => 42
a.call # => 42
# Text will be printed only once because there is memoization
# with `true` result of condition block.
```### Memoization with Time-to-Live (TTL)
```ruby
class A
include Memerydef call
puts "calculating"
42
endmemoize :call, ttl: 3 # seconds
enda = A.new
a.call # => 42
# calculating
a.call # => 42
a.call # => 42
# Text will be printed again only after 3 seconds of time-to-live.
# 3 seconds later...
a.call # => 42
# calculating
a.call # => 42
a.call # => 42
# another 3 seconds later...
a.call # => 42
# calculating
a.call # => 42
a.call # => 42
```### Checking if a Method is Memoized
```ruby
class A
include Memerymemoize def call
puts "calculating"
42
enddef execute
puts "non-memoized"
end
enda = A.new
a.memoized?(:call) # => true
a.memoized?(:execute) # => false
```## Differences from Other Gems
Memery is similar to [Memoist](https://github.com/matthewrudy/memoist), but it doesn't override methods. Instead, it uses Ruby 2's `Module.prepend` feature. This approach is cleaner, allowing you to inspect the original method body with `method(:x).super_method.source`, and it ensures that subclasses' methods function properly. If you redefine a memoized method in a subclass, it won't be memoized by default. You can memoize it normally without needing an awkward `identifier: ` argument, and it will just work:
```ruby
class A
include Memerymemoize def x(param)
param
end
endclass B < A
memoize def x(param)
super(2) * param
end
endb = B.new
b.x(1) # => 2
b.x(2) # => 4
b.x(3) # => 6b.instance_variable_get(:@_memery_memoized_values)
# => {:x_70318201388120=>{[1]=>2, [2]=>4, [3]=>6}, :x_70318184636620=>{[2]=>2}}
```Note how both methods' return values are cached separately without interfering with each other.
Another key difference is that Memery doesn't change the method's signature (no extra `reload` parameter). If you need an unmemoized result, simply create an unmemoized version of the method:
```ruby
memoize def users
get_users
enddef get_users
# ...
end
```Alternatively, you can clear the entire instance's cache:
```ruby
a.clear_memery_cache!
```You can also provide a block, though this approach is somewhat hacky:
```ruby
a.users {}
```## Object Shape Optimization
In Ruby 3.2, a new optimization called "object shape" was introduced, which can have negative interactions with dynamically added instance variables. Memery minimizes this impact by introducing only one new instance variable after initialization (`@_memery_memoized_values`). If you need to ensure a specific object shape, you can call `clear_memery_cache!` in your initializer to set the instance variable ahead of time.
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/tycooon/memery.
## License
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
## Author
Created by Yuri Smirnov.