{"id":13878719,"url":"https://github.com/panorama-ed/memo_wise","last_synced_at":"2025-05-14T04:07:37.463Z","repository":{"id":38953295,"uuid":"261538171","full_name":"panorama-ed/memo_wise","owner":"panorama-ed","description":"The wise choice for Ruby memoization","archived":false,"fork":false,"pushed_at":"2025-05-06T19:56:07.000Z","size":805,"stargazers_count":605,"open_issues_count":14,"forks_count":25,"subscribers_count":29,"default_branch":"main","last_synced_at":"2025-05-06T20:22:49.233Z","etag":null,"topics":["benchmarks","gem","memoization","memoization-helper","memoization-library","optimization","performance","performance-optimization","ruby","ruby-gem","ruby-memoization"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/panorama-ed.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-05-05T17:23:14.000Z","updated_at":"2025-05-06T19:05:54.000Z","dependencies_parsed_at":"2023-02-10T12:00:23.408Z","dependency_job_id":"9a5773ba-90dd-429a-8b55-08f47f8cc886","html_url":"https://github.com/panorama-ed/memo_wise","commit_stats":{"total_commits":248,"total_committers":18,"mean_commits":"13.777777777777779","dds":0.7701612903225806,"last_synced_commit":"b2bd327ac1d0adc853d496fa2f3a1ebf67313361"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/panorama-ed%2Fmemo_wise","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/panorama-ed%2Fmemo_wise/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/panorama-ed%2Fmemo_wise/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/panorama-ed%2Fmemo_wise/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/panorama-ed","download_url":"https://codeload.github.com/panorama-ed/memo_wise/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254069217,"owners_count":22009511,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["benchmarks","gem","memoization","memoization-helper","memoization-library","optimization","performance","performance-optimization","ruby","ruby-gem","ruby-memoization"],"created_at":"2024-08-06T08:01:57.710Z","updated_at":"2025-05-14T04:07:37.425Z","avatar_url":"https://github.com/panorama-ed.png","language":"Ruby","funding_links":[],"categories":["Ruby","Core Extensions"],"sub_categories":[],"readme":"\n\u003cp\u003e\n  \u003cimg src=\"logo/logo.png\" width=\"175\"/\u003e\n\u003c/p\u003e\n\n# `MemoWise`\n\n[![Tests](https://github.com/panorama-ed/memo_wise/workflows/Main/badge.svg)](https://github.com/panorama-ed/memo_wise/actions?query=workflow%3AMain)\n[![Code Coverage](https://codecov.io/gh/panorama-ed/memo_wise/branch/main/graph/badge.svg)](https://codecov.io/gh/panorama-ed/memo_wise)\n[![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/gems/memo_wise)\n[![Gem Version](https://img.shields.io/gem/v/memo_wise.svg)](https://rubygems.org/gems/memo_wise)\n[![Gem Downloads](https://img.shields.io/gem/dt/memo_wise.svg)](https://rubygems.org/gems/memo_wise)\n\n## Why `MemoWise`?\n\n`MemoWise` is **the wise choice for Ruby memoization**, featuring:\n\n  * Fast performance of memoized reads (with [benchmarks](#benchmarks))\n  * Support for [resetting](https://rubydoc.info/github/panorama-ed/memo_wise/MemoWise#reset_memo_wise-instance_method) and [presetting](https://rubydoc.info/github/panorama-ed/memo_wise/MemoWise#preset_memo_wise-instance_method) memoized values\n  * Support for memoization on frozen objects\n  * Support for memoization of class and module methods\n  * Support for inheritance of memoized class and instance methods\n  * Documented and tested [thread-safety guarantees](#thread-safety)\n  * Full [documentation](https://rubydoc.info/github/panorama-ed/memo_wise/MemoWise) and [test coverage](https://codecov.io/gh/panorama-ed/memo_wise)!\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'memo_wise'\n```\n\nAnd then execute:\n\n    $ bundle install\n\nOr install it yourself as:\n\n    $ gem install memo_wise\n\n## Usage\n\nWhen you `prepend MemoWise` within a class or module, `MemoWise` exposes three\nmethods:\n\n- [`memo_wise`](https://rubydoc.info/github/panorama-ed/memo_wise/MemoWise#memo_wise-class_method)\n- [`preset_memo_wise`](https://rubydoc.info/github/panorama-ed/memo_wise/MemoWise#preset_memo_wise-instance_method)\n- [`reset_memo_wise`](https://rubydoc.info/github/panorama-ed/memo_wise/MemoWise#reset_memo_wise-instance_method)\n\n```ruby\nclass Example\n  prepend MemoWise\n\n  def slow_value(x)\n    sleep x\n    x\n  end\n  memo_wise :slow_value\n\n  private\n\n  # maintains privacy of the memoized method\n  def private_slow_method(x)\n    sleep x\n    x\n  end\n  memo_wise :private_slow_method\nend\n\nex = Example.new\nex.slow_value(2) # =\u003e 2 # Sleeps for 2 seconds before returning\nex.slow_value(2) # =\u003e 2 # Returns immediately because the result is memoized\n\nex.reset_memo_wise(:slow_value) # Resets all memoized results for slow_value\nex.slow_value(2) # =\u003e 2 # Sleeps for 2 seconds before returning\nex.slow_value(2) # =\u003e 2 # Returns immediately because the result is memoized\n# NOTE: Memoization can also be reset for all methods, or for just one argument.\n\nex.preset_memo_wise(:slow_value, 3) { 4 } # Store 4 as the result for slow_value(3)\nex.slow_value(3) # =\u003e 4 # Returns immediately because the result is memoized\nex.reset_memo_wise # Resets all memoized results for all methods on ex\n```\n\nThe same three methods are exposed for class methods as well:\n\n```ruby\nclass Example\n  prepend MemoWise\n\n  def self.class_slow_value(x)\n    sleep x\n    x\n  end\n  memo_wise self: :class_slow_value\nend\n\nExample.class_slow_value(2) # =\u003e 2 # Sleeps for 2 seconds before returning\nExample.class_slow_value(2) # =\u003e 2 # Returns immediately because the result is memoized\n\nExample.reset_memo_wise(:class_slow_value) # Resets all memoized results for class_slow_value\n\nExample.preset_memo_wise(:class_slow_value, 3) { 4 } # Store 4 as the result for slow_value(3)\nExample.class_slow_value(3) # =\u003e 4 # Returns immediately because the result is memoized\nExample.reset_memo_wise # Resets all memoized results for all methods on class\n```\n\n**NOTE:** Methods which take implicit or explicit block arguments cannot be\nmemoized.\n\nFor more usage details, see our detailed [documentation](#documentation).\n\n## Benchmarks\n\nBenchmarks are run in GitHub Actions, and the tables below are updated with every code change. **Values \u003e1.00x represent how much _slower_ each gem’s memoized value retrieval is than the latest commit of `MemoWise`**, according to [`benchmark-ips`](https://github.com/evanphx/benchmark-ips) (2.14.0).\n\nResults using Ruby 3.4.3:\n\n|Method arguments|`alt_memery` (2.1.0)|`dry-core`\\* (1.1.0)|`memery` (1.7.0)|`memoist3` (1.0.0)|`short_circu_it` (0.29.3)|\n|--|--|--|--|--|--|\n|`()` (none)|12.27x|0.58x|3.36x|2.79x|18.43x|\n|`(a)`|9.50x|0.99x|3.79x|14.93x|14.16x|\n|`(a, b)`|7.51x|0.83x|2.96x|11.84x|11.19x|\n|`(a:)`|14.32x|1.00x|6.46x|19.60x|12.81x|\n|`(a:, b:)`|12.24x|0.86x|5.56x|20.89x|10.78x|\n|`(a, b:)`|11.98x|0.85x|5.42x|16.25x|10.76x|\n|`(a, *args)`|1.91x|0.66x|0.75x|2.96x|2.80x|\n|`(a:, **kwargs)`|2.73x|0.71x|1.22x|4.69x|2.41x|\n|`(a, *args, b:, **kwargs)`|1.76x|0.63x|0.84x|3.00x|1.52x|\n\n\\* `dry-core`\n[may cause incorrect behavior caused by hash collisions](https://github.com/dry-rb/dry-core/issues/63).\n\nYou can run benchmarks yourself with:\n\n```bash\n$ cd benchmarks\n$ bundle install\n$ bundle exec ruby benchmarks.rb\n```\n\nIf your results differ from what's posted here, or if there's another gem we\nshould be benchmarking,\n[let us know](https://github.com/panorama-ed/memo_wise/issues/new)!\n\n## Thread Safety\n\nMemoWise makes the following **thread safety** guarantees on all supported Ruby\nversions:\n\n1. **Before** a value has been memoized\n\n   * Contended calls from multiple threads...\n      * May each call the original method\n      * May return different valid results (when the method is nondeterministic,\n        like `rand`)\n      * Will memoize exactly one valid return value\n\n2. **After** a value has been memoized\n\n   * Contended calls from multiple threads...\n     * Always return the same memoized value\n\n## Documentation\n\n### Automatically Generated Docs\n\nWe maintain API documentation using [YARD](https://yardoc.org/), which is\npublished automatically at\n[RubyDoc.info](https://rubydoc.info/gems/memo_wise).\n\nTo generate documentation locally or run documentation tests,\nfirst install the `docs` dependencies (e.g. `yard`) as follows:\n\n```bash\nBUNDLE_WITH=docs bundle install\n```\n\n### Hot Reloading Docs Locally\n\nTo edit documentation locally and see it rendered in your browser\nusing hot reloading, run:\n\n```bash\nBUNDLE_WITH=docs bundle exec yard server --reload\n```\n\nYou can then open your web browser to `http://127.0.0.1:8808/`. As you\nedit documentation locally, reload your browser to see it generated.\n\n### Static Generate Docs Locally\n\nTo statically generate documentation locally, run:\n\n```bash\nbundle exec yard\n```\n\nYou can then open the generated documentation at `docs/index.html`.\n\n### Test all Docs Examples\n\nWe use [yard-doctest](https://github.com/p0deje/yard-doctest) to test all\ncode examples in our YARD documentation. To run `doctest` locally:\n\n```bash\nbundle exec yard doctest\n```\n\nWe use [dokaz](https://github.com/zverok/dokaz) to test all code examples in\nthis README.md file, and all other non-code documentation. To run `dokaz`\nlocally:\n\n```bash\nbundle exec dokaz\n```\n\n## A Note on Testing\n\nWhen testing memoized *module* methods, note that some testing setups will\nreuse the same instance (which `include`s/`extend`s/`prepend`s the module)\nacross tests, which can result in confusing test failures when this differs from\nhow you use the code in production.\n\nFor example, Rails view helpers are modules that are commonly tested with a\n[shared `view` instance](https://github.com/rails/rails/blob/291a3d2ef29a3842d1156ada7526f4ee60dd2b59/actionview/lib/action_view/test_case.rb#L203-L214). Rails initializes a new view instance for each web request so any view helper\nmethods would only be memoized for the duration of that web request, but in\ntests (such as when using\n[`rspec-rails`'s `helper`](https://github.com/rspec/rspec-rails/blob/main/lib/rspec/rails/example/helper_example_group.rb#L22-L27)),\nthe memoization may persist across tests. In this case, simply reset the\nmemoization between your tests with something like:\n\n```ruby\nafter(:each) { helper.reset_memo_wise }\n```\n\n## Further Reading\n\nWe presented at RubyConf 2021:\n\n- Achieving Fast Method Metaprogramming: Lessons from `MemoWise`\n  ([slides](https://docs.google.com/presentation/d/1XgERQ0YHplwJKM3wNQwZn584d_9szYZp2WsDEXoY_7Y/edit?usp=sharing) /\n  [benchmarks](https://gist.github.com/JacobEvelyn/17b7b000e50151c30eaea928f1fcdc11))\n\nAnd we've written more about `MemoWise` in a series of blog posts:\n\n- [Introducing: MemoWise](https://medium.com/building-panorama-education/introducing-memowise-51a5f0523489)\n- [Optimizing MemoWise Performance](https://ja.cob.land/optimizing-memowise-performance)\n- [Esoteric Ruby in MemoWise](https://jemma.dev/blog/esoteric-ruby-in-memowise)\n\n## Logo\n\n`MemoWise`'s logo was created by [Luci Cooke](https://www.lucicooke.com/). The\nlogo is licensed under a\n[Creative Commons Attribution-NonCommercial 4.0 International License](https://creativecommons.org/licenses/by-nc/4.0/deed.en).\n\n## Contributing\n\n[Bug reports](https://github.com/panorama-ed/memo_wise/issues) and\n[pull requests](https://github.com/panorama-ed/memo_wise/pulls) are welcome on GitHub at\nhttps://github.com/panorama-ed/memo_wise. This project is intended to be a safe,\nwelcoming space for collaboration, and contributors are expected to adhere to\nthe [code of conduct](https://github.com/panorama-ed/memo_wise/blob/main/CODE_OF_CONDUCT.md).\n\n## Releasing\n\nTo make a new release of `MemoWise` to\n[RubyGems](https://rubygems.org/gems/memo_wise), first install the `release`\ndependencies (e.g. `rake`) as follows:\n\n```shell\nBUNDLE_WITH=release bundle install\n```\n\nThen carry out these steps:\n\n1. Update `CHANGELOG.md`:\n   - Add an entry for the upcoming version _x.y.z_\n   - Move content from _Unreleased_ to the upcoming version _x.y.z_\n   - Update the diff links for this version and _Unreleased_ in `CHANGELOG.md`\n   - Change _Unreleased_ section to say:\n     ```\n     **Gem enhancements:** none\n\n     _No breaking changes!_\n\n     **Project enhancements:** none\n     ```\n\n2. Update `lib/memo_wise/version.rb`\n   - Replace with upcoming version _x.y.z_\n   - Run `bundle install` to update `Gemfile.lock`\n   - Commit with title `Bump version to x.y.z`\n\n3. `bundle exec rake release`\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n## Code of Conduct\n\nEveryone interacting in the `MemoWise` project's codebases, issue trackers, chat\nrooms and mailing lists is expected to follow the\n[code of conduct](https://github.com/panorama-ed/memo_wise/blob/main/CODE_OF_CONDUCT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpanorama-ed%2Fmemo_wise","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpanorama-ed%2Fmemo_wise","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpanorama-ed%2Fmemo_wise/lists"}