Alt Memery
Alt Memery allows to memoize methods return values.
The native simplest memoization in Ruby looks like this:
def user
@user ||= User.find(some_id)
end
But if you want to memoize falsy values — you have to use defined?
instead of ||=
:
def user
return @user if defined?(@user)
@user = User.find(some_id)
end
But with memoization gems, like this one, you can simplify your code:
memoize def user
User.find(some_id)
end
Also, you're getting additional features, like conditions of memoization, time-to-live, handy memoized values flushing, etc.
Alt?
It's a fork of Memery gem.
Original Memery uses prepend Module.new
with memoized methods, not touching original ones.
This approach has advantages, but also has problems, see discussion here:
tycooon#1
So, this fork uses UnboundMethod
as I've suggested in the PR above.
Difference with other gems
Such gems like Memoist override methods.
So, if you want to memoize a method in a child class with the same named memoized method
in a parent class — you have to use something like awkward identifier:
argument.
This gem allows you to just memoize methods when you want to.
Note how both method's return values are cached separately and don't interfere with each other.
The other key difference is that it doesn't change method's arguments
(no extra param like reload
). If you need to get unmemoize result of method —
just call the #clear_memery_cache!
method with needed memoized method names:
a.clear_memery_cache! :foo, :bar
Without arguments, #clear_memery_cache!
will clear the whole instance's cache.
Installation
Add this line to your application's Gemfile:
gem 'alt_memery'
And then execute:
bundle install
Or install it yourself as:
gem install alt_memery
Usage
Requirement
Since it's a fork of memery
gem, require with:
require 'memery'
Or from the Gemfile
:
gem 'alt_memery', require: 'memery'
Code
class A
include Memery
memoize def call
puts "calculating"
42
end
# or:
# def call
# ...
# end
# memoize :call
end
a = A.new
a.call # => 42
a.call # => 42
a.call # => 42
# Text will be printed only once.
a.call { 1 } # => 42
# Will print because passing a block disables memoization
Methods with arguments are supported and the memoization will be done based on arguments using an internal hash. So this will work as expected:
class A
include Memery
memoize def call(arg1, arg2)
puts "calculating"
arg1 + arg2
end
end
a = A.new
a.call(1, 5) # => 6
a.call(2, 15) # => 17
a.call(1, 5) # => 6
# Text will be printed only twice, once per unique argument list.
For class methods:
class B
class << self
include Memery
memoize def call
puts "calculating"
42
end
end
end
B.call # => 42
B.call # => 42
B.call # => 42
# Text will be printed only once.
For conditional memoization:
class A
include Memery
attr_accessor :environment
def call
puts "calculating"
42
end
memoize :call, condition: -> { environment == 'production' }
end
a = 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.
For memoization with time-to-live:
class A
include Memery
def call
puts "calculating"
42
end
memoize :call, ttl: 3 # seconds
end
a = 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
Check if method is memoized:
class A
include Memery
memoize def call
puts "calculating"
42
end
def execute
puts "non-memoized"
end
end
a = A.new
a.memoized?(:call) # => true
a.memoized?(:execute) # => false
If you want to see memoized method source:
class A
include Memery
memoize def call
puts "calculating"
42
end
end
# This will print memoization logic, don't use it.
# The same for `show-source A#call` in `pry`.
puts A.instance_method(:call).source
# And this will work correctly.
puts A.memoized_methods[:call].source
But if a memoized method has been defined in an included module — it'd be a bit harder:
module A
include Memery
memoize def foo
'source'
end
end
module B
include Memery
include A
memoize def foo
"Get this #{super}!"
end
end
class C
include B
end
puts C.instance_method(:foo).owner.memoized_methods[:foo].source
# memoize def foo
# "Get this #{super}!"
# end
puts C.instance_method(:foo).super_method.owner.memoized_methods[:foo].source
# memoize def foo
# 'source'
# end
Development
After checking out the repo, run bundle install
to install dependencies.
Then, run toys rspec
to run the tests.
To install this gem onto your local machine, run toys gem install
.
To release a new version, run toys gem release %version%
.
See how it works here.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/AlexWayfer/alt_memery.
License
The gem is available as open source under the terms of the MIT License.