Skip to content

Instantly share code, notes, and snippets.

@Kingdutch
Last active November 17, 2015 12:24
Show Gist options
  • Select an option

  • Save Kingdutch/299b3c6fd5702ad2ea50 to your computer and use it in GitHub Desktop.

Select an option

Save Kingdutch/299b3c6fd5702ad2ea50 to your computer and use it in GitHub Desktop.
This is an example response for the Vestingbar Beer API (VBBAPI).
class BeersController < ApplicationController
before_action :set_beer, only: [:show, :update, :destroy]
# GET /beers
def index
@beers = Beer.all
data = []
included = []
# set up the links for this request (e.g. self, next, last, etc.)
links = {
self: request.original_url
}
# massage the data into shape
# TODO: Not all requests should return price, temporary or active
should_include = {
beer_types: Array.new
}
for beer in @beers
entry = {
type: 'beers',
id: beer.id,
attributes: {
name: beer.name,
description: beer.description,
volume: beer.volume,
alcohol: beer.alcohol,
price: beer.price,
temporary: beer.temporary,
active: beer.active
},
relationships: {
beer_type: {
# TODO: add dynamically generated links here
data: { type: 'beer_types', id: beer.beer_types_id }
}
},
links: {
# TODO: add dynamically generated link to self here
}
}
should_include[:beer_types].push(beer.beer_types_id)
data.push(entry)
end
print 'Beer types array:'
puts should_include[:beer_types]
# find the included beer types and pass them along
@beer_types = BeerType.find(should_include[:beer_types].uniq)
for type in @beer_types
entry = {
type: 'beer_types',
id: type.id,
attributes: {
name: type.name,
description: type.description
},
links: {
# TODO: add dynamically generated link to self here
}
}
included.push(entry)
end
render json: { links: links, data: data, included: included }
end
# GET /beers/1
def show
render json: @beer
end
# POST /beers
def create
@beer = Beer.new(beer_params)
if @beer.save
render json: @beer, status: :created, location: @beer
else
render json: @beer.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /beers/1
def update
if @beer.update(beer_params)
render json: @beer
else
render json: @beer.errors, status: :unprocessable_entity
end
end
# DELETE /beers/1
def destroy
@beer.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_beer
@beer = Beer.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def beer_params
params.require(:beer)
.permit(:name, :beer_types_id, :volume, :alcohol, :price,
:description, :temporary, :active)
end
end
class CreateBeers < ActiveRecord::Migration
def change
create_table :beers do |t|
t.string :name, :null => false
t.references :beer_types, index: true, foreign_key: true
t.decimal :volume, :null => false
t.decimal :alcohol, precision: 3, scale: 1, :null => false
t.decimal :price, precision: 5, scale: 2, :null => false
t.text :description, :null => false
t.boolean :temporary, default: false
t.boolean :active, default: true
t.timestamps
end
add_index :beers, :name, unique: true
end
end
# This file should be in config/initializers -- don't copy this line
# Be sure to restart your server when you modify this file.
# Serialize models using AWS as json_api instead of plain json
ActiveModel::Serializer.config.adapter = :json_api
class Beer < ActiveRecord::Base
belongs_to :beer_types, class_name: 'BeerType'
validates :name, :presence => true
validates :volume, :presence => true
validates :alcohol, :presence => true
validates :price, :presence => true
validates :description, :presence => true
end
{
"links": {
"self": "http://localhost:3000/beers/"
},
"data": [
{
"type": "beers",
"id": 1,
"attributes": {
"name": "Saison 1",
"description": "The first Saison we have!",
"volume": 0,
"alcohol": "8.5",
"price": "10.2",
"temporary": false,
"active": true
},
"relationships": {
"beer_type": {
"data": {
"type": "beer_types",
"id": 1
}
}
},
"links": {}
}
],
"included": [
{
"type": "beer_types",
"id": 1,
"attributes": {
"name": "Saison",
"description": "A pale ale that is generally around 7% abv, highly carbonated, fruity, spicy, and often bottle conditioned."
},
"links": {}
}
]
}
@Kingdutch

Copy link
Copy Markdown
Author

It tries to adhere to the JSON API standard but this creates controllers that do quite a lot of work.

It should be possible to refactor this and move work to the controller, although that might increase the number of database requests for Beer Types which is now just 1, meaning a listing of all beers involves just two requests.

@Kingdutch

Copy link
Copy Markdown
Author

The beer_types model, migration and controller are all trivial and are thus omitted.

They can be created using rails g scaffold BeerType name:string:uniq description:text

@Kingdutch

Copy link
Copy Markdown
Author

The https://github.com/rails-api/active_model_serializers json_api serializer is probably more to our taste than the json_api gem which does a lot more magic. I need to find out how to set the serializers to json_api format.

Furthermore, there's extra loading of the BeerType model in the index method which is not needed in the show method. This is because the show method properly uses the serializer which is aware of the beertype and renders it correctly while the index method doesn't do this.

@Kingdutch

Copy link
Copy Markdown
Author

The jsonapi_serializer.rb file I just added changes the following:

{
  "id": 1,
  "name": "Saison 1",
  "volume": 0,
  "alcohol": "8.5",
  "price": "10.2",
  "description": "The first Saison we have!",
  "active": true,
  "beer_types": {
    "id": 1,
    "name": "Saison",
    "description": "A pale ale that is generally around 7% abv, highly carbonated, fruity, spicy, and often bottle conditioned."
  }
}

Into

{
    "data": {
        "id": "1",
        "type": "beers",
        "attributes": {
            "name": "Saison 1",
            "volume": 0,
            "alcohol": "8.5",
            "price": "10.2",
            "description": "The first Saison we have!",
            "active": true
        },
        "relationships": {
            "beer_types": {
                "data": {
                    "id": "1",
                    "type": "beer_types"
                }
            }
        }
    }
}

Which is pretty awesome

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment