Permalink
Browse files

Allow memoized methods to be reloaded and allow multiple symbols

  • Loading branch information...
josh committed Jul 18, 2008
1 parent 7430c41 commit e1f23da53cef20a60e4bf458d959fe2bfe7d52ea
Showing with 43 additions and 28 deletions.
  1. +17 −15 activesupport/lib/active_support/memoizable.rb
  2. +26 −13 activesupport/test/memoizable_test.rb
@@ -5,28 +5,30 @@ def self.included(base) #:nodoc:
end
module ClassMethods
- def memoize(symbol)
- original_method = "_unmemoized_#{symbol}"
- memoized_ivar = "@_memoized_#{symbol}"
- raise "Already memoized #{symbol}" if instance_methods.map(&:to_s).include?(original_method)
+ def memoize(*symbols)
+ symbols.each do |symbol|
+ original_method = "unmemoized_#{symbol}"
+ memoized_ivar = "@#{symbol}"
+ raise "Already memoized #{symbol}" if instance_methods.map(&:to_s).include?(original_method)
- alias_method original_method, symbol
- class_eval <<-EOS, __FILE__, __LINE__
- def #{symbol}
- if defined? #{memoized_ivar}
- #{memoized_ivar}
- else
- #{memoized_ivar} = #{original_method}
+ alias_method original_method, symbol
+ class_eval <<-EOS, __FILE__, __LINE__
+ def #{symbol}(reload = false)
+ if !reload && defined? #{memoized_ivar}
+ #{memoized_ivar}
+ else
+ #{memoized_ivar} = #{original_method}.freeze
+ end
end
- end
- EOS
+ EOS
+ end
end
end
def freeze
methods.each do |method|
- if m = method.to_s.match(/\A_unmemoized_(.*)/)
- send(m[1]).freeze
+ if m = method.to_s.match(/\Aunmemoized_(.*)/)
+ send(m[1])
end
end
super
@@ -8,38 +8,51 @@ class Person
def name
fetch_name_from_floppy
end
- memoize :name
def age
nil
end
- memoize :age
+
+ def random
+ rand(0)
+ end
+
+ memoize :name, :age, :random
private
def fetch_name_from_floppy
"Josh"
end
end
+ def setup
+ @person = Person.new
+ end
+
def test_memoization
- person = Person.new
- assert_equal "Josh", person.name
+ assert_equal "Josh", @person.name
+
+ @person.expects(:fetch_name_from_floppy).never
+ 2.times { assert_equal "Josh", @person.name }
+ end
- person.expects(:fetch_name_from_floppy).never
- 2.times { assert_equal "Josh", person.name }
+ def test_reloadable
+ random = @person.random
+ assert_equal random, @person.random
+ assert_not_equal random, @person.random(:reload)

This comment has been minimized.

Show comment
Hide comment
@DefV

DefV Jul 18, 2008

Contributor

This test will not always pass :-)

@DefV

DefV Jul 18, 2008

Contributor

This test will not always pass :-)

end
def test_memoized_methods_are_frozen
- person = Person.new
- person.freeze
- assert_equal "Josh", person.name
- assert_equal true, person.name.frozen?
+ assert_equal true, @person.name.frozen?
+
+ @person.freeze
+ assert_equal "Josh", @person.name
+ assert_equal true, @person.name.frozen?
end
def test_memoization_frozen_with_nil_value
- person = Person.new
- person.freeze
- assert_equal nil, person.age
+ @person.freeze
+ assert_equal nil, @person.age
end
def test_double_memoization

2 comments on commit e1f23da

@NZKoz

This comment has been minimized.

Show comment
Hide comment
@NZKoz

NZKoz Jul 18, 2008

Member

Unless it’s run on debian

Member

NZKoz replied Jul 18, 2008

Unless it’s run on debian

@nbibler

This comment has been minimized.

Show comment
Hide comment
@nbibler

nbibler Jul 18, 2008

Contributor

It would probably be better to replace the random calls with an incrementing instance variable. For instance:

class Person
  def current_count
    @count ||= 0
    @count += 1
  end
  memoize :current_count

You can then reliably test expected values against the memoization.

Contributor

nbibler replied Jul 18, 2008

It would probably be better to replace the random calls with an incrementing instance variable. For instance:

class Person
  def current_count
    @count ||= 0
    @count += 1
  end
  memoize :current_count

You can then reliably test expected values against the memoization.

Please sign in to comment.