Permalink
Browse files

Added Memoizable mixin for caching simple lazy loaded attributes

  • Loading branch information...
josh committed Jul 15, 2008
1 parent d27dd86 commit 8a9934a9d9fc98b56c4566ae2e3fd4d83e505d3e
@@ -1,5 +1,7 @@
*Edge*

* Added Memoizable mixin for caching simple lazy loaded attributes [Josh Peek]

* Move the test related core_ext stuff out of core_ext so it's only loaded by the test helpers. [Michael Koziarski]

* Add Inflection rules for String#humanize. #535 [dcmanges]
@@ -43,6 +43,7 @@
require 'active_support/ordered_options'
require 'active_support/option_merger'

require 'active_support/memoizable'
require 'active_support/string_inquirer'

require 'active_support/values/time_zone'
@@ -0,0 +1,32 @@
module ActiveSupport
module Memoizable
def self.included(base) #:nodoc:
base.extend(ClassMethods)
end

module ClassMethods
def memorize(symbol)
original_method = "_unmemorized_#{symbol}"
alias_method original_method, symbol
class_eval <<-EOS, __FILE__, __LINE__
def #{symbol}
if instance_variable_defined?(:@#{symbol})
@#{symbol}
else
@#{symbol} = #{original_method}
end
end
EOS
end
end

def freeze
methods.each do |method|
if m = method.to_s.match(/^_unmemorized_(.*)/)
send(m[1]).freeze
end
end
super
end
end
end
@@ -0,0 +1,45 @@
require 'abstract_unit'

uses_mocha 'Memoizable' do
class MemoizableTest < Test::Unit::TestCase
class Person
include ActiveSupport::Memoizable

def name
fetch_name_from_floppy
end
memorize :name

def age
nil
end
memorize :age

private
def fetch_name_from_floppy
"Josh"
end
end

def test_memoization
person = Person.new
assert_equal "Josh", person.name

person.expects(:fetch_name_from_floppy).never
2.times { assert_equal "Josh", person.name }
end

def test_memoized_methods_are_frozen
person = Person.new
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
end
end
end

11 comments on commit 8a9934a

@knzconnor

This comment has been minimized.

Copy link
Contributor

knzconnor replied Jul 16, 2008

Is the inconsistency between memoize (the usage I am used to and used in the file name and the module name) and memorize (the actual call) intentional?

@josh

This comment has been minimized.

Copy link
Member

josh replied Jul 16, 2008

It was fixed in 001c8beb4

@matthewrudy

This comment has been minimized.

Copy link
Contributor

matthewrudy replied Jul 16, 2008

why doesn’t it use alias_method_chain?
or atleast the same structure of aliasing.

with this we get a non-standard name for the function that bypasses memoizing.

I’d also want a .blah(true) which would force reload

@humanzz

This comment has been minimized.

Copy link

humanzz replied Jul 16, 2008

I have previously implemented the memoize feature as method_cache plugin at http://github.com/humanzz/method_cache. But method_cache had a couple more features: in addition to caching results in instance variables it also stored the result in the cache store. It’d be great if memoize adds such feature.

@josh

This comment has been minimized.

Copy link
Member

josh replied Jul 16, 2008

@matthewruby I’m trying to avoid alias_method_chain. But if access to the original method is important, maybe we could name it “name_without_memoization”.

@humanzz No to RAILS_CACHE. Most of time you a memorizing simple string manipulations or integer calculations. They are either short lived and should die with the object or long live and you want to deal with the memcache protocol latency.

@michaelklishin

This comment has been minimized.

Copy link
Contributor

michaelklishin replied Jul 17, 2008

Very valuable addition, definitely needed to go in as a separate module while patches that fix 500 problem with Nginx proxying get dusty.

@michaelklishin

This comment has been minimized.

Copy link
Contributor

michaelklishin replied Jul 17, 2008

and I like the way alias method chain is avoided here.

@michaelklishin

This comment has been minimized.

Copy link
Contributor

michaelklishin replied Jul 17, 2008

and I like the way alias method chain is avoided here.

@josh

This comment has been minimized.

Copy link
Member

josh replied Jul 17, 2008

BTW, this is module is NOT include anywhere by default. To use it include ActiveSupport::Memoizable

@nbibler

This comment has been minimized.

Copy link
Contributor

nbibler replied Jul 18, 2008

As far as implementations go, this is alright, but there doesn’t appear to be a simple way to reload the data. Personally, I like Marcel Molina’s implementation of this same feature in the AWS-S3 library.

@josh

This comment has been minimized.

Copy link
Member

josh replied Jul 18, 2008

I didn’t realize that amount of people who would be using this outside of core. I added a few of your requests.

http://github.com/rails/rails/commit/e1f23da53cef20a60e4bf458d959fe2bfe7d52ea

Please sign in to comment.