Skip to content

Commit

Permalink
Association read from cache
Browse files Browse the repository at this point in the history
  • Loading branch information
Luca Guidi committed Aug 16, 2009
1 parent be1cf8d commit f08258b
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 7 deletions.
76 changes: 70 additions & 6 deletions lib/activerecord/lib/active_record/associations.rb
@@ -1,12 +1,21 @@
module ActiveRecord
module Associations
module ClassMethods
# def has_many_with_association_cache(association_id, options = {}, &extension)
# has_many_without_association_cache(association_id, options, &extension)
# add_has_many_cache_callbacks if options[:cached]
# end
# alias_method_chain :has_many, :association_cache
#
def has_many(association_id, options = {}, &extension) #:nodoc:
reflection = create_has_many_reflection(association_id, options, &extension)

configure_dependency_for_has_many(reflection)
add_association_callbacks(reflection.name, reflection.options)

if options[:through]
collection_accessor_methods(reflection, HasManyThroughAssociation, options)
else
collection_accessor_methods(reflection, HasManyAssociation, options)
end

# add_has_many_cache_callbacks if options[:cached]
end

# def add_has_many_cache_callbacks
# method_name = :has_many_after_save_cache_expire
# return if respond_to? method_name
Expand All @@ -21,6 +30,61 @@ module ClassMethods
# after_save method_name
# end

def collection_reader_method(reflection, association_proxy_class, options)
define_method(reflection.name) do |*params|
force_reload = params.first unless params.empty?

association = if options[:cached]
cache_read(reflection)
else
association_instance_get(reflection.name)
end

unless association
association = association_proxy_class.new(self, reflection)
if options[:cached]
cache_write(reflection, association)
else
association_instance_set(reflection.name, association)
end
end

if force_reload
association.reload
cache_write(reflection, association) if options[:cached]
end

association
end

define_method("#{reflection.name.to_s.singularize}_ids") do
if send(reflection.name).loaded? || reflection.options[:finder_sql]
send(reflection.name).map(&:id)
else
send(reflection.name).all(:select => "#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map(&:id)
end
end
end

def collection_accessor_methods(reflection, association_proxy_class, options, writer = true)
collection_reader_method(reflection, association_proxy_class, options)

if writer
define_method("#{reflection.name}=") do |new_value|
# Loads proxy class instance (defined in collection_reader_method) if not already loaded
association = send(reflection.name)
association.replace(new_value)
cache_write(reflection, association) if options[:cached]
association
end

define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
ids = (new_value || []).reject { |nid| nid.blank? }
send("#{reflection.name}=", reflection.class_name.constantize.find(ids))
end
end
end

valid_keys_for_has_many_association << :cached
valid_keys_for_belongs_to_association << :cached
end
Expand Down
4 changes: 4 additions & 0 deletions lib/activerecord/lib/active_record/base.rb
Expand Up @@ -8,6 +8,10 @@ def associations_cache
self.class.associations_cache
end

def cache_read(reflection)
associations_cache.read association_cache_key(reflection)
end

def cache_write(reflection, value)
associations_cache.write association_cache_key(reflection), value
end
Expand Down
8 changes: 7 additions & 1 deletion spec/active_record/base_spec.rb
Expand Up @@ -16,7 +16,13 @@
@post.send(:association_cache_key, @reflection).should == "#{@post.cache_key}/#{@reflection.name}"
end

it "should store in cache" do
it "should read from cache" do
association_cache_key = @post.send(:association_cache_key, @reflection)
@cache.write(association_cache_key, "value")
@post.send(:cache_read, @reflection).should == "value"
end

it "should write in cache" do
association_cache_key = @post.send(:association_cache_key, @reflection)
@post.send(:cache_write, @reflection, "value").should be_true
@cache.read(association_cache_key).should == "value"
Expand Down
1 change: 1 addition & 0 deletions spec/spec_helper.rb
@@ -1,6 +1,7 @@
$:.unshift "#{File.dirname(__FILE__)}/../lib"
require "cached-models"
require "factory_girl"
require "ruby-debug"
require "memcache"

ActiveRecord::Base.send(:class_variable_set, :@@associations_cache, ActiveSupport::Cache.lookup_store(:mem_cache_store))
Expand Down

0 comments on commit f08258b

Please sign in to comment.