Makes using Roar's representers in your Rails app fun.
roar-rails development will be discontinued in the future and we will encourage users to begin migrating to Trailblazer (and trailblazer-rails).
Roar is a framework for parsing and rendering REST documents. For a better overview about representers please check the roar repository.
Roar-rails gives you conventions and convenient access to a lot of Roar's functionality within your Rails app.
- Rendering with responders
- Parsing incoming documents
- URL helpers in representers
- Better tests
- Autoloading
- Generators
This gem works with all Rails >= 3.x.
Add it to your app's Gemfile.
gem "roar-rails"Note: For Rails >= 4.2, you need to add the responders gem, too, if you use respond_with. This has to be before the roar-rails entry in the Gemfile.
gem "responders"
gem "roar-rails"The generator will create the representer modules in app/representers for you.
Here's an example.
rails g representer Band id nameThis will create the file app/representers/band_representer.rb with the following content,
module BandRepresenter
include Roar::JSON
property :id
property :name
endYou can change the format (e.g. XML), and pass arbitrary options to customize the generated representer. For all available options, just run
rails g representerroar-rails provides a number of baked-in rendering methods.
Easily render resources using representers with the built-in responder.
class SingersController < ApplicationController
include Roar::Rails::ControllerAdditions
respond_to :json
def show
singer = Singer.find_by_id(params[:id])
respond_with singer
end
endThe representer name will be infered from the passed model class (e.g. a Singer instance gets the SingerRepresenter). If the passed model is a collection it will be extended using a representer. The representer name will be computed from the controller name (e.g. a SingersController uses the SingersRepresenter).
Need to use a representer with a different name than your model? You may always pass it in using the :represent_with option:
respond_with singers, :represent_with => MusicianCollectionRepresenter
endIf you don't want to use conventions or pass representers you can configure them on the class level using ::represents. This will also call respond_to for you.
class SingersController < ApplicationController
represents :json, MusicianThis will use the MusicianRepresenter for models and MusiciansRepresenter for representing collections.
Note that ::represents also allows fine-tuning.
class SingersController < ApplicationController
represents :json, :entity => MusicianRepresenter, :collection => MusicianCollectionRepresenterYou might pass strings as representer names to ::represents, they will be constantized at run-time when needed.
In place of #respond_with, you can also use #render to serialize objects using representers.
class SingersController < ApplicationController
include Roar::Rails::ControllerAdditions
include Roar::Rails::ControllerAdditions::Render
def show
singer = Singer.find_by_id(params[:id])
render json: singer
end
endIf you don't want to write a dedicated representer for a collection of items (highly recommended, thou) but rather use a representer for each item, use the :represent_items_with option.
class SingersController < ApplicationController
def index
singers = Musician.find(:all)
respond_with singers, :represent_items_with => SingerRepresenter
end
endIn #create and #update actions it is often necessary to parse the incoming representation and map it to a model instance. Use the #consume! method for this.
The client must provide a Content-Type request header with proper MIME type to let #consume! know which representer to use.
class SingersController < ApplicationController
respond_to :json
def create
singer = Singer.new
consume!(singer)
respond_with singer
end
endFor instance, if content type is set to application/xml the consume! call will roughly do the following.
singer.
extend(SingerRepresenter)
from_xml(request.body)So, #consume! helps you figuring out the representer module and reading the incoming document. Just like Rails, depending on the registered MIME type for Content-type it picks the deserialize method (e.g. from_json vs. from_xml)
It is important to provide a known content type in the request. If it is missing or not supported by the responder
#consume! will raise an exception Roar::Rails::ControllerAdditions::UnsupportedMediaType. Unless you rescue the exception the action will stop and respond with HTTP status 406 Unsupported Media Type.
Note that #consume! respects settings from #represents. It uses the same mechanics known from #respond_with to choose a representer.
consume!(singer, :represent_with => MusicianRepresenter)If you prefer roar's decorator approach over extend, just go for it. roar-rails will figure out automatically which represent strategy to use. Be sure to use roar >= 0.11.17.
class SingerRepresenter < Roar::Decorator
include Roar::JSON
include Roar::Hypermedia
property :name
link :self do
singer_url(represented)
end
endIn decorators' link blocks you currently have to use represented to get the actual represented model (this is self in module representers).
Both rendering and consuming support passing user options to the representer.
With #respond_with, any additional options will be passed to to_json (or whatever format you're using).
respond_with @singer, :current_user => current_userSame goes with #consume!, passing options to from_json.
consume! Singer.new, :current_user => current_userNote: If you pass in options to a representer, you must process them youself. For rendering, use :getter in the representer.
property :username, getter: lambda { |args| args[:current_user].name }That'll render the current_user's name as the username property.
More docs about passing and processing option can be found here.
Any URL helpers from the Rails app are automatically available in representers.
module FruitRepresenter
include Roar::JSON
include Roar::Hypermedia
link :self do
fruit_url self
end
endTo get the hyperlinks up and running, please make sure to set the right host name in your environment files (config/environments):
config.representer.default_url_options = {:host => "127.0.0.1:3000"}Attention: If you are using representers from a gem your Rails URL helpers might not work in these modules. This is due to a loading order problem in Rails. As a workaround, don't require the representers in the gem but load them as late as possible, usually it works when you require in the controller. We are working on fixing that problem.
By default, roar-rails will extend/decorate any model passed to respond_with for any request format. When adding roar-rails to a legacy project, you might want to restrict roar-rails' representing and fall back to the old behavior for certain formats. This can be configured both globally and on a per action basis.
To restrict representing globally to a particular format you can set the config.representer.represented_formats in your environment's configuration to an array of formats. For example the following will only represent hal and json requests.
config.representer.represented_formats = [:hal, :json]The global configuration (or lack thereof) can be overridden by supplying the :represented_formats array when calling respond_with. The following will only represent @resource for the hal format in the show action. For any other format, it will expose the resource using Rails' old behavior.
class MyController < ApplicationController
def show
...
respond_with @resource, :represented_formats => [:hal]
end
endYou can entirely suppress roar-rails in respond_with by passing in an empty array.
class MyController < ApplicationController
def show
...
respond_with @resource, :represented_formats => []
end
endPut your representers in app/representers and they will be autoloaded by Rails. Also, frequently used modules as media representers and features don't need to be required manually.
In a JSON-API environment, only one representer is written for both singular and collection resources. However, you have to configure represents accordingly so it knows what representer to use for collections.
class SongsController < ApplicationController
represents :json_api, entity: SongRepresenter, collection: SongRepresenter.for_collectionNote: this is a temporary work-around, we're trying to fix that in Rails/roar-rails itself [May 2014].
Rails 4.1 and up expects you to manually register a global HAL or JSON-API renderer, or respond_with will throw an ActionController::MissingRenderer exception.
One fix is to add this to config/initializers/mime_types.rb right below Mime::Type.register 'application/hal+json', :hal:
ActionController::Renderers.add :hal do |obj, options|
self.content_type ||= Mime[:hal]
obj
endSimilarly, for JSON-API, below Mime::Type.register 'application/vnd.api+json', :json_api add:
ActionController::Renderers.add :json_api do |obj, options|
self.content_type ||= Mime[:json_api]
obj
end- Railslove and especially Michael Bumann [bumi] have heavily supported development of roar-rails ("resource :singers").
Roar-rails is released under the MIT License.