{"id":19552783,"url":"https://github.com/ruby-grape/grape-roar","last_synced_at":"2025-08-06T08:06:03.181Z","repository":{"id":6084445,"uuid":"7311087","full_name":"ruby-grape/grape-roar","owner":"ruby-grape","description":"Use Roar with Grape.","archived":false,"fork":false,"pushed_at":"2017-10-31T20:35:56.000Z","size":63,"stargazers_count":58,"open_issues_count":12,"forks_count":14,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-07-27T20:43:48.210Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ruby-grape.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-12-24T20:07:20.000Z","updated_at":"2025-01-23T23:54:42.000Z","dependencies_parsed_at":"2022-09-22T14:25:35.820Z","dependency_job_id":null,"html_url":"https://github.com/ruby-grape/grape-roar","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/ruby-grape/grape-roar","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruby-grape%2Fgrape-roar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruby-grape%2Fgrape-roar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruby-grape%2Fgrape-roar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruby-grape%2Fgrape-roar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ruby-grape","download_url":"https://codeload.github.com/ruby-grape/grape-roar/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruby-grape%2Fgrape-roar/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269042263,"owners_count":24349655,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-06T02:00:09.910Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-11T04:19:41.254Z","updated_at":"2025-08-06T08:06:03.159Z","avatar_url":"https://github.com/ruby-grape.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"Grape::Roar\n------------\n\n[![Gem Version](http://img.shields.io/gem/v/grape-roar.svg)](http://badge.fury.io/rb/grape-roar)\n[![Build Status](http://img.shields.io/travis/ruby-grape/grape-roar.svg)](https://travis-ci.org/ruby-grape/grape-roar)\n[![Dependency Status](https://gemnasium.com/ruby-grape/grape-roar.svg)](https://gemnasium.com/ruby-grape/grape-roar)\n[![Code Climate](https://codeclimate.com/github/ruby-grape/grape-roar.svg)](https://codeclimate.com/github/ruby-grape/grape-roar)\n\nUse [Roar](https://github.com/apotonick/roar) with [Grape](https://github.com/intridea/grape).\n\nDemo\n----\n\nThe [grape-with-roar](https://github.com/ruby-grape/grape-with-roar) project deployed [here on heroku](http://grape-with-roar.herokuapp.com).\n\nInstallation\n------------\n\nAdd the `grape`, `roar` and `grape-roar` gems to Gemfile.\n\n```ruby\ngem 'grape'\ngem 'roar'\ngem 'grape-roar'\n```\n\nIf you're upgrading from an older version of this gem, please see [UPGRADING](UPGRADING.md).\n\nUsage\n-----\n\n### Tell your API to use Grape::Formatter::Roar\n\n```ruby\nclass API \u003c Grape::API\n  format :json\n  formatter :json, Grape::Formatter::Roar\nend\n```\n\n### Use Grape's Present\n\nInclude Grape::Roar::Representer into a representer module *after* any Roar mixins, then use Grape's `present` keyword.\n\n```ruby\nmodule ProductRepresenter\n  include Roar::JSON\n  include Roar::Hypermedia\n  include Grape::Roar::Representer\n\n  property :title\n  property :id\nend\n```\n\n```ruby\nget 'product/:id' do\n  present Product.find(params[:id]), with: ProductRepresenter\nend\n```\n\nPresenting collections works the same way. The following example returns an embedded set of products in the HAL Hypermedia format.\n\n```ruby\nmodule ProductsRepresenter\n  include Roar::JSON::HAL\n  include Roar::Hypermedia\n  include Grape::Roar::Representer\n\n  collection :entries, extend: ProductRepresenter, as: :products, embedded: true\nend\n```\n\n```ruby\nget 'products' do\n  present Product.all, with: ProductsRepresenter\nend\n```\n\n### Accessing the Request Inside a Representer\n\nThe 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.\n\n```ruby\nmodule ProductRepresenter\n  include Roar::JSON\n  include Roar::Hypermedia\n  include Grape::Roar::Representer\n\n  link :self do |opts|\n    request = Grape::Request.new(opts[:env])\n    \"#{request.url}\"\n  end\nend\n```\n\n### Decorators\n\nIf you prefer to use a decorator class instead of modules.\n\n```ruby\nclass ProductRepresenter \u003c Grape::Roar::Decorator\n  include Roar::JSON\n  include Roar::Hypermedia\n\n  link :self do |opts|\n    \"#{request(opts).url}/#{represented.id}\"\n  end\n\n  private\n\n  def request(opts)\n    Grape::Request.new(opts[:env])\n  end\nend\n```\n\n```ruby\nget 'products' do\n  present Product.all, with: ProductsRepresenter\nend\n```\n\n### Relation Extensions\n\nIf 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](#designing-adapters). \n\n#### Designing Representers\n\nArguments 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](https://github.com/trailblazer/roar) and [representable](http://trailblazer.to/gems/representable/3.0/api.html) accept).\n\nA 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.\n\nOtherwise, 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. \n\n##### Example Models\n\n```ruby\nclass Item \u003c ActiveRecord::Base\n  belongs_to :cart\nend\n\nclass Cart \u003c ActiveRecord::Base\n  has_many :items  \nend\n```\n\n##### Example Representers\n\n```ruby\nclass ItemEntity \u003c Grape::Roar::Decorator\n  include Roar::JSON\n  include Roar::JSON::HAL\n  include Roar::Hypermedia\n\n  include Grape::Roar::Extensions::Relations\n\n  # Cart will be presented under the _embedded key\n  relation :belongs_to, :cart, embedded: true\n\n  link_self\nend\n\nclass CartEntity \u003c Grape::Roar::Decorator\n  include Roar::JSON\n  include Roar::JSON::HAL\n  include Roar::Hypermedia\n\n  include Grape::Roar::Extensions::Relations\n\n  # Items will be presented under the _links key\n  relation :has_many, :items, embedded: false\n\n  link_self\nend\n```\n\nAlthough 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`. \n\n##### Example Item\n```javascript\n{\n    \"_embedded\": {\n        \"cart\": {\n            \"_links\": {\n                \"self\": {\n                    \"href\": \"http://example.org/carts/1\"\n                },\n                \"items\": [{\n                    \"href\": \"http://example.org/items/1\"\n                }]\n            }\n        }\n    },\n    \"_links\": {\n        \"self\": {\n            \"href\": \"http://example.org/items/1\"\n        }\n    }\n}\n```\n\n##### Example Cart\n```javascript\n{\n    \"_links\": {\n        \"self\": {\n            \"href\": \"http://example.org/carts/1\"\n        },\n        \"items\": [{\n            \"href\": \"http://example.org/items/1\"\n        }, {\n            \"href\": \"http://example.org/items/2\"\n        }, {\n            \"href\": \"http://example.org/items/3\"\n        }, {\n            \"href\": \"http://example.org/items/4\"\n        }, {\n            \"href\": \"http://example.org/items/5\"\n        }]\n    }\n}\n```\n\n#### Errors\n\nShould 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:\n\n```ruby\nGrape::Roar::Extensions::Relations::Exceptions::InvalidRelationError:\n  Expected Mongoid::Relations::Referenced::One, got Mongoid::Relations::Referenced::Many!\n```\n\n#### Change how URLs are presented\n\nThe `opts` hash below is the same one as shown in prior examples.\n\n##### Override base URI mappings\n```ruby\nclass BarEntity \u003c Grape::Roar::Decorator\n  include Roar::JSON\n  include Roar::JSON::HAL\n  include Roar::Hypermedia\n\n  include Grape::Roar::Extensions::Relations\n\n  # This is our default implementation\n  map_base_url do |opts|\n    request = Grape::Request.new(opts[:env])\n    \"#{request.base_url}#{request.script_name}\"\n  end\n\n  relation :has_many, :bars, embedded: false\nend\n```\n\n##### Override resource URI mappings\n```ruby\nclass BarEntity \u003c Grape::Roar::Decorator\n  include Roar::JSON\n  include Roar::JSON::HAL\n  include Roar::Hypermedia\n\n  include Grape::Roar::Extensions::Relations\n\n  # This is our default implementation\n  map_resource_path do |_opts, object, relation_name|\n    \"#{relation_name}/#{object.id}\"\n  end\n\n  relation :has_many, :bars, embedded: false\nend\n```\n\n#### Designing Adapters\n\nIf 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.\n\n##### Example: ActiveRecord Adapter\n\n```ruby\nmodule Extensions\n  module RelationalModels\n    module Adapter\n      class ActiveRecord \u003c Base\n        include Validations::ActiveRecord\n\n        # We map your domain object to the correct adapter\n        # during runtime.\n        valid_for { |klass| klass \u003c ::ActiveRecord::Base }\n\n        def collection_methods\n          @collection_methods ||= %i(has_many has_and_belongs_to_many)\n        end\n\n        def name_for_represented(represented)\n          klass_name = case represented\n                       when ::ActiveRecord::Relation\n                         represented.klass.name\n                       else\n                         represented.class.name\n                       end\n          klass_name.demodulize.pluralize.downcase\n        end\n\n        def single_entity_methods\n          @single_entity_methods ||= %i(has_one belongs_to)\n        end\n      end\n    end\n  end\nend\n```\n\n##### Validations\n\nErrors are handled by creating methods corresponding to those in `collection_methods` and `single_entity_methods`. For example, this is the validator for `belongs_to`:\n\n```ruby\nmodule ActiveRecord\n  include Validations::Misc\n\n  def belongs_to_valid?(relation)\n    relation = klass.reflections[relation]\n\n    return true if relation.is_a?(\n      ::ActiveRecord::Reflection::BelongsToReflection\n    )\n\n    # Provided by Validations::Misc\n    invalid_relation(\n      ::ActiveRecord::Reflection::BelongsToReflection,\n      relation.class\n    )\n  end\nend\n```\n\nAfter 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. \n\n\nContributing\n------------\n\nSee [CONTRIBUTING](CONTRIBUTING.md).\n\nCopyright and License\n---------------------\n\nMIT License, see [LICENSE](LICENSE) for details.\n\n(c) 2012-2014 [Daniel Doubrovkine](https://github.com/dblock) \u0026 Contributors, [Artsy](https://artsy.net)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruby-grape%2Fgrape-roar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fruby-grape%2Fgrape-roar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruby-grape%2Fgrape-roar/lists"}