All Projects → panorama-ed → memo_wise

panorama-ed / memo_wise

Licence: MIT license
The wise choice for Ruby memoization

Programming Languages

ruby
36898 projects - #4 most used programming language
shell
77523 projects

Projects that are alternatives of or similar to memo wise

Api Fuzzer
API Fuzzer which allows to fuzz request attributes using common pentesting techniques and lists vulnerabilities
Stars: ✭ 238 (-51.03%)
Mutual labels:  ruby-gem, gem
filtered
Filters ActiveRecord queries in a nice way
Stars: ✭ 28 (-94.24%)
Mutual labels:  ruby-gem, gem
jsonapi-serializer-formats
💎 Gem to enrich jsonapi-serializer with multiple formats
Stars: ✭ 20 (-95.88%)
Mutual labels:  ruby-gem, gem
syobocal
Simle gem for Syboi Calendar
Stars: ✭ 13 (-97.33%)
Mutual labels:  ruby-gem, gem
Stitches
Create a Microservice in Rails with minimal ceremony
Stars: ✭ 371 (-23.66%)
Mutual labels:  ruby-gem, gem
multi-tenancy-devise
mtdevise adds basecamp style user logins to your ruby on rails application.
Stars: ✭ 27 (-94.44%)
Mutual labels:  ruby-gem, gem
tacky
Primitive Object Memoization for Ruby
Stars: ✭ 14 (-97.12%)
Mutual labels:  ruby-gem, memoization
rails cursor pagination
Add cursor pagination to your ActiveRecord backed application
Stars: ✭ 21 (-95.68%)
Mutual labels:  ruby-gem, gem
invokable
Objects are functions! Treat any Object or Class as a Proc (like Enumerable but for Procs).
Stars: ✭ 40 (-91.77%)
Mutual labels:  ruby-gem, memoization
log-symbols
A ruby 💎gem💎 for generating log symbols
Stars: ✭ 14 (-97.12%)
Mutual labels:  ruby-gem, gem
churnalizer
Analyze your Ruby app for Churn vs Complexity
Stars: ✭ 17 (-96.5%)
Mutual labels:  ruby-gem, gem
Motion
Reactive frontend UI components for Rails in pure Ruby
Stars: ✭ 498 (+2.47%)
Mutual labels:  ruby-gem, gem
grape-jwt-authentication
A reusable Grape JWT authentication concern
Stars: ✭ 31 (-93.62%)
Mutual labels:  ruby-gem, gem
make model searchable
Adds simlpe search functionality to models
Stars: ✭ 27 (-94.44%)
Mutual labels:  ruby-gem, gem
pixitar
🧝 Pixitar is an avatar generation library written in Ruby.
Stars: ✭ 20 (-95.88%)
Mutual labels:  ruby-gem, gem
rspec n
A ruby gem that runs RSpec N times.
Stars: ✭ 37 (-92.39%)
Mutual labels:  ruby-gem, gem
Devise masquerade
Extension for devise, enable login as functionality. Add link to the masquerade_path(resource) and use it.
Stars: ✭ 380 (-21.81%)
Mutual labels:  ruby-gem, gem
Unimidi
Realtime MIDI IO for Ruby
Stars: ✭ 229 (-52.88%)
Mutual labels:  ruby-gem, gem
Discogs
A Ruby wrapper of the Discogs.com API
Stars: ✭ 195 (-59.88%)
Mutual labels:  ruby-gem
Dry Schema
Coercion and validation for data structures
Stars: ✭ 249 (-48.77%)
Mutual labels:  ruby-gem

MemoWise

Tests Code Coverage Yard Docs Gem Version Gem Downloads

Why MemoWise?

MemoWise is the wise choice for Ruby memoization, featuring:

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

  private

  # maintains privacy of the memoized method
  def private_slow_method(x)
    sleep x
    x
  end
  memo_wise :private_slow_method
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

The same three methods are exposed for class methods as well:

class Example
  prepend MemoWise

  def self.class_slow_value(x)
    sleep x
    x
  end
  memo_wise self: :class_slow_value
end

Example.class_slow_value(2) # => 2 # Sleeps for 2 seconds before returning
Example.class_slow_value(2) # => 2 # Returns immediately because the result is memoized

Example.reset_memo_wise(:class_slow_value) # Resets all memoized results for class_slow_value

Example.preset_memo_wise(:class_slow_value, 3) { 4 } # Store 4 as the result for slow_value(3)
Example.class_slow_value(3) # => 4 # Returns immediately because the result is memoized
Example.reset_memo_wise # Resets all memoized results for all methods on class

NOTE: Methods which take implicit or explicit block arguments cannot be memoized.

For more usage details, see our detailed documentation.

Benchmarks

Benchmarks are run in GitHub Actions, and the tables below are updated with every code change. Values >1.00x represent how much slower each gem’s memoized value retrieval is than the latest commit of MemoWise, according to benchmark-ips (2.11.0).

Results using Ruby 3.2.1:

Method arguments Dry::Core* (1.0.0) Memery (1.4.1)
() (none) 0.55x 3.51x
(a) 1.54x 7.47x
(a, b) 1.23x 5.84x
(a:) 1.46x 12.49x
(a:, b:) 1.17x 9.22x
(a, b:) 1.18x 9.23x
(a, *args) 0.78x 1.47x
(a:, **kwargs) 0.81x 2.11x
(a, *args, b:, **kwargs) 0.70x 1.39x

* Dry::Core may cause incorrect behavior caused by hash collisions.

Results using Ruby 2.7.7 (because these gems raise errors in Ruby 3.x):

Method arguments DDMemoize (1.0.0) Memoist (0.16.2) Memoized (1.1.1) Memoizer (1.0.3)
() (none) 23.50x 2.42x 27.07x 2.93x
(a) 22.08x 15.01x 23.11x 12.55x
(a, b) 17.31x 12.57x 17.80x 10.67x
(a:) 29.93x 23.64x 24.65x 21.39x
(a:, b:) 24.85x 20.41x 21.46x 18.96x
(a, b:) 23.42x 19.53x 19.65x 17.56x
(a, *args) 3.10x 2.29x 3.28x 1.95x
(a:, **kwargs) 2.91x 2.42x 2.59x 2.21x
(a, *args, b:, **kwargs) 2.17x 1.89x 1.96x 1.74x

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!

Thread Safety

MemoWise makes the following thread safety guarantees on all supported Ruby versions:

  1. Before a value has been memoized

    • Contended calls from multiple threads...
      • May each call the original method
      • May return different valid results (when the method is nondeterministic, like rand)
      • Will memoize exactly one valid return value
  2. After a value has been memoized

    • Contended calls from multiple threads...
      • Always return the same memoized value

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

We use dokaz to test all code examples in this README.md file, and all other non-code documentation. To run dokaz locally:

bundle exec dokaz

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 }

Further Reading

We presented at RubyConf 2021:

  • Achieving Fast Method Metaprogramming: Lessons from MemoWise (slides / benchmarks)

And we've written more about MemoWise in a series of blog posts:

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 --local with 'release'
bundle install

Then carry out these steps:

  1. Update CHANGELOG.md:

    • Add an entry for the upcoming version x.y.z
    • Add a link for this version's comparison to the bottom of CHANGELOG.md
    • Move content from Unreleased to the upcoming version x.y.z
    • Change Unreleased section to say:
      **Gem enhancements:** none
      
      _No breaking changes!_
      
      **Project enhancements:** none
      
    • Commit with title Update CHANGELOG.md for x.y.z
  2. Update lib/memo_wise/version.rb

    • Replace with upcoming version x.y.z
    • Run bundle install to update Gemfile.lock
    • Commit with title Bump version to x.y.z
  3. 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.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].