class Draper::Decorator
Attributes
@return [Hash] extra data to be used in user-defined methods.
@return the object being decorated.
@return the object being decorated.
@return the object being decorated.
@return the object being decorated.
Public Class Methods
@return [Class] the class created by {decorate_collection}.
# File lib/draper/decorator.rb, line 242 def self.collection_decorator_class name = collection_decorator_name name.constantize rescue NameError => error raise if name && !error.missing_name?(name) Draper::CollectionDecorator end
Decorates a collection of objects. The class of the collection decorator is inferred from the decorator class if possible (e.g. ‘ProductDecorator` maps to `ProductsDecorator`), but otherwise defaults to {Draper::CollectionDecorator}.
@param [Object] object
collection to decorate.
@option options [Class, nil] :with (self)
the decorator class used to decorate each item. When `nil`, it is inferred from each item.
@option options [Hash] :context
extra data to be stored in the collection decorator.
# File lib/draper/decorator.rb, line 143 def self.decorate_collection(object, options = {}) options.assert_valid_keys(:with, :context) collection_decorator_class.new(object, options.reverse_merge(with: self)) end
Sets the source class corresponding to the decorator class.
@note This is only necessary if you wish to proxy class methods to the
source (including when using {decorates_finders}), and the source class cannot be inferred from the decorator class (e.g. `ProductDecorator` maps to `Product`).
@param [String, Symbol, Class] object_class
source class (or class name) that corresponds to this decorator.
@return [void]
# File lib/draper/decorator.rb, line 58 def self.decorates(object_class) @object_class = object_class.to_s.camelize.constantize alias_object_to_object_class_name end
Automatically decorate an association.
@param [Symbol] association
name of the association to decorate (e.g. `:products`).
@option options [Class] :with
the decorator to apply to the association.
@option options [Symbol] :scope
a scope to apply when fetching the association.
@option options [Hash, call] :context
extra data to be stored in the associated decorator. If omitted, the associated decorator's context will be the same as the parent decorator's. If a Proc is given, it will be called with the parent's context and should return a new context hash for the association.
@return [void]
# File lib/draper/decorator.rb, line 109 def self.decorates_association(association, options = {}) options.assert_valid_keys(:with, :scope, :context) define_method(association) do decorated_associations[association] ||= Draper::DecoratedAssociation.new(self, association, options) decorated_associations[association].call end end
@overload decorates_associations
(*associations, options = {})
Automatically decorate multiple associations. @param [Symbols*] associations names of the associations to decorate. @param [Hash] options see {decorates_association}. @return [void]
# File lib/draper/decorator.rb, line 124 def self.decorates_associations(*associations) options = associations.extract_options! associations.each do |association| decorates_association(association, options) end end
Automatically decorates ActiveRecord finder methods, so that you can use ‘ProductDecorator.find(id)` instead of `ProductDecorator.decorate(Product.find(id))`.
Finder methods are applied to the {object_class}.
@return [void]
# File lib/draper/decorator.rb, line 91 def self.decorates_finders extend Draper::Finders end
Automatically delegates instance methods to the source object. Class methods will be delegated to the {object_class}, if it is set.
@return [void]
# File lib/draper/decorator.rb, line 45 def self.delegate_all include Draper::AutomaticDelegation end
Wraps an object in a new instance of the decorator.
Decorators may be applied to other decorators. However, applying a decorator to an instance of itself will create a decorator with the same source as the original, rather than redecorating the other instance.
@param [Object] object
object to decorate.
@option options [Hash] :context ({})
extra data to be stored in the decorator and used in user-defined methods.
# File lib/draper/decorator.rb, line 30 def initialize(object, options = {}) options.assert_valid_keys(:context) @object = object @context = options.fetch(:context, {}) handle_multiple_decoration(options) if object.instance_of?(self.class) end
Returns the source class corresponding to the decorator class, as set by {decorates}, or as inferred from the decorator class name (e.g. ‘ProductDecorator` maps to `Product`).
@return [Class] the source class that corresponds to this decorator.
# File lib/draper/decorator.rb, line 68 def self.object_class @object_class ||= inferred_object_class end
Checks whether this decorator class has a corresponding {object_class}.
# File lib/draper/decorator.rb, line 73 def self.object_class? object_class rescue Draper::UninferrableSourceError false end
Private Class Methods
# File lib/draper/decorator.rb, line 257 def self.alias_object_to_object_class_name alias_method object_class.name.underscore, :object if object_class? end
# File lib/draper/decorator.rb, line 274 def self.collection_decorator_name plural = object_class_name.pluralize raise NameError if plural == object_class_name "#{plural}Decorator" end
# File lib/draper/decorator.rb, line 266 def self.inferred_object_class name = object_class_name name.constantize rescue NameError => error raise if name && !error.missing_name?(name) raise Draper::UninferrableSourceError.new(self) end
# File lib/draper/decorator.rb, line 252 def self.inherited(subclass) subclass.alias_object_to_object_class_name super end
# File lib/draper/decorator.rb, line 261 def self.object_class_name raise NameError if name.nil? || name.demodulize !~ /.+Decorator$/ name.chomp("Decorator") end
Public Instance Methods
Compares the source object with a possibly-decorated object.
@return [Boolean]
# File lib/draper/decorator.rb, line 172 def ==(other) Draper::Decoratable::Equality.test(object, other) end
@return [Array<Class>] the list of decorators that have been applied to
the object.
# File lib/draper/decorator.rb, line 150 def applied_decorators chain = object.respond_to?(:applied_decorators) ? object.applied_decorators : [] chain << self.class end
@return [Hash] the object’s attributes, sliced to only include those implemented by the decorator.
# File lib/draper/decorator.rb, line 231 def attributes object.attributes.select {|attribute, _| respond_to?(attribute) } end
Checks if this object is decorated.
@return [true]
# File lib/draper/decorator.rb, line 165 def decorated? true end
Checks if a given decorator has been applied to the object.
@param [Class] decorator_class
# File lib/draper/decorator.rb, line 158 def decorated_with?(decorator_class) applied_decorators.include?(decorator_class) end
Delegates equality to :== as expected
@return [Boolean]
# File lib/draper/decorator.rb, line 179 def eql?(other) self == other end
Returns a unique hash for a decorated object based on the decorator class and the object being decorated.
@return [Fixnum]
# File lib/draper/decorator.rb, line 187 def hash self.class.hash ^ object.hash end
# File lib/draper/decorator.rb, line 210 def inspect ivars = instance_variables.map do |name| "#{name}=#{instance_variable_get(name).inspect}" end _to_s.insert(-2, " #{ivars.join(", ")}") end
Checks if ‘self.instance_of?(klass)` or `object.instance_of?(klass)`
@param [Class] klass
# File lib/draper/decorator.rb, line 202 def instance_of?(klass) super || object.instance_of?(klass) end
Checks if ‘self.kind_of?(klass)` or `object.kind_of?(klass)`
@param [Class] klass
# File lib/draper/decorator.rb, line 194 def kind_of?(klass) super || object.kind_of?(klass) end
ActiveModel
compatibility @private
# File lib/draper/decorator.rb, line 225 def to_model self end
Private Instance Methods
# File lib/draper/decorator.rb, line 289 def decorated_associations @decorated_associations ||= {} end
# File lib/draper/decorator.rb, line 280 def handle_multiple_decoration(options) if object.applied_decorators.last == self.class @context = object.context unless options.has_key?(:context) @object = object.object else warn "Reapplying #{self.class} decorator to target that is already decorated with it. Call stack:\n#{caller(1).join("\n")}" end end