SkillAgentSearch skills...

Yaks

Ruby library for building hypermedia APIs

Install / Use

/learn @plexus/Yaks
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Gem Version Build Status Code Climate Gitter(https://badges.gitter.im/Join Chat.svg)

Yaks

<img align="left" src="https://raw.githubusercontent.com/plexus/yaks/master/graphics/logo_small.png">

The library that understands hypermedia.

If you use Yaks please help out by filling out the Yaks Users Survey

Yaks takes your data and transforms it into hypermedia formats such as HAL, JSON-API, or HTML. It allows you to build APIs that are discoverable and browsable. It is built from the ground up around linked resources, a concept central to the architecture of the web.

Yaks consists of a resource representation that is independent of any output type. A Yaks mapper transforms an object into a resource, which can then be serialized into whichever output format the client requested. These formats are presently supported:

  • HAL
  • JSON API
  • Collection+JSON
  • HTML
  • HALO
  • Transit

Table of Contents

Packages

State of Development

Recent focus has been on stabilizing the core classes, improving format support, and increasing test (mutation) coverage. We are committed to a stable public API and semantic version. On the 0.x line the minor version is bumped when non-backwards compatible changes are introduced. After 1.x regular semver conventions will be used.

Concepts

Yaks is a processing pipeline, you create and configure the pipeline, then feed data through it.

yaks = Yaks.new do
  default_format :hal
  rel_template 'http://api.example.com/rels/{rel}'
  format_options(:hal, plural_links: [:copyright])
  mapper_namespace ::MyAPI
  json_serializer do |data|
    JSON.dump(data)
  end
end

yaks.call(product)

Yaks performs this serialization in three steps

  • It maps your data to a Yaks::Resource
  • It formats the resource to a syntax tree representation
  • It serializes to get the final output

For JSON types, the "syntax tree" is just a combination of Ruby primitives, nested arrays and hashes with strings, numbers, booleans, nils.

A Resource is an abstraction shared by all output formats. It can contain key-value attributes, RFC5988 style links, and embedded sub-resources.

To build an API you create a "mapper" for each type of object you want to represent. Yaks takes care of the rest.

For all configuration options see Yaks::Config::DSL.

See also the API Docs on rdoc.info

Mappers

Say your app has a Post object for blog posts. To serve posts over your API, define a PostMapper

class PostMapper < Yaks::Mapper
  link :self, '/api/posts/{id}'

  attributes :id, :title

  has_one :author
  has_many :comments
end

Configure a Yaks instance and start serializing!

yaks = Yaks.new
yaks.call(post)

or a bit more elaborate

yaks = Yaks.new do
  default_format :json_api
  rel_template 'http://api.example.com/rels/{rel}'
  format_options(:hal, plural_links: [:copyright])
end

yaks.call(post, mapper: ::PostMapper, format: :hal)

Attributes

Use the attribute or attributes DSL methods to specify which attributes of your model you want to expose, as in the example above. You can override the load_attribute method to change how attributes are fetched from the model.

For example, if you are representing data that is stored in a Hash, you could do

class PostHashMapper < Yaks::Mapper
  attributes :id, :body

  # @param name [Symbol]
  def load_attribute(name)
    object[name]
  end
end

The attribute method may also take a block that will be called with the context of the mapper instance. The default implementation will use the block if provided, otherwise it will first try to find a matching method for an attribute on the mapper itself, and will then fall back to calling the actual model. So you can add extra 'virtual' attributes like so :

class CommentMapper < Yaks::Mapper
  attributes :body, :date
  attribute :id do
    "Id-#{object.id}"
  end

  def date
    object.created_at.strftime("at %I:%M%p")
  end
end

Forms

Mapper can contain form defintions, for formats that support them. The form DSL mimics the HTML5 field and attribute names.

class PostMapper < Yaks::Mapper
  attributes :id, :body, :date

  form :add_comment do
    action '/api/comments'
    method 'POST'
    media_type 'application/json'

    text :body
    hidden :post_id, value: -> { object.id }
  end
end

TODO: add more info on form element types, attributes, conditional rendering of forms, dynamic form sections, ...

Filtering

You can override #attributes, or #associations.

class SongMapper < Yaks::Mapper
  attributes :title, :duration, :lyrics

  has_one :artist
  has_one :album

  def minimal?
    env['HTTP_PREFER'] =~ /minimal/
  end

  # @return Array<Yaks::Mapper::Attribute>
  def attributes
    return super.reject {|attr| attr.name.equal? :lyrics } if minimal?
    super
  end

  # @return Array<Yaks::Mapper::Association>
  def associations
    return [] if minimal?
    super
  end
end

Links

You can specify link templates that will be expanded with model attributes. The link relation name should be a registered IANA link relation or a URL. The template syntax follows RFC6570 URI templates.

class FooMapper < Yaks::Mapper
  link :self, '/api/foo/{id}'
  link 'http://api.foo.com/rels/comments', '/api/foo/{id}/comments'
end

To prevent a link to be expanded, add expand: false as an option. Now the actual template will be rendered in the result, so clients can use it to generate links from.

To partially expand the template, pass an array with field names to expand. e.g.

class ProductMapper < Yaks::Mapper
  link 'http://api.foo.com/rels/line_item', '/api/line_items?product_id={product_id}&quantity={quantity}', expand: [:product_id]
end

# "_links": {
#    "http://api.foo.com/rels/line_item": {
#      "href": "/api/line_items?product_id=273&quantity={quantity}",
#      "templated": true
#    }
# }

You can pass a proc instead of a template, in that case the proc will be resolved in the context of the mapper. What this means is that, if the proc takes no arguments, it will be evaluated with the mapper instance as the value of self. If the proc does take an argument, then it will receive the mapper instance, and will be evaluated as a closure, i.e. with access to the scope in which it was defined.

class FooMapper < Yaks::Mapper
  link 'http://api.foo.com/rels/go_home', -> { home_url }
  # by default calls object.home_url

  def home_url
    object.setting('home_url')
  end
end

To only include links based on certain conditions, add an :if option, passing it a block. The block will be resolved in the context of the mapper, as explained before.

For example, say you want to notify the consumer of your API that upon confirming an order, the previously held cart is no longer valid, you could use the IANA standard invalidates rel to communicate this.

class OrderMapper < Yaks::Mapper
  link :invalidates, '/api/cart', if: ->{ env['api.invalidate_cart'] }
end

Associations

Use has_one for an association that returns a single object, or has_many for embedding a collection.

Options

  • :mapper : Use a specific for each instance, will be derived from the class name if omitted (see Policy vs Configuration)
  • :collection_mapper : For mapping the collection as a whole, this defaults to Yaks::CollectionMapper, but you can subclass it for example to add links or attributes on the collection itself
  • :rel : Set the r
View on GitHub
GitHub Stars236
CategoryDevelopment
Updated2d ago
Forks24

Languages

Ruby

Security Score

95/100

Audited on Apr 3, 2026

No findings