Add extra specs

This commit is contained in:
Andrew Haines 2012-12-02 14:13:32 +00:00
parent 18ebac8153
commit 4d03887b66
4 changed files with 217 additions and 114 deletions

View File

@ -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)

View File

@ -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 == "<a href=\"/World\">Hello</a>"
subject.sample_link.should == %{<a href="/World">Hello</a>}
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

View File

@ -9,6 +9,10 @@ class ProductDecorator < Draper::Decorator
:overridden
end
def self.overridable
:overridden
end
def self.my_class_method
end

View File

@ -84,6 +84,10 @@ class Product < ActiveRecord::Base
:overridable
end
def self.overridable
:overridable
end
private
def private_title