SlideShare a Scribd company logo
Finding Restfulness
Sam Lown, April 2014
Who am I?
• CTO and co-founder of Cabify.
• Freelancer for many years before.
• Obsessed with finding simple, elegant solutions
to problems.
What am I going to
talk about?
I’m not going to talk about
creating REST APIs in Rails
(although there is some mixed in)
For that, checkout
Javier Ramirez’s talks:
Google: “Javier Ramirez REST”
I’m going show you
A brand new way of
creating REST APIs in
Ruby!
REST is about Resources
that re-use as much from
HTTP as possible.
and just in case, Sam’s summary of REST:
Once upon a time…
“something was
irritating Sam”
Rails Controllers
Specifically:
They started to feel

out of place

with the problems I was solving.
Take the following
basic controller…
class PostController < ApplicationController!
respond_to :html, :json!
!
def index!
@posts = Post.all!
respond_with(@posts)!
end!
!
def show!
@post = Post.find(params[:id])!
respond_with(@post)!
end!
!
def new!
@post = Post.new!
respond_with(@post)!
end!
!
def create!
@post = Post.create(params[:post])!
respond_with(@post)!
end!
!
def edit!
@post = Post.find(params[:id])!
end!
!
def update!
@post = Post.find(params[:id])!
respond_with(@post)!
end!
!
def delete!
@post = Post.find(params[:id])!
@post.destroy!
respond_with(@post)!
end!
end
posts_controller.rb:
routes.rb:
PostApp::Application.routes.draw do!
resources :posts!
end
1st irritant: too many methods
!
I only need JSON!"
!
(easy to fix…)
class PostController < ApplicationController!
respond_to :json!
!
def index!
@posts = Post.all!
respond_with(@posts)!
end!
!
def show!
@post = Post.find(params[:id])!
respond_with(@post)!
end!
!
def create!
@post = Post.create(params[:post])!
respond_with(@post)!
end!
!
def update!
@post = Post.find(params[:id])!
respond_with(@post)!
end!
!
def delete!
@post = Post.find(params[:id])!
@post.destroy!
respond_with(@post)!
end!
end
posts_controller.rb:
routes.rb:
PostApp::Application.routes.draw do!
resources :posts!
end
2nd irritant:

adding sub-actions
class PostController < ApplicationController!
respond_to :json!
!
def index!
@posts = Post.all!
respond_with(@posts)!
end!
!
def show!
@post = Post.find(params[:id])!
respond_with(@post)!
end!
!
def create!
@post = Post.create(params[:post])!
respond_with(@post)!
end!
!
def update!
@post = Post.find(params[:id])!
respond_with(@post)!
end!
!
def latest!
@posts = Post.order_by(:created_at)!
respond_with(@posts)!
end!
!
def last!
@post = Post.last!
respond_with(@post)!
end!
!
def delete!
@post = Post.find(params[:id])!
@post.destroy!
respond_with(@post)!
end!
end
posts_controller.rb:
routes.rb:
PostApp::Application.routes.draw do!
resources :posts do!
member do!
get :last!
end!
collection do!
get :latest!
end!
end!
end
Always felt a bit

“stuck on”
3rd irritant:

parameter namespaces
class Post extends Backbone.Model!
!
paramRoot: ‘post’!
!
url: ->!
“/posts/” + @id!
!
parse: (res) ->!
if res.post?!
res.post!
else!
res
Coffeescript Model:
{!
“post”: {!
“title”: “Example post”,!
“created_at”: “2014-04-21T16:10:00Z”!
}!
}
JSON from server:
Backbone expects urls to

return data directly!
4th irritant:

query, path, and params,
all mixed up
Request:

PUT /posts/1234?draft=false



Body:!
{!
“post”: { …. }!
}!
!
Action:"
def update!
@post = Post.find(params[:id])!
unless params[:draft]!
@post.update_attributes(params[:post])!
end!
respond_with(@post)!
end

Different data, with"
different meanings,"
all in the same place?
5th irritant:

collection and element

confusion!
(and probably the most irritating)
Take the Wikipedia REST Page:
Resource GET PUT POST DELETE
Collection URI

/resources
List
collection
Replace
collection
Create

new

element
Delete
collection
Element

URI

/resources/id
Retrieve
element
Replace
element
not used
Delete
element
Seriously, what?
Resource GET PUT POST DELETE
Collection URI

/resources
List
collection
Replace
collection
Create

new

element
Delete
collection
Element

URI

/resources/id
Retrieve
element
Replace
element
not used
Delete
element
Root of irritations:
Rails controllers were
designed for web views,
not APIs!
Are there any other ways
of creating APIs in Ruby?
So, I thought to myself,
Google: “Rails API”
gem install rails-api
Rails API
“Rails::API is a subset of a normal Rails
application, created for applications
that don't require all functionality that a
complete Rails application provides.”
https://github.com/rails-api/rails-api
Rails without views.
Translation:
Cool, buy not really what we’re looking for.
Grape
“Grape allows you to build lightweight
APIs with Ruby when you don't need the
heavy lifting power of large frameworks
like Rails. It's a REST-like API micro-
framework built to complement existing
web application frameworks by providing
a simple DSL to easily provide APIs.”
http://intridea.github.io/grape/
But whats that about
DSLs and REST-like?
sounds okay…
we need a code sample…
module Twitter!
class API < Grape::API!
!
version 'v1', using: :header, vendor: 'twitter'!
format :json!
!
helpers do!
def current_user!
@current_user ||= User.authorize!(env)!
end!
!
def authenticate!!
error!('401 Unauthorized', 401) unless current_user!
end!
end!
!
resource :statuses do!
!
desc "Return a public timeline."!
get :public_timeline do!
Status.limit(20)!
end!
!
desc "Return a personal timeline."!
get :home_timeline do!
authenticate!!
current_user.statuses.limit(20)!
end!
!
desc "Return a status."!
params do!
requires :id, type: Integer, desc: "Status id."!
end!
route_param :id do!
get do!
Status.find(params[:id])!
end!
end!
!
desc "Create a status."!
params do!
requires :status, type: String, desc: "Your status."!
end!
post do!
authenticate!!
Status.create!({!
user: current_user,!
text: params[:status]!
})!
end!
!
desc "Update a status."!
Crap, it doesn’t fit on a
single slide…
Probably because it tries
to do everything in a
single Ruby file.
Not a good start.
Let the nitpicking feature
identification start…
module Twitter!
class API < Grape::API!
!
version 'v1', using: :header, vendor: 'twitter'!
format :json!
!
helpers do!
def current_user!
@current_user ||= User.authorize!(env)!
end!
!
def authenticate!!
error!('401 Unauthorized', 401) unless current_user!
end!
end!
Versioning
Helpers
Error handling
resource :statuses do!
!
desc "Return a public timeline."!
get :public_timeline do!
Status.limit(20)!
end!
!
desc "Return a status."!
params do!
requires :id, type: Integer, desc: "Status id."!
end!
route_param :id do!
get do!
Status.find(params[:id])!
end!
end
Resources, kind of
Strange rack like

descriptions
Parameter filtering
Path elements?
HTTP

Method
desc "Update a status."!
params do!
requires :id, type: String, desc: "Status ID."!
requires :status, type: String, desc: "Your status."!
end!
put ':id' do!
authenticate!!
current_user.statuses.find(params[:id]).update({!
user: current_user,!
text: params[:status]!
})!
end!
desc "Delete a status."!
params do!
requires :id, type: String, desc: "Status ID."!
end!
delete ':id' do!
authenticate!!
current_user.statuses.find(params[:id]).destroy!
end!
returned object

converted to JSON
more parameter filtering
wtf? more parameter filtering!
Grape does some
cool stuff!
don’t get me wrong
Sam isn’t feeling the
love.
Google
Google
Google
Google
(can’t actually remember what)
webmachine for Ruby
gem install webmachine
“webmachine-ruby is a port of Webmachine,
which is written in Erlang. The goal of both
projects is to expose interesting parts of the
HTTP protocol to your application in a
declarative way. This means you are less
concerned with handling requests directly and
more with describing the behaviour of the
resources that make up your application.”
https://github.com/seancribbs/webmachine-ruby
Okay, this sounds
more like it!
Sam likes things that
are totally different!
let’s have a look at the code
require 'webmachine'!
class MyResource < Webmachine::Resource!
def to_html!
"<html><body>Hello, world!</body></html>"!
end!
end!
!
# Start a web server on localhost!
MyResource.run
Interesting, “Resource”
Getting started:
Webmachine.application.routes do!
add ['myresource'], MyResource!
end!
!
# Start a web server on localhost!
Webmachine.application.run
Simple routing to specific resources!
Routing:
Some Cool HTTP tracing thing!
HTTP features are exposed via
callbacks:
Webmachine::Resource::Callbacks
After this point examples
started to become a bit
scarce…
A few Resource callbacks:
is_authorized?

forbidden?

is_conflict?!
allow_missing_post?!
content_types_accepted!
last_modified!
generate_etag!
languages_provided
etc. etc.
Callbacks in action:
require 'webmachine'!
class MyResource < Webmachine::Resource!
!
def is_authorized?!
request.query[‘name’] == ‘sam’!
end!
!
def to_html!
"<html><body>Hello, world!</body></html>"!
end!
(Disclaimer, I’m not actually sure if the

request query bit is correct)
To be honest, it all
looked a bit confusing.
But I tried it anyway.
Sam’s new theory:
“If you can’t get it to do
anything useful within a day,
its too freeking complicated”
FAIL
But maybe I can do
something about this…
I know I want
simplicity.
I like the idea of

resources

being the focus of requests.
I want

simple routing.
I want my query, path,
and body parameters

separate.
I like the funky
callback stuff.
I want to be as close to

HTTP and REST

as possible.
I still thought I could do
something about this…
Restfulness
and thus was born
“Restfulness is a Ruby library that helps
create REST based APIs to your
services. The focus is placed on
performing HTTP actions on
resources via specific routes, as
opposed to assigning routes and HTTP
actions to methods or blocks of code”
https://github.com/samlown/restfulness
Lets get started.
Routes
class MyAPI < Restfullness::Application!
routes do!
scope 'api' do!
add 'post', Posts::ItemResource!
add ‘posts', Posts::ListResource do!
add ‘favourite', Posts::FavResource!
end!
end!
end!
end
Routes build paths"
that point to Resources!
Paths recognised from routes
/api/post

/api/post/1234!
/api/posts!
/api/posts/1234!
/api/posts/favourite!
/api/posts/favourite/1234
Convention: each path allows"
optional ID on end!
add 'post', Posts::ItemResource do!
add :post_id, ‘comments’,!
Posts::CommentsResource
end!
Symbols in routes catch anything!
Interpolation
/api/post

/api/post/1234!
/api/post/1234/comments!
/api/post/1234/comments/1234
Route Rules
• Route entries form paths when joined with ‘/‘
• Order is important.
• Strings are matched.
• Symbols match anything, and are accessible
as path attributes.
• Every path gets an :id parameter at the end,
that may or may not have a null value.
• Scopes avoid repeating path entries.
And thats it.
Routing in Restfulness
is incredibly simple
Resources
class ProjectResource < Restfulness::Resource!
!
def get!
project!
end!
!
def patch!
project.update(request.params)!
end!
!
protected!
!
def project!
@project ||= Project.find(request.path[:id])!
end!
!
end
HTTP methods
The object returned is converted to JSON
def get!
project!
end!
# Somewhere inside Restfulness!
result = resource.send(method)!
response.body =

MultJson.dump(result)
Resource Methods
get!
head

post!
patch!
put!
delete!
options
Method not defined?

Don’t expect a response!
curl -v —X OPTIONS https://cabify.com/api/regions!


< HTTP/1.1 204 No Content

< Allow: GET, OPTIONS!
!
!
curl -v —X POST https://cabify.com/api/regions!


< HTTP/1.1 405 Method Not Allowed
Request Object
request.uri # URI object!
request.headers # Hash, Symbol => Val!
!
request.path.to_s # '/project/123456'!
request.path # ['project', '123456']!
request.path[:id] # '123456'!
request.path[0] # ‘project'!
!
request.query # {:page => 1}!
request.query[:page] # 1!
!
request.body # "{'key':'value'}"!
!
request.params # {'key' => 'value'}
Response Object
def get!
if project.is_a_teapot?!
response.status = 418!
response.headers[‘X-Tea-Pot’] = ‘true’!
end!
project!
end
Resource Callbacks
class ProjectResource < Restfulness::Resource!
!
def exists?!
!project.nil?!
end!
!
def last_modified!
project.updated_at.to_s!
end!
!
def get!
project!
end



………!
Callbacks return booleans,

or values to return to client.
Callbacks control flow.
!
If a boolean callback returns false,
an error is raised and instantly
stops execution of the Resource!
(error handling coming up!)
Supported callbacks
exists?!
authorized?

allowed?!
last_modified!
etag
(never needed more!)
Errors and status
codes
class ProjectResource < ApplicationResource!
!
def patch!
unless project.update_attributes(request.params)!
forbidden!(project)!
end!
project!
end!
!
end!
Fail?

Halt and provide 403 response
Supported responses:
not_modified!!
bad_request!

unauthorized!!
forbidden!!
resource_not_found!!
request_timeout!!
conflict!!
gone!!
unprocessable_entity!
Your favourite error method not there?
def im_a_teapot!(payload = “”)!
error!(418, payload)!
end
Add it!
Fine tuning error
messages
class ApplicationResource < Restfulness::Resource!
!
def error!(status, payload = "", opts = {})!
case payload!
when ActiveRecord::Base!
payload = {!
:errors => payload.errors.full_messages!
}!
end!
super(status, payload, opts)!
end!
end!
Yes! Wrap, and provide all
errors in the same format!
Running Restfulness
Restfulness runs on Rack,
so you can easily plug it
into existing projects (Rails)
or run it on its own (Sinatra)
Standalone
$ vi config.ru!
!
require 'my_app'!
run MyApp.new!
!
$ rackup
Or as part of a Rails project
# In Rails application.rb!
config.autoload_paths += [!
“#{config.root}/app/resources”,

“#{config.root}/app/apis”!
]!
!
# In Rails Routes!
YourRailsApp::Application.routes.draw do !
mount MyApi.new => '/api'!
…!
This way you get auto-reloading
I hope you too find

Restfulness

:-)
that’s about all there is to it!
https://github.com/samlown/restfulness
?
(for 10€ off you next Cabify ride: “MADRIDRB”)

More Related Content

PDF
Testing web APIs
PDF
Master the New Core of Drupal 8 Now: with Symfony and Silex
PDF
Associations & JavaScript
PDF
Don't Ignore Your Errors!
KEY
Enter the app era with ruby on rails (rubyday)
PDF
Rails for Beginners - Le Wagon
PPTX
The JSON REST API for WordPress
PDF
Building Better Web APIs with Rails
Testing web APIs
Master the New Core of Drupal 8 Now: with Symfony and Silex
Associations & JavaScript
Don't Ignore Your Errors!
Enter the app era with ruby on rails (rubyday)
Rails for Beginners - Le Wagon
The JSON REST API for WordPress
Building Better Web APIs with Rails

What's hot (20)

PDF
Rails 4.0
PDF
Enter the app era with ruby on rails
PDF
Stackup New Languages Talk: Ember is for Everybody
PDF
Ingo Muschenetz: Titanium Studio Deep Dive
PPT
First app
KEY
Rails vu d'un Javaiste
PDF
Introduction à Ruby
PDF
JSON REST API for WordPress
PDF
Turn your spaghetti code into ravioli with JavaScript modules
PDF
Create responsive websites with Django, REST and AngularJS
PDF
Alfresco tech talk live public api episode 64
PDF
Gem That (2009)
PDF
When To Use Ruby On Rails
PDF
OpenERP and Perl
PPT
What you can do In WatiR
PDF
Django Heresies
PDF
ApacheCon 2005
PPTX
django Forms in a Web API World
PDF
How we improved performance at Mixbook
PDF
The Dark Art of Rails Plugins (2008)
Rails 4.0
Enter the app era with ruby on rails
Stackup New Languages Talk: Ember is for Everybody
Ingo Muschenetz: Titanium Studio Deep Dive
First app
Rails vu d'un Javaiste
Introduction à Ruby
JSON REST API for WordPress
Turn your spaghetti code into ravioli with JavaScript modules
Create responsive websites with Django, REST and AngularJS
Alfresco tech talk live public api episode 64
Gem That (2009)
When To Use Ruby On Rails
OpenERP and Perl
What you can do In WatiR
Django Heresies
ApacheCon 2005
django Forms in a Web API World
How we improved performance at Mixbook
The Dark Art of Rails Plugins (2008)
Ad

Viewers also liked (20)

PPTX
Kunden din er digital - er din bedrift?
ODT
Projecte educactiu de qualitat 23 04-2014
PPTX
Presentation1
PPT
The Rainbow fish story. How else could Rainbow fish make friends ?
PPTX
CxO Academy - Voiko minustakin tulla karismaattisempi
PPTX
Inscibe and Squeeze
PPTX
Huippumyyjän profiili - Haaga-Helian myynnin koulutusohjelma
PPTX
Johtajuus, Karisma, Puhetaito - JCI Koli 19.3.2016
PPTX
Yhdistystoiminta kriisissä 2014 11-20
PPTX
Paras neuvo johtamiseen ja johtajan muotokuva aka Hinkan reikäleipä
PDF
Node detection technique for node replication attack in mobile sensor network
PPTX
Johtajuus, Karisma, Puhetaito
PPTX
Huomisen huippujohtaja
PPTX
Oracle sunu
DOC
ahamd ezz eng cv 2015
PPTX
Moto SATNAV
PPTX
Patrycja Snopek
PPTX
Existentialist
PPTX
Competitive Financing from Norway
PPTX
Giek innovasjon, Norge Telemark
Kunden din er digital - er din bedrift?
Projecte educactiu de qualitat 23 04-2014
Presentation1
The Rainbow fish story. How else could Rainbow fish make friends ?
CxO Academy - Voiko minustakin tulla karismaattisempi
Inscibe and Squeeze
Huippumyyjän profiili - Haaga-Helian myynnin koulutusohjelma
Johtajuus, Karisma, Puhetaito - JCI Koli 19.3.2016
Yhdistystoiminta kriisissä 2014 11-20
Paras neuvo johtamiseen ja johtajan muotokuva aka Hinkan reikäleipä
Node detection technique for node replication attack in mobile sensor network
Johtajuus, Karisma, Puhetaito
Huomisen huippujohtaja
Oracle sunu
ahamd ezz eng cv 2015
Moto SATNAV
Patrycja Snopek
Existentialist
Competitive Financing from Norway
Giek innovasjon, Norge Telemark
Ad

Similar to Finding Restfulness - Madrid.rb April 2014 (20)

PDF
JSON REST API for WordPress
PPTX
REST APIs in Laravel 101
KEY
Supa fast Ruby + Rails
PDF
Introduction to Ruby on Rails
PDF
AtlasCamp2014: Introducing the Confluence REST API
PDF
APIs REST Usables con Hypermedia por Javier Ramirez, para codemotion
PDF
Bullet: The Functional PHP Micro-Framework
PDF
Pourquoi ruby et rails déchirent
PDF
Velocity EU 2014 — Offline-first web apps
PDF
Beyond MVC: from Model to Domain
PDF
MVS: An angular MVC
PPTX
Graphql + Symfony | Александр Демченко | CODEiD
PDF
Symfony + GraphQL
PDF
Be happy with Ruby on Rails - CEUNSP Itu
PDF
Building web framework with Rack
KEY
WordPress APIs
PDF
Ruby MVC from scratch with Rack
KEY
Ruby on Rails survival guide of an aged Java developer
PDF
Build REST API clients for AngularJS
JSON REST API for WordPress
REST APIs in Laravel 101
Supa fast Ruby + Rails
Introduction to Ruby on Rails
AtlasCamp2014: Introducing the Confluence REST API
APIs REST Usables con Hypermedia por Javier Ramirez, para codemotion
Bullet: The Functional PHP Micro-Framework
Pourquoi ruby et rails déchirent
Velocity EU 2014 — Offline-first web apps
Beyond MVC: from Model to Domain
MVS: An angular MVC
Graphql + Symfony | Александр Демченко | CODEiD
Symfony + GraphQL
Be happy with Ruby on Rails - CEUNSP Itu
Building web framework with Rack
WordPress APIs
Ruby MVC from scratch with Rack
Ruby on Rails survival guide of an aged Java developer
Build REST API clients for AngularJS

Recently uploaded (20)

PPTX
Advanced SystemCare Ultimate Crack + Portable (2025)
PPTX
AMADEUS TRAVEL AGENT SOFTWARE | AMADEUS TICKETING SYSTEM
PDF
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
PDF
17 Powerful Integrations Your Next-Gen MLM Software Needs
PDF
Tally Prime Crack Download New Version 5.1 [2025] (License Key Free
PDF
Complete Guide to Website Development in Malaysia for SMEs
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PDF
Autodesk AutoCAD Crack Free Download 2025
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PDF
Design an Analysis of Algorithms I-SECS-1021-03
PDF
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
PDF
medical staffing services at VALiNTRY
PDF
iTop VPN 6.5.0 Crack + License Key 2025 (Premium Version)
PDF
Digital Systems & Binary Numbers (comprehensive )
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PPTX
L1 - Introduction to python Backend.pptx
PPTX
Operating system designcfffgfgggggggvggggggggg
PPTX
CHAPTER 2 - PM Management and IT Context
PDF
Navsoft: AI-Powered Business Solutions & Custom Software Development
Advanced SystemCare Ultimate Crack + Portable (2025)
AMADEUS TRAVEL AGENT SOFTWARE | AMADEUS TICKETING SYSTEM
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
17 Powerful Integrations Your Next-Gen MLM Software Needs
Tally Prime Crack Download New Version 5.1 [2025] (License Key Free
Complete Guide to Website Development in Malaysia for SMEs
Adobe Illustrator 28.6 Crack My Vision of Vector Design
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
Autodesk AutoCAD Crack Free Download 2025
Wondershare Filmora 15 Crack With Activation Key [2025
Design an Analysis of Algorithms I-SECS-1021-03
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
medical staffing services at VALiNTRY
iTop VPN 6.5.0 Crack + License Key 2025 (Premium Version)
Digital Systems & Binary Numbers (comprehensive )
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
L1 - Introduction to python Backend.pptx
Operating system designcfffgfgggggggvggggggggg
CHAPTER 2 - PM Management and IT Context
Navsoft: AI-Powered Business Solutions & Custom Software Development

Finding Restfulness - Madrid.rb April 2014

  • 2. Who am I? • CTO and co-founder of Cabify. • Freelancer for many years before. • Obsessed with finding simple, elegant solutions to problems.
  • 3. What am I going to talk about?
  • 4. I’m not going to talk about creating REST APIs in Rails (although there is some mixed in)
  • 5. For that, checkout Javier Ramirez’s talks: Google: “Javier Ramirez REST”
  • 6. I’m going show you A brand new way of creating REST APIs in Ruby!
  • 7. REST is about Resources that re-use as much from HTTP as possible. and just in case, Sam’s summary of REST:
  • 8. Once upon a time…
  • 11. They started to feel
 out of place
 with the problems I was solving.
  • 12. Take the following basic controller…
  • 13. class PostController < ApplicationController! respond_to :html, :json! ! def index! @posts = Post.all! respond_with(@posts)! end! ! def show! @post = Post.find(params[:id])! respond_with(@post)! end! ! def new! @post = Post.new! respond_with(@post)! end! ! def create! @post = Post.create(params[:post])! respond_with(@post)! end! ! def edit! @post = Post.find(params[:id])! end! ! def update! @post = Post.find(params[:id])! respond_with(@post)! end! ! def delete! @post = Post.find(params[:id])! @post.destroy! respond_with(@post)! end! end posts_controller.rb: routes.rb: PostApp::Application.routes.draw do! resources :posts! end
  • 14. 1st irritant: too many methods ! I only need JSON!" ! (easy to fix…)
  • 15. class PostController < ApplicationController! respond_to :json! ! def index! @posts = Post.all! respond_with(@posts)! end! ! def show! @post = Post.find(params[:id])! respond_with(@post)! end! ! def create! @post = Post.create(params[:post])! respond_with(@post)! end! ! def update! @post = Post.find(params[:id])! respond_with(@post)! end! ! def delete! @post = Post.find(params[:id])! @post.destroy! respond_with(@post)! end! end posts_controller.rb: routes.rb: PostApp::Application.routes.draw do! resources :posts! end
  • 17. class PostController < ApplicationController! respond_to :json! ! def index! @posts = Post.all! respond_with(@posts)! end! ! def show! @post = Post.find(params[:id])! respond_with(@post)! end! ! def create! @post = Post.create(params[:post])! respond_with(@post)! end! ! def update! @post = Post.find(params[:id])! respond_with(@post)! end! ! def latest! @posts = Post.order_by(:created_at)! respond_with(@posts)! end! ! def last! @post = Post.last! respond_with(@post)! end! ! def delete! @post = Post.find(params[:id])! @post.destroy! respond_with(@post)! end! end posts_controller.rb: routes.rb: PostApp::Application.routes.draw do! resources :posts do! member do! get :last! end! collection do! get :latest! end! end! end Always felt a bit
 “stuck on”
  • 19. class Post extends Backbone.Model! ! paramRoot: ‘post’! ! url: ->! “/posts/” + @id! ! parse: (res) ->! if res.post?! res.post! else! res Coffeescript Model: {! “post”: {! “title”: “Example post”,! “created_at”: “2014-04-21T16:10:00Z”! }! } JSON from server: Backbone expects urls to
 return data directly!
  • 20. 4th irritant:
 query, path, and params, all mixed up
  • 21. Request:
 PUT /posts/1234?draft=false
 
 Body:! {! “post”: { …. }! }! ! Action:" def update! @post = Post.find(params[:id])! unless params[:draft]! @post.update_attributes(params[:post])! end! respond_with(@post)! end
 Different data, with" different meanings," all in the same place?
  • 22. 5th irritant:
 collection and element
 confusion! (and probably the most irritating)
  • 23. Take the Wikipedia REST Page: Resource GET PUT POST DELETE Collection URI
 /resources List collection Replace collection Create
 new
 element Delete collection Element
 URI
 /resources/id Retrieve element Replace element not used Delete element
  • 24. Seriously, what? Resource GET PUT POST DELETE Collection URI
 /resources List collection Replace collection Create
 new
 element Delete collection Element
 URI
 /resources/id Retrieve element Replace element not used Delete element
  • 25. Root of irritations: Rails controllers were designed for web views, not APIs!
  • 26. Are there any other ways of creating APIs in Ruby? So, I thought to myself,
  • 29. “Rails::API is a subset of a normal Rails application, created for applications that don't require all functionality that a complete Rails application provides.” https://github.com/rails-api/rails-api
  • 30. Rails without views. Translation: Cool, buy not really what we’re looking for.
  • 31. Grape
  • 32. “Grape allows you to build lightweight APIs with Ruby when you don't need the heavy lifting power of large frameworks like Rails. It's a REST-like API micro- framework built to complement existing web application frameworks by providing a simple DSL to easily provide APIs.” http://intridea.github.io/grape/
  • 33. But whats that about DSLs and REST-like? sounds okay… we need a code sample…
  • 34. module Twitter! class API < Grape::API! ! version 'v1', using: :header, vendor: 'twitter'! format :json! ! helpers do! def current_user! @current_user ||= User.authorize!(env)! end! ! def authenticate!! error!('401 Unauthorized', 401) unless current_user! end! end! ! resource :statuses do! ! desc "Return a public timeline."! get :public_timeline do! Status.limit(20)! end! ! desc "Return a personal timeline."! get :home_timeline do! authenticate!! current_user.statuses.limit(20)! end! ! desc "Return a status."! params do! requires :id, type: Integer, desc: "Status id."! end! route_param :id do! get do! Status.find(params[:id])! end! end! ! desc "Create a status."! params do! requires :status, type: String, desc: "Your status."! end! post do! authenticate!! Status.create!({! user: current_user,! text: params[:status]! })! end! ! desc "Update a status."!
  • 35. Crap, it doesn’t fit on a single slide…
  • 36. Probably because it tries to do everything in a single Ruby file. Not a good start.
  • 37. Let the nitpicking feature identification start…
  • 38. module Twitter! class API < Grape::API! ! version 'v1', using: :header, vendor: 'twitter'! format :json! ! helpers do! def current_user! @current_user ||= User.authorize!(env)! end! ! def authenticate!! error!('401 Unauthorized', 401) unless current_user! end! end! Versioning Helpers Error handling
  • 39. resource :statuses do! ! desc "Return a public timeline."! get :public_timeline do! Status.limit(20)! end! ! desc "Return a status."! params do! requires :id, type: Integer, desc: "Status id."! end! route_param :id do! get do! Status.find(params[:id])! end! end Resources, kind of Strange rack like
 descriptions Parameter filtering Path elements? HTTP
 Method
  • 40. desc "Update a status."! params do! requires :id, type: String, desc: "Status ID."! requires :status, type: String, desc: "Your status."! end! put ':id' do! authenticate!! current_user.statuses.find(params[:id]).update({! user: current_user,! text: params[:status]! })! end! desc "Delete a status."! params do! requires :id, type: String, desc: "Status ID."! end! delete ':id' do! authenticate!! current_user.statuses.find(params[:id]).destroy! end! returned object
 converted to JSON more parameter filtering wtf? more parameter filtering!
  • 41. Grape does some cool stuff! don’t get me wrong
  • 42. Sam isn’t feeling the love.
  • 44. webmachine for Ruby gem install webmachine
  • 45. “webmachine-ruby is a port of Webmachine, which is written in Erlang. The goal of both projects is to expose interesting parts of the HTTP protocol to your application in a declarative way. This means you are less concerned with handling requests directly and more with describing the behaviour of the resources that make up your application.” https://github.com/seancribbs/webmachine-ruby
  • 47. Sam likes things that are totally different! let’s have a look at the code
  • 48. require 'webmachine'! class MyResource < Webmachine::Resource! def to_html! "<html><body>Hello, world!</body></html>"! end! end! ! # Start a web server on localhost! MyResource.run Interesting, “Resource” Getting started:
  • 49. Webmachine.application.routes do! add ['myresource'], MyResource! end! ! # Start a web server on localhost! Webmachine.application.run Simple routing to specific resources! Routing:
  • 50. Some Cool HTTP tracing thing!
  • 51. HTTP features are exposed via callbacks: Webmachine::Resource::Callbacks
  • 52. After this point examples started to become a bit scarce…
  • 53. A few Resource callbacks: is_authorized?
 forbidden?
 is_conflict?! allow_missing_post?! content_types_accepted! last_modified! generate_etag! languages_provided etc. etc.
  • 54. Callbacks in action: require 'webmachine'! class MyResource < Webmachine::Resource! ! def is_authorized?! request.query[‘name’] == ‘sam’! end! ! def to_html! "<html><body>Hello, world!</body></html>"! end! (Disclaimer, I’m not actually sure if the
 request query bit is correct)
  • 55. To be honest, it all looked a bit confusing.
  • 56. But I tried it anyway.
  • 57. Sam’s new theory: “If you can’t get it to do anything useful within a day, its too freeking complicated”
  • 58. FAIL
  • 59. But maybe I can do something about this…
  • 60. I know I want simplicity.
  • 61. I like the idea of
 resources
 being the focus of requests.
  • 63. I want my query, path, and body parameters
 separate.
  • 64. I like the funky callback stuff.
  • 65. I want to be as close to
 HTTP and REST
 as possible.
  • 66. I still thought I could do something about this…
  • 68. “Restfulness is a Ruby library that helps create REST based APIs to your services. The focus is placed on performing HTTP actions on resources via specific routes, as opposed to assigning routes and HTTP actions to methods or blocks of code” https://github.com/samlown/restfulness
  • 71. class MyAPI < Restfullness::Application! routes do! scope 'api' do! add 'post', Posts::ItemResource! add ‘posts', Posts::ListResource do! add ‘favourite', Posts::FavResource! end! end! end! end Routes build paths" that point to Resources!
  • 72. Paths recognised from routes /api/post
 /api/post/1234! /api/posts! /api/posts/1234! /api/posts/favourite! /api/posts/favourite/1234 Convention: each path allows" optional ID on end!
  • 73. add 'post', Posts::ItemResource do! add :post_id, ‘comments’,! Posts::CommentsResource end! Symbols in routes catch anything! Interpolation /api/post
 /api/post/1234! /api/post/1234/comments! /api/post/1234/comments/1234
  • 74. Route Rules • Route entries form paths when joined with ‘/‘ • Order is important. • Strings are matched. • Symbols match anything, and are accessible as path attributes. • Every path gets an :id parameter at the end, that may or may not have a null value. • Scopes avoid repeating path entries.
  • 75. And thats it. Routing in Restfulness is incredibly simple
  • 77. class ProjectResource < Restfulness::Resource! ! def get! project! end! ! def patch! project.update(request.params)! end! ! protected! ! def project! @project ||= Project.find(request.path[:id])! end! ! end HTTP methods
  • 78. The object returned is converted to JSON def get! project! end! # Somewhere inside Restfulness! result = resource.send(method)! response.body =
 MultJson.dump(result)
  • 80. Method not defined?
 Don’t expect a response! curl -v —X OPTIONS https://cabify.com/api/regions! 
 < HTTP/1.1 204 No Content
 < Allow: GET, OPTIONS! ! ! curl -v —X POST https://cabify.com/api/regions! 
 < HTTP/1.1 405 Method Not Allowed
  • 82. request.uri # URI object! request.headers # Hash, Symbol => Val! ! request.path.to_s # '/project/123456'! request.path # ['project', '123456']! request.path[:id] # '123456'! request.path[0] # ‘project'! ! request.query # {:page => 1}! request.query[:page] # 1! ! request.body # "{'key':'value'}"! ! request.params # {'key' => 'value'}
  • 84. def get! if project.is_a_teapot?! response.status = 418! response.headers[‘X-Tea-Pot’] = ‘true’! end! project! end
  • 86. class ProjectResource < Restfulness::Resource! ! def exists?! !project.nil?! end! ! def last_modified! project.updated_at.to_s! end! ! def get! project! end
 
 ………! Callbacks return booleans,
 or values to return to client.
  • 87. Callbacks control flow. ! If a boolean callback returns false, an error is raised and instantly stops execution of the Resource! (error handling coming up!)
  • 90. class ProjectResource < ApplicationResource! ! def patch! unless project.update_attributes(request.params)! forbidden!(project)! end! project! end! ! end! Fail?
 Halt and provide 403 response
  • 92. Your favourite error method not there? def im_a_teapot!(payload = “”)! error!(418, payload)! end Add it!
  • 94. class ApplicationResource < Restfulness::Resource! ! def error!(status, payload = "", opts = {})! case payload! when ActiveRecord::Base! payload = {! :errors => payload.errors.full_messages! }! end! super(status, payload, opts)! end! end! Yes! Wrap, and provide all errors in the same format!
  • 96. Restfulness runs on Rack, so you can easily plug it into existing projects (Rails) or run it on its own (Sinatra)
  • 97. Standalone $ vi config.ru! ! require 'my_app'! run MyApp.new! ! $ rackup
  • 98. Or as part of a Rails project # In Rails application.rb! config.autoload_paths += [! “#{config.root}/app/resources”,
 “#{config.root}/app/apis”! ]! ! # In Rails Routes! YourRailsApp::Application.routes.draw do ! mount MyApi.new => '/api'! …! This way you get auto-reloading
  • 99. I hope you too find
 Restfulness
 :-) that’s about all there is to it! https://github.com/samlown/restfulness
  • 100. ? (for 10€ off you next Cabify ride: “MADRIDRB”)