Top Related Projects
ActiveModel::Serializer implementation and Rails hooks
No Longer Maintained - A lightning fast JSON:API serializer for Ruby Objects.
A fast JSON:API serializer for Ruby (fork of Netflix/fast_jsonapi)
General ruby templating with json, bson, xml, plist and msgpack support
Quick Overview
Roar is a Ruby gem that provides a powerful and flexible way to define representers for your Ruby objects. It allows you to create JSON, XML, and other format representations of your data, making it easier to build APIs and handle data serialization/deserialization.
Pros
- Flexible and customizable, allowing for complex data representations
- Supports multiple formats including JSON, XML, and YAML
- Integrates well with other Ruby frameworks like Ruby on Rails
- Provides a clean DSL for defining representers
Cons
- Learning curve can be steep for beginners
- Documentation could be more comprehensive
- Less actively maintained compared to some alternatives
- Performance may be slower than more lightweight serialization libraries
Code Examples
Creating a basic representer:
require 'roar/json'
class ArticleRepresenter < Roar::Decorator
include Roar::JSON
property :title
property :content
property :author, extend: AuthorRepresenter
end
Rendering an object to JSON:
article = Article.new(title: "Roar Overview", content: "Roar is awesome!")
ArticleRepresenter.new(article).to_json
# => {"title":"Roar Overview","content":"Roar is awesome!"}
Parsing JSON into an object:
json = '{"title":"New Article","content":"Some content"}'
article = Article.new
ArticleRepresenter.new(article).from_json(json)
article.title # => "New Article"
Getting Started
- Add Roar to your Gemfile:
gem 'roar'
- Install the gem:
bundle install
- Create a representer for your model:
require 'roar/json'
class UserRepresenter < Roar::Decorator
include Roar::JSON
property :name
property :email
end
- Use the representer to render or parse JSON:
user = User.new(name: "John Doe", email: "john@example.com")
UserRepresenter.new(user).to_json
# => {"name":"John Doe","email":"john@example.com"}
Competitor Comparisons
ActiveModel::Serializer implementation and Rails hooks
Pros of Active Model Serializers
- Tighter integration with Rails ecosystem
- More familiar syntax for Rails developers
- Easier to set up and use for simple serialization tasks
Cons of Active Model Serializers
- Less flexible for complex serialization scenarios
- Performance can be slower for large datasets
- Limited support for deserialization
Code Comparison
Active Model Serializers:
class UserSerializer < ActiveModel::Serializer
attributes :id, :name, :email
has_many :posts
end
Roar:
module UserRepresenter
include Roar::JSON
property :id
property :name
property :email
collection :posts, extend: PostRepresenter
end
Active Model Serializers provides a more Rails-like approach with a class-based serializer, while Roar uses a module-based approach that can be mixed into existing classes. Roar offers more flexibility in terms of representation and supports both serialization and deserialization out of the box.
Active Model Serializers is generally easier to set up and use for simple scenarios, especially within Rails applications. However, Roar provides more control over the serialization process and can be more suitable for complex use cases or when working with hypermedia APIs.
The choice between these libraries depends on the specific requirements of your project, the complexity of your serialization needs, and your preference for Rails integration versus flexibility and control.
No Longer Maintained - A lightning fast JSON:API serializer for Ruby Objects.
Pros of fast_jsonapi
- Significantly faster serialization performance
- Built-in support for JSON:API specification
- Simpler setup and configuration for basic use cases
Cons of fast_jsonapi
- Less flexible for complex serialization scenarios
- Limited support for custom serialization logic
- Tighter coupling to ActiveRecord models
Code Comparison
fast_jsonapi:
class UserSerializer
include FastJsonapi::ObjectSerializer
attributes :name, :email
has_many :posts
end
roar:
module UserRepresenter
include Roar::JSON
property :name
property :email
collection :posts, extend: PostRepresenter
end
Key Differences
- fast_jsonapi focuses on speed and JSON:API compliance, while roar offers more flexibility and customization options
- roar supports multiple formats (JSON, XML, etc.), whereas fast_jsonapi is JSON-specific
- fast_jsonapi is more opinionated and requires less boilerplate for common use cases
- roar allows for more fine-grained control over serialization and deserialization processes
Use Cases
- Choose fast_jsonapi for high-performance JSON:API-compliant APIs with straightforward serialization needs
- Opt for roar when dealing with complex object structures, multiple formats, or requiring extensive customization in serialization logic
A fast JSON:API serializer for Ruby (fork of Netflix/fast_jsonapi)
Pros of jsonapi-serializer
- Specifically designed for JSON:API specification compliance
- Simpler API and easier to use for JSON:API-focused projects
- Better performance for JSON:API serialization tasks
Cons of jsonapi-serializer
- Less flexible for non-JSON:API serialization formats
- More limited in terms of customization options
- Lacks support for deserialization (parsing) operations
Code Comparison
roar example:
class ArticleRepresenter < Roar::Decorator
include Roar::JSON::JSONAPI
type :articles
property :title
property :content
end
jsonapi-serializer example:
class ArticleSerializer
include JSONAPI::Serializer
attributes :title, :content
end
Summary
roar is a more versatile serialization library that supports multiple formats and offers extensive customization options. It's suitable for projects requiring flexibility in representation formats.
jsonapi-serializer is focused specifically on JSON:API serialization, providing a simpler API and better performance for JSON:API-compliant projects. However, it lacks deserialization support and is less flexible for other formats.
Choose roar for projects requiring diverse serialization formats or extensive customization. Opt for jsonapi-serializer when working exclusively with JSON:API and prioritizing simplicity and performance.
General ruby templating with json, bson, xml, plist and msgpack support
Pros of RABL
- Simpler syntax and easier to learn for beginners
- Better performance for large datasets
- More extensive documentation and examples
Cons of RABL
- Less flexibility in object representation
- Limited support for hypermedia APIs
- Fewer options for customization and extensibility
Code Comparison
RABL:
collection @posts
attributes :id, :title, :body
child(:author) { attributes :name }
node(:custom_field) { |post| post.calculate_something }
Roar:
module PostRepresenter
include Roar::JSON
property :id
property :title
property :body
property :author, extend: AuthorRepresenter
property :custom_field, exec_context: :decorator
def custom_field
represented.calculate_something
end
end
Summary
RABL is generally easier to use and performs better with large datasets, making it a good choice for simpler API scenarios. Roar, on the other hand, offers more flexibility and better support for hypermedia APIs, making it suitable for complex, highly customized API representations. The choice between the two depends on the specific requirements of your project and your team's familiarity with the libraries.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Roar
Resource-Oriented Architectures in Ruby.
Table of Contents
- Introduction
- Representable
- Installation
- Defining Representers
- Rendering
- Parsing
- Module Representers
- Collections
- Nesting
- Inline Representer
- Syncing Objects
- Coercion
- More Features
- Hypermedia
- Passing Options
- Specify Decorator
- Consuming Hypermedia
- Media Formats
- HAL-JSON
- JSON API
- Client-Side Support
- HTTP Support
- XML
- Support
- License
Introduction
Roar is a framework for parsing and rendering REST documents. Nothing more.
Representers let you define your API document structure and semantics. They allow both rendering representations from your models and parsing documents to update your Ruby objects. The bi-directional nature of representers make them interesting for both server and client usage.
Roar comes with built-in JSON, JSON-HAL and XML support. JSON API support is available via the JSON API gem. Its highly modular architecture provides features like coercion, hypermedia, HTTP transport, client caching and more.
Roar is completely framework-agnostic and loves being used in web kits like Rails, Hanami, Sinatra, Roda, etc. If you use Rails, consider roar-rails for an enjoyable integration.
Representable
Roar is just a thin layer on top of the representable gem. While Roar gives you a DSL and behaviour for creating hypermedia APIs, representable implements all the mapping functionality.
If in need for a feature, make sure to check the representable API docs first.
Installation
The roar gem runs with all Ruby versions >= 1.9.3.
gem 'roar'
To use roar with Ruby versions < 2.2.0, add a version pin to your Gemfile:
gem 'sinatra', '~> 1.4'
Dependencies
Roar does not bundle dependencies for JSON and XML.
If you want to use JSON, add the following to your Gemfile
:
gem 'multi_json'
If you want to use XML, add the following to your Gemfile
:
gem 'nokogiri'
Defining Representers
Let's see how representers work. They're fun to use.
require 'roar/decorator'
require 'roar/json'
class SongRepresenter < Roar::Decorator
include Roar::JSON
property :title
end
API documents are defined using a decorator class. You can define plain attributes using the ::property
method.
Now let's assume we'd have Song
which is an ActiveRecord
class. Please note that Roar is not limited to ActiveRecord. In fact, it doesn't really care whether it's representing ActiveRecord, Sequel::Model
or just an OpenStruct instance.
class Song < ActiveRecord::Base
end
Rendering
To render a document, you apply the representer to your model.
song = Song.new(title: "Medicine Balls")
SongRepresenter.new(song).to_json #=> {"title":"Medicine Balls"}
Here, the song
objects gets wrapped (or "decorated") by the decorator. It is treated as immutable - Roar won't mix in any behaviour.
Parsing
The cool thing about representers is: they can be used for rendering and parsing. See how easy updating your model from a document is.
song = Song.new(title: "Medicine Balls")
SongRepresenter.new(song).from_json('{"title":"Linoleum"}')
song.title #=> Linoleum
Unknown attributes in the parsed document are simply ignored, making half-baked solutions like strong_parameters
redundant.
Module Representers
Module Representers are deprecated in Roar 1.1 and will be removed in Roar 2.0.
In place of inheriting from Roar::Decorator
, you can also extend a singleton object with a representer module. Decorators and module representers actually have identical features. You can parse, render, nest, go nuts with both of them.
song = Song.new(title: "Fate")
song.extend(SongRepresenter)
song.to_json #=> {"title":"Fate"}
Here, the representer is injected into the actual model and gives us a new #to_json
method.
This also works both ways.
song = Song.new
song.extend(SongRepresenter)
song.from_json('{"title":"Fate"}')
song #=> {"title":"Fate"}
It's worth noting though that many people dislike #extend
due to well-known performance issues and object pollution. As such this approach is no longer recommended. In this README we'll use decorators to illustrate this library.
Collections
Roar (or rather representable) also allows mapping collections in documents.
class SongRepresenter < Roar::Decorator
include Roar::JSON
property :title
collection :composers
end
Where ::property
knows how to handle plain attributes, ::collection
does lists.
song = Song.new(title: "Roxanne", composers: ["Sting", "Stu Copeland"])
SongRepresenter.new(song).to_json #=> {"title":"Roxanne","composers":["Sting","Stu Copeland"]}
And, yes, this also works for parsing: from_json
will create and populate the array of the composers
attribute.
Nesting
Now what if we need to tackle with collections of Song
s? We need to implement an Album
class.
class Album < ActiveRecord::Base
has_many :songs
end
Another representer to represent.
class AlbumRepresenter < Roar::Decorator
include Roar::JSON
property :title
collection :songs, extend: SongRepresenter, class: Song
end
Both ::property
and ::collection
accept options for nesting representers into representers.
The extend:
option tells Roar which representer to use for the nested objects (here, the array items of the album.songs
field). When parsing a document class:
defines the nested object type.
Consider the following object setup.
album = Album.new(title: "True North")
album.songs << Song.new(title: "The Island")
album.songs << Song.new(title: "Changing Tide")
You apply the AlbumRepresenter
and you get a nested document.
AlbumRepresenter.new(album).to_json #=> {"title":"True North","songs":[{"title":"The Island"},{"title":"Changing Tide"}]}
This works vice-versa.
album = Album.new
AlbumRepresenter.new(album).from_json('{"title":"Indestructible","songs":[{"title":"Tropical London"},{"title":"Roadblock"}]}')
puts album.songs[1] #=> #<Song title="Roadblock">
The nesting of two representers can map composed object as you find them in many many APIs.
In case you're after virtual nesting, where a nested block in your document still maps to the same outer object, check out the ::nested
method.
Inline Representer
Sometimes you don't wanna create two separate representers - although it makes them reusable across your app. Use inline representers if you're not intending this.
class AlbumRepresenter < Roar::Decorator
include Roar::JSON
property :title
collection :songs, class: Song do
property :title
end
end
This will give you the same rendering and parsing behaviour as in the previous example with just one module.
Syncing Objects
Usually, when parsing, nested objects are created from scratch. If you want nested objects to be updated instead of being newly created, use parse_strategy:
.
class AlbumRepresenter < Roar::Decorator
include Roar::JSON
property :title
collection :songs, extend: SongRepresenter, parse_strategy: :sync
end
This will advise Roar to update existing songs
.
album.songs[0].object_id #=> 81431220
AlbumRepresenter.new(album).from_json('{"title":"True North","songs":[{"title":"Secret Society"},{"title":"Changing Tide"}]}')
album.songs[0].title #=> Secret Society
album.songs[0].object_id #=> 81431220
Roar didn't create a new Song
instance but updated its attributes, only.
We're currently working on better strategies to easily implement POST
and PUT
semantics in your APIs without having to worry about the nitty-gritties.
Coercion
Roar provides coercion with the dry-types gem.
require 'roar/coercion'
require 'roar/json'
class SongRepresenter < Roar::Decorator
include Roar::JSON
include Roar::Coercion
property :title
property :released_at, type: Types::DateTime
end
The :type
option allows to set a dry-types-compatible type.
song = Song.new
SongRepresenter.new(song).from_json('{"released_at":"1981/03/31"}')
song.released_at #=> 1981-03-31T00:00:00+00:00
More Features
Roar/representable gives you many more mapping features like renaming attributes, wrapping, passing options, etc. See the representable documentation for a detailed explanation.
Hypermedia
Roar comes with built-in support for embedding and processing hypermedia in your documents.
class SongRepresenter < Roar::Decorator
include Roar::JSON
include Roar::Hypermedia
property :title
link :self do
"http://songs/#{title}"
end
end
The Hypermedia
feature allows declaring links using the ::link
method. In the block, you have access to the represented model. When using representer modules, the block is executed in the model's context.
However, when using decorators, the context is the decorator instance, allowing you to access additional data. Use represented
to retrieve model data.
class SongRepresenter < Roar::Decorator
# ..
link :self do
"http://songs/#{represented.title}"
end
end
This will render links into your representation.
SongRepresenter.new(song).to_json #=> {"title":"Roxanne","links":[{"rel":"self","href":"http://songs/Roxanne"}]}
Per default, links are pushed into the hash using the links
key. Link blocks are executed in represented context, allowing you to call any instance method of your model (here, we call #title
).
Also, note that roar-rails allows using URL helpers in link blocks.
Passing Options
Sometimes you need more data in the link block. Data that's not available from the represented model.
class SongRepresenter < Roar::Decorator
include Roar::JSON
property :title
link :self do |opts|
"http://#{opts[:base_url]}songs/#{title}"
end
end
Pass this data to the rendering method.
representer = SongRepresenter.new(song)
representer.to_json(base_url: "localhost:3001/")
Any options passed to #to_json
will be available as block arguments in the link blocks.
Specify Decorator
If you have a property that is a separate class or model, you can specify a decorator for that property. Suppose there is a separate Artist
model for an album. When the album is eagerly loaded, the artist model could be represented along with it.
class ArtistRepresenter < Roar::Decorator
property :name
end
class AlbumRepresenter < Roar::Decorator
# ..
property :artist, decorator: ArtistRepresenter
end
Consuming Hypermedia
Since we defined hypermedia attributes in the representer we can also consume this hypermedia when we parse documents.
representer.from_json('{"title":"Roxanne","links":[{"rel":"self","href":"http://songs/Roxanne"}]}')
representer.links[:self].href #=> "http://songs/Roxanne"
Reading link attributes works by using #links[]
on the consuming instance.
This allows an easy way to discover hypermedia and build navigational logic on top.
Media Formats
While Roar comes with a built-in hypermedia format, there's official media types that are widely recognized. Roar currently supports HAL and JSON API.
Simply by including a module you make your representer understand the media type. This makes it easy to change formats during evaluation.
HAL-JSON
The HAL format is a simple media type that defines embedded resources and hypermedia.
require 'roar/json/hal'
class SongRepresenter < Roar::Decorator
include Roar::JSON::HAL
property :title
link :self do
"http://songs/#{title}"
end
end
Documentation for HAL can be found in the API docs.
Make sure you understand the different contexts for links when using decorators.
Hypermedia
Including the Roar::JSON::HAL
module adds some more DSL methods to your module. It still allows using ::link
but treats them slightly different.
representer.to_json
#=> {"title":"Roxanne","_links":{"self":{"href":"http://songs/Roxanne"}}}
According to the HAL specification, links are now key with their rel
attribute under the _links
key.
Parsing works like-wise: Roar will use the same setters as before but it knows how to read HAL.
Nesting
Nested, or embedded, resources can be defined using the :embedded
option.
class AlbumRepresenter < Roar::Decorator
include Roar::JSON::HAL
property :title
collection :songs, class: Song, embedded: true do
property :title
end
end
To embed a resource, you can use an inline representer or use :extend
to specify the representer name.
AlbumRepresenter.new(album).to_json
#=> {"title":"True North","_embedded":{"songs":[{"title":"The Island"},{"title":"Changing Tide"}]}}
HAL keys nested resources under the _embedded
key and then by their type.
All HAL features in Roar are discussed in the API docs, including array links.
JSON API
Roar also supports JSON API via the Roar JSON API gem.
Client-Side Support
Being a bi-directional mapper that does rendering and parsing, Roar representers are perfectly suitable for use in clients, too. In many projects, representers are shared as gems between server and client.
Consider the following shared representer.
class SongRepresenter < Roar::Decorator
include Roar::JSON
include Roar::Hypermedia
property :title
property :id
link :self do
"http://songs/#{title}"
end
end
In a client where you don't have access to the database it is common to use OpenStruct
classes as domain objects.
require 'roar/client'
require 'roar/json'
class Song < OpenStruct
include Roar::JSON
include SongRepresenter
include Roar::Client
end
HTTP Support
The Client
module mixes all necessary methods into the client class, e.g. it provides HTTP support
song = Song.new(title: "Roxanne")
song.post(uri: "http://localhost:4567/songs", as: "application/json")
song.id #=> 42
What happens here?
- You're responsible for initializing the client object with attributes. This can happen with in the constructor or using accessors.
post
will use the includedSongRepresenter
to compile the document using#to_json
.- The document gets
POST
ed to the passed URL. - If a document is returned, it is deserialized and the client's attributes are updated.
This is a very simple but efficient mechanism for working with representations in a client application.
Roar works with all HTTP request types, check out GET
.
song = Client::Song.new
song.get(uri: "http://localhost:4567/songs/1", as: "application/json")
song.title #=> "Roxanne"
song.links[:self].href #=> http://localhost/songs/1
As GET
is not supposed to send any data, you can use #get
on an empty object to populate it with the server data.
HTTPS
Roar supports SSL connections - they are automatically detected via the protocol.
song.get(uri: "https://localhost:4567/songs/1")
Basic Authentication
The HTTP verbs allow you to specify credentials for HTTP basic auth.
song.get(uri: "http://localhost:4567/songs/1", basic_auth: ["username", "secret_password"])
Client SSL certificates
(Only currently supported with Net:Http)
song.get(uri: "http://localhost:4567/songs/1", pem_file: "/path/to/client/cert.pem", ssl_verify_mode: OpenSSL::SSL::VERIFY_PEER)
Note: ssl_verify_mode is not required and will default to OpenSSL::SSL::VERIFY_PEER)
Request customization
All verbs yield the request object before the request is sent, allowing to modify it. It is a Net::HTTP::Request
instance (unless you use Faraday).
song.get(uri: "http://localhost:4567/songs/1") do |req|
req.add_field("Cookie", "Yumyum")
end
Error handling
In case of a non-2xx response status, #get
and friends raise a Roar::Transport::Error
exception. The original response can be accessed as follows.
song.get(uri: "http://localhost/songs1") # not-existing.
rescue Roar::Transport::Error => exception
exception.response.code #=> 404
XML
Roar also comes with XML support.
class SongRepresenter < Roar::Decorator
include Roar::XML
include Roar::Hypermedia
property :title
property :id
link :self do
"http://songs/#{title}"
end
end
Include the Roar::XML
engine and get bi-directional XML for your objects.
song = Song.new(title: "Roxanne", id: 42)
SongRepresenter.new(song).to_xml
Note that you now use #to_xml
and #from_xml
.
<song>
<title>Roxanne</title>
<id>42</id>
<link rel="self" href="http://songs/Roxanne"/>
</song>
Please consult the representable XML documentation for all its great features.
Support
Questions? Need help? Free 1st Level Support on irc.freenode.org#roar ! We also have a mailing list, yiha!
License
Roar is released under the MIT License.
Top Related Projects
ActiveModel::Serializer implementation and Rails hooks
No Longer Maintained - A lightning fast JSON:API serializer for Ruby Objects.
A fast JSON:API serializer for Ruby (fork of Netflix/fast_jsonapi)
General ruby templating with json, bson, xml, plist and msgpack support
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot