diff --git a/lib/draper/base.rb b/lib/draper/base.rb index 23b579f..884588d 100644 --- a/lib/draper/base.rb +++ b/lib/draper/base.rb @@ -53,6 +53,32 @@ module Draper define_method(input){ @model } end + # Typically called withing a decorator definition, this method causes + # the assocation to be decorated when it is retrieved. + # + # @param [Symbol] name of association to decorate, like `:products` + # @option opts [Class] :with The decorator to decorate the association with + def self.decorates_association(association_symbol, options = {}) + define_method(association_symbol) do + orig_association = model.send(association_symbol) + return orig_association if orig_association.nil? + if options[:with] + options[:with].decorate(orig_association) + else + reflection = model.class.reflect_on_association(association_symbol) + "#{reflection.klass}Decorator".constantize.decorate(orig_association) + end + end + end + + # A convenience method for decorating multiple associations. Calls + # decorates_association on each of the given symbols. + # + # @param [Symbols*] name of associations to decorate + def self.decorates_associations(*association_symbols) + association_symbols.each{ |sym| decorates_association(sym) } + end + # Specifies a black list of methods which may *not* be proxied to # to the wrapped object. # diff --git a/spec/draper/base_spec.rb b/spec/draper/base_spec.rb index e0457db..3670e05 100644 --- a/spec/draper/base_spec.rb +++ b/spec/draper/base_spec.rb @@ -74,6 +74,44 @@ describe Draper::Base do end end + context(".decorates_association") do + context "for collection associations" do + before(:each){ subject.class_eval{ decorates_association :similar_products } } + it "causes the association's method to return a collection of wrapped objects" do + subject.similar_products.each{ |decorated| decorated.should be_instance_of(ProductDecorator) } + end + end + + context "for a singular association" do + before(:each){ subject.class_eval{ decorates_association :previous_version } } + it "causes the association's method to return a single wrapped object if the association is singular" do + subject.previous_version.should be_instance_of(ProductDecorator) + end + + it "causes the association's method to return nil if the association is nil" do + source.stub(:previous_version){ nil } + subject.previous_version.should be_nil + end + end + + context "with a specific decorator specified" do + before(:each){ subject.class_eval{ decorates_association :previous_version, :with => SpecificProductDecorator } } + it "causes the association to be decorated with the specified association" do + subject.previous_version.should be_instance_of(SpecificProductDecorator) + end + end + end + + context('.decorates_associations') do + subject { Decorator } + it "decorates each of the associations" do + subject.should_receive(:decorates_association).with(:similar_products) + subject.should_receive(:decorates_association).with(:previous_version) + + subject.decorates_associations :similar_products, :previous_version + end + end + context(".model / .to_model") do it "should return the wrapped object" do subject.to_model.should == source diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 52db66b..5d04ab0 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -13,5 +13,6 @@ require './spec/support/samples/namespaced_product.rb' require './spec/support/samples/namespaced_product_decorator.rb' require './spec/support/samples/product.rb' require './spec/support/samples/product_decorator.rb' +require './spec/support/samples/specific_product_decorator.rb' require './spec/support/samples/widget.rb' require './spec/support/samples/widget_decorator.rb' diff --git a/spec/support/samples/product.rb b/spec/support/samples/product.rb index 42f9062..fe5d371 100644 --- a/spec/support/samples/product.rb +++ b/spec/support/samples/product.rb @@ -44,4 +44,16 @@ class Product < ActiveRecord::Base def block yield end + + def self.reflect_on_association(association_symbol) + OpenStruct.new(:klass => self) + end + + def similar_products + [Product.new, Product.new] + end + + def previous_version + Product.new + end end diff --git a/spec/support/samples/specific_product_decorator.rb b/spec/support/samples/specific_product_decorator.rb new file mode 100644 index 0000000..ba514bd --- /dev/null +++ b/spec/support/samples/specific_product_decorator.rb @@ -0,0 +1,4 @@ +require './spec/support/samples/product_decorator' + +class SpecificProductDecorator < ProductDecorator +end \ No newline at end of file