Skip to content

Commit

Permalink
Documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
haines committed Jan 7, 2013
1 parent 0b8d88f commit 0888133
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 128 deletions.
2 changes: 1 addition & 1 deletion .yardopts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
yardoc 'lib/draper/**/*.rb' -m markdown
yardoc 'lib/draper/**/*.rb' -m markdown --no-private
28 changes: 23 additions & 5 deletions lib/draper/collection_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,23 @@ class CollectionDecorator
include Enumerable
include ViewHelpers

# @return [Hash] extra data to be used in user-defined methods, and passed
# to each item's decorator.
attr_accessor :context

array_methods = Array.instance_methods - Object.instance_methods
delegate :==, :as_json, *array_methods, to: :decorated_collection

# @param source collection to decorate
# @option options [Class] :with the class used to decorate items
# @option options [Hash] :context context available to each item's decorator
# @param [Enumerable] source
# collection to decorate.
# @option options [Class, nil] :with (nil)
# the decorator class used to decorate each item. When `nil`, it is
# inferred from the collection decorator class if possible (e.g.
# `ProductsDecorator` maps to `ProductDecorator`), otherwise each item's
# {Decoratable#decorate decorate} method will be used.
# @option options [Hash] :context ({})
# extra data to be stored in the collection decorator and used in
# user-defined methods, and passed to each item's decorator.
def initialize(source, options = {})
options.assert_valid_keys(:with, :context)
@source = source
Expand All @@ -22,10 +31,14 @@ class << self
alias_method :decorate, :new
end

# @return [Array] the decorated items.
def decorated_collection
@decorated_collection ||= source.map{|item| decorate_item(item)}
end

# Delegated to the decorated collection when using the block form
# (`Enumerable#find`) or to the decorator class if not
# (`ActiveRecord::FinderMethods#find`)
def find(*args, &block)
if block_given?
decorated_collection.find(*args, &block)
Expand All @@ -49,18 +62,25 @@ def context=(value)
each {|item| item.context = value } if @decorated_collection
end

# @return [Class] the decorator class used to decorate each item, as set by
# {#initialize} or as inferred from the collection decorator class (e.g.
# `ProductsDecorator` maps to `ProductDecorator`).
def decorator_class
@decorator_class ||= self.class.inferred_decorator_class
end

protected

# @return the collection being decorated.
attr_reader :source

# Decorates the given item.
def decorate_item(item)
item_decorator.call(item, context: context)
end

private

def self.inferred_decorator_class
decorator_name = "#{name.chomp("Decorator").singularize}Decorator"
decorator_uninferrable if decorator_name == name
Expand All @@ -75,8 +95,6 @@ def self.decorator_uninferrable
raise Draper::UninferrableDecoratorError.new(self)
end

private

def item_decorator
@item_decorator ||= begin
decorator_class.method(:decorate)
Expand Down
103 changes: 71 additions & 32 deletions lib/draper/decoratable.rb
Original file line number Diff line number Diff line change
@@ -1,44 +1,83 @@
module Draper::Decoratable
extend ActiveSupport::Concern
module Draper
# Provides shortcuts to decorate objects directly, so you can do
# `@product.decorate` instead of `ProductDecorator.new(@product)`.
#
# This module is included by default into `ActiveRecord::Base` and
# `Mongoid::Document`, but you're using another ORM, or want to decorate
# plain old Ruby objects, you can include it manually.
module Decoratable
extend ActiveSupport::Concern

def decorate(options = {})
decorator_class.decorate(self, options)
end

def decorator_class
self.class.decorator_class
end

def applied_decorators
[]
end
# Decorates the object using the inferred {#decorator_class}.
# @param [Hash] options
# see {Decorator#initialize}
def decorate(options = {})
decorator_class.decorate(self, options)
end

def decorated_with?(decorator_class)
false
end
# (see ClassMethods#decorator_class)
def decorator_class
self.class.decorator_class
end

def decorated?
false
end
# The list of decorators that have been applied to the object.
#
# @return [Array<Class>] `[]`
def applied_decorators
[]
end

def ==(other)
super || (other.respond_to?(:source) && self == other.source)
end
# (see Decorator#decorated_with?)
# @return [false]
def decorated_with?(decorator_class)
false
end

module ClassMethods
def decorate(options = {})
decorator_class.decorate_collection(self.scoped, options)
# Checks if this object is decorated.
#
# @return [false]
def decorated?
false
end

def decorator_class
prefix = respond_to?(:model_name) ? model_name : name
"#{prefix}Decorator".constantize
rescue NameError
raise Draper::UninferrableDecoratorError.new(self)
# Compares with possibly-decorated objects.
#
# @return [Boolean]
def ==(other)
super || (other.respond_to?(:source) && self == other.source)
end

def ===(other)
super || (other.respond_to?(:source) && super(other.source))
module ClassMethods

# Decorates a collection of objects. Used at the end of a scope chain.
#
# @example
# Product.popular.decorate
# @param [Hash] options
# see {Decorator.decorate_collection}.
def decorate(options = {})
decorator_class.decorate_collection(self.scoped, options)
end

# Infers the decorator class to be used by {Decoratable#decorate} (e.g.
# `Product` maps to `ProductDecorator`).
#
# @return [Class] the inferred decorator class.
def decorator_class
prefix = respond_to?(:model_name) ? model_name : name
"#{prefix}Decorator".constantize
rescue NameError
raise Draper::UninferrableDecoratorError.new(self)
end

# Compares with possibly-decorated objects.
#
# @return [Boolean]
def ===(other)
super || (other.respond_to?(:source) && super(other.source))
end

end

end
end
1 change: 1 addition & 0 deletions lib/draper/decorated_association.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module Draper
# @private
class DecoratedAssociation

def initialize(owner, association, options)
Expand Down
Loading

0 comments on commit 0888133

Please sign in to comment.