I started with simple, concrete hypermedia for Objectives. For the collection I created a collection
method, into which I passed the Objectives to create a collection+json
object. For a single record, I loop through its associations with reflect_on_all_associations
, and create links to all its associated objects.
I then moved this logic to ApplicationController and abstracted it for use with Indicators.
Namespaces in this hackery were a little tricky: self_link
could theoretically mean either:
{"rel": "self", "href": "/path/to/self"}
or
Link: <http://localhost:3000/path/to/self?page=1>;rel=self
I don't know Rails well enough (yet!) to create a new renderer. The Rails 3 respond_to do |format|
is messy, but for my small customizations it worked:
respond_to do |format|
format.html # index.html.erb
format.json { render json: collection(@indicators),
content_type: "collection+json"
response.headers['Link'] = link_header(current_page, last_page) }
end
I added pagination at the end. I needed a page_num util to do some calculations for the Link header, and my code for that is pretty wet (i.e. not DRY).
The billboard URL was also very un-Railsy. I explictly defined everything in root_controller.rb
, as opposed to looping through the top-level models.
I'm sure I didn't form rel
attributes well. I didn't have a clear idea of what a client might be expecting.
Before I go forward, I really want to extract this into a gem, and see if I can get more logic in the Model. From my experiment today it seems that a lot of this behavior is very generalizable, and could be customized easily (if it's a good gem, which I don't yet know how to write).
I imagine a 'hypermediafy' gem that:
- moves more logic to the model
- uses ActiveSupport::Concern
- can extend will_paginate and kaminari
One could implement it by doing:
# /app/models/indicator.rb
class Indicator < ActiveRecord::Base
attr_accessible :attribute ...
belongs_to :objective
has_many :measurements, :datasets
paginates_per 10
hyperlinks_for :objective, :measurements # whitelist relations to pass to `reflect_on_all_assocations`
paginate_header :prev, :next # Adds rel=prev and rel=next links in the Link header
# when used with a pagination gem like will_paginate or kaminari
end
# /app/controllers/indicators_controller.rb
class IndicatorsController < ApplicationController
# GET /indicators
# GET /indicators.json
def index
@indicators = Indicator.page(params[:page])
respond_to do |format| # responds to 'Accept' headers
format.html # index.html.erb
format.json { render hypermedia: @indicators } # or render collection: @indicators
# sets Content-Type to 'collection+json'
end
end
# ...
end
I haven't yet done anything with authorization or PUT/POST actions. That's up next.
I also want to start defining ALPS.io semantics, and I could use help getting started there.
@beechnut
looks like you're making good progress. i'll be interested in the "gem-ification" process; esp regarding generating links.
let me know what your plans are for ALPS semantics. i'd be happy to pitch in and/or kibitz along the way.