Grape::Entity

repository·master·Indexed 20 days ago

https://github.com/ruby-grape/grape-entity

A presentation layer for Ruby APIs that allows developers to define structured, reusable, and conditional data representations (entities) for their models. It provides a DSL to control field exposure, handle nested structures, apply custom formatting via `format_with`, and manage conditional visibility using `:if` and `:unless` options. Commonly used with the Grape framework to transform data models into consistent API responses.

Tokens
5.6K
Snippets
20
Records
28
Agent score
71%

What's inside grape-entity

  1. Introduction to Grape::Entity

    master
    Grape::Entity is an API-focused facade designed to sit on top of an object model. It provides a way to define how your data models should be represented when sent through an API, allowing for controlled exposure of fields, conditional logic, and nested structures. It is commonly used with the Grape framework.
  2. Access the options hash and attribute path

    master

    During runtime exposure or conditional checking, you can access metadata via the options hash:

    • :version: The API version (api.version).
    • :collection: Boolean indicating if the object is an array.
    • :env: The runtime environment (includes options[:env]['grape.request.params']).
    • :attr_path: An array representing the path to the current attribute (e.g., [:user, :address, :city]). This is useful for tracking which attribute is being processed in nested structures.
  3. Install grape-entity

    master

    To add grape-entity to your Ruby application, add the gem to your Gemfile and run bundle.

    Alternatively, you can install the gem directly via the command line.

    # In your Gemfile
    gem 'grape-entity'
    $ bundle
    # OR
    $ gem install grape-entity
  4. Upgrade to Grape Entity >= 0.8.2

    master

    When upgrading to version 0.8.2 or higher, note the following:

    1. Ruby Support: Official support for Ruby < 2.5 has been removed. Ruby 2.5 is only in testing mode and is not officially supported.

    2. Ruby 3.0 Compatibility: Due to changes in block handling in Ruby 3.0, the following pattern is deprecated: expose :that_method_without_args, &:method_without_args

    Recommended Pattern: For simple value setting, use the as: option instead: expose :method_without_args, as: :that_method_without_args

    # Deprecated pattern (in Ruby 3.0+)
    expose :that_method_without_args, &:method_without_args
    
    # Preferred pattern
    expose :method_without_args, as: :that_method_without_args
  5. Upgrade to Grape Entity >= 1.1.0

    master

    When upgrading to version 1.1.0 or higher, note the following changes:

    1. Dependency Change: The multi_json gem is no longer a runtime dependency. If your application relies on a configured MultiJson adapter, you must explicitly add multi_json to your Gemfile.

    2. Error Handling for Missing Methods: The behavior of expose :x, &:missing_method has changed. It no longer raises an ArgumentError during the definition phase. Instead, unknown methods will now raise a native NoMethodError at call time.

    Action Required: If you have rescue clauses around entity rendering intended to catch typos in method names (e.g., rescue ArgumentError), update them to rescue NoMethodError.

    # Old pattern (may no longer catch typos)
    begin
      present user, with: UserEntity
    rescue ArgumentError
      # ...
    end
    
    # New pattern for version >= 1.1.0
    begin
      present user, with: UserEntity
    rescue NoMethodError
      # ...
    end
  6. Define data representations with Grape::Entity

    master

    An Entity is a lightweight structure used to represent application data in a consistent, abstracted way for your API. You define an entity by subclassing Grape::Entity and using the expose method to declare which attributes should be included in the API response.

    Entities can provide documentation for fields, transform values using blocks or procs, and conditionally expose attributes based on runtime options or object state.

    module API
      module Entities
        class User < Grape::Entity
          expose :first_name, :last_name, :screen_name, :location
          expose :field, documentation: { type: "string", desc: "describe the field" }
          expose :latest_status, using: API::Status, as: :status, unless: { collection: true }
          expose :email, if: { type: :full }
          expose :new_attribute, if: { version: 'v2' }
          expose(:name) { |model, options| [model.first_name, model.last_name].join(' ') }
        end
      end
    end
  7. Use an Entity in a Grape API

    master

    Once an entity is defined, use the present method within your Grape API to transform your data models into the entity's representation. You can pass runtime options (like type: :full) to trigger conditional exposures defined in the entity.

    module API
      class Users < Grape::API
        version 'v2'
    
        desc 'User index', { params: API::Entities::User.documentation }
        get '/users' do
          @users = User.all
          type = current_user.admin? ? :full : :default
          present @users, with: API::Entities::User, type: type
        end
      end
    end
  8. Define API responses using Grape::Entity

    master

    To define an entity, create a class that inherits from Grape::Entity. You use the expose method to specify which attributes of the underlying object should be included in the API response. You can also apply formatting, conditional logic, and nesting within the definition.

    module API
      module Entities
        class Status < Grape::Entity
          format_with(:iso_timestamp) { |dt| dt.iso8601 }
    
          expose :user_name
          expose :text, documentation: { type: "String", desc: "Status update text." }
          expose :ip, if: { type: :full }
          expose :user_type, :user_id, if: lambda { |status, options| status.user.public? }
          expose :location, merge: true
          expose :contact_info do
            expose :phone
            expose :address, merge: true, using: API::Entities::Address
          end
          expose :digest do |status, options|
            Digest::MD5.hexdigest status.txt
          end
          expose :replies, using: API::Entities::Status, as: :responses
          expose :last_reply, using: API::Entities::Status do |status, options|
            status.replies.last
          end
    
          with_options(format_with: :iso_timestamp) do
            expose :created_at
            expose :updated_at
          end
        end
      end
    end
    
    module API
      module Entities
        class StatusDetailed < API::Entities::Status
          expose :internal_id
        end
      end
    end