Merge pull request #2 from johnbintz/decorates_with

Select the decorator to use for each @ivar
This commit is contained in:
Rob Hanlon 2012-10-17 18:14:09 -07:00
commit 5685bbe0a1
2 changed files with 47 additions and 9 deletions

View File

@ -13,10 +13,27 @@ require 'active_support/core_ext/class/attribute'
#
# @thing_1 and @thing_2 will be decorated right before a rendering occurs.
#
# You can also specify the decorator you wish to use for a particular instance variable:
#
# class StuffController < ApplicationController
# include DecoratesBeforeRendering
#
# decorates :thing_1, :with => ThingListDecorator
# decorates :thing_2
# end
#
# @thing_1 will be a ThingListDecorator (or contain them), and @thing_2 will be a Thing2Decorator.
#
module DecoratesBeforeRendering
module ClassMethods
def decorates(*unsigiled_ivar_names)
self.__ivars_to_decorate__ = unsigiled_ivar_names.map { |i| "@#{i}" }
options = {}
if unsigiled_ivar_names.last.instance_of?(::Hash)
options = unsigiled_ivar_names.pop
end
self.__ivars_to_decorate__ ||= []
self.__ivars_to_decorate__ << [ unsigiled_ivar_names.map { |i| "@#{i}" }, options ]
end
end
@ -30,16 +47,20 @@ private
def __decorate_ivars__
ivars_to_decorate = self.class.__ivars_to_decorate__
return if ivars_to_decorate.nil?
return if ivars_to_decorate.nil? or ivars_to_decorate.empty?
ivars_to_decorate.each do |ivar_name|
ivar = instance_variable_get(ivar_name)
instance_variable_set(ivar_name, __decorator_for__(ivar)) unless ivar.nil?
ivars_to_decorate.each do |ivar_names, options|
ivar_names.each do |ivar_name|
if ivar = instance_variable_get(ivar_name)
decorator = (options[:with] || __decorator_for__(ivar)).decorate(ivar)
instance_variable_set(ivar_name, decorator)
end
end
end
end
def __decorator_for__(ivar)
__decorator_name_for__(ivar).constantize.decorate(ivar)
__decorator_name_for__(ivar).constantize
end
def __decorator_name_for__(ivar)

View File

@ -1,12 +1,14 @@
require_relative '../lib/decorates_before_rendering'
class MyCompletelyFakeModelDecorator; end
class MyOtherCompletelyFakeModelDecorator; end
describe DecoratesBeforeRendering do
# NOTE: these are married together, so they're tested together.
describe '::decorates + #render' do
let(:sentinel) { double(:sentinel) }
let(:ivar) { double('@ivar') }
let(:ivars) { double('@ivars') }
# NOTE: This superclass is here so we know that the correct render gets
# called. It can't be defined in the subclass, or else that one
@ -27,16 +29,17 @@ describe DecoratesBeforeRendering do
Class.new(superclass) do
include DecoratesBeforeRendering
attr_reader :ivar
attr_reader :ivar, :ivars
def initialize(sentinel, ivar)
def initialize(sentinel, ivar, ivars = nil)
super(sentinel)
@ivar = ivar
@ivars = ivars
end
end
end
let(:instance) { klass.new(sentinel, ivar) }
let(:instance) { klass.new(sentinel, ivar, ivars) }
let(:args) { double('*args') }
context "no ivars" do
@ -92,5 +95,19 @@ describe DecoratesBeforeRendering do
subclass_instance.render(args)
end
end
context "Specify a different decorator class for an automatic decorator" do
it "should function correctly" do
klass.decorates(:ivars, :with => MyOtherCompletelyFakeModelDecorator)
klass.decorates(:ivar)
subclass_instance = Class.new(klass).new(sentinel, ivar, ivars)
sentinel.should_receive(:render).with(args)
MyOtherCompletelyFakeModelDecorator.should_receive(:decorate).with(ivars)
MyCompletelyFakeModelDecorator.should_receive(:decorate).with(ivar)
ivar.stub_chain(:class, :model_name => 'MyCompletelyFakeModel')
subclass_instance.render(args)
end
end
end
end