lib/pragma/decorator/collection.rb
# frozen_string_literal: true
module Pragma
module Decorator
# This module is used to represent collections of objects.
#
# It will wrap the collection in a +data+ property so that you can include meta-data about the
# collection at the root level.
#
# @example Using Collection to include a total count
# class ArticlesDecorator < Pragma::Decorator::Base
# include Pragma::Decorator::Collection
#
# property :total_count, exec_context: :decorator
#
# def total_count
# represented.count
# end
# end
#
# # {
# # "data": [
# # { "...": "..." },
# # { "...": "..." },
# # { "...": "..." }
# # ],
# # "total_count": 150
# # }
# ArticlesDecorator.new(Article.all).to_hash
module Collection
def self.included(klass)
klass.include InstanceMethods
klass.extend ClassMethods
klass.class_eval do
collection :data, exec_context: :decorator, getter: (lambda do |options:, **|
represented_collection = if self.class.instance_decorator.is_a?(Proc)
represented.map do |item|
self.class.instance_decorator.call(item).represent(item).to_hash(options)
end
elsif self.class.instance_decorator
self.class.instance_decorator.represent(represented.to_a).to_hash(options)
else
represented
end
represented_collection
end)
end
end
module InstanceMethods # :nodoc:
# Overrides the type of the resource to be +list+, for compatibility with {Type}.
#
# @see Type
def type
'list'
end
end
module ClassMethods # :nodoc:
# Defines the decorator to use for each resource in the collection.
#
# @param decorator [Class|Proc] a decorator class, or a callable accepting a represented
# object as argument and returning a decorator class
def decorate_with(decorator)
@instance_decorator = decorator
end
# Returns the instance decorator for this collection.
#
# If no decorator was set manually with {#decorate_with}, this assumes the decorator is in
# the same namespace as the current class and is called +Instance+.
#
# @return [Class\Proc] the instance decorator to use
def instance_decorator
@instance_decorator ||= Object.const_get((
name.split('::')[0..-2] + ['Instance']
).join('::'))
end
end
end
end
end