Making Rails Really restful

Preview:

Citation preview

Making Rails really RestfulImproving the world of Resources on Rails with Restfulie

Fabio Akita - akitaonrails.com - @akitaonrails

Wednesday, June 9, 2010

The greatest thing about talking to you

Wednesday, June 9, 2010

We’ve been through this already!

Wednesday, June 9, 2010

We’ve been through this already!

No WSDL and WS-*

Wednesday, June 9, 2010

We’ve been through this already!

No WSDL and WS-*No XML-RPC

Wednesday, June 9, 2010

We’ve been through this already!

No WSDL and WS-*No XML-RPC

No $$ enterprisey stuff

Wednesday, June 9, 2010

We’ve been through this already!

No WSDL and WS-*No XML-RPC

No $$ enterprisey stuff

♥ HTTP

Wednesday, June 9, 2010

♥ HTTP

Wednesday, June 9, 2010

♥ HTTP

Shared Nothing Architecture

Wednesday, June 9, 2010

♥ HTTP

Shared Nothing ArchitectureStateless

Wednesday, June 9, 2010

♥ HTTP

Shared Nothing ArchitectureStateless

Caching

Wednesday, June 9, 2010

♥ HTTP

Shared Nothing ArchitectureStateless

CachingProxies and Reverse Proxies

Wednesday, June 9, 2010

♥ HTTP

Shared Nothing ArchitectureStateless

CachingProxies and Reverse Proxies

Wednesday, June 9, 2010

Discovering a world of

Resources on RailsDavid Heinemeir Hansson RailsConf 2006

Wednesday, June 9, 2010

Create Read Update Delete

Wednesday, June 9, 2010

Wednesday, June 9, 2010

GET POST PUT DELETE

!nd create update destroy

SELECT INSERT UPDATE DELETE

Wednesday, June 9, 2010

GET POST PUT DELETE

!nd create update destroy

SELECT INSERT UPDATE DELETE

Wednesday, June 9, 2010

GET POST PUT DELETE

!nd create update destroy

SELECT INSERT UPDATE DELETE

Wednesday, June 9, 2010

GET POST PUT DELETE

!nd create update destroy

SELECT INSERT UPDATE DELETE

Wednesday, June 9, 2010

POST /people/createGET /people/show/1POST /people/update/1POST /people/destroy/1

Wednesday, June 9, 2010

POST /peopleGET /people/1PUT /people/1DELETE /people/1

Wednesday, June 9, 2010

MyApp::Application.routes.draw do |map| resources :peopleend

Wednesday, June 9, 2010

class PeopleController < ApplicationController # POST /people/1 # POST /people/1.xml def create end

# GET /people # GET /people.xml def show end

# PUT /people/1 # PUT /people/1.xml def update end

# DELETE /people/1 # DELETE /people/1.xml def destroy endend

form_for(@person) do |f| f.text_field :nameend

Wednesday, June 9, 2010

class PeopleController < ApplicationController # POST /people/1 # POST /people/1.xml def create end

# GET /people # GET /people.xml def show end

# PUT /people/1 # PUT /people/1.xml def update end

# DELETE /people/1 # DELETE /people/1.xml def destroy endend

link_to 'Show', person

Wednesday, June 9, 2010

class PeopleController < ApplicationController # POST /people/1 # POST /people/1.xml def create end

# GET /people # GET /people.xml def show end

# PUT /people/1 # PUT /people/1.xml def update end

# DELETE /people/1 # DELETE /people/1.xml def destroy endend

form_for(@person) do |f| f.text_field :nameend

Wednesday, June 9, 2010

class PeopleController < ApplicationController # POST /people/1 # POST /people/1.xml def create end

# GET /people # GET /people.xml def show end

# PUT /people/1 # PUT /people/1.xml def update end

# DELETE /people/1 # DELETE /people/1.xml def destroy endend

link_to 'Destroy', person, :method => :delete

Wednesday, June 9, 2010

Answering to mime types

Wednesday, June 9, 2010

Answering to mime types

One controller for many clients

Wednesday, June 9, 2010

Answering to mime types

One controller for many clientsOne action returning different representations

Wednesday, June 9, 2010

Answering to mime types

One controller for many clientsOne action returning different representations

“Content Negotiation"

Wednesday, June 9, 2010

class PeopleController < ApplicationController def index @people = Person.all

respond_to do |format| format.html # index.html.erb format.js # index.js.erb format.atom # index.atom.builder format.xml { render :xml => @people } end endend

Wednesday, June 9, 2010

class PeopleController < ApplicationController respond_to :html, :js, :xml def index @people = Person.all respond_with(@people) endend

Wednesday, June 9, 2010

class PeopleController < ApplicationController respond_to :html, :js, :xml def index @people = Person.all respond_with(@people) endend

Wednesday, June 9, 2010

class PeopleController < ApplicationController respond_to :html, :js, :xml def index @people = Person.all respond_with(@people) endend

GET /people=> returns HTML

GET /people.xml=> returns XML

Accept: text/javascriptGET /people=> returns JS

Accept: text/xmlGET /people=> returns XML

Wednesday, June 9, 2010

class PeopleController < ApplicationController respond_to :html, :js, :xml def index @people = Person.all respond_with(@people) endend

GET /people=> returns HTML

GET /people.xml=> returns XML

Accept: text/javascriptGET /people=> returns JS

Accept: text/xmlGET /people=> returns XML

Wednesday, June 9, 2010

class PeopleController < ApplicationController respond_to :html, :js, :xml def index @people = Person.all respond_with(@people) endend

GET /people=> returns HTML

GET /people.xml=> returns XML

Accept: text/javascriptGET /people=> returns JS

Accept: text/xmlGET /people=> returns XML

Wednesday, June 9, 2010

class PeopleController < ApplicationController respond_to :html, :js, :xml def index @people = Person.all respond_with(@people) endend

GET /people=> returns HTML

GET /people.xml=> returns XML

Accept: text/javascriptGET /people=> returns JS

Accept: text/xmlGET /people=> returns XML

Wednesday, June 9, 2010

2008Rails 2.2

Wednesday, June 9, 2010

class PeopleController < ApplicationController respond_to :html, :js, :xml def show @person = Person.find(params[:id]) if stale?(:etag => @person, :last_modified => @person.created_at.utc, :public => true) respond_with @person end endend

Wednesday, June 9, 2010

class PeopleController < ApplicationController respond_to :html, :js, :xml def show @person = Person.find(params[:id]) if stale?(:etag => @person, :last_modified => @person.created_at.utc, :public => true) respond_with @person end endend

Wednesday, June 9, 2010

class PeopleController < ApplicationController respond_to :html, :js, :xml def show @person = Person.find(params[:id]) if stale?(:etag => @person, :last_modified => @person.created_at.utc, :public => true) respond_with @person end endend

GET /people/1

Wednesday, June 9, 2010

class PeopleController < ApplicationController respond_to :html, :js, :xml def show @person = Person.find(params[:id]) if stale?(:etag => @person, :last_modified => @person.created_at.utc, :public => true) respond_with @person end endend

GET /people/1=> returns:

HTTP/1.1 200 OK Etag: "fd19b85b6ba49b5778de34310d141319"Last-Modified: Fri, 21 May 2010 05:31:11 GMTContent-Type: text/html; charset=utf-8Cache-Control: public...

Wednesday, June 9, 2010

class PeopleController < ApplicationController respond_to :html, :js, :xml def show @person = Person.find(params[:id]) if stale?(:etag => @person, :last_modified => @person.created_at.utc, :public => true) respond_with @person end endend

GET /people/1=> returns:

HTTP/1.1 200 OK Etag: "fd19b85b6ba49b5778de34310d141319"Last-Modified: Fri, 21 May 2010 05:31:11 GMTContent-Type: text/html; charset=utf-8Cache-Control: public...

Wednesday, June 9, 2010

class PeopleController < ApplicationController respond_to :html, :js, :xml def show @person = Person.find(params[:id]) if stale?(:etag => @person, :last_modified => @person.created_at.utc, :public => true) respond_with @person end endend

If-None-Match: "fd19b85b6ba49b5778de34310d141319"GET /people/1

Wednesday, June 9, 2010

class PeopleController < ApplicationController respond_to :html, :js, :xml def show @person = Person.find(params[:id]) if stale?(:etag => @person, :last_modified => @person.created_at.utc, :public => true) respond_with @person end endend

If-None-Match: "fd19b85b6ba49b5778de34310d141319"GET /people/1

Wednesday, June 9, 2010

class PeopleController < ApplicationController respond_to :html, :js, :xml def show @person = Person.find(params[:id]) if stale?(:etag => @person, :last_modified => @person.created_at.utc, :public => true) respond_with @person end endend

If-None-Match: "fd19b85b6ba49b5778de34310d141319"GET /people/1=> returns:

HTTP/1.1 304 Not Modified Etag: "fd19b85b6ba49b5778de34310d141319"Last-Modified: Fri, 21 May 2010 05:31:11 GMTCache-Control: public...

Wednesday, June 9, 2010

class PeopleController < ApplicationController respond_to :html, :js, :xml def show @person = Person.find(params[:id]) if stale?(:etag => @person, :last_modified => @person.created_at.utc, :public => true) respond_with @person end endend

If-None-Match: "fd19b85b6ba49b5778de34310d141319"GET /people/1=> returns:

HTTP/1.1 304 Not Modified Etag: "fd19b85b6ba49b5778de34310d141319"Last-Modified: Fri, 21 May 2010 05:31:11 GMTCache-Control: public...

Wednesday, June 9, 2010

But!

Wednesday, June 9, 2010

CRUD is not a goal,it’s an aspiration,

a design technique

Wednesday, June 9, 2010

And we are not done yet!

Wednesday, June 9, 2010

Why not?

ConsistencySimplicity

Discoverability

Wednesday, June 9, 2010

Why not?

ConsistencySimplicity

Discoverability

Wednesday, June 9, 2010

Why not?

ConsistencySimplicity

Discoverability

Wednesday, June 9, 2010

This is NOT necessarily REST

Wednesday, June 9, 2010

This is NOT necessarily REST

Using HTTP

Wednesday, June 9, 2010

This is NOT necessarily REST

Using HTTPUsing HTTP Verbs (PUT, DELETE)

Wednesday, June 9, 2010

This is NOT necessarily REST

Using HTTPUsing HTTP Verbs (PUT, DELETE)

Pretty URIs

Wednesday, June 9, 2010

Most of what we call “REST” is not REST

Wednesday, June 9, 2010

REST Principles

Wednesday, June 9, 2010

REST Principles

Identi"cation of Resources

Wednesday, June 9, 2010

REST Principles

Identi"cation of ResourcesManipulate resources through representations

Wednesday, June 9, 2010

REST Principles

Identi"cation of ResourcesManipulate resources through representations

Self-descriptive messages

Wednesday, June 9, 2010

REST Principles

Identi"cation of ResourcesManipulate resources through representations

Self-descriptive messagesHATEOAS

Wednesday, June 9, 2010

REST Goals

Wednesday, June 9, 2010

REST Goals

Scalability of component interactions

Wednesday, June 9, 2010

REST Goals

Scalability of component interactionsGenerality of interfaces

Wednesday, June 9, 2010

REST Goals

Scalability of component interactionsGenerality of interfaces

Independent deployment of components

Wednesday, June 9, 2010

REST Goals

Scalability of component interactionsGenerality of interfaces

Independent deployment of componentsReduce latency, enforce security

Wednesday, June 9, 2010

REST Goals

Scalability of component interactionsGenerality of interfaces

Independent deployment of componentsReduce latency, enforce security

http://en.wikipedia.org/wiki/Representational_State_Transfer#Guiding_principles_of_a_REST_interface

Wednesday, June 9, 2010

Constraints are liberating(a straight jacket for your mind)

Wednesday, June 9, 2010

Wednesday, June 9, 2010

What are we doing right?

Wednesday, June 9, 2010

What are we doing right?

Uniform Interface (URI Templates)

Wednesday, June 9, 2010

What are we doing right?

Uniform Interface (URI Templates)HTTP Verbs (some)

Wednesday, June 9, 2010

What are we doing right?

Uniform Interface (URI Templates)HTTP Verbs (some)

HTTP Status Codes (some)

Wednesday, June 9, 2010

What are we doing right?

Uniform Interface (URI Templates)HTTP Verbs (some)

HTTP Status Codes (some)Content Negotiation

Wednesday, June 9, 2010

What is missing?

Wednesday, June 9, 2010

What is missing?

More HTTP (verbs, status codes, headers)

Wednesday, June 9, 2010

What is missing?

More HTTP (verbs, status codes, headers)Hyperlinks

Wednesday, June 9, 2010

What is missing?

More HTTP (verbs, status codes, headers)Hyperlinks

Semantic (media-types)

Wednesday, June 9, 2010

GET POST DELETE

!nd create update destroy

SELECT INSERT UPDATE DELETE

PUT

Wednesday, June 9, 2010

GET POST DELETE

!nd create update destroy

SELECT INSERT UPDATE DELETE

POST

Wednesday, June 9, 2010

GET POST DELETE

!nd create update destroy

SELECT INSERT UPDATE DELETE

PATCH

Wednesday, June 9, 2010

GET POST PUT PATCH DELETE

!nd create replace update destroy

SELECT INSERT UPDATE UPDATE DELETE

RFC 5789 - http://www.innoq.com/blog/st/2010/03/rfc_5789_patch_method_for_http.html

Wednesday, June 9, 2010

Why bother?

Wednesday, June 9, 2010

Why bother?

Consistency

Wednesday, June 9, 2010

Why bother?

ConsistencySimplicity

Wednesday, June 9, 2010

Why bother?

ConsistencySimplicity

Discoverability

Wednesday, June 9, 2010

Richardson Restful ModelRichardson Restful Model

Level 3 Hypermedia

Level 2 HTTP

Level 1 URI

Wednesday, June 9, 2010

Richardson Restful ModelRichardson Restful Model

Level 3 Hypermedia

Level 2 HTTP

Level 1 URI

Wednesday, June 9, 2010

Richardson Restful ModelRichardson Restful Model

Level 3 Hypermedia

Level 2 HTTP

Level 1 URI

Wednesday, June 9, 2010

Richardson Restful ModelRichardson Restful Model

Level 3 Hypermedia

Level 2 HTTP

Level 1 URI

Wednesday, June 9, 2010

The Web as it is

Wednesday, June 9, 2010

The Web as it is

Wednesday, June 9, 2010

Evaluation Workflow

Wednesday, June 9, 2010

Evaluation WorkflowWhen there is a new app

Wednesday, June 9, 2010

Evaluation WorkflowWhen there is a new app

Then move forward to evaluate it

Wednesday, June 9, 2010

Evaluation WorkflowWhen there is a new app

Then move forward to evaluate it

When there is an app being evaluated

Wednesday, June 9, 2010

Evaluation WorkflowWhen there is a new app

Then move forward to evaluate it

When there is an app being evaluatedAnd the app passes the criteria

Wednesday, June 9, 2010

Evaluation WorkflowWhen there is a new app

Then move forward to evaluate it

When there is an app being evaluatedAnd the app passes the criteria

Then approve the app

Wednesday, June 9, 2010

Evaluation WorkflowWhen there is a new app

Then move forward to evaluate it

When there is an app being evaluatedAnd the app passes the criteria

Then approve the app

When there is an app being evaluated

Wednesday, June 9, 2010

Evaluation WorkflowWhen there is a new app

Then move forward to evaluate it

When there is an app being evaluatedAnd the app passes the criteria

Then approve the app

When there is an app being evaluated

And the app doesn't pass the criteria

Wednesday, June 9, 2010

Evaluation WorkflowWhen there is a new app

Then move forward to evaluate it

When there is an app being evaluatedAnd the app passes the criteria

Then approve the app

When there is an app being evaluated

And the app doesn't pass the criteriaThen decline the app

Wednesday, June 9, 2010

Wednesday, June 9, 2010

Wednesday, June 9, 2010

Wednesday, June 9, 2010

Wednesday, June 9, 2010

Wednesday, June 9, 2010

Wednesday, June 9, 2010

Wednesday, June 9, 2010

Wednesday, June 9, 2010

Wednesday, June 9, 2010

Wednesday, June 9, 2010

The Automated Web?

Wednesday, June 9, 2010

The Automated Web?

?

Wednesday, June 9, 2010

require 'rubygems'require 'mechanize'

agent = Mechanize.new { |agent| agent.user_agent_alias = "Mac Safari" }

agent.get("http://localhost:3000/apps?state=new")

Wednesday, June 9, 2010

require 'rubygems'require 'mechanize'

agent = Mechanize.new { |agent| agent.user_agent_alias = "Mac Safari" }

agent.get("http://localhost:3000/apps?state=new")

Wednesday, June 9, 2010

require 'rubygems'require 'mechanize'

agent = Mechanize.new { |agent| agent.user_agent_alias = "Mac Safari" }

agent.get("http://localhost:3000/apps?state=new")

Wednesday, June 9, 2010

agent.click agent.page.link_with(:href => /apps\/\d+/)

evaluate = agent.page.link_with(:text => /Evaluate/)agent.post evaluate.href, "" if evaluate

approve = agent.page.link_with :text => /Approve/ decline = agent.page.link_with :text => /Decline/

Wednesday, June 9, 2010

agent.click agent.page.link_with(:href => /apps\/\d+/)

evaluate = agent.page.link_with(:text => /Evaluate/)agent.post evaluate.href, "" if evaluate

approve = agent.page.link_with :text => /Approve/ decline = agent.page.link_with :text => /Decline/

Wednesday, June 9, 2010

agent.click agent.page.link_with(:href => /apps\/\d+/)

evaluate = agent.page.link_with(:text => /Evaluate/)agent.post evaluate.href, "" if evaluate

approve = agent.page.link_with :text => /Approve/ decline = agent.page.link_with :text => /Decline/

Wednesday, June 9, 2010

agent.click agent.page.link_with(:href => /apps\/\d+/)

evaluate = agent.page.link_with(:text => /Evaluate/)agent.post evaluate.href, "" if evaluate

approve = agent.page.link_with :text => /Approve/ decline = agent.page.link_with :text => /Decline/

Wednesday, June 9, 2010

if approve && decline description = agent.page.search("pre").text

if description =~ /Flash/ agent.post decline.href, ""

elsif description =~ /sex/ agent.post decline.href, ""

else agent.post approve.href, ""

end

end

Wednesday, June 9, 2010

if approve && decline description = agent.page.search("pre").text

if description =~ /Flash/ agent.post decline.href, ""

elsif description =~ /sex/ agent.post decline.href, ""

else agent.post approve.href, ""

end

end

Wednesday, June 9, 2010

if approve && decline description = agent.page.search("pre").text

if description =~ /Flash/ agent.post decline.href, ""

elsif description =~ /sex/ agent.post decline.href, ""

else agent.post approve.href, ""

end

end

Wednesday, June 9, 2010

if approve && decline description = agent.page.search("pre").text

if description =~ /Flash/ agent.post decline.href, ""

elsif description =~ /sex/ agent.post decline.href, ""

else agent.post approve.href, ""

end

end

Wednesday, June 9, 2010

No Mechanization?

Wednesday, June 9, 2010

No Mechanization?

Hyperlinks

Wednesday, June 9, 2010

No Mechanization?

HyperlinksNo Semantics

Wednesday, June 9, 2010

No Mechanization?

HyperlinksNo Semantics

Human Heuristics

Wednesday, June 9, 2010

require 'rubygems'require 'active_resource'

class App < ActiveResource::Base self.site = "http://localhost:3000/"end

apps = App.find(:all, :params => { :state => "new" })

Wednesday, June 9, 2010

require 'rubygems'require 'active_resource'

class App < ActiveResource::Base self.site = "http://localhost:3000/"end

apps = App.find(:all, :params => { :state => "new" })

GET /apps?state=new

Wednesday, June 9, 2010

require 'rubygems'require 'active_resource'

class App < ActiveResource::Base self.site = "http://localhost:3000/"end

apps = App.find(:all, :params => { :state => "new" })

GET /apps?state=new

Wednesday, June 9, 2010

require 'rubygems'require 'active_resource'

class App < ActiveResource::Base self.site = "http://localhost:3000/"end

apps = App.find(:all, :params => { :state => "new" })

GET /apps?state=new

Wednesday, June 9, 2010

require 'rubygems'require 'active_resource'

class App < ActiveResource::Base self.site = "http://localhost:3000/"end

apps = App.find(:all, :params => { :state => "new" })

GET /apps?state=new

Wednesday, June 9, 2010

ActionController::Routing::Routes.draw do |map| map.resources :apps, :member => { :evaluate => :post, :approve => :post, :decline => :post }end

POST /apps/1/evaluatePOST /apps/1/declinePOST /apps/1/approve

Wednesday, June 9, 2010

POST /apps/1/evaluatePOST /apps/1/declinePOST /apps/1/approve

app = apps.firstapp.post(:evaluate) if app.state == "new"

if apps.first.description =~ /Flash/ app.post(:decline)

elsif apps.first.description =~ /sex/ app.post(:decline)

else app.post(:approve)end

Wednesday, June 9, 2010

POST /apps/1/evaluatePOST /apps/1/declinePOST /apps/1/approve

app = apps.firstapp.post(:evaluate) if app.state == "new"

if apps.first.description =~ /Flash/ app.post(:decline)

elsif apps.first.description =~ /sex/ app.post(:decline)

else app.post(:approve)end

Wednesday, June 9, 2010

POST /apps/1/evaluatePOST /apps/1/declinePOST /apps/1/approve

app = apps.firstapp.post(:evaluate) if app.state == "new"

if apps.first.description =~ /Flash/ app.post(:decline)

elsif apps.first.description =~ /sex/ app.post(:decline)

else app.post(:approve)end

Wednesday, June 9, 2010

POST /apps/1/evaluatePOST /apps/1/declinePOST /apps/1/approve

app = apps.firstapp.post(:evaluate) if app.state == "new"

if apps.first.description =~ /Flash/ app.post(:decline)

elsif apps.first.description =~ /sex/ app.post(:decline)

else app.post(:approve)end

Wednesday, June 9, 2010

POST /apps/1/evaluatePOST /apps/1/declinePOST /apps/1/approve

app = apps.firstapp.post(:evaluate) if app.state == "new"

if apps.first.description =~ /Flash/ app.post(:decline)

elsif apps.first.description =~ /sex/ app.post(:decline)

else app.post(:approve)end

Wednesday, June 9, 2010

POST /apps/1/evaluatePOST /apps/1/declinePOST /apps/1/approve

app = apps.firstapp.post(:evaluate) if app.state == "new"

if apps.first.description =~ /Flash/ app.post(:decline)

elsif apps.first.description =~ /sex/ app.post(:decline)

else app.post(:approve)end

Wednesday, June 9, 2010

POST /apps/1/evaluatePOST /apps/1/declinePOST /apps/1/approve

app = apps.firstapp.post(:evaluate) if app.state == "new"

if apps.first.description =~ /Flash/ app.post(:decline)

elsif apps.first.description =~ /sex/ app.post(:decline)

else app.post(:approve)end

Wednesday, June 9, 2010

application/xml

<?xml version="1.0" encoding="UTF-8"?><app> <id type="integer">1</id> <title>Facebook</title> <updated_at type="datetime">2010-06-02T14:16:45Z</updated_at> <state>new</state> <description>Facebook for iPhone ... on the go.</description> <price type="float">0.0</price></app>

Wednesday, June 9, 2010

Why not ActiveResource?

Wednesday, June 9, 2010

Why not ActiveResource?

No Hyperlinks

Wednesday, June 9, 2010

Why not ActiveResource?

No HyperlinksNo Semantics

Wednesday, June 9, 2010

Why not ActiveResource?

No HyperlinksNo Semantics

Doesn’t respect HTTP (etag, last modi"ed)

Wednesday, June 9, 2010

Why not ActiveResource?

No HyperlinksNo Semantics

Doesn’t respect HTTP (etag, last modi"ed)Ruby on Rails only Conventions

Wednesday, June 9, 2010

Why not other web clients?HTTParty, RestClient, Typhoeus, Curb

Wednesday, June 9, 2010

Why not other web clients?

Most are not really “REST”

HTTParty, RestClient, Typhoeus, Curb

Wednesday, June 9, 2010

Why not other web clients?

Most are not really “REST”Just URI consumers

HTTParty, RestClient, Typhoeus, Curb

Wednesday, June 9, 2010

Why not other web clients?

Most are not really “REST”Just URI consumers

Wrappers for low level HTTP connections

HTTParty, RestClient, Typhoeus, Curb

Wednesday, June 9, 2010

One goal is to avoid tight coupling

Wednesday, June 9, 2010

http://restfulie.caelumobjects.com/

Wednesday, June 9, 2010

Restfulie

Wednesday, June 9, 2010

Restfulie

Client and Server solution

Wednesday, June 9, 2010

Restfulie

Client and Server solutionXML, JSON, Atom Representations

Wednesday, June 9, 2010

Restfulie

Client and Server solutionXML, JSON, Atom Representations

Enhances Ruby on Rails

Wednesday, June 9, 2010

Restfulie

Client and Server solutionXML, JSON, Atom Representations

Enhances Ruby on RailsREST Client

Wednesday, June 9, 2010

Restfulie

@guilhermesilveira

@caueguerra

@georgeguimaraes

@lfcipriani

Wednesday, June 9, 2010

app/controllers/apps_controller.rb

class AppsController < ApplicationController restfulie respond_to :html, :xml, :atom inherit_resources ...end

Wednesday, June 9, 2010

app/controllers/apps_controller.rb

class AppsController < ApplicationController restfulie respond_to :html, :xml, :atom inherit_resources ...end

Wednesday, June 9, 2010

app/controllers/apps_controller.rb

class AppsController < ApplicationController restfulie respond_to :html, :xml, :atom inherit_resources ...end

Wednesday, June 9, 2010

app/controllers/apps_controller.rb

class AppsController < ApplicationController restfulie respond_to :html, :xml, :atom inherit_resources ...end

Wednesday, June 9, 2010

app/controllers/apps_controller.rb

class AppsController < ApplicationController restfulie respond_to :html, :xml, :atom inherit_resources ...end

Wednesday, June 9, 2010

Tokamak

Wednesday, June 9, 2010

Tokamak

Wednesday, June 9, 2010

app/views/apps/index.tokamak

collection(@apps) do |collection| collection.values do |values| values.id apps_url values.title "Apps List" values.updated_at Time.now.utc end collection.members do |member, app| partial "member", :locals => { :member => member, :app => app } endend

Wednesday, June 9, 2010

app/views/apps/index.tokamak

collection(@apps) do |collection| collection.values do |values| values.id apps_url values.title "Apps List" values.updated_at Time.now.utc end collection.members do |member, app| partial "member", :locals => { :member => member, :app => app } endend

Wednesday, June 9, 2010

app/views/apps/_member.tokamak

member.values do |value| value.id app.id value.title app.name value.updated_at app.updated_at.utc value.state app.state value.description app.description value.price app.priceendmember.link "show", app_url(app) if app.state == "new" member.link "evaluate", evaluate_app_url(app)elsif app.state == "evaluating" member.link "approve", approve_app_url(app) member.link "decline", decline_app_url(app)end

Wednesday, June 9, 2010

app/views/apps/_member.tokamak

member.values do |value| value.id app.id value.title app.name value.updated_at app.updated_at.utc value.state app.state value.description app.description value.price app.priceendmember.link "show", app_url(app) if app.state == "new" member.link "evaluate", evaluate_app_url(app)elsif app.state == "evaluating" member.link "approve", approve_app_url(app) member.link "decline", decline_app_url(app)end

Wednesday, June 9, 2010

app/views/apps/_member.tokamak

member.values do |value| value.id app.id value.title app.name value.updated_at app.updated_at.utc value.state app.state value.description app.description value.price app.priceendmember.link "show", app_url(app) if app.state == "new" member.link "evaluate", evaluate_app_url(app)elsif app.state == "evaluating" member.link "approve", approve_app_url(app) member.link "decline", decline_app_url(app)end

Wednesday, June 9, 2010

app/views/apps/_member.tokamak

member.values do |value| value.id app.id value.title app.name value.updated_at app.updated_at.utc value.state app.state value.description app.description value.price app.priceendmember.link "show", app_url(app) if app.state == "new" member.link "evaluate", evaluate_app_url(app)elsif app.state == "evaluating" member.link "approve", approve_app_url(app) member.link "decline", decline_app_url(app)end

Wednesday, June 9, 2010

app/views/apps/_member.tokamak

member.values do |value| value.id app.id value.title app.name value.updated_at app.updated_at.utc value.state app.state value.description app.description value.price app.priceendmember.link "show", app_url(app) if app.state == "new" member.link "evaluate", evaluate_app_url(app)elsif app.state == "evaluating" member.link "approve", approve_app_url(app) member.link "decline", decline_app_url(app)end

Wednesday, June 9, 2010

app/views/apps/_member.tokamak

member.values do |value| value.id app.id value.title app.name value.updated_at app.updated_at.utc value.state app.state value.description app.description value.price app.priceendmember.link "show", app_url(app) if app.state == "new" member.link "evaluate", evaluate_app_url(app)elsif app.state == "evaluating" member.link "approve", approve_app_url(app) member.link "decline", decline_app_url(app)end

Wednesday, June 9, 2010

curl -H “Accept: application/atom+xml” http://localhost:3000/apps/1

<entry xmlns="http://www.w3.org/2005/Atom"> <id>1</id> <title>Facebook</title> <updated_at>2010-06-02T14:16:45Z</updated_at> <state>new</state> <description>Facebook for iPhone ... on the go. </description> <price>0.0</price> <link href="http://localhost:3000/apps/1" rel="show" type="application/atom+xml"/> <link href="http://localhost:3000/apps/1/evaluate" rel="evaluate" type="application/atom+xml"/></entry>

Wednesday, June 9, 2010

curl -H “Accept: application/atom+xml” http://localhost:3000/apps/1

<entry xmlns="http://www.w3.org/2005/Atom"> <id>1</id> <title>Facebook</title> <updated_at>2010-06-02T14:16:45Z</updated_at> <state>new</state> <description>Facebook for iPhone ... on the go. </description> <price>0.0</price> <link href="http://localhost:3000/apps/1" rel="show" type="application/atom+xml"/> <link href="http://localhost:3000/apps/1/evaluate" rel="evaluate" type="application/atom+xml"/></entry>

Wednesday, June 9, 2010

curl -H “Accept: application/atom+xml” http://localhost:3000/apps/1

<entry xmlns="http://www.w3.org/2005/Atom"> <id>1</id> <title>Facebook</title> <updated_at>2010-06-02T14:16:45Z</updated_at> <state>new</state> <description>Facebook for iPhone ... on the go. </description> <price>0.0</price> <link href="http://localhost:3000/apps/1" rel="show" type="application/atom+xml"/> <link href="http://localhost:3000/apps/1/evaluate" rel="evaluate" type="application/atom+xml"/></entry>

Wednesday, June 9, 2010

<?xml version="1.0"?><app> <id>1</id> <title>Facebook</title> <updated_at>2010-06-02T14:16:45Z</updated_at> <state>new</state> <description>Facebook for iPhone ... on the go. </description> <price>0.0</price> <link href="http://localhost:3000/apps/1" rel="show" type="application/xml"/> <link href="http://localhost:3000/apps/1/evaluate" rel="evaluate" type="application/xml"/></app>

curl -H “Accept: application/xml” http://localhost:3000/apps/1

Wednesday, June 9, 2010

<?xml version="1.0"?><app> <id>1</id> <title>Facebook</title> <updated_at>2010-06-02T14:16:45Z</updated_at> <state>new</state> <description>Facebook for iPhone ... on the go. </description> <price>0.0</price> <link href="http://localhost:3000/apps/1" rel="show" type="application/xml"/> <link href="http://localhost:3000/apps/1/evaluate" rel="evaluate" type="application/xml"/></app>

curl -H “Accept: application/xml” http://localhost:3000/apps/1

Wednesday, June 9, 2010

<?xml version="1.0"?><app> <id>1</id> <title>Facebook</title> <updated_at>2010-06-02T14:16:45Z</updated_at> <state>new</state> <description>Facebook for iPhone ... on the go. </description> <price>0.0</price> <link href="http://localhost:3000/apps/1" rel="show" type="application/xml"/> <link href="http://localhost:3000/apps/1/evaluate" rel="evaluate" type="application/xml"/></app>

curl -H “Accept: application/xml” http://localhost:3000/apps/1

Wednesday, June 9, 2010

Mechanizable Web

Wednesday, June 9, 2010

Mechanizable Web

Hyperlinks

Wednesday, June 9, 2010

Mechanizable Web

HyperlinksLink Relations

Wednesday, June 9, 2010

Mechanizable Web

HyperlinksLink Relations

Media Types

Wednesday, June 9, 2010

Mechanizable Web

HyperlinksLink Relations

Media TypesDomain Application Protocol (DAP)

Wednesday, June 9, 2010

Mechanizable Web

HyperlinksLink Relations

Media TypesDomain Application Protocol (DAP)

HATEOAS

Wednesday, June 9, 2010

Mechanizable Web

HyperlinksLink Relations

Media TypesDomain Application Protocol (DAP)

HATEOASHypermedia As The Engine of Application State

Wednesday, June 9, 2010

Protocols

Contract

Wednesday, June 9, 2010

Protocols

Contract

HTTP idioms

Entry-point URIs

Media Types

Protocol

Wednesday, June 9, 2010

Formats Link Relations

Processing Models

Media Type

Schema

Protocols

Contract

HTTP idioms

Entry-point URIs

Media Types

Protocol

Wednesday, June 9, 2010

Improving Restfulie

Wednesday, June 9, 2010

Improving Restfulie

Single interface for representations

Wednesday, June 9, 2010

Improving Restfulie

Single interface for representationsSmart defaults for serialization

Wednesday, June 9, 2010

Improving Restfulie

Single interface for representationsSmart defaults for serialization

XML serialization compatible with Rails

Wednesday, June 9, 2010

Improving Restfulie

Single interface for representationsSmart defaults for serialization

XML serialization compatible with RailsRails 3 compatibility

Wednesday, June 9, 2010

Wednesday, June 9, 2010

One more thing

Wednesday, June 9, 2010

Mikyung

Wednesday, June 9, 2010

Evaluation WorkflowWhen there is a new app

Then move forward to evaluate it

When there is an app being evaluatedAnd the app passes the criteria

Then approve the app

When there is an app being evaluated

And the app doesn't pass the criteriaThen decline the app

Wednesday, June 9, 2010

evaluation_process.rb

require 'restfulie'

class EvaluationProcess < Restfulie::Client::Mikyung::RestProcessModel at "http://localhost:3000/apps/list_new" current_dir File.dirname(__FILE__) follow true

def initialize(*black_list) @black_list = black_list end

def completed?(resource) resource.entries.size == 0 end

def self.run goal = EvaluationProcess.new("Flash", "sex") result = Restfulie::Mikyung.new.achieve(goal).run puts result.response.body endend

Wednesday, June 9, 2010

evaluation_process.rb

require 'restfulie'

class EvaluationProcess < Restfulie::Client::Mikyung::RestProcessModel at "http://localhost:3000/apps/list_new" current_dir File.dirname(__FILE__) follow true

def initialize(*black_list) @black_list = black_list end

def completed?(resource) resource.entries.size == 0 end

def self.run goal = EvaluationProcess.new("Flash", "sex") result = Restfulie::Mikyung.new.achieve(goal).run puts result.response.body endend

Wednesday, June 9, 2010

evaluation_process.rb

require 'restfulie'

class EvaluationProcess < Restfulie::Client::Mikyung::RestProcessModel at "http://localhost:3000/apps/list_new" current_dir File.dirname(__FILE__) follow true

def initialize(*black_list) @black_list = black_list end

def completed?(resource) resource.entries.size == 0 end

def self.run goal = EvaluationProcess.new("Flash", "sex") result = Restfulie::Mikyung.new.achieve(goal).run puts result.response.body endend

Wednesday, June 9, 2010

evaluation_process.rb

require 'restfulie'

class EvaluationProcess < Restfulie::Client::Mikyung::RestProcessModel at "http://localhost:3000/apps/list_new" current_dir File.dirname(__FILE__) follow true

def initialize(*black_list) @black_list = black_list end

def completed?(resource) resource.entries.size == 0 end

def self.run goal = EvaluationProcess.new("Flash", "sex") result = Restfulie::Mikyung.new.achieve(goal).run puts result.response.body endend

Wednesday, June 9, 2010

steps/evaluation_process.rb

def is_valid?(app) result = true @black_list.each do |word| if app.description =~ /#{word}/ result = false end end resultend

Then "move forward to evaluate it" do |resource| @app = @app.links.evaluate.follow.post!("") resourceend

When "the app passes the criteria" do |resource| is_valid?(@app)end

Wednesday, June 9, 2010

steps/evaluation_process.rb

def is_valid?(app) result = true @black_list.each do |word| if app.description =~ /#{word}/ result = false end end resultend

Then "move forward to evaluate it" do |resource| @app = @app.links.evaluate.follow.post!("") resourceend

When "the app passes the criteria" do |resource| is_valid?(@app)end

Wednesday, June 9, 2010

scenarios/evaluation_process.scenario

When there is a new appThen move forward to evaluate it

When there is an app being evaluatedAnd the app passes the criteriaThen approve the app

When there is an app being evaluatedAnd the app doesnt pass the criteriaThen decline the app

Wednesday, June 9, 2010

scenarios/evaluation_process.scenario

When there is a new appThen move forward to evaluate it

When there is an app being evaluatedAnd the app passes the criteriaThen approve the app

When there is an app being evaluatedAnd the app doesnt pass the criteriaThen decline the app

This is Ruby Code!

Wednesday, June 9, 2010

EvaluationProcess.run

http://bit.ly/railsconf2010-restfulie

Wednesday, June 9, 2010

Adaptable Client

Wednesday, June 9, 2010

Adaptable Client

Goal Oriented

Wednesday, June 9, 2010

Adaptable Client

Goal OrientedPattern Matching

Wednesday, June 9, 2010

Adaptable Client

Goal OrientedPattern Matching

Can adapt to some changes

Wednesday, June 9, 2010

Richardson Restful ModelRichardson Restful Model

Level 3 Hypermedia

Level 2 HTTP

Level 1 URI

Wednesday, June 9, 2010

Caelum Restful ModelCaelum Restful Model

Level 5 Code on Demand

Level 4 Adaptable Clients

Level 3 Hypermedia

Level 2 HTTP

Level 1 URI

Wednesday, June 9, 2010

We’re not done yet!

Wednesday, June 9, 2010

Wednesday, June 9, 2010

Thank you!@AkitaOnRails

http://bit.ly/railsconf2010-restfulie

Wednesday, June 9, 2010

Recommended