Skip to content

Commit

Permalink
Allow memoized methods to be reloaded and allow multiple symbols
Browse files Browse the repository at this point in the history
  • Loading branch information
josh committed Jul 18, 2008
1 parent 7430c41 commit e1f23da
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 28 deletions.
32 changes: 17 additions & 15 deletions activesupport/lib/active_support/memoizable.rb
Expand Up @@ -5,28 +5,30 @@ def self.included(base) #:nodoc:
end end


module ClassMethods module ClassMethods
def memoize(symbol) def memoize(*symbols)
original_method = "_unmemoized_#{symbol}" symbols.each do |symbol|
memoized_ivar = "@_memoized_#{symbol}" original_method = "unmemoized_#{symbol}"
raise "Already memoized #{symbol}" if instance_methods.map(&:to_s).include?(original_method) memoized_ivar = "@#{symbol}"
raise "Already memoized #{symbol}" if instance_methods.map(&:to_s).include?(original_method)


alias_method original_method, symbol alias_method original_method, symbol
class_eval <<-EOS, __FILE__, __LINE__ class_eval <<-EOS, __FILE__, __LINE__
def #{symbol} def #{symbol}(reload = false)
if defined? #{memoized_ivar} if !reload && defined? #{memoized_ivar}
#{memoized_ivar} #{memoized_ivar}
else else
#{memoized_ivar} = #{original_method} #{memoized_ivar} = #{original_method}.freeze
end
end end
end EOS
EOS end
end end
end end


def freeze def freeze
methods.each do |method| methods.each do |method|
if m = method.to_s.match(/\A_unmemoized_(.*)/) if m = method.to_s.match(/\Aunmemoized_(.*)/)
send(m[1]).freeze send(m[1])
end end
end end
super super
Expand Down
39 changes: 26 additions & 13 deletions activesupport/test/memoizable_test.rb
Expand Up @@ -8,38 +8,51 @@ class Person
def name def name
fetch_name_from_floppy fetch_name_from_floppy
end end
memoize :name


def age def age
nil nil
end end
memoize :age
def random
rand(0)
end

memoize :name, :age, :random


private private
def fetch_name_from_floppy def fetch_name_from_floppy
"Josh" "Josh"
end end
end end


def setup
@person = Person.new
end

def test_memoization 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 def test_reloadable
2.times { assert_equal "Josh", person.name } random = @person.random
assert_equal random, @person.random
assert_not_equal random, @person.random(:reload)

This comment has been minimized.

Copy link
@DefV

DefV Jul 18, 2008

Contributor

This test will not always pass :-)

end end


def test_memoized_methods_are_frozen def test_memoized_methods_are_frozen
person = Person.new assert_equal true, @person.name.frozen?
person.freeze
assert_equal "Josh", person.name @person.freeze
assert_equal true, person.name.frozen? assert_equal "Josh", @person.name
assert_equal true, @person.name.frozen?
end end


def test_memoization_frozen_with_nil_value def test_memoization_frozen_with_nil_value
person = Person.new @person.freeze
person.freeze assert_equal nil, @person.age
assert_equal nil, person.age
end end


def test_double_memoization def test_double_memoization
Expand Down

2 comments on commit e1f23da

@NZKoz
Copy link
Member

@NZKoz NZKoz commented on e1f23da Jul 18, 2008

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless it’s run on debian

@nbibler
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.