Properly proxy to Enumerable or AR::Finder method.

Decorated associations implement Enumerable, but they also should
emulate the AR::Finder stuff.

    @person = PersonDecorator.find(params[:person_id])
    @pet = @person.pets.find(params[:id])

The problem is, pets delegated to Enumerable always, instead of
to the finder method. This patch makes it delegate to Enumerable if
a block is given, and Finder if not.

Fixes #214.
This commit is contained in:
Steve Klabnik 2012-08-18 11:54:16 +02:00
parent 5da443366c
commit d46d19205e
3 changed files with 34 additions and 1 deletions

View File

@ -64,6 +64,7 @@ module Draper
orig_association = model.send(association_symbol)
return orig_association if orig_association.nil?
return decorated_associations[association_symbol] if decorated_associations[association_symbol]
orig_association = orig_association.send(options[:scope]) if options[:scope]
@ -79,7 +80,7 @@ module Draper
orig_association.class
end
"#{klass}Decorator".constantize.decorate(orig_association, options)
decorated_associations[association_symbol] = "#{klass}Decorator".constantize.decorate(orig_association, options)
end
end
@ -303,5 +304,9 @@ module Draper
model.class.reflect_on_association(association)
end
end
def decorated_associations
@decorated_associations ||= {}
end
end
end

View File

@ -1,3 +1,4 @@
require 'active_support/core_ext/object/blank'
module Draper
class DecoratedEnumerableProxy
include Enumerable
@ -13,6 +14,16 @@ module Draper
end
alias_method :to_ary, :decorated_collection
def find(ifnone_or_id = nil, &blk)
if block_given?
source.find(ifnone_or_id, &blk)
else
obj = decorated_collection.first
return nil if obj.blank?
obj.class.find(ifnone_or_id)
end
end
def method_missing (method, *args, &block)
@wrapped_collection.send(method, *args, &block)
end

View File

@ -178,6 +178,23 @@ describe Draper::Base do
subject.previous_version.should be_nil
end
end
context "#find" do
before(:each){ subject.class_eval{ decorates_association :similar_products } }
context "with a block" do
it "delegates to #each" do
subject.similar_products.source.should_receive :find
subject.similar_products.find {|p| p.title == "title" }
end
end
context "without a block" do
it "calls a finder method" do
subject.similar_products.source.should_not_receive :find
subject.similar_products.find 1
end
end
end
end
context('.decorates_associations') do