Skip to content

Commit

Permalink
Merge pull request #406 from haines/collection_decorator
Browse files Browse the repository at this point in the history
Unfreeze collection decorator
  • Loading branch information
steveklabnik committed Dec 30, 2012
2 parents e4fa239 + 68151a8 commit c6d60e6
Show file tree
Hide file tree
Showing 6 changed files with 21 additions and 39 deletions.
15 changes: 5 additions & 10 deletions lib/draper/collection_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,17 @@ class CollectionDecorator
include Enumerable
include ViewHelpers

attr_reader :source
alias_method :to_source, :source

attr_accessor :context

array_methods = Array.instance_methods - Object.instance_methods
delegate :as_json, *array_methods, to: :decorated_collection
delegate :==, :as_json, *array_methods, to: :decorated_collection

# @param source collection to decorate
# @option options [Class] :with the class used to decorate items
# @option options [Hash] :context context available to each item's decorator
def initialize(source, options = {})
options.assert_valid_keys(:with, :context)
@source = source.to_a.dup.freeze
@source = source
@decorator_class = options[:with]
@context = options.fetch(:context, {})
end
Expand All @@ -26,7 +23,7 @@ class << self
end

def decorated_collection
@decorated_collection ||= source.map{|item| decorate_item(item)}.freeze
@decorated_collection ||= source.map{|item| decorate_item(item)}
end

def find(*args, &block)
Expand All @@ -37,10 +34,6 @@ def find(*args, &block)
end
end

def ==(other)
source == (other.respond_to?(:source) ? other.source : other)
end

def to_s
klass = begin
decorator_class
Expand All @@ -62,6 +55,8 @@ def decorator_class

protected

attr_reader :source

def decorate_item(item)
item_decorator.call(item, context: context)
end
Expand Down
35 changes: 11 additions & 24 deletions spec/draper/collection_decorator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,6 @@
subject.map{|item| item.source}.should == source
end

describe "#source" do
it "duplicates the source collection" do
subject.source.should == source
subject.source.should_not be source
end

it "is frozen" do
subject.source.should be_frozen
end

it "is aliased to #to_source" do
subject.to_source.should == source
end
end

describe "#decorated_collection" do
it "is frozen" do
subject.decorated_collection.should be_frozen
end
end

context "with context" do
subject { Draper::CollectionDecorator.new(source, with: ProductDecorator, context: {some: 'context'}) }

Expand Down Expand Up @@ -151,7 +130,6 @@
describe "#find" do
context "with a block" do
it "decorates Enumerable#find" do
subject.stub decorated_collection: []
subject.decorated_collection.should_receive(:find)
subject.find {|p| p.title == "title" }
end
Expand Down Expand Up @@ -208,13 +186,12 @@
describe "#to_ary" do
# required for `render @collection` in Rails
it "delegates to the decorated collection" do
subject.stub decorated_collection: double(to_ary: :an_array)
subject.decorated_collection.stub to_ary: :an_array
subject.to_ary.should == :an_array
end
end

it "delegates array methods to the decorated collection" do
subject.stub decorated_collection: []
subject.decorated_collection.should_receive(:[]).with(42).and_return(:the_answer)
subject[42].should == :the_answer
end
Expand Down Expand Up @@ -251,6 +228,16 @@
a.should_not == b
end
end

context "when the decorated collection has been modified" do
it "is no longer equal to the source" do
a = Draper::CollectionDecorator.new(source, with: ProductDecorator)
b = source.dup

a << Product.new.decorate
a.should_not == b
end
end
end

describe "#to_s" do
Expand Down
2 changes: 1 addition & 1 deletion spec/draper/decoratable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@

decorator.should be_a Draper::CollectionDecorator
decorator.decorator_class.should be WidgetDecorator
decorator.source.should == Product.scoped
decorator.should == Product.scoped
end

it "accepts context" do
Expand Down
4 changes: 2 additions & 2 deletions spec/draper/decorated_association_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@
let(:options) { {scope: :foo} }

it "applies the scope before decoration" do
scoped = [:scoped]
scoped = [Product.new]
associated.should_receive(:foo).and_return(scoped)
decorated_association.call.source.should == scoped
decorated_association.call.should == scoped
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion spec/draper/decorator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@

it "returns a collection decorator" do
subject.should be_a Draper::CollectionDecorator
subject.source.should == source
subject.should == source
end

it "uses itself as the item decorator by default" do
Expand Down
2 changes: 1 addition & 1 deletion spec/draper/finders_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
Product.stub(:find_all_by_name).and_return(found)
decorator = ProductDecorator.find_all_by_name("apples")
decorator.should be_a Draper::CollectionDecorator
decorator.source.should == found
decorator.should == found
end
end

Expand Down

0 comments on commit c6d60e6

Please sign in to comment.