New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor to use memoizable #24
Changes from all commits
81ed2fd
f21ffd9
212c653
101e358
6744cab
7e2edfc
878b3e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1 @@ | ||
* Update #hash to be memoized automatically | ||
* Refactor memoization to use memoizable |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
--- | ||
threshold: 7 | ||
total_score: 53 | ||
threshold: 4 | ||
total_score: 24 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
--- | ||
threshold: 17.5 | ||
threshold: 15.0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,62 +32,6 @@ def memoize(*methods) | |
self | ||
end | ||
|
||
# Test if an instance method is memoized | ||
# | ||
# @example | ||
# class Foo | ||
# include Adamantium | ||
# | ||
# def bar | ||
# end | ||
# memoize :bar | ||
# | ||
# end | ||
# | ||
# Foo.memoized?(:bar) # true | ||
# Foo.memoized?(:baz) # false, does not care if method acutally exists | ||
# | ||
# @param [Symbol] name | ||
# | ||
# @return [true] | ||
# if method is memoized | ||
# | ||
# @return [false] | ||
# otherwise | ||
# | ||
# @api private | ||
def memoized?(name) | ||
memoized_methods.key?(name) | ||
end | ||
|
||
# Return original instance method | ||
# | ||
# @example | ||
# | ||
# class Foo | ||
# include Adamantium | ||
# | ||
# def bar | ||
# end | ||
# memoize :bar | ||
# | ||
# end | ||
# | ||
# Foo.original_instance_method(:bar) #=> UnboundMethod, where source_location still points to original! | ||
# | ||
# @param [Symbol] name | ||
# | ||
# @return [UnboundMethod] | ||
# if method was memoized before | ||
# | ||
# @raise [ArgumentError] | ||
# otherwise | ||
# | ||
# @api public | ||
def original_instance_method(name) | ||
memoized_methods[name] | ||
end | ||
|
||
private | ||
|
||
# Hook called when module is included | ||
|
@@ -114,62 +58,8 @@ def included(descendant) | |
# | ||
# @api private | ||
def memoize_method(method_name, freezer) | ||
method = instance_method(method_name) | ||
if method.arity.nonzero? | ||
fail ArgumentError, 'Cannot memoize method with nonzero arity' | ||
end | ||
memoized_methods[method_name] = method | ||
visibility = method_visibility(method_name) | ||
define_memoize_method(method, freezer) | ||
send(visibility, method_name) | ||
end | ||
|
||
# Return original method registry | ||
# | ||
# @return [Hash<Symbol, UnboundMethod>] | ||
# | ||
# @api private | ||
def memoized_methods | ||
@memoized_methods ||= ThreadSafe::Hash.new do |_memoized_methods, name| | ||
fail ArgumentError, "No method #{name.inspect} was memoized" | ||
end | ||
end | ||
|
||
# Define a memoized method that delegates to the original method | ||
# | ||
# @param [UnboundMethod] method | ||
# the method to memoize | ||
# @param [#call] freezer | ||
# a freezer for memoized values | ||
# | ||
# @return [undefined] | ||
# | ||
# @api private | ||
def define_memoize_method(method, freezer) | ||
method_name = method.name.to_sym | ||
undef_method(method_name) | ||
define_method(method_name) do || | ||
memory.fetch(method_name) do | ||
value = method.bind(self).call | ||
frozen = freezer.call(value) | ||
store_memory(method_name, frozen) | ||
end | ||
end | ||
end | ||
|
||
# Return the method visibility of a method | ||
# | ||
# @param [String, Symbol] method | ||
# the name of the method | ||
# | ||
# @return [Symbol] | ||
# | ||
# @api private | ||
def method_visibility(method) | ||
if private_method_defined?(method) then :private | ||
elsif protected_method_defined?(method) then :protected | ||
else :public | ||
end | ||
memoized_methods[method_name] = Memoizable::MethodBuilder | ||
.new(self, method_name, freezer).call | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dkubb I already was thinking hard about creating a
Creating an instance and delegating to def self.call(*arguments)
new(*arguments).call
end Would DRY up a lot of places in my code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mbj do you find yourself doing this kind of thing in your gems that dynamically add methods? I really like this approach and will probably even look at doing it in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Usage of such a class MethodBuilder
include MethodObject, Concord.new(:object, :method_name, :freezer)
def call
calculate_something_based_on_instance_state
end
end
MethodBuilder.call(self, method_name, freezer) |
||
end | ||
|
||
end # ModuleMethods | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary? If so, should we push another gem release? It appears that latest memoizable gem (v0.2.0) has diverged from master by a few commits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's necessary because of a few changes I made in memoizable; nothing that breaks the interface though.
I'll probably release memoizable 0.3.0 once I test all this properly with some other dependent gems.