- Demo
- Installation
- Usage
- Contributing
- Copyright and License
See grape-with-roar.
Add the grape, roar and grape-roar gems to Gemfile.
gem 'grape'
gem 'roar'
gem 'grape-roar'If you're upgrading from an older version of this gem, please see UPGRADING.
class API < Grape::API
  format :json
  formatter :json, Grape::Formatter::Roar
endInclude Grape::Roar::Representer into a representer module after any Roar mixins, then use Grape's present keyword.
module ProductRepresenter
  include Roar::JSON
  include Roar::Hypermedia
  include Grape::Roar::Representer
  property :title
  property :id
endget 'product/:id' do
  present Product.find(params[:id]), with: ProductRepresenter
endPresenting collections works the same way. The following example returns an embedded set of products in the HAL Hypermedia format.
module ProductsRepresenter
  include Roar::JSON::HAL
  include Roar::Hypermedia
  include Grape::Roar::Representer
  collection :entries, extend: ProductRepresenter, as: :products, embedded: true
endget 'products' do
  present Product.all, with: ProductsRepresenter
endThe formatter invokes to_json on presented objects and provides access to the requesting environment via the env option. The following example renders a full request URL in a representer.
module ProductRepresenter
  include Roar::JSON
  include Roar::Hypermedia
  include Grape::Roar::Representer
  link :self do |opts|
    request = Grape::Request.new(opts[:env])
    "#{request.url}"
  end
endIf you prefer to use a decorator class instead of modules.
class ProductRepresenter < Grape::Roar::Decorator
  include Roar::JSON
  include Roar::Hypermedia
  link :self do |opts|
    "#{request(opts).url}/#{represented.id}"
  end
  private
  def request(opts)
    Grape::Request.new(opts[:env])
  end
endget 'products' do
  present Product.all, with: ProductsRepresenter
endIf you use either ActiveRecord or Mongoid, you can use the Grape::Roar::Extensions::Relations DSL to expose the relationships in between your models as a HAL response. The DSL methods used are the same regardless of what your ORM/ODM is, as long as there exists an adapter for it.
Arguments passed to #relation are forwarded to roar. Single member relations (e.g. belongs_to) are represented using #property, collections are represented using #collection; arguments provided to #relation will be passed through these methods (i.e. additional arguments roar and representable accept).
A default base URI is constructed from a Grape::Request by concatenating the #base_url and #script_name properties. The resource path is extracted from the name of the relation.
Otherwise, the extensions attempt to look up the correct representer module/class for the objects (e.g. we infer the extend argument). You can always specify the correct representer to use on your own.
class Item < ActiveRecord::Base
  belongs_to :cart
end
class Cart < ActiveRecord::Base
  has_many :items  
endclass ItemEntity < Grape::Roar::Decorator
  include Roar::JSON
  include Roar::JSON::HAL
  include Roar::Hypermedia
  include Grape::Roar::Extensions::Relations
  # Cart will be presented under the _embedded key
  relation :belongs_to, :cart, embedded: true
  link_self
end
class CartEntity < Grape::Roar::Decorator
  include Roar::JSON
  include Roar::JSON::HAL
  include Roar::Hypermedia
  include Grape::Roar::Extensions::Relations
  # Items will be presented under the _links key
  relation :has_many, :items, embedded: false
  link_self
endAlthough this example uses Grape::Roar::Decorator, you can also use a module as show in prior examples above. If doing so, you no longer have to mix in Grape::Roar::Representer.
{
    "_embedded": {
        "cart": {
            "_links": {
                "self": {
                    "href": "http://example.org/carts/1"
                },
                "items": [{
                    "href": "http://example.org/items/1"
                }]
            }
        }
    },
    "_links": {
        "self": {
            "href": "http://example.org/items/1"
        }
    }
}{
    "_links": {
        "self": {
            "href": "http://example.org/carts/1"
        },
        "items": [{
            "href": "http://example.org/items/1"
        }, {
            "href": "http://example.org/items/2"
        }, {
            "href": "http://example.org/items/3"
        }, {
            "href": "http://example.org/items/4"
        }, {
            "href": "http://example.org/items/5"
        }]
    }
}Should you incorrectly describe a relationship (e.g. you specify has_one but your model specifies has_many), an exception will be raised to notify you of the mismatch:
Grape::Roar::Extensions::Relations::Exceptions::InvalidRelationError:
  Expected Mongoid::Association::Referenced::HasOne, got Mongoid::Association::Referenced::HasMany!The opts hash below is the same one as shown in prior examples.
class BarEntity < Grape::Roar::Decorator
  include Roar::JSON
  include Roar::JSON::HAL
  include Roar::Hypermedia
  include Grape::Roar::Extensions::Relations
  # This is our default implementation
  map_base_url do |opts|
    request = Grape::Request.new(opts[:env])
    "#{request.base_url}#{request.script_name}"
  end
  relation :has_many, :bars, embedded: false
endclass BarEntity < Grape::Roar::Decorator
  include Roar::JSON
  include Roar::JSON::HAL
  include Roar::Hypermedia
  include Grape::Roar::Extensions::Relations
  # This is our default implementation
  map_resource_path do |_opts, object, relation_name|
    "#{relation_name}/#{object.id}"
  end
  relation :has_many, :bars, embedded: false
endIf you have custom domain objects, you can create an adapter to make your models compatible with the DSL methods. Below is an example of the ActiveRecord adapter.
module Extensions
  module RelationalModels
    module Adapter
      class ActiveRecord < Base
        include Validations::ActiveRecord
        # We map your domain object to the correct adapter
        # at runtime.
        valid_for { |klass| klass < ::ActiveRecord::Base }
        def collection_methods
          @collection_methods ||= %i(has_many has_and_belongs_to_many)
        end
        def name_for_represented(represented)
          klass_name = case represented
                       when ::ActiveRecord::Relation
                         represented.klass.name
                       else
                         represented.class.name
                       end
          klass_name.demodulize.pluralize.downcase
        end
        def single_entity_methods
          @single_entity_methods ||= %i(has_one belongs_to)
        end
      end
    end
  end
endErrors are handled by creating methods corresponding to those in collection_methods and single_entity_methods. For example, this is the validator for belongs_to:
module ActiveRecord
  include Validations::Misc
  def belongs_to_valid?(relation)
    relation = klass.reflections[relation]
    return true if relation.is_a?(
      ::ActiveRecord::Reflection::BelongsToReflection
    )
    # Provided by Validations::Misc
    invalid_relation(
      ::ActiveRecord::Reflection::BelongsToReflection,
      relation.class
    )
  end
endAfter writing your validation methods, just mix them into your adapter. You can choose to not write validation methods; they are only invoked if your adapter responds to them.
See CONTRIBUTING.
MIT License, see LICENSE for details.
(c) 2012-2025 Daniel Doubrovkine & Contributors, Artsy