MemoWise
Why MemoWise?
MemoWise is the wise choice for Ruby memoization, featuring:
- Fast performance of memoized reads (with benchmarks)
- Support for resetting and presetting memoized values
- Support for memoization on frozen objects
- Support for memoization of class and module methods
- Full documentation and test coverage!
Installation
Add this line to your application's Gemfile:
gem 'memo_wise'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install memo_wise
Usage
When you prepend MemoWise within a class or module, MemoWise exposes three
methods:
class Example
prepend MemoWise
def slow_value(x)
sleep x
x
end
memo_wise :slow_value
end
ex = Example.new
ex.slow_value(2) # => 2 # Sleeps for 2 seconds before returning
ex.slow_value(2) # => 2 # Returns immediately because the result is memoized
ex.reset_memo_wise(:slow_value) # Resets all memoized results for slow_value
ex.slow_value(2) # => 2 # Sleeps for 2 seconds before returning
ex.slow_value(2) # => 2 # Returns immediately because the result is memoized
# NOTE: Memoization can also be reset for all methods, or for just one argument.
ex.preset_memo_wise(:slow_value, 3) { 4 } # Store 4 as the result for slow_value(3)
ex.slow_value(3) # => 4 # Returns immediately because the result is memoized
ex.reset_memo_wise # Resets all memoized results for all methods on ex
Methods which take implicit or explicit block arguments cannot be memoized.
For more usage details, see our detailed documentation.
Benchmarks
Benchmarks measure memoized value retrieval time using
benchmark-ips. All benchmarks are
run on Ruby 3.0.0, except as indicated below for specific gems. Benchmarks are
run in GitHub Actions and updated in every PR that changes code.
| Method arguments | memo_wise (0.1.0) |
memery (1.3.0) |
memoist* (0.16.2) |
memoized* (1.0.2) |
memoizer* (1.0.3) |
|---|---|---|---|---|---|
() (none) |
baseline | 13.17x slower | 2.85x slower | 1.30x slower | 3.05x slower |
(a, b) |
baseline | 1.93x slower | 2.20x slower | 1.97x slower | 1.86x slower |
(a:, b:) |
baseline | 3.05x slower | 2.34x slower | 2.27x slower | 2.14x slower |
(a, b:) |
baseline | 1.50x slower | 1.63x slower | 1.56x slower | 1.48x slower |
(a, *args) |
baseline | 1.91x slower | 2.13x slower | 1.90x slower | 1.88x slower |
(a:, **kwargs) |
baseline | 3.08x slower | 2.37x slower | 2.24x slower | 2.09x slower |
(a, *args, b:, **kwargs) |
baseline | 1.72x slower | 1.61x slower | 1.56x slower | 1.67x slower |
*Indicates a benchmark run on Ruby 2.7.2 because the gem raises errors in Ruby 3.0.0 due to its incorrect handling of keyword arguments.
You can run benchmarks yourself with:
$ cd benchmarks
$ bundle install
$ bundle exec ruby benchmarks.rb
If your results differ from what's posted here, let us know!
Development
After checking out the repo, run bin/setup to install dependencies. Then, run
rake spec to run the tests. You can also run bin/console for an interactive
prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To
release a new version, update the version number in version.rb, and then run
bundle exec rake release, which will create a git tag for the version, push
git commits and tags, and push the .gem file to
rubygems.org.
Documentation
Documentation is Automatically Generated
We maintain API documentation using YARD, which is published automatically at RubyDoc.info. To edit documentation locally and see it rendered in your browser, run:
bundle exec yard server
Documentation Examples are Automatically Tested
We use yard-doctest to test all
code examples in our YARD documentation. To run doctest locally:
bundle exec yard doctest
A Note on Testing
When testing memoized module methods, note that some testing setups will
reuse the same instance (which includes/extends/prepends the module)
across tests, which can result in confusing test failures when this differs from
how you use the code in production.
For example, Rails view helpers are modules that are commonly tested with a
shared view instance. Rails initializes a new view instance for each web request so any view helper
methods would only be memoized for the duration of that web request, but in
tests (such as when using
rspec-rails's helper),
the memoization may persist across tests. In this case, simply reset the
memoization between your tests with something like:
after(:each) { helper.reset_memo_wise }
Logo
MemoWise's logo was created by Luci Cooke. The
logo is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/panorama-ed/memo_wise. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
Releasing
To make a new release of MemoWise to
RubyGems, first install the release
dependencies (e.g. rake) as follows:
bundle config set --local with 'release'
bundle install
Then carry out these steps:
Update
CHANGELOG.md:- Add an entry for the upcoming version x.y.z
- Move content from Unreleased to the upcoming version x.y.z
- Commit with title
Update CHANGELOG.md for x.y.z
Update
lib/memo_wise/version.rb- Replace with upcoming version x.y.z
- Commit with title
Bump version to x.y.z
bundle exec rake release
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the MemoWise project's codebases, issue trackers, chat
rooms and mailing lists is expected to follow the
code of conduct.