{"id":13879531,"url":"https://github.com/tycooon/memery","last_synced_at":"2025-05-15T04:06:02.455Z","repository":{"id":27573620,"uuid":"114456661","full_name":"tycooon/memery","owner":"tycooon","description":"A gem for memoization in Ruby","archived":false,"fork":false,"pushed_at":"2025-03-04T17:34:07.000Z","size":77,"stargazers_count":190,"open_issues_count":0,"forks_count":14,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-14T05:55:44.993Z","etag":null,"topics":["memoization","ruby"],"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/tycooon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"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":"2017-12-16T11:50:15.000Z","updated_at":"2025-04-10T19:01:49.000Z","dependencies_parsed_at":"2024-04-06T11:25:32.099Z","dependency_job_id":"4fdf1990-3630-40b7-8d8e-bc19fc442002","html_url":"https://github.com/tycooon/memery","commit_stats":{"total_commits":81,"total_committers":7,"mean_commits":"11.571428571428571","dds":0.2716049382716049,"last_synced_commit":"9bcae5e2d3d17b27c8be21b18fc53db69ae1b407"},"previous_names":["tycooon/mememaster"],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tycooon%2Fmemery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tycooon%2Fmemery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tycooon%2Fmemery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tycooon%2Fmemery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tycooon","download_url":"https://codeload.github.com/tycooon/memery/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254270646,"owners_count":22042859,"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":["memoization","ruby"],"created_at":"2024-08-06T08:02:24.127Z","updated_at":"2025-05-15T04:05:57.426Z","avatar_url":"https://github.com/tycooon.png","language":"Ruby","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)\n\nMemery is a Ruby gem that simplifies memoization of method return values. In Ruby, memoization typically looks like this:\n\n```ruby\ndef user\n  @user ||= User.find(some_id)\nend\n```\n\nHowever, 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:\n\n```ruby\ndef user\n  @user ||= begin\n    some_id = calculate_id\n    klass = calculate_klass\n    klass.find(some_id)\n  end\nend\n```\n\nTo handle these situations, memoization gems like Memery exist. The example above can be rewritten using Memery as follows:\n\n```ruby\nmemoize def user\n  some_id = calculate_id\n  klass = calculate_klass\n  klass.find(some_id)\nend\n```\n\n## Installation\n\nAdd `gem \"memery\"` to your Gemfile.\n\n## Usage\n\n```ruby\nclass A\n  include Memery\n\n  memoize def call\n    puts \"calculating\"\n    42\n  end\n\n  # Alternatively:\n  # def call\n  #   ...\n  # end\n  # memoize :call\nend\n\na = A.new\na.call # =\u003e 42\na.call # =\u003e 42\na.call # =\u003e 42\n# \"calculating\" will only be printed once.\n\na.call { 1 } # =\u003e 42\n# \"calculating\" will be printed again because passing a block disables memoization.\n```\n\nMemoization works with methods that take arguments. The memoization is based on these arguments using an internal hash, so the following will work as expected:\n\n```ruby\nclass A\n  include Memery\n\n  memoize def call(arg1, arg2)\n    puts \"calculating\"\n    arg1 + arg2\n  end\nend\n\na = A.new\na.call(1, 5) # =\u003e 6\na.call(2, 15) # =\u003e 17\na.call(1, 5) # =\u003e 6\n# \"calculating\" will be printed twice, once for each unique argument list.\n```\n\nFor class methods:\n\n```ruby\nclass B\n  class \u003c\u003c self\n    include Memery\n\n    memoize def call\n      puts \"calculating\"\n      42\n    end\n  end\nend\n\nB.call # =\u003e 42\nB.call # =\u003e 42\nB.call # =\u003e 42\n# \"calculating\" will only be printed once.\n```\n\n### Conditional Memoization\n\n```ruby\nclass A\n  include Memery\n\n  attr_accessor :environment\n\n  def call\n    puts \"calculating\"\n    42\n  end\n\n  memoize :call, condition: -\u003e { environment == 'production' }\nend\n\na = A.new\na.environment = 'development'\na.call # =\u003e 42\n# calculating\na.call # =\u003e 42\n# calculating\na.call # =\u003e 42\n# calculating\n# Text will be printed every time because result of condition block is `false`.\n\na.environment = 'production'\na.call # =\u003e 42\n# calculating\na.call # =\u003e 42\na.call # =\u003e 42\n# Text will be printed only once because there is memoization\n# with `true` result of condition block.\n```\n\n### Memoization with Time-to-Live (TTL)\n\n```ruby\nclass A\n  include Memery\n\n  def call\n    puts \"calculating\"\n    42\n  end\n\n  memoize :call, ttl: 3 # seconds\nend\n\na = A.new\na.call # =\u003e 42\n# calculating\na.call # =\u003e 42\na.call # =\u003e 42\n# Text will be printed again only after 3 seconds of time-to-live.\n# 3 seconds later...\na.call # =\u003e 42\n# calculating\na.call # =\u003e 42\na.call # =\u003e 42\n# another 3 seconds later...\na.call # =\u003e 42\n# calculating\na.call # =\u003e 42\na.call # =\u003e 42\n```\n\n### Checking if a Method is Memoized\n\n```ruby\nclass A\n  include Memery\n\n  memoize def call\n    puts \"calculating\"\n    42\n  end\n\n  def execute\n    puts \"non-memoized\"\n  end\nend\n\na = A.new\n\na.memoized?(:call) # =\u003e true\na.memoized?(:execute) # =\u003e false\n```\n\n### Marshal-compatible Memoization\n\nIn order for objects to be marshaled and loaded in a different Ruby process,\nhashed arguments must be disabled in order for memoized values to be retained.\nNote that this can have a performance impact if the memoized method contains\narguments.\n\n```ruby\nMemery.use_hashed_arguments = false\n\nclass A\n  include Memery\n\n  memoize def call\n    puts \"calculating\"\n    42\n  end\nend\n\na = A.new\na.call\n\nMarshal.dump(a)\n# =\u003e \"\\x04\\bo:\\x06A\\x06:\\x1D@_memery_memoized_values{\\x06:\\tcallS:3Memery::ClassMethods::MemoizationModule::Cache\\a:\\vresulti/:\\ttimef\\x14663237.14822323\"\n\n# ...in another Ruby process:\na = Marshal.load(\"\\x04\\bo:\\x06A\\x06:\\x1D@_memery_memoized_values{\\x06:\\tcallS:3Memery::ClassMethods::MemoizationModule::Cache\\a:\\vresulti/:\\ttimef\\x14663237.14822323\")\na.call # =\u003e 42\n```\n\n## Differences from Other Gems\n\nMemery 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:\n\n```ruby\nclass A\n  include Memery\n\n  memoize def x(param)\n    param\n  end\nend\n\nclass B \u003c A\n  memoize def x(param)\n    super(2) * param\n  end\nend\n\nb = B.new\nb.x(1) # =\u003e 2\nb.x(2) # =\u003e 4\nb.x(3) # =\u003e 6\n\nb.instance_variable_get(:@_memery_memoized_values)\n# =\u003e {:x_70318201388120=\u003e{[1]=\u003e2, [2]=\u003e4, [3]=\u003e6}, :x_70318184636620=\u003e{[2]=\u003e2}}\n```\n\nNote how both methods' return values are cached separately without interfering with each other.\n\nAnother 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:\n\n```ruby\nmemoize def users\n  get_users\nend\n\ndef get_users\n  # ...\nend\n```\n\nAlternatively, you can clear the entire instance's cache:\n\n```ruby\na.clear_memery_cache!\n```\n\nYou can also provide a block, though this approach is somewhat hacky:\n\n```ruby\na.users {}\n```\n\n## Object Shape Optimization\n\nIn 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.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/tycooon/memery.\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## Author\n\nCreated by Yuri Smirnov.\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftycooon%2Fmemery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftycooon%2Fmemery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftycooon%2Fmemery/lists"}