Compare commits
5 Commits
decorates_
...
master
Author | SHA1 | Date |
---|---|---|
John Bintz | 33b240196c | |
John Bintz | 83a36a2282 | |
Rob Hanlon | 17be15a2e4 | |
Rob Hanlon | 1cb319dc05 | |
Rob Hanlon | 5685bbe0a1 |
|
@ -0,0 +1,10 @@
|
|||
# DecoratesBeforeRendering Changelog
|
||||
|
||||
## 0.0.2
|
||||
|
||||
* A specific decorator class can be provided using the :with option to decorates. Thanks to
|
||||
@johnbintz!
|
||||
|
||||
## 0.0.1
|
||||
|
||||
* Initial release.
|
|
@ -2,6 +2,7 @@
|
|||
require "decorates_before_rendering/version"
|
||||
require 'active_support/core_ext/string/inflections'
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
require 'active_support/concern'
|
||||
|
||||
# Decorates the specified fields. For instance, if you have
|
||||
#
|
||||
|
@ -24,16 +25,39 @@ require 'active_support/core_ext/class/attribute'
|
|||
#
|
||||
# @thing_1 will be a ThingListDecorator (or contain them), and @thing_2 will be a Thing2Decorator.
|
||||
#
|
||||
# For Draper 1.0 and above, collection elements are no longer decorated with
|
||||
# Decorator.decorate(collection), but with Decorator.decorate_collection(collection).
|
||||
# Specify that you want to decorate a collection, and with what decorator, with this syntax:
|
||||
#
|
||||
# class StuffController < ApplicationController
|
||||
# include DecoratesBeforeRendering
|
||||
#
|
||||
# decorates_collection :things_1, :with => ThingListDecorator
|
||||
# end
|
||||
#
|
||||
module DecoratesBeforeRendering
|
||||
module ClassMethods
|
||||
def decorates(*unsigiled_ivar_names)
|
||||
options = {}
|
||||
if unsigiled_ivar_names.last.instance_of?(::Hash)
|
||||
options = unsigiled_ivar_names.pop
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
class_attribute :__decorates__, :instance_writer => false
|
||||
class_attribute :__decorates_collection__, :instance_writer => false
|
||||
|
||||
class_eval do
|
||||
def self.decorates(*args)
|
||||
options = args.extract_options!
|
||||
|
||||
self.__decorates__ ||= []
|
||||
self.__decorates__ << [ args.map { |i| "@#{i}" }, options ]
|
||||
end
|
||||
|
||||
self.__ivars_to_decorate__ ||= []
|
||||
self.__ivars_to_decorate__ << [ unsigiled_ivar_names.map { |i| "@#{i}" }, options ]
|
||||
def self.decorates_collection(*args)
|
||||
options = args.extract_options!
|
||||
|
||||
raise ArgumentError, ":with is required for now" if !options[:with]
|
||||
|
||||
self.__decorates_collection__ ||= []
|
||||
self.__decorates_collection__ << [ args.map { |i| "@#{i}" }, options ]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -45,15 +69,31 @@ module DecoratesBeforeRendering
|
|||
private
|
||||
|
||||
def __decorate_ivars__
|
||||
ivars_to_decorate = self.class.__ivars_to_decorate__
|
||||
return if (__decorates__.nil? || __decorates__.empty?) and
|
||||
(__decorates_collection__.nil? || __decorates_collection__.empty?)
|
||||
|
||||
return if ivars_to_decorate.nil? or ivars_to_decorate.empty?
|
||||
if !__decorates__.nil?
|
||||
__decorate_ivar_names__(__decorates__) do |ivar_name, ivar, options|
|
||||
decorator = options.key?(:with) ? options.fetch(:with) : __decorator_for__(ivar)
|
||||
decorated = decorator.decorate(ivar)
|
||||
instance_variable_set(ivar_name, decorated)
|
||||
end
|
||||
end
|
||||
|
||||
ivars_to_decorate.each do |ivar_names, options|
|
||||
if !__decorates_collection__.nil?
|
||||
__decorate_ivar_names__(__decorates_collection__) do |ivar_name, ivar, options|
|
||||
decorated = options.fetch(:with).decorate_collection(ivar)
|
||||
instance_variable_set(ivar_name, decorated)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def __decorate_ivar_names__(ivars)
|
||||
ivars.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)
|
||||
ivar = instance_variable_get(ivar_name)
|
||||
if ivar
|
||||
yield ivar_name, ivar, options
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -69,16 +109,13 @@ private
|
|||
|
||||
def __model_name_for__(ivar)
|
||||
if ivar.respond_to?(:model_name)
|
||||
ivar
|
||||
source = ivar
|
||||
elsif ivar.class.respond_to?(:model_name)
|
||||
ivar.class
|
||||
source = ivar.class
|
||||
else
|
||||
raise ArgumentError, "#{ivar} does not have an associated model"
|
||||
end.model_name
|
||||
end
|
||||
end
|
||||
|
||||
def self.included(base)
|
||||
base.class_attribute :__ivars_to_decorate__, :instance_accessor => false
|
||||
base.extend ClassMethods
|
||||
source.model_name
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module DecoratesBeforeRendering
|
||||
unless defined? DecoratesBeforeRendering::VERSION
|
||||
VERSION = "0.0.1"
|
||||
VERSION = "0.0.3"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,44 +4,44 @@ class MyCompletelyFakeModelDecorator; end
|
|||
class MyOtherCompletelyFakeModelDecorator; end
|
||||
|
||||
describe DecoratesBeforeRendering 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
|
||||
# will be the one that's used, as modules sit above their includers
|
||||
# in the class hierarchy.
|
||||
let(:superclass) do
|
||||
Class.new do
|
||||
def initialize(sentinel)
|
||||
@sentinel = sentinel
|
||||
end
|
||||
|
||||
def render(*args)
|
||||
@sentinel.render(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
let(:klass) do
|
||||
Class.new(superclass) do
|
||||
include DecoratesBeforeRendering
|
||||
|
||||
attr_reader :ivar, :ivars
|
||||
|
||||
def initialize(sentinel, ivar, ivars = nil)
|
||||
super(sentinel)
|
||||
|
||||
@ivar = ivar
|
||||
@ivars = ivars
|
||||
end
|
||||
end
|
||||
end
|
||||
let(:instance) { klass.new(sentinel, ivar, ivars) }
|
||||
let(:args) { double('*args') }
|
||||
|
||||
# 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
|
||||
# will be the one that's used, as modules sit above their includers
|
||||
# in the class hierarchy.
|
||||
let(:superclass) do
|
||||
Class.new do
|
||||
def initialize(sentinel)
|
||||
@sentinel = sentinel
|
||||
end
|
||||
|
||||
def render(*args)
|
||||
@sentinel.render(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
let(:klass) do
|
||||
Class.new(superclass) do
|
||||
include DecoratesBeforeRendering
|
||||
|
||||
attr_reader :ivar, :ivars
|
||||
|
||||
def initialize(sentinel, ivar, ivars = nil)
|
||||
super(sentinel)
|
||||
|
||||
@ivar = ivar
|
||||
@ivars = ivars
|
||||
end
|
||||
end
|
||||
end
|
||||
let(:instance) { klass.new(sentinel, ivar, ivars) }
|
||||
let(:args) { double('*args') }
|
||||
|
||||
context "no ivars" do
|
||||
it 'should render' do
|
||||
sentinel.should_receive(:render).with(args)
|
||||
|
@ -109,5 +109,22 @@ describe DecoratesBeforeRendering do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
# for draper >= 1.0
|
||||
describe "#decorates_collection + #render" do
|
||||
it "requires decorator class (for now)" do
|
||||
expect {
|
||||
klass.decorates_collection(:ivars)
|
||||
}.to raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "should decorate collection and render" do
|
||||
klass.decorates_collection(:ivars, :with => MyCompletelyFakeModelDecorator)
|
||||
subclass_instance = Class.new(klass).new(sentinel, ivar, ivars)
|
||||
sentinel.should_receive(:render).with(args)
|
||||
MyCompletelyFakeModelDecorator.should_receive(:decorate_collection).with(ivars)
|
||||
subclass_instance.render(args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue