class Draper::Decorator

Attributes

context[RW]

@return [Hash] extra data to be used in user-defined methods.

model[R]

@return the object being decorated.

object[R]

@return the object being decorated.

source[R]

@return the object being decorated.

to_source[R]

@return the object being decorated.

Public Class Methods

collection_decorator_class() click to toggle source

@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
decorate(object, options = {})
Alias for: new
decorate_collection(object, options = {}) click to toggle source

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
decorates(object_class) click to toggle source

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
decorates_association(association, options = {}) click to toggle source

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
decorates_associations(*associations) click to toggle source

@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
decorates_finders() click to toggle source

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
delegate_all() click to toggle source

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
new(object, options = {}) click to toggle source

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
Also aliased as: decorate
object_class() click to toggle source

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
Also aliased as: source_class
object_class?() click to toggle source

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
Also aliased as: source_class?
source_class()
Alias for: object_class
source_class?()
Alias for: object_class?

Private Class Methods

alias_object_to_object_class_name() click to toggle source
# 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
collection_decorator_name() click to toggle source
# 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
inferred_object_class() click to toggle source
# 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
inherited(subclass) click to toggle source
Calls superclass method
# File lib/draper/decorator.rb, line 252
def self.inherited(subclass)
  subclass.alias_object_to_object_class_name
  super
end
object_class_name() click to toggle source
# 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

==(other) click to toggle source

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
applied_decorators() click to toggle source

@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
attributes() click to toggle source

@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
decorated?() click to toggle source

Checks if this object is decorated.

@return [true]

# File lib/draper/decorator.rb, line 165
def decorated?
  true
end
decorated_with?(decorator_class) click to toggle source

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
eql?(other) click to toggle source

Delegates equality to :== as expected

@return [Boolean]

# File lib/draper/decorator.rb, line 179
def eql?(other)
  self == other
end
hash() click to toggle source

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
inspect() click to toggle source
# 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
instance_of?(klass) click to toggle source

Checks if ‘self.instance_of?(klass)` or `object.instance_of?(klass)`

@param [Class] klass

Calls superclass method
# File lib/draper/decorator.rb, line 202
def instance_of?(klass)
  super || object.instance_of?(klass)
end
is_a?(klass)
Alias for: kind_of?
kind_of?(klass) click to toggle source

Checks if ‘self.kind_of?(klass)` or `object.kind_of?(klass)`

@param [Class] klass

Calls superclass method
# File lib/draper/decorator.rb, line 194
def kind_of?(klass)
  super || object.kind_of?(klass)
end
Also aliased as: is_a?
to_model() click to toggle source

ActiveModel compatibility @private

# File lib/draper/decorator.rb, line 225
def to_model
  self
end

Private Instance Methods

decorated_associations() click to toggle source
# File lib/draper/decorator.rb, line 289
def decorated_associations
  @decorated_associations ||= {}
end
handle_multiple_decoration(options) click to toggle source
# 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