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.
Public Class Methods
@return [Class] the class created by {decorate_collection}.
# File lib/draper/decorator.rb, line 228 def self.collection_decorator_class name = collection_decorator_name name_constant = name&.safe_constantize name_constant || 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 140 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 60 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 106 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 121 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 88 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 47 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 32 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 70 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 75 def self.object_class? object_class rescue Draper::UninferrableObjectError false end
Private Class Methods
# File lib/draper/decorator.rb, line 242 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 259 def self.collection_decorator_name singular = object_class_name plural = singular&.pluralize "#{plural}Decorator" unless plural == singular end
# File lib/draper/decorator.rb, line 251 def self.inferred_object_class name = object_class_name name_constant = name&.safe_constantize return name_constant unless name_constant.nil? raise Draper::UninferrableObjectError.new(self) end
# File lib/draper/decorator.rb, line 237 def self.inherited(subclass) subclass.alias_object_to_object_class_name super end
# File lib/draper/decorator.rb, line 246 def self.object_class_name return nil 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 169 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 147 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 217 def attributes object.attributes.select {|attribute, _| respond_to?(attribute) } end
Checks if this object is decorated.
@return [true]
# File lib/draper/decorator.rb, line 162 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 155 def decorated_with?(decorator_class) applied_decorators.include?(decorator_class) end
Delegates equality to :== as expected
@return [Boolean]
# File lib/draper/decorator.rb, line 176 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 184 def hash self.class.hash ^ object.hash end
Checks if `self.instance_of?(klass)` or `object.instance_of?(klass)`
@param [Class] klass
# File lib/draper/decorator.rb, line 200 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 191 def kind_of?(klass) super || object.kind_of?(klass) end
ActiveModel
compatibility @private
# File lib/draper/decorator.rb, line 211 def to_model self end
Private Instance Methods
# File lib/draper/decorator.rb, line 275 def decorated_associations @decorated_associations ||= {} end
# File lib/draper/decorator.rb, line 266 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