diff --git a/lib/draper/base.rb b/lib/draper/base.rb index 3e46b88..712fd87 100644 --- a/lib/draper/base.rb +++ b/lib/draper/base.rb @@ -1,9 +1,9 @@ module Draper - class Base + class Base require 'active_support/core_ext/class/attribute' class_attribute :denied, :allowed, :model_class attr_accessor :model - + DEFAULT_DENIED = Object.new.methods << :method_missing FORCED_PROXY = [:to_param] self.denied = DEFAULT_DENIED @@ -11,24 +11,24 @@ module Draper def initialize(input) input.inspect self.class.model_class = input.class if model_class.nil? - @model = input + @model = input build_methods end - + def self.find(input) self.new(model_class.find(input)) end - + def self.decorates(input) self.model_class = input.to_s.classify.constantize end - + def self.denies(*input_denied) raise ArgumentError, "Specify at least one method (as a symbol) to exclude when using denies" if input_denied.empty? raise ArgumentError, "Use either 'allows' or 'denies', but not both." if self.allowed? self.denied += input_denied end - + def self.allows(*input_allows) raise ArgumentError, "Specify at least one method (as a symbol) to allow when using allows" if input_allows.empty? raise ArgumentError, "Use either 'allows' or 'denies', but not both." unless (self.denied == DEFAULT_DENIED) @@ -38,7 +38,7 @@ module Draper def self.decorate(input) input.respond_to?(:each) ? input.map{|i| new(i)} : new(input) end - + def helpers @helpers ||= ApplicationController::all_helpers end @@ -47,16 +47,16 @@ module Draper def self.lazy_helpers self.send(:include, Draper::LazyHelpers) end - + def self.model_name ActiveModel::Name.new(model_class) end - + def to_model @model end - - private + + private def select_methods specified = self.allowed || (model.public_methods - denied) (specified - self.public_methods) + FORCED_PROXY @@ -69,7 +69,7 @@ module Draper model.send method, *args, &block end end - end - end + end + end end -end \ No newline at end of file +end diff --git a/spec/base_spec.rb b/spec/base_spec.rb index 61aecfd..1bf8828 100644 --- a/spec/base_spec.rb +++ b/spec/base_spec.rb @@ -3,7 +3,7 @@ require 'draper' describe Draper::Base do subject{ Draper::Base.new(source) } - let(:source){ Product.new } + let(:source){ Product.new } context(".lazy_helpers") do it "makes Rails helpers available without using the h. proxy" do @@ -11,7 +11,7 @@ describe Draper::Base do subject.send(:pluralize, 5, "cat").should == "5 cats" end end - + context(".model_name") do it "should return an ActiveModel::Name instance" do Draper::Base.model_name.should be_instance_of(ActiveModel::Name) @@ -23,14 +23,14 @@ describe Draper::Base do ProductDecorator.new(source).model_class == Product end end - + context(".model / .to_model") do it "should return the wrapped object" do subject.to_model.should == source subject.model.should == source end end - + context("selecting methods") do it "echos the methods of the wrapped class except default exclusions" do source.methods.each do |method| @@ -39,41 +39,41 @@ describe Draper::Base do end end end - + it "should not override a defined method with a source method" do DecoratorWithApplicationHelper.new(source).length.should == "overridden" end - + it "should always proxy to_param" do source.send :class_eval, "def to_param; 1; end" Draper::Base.new(source).to_param.should == 1 end - + it "should not copy the .class, .inspect, or other existing methods" do source.class.should_not == subject.class source.inspect.should_not == subject.inspect source.to_s.should_not == subject.to_s end - end + end it "should wrap source methods so they still accept blocks" do subject.block{"marker"}.should == "marker" end - + context ".find" do it "should lookup the associated model when passed an integer" do pd = ProductDecorator.find(1) pd.should be_instance_of(ProductDecorator) pd.model.should be_instance_of(Product) end - + it "should lookup the associated model when passed a string" do pd = ProductDecorator.find("1") pd.should be_instance_of(ProductDecorator) pd.model.should be_instance_of(Product) end end - + context ".decorate" do it "should return a collection of wrapped objects when given a collection of source objects" do sources = [Product.new, Product.new] @@ -82,106 +82,106 @@ describe Draper::Base do output.size.should == sources.size output.each{ |decorated| decorated.should be_instance_of(Draper::Base) } end - + it "should return a single wrapped object when given a single source object" do output = Draper::Base.decorate(source) output.should be_instance_of(Draper::Base) end end - - describe "a sample usage with denies" do + + describe "a sample usage with denies" do let(:subject_with_denies){ DecoratorWithDenies.new(source) } - + it "should proxy methods not listed in denies" do subject_with_denies.should respond_to(:hello_world) end - + it "should not echo methods specified with denies" do subject_with_denies.should_not respond_to(:goodnight_moon) end it "should not clobber other decorators' methods" do subject.should respond_to(:hello_world) - end - + end + it "should not allow method_missing to circumvent a deny" do expect{subject_with_denies.title}.to raise_error(NoMethodError) - end + end end - + describe "a sample usage with allows" do let(:subject_with_allows){ DecoratorWithAllows.new(source) } - + it "should echo the allowed method" do subject_with_allows.should respond_to(:upcase) end - + it "should echo _only_ the allowed method" do subject_with_allows.should_not respond_to(:downcase) end end - + describe "invalid usages of allows and denies" do let(:blank_allows){ - class DecoratorWithInvalidAllows < Draper::Base + class DecoratorWithInvalidAllows < Draper::Base allows end } - + let(:blank_denies){ - class DecoratorWithInvalidDenies < Draper::Base + class DecoratorWithInvalidDenies < Draper::Base denies end } - + let(:using_allows_then_denies){ - class DecoratorWithAllowsAndDenies < Draper::Base + class DecoratorWithAllowsAndDenies < Draper::Base allows :hello_world denies :goodnight_moon end - } - + } + let(:using_denies_then_allows){ - class DecoratorWithDeniesAndAllows < Draper::Base + class DecoratorWithDeniesAndAllows < Draper::Base denies :goodnight_moon - allows :hello_world + allows :hello_world end } it "should raise an exception for a blank allows" do expect {blank_allows}.should raise_error(ArgumentError) end - + it "should raise an exception for a blank denies" do expect {blank_denies}.should raise_error(ArgumentError) end - + it "should raise an exception for calling allows then denies" do expect {using_allows_then_denies}.should raise_error(ArgumentError) end - + it "should raise an exception for calling denies then allows" do expect {using_denies_then_allows}.should raise_error(ArgumentError) end end - + context "in a Rails application" do let(:decorator){ DecoratorWithApplicationHelper.decorate(Object.new) } - + it "should have access to ApplicationHelper helpers" do decorator.uses_hello_world == "Hello, World!" end - + it "should be able to use the content_tag helper" do decorator.sample_content.to_s.should == "Hello, World!" end - + it "should be able to use the link_to helper" do decorator.sample_link.should == "Hello" end - + it "should be able to use the pluralize helper" do decorator.sample_truncate.should == "Once..." end end -end \ No newline at end of file +end