pragmarb/pragma-decorator

View on GitHub
lib/pragma/decorator/collection.rb

Summary

Maintainability
A
0 mins
Test Coverage
# 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