Provide support for class methods. Reintroduce the decorates class method, which may be used to hint at which class to delegate class methods to.

This commit is contained in:
Chris Heald 2012-12-01 05:16:35 -07:00 committed by Andrew Haines
parent 41ca1a3203
commit 731995a5fe
5 changed files with 59 additions and 22 deletions

View File

@ -45,7 +45,33 @@ module Draper
# @option options [Class, Symbol] :for The model class to find
def self.has_finders(options = {})
extend Draper::Finders
self.finder_class = options[:for] || name.chomp("Decorator")
if klass = options.delete(:for)
decorates klass
end
end
class << self
# Specify the class that this class decorates.
#
# @param [String, Symbol, Class] Class or name of class to decorate.
def decorates(klass)
@source_class = klass.kind_of?(Class) ? klass : klass.to_s.classify.constantize
end
# Provides access to the class that this decorator decorates
#
# @return [Class]: The class wrapped by the decorator
def source_class
@source_class ||= name.chomp("Decorator").constantize
end
def method_missing(method, *args, &block)
source_class.send(method, *args, &block)
end
def respond_to?(method, include_private = false)
super || source_class.respond_to?(method)
end
end
# Typically called within a decorator definition, this method causes

20
lib/draper/finders.rb Normal file → Executable file
View File

@ -1,29 +1,24 @@
module Draper
module Finders
attr_reader :finder_class
def finder_class=(klass)
@finder_class = klass.to_s.camelize.constantize
end
def find(id, options = {})
decorate(finder_class.find(id), options)
decorate(source_class.find(id), options)
end
def all(options = {})
decorate_collection(finder_class.all, options)
decorate_collection(source_class.all, options)
end
def first(options = {})
decorate(finder_class.first, options)
decorate(source_class.first, options)
end
def last(options = {})
decorate(finder_class.last, options)
decorate(source_class.last, options)
end
def method_missing(method, *args, &block)
result = finder_class.send(method, *args, &block)
result = source_class.send(method, *args, &block)
options = args.extract_options!
case method.to_s
@ -35,10 +30,5 @@ module Draper
result
end
end
def respond_to?(method, include_private = false)
super || finder_class.respond_to?(method)
end
end
end

View File

@ -450,12 +450,12 @@ describe Draper::Decorator do
context "with no options" do
it "infers the finder class" do
ProductDecorator.finder_class.should be Product
ProductDecorator.source_class.should be Product
end
context "for a namespaced model" do
it "infers the finder class" do
Namespace::ProductDecorator.finder_class.should be Namespace::Product
Namespace::ProductDecorator.source_class.should be Namespace::Product
end
end
end
@ -466,21 +466,21 @@ describe Draper::Decorator do
context "with a symbol" do
it "sets the finder class" do
subject.has_finders for: :product
subject.finder_class.should be Product
subject.source_class.should be Product
end
end
context "with a string" do
it "sets the finder class" do
subject.has_finders for: "some_thing"
subject.finder_class.should be SomeThing
subject.source_class.should be SomeThing
end
end
context "with a class" do
it "sets the finder_class" do
it "sets the source_class" do
subject.has_finders for: Namespace::Product
subject.finder_class.should be Namespace::Product
subject.source_class.should be Namespace::Product
end
end
end
@ -494,4 +494,18 @@ 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
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
end
end
end

4
spec/dummy/app/decorators/post_decorator.rb Normal file → Executable file
View File

@ -22,4 +22,8 @@ class PostDecorator < Draper::Decorator
def url_with_id
h.post_url(id: id)
end
def link
h.link_to id.to_s, self
end
end

3
spec/dummy/spec/decorators/post_decorator_spec.rb Normal file → Executable file
View File

@ -20,4 +20,7 @@ describe PostDecorator do
subject.url_with_id.should == "http://www.example.com/en/posts/#{source.id}"
end
it "can be passed implicitly to url_for" do
subject.link.should == "<a href=\"/en/posts/#{source.id}\">#{source.id}</a>"
end
end