Halogen
This library provides a framework-agnostic interface for generating HAL+JSON representations of resources in Ruby.
Installation
Add this line to your application's Gemfile:
gem 'halogen'
And then execute:
$ bundle
Or install it yourself as:
$ gem install halogen
Usage
Basic usage
Create a simple representer class and include Halogen:
class GoatRepresenter
include Halogen
property :name do
'Gideon'
end
link :self do
'/goats/gideon'
end
end
Instantiate:
repr = GoatRepresenter.new
Then call repr.render
:
{
name: 'Gideon',
_links: {
self: { href: '/goats/gideon' }
}
}
Or repr.to_json
:
'{"name": "Gideon", "_links": {"self": {"href": "/goats/gideon"}}}'
Representer types
1. Simple
Not associated with any particular resource or collection. For example, an API entry point:
class ApiRootRepresenter
include Halogen
link(:self) { '/api' }
end
2. Resource
Represents a single item:
class GoatRepresenter
include Halogen
resource :goat
end
When a resource is declared, #initialize
expects the resource as the first argument:
repr = GoatRepresenter.new(Goat.new, ...)
This makes property definitions cleaner:
property :name # now calls Goat#name by default
3. Collection
Represents a collection of items. When a collection is declared, the embedded resource with the same name will always be embedded, whether it is requested via standard embed options or not.
class GoatKidsRepresenter
include Halogen
collection :kids
embed(:kids) { ... } # always embedded
end
Defining properties, links and embeds
Properties can be defined in several ways:
property(:age) { "#{goat.age} years old" }
property :age # => Goat#age, if resource is declared
property :age do
goat.age.round
end
property(:age) { calculate_age }
def calculate_age
...
end
Conditionals
The inclusion of properties can be determined by conditionals using if
and
unless
options. For example, with a method name:
property :age, if: :include_age?
def include_age?
goat.age < 10
end
With a proc:
property :age, unless: proc { goat.age.nil? }, value: ...
For links and embeds:
link :kids, :templated, unless: :exclude_kids_link?, value: ...
embed :kids, if: proc { goat.kids.size > 0 } do
...
end
Links
Simple link:
link(:root) { '/' }
# => { _links: { root: { href: '/' } } ... }
Templated link:
link(:find, :templated) { '/goats/{?id}' }
# => { _links: { find: { href: '/goats/{?id}', templated: true } } ... }
Optional links:
representer = MyRepresenterWithManyLinks.new(include_links: false)
representation = representer.render
representation[:_links] #nil
Embedded resources
Embedded resources are not rendered by default. They will be included if both of the following conditions are met:
- The proc returns either a Halogen instance or an array of Halogen instances
- The embed is requested via the parent representer's options, e.g.:
GoatRepresenter.new(embed: { kids: true, parents: false })
Embedded resources can be nested to any depth, e.g.:
GoatRepresenter.new(embed: {
kids: {
foods: {
ingredients: true
},
pasture: true
}
})
Using with Rails
If Halogen is loaded in a Rails application, Rails url helpers will be available in representers:
link(:new) { new_goat_url }
More examples
What's with the goat?
It is majestic.
Contributing
- Fork it ( https://github.com/mode/halogen/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request