diff --git a/lib/draper/base.rb b/lib/draper/base.rb index 0a40e51..1baf477 100644 --- a/lib/draper/base.rb +++ b/lib/draper/base.rb @@ -93,7 +93,7 @@ module Draper # @param [Object] instance(s) to wrap # @param [Object] context (optional) def self.decorate(input, context = {}) - input.respond_to?(:each) ? input.map{|i| new(i, context)} : new(input, context) + input.respond_to?(:each) ? DecoratedEnumerableProxy.new(input, self, context) : new(input, context) end # Access the helpers proxy to call built-in and user-defined @@ -158,5 +158,32 @@ module Draper specified = self.allowed || (model.public_methods.map{|s| s.to_sym} - denied.map{|s| s.to_sym}) (specified - self.public_methods.map{|s| s.to_sym}) + FORCED_PROXY end + + class DecoratedEnumerableProxy + include Enumerable + + def initialize(collection, klass, context) + @wrapped_collection, @klass, @context = collection, klass, context + end + + # Implementation of Enumerable#each that proxyes to the wrapped collection + def each(&block) + @wrapped_collection.each { |member| block.call(@klass.new(member, @context)) } + end + + # Implement to_arry so that render @decorated_collection is happy + def to_ary + @wrapped_collection.to_ary + end + + def method_missing (meth, *args, &block) + @wrapped_collection.send(meth, *args, &block) + end + + def to_s + "#" + end + end + end end diff --git a/spec/base_spec.rb b/spec/base_spec.rb index 23b7fe8..2fddfa1 100644 --- a/spec/base_spec.rb +++ b/spec/base_spec.rb @@ -150,6 +150,7 @@ describe Draper::Base do its(:context) { should eq(context) } end end + end context('.==') do @@ -159,6 +160,32 @@ describe Draper::Base do end end + describe "collection decoration" do + + # Implementation of #decorate that returns an array + # of decorated objects is insufficient to deal with + # situations where the original collection has been + # expanded with the use of modules (as often the case + # with paginator gems) or is just more complex then + # an array. + module Paginator; def page_number; "magic_value"; end; end + Array.send(:include, Paginator) + let(:paged_array) { [Product.new, Product.new] } + subject { ProductDecorator.decorate(paged_array) } + + it "should proxy all calls to decorated collection" do + paged_array.page_number.should == "magic_value" + subject.page_number.should == "magic_value" + end + + it "should support Rails partial lookup for a collection" do + # to support Rails render @collection the returned collection + # (or its proxy) should implement #to_ary. + subject.respond_to?(:to_ary).should be true + subject.to_a.first.should == ProductDecorator.decorate(paged_array.first) + end + end + describe "a sample usage with denies" do let(:subject_with_denies){ DecoratorWithDenies.new(source) }