Raise UninferrableSourceErrors
This commit is contained in:
parent
6736fd7d87
commit
8ef5bf2f02
|
@ -48,4 +48,10 @@ module Draper
|
|||
super("Could not infer a decorator for #{klass}.")
|
||||
end
|
||||
end
|
||||
|
||||
class UninferrableSourceError < NameError
|
||||
def initialize(klass)
|
||||
super("Could not infer a source for #{klass}.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -34,27 +34,25 @@ module Draper
|
|||
alias_method :decorate, :new
|
||||
end
|
||||
|
||||
UNKNOWN_SOURCE_ERROR_MESSAGE = "Cannot find source_class for %s. Use `decorates` to specify the source_class."
|
||||
|
||||
# Specify the class that this class decorates.
|
||||
#
|
||||
# @param [String, Symbol, Class] Class or name of class to decorate.
|
||||
def self.decorates(klass)
|
||||
@source_class = klass.kind_of?(Class) ? klass : klass.to_s.classify.constantize
|
||||
@source_class = klass.to_s.classify.constantize
|
||||
end
|
||||
|
||||
# Provides access to the class that this decorator decorates
|
||||
#
|
||||
# @return [Class]: The class wrapped by the decorator
|
||||
# @return [Class] The source class corresponding to this
|
||||
# decorator class
|
||||
def self.source_class
|
||||
@source_class ||= begin
|
||||
raise NameError.new(UNKNOWN_SOURCE_ERROR_MESSAGE % "<unnamed decorator>") if name.nil? or name.chomp("Decorator").blank?
|
||||
begin
|
||||
name.chomp("Decorator").constantize
|
||||
rescue NameError
|
||||
raise NameError.new(UNKNOWN_SOURCE_ERROR_MESSAGE % "`#{name.chomp("Decorator")}`")
|
||||
end
|
||||
end
|
||||
@source_class ||= inferred_source_class
|
||||
end
|
||||
|
||||
# Checks whether this decorator class has a corresponding
|
||||
# source class
|
||||
def self.source_class?
|
||||
source_class
|
||||
rescue Draper::UninferrableSourceError
|
||||
false
|
||||
end
|
||||
|
||||
# Automatically decorates ActiveRecord finder methods, so that
|
||||
|
@ -184,7 +182,7 @@ module Draper
|
|||
end
|
||||
|
||||
def method_missing(method, *args, &block)
|
||||
if allow?(method) && source.respond_to?(method)
|
||||
if delegatable_method?(method)
|
||||
self.class.define_proxy(method)
|
||||
send(method, *args, &block)
|
||||
else
|
||||
|
@ -193,32 +191,41 @@ module Draper
|
|||
end
|
||||
|
||||
def respond_to?(method, include_private = false)
|
||||
super || (allow?(method) && source.respond_to?(method, include_private))
|
||||
super || delegatable_method?(method)
|
||||
end
|
||||
|
||||
def self.method_missing(method, *args, &block)
|
||||
# We see if we have a valid source here rather than in the #send since we don't want to accidentally capture
|
||||
# NameError raised by a method called on a valid #source_class
|
||||
begin
|
||||
source_class
|
||||
rescue NameError
|
||||
return super
|
||||
if delegatable_method?(method)
|
||||
source_class.send(method, *args, &block)
|
||||
else
|
||||
super
|
||||
end
|
||||
|
||||
# If we pass the source_class invocation, then we can send the method on to the decorated class.
|
||||
source_class.send(method, *args, &block)
|
||||
end
|
||||
|
||||
def self.respond_to?(method, include_private = false)
|
||||
super || begin
|
||||
source_class.respond_to?(method)
|
||||
rescue NameError
|
||||
false
|
||||
end
|
||||
super || delegatable_method?(method)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def delegatable_method?(method)
|
||||
allow?(method) && source.respond_to?(method)
|
||||
end
|
||||
|
||||
def self.delegatable_method?(method)
|
||||
source_class? && source_class.respond_to?(method)
|
||||
end
|
||||
|
||||
def self.inferred_source_class
|
||||
raise Draper::UninferrableSourceError.new(self) if name.nil? || name.chomp("Decorator").empty?
|
||||
|
||||
begin
|
||||
name.chomp("Decorator").constantize
|
||||
rescue NameError
|
||||
raise Draper::UninferrableSourceError.new(self)
|
||||
end
|
||||
end
|
||||
|
||||
def self.define_proxy(method)
|
||||
define_method(method) do |*args, &block|
|
||||
source.send(method, *args, &block)
|
||||
|
|
|
@ -471,55 +471,55 @@ describe Draper::Decorator do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#respond_to?" do
|
||||
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) }.to_not raise_error(NameError)
|
||||
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 == false
|
||||
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 == true
|
||||
subject.respond_to?(:denies_all).should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#source_class" do
|
||||
describe ".source_class" do
|
||||
context "when called on an anonymous decorator" do
|
||||
subject { -> { Class.new(Draper::Decorator).source_class } }
|
||||
it { should raise_error(NameError, /Use `decorates` to specify the source_class/) }
|
||||
subject { ->{ Class.new(Draper::Decorator).source_class } }
|
||||
it { should raise_error Draper::UninferrableSourceError }
|
||||
end
|
||||
|
||||
context "when called on a decorator that can't infer the class name" do
|
||||
subject { -> { SpecificProductDecorator.source_class } }
|
||||
it { should raise_error(NameError, /Use `decorates` to specify the source_class/) }
|
||||
subject { ->{ SpecificProductDecorator.source_class } }
|
||||
it { should raise_error Draper::UninferrableSourceError }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#method_missing" do
|
||||
describe ".method_missing" do
|
||||
context "when called on an anonymous decorator" do
|
||||
subject { lambda { Class.new(Draper::Decorator).fizzbuzz } }
|
||||
it { should raise_error(NoMethodError) }
|
||||
subject { ->{ Class.new(Draper::Decorator).fizzbuzz } }
|
||||
it { should raise_error NoMethodError }
|
||||
end
|
||||
|
||||
context "when called on an uninferrable decorator" do
|
||||
subject { lambda { SpecificProductDecorator.fizzbuzz } }
|
||||
it { should raise_error(NoMethodError) }
|
||||
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 { lambda { ProductDecorator.model_name } }
|
||||
it { should_not raise_error(NoMethodError) }
|
||||
subject { ->{ ProductDecorator.model_name } }
|
||||
it { should_not raise_error }
|
||||
end
|
||||
|
||||
context "for a method unknown to the inferred class" do
|
||||
subject { lambda { ProductDecorator.fizzbuzz } }
|
||||
it { should raise_error(NoMethodError) }
|
||||
subject { ->{ ProductDecorator.fizzbuzz } }
|
||||
it { should raise_error NoMethodError }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue