From 731995a5feac4cd06cf9328d2892c0eca9992db6 Mon Sep 17 00:00:00 2001 From: Chris Heald Date: Sat, 1 Dec 2012 05:16:35 -0700 Subject: [PATCH] Provide support for class methods. Reintroduce the decorates class method, which may be used to hint at which class to delegate class methods to. --- lib/draper/decorator.rb | 28 ++++++++++++++++++- lib/draper/finders.rb | 20 ++++--------- spec/draper/decorator_spec.rb | 26 +++++++++++++---- spec/dummy/app/decorators/post_decorator.rb | 4 +++ .../spec/decorators/post_decorator_spec.rb | 3 ++ 5 files changed, 59 insertions(+), 22 deletions(-) mode change 100644 => 100755 lib/draper/finders.rb mode change 100644 => 100755 spec/dummy/app/decorators/post_decorator.rb mode change 100644 => 100755 spec/dummy/spec/decorators/post_decorator_spec.rb diff --git a/lib/draper/decorator.rb b/lib/draper/decorator.rb index dc15473d..e4ab30d9 100755 --- a/lib/draper/decorator.rb +++ b/lib/draper/decorator.rb @@ -45,7 +45,33 @@ class << self # @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 diff --git a/lib/draper/finders.rb b/lib/draper/finders.rb old mode 100644 new mode 100755 index 40c9564f..2ffcecdb --- a/lib/draper/finders.rb +++ b/lib/draper/finders.rb @@ -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 @@ def method_missing(method, *args, &block) result end end - - def respond_to?(method, include_private = false) - super || finder_class.respond_to?(method) - end - end end diff --git a/spec/draper/decorator_spec.rb b/spec/draper/decorator_spec.rb index cb783cd7..c0620710 100755 --- a/spec/draper/decorator_spec.rb +++ b/spec/draper/decorator_spec.rb @@ -450,12 +450,12 @@ 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 @@ 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 @@ 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 diff --git a/spec/dummy/app/decorators/post_decorator.rb b/spec/dummy/app/decorators/post_decorator.rb old mode 100644 new mode 100755 index 58454eab..5cf0ec38 --- a/spec/dummy/app/decorators/post_decorator.rb +++ b/spec/dummy/app/decorators/post_decorator.rb @@ -22,4 +22,8 @@ def url_with_model def url_with_id h.post_url(id: id) end + + def link + h.link_to id.to_s, self + end end diff --git a/spec/dummy/spec/decorators/post_decorator_spec.rb b/spec/dummy/spec/decorators/post_decorator_spec.rb old mode 100644 new mode 100755 index 6114df10..4817997e --- a/spec/dummy/spec/decorators/post_decorator_spec.rb +++ b/spec/dummy/spec/decorators/post_decorator_spec.rb @@ -20,4 +20,7 @@ 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 == "#{source.id}" + end end