Snapi

Section 5

Serializer

Serializer

Responsibilities

  1. Converts response into Collection+JSON

Collection+JSON

Collection+JSON is a JSON-based read/write hypermedia-type designed to support management and querying of simple collections.

 

It uses a JSON structure to contains things like collections, refs, templates, queries, and commands.

https://github.com/collection-json/spec

include magic

class ContactApp < BaseApp
  include Omicron.curation(ContactCurator)
  include Omicron.serialization(ContactSerializer)

  get "/contacts" do
    pipe(default_response, :through => [
      :serialize
    ])
  end

  get "/contacts/search" do
    response = curate(artifact, :action => :search)
    serialize(response)
  end
end

We use the include magic to
inject serialize into the app. 

class Response
  include Virtus.value_object

  values do
    attribute :status, Integer, :default => 200
    attribute :headers, Hash, :default => {}
    attribute :collection, Array, :default => []
    attribute :original_collection, Array, :default => []
    attribute :page_information, Hash
    attribute :error_message, String
  end
end

include magic

Omicron.serialization takes the response and will return one of three things:

  1. Errors if response.error_message
  2. "" if no response.no_content?
  3. Call serialize on Serializer and return JSON
class ContactApp < BaseApp
  include Omicron.serialization(ContactSerializer)

  get "/contacts/search" do
    response = curate(artifact, :action => :search)
    serialize(response)
  end
end

Scribe

All serializers inherit from Scribe which deals with the details of serialization. This allows you to only need a few functions in your serializer.

  1. search_url
  2. base_collection
  3. as_item

Serializer

class ContactSerializer < Scribe
  private

  def search_url(*args)
    search_contacts_url(*args)
  end

  def base_collection
    ...
  end

  def as_item(object)
    ...
  end
end

search_url

class ContactSerializer < Scribe
  private

  def search_url(*args)
    search_contacts_url(*args)
  end
end

base_collection

Returned for every request. Contains:

  • version  -- the current version of the api
  • href     -- href of this collection
  • rel      -- unique identifier
  • template -- for creating/updating
  • links    -- associated links
  • queries  -- list of queries
  • commands -- list of commands
class ContactSerializer < Scribe
  private

  def base_collection
    {
      collection: {
        version: API_VERSION,
        href: contacts_url,
        rel: "contacts",
        template: {
          data: [
            {name: "label", value: nil},
            {name: "type", value: Contact.type}
          ]
        },
        links: [
          {rel: "contact_email_addresses", href: contact_email_addresses_url},
          {rel: "self", href: request_url}
        ],
        queries: [
          {
            rel: "search",
            href: search_contacts_url,
            data: [
              {name: "team_id", value: nil},
              {name: "can_receive_push_notifications", value: nil}
            ]
          }
        ],
        commands: [
          {
            rel: "create_bulk_contacts",
            href: create_bulk_contacts_contacts_url,
            data: [
              {name: "team_id", value: nil},]
            ]
          }
        ]
      }
    }
  end
end

as_item

Called for every item in response.collection.

  • href  -- href of this collection
    
  • data  -- data for the item
  • links -- associated links
class ContactSerializer < Scribe
  private

  def as_item(object)
    {
      href: contact_url(object.id),
      data: [
        {name: "id", value: object.id},
        {name: "type", value: object.type},
        {name: "address_city", value: object.address_city},
        {name: "address_country", value: object.address_country},
        {name: "address_state", value: object.address_state},
        {name: "address_street1", value: object.address_street1},
        {name: "address_street2", value: object.address_street2},
        {name: "address_zip", value: object.address_zip}
        {name: "created_at", value: object.created_at, type: "DateTime"},
        {name: "updated_at", value: object.updated_at, type: "DateTime"},
      ],
      links: [
        {
          rel: "contact_email_addresses", 
          href: search_contact_email_addresses_url(:contact_id => object.id)
        },
        {
          rel: "contact_phone_numbers", 
          href: search_contact_phone_numbers_url(:contact_id => object.id)
        },
        {rel: "member", href: member_url(object.member_id)},
      ]
    }
  end
end
{
  "collection": {
    "version": "3.454.0",
    "href": "http://localhost:3000/contacts",
    "rel": "contacts",
    "template": {
      "data": [
        {
          "name": "label",
          "value": null
        },
        {
          "name": "address_street1",
          "value": null
        },
        ...
        {
          "name": "type",
          "value": "contact"
        }
      ]
    },
    "links": [
      {
        "rel": "contact_email_addresses",
        "href": "http://localhost:3000/contact_email_addresses"
      },
      ....
      {
        "rel": "self",
        "href": "http://localhost:3000/contacts"
      }
    ],
    "queries": [
      {
        "rel": "search",
        "href": "http://localhost:3000/contacts/search",
        "data": [
          {
            "name": "team_id",
            "value": null
          },
        ]
      }
    ]
  }
}
/contacts
{
  "collection": {
    "version": "3.454.0",
    "href": "http://localhost:3000/contacts",
    "rel": "contacts",
    "template": {
      ...
    },
    "links": [
      ....
    ],
    "queries": [
      ...
    ],
    "items": [
      {
        "href": "http://localhost:3000/contacts/1",
        "data": [
          {
            "name": "id",
            "value": 1
          },
          {
            "name": "type",
            "value": "contact"
          },
          {
            "name": "member_id",
            "value": 63
          },
          {
            "name": "created_at",
            "value": "2017-05-10T17:28:05Z",
            "type": "DateTime"
          },
        ],
        "links": [
          {
            "rel": "contact_email_addresses",
            "href": "http://localhost:3000/contact_email_addresses/search?contact_id=1"
          },
        ],
        "rel": "contact-1"
      }
    ]
  }
}
/contacts/1

Serializer

Responsibilities

  1. Converts response into Collection JSON

https://github.com/collection-json/spec

Thank you!

Snapi Section 05 Serializer

By Dustin McCraw

Snapi Section 05 Serializer

  • 798