Handle options properly in Decoratable#decorate

In 30104b0 I had accidentally nuked the options but didn't realise
as there was no spec coverage.

I then discovered that, due to memoization, `#decorate` did not
use options after the first call:

    product.decorate(role: :admin)
    product.decorate(role: :user)  # still has admin role!

I ditched the `#decorator` alias since `#decorate` seems far more
canonical, and dropped the block passing syntax, since this seems
orthogonal to the main purpose of the method.
This commit is contained in:
Andrew Haines 2012-11-08 01:14:49 +00:00
parent 7748ce5fcb
commit 52daa3125b
2 changed files with 40 additions and 43 deletions

View File

@ -1,11 +1,9 @@
module Draper::Decoratable
extend ActiveSupport::Concern
def decorator(options = {})
@decorator ||= decorator_class.decorate(self)
block_given? ? yield(@decorator) : @decorator
def decorate(options = {})
decorator_class.decorate(self, options)
end
alias_method :decorate, :decorator
def decorator_class
self.class.decorator_class
@ -25,8 +23,7 @@ module Draper::Decoratable
module ClassMethods
def decorate(options = {})
collection_decorator = decorator_class.decorate_collection(self.scoped, options)
block_given? ? yield(collection_decorator) : collection_decorator
decorator_class.decorate_collection(self.scoped, options)
end
def decorator_class

View File

@ -3,17 +3,19 @@ require 'spec_helper'
describe Draper::Decoratable do
subject { Product.new }
describe '#decorator' do
its(:decorator) { should be_kind_of(ProductDecorator) }
its(:decorator) { should be(subject.decorator) }
describe "#decorate" do
it "returns a decorator for self" do
subject.decorate.should be_a ProductDecorator
subject.decorate.source.should be subject
end
it 'have abillity to pass block' do
a = Product.new.decorator { |d| d.awesome_title }
a.should eql "Awesome Title"
it "accepts options" do
decorator = subject.decorate(some: "options")
decorator.options.should == {some: "options"}
end
it 'is aliased to .decorate' do
subject.decorator.model.should == subject.decorate.model
it "is not memoized" do
subject.decorate.should_not be subject.decorate
end
end
@ -43,6 +45,27 @@ describe Draper::Decoratable do
end
end
describe ".decorate" do
it "returns a collection decorator" do
Product.stub(:scoped).and_return([Product.new])
Product.stub(:decorator_class).and_return(WidgetDecorator)
decorator = Product.decorate
decorator.should be_a Draper::CollectionDecorator
decorator.decorator_class.should be WidgetDecorator
decorator.source.should be Product.scoped
end
it "accepts options" do
decorator = Product.decorate(some: "options")
decorator.options.should == {some: "options"}
end
it "is not memoized" do
Product.decorate.should_not be Product.decorate
end
end
describe ".decorator_class" do
context "for non-ActiveModel classes" do
it "infers the decorator from the class" do
@ -57,39 +80,16 @@ describe Draper::Decoratable do
end
end
context "for namespaced ActiveModel classes" do
it "infers the decorator from the model name" do
Namespace::Product.decorator_class.should be Namespace::ProductDecorator
end
end
context "when the decorator can't be inferred" do
it "throws an UninferrableDecoratorError" do
expect{UninferrableDecoratorModel.decorator_class}.to raise_error Draper::UninferrableDecoratorError
end
end
end
describe Draper::Decoratable::ClassMethods do
shared_examples_for "a call to Draper::Decoratable::ClassMethods#decorate" do
subject { klass.limit }
its(:decorate) { should be_kind_of(Draper::CollectionDecorator) }
it "decorate the collection" do
subject.decorate.size.should == 1
subject.decorate.to_ary[0].model.should be_a(klass)
end
it "return a new instance each time it is called" do
subject.decorate.should_not == subject.decorate
end
end
describe '#decorate - decorate collections of AR objects' do
let(:klass) { Product }
it_should_behave_like "a call to Draper::Decoratable::ClassMethods#decorate"
end
describe '#decorate - decorate collections of namespaced AR objects' do
let(:klass) { Namespace::Product }
it_should_behave_like "a call to Draper::Decoratable::ClassMethods#decorate"
end
end
end