Supa

Ruby object → JSON serialization.

Build Status Code Climate Test Coverage Issue Count

Introduction

Installation

Add this line to your application's Gemfile

gem 'supa'

And then execute

bundle install

Or install it yourself as

gem install supa

Usage

Example

class Article
  attr_accessor :id, :title, :text, :author, :comments
end
class Author
  attr_accessor :id, :first_name, :last_name
end
class Comment
  attr_accessor :id, :text
end
class ArticleRepresenter
  include Supa::Representable

  define do
    namespace :jsonapi do
      virtual :version, getter: 1.1, modifier: :to_s
    end

    namespace :meta do
      attribute :locale, getter: :language, exec_context: :representer
      attribute :date, exec_context: :representer
    end

    namespace :data do
      attribute :id
      virtual :type, getter: 'articles'

      namespace :attributes do
        attribute :title
        attribute :text
      end

      namespace :relationships do
        object :author do
          namespace :data do
            attribute :id
            virtual :type, getter: 'authors'
          end
        end

        namespace :comments do
          collection :data, getter: :comments do
            attribute :id
            virtual :type, getter: 'comments'
          end
        end
      end
    end

    collection :included, getter: :author do
      attribute :id
      virtual :type, getter: 'authors'

      namespace :attributes do
        attribute :first_name
        attribute :last_name
      end
    end

    append :included, getter: :comments do
      attribute :id
      virtual :type, getter: 'comments'

      namespace :attributes do
        attribute :text
      end
    end
  end

  def to_s(value)
    value.to_s
  end

  def language
    'en'
  end

  def date
    Date.today.iso8601
  end
end
ArticleRepresenter.new(Article.new).to_json
{
  "jsonapi": {
    "version": "1.1"
  },
  "meta": {
    "locale": "en",
    "date": "2050-01-01"
  },
  "data": {
    "id": "7aa15512-1f9d-4a86-98ad-4bb0aae487a2",
    "type": "articles",
    "attributes": {
      "title": "Pilot wave theory",
      "text": "In theoretical physics, the pilot wave theory was the first known example of a hidden variable theory, presented by Louis de Broglie in 1927. Its more modern version, the de Broglie–Bohm theory, remains a non-mainstream attempt to interpret quantum mechanics as a deterministic theory, avoiding troublesome notions such as wave–particle duality, instantaneous wave function collapse and the paradox of Schrödinger's cat."
    },
    "relationships": {
      "author": {
        "data": {
          "id": "52139b0b-bd22-4fc7-adc8-593f16ae034f",
          "type": "authors"
        }
      },
      "comments": {
        "data": [
          {
            "id": "35a88ca5-80ec-4e49-9357-d8a16b8873f8",
            "type": "comments"
          },
          {
            "id": "0e02b198-299a-4e6b-99a0-8f2c33c15b1d",
            "type": "comments"
          }
        ]
      }
    }
  },
  "included": [
    {
      "id": "52139b0b-bd22-4fc7-adc8-593f16ae034f",
      "type": "authors",
      "attributes": {
        "first_name": "Louis",
        "last_name": "de Broglie"
      }
    },
    {
      "id": "35a88ca5-80ec-4e49-9357-d8a16b8873f8",
      "type": "comments",
      "attributes": {
        "text": "There can exist empty waves, represented by wave functions propagating in space and time but not carrying energy or momentum, and not associated with a particle."
      }
    },
    {
      "id": "0e02b198-299a-4e6b-99a0-8f2c33c15b1d",
      "type": "comments",
      "attributes": {
        "text": "Let's call the concept ghost waves."
      }
    }
  ]
}

attribute

Attributes will be retrieved from correspondingly named instance methods unless a getter is defined:

class ExampleRepresenter
  include Supa::Representable

  define do
    attribute :name
  end
end

ExampleRepresenter.new(OpenStruct.new(name: 'Heidi')).to_hash

  #=> {
  #=>   name: 'Heidi'
  #=> }

A getter can take several forms:

1. Method name

class ExampleRepresenter
  include Supa::Representable

  define do
    attribute :name, getter: :full_name
  end
end

class Person
  attr_accessor :full_name
end

example = Person.new
example.full_name = 'Heidi Shepherd'

ExampleRepresenter.new(example).to_hash

  #=> {
  #=>   name: 'Heidi Shepherd'
  #=> }

The lookup order is to first check the object instance and then the representer for a matching method.

2. Hash key

class ExampleRepresenter
  include Supa::Representable

  define do
    attribute :name, getter: 'full_name'
  end
end

example = {
  'full_name' => 'Heidi Shepherd'
}

ExampleRepresenter.new(example).to_hash

  #=> {
  #=>   name: 'Heidi Shepherd'
  #=> }

attributes

It retrieves attributes from correspondingly named instance methods or hash keys when the represented object is a Hash.

class ExampleRepresenter
  include Supa::Representable

  define do
    attributes :first_name, :last_name, hide_when_empty: true
  end
end

class Person
  attr_accessor :first_name, :last_name
end

example = Person.new
example.first_name = 'Heidi'
example.last_name = 'Shepherd'

ExampleRepresenter.new(example).to_hash

  #=> {
  #=>   first_name: 'Heidi',
  #=    last_name: 'Shepherd'
  #=> }

virtual

Virtual is an attribute that doesn't exist in representing object and defind as string.

class ExampleRepresenter
  include Supa::Representable

  define do
    attribute :version, getter: 1.0
    attribute :type, getter: 'documentation'
  end
end

ExampleRepresenter.new({}).to_hash

{
  version: 1.0,
  type: 'documentation',
}

namespace

object

collection

append

Development

To install dependencies

bin/setup

To run tests

bundle exec rake test

To run benchmarks

bundle exec rake bench

To spin up an interactive prompt that will allow you to experiment

bin/console

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/distribusion/supa. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.