From 4d03887b665898c2b32c42429e37b955ec3d0d54 Mon Sep 17 00:00:00 2001 From: Andrew Haines Date: Sun, 2 Dec 2012 14:13:32 +0000 Subject: [PATCH] Add extra specs --- lib/draper/decorator.rb | 8 +- spec/draper/decorator_spec.rb | 315 ++++++++++++------- spec/support/decorators/product_decorator.rb | 4 + spec/support/models/product.rb | 4 + 4 files changed, 217 insertions(+), 114 deletions(-) diff --git a/lib/draper/decorator.rb b/lib/draper/decorator.rb index 5fcb5ac..4a31f39 100755 --- a/lib/draper/decorator.rb +++ b/lib/draper/decorator.rb @@ -217,15 +217,19 @@ module Draper end def self.inferred_source_class - raise Draper::UninferrableSourceError.new(self) if name.nil? || name.chomp("Decorator").empty? + uninferrable_source if name.nil? || name.demodulize !~ /.+Decorator$/ begin name.chomp("Decorator").constantize rescue NameError - raise Draper::UninferrableSourceError.new(self) + uninferrable_source end end + def self.uninferrable_source + raise Draper::UninferrableSourceError.new(self) + end + def self.define_proxy(method) define_method(method) do |*args, &block| source.send(method, *args, &block) diff --git a/spec/draper/decorator_spec.rb b/spec/draper/decorator_spec.rb index 2988412..6401571 100755 --- a/spec/draper/decorator_spec.rb +++ b/spec/draper/decorator_spec.rb @@ -125,6 +125,97 @@ describe Draper::Decorator do end end + describe ".decorates" do + subject { Class.new(Draper::Decorator) } + + context "with a symbol" do + it "sets .source_class" do + subject.decorates :product + subject.source_class.should be Product + end + end + + context "with a string" do + it "sets .source_class" do + subject.decorates "product" + subject.source_class.should be Product + end + end + + context "with a class" do + it "sets .source_class" do + subject.decorates Product + subject.source_class.should be Product + end + end + end + + describe ".source_class" do + context "when not set by .decorates" do + context "for an anonymous decorator" do + subject { Class.new(Draper::Decorator) } + + it "raises an UninferrableSourceError" do + expect{subject.source_class}.to raise_error Draper::UninferrableSourceError + end + end + + context "for a decorator without a corresponding source" do + subject { SpecificProductDecorator } + + it "raises an UninferrableSourceError" do + expect{subject.source_class}.to raise_error Draper::UninferrableSourceError + end + end + + context "for a decorator called Decorator" do + subject { Draper::Decorator } + + it "raises an UninferrableSourceError" do + expect{subject.source_class}.to raise_error Draper::UninferrableSourceError + end + end + + context "for a decorator with a name not ending in Decorator" do + subject { DecoratorWithApplicationHelper } + + it "raises an UninferrableSourceError" do + expect{subject.source_class}.to raise_error Draper::UninferrableSourceError + end + end + + context "for an inferrable source" do + subject { ProductDecorator } + + it "infers the source" do + subject.source_class.should be Product + end + end + + context "for a namespaced inferrable source" do + subject { Namespace::ProductDecorator } + + it "infers the namespaced source" do + subject.source_class.should be Namespace::Product + end + end + end + end + + describe ".source_class?" do + subject { Class.new(Draper::Decorator) } + + it "returns truthy when .source_class is set" do + subject.stub(:source_class).and_return(Product) + subject.source_class?.should be_true + end + + it "returns false when .source_class is not inferrable" do + subject.stub(:source_class).and_raise(Draper::UninferrableSourceError.new(subject)) + subject.source_class?.should be_false + end + end + describe ".decorates_association" do let(:decorator_class) { Class.new(ProductDecorator) } before { decorator_class.decorates_association :similar_products, with: ProductDecorator } @@ -327,67 +418,110 @@ describe Draper::Decorator do end end - describe "method proxying" do - let(:decorator_class) { Class.new(ProductDecorator) } + describe ".respond_to?" do + subject { Class.new(ProductDecorator) } - it "does not proxy methods that are defined on the decorator" do - subject.overridable.should be :overridden - end - - it "does not proxy methods inherited from Object" do - subject.inspect.should_not be source.inspect - end - - it "proxies missing methods that exist on the source" do - source.stub(:hello_world).and_return(:proxied) - subject.hello_world.should be :proxied - end - - it "adds proxied methods to the decorator when they are used" do - subject.methods.should_not include :hello_world - subject.hello_world - subject.methods.should include :hello_world - end - - it "passes blocks to proxied methods" do - subject.block{"marker"}.should == "marker" - end - - it "does not confuse Kernel#Array" do - Array(subject).should be_a Array - end - - it "proxies delegated methods" do - subject.delegated_method.should == "Yay, delegation" - end - - it "does not proxy private methods" do - expect{subject.private_title}.to raise_error NoMethodError - end - - context "with method security" do - it "respects allows" do - source.stub(:hello_world, :goodnight_moon).and_return(:proxied) - subject.class.allows :hello_world - - subject.hello_world.should be :proxied - expect{subject.goodnight_moon}.to raise_error NameError + context "without a source class" do + it "returns true for its own class methods" do + subject.should respond_to :my_class_method end - it "respects denies" do - source.stub(:hello_world, :goodnight_moon).and_return(:proxied) - subject.class.denies :goodnight_moon + it "returns false for other class methods" do + subject.should_not respond_to :sample_class_method + end + end - subject.hello_world.should be :proxied - expect{subject.goodnight_moon}.to raise_error NameError + context "with a source_class" do + before { subject.decorates :product } + + it "returns true for its own class methods" do + subject.should respond_to :my_class_method end - it "respects denies_all" do - source.stub(:hello_world, :goodnight_moon).and_return(:proxied) - subject.class.denies_all + it "returns true for the source's class methods" do + subject.should respond_to :sample_class_method + end + end + end - expect{subject.hello_world}.to raise_error NameError - expect{subject.goodnight_moon}.to raise_error NameError + describe "proxying" do + context "instance methods" do + let(:decorator_class) { Class.new(ProductDecorator) } + + it "does not proxy methods that are defined on the decorator" do + subject.overridable.should be :overridden + end + + it "does not proxy methods inherited from Object" do + subject.inspect.should_not be source.inspect + end + + it "proxies missing methods that exist on the source" do + source.stub(:hello_world).and_return(:proxied) + subject.hello_world.should be :proxied + end + + it "adds proxied methods to the decorator when they are used" do + subject.methods.should_not include :hello_world + subject.hello_world + subject.methods.should include :hello_world + end + + it "passes blocks to proxied methods" do + subject.block{"marker"}.should == "marker" + end + + it "does not confuse Kernel#Array" do + Array(subject).should be_a Array + end + + it "proxies delegated methods" do + subject.delegated_method.should == "Yay, delegation" + end + + it "does not proxy private methods" do + expect{subject.private_title}.to raise_error NoMethodError + end + + context "with method security" do + it "respects allows" do + source.stub(:hello_world, :goodnight_moon).and_return(:proxied) + subject.class.allows :hello_world + + subject.hello_world.should be :proxied + expect{subject.goodnight_moon}.to raise_error NameError + end + + it "respects denies" do + source.stub(:hello_world, :goodnight_moon).and_return(:proxied) + subject.class.denies :goodnight_moon + + subject.hello_world.should be :proxied + expect{subject.goodnight_moon}.to raise_error NameError + end + + it "respects denies_all" do + source.stub(:hello_world, :goodnight_moon).and_return(:proxied) + subject.class.denies_all + + expect{subject.hello_world}.to raise_error NameError + expect{subject.goodnight_moon}.to raise_error NameError + end + end + end + + context "class methods" do + subject { Class.new(ProductDecorator) } + let(:source_class) { Product } + before { subject.decorates source_class } + + it "does not proxy methods that are defined on the decorator" do + subject.overridable.should be :overridden + end + + it "proxies missing methods that exist on the source" do + source_class.stub(:hello_world).and_return(:proxied) + subject.hello_world.should be :proxied end end end @@ -425,7 +559,7 @@ describe Draper::Decorator do end it "is able to use the link_to helper" do - subject.sample_link.should == "Hello" + subject.sample_link.should == %{Hello} end it "is able to use the truncate helper" do @@ -461,71 +595,28 @@ describe Draper::Decorator do end end - context "class methods" do - it "passes through to the underlying wrapped class" do - ProductDecorator.sample_class_method.should == Product.sample_class_method + describe ".method_missing" do + context "when called on an anonymous decorator" do + subject { ->{ Class.new(Draper::Decorator).fizzbuzz } } + it { should raise_error NoMethodError } end - context "when told to decorate a different class " do - subject { decorator_class } - before { decorator_class.decorates :product } - - it "should manually set the class to pass methods to" do - subject.sample_class_method.should == Product.sample_class_method - end + context "when called on an uninferrable decorator" do + subject { ->{ SpecificProductDecorator.fizzbuzz } } + it { should raise_error NoMethodError } end - describe ".respond_to?" do - context "when using a decorator that cannot infer an underlying model" do - subject { Class.new(Draper::Decorator) } - it "should not throw an exception during respond_to? due to an inability to find the inferred decorated class" do - expect { subject.respond_to?(:fizzbuzz) }.not_to raise_error - end - - it "should return false for methods that it can't find" do - subject.respond_to?(:fizzbuzz).should be_false - end - - it "should return true for methods that it can find" do - subject.respond_to?(:denies_all).should be_true - end - end - end - - describe ".source_class" do - context "when called on an anonymous decorator" do - subject { ->{ Class.new(Draper::Decorator).source_class } } - it { should raise_error Draper::UninferrableSourceError } + context "when called on an inferrable decorator" do + context "for a method known to the inferred class" do + subject { ->{ ProductDecorator.model_name } } + it { should_not raise_error } end - context "when called on a decorator that can't infer the class name" do - subject { ->{ SpecificProductDecorator.source_class } } - it { should raise_error Draper::UninferrableSourceError } - end - end - - describe ".method_missing" do - context "when called on an anonymous decorator" do - subject { ->{ Class.new(Draper::Decorator).fizzbuzz } } + context "for a method unknown to the inferred class" do + subject { ->{ ProductDecorator.fizzbuzz } } it { should raise_error NoMethodError } end - - context "when called on an uninferrable decorator" do - subject { ->{ SpecificProductDecorator.fizzbuzz } } - it { should raise_error NoMethodError } - end - - context "when called on an inferrable decorator" do - context "for a method known to the inferred class" do - subject { ->{ ProductDecorator.model_name } } - it { should_not raise_error } - end - - context "for a method unknown to the inferred class" do - subject { ->{ ProductDecorator.fizzbuzz } } - it { should raise_error NoMethodError } - end - end end end + end diff --git a/spec/support/decorators/product_decorator.rb b/spec/support/decorators/product_decorator.rb index 7408c64..18a5d93 100644 --- a/spec/support/decorators/product_decorator.rb +++ b/spec/support/decorators/product_decorator.rb @@ -9,6 +9,10 @@ class ProductDecorator < Draper::Decorator :overridden end + def self.overridable + :overridden + end + def self.my_class_method end diff --git a/spec/support/models/product.rb b/spec/support/models/product.rb index f061ea7..d9c08ca 100644 --- a/spec/support/models/product.rb +++ b/spec/support/models/product.rb @@ -84,6 +84,10 @@ class Product < ActiveRecord::Base :overridable end + def self.overridable + :overridable + end + private def private_title