Posted on 17 Sep 2012
This post was originally published on the
SmartLogic Blog.
This past weekend, I headed down to REST Fest in Greenville, South Carolina. My weekend lived up to my expectations: though I didn’t get much of a chance to work on Frenetic, I spread the word about Raddocs and RAD, and learned a lot from the API developers at the event.
For those of you who couldn’t make it, here’s a quick wrap-up of three things I learned, and, at the end of the post, the video of my 5in5 talk:
What a bad API looks like
During Mike Amundsen’s presentation he said, “Exposing your database is like showing me your underwear. I don’t wanna see that.” This was both enlightening and disheartening. My previous APIs have been mostly database table to resource. While they do their job, they definitely could be better. In the future I will definitely put more thought into the design of my APIs.
Before attending REST Fest, I didn’t know that if you create a short link relation you have to register it with a registry such as IANA or MicroFormats. The way around this is to create link relations as URIs.
An example from the Helpdesk Hackday at REST Fest:
<atom:link rel="http://helpdesk.hackday.2012.restfest.org/rels/ticket" href="http://.../tickets/9172361" type="application/vnd.org.restfest.2012.hackday+xml" />
In the same vein as registering link relations, I didn’t know you were supposed to register media types. The HTTP spec calls for them to be registered in RFC 2616. Here’s the gist: “Media-type values are registered with the Internet Assigned Number Authority (IANA [19]). The media type registration process is outlined in RFC 1590 [17]. Use of non-registered media types is discouraged.”
Linking in JSON
I learned of a new hypermedia format for JSON called Siren. I had previously seen HAL before, but I like that Siren gives you the ability to include actions in the resource. Siren is located here on GitHub.
In addition to learning about Siren, I also learned more about Collection+JSON. I had seen it before but I had only glanced at it. Collection+JSON is located here. I will definitely be giving both of these a more thorough look through for my next API project.
Overall, the weekend was jam packed with new information. Were you there? What was the most useful thing you learned? Comment here or tweet me, @ericoestrich.
And, as promised, here’s my 5in5 talk:
REST Fest 2012 \ Eric Oestrich \ FiveInFive from REST Fest on Vimeo.
Posted on 12 Sep 2012
This post was originally published on the
SmartLogic Blog.
I am pretty excited about attending REST Fest this week. I am most looking forward to the following:
Getting together with other API developers
Being able to hang out with other developers who are interested in APIs is definitely the thing I’m most looking forward to. I want to absorb as much information as I can from this event.
Hacking on Frenetic
On the Thursday Hack day I want to play around with the gem Frenetic, https://github.com/dlindahl/frenetic. Right now it’s kinda awkward to use it as a client because it requires a crazy amount of chaining. I would like to be able to knock that down a lot.
api.get(api.description.links.orders.href).body.resources.orders
In addition to getting the previous command shorter, Frenetic doesn’t seem to give you easy accessors to the links that the API returns.
Talking more about Raddocs and RAD
Since I’m going to be giving a 5in5 talk when I attend REST Fest, it’s the perfect opportunity to put Raddocs and RAD in front of other developers faces. I will be giving a modified version of my “cURLin’ for Docs” presentation.
Looking forward to heading to Greenville! Tweet me @ericoestrich if you want to meet up when we’re there.
Posted on 06 Sep 2012
Last week SmartLogic had an internal conference where I gave a presentation on hypermedia APIs with Rails, located here. I created an example app that’s on github. My favorite part about this talk was at the end when I attempted to change the routes the app serves up and see if my hal client still worked. It passed with flying colors.
I used the frenetic gem to create my hal client. It’s pretty verbose currently and let’s just gloss over all those periods. It’s a nice starting point for a HAL api.
require 'frenetic'
MyAPI = Frenetic.new({
'url' => 'http://hypermedia.dev',
'username' => 'qxpRbQpqAw3YugKUpErW',
'password' => 'qxpRbQpqAw3YugKUpErW',
'headers' => {
'accept' => 'application/hal+json'
}
})
class Order < Frenetic::Resource
api_client { MyAPI }
def self.orders
api.get(api.description.links.orders.href).body.resources.orders.map do |order|
new(order)
end
end
end
p Order.orders
Resources
Posted on 05 Sep 2012
Yesterday I tried out webmachine-ruby. Overall it left a good impression on me. One thing I really liked was the separation of a single resource and a collection resource.
App = Webmachine::Application.new do |app|
app.routes do
add ["orders"], OrdersResource
add ["orders", :id], OrderResource
end
end
Any call to “/orders” will go to the OrdersResource, since you’re acting on a collection, and any call with an “id” in it will go to the single resource.
I also really liked the idea of callbacks on a resource. To point out one, in order to get a resource to respond to a certain method, you add it to the #allowed_methods
:
class OrderResource
def allowed_methods
["GET", "POST"]
end
def content_types_provided
[["application/json", :to_json]]
end
end
The #content_types_provided
callback will call #to_json
if it sees “application/json”.
One pretty big issue I faced was a lack of examples on how to use its statemachine. The example apps listed only do GET
and POST
requests but there wasn’t an example on how do DELETE
or PUT
something. Luckily I had access to someone who has used it before and could help me with the issues I came across. I’m hoping this example app can be of use to someone else looking into WebMachine.
My resulting example app is here as a gist and I’ve embedded the main app file.
require 'webmachine'
require 'webmachine/adapters/rack'
require 'json'
class Order
attr_accessor :id, :email, :date
DB = {}
def self.all
DB.values
end
def self.find(id)
DB[id]
end
def self.next_id
DB.keys.max.to_i + 1
end
def self.delete_all
DB.clear
end
def to_json(options = {})
"{\"email\":\"#{@email}\", \"date\":\"#{@date}\", \"id\":#{@id}"
end
def initialize(attrs = {})
attrs.each do |attr, value|
send("#{attr}=", value) if respond_to?(attr)
end
end
def save(id = nil)
self.id = id || self.class.next_id
DB[self.id] = self
end
def destroy
DB.delete(id)
end
end
class JsonResource < Webmachine::Resource
def content_types_provided
[["application/json", :to_json]]
end
def content_types_accepted
[["application/json", :from_json]]
end
private
def params
JSON.parse(request.body.to_s)
end
end
class OrdersResource < JsonResource
def allowed_methods
["GET", "POST"]
end
def to_json
{
:orders => Order.all
}.to_json
end
def create_path
@id = Order.next_id
"/orders/#@id"
end
def post_is_create?
true
end
private
def from_json
order = Order.new(params).save(@id)
end
end
class OrderResource < JsonResource
def allowed_methods
["GET", "DELETE", "PUT"]
end
def id
request.path_info[:id].to_i
end
def delete_resource
Order.find(id).destroy
true
end
def to_json
order = Order.find(id)
order.to_json
end
private
def from_json
order = Order.new(params)
order.save(id)
end
end
App = Webmachine::Application.new do |app|
app.routes do
add ["orders"], OrdersResource
add ["orders", :id], OrderResource
add ['trace', '*'], Webmachine::Trace::TraceResource
end
end
Posted on 29 Aug 2012
I recently converted my blog over to Jekyll, mainly to not have MySQL running. One thing I did though was set up git deployment. I mostly followed the Jekyll guide, but I different when setting up the jekyll gem.
The starting post-receive from Jekyll Deployment Guide.
hooks/post-receive (original)
GIT_REPO=$HOME/myrepo.git
TMP_GIT_CLONE=$HOME/tmp/myrepo
PUBLIC_WWW=/var/www/myrepo
git clone $GIT_REPO $TMP_GIT_CLONE
jekyll --no-auto $TMP_GIT_CLONE $PUBLIC_WWW
rm -Rf $TMP_GIT_CLONE
exit
I did bundler install --deployment
so that it would be in the local directory. The problem with this is that jekyll then picks up the test posts in the gem as your posts. Now I had a ton of bad posts on my blog. (You may have noticed them in the feed.)
I also set it up so that it doesn’t blow away the repo (and the gems) every time I push up a new post.
hooks/post-receive (final)
GIT_REPO=$HOME/repos/blog.oestrich.org
TMP_GIT_CLONE=$HOME/tmp/blog.oestrich.org
PUBLIC_WWW=$HOME/vhosts/blog.oestrich.org
unset GIT_DIR
if [ -d $TMP_GIT_CLONE ]; then
cd $TMP_GIT_CLONE
git pull origin master
else
git clone $GIT_REPO $TMP_GIT_CLONE
cd $TMP_GIT_CLONE
fi
bundle install --deployment
# Remove the sample jekyll site from the test suite
rm -rf $TMP_GIT_CLONE/vendor/bundle/ruby/1.9.1/gems/jekyll-0.11.2/test/source
bundle exec jekyll --no-auto $TMP_GIT_CLONE $PUBLIC_WWW
exit