Ruby on Rails
Part3: RESTful & Ajax
   ihower@gmail.com




     http://creativecommons.org/licenses/by-nc/2.5/tw/
Browser                                                    MVC
                                                       Mode...
Browser                                   Controller     Action
                                                          ...
Representational State Transfer
                   (     REST)


•   Roy Fielding       2000


•           SOAP XML-RPC

•...
Rails RESTful
                ?
RESTful       :

designing controller
and action is chaos
controller
class EventsController < ApplicationController

index
show
new
create
edit
update
destroy
controller
class EventsController < ApplicationController

index                             watch_list
show              ...
controller
class EventsController < ApplicationController

index                             watch_list
show              ...
named routes
# routes.rb
map.connect '/:controller/:action/:id'

<%= link_to ‘text’, :controller => ‘events’,
            ...
named routes
# routes.rb
map.connect '/:controller/:action/:id'

<%= link_to ‘text’, :controller => ‘events’,
            ...
named routes
 # routes.rb
 map.connect '/:controller/:action/:id'

<%= link_to ‘text’, :controller => ‘events’,
          ...
named routes
 # routes.rb
 map.connect '/:controller/:action/:id'

<%= link_to ‘text’, :controller => ‘events’,
          ...
named routes
 # routes.rb
 map.connect '/:controller/:action/:id'

<%= link_to ‘text’, :controller => ‘events’,
          ...
:



controllers   actions!!
The ideas from
   CRUD...
HTTP methods           (RFC 2616)




POST     GET     PUT         DELETE


Create   Read   Update        Delete
HTTP methods                  (RFC 2616)




POST     GET             PUT        DELETE


Create   Read          Update   ...
/events/create

 /events/show/1

/events/update/1

/events/destroy/1
/events/create
                       Add
                    HTTP method
 /events/show/1

/events/update/1

/events/destr...
/events/create
                       Add
                    HTTP method
 /events/show/1

/events/update/1

/events/destr...
/events/create                    POST /events
                       Add
                    HTTP method
 /events/show/1 ...
/events/create                    POST /events
                       Add
                    HTTP method
 /events/show/1 ...
CRUD-based action names
   get things simpler
create   show   update   delete


POST     GET     PUT     DELETE
CRUD-based action names
   get things simpler
create   show         update    delete


POST     GET           PUT     DELE...
routes.rb
ActionController::Routing::Routes.draw do |map|
	 map.resources :events
end




                 named routes   ...
routes.rb
ActionController::Routing::Routes.draw do |map|
	 map.resources :events
end

                 a resource is
    ...
routes.rb
ActionController::Routing::Routes.draw do |map|
	 map.resources :events
end

                    a resource is
 ...
routes.rb
ActionController::Routing::Routes.draw do |map|
	 map.resources :events
end

                    a resource is
 ...
routes.rb
ActionController::Routing::Routes.draw do |map|
	 map.resources :events
end

                    a resource is
 ...
routes.rb
ActionController::Routing::Routes.draw do |map|
	 map.resources :events
end

                    a resource is
 ...
RESTful
CRUD
index                       The default
                                           request method is
                     ...
show                     The default
                                        request method is
                           ...
new/create
<%= link_to ‘new event’, new_event_path %>

class EventsController < ApplicationController
  def new
     @even...
new/create
<%= link_to ‘new event’, new_event_path %>

class EventsController < ApplicationController
  def new
     @even...
new/create
<%= link_to ‘new event’, new_event_path %>

class EventsController < ApplicationController
  def new
     @even...
new/create
<%= link_to ‘new event’, new_event_path %>

class EventsController < ApplicationController
  def new           ...
new/create
<%= link_to ‘new event’, new_event_path %>

class EventsController < ApplicationController
  def new           ...
edit/update
<%= link_to event.name, edit_event_path(event) %>

  class EventsController < ApplicationController
    def ed...
edit/update
<%= link_to event.name, edit_event_path(event) %>

  class EventsController < ApplicationController
    def ed...
edit/update
<%= link_to event.name, edit_event_path(event) %>

  class EventsController < ApplicationController
    def ed...
edit/update
<%= link_to event.name, edit_event_path(event) %>

  class EventsController < ApplicationController
    def ed...
edit/update
<%= link_to event.name, edit_event_path(event) %>

  class EventsController < ApplicationController
    def ed...
the request
                    destroy                        method is
                                                 ...
4 HTTP methods, 4 URL helper, 7 actions

        Helper                GET          POST        PUT       DELETE


       ...
Singular and Plural RESTful
             Routes
• show, new, edit, destroy
• index, new, create
Singular and Plural RESTful
             Routes
• show, new, edit, destroy
• index, new, create
event_path(@event)
Singular and Plural RESTful
             Routes
• show, new, edit, destroy
• index, new, create
event_path(@event)
       ...
Singular and Plural RESTful
             Routes
• show, new, edit, destroy
• index, new, create
event_path(@event)
       ...
Singular and Plural RESTful
             Routes
• show, new, edit, destroy
• index, new, create
event_path(@event)
       ...
[custom route]_event[s]_path( event )
new, edit

     [custom route]_event[s]_path( event )
?
new, edit
                      ?

     [custom route]_event[s]_path( event )
_path
                      ?            _url              http://domain/
new, edit
                      ?

     [custom ...
_path
                            ?               _url           http://domain/
new, edit
                            ?

 ...
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id'
link_to event.name, :controller => ‘events’,
                    :action => :show , ...
map.connect ':controller/:action/:id'
link_to event.name, :controller => ‘events’,
                    :action => :show , ...
map.connect ':controller/:action/:id'
link_to event.name, :controller => ‘events’,
                    :action => :show , ...
map.connect ':controller/:action/:id'
link_to event.name, :controller => ‘events’,
                    :action => :show , ...
PUT? DELETE?
The PUT&DELETE Cheat
The PUT&DELETE Cheat
•            PUT&DELETE method
The PUT&DELETE Cheat
•                   PUT&DELETE method
• Rails   _method
The PUT&DELETE Cheat
  •                          PUT&DELETE method
  • Rails         _method
<form id="edit_events_1" met...
The PUT&DELETE Cheat
  •                          PUT&DELETE method
  • Rails         _method
<form id="edit_events_1" met...
The PUT&DELETE Cheat
   •                                   PUT&DELETE method
   • Rails              _method
<form id="ed...
The PUT&DELETE Cheat
   •                                   PUT&DELETE method
   • Rails              _method
<form id="ed...
The Problem
The Problem
•   HTML            GET/POST   HTML forms
       PUT/DELETE
The Problem
•   HTML              GET/POST       HTML forms
       PUT/DELETE

•       XmlHttpRequest    ( Ajax request)
 ...
The Problem
•   HTML                GET/POST     HTML forms
       PUT/DELETE

•       XmlHttpRequest    ( Ajax request)
 ...
The Problem
•   HTML                GET/POST     HTML forms
       PUT/DELETE

•       XmlHttpRequest    ( Ajax request)
 ...
The Problem
•   HTML                GET/POST     HTML forms
       PUT/DELETE

•       XmlHttpRequest    ( Ajax request)
 ...
As a Ruby On Rails special, Prototype also reacts
to other verbs (such as 'put' and 'delete' by
actually using 'post' and ...
The type of request to make ("POST" or "GET"), default is
"GET". Note: Other HTTP request methods, such as PUT
           ...
4 HTTP methods, 4 URL helper, 7 actions

        Helper                GET          POST        PUT       DELETE


       ...
standardization on
         action name
The heart of the Rails’s REST support is a technique for
    creating bundles of n...
RESTful
Scaffold
respond_to
       ?
One Action, Multiple
     Response Formats
def index
    @users = User.find(:all)
    respond_to do |format|
        forma...
format.html
format.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
 "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
  <m...
format.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
 "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
  <m...
format.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
 "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
  <m...
you don't need this!
def show_html
    @users = User.find(:all)
end

def show_xml
    @users = User.find(:all)
    render ...
you don't need this!
def show_html
    @users = User.find(:all)
end

def show_xml
    @users = User.find(:all)
    render ...
you don't need this!
def show_html
    @users = User.find(:all)
end

def show_xml
    @users = User.find(:all)
    render ...
action

Don’t repeat yourself
formats
• format.html     • format.csv
• format.xml      • format.xls
• format.js       • format.yaml
• format.json     • ...
http://registrano.com/events/5381ae/attendees
http://registrano.com/events/5381ae/attendees
http://registrano.com/events/5381ae/attendees




http://registrano.com/events/5381ae/attendees.csv
http://registrano.com/events/5381ae/attendees




http://registrano.com/events/5381ae/attendees.csv

                http:...
(   UI   )
XML API
             JSON API




(   UI   )
XML API
                      JSON API




(   UI        )
         Adobe Flex
Rails: how to know?
URL
        http://localhost:3000/users.xml


     template
<%= link_to ‘User List’, formatted_users_path(:xml) %>



<a h...
HTTP request Headers
GET /users HTTP/1.1

Host: localhost:3000
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; zh-T...
HTTP request Headers
GET /users HTTP/1.1

Host: localhost:3000
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; zh-T...
•        params[:format]

    GET /users/1?format=xml
•                Controller code
class ApplicationController < ActionController::Base
   before_filter :adjust_format_for_i...
custom format
# config/initializers/mime_types.rb
Mime::Type.register ‘audio/mpeg’, :mp3?Mime::Type.register ‘audio/mpegur...
custom format
# config/initializers/mime_types.rb
Mime::Type.register ‘audio/mpeg’, :mp3?Mime::Type.register ‘audio/mpegur...
custom format
# config/initializers/mime_types.rb
Mime::Type.register ‘audio/mpeg’, :mp3?Mime::Type.register ‘audio/mpegur...
custom format
# config/initializers/mime_types.rb
Mime::Type.register ‘audio/mpeg’, :mp3?Mime::Type.register ‘audio/mpegur...
template
       ?
template

• format (minetype)      template generator
  (renderer)
• Rails2             action.minetype.renderer
         ...
template
  def index
      @users = User.find(:all)
      respond_to do |format|
          format.html # index.html.erb
  ...
template
  def index
      @users = User.find(:all)
      respond_to do |format|
          format.html # index.html.erb
  ...
erb template
•     ruby code
•             HTML (   format.html)
    <h1><%= @event.name %></h1>
erb template
•     ruby code
•             HTML (   format.html)
    <h1><%= @event.name %></h1>
erb template
•     ruby code
•             HTML (   format.html)
    <h1><%= @event.name %></h1>




        <h1>OSDC 2008...
erb template
•     ruby code
•             HTML (    format.html)
    <h1><%= @event.name %></h1>

                  show....
builder template
 •        Ruby                XML
xml.instruct!
xml.title "This is a title"
xml.person do
  xml.first_nam...
builder template
 •        Ruby                XML
xml.instruct!
xml.title "This is a title"
xml.person do
  xml.first_nam...
builder template
 •        Ruby                XML
xml.instruct!
xml.title "This is a title"
xml.person do
  xml.first_nam...
builder template
 •        Ruby                XML
xml.instruct!
xml.title "This is a title"
xml.person do
  xml.first_nam...
builder template (cont.)
•       Atom feed Rails2             Atom helper
atom_feed do |feed|
   feed.title( @feed_title )...
Ajax on Rails
Ajax
    link_to_remote ‘Terms’, :url => terms_path, :update => ‘content’

<a onclick="$.ajax({async:true, beforeSend:func...
Ajax   (1)
Ajax

<a onclick="$.ajax({async:true, beforeSend:function(xhr) {xhr.setRequestHeader
('Accept', 'text/javascript, text/htm...
RJS template
<%= link_to_remote ‘ajax show’, :url => event_path(@event) %>
RJS template
<%= link_to_remote ‘ajax show’, :url => event_path(@event) %>
RJS template
<%= link_to_remote ‘ajax show’, :url => event_path(@event) %>

      def show
        @event = Event.find(par...
RJS template
<%= link_to_remote ‘ajax show’, :url => event_path(@event) %>

      def show
        @event = Event.find(par...
RJS template
<%= link_to_remote ‘ajax show’, :url => event_path(@event) %>

      def show
        @event = Event.find(par...
RJS template
<%= link_to_remote ‘ajax show’, :url => event_path(@event) %>

      def show
        @event = Event.find(par...
RJS template
<%= link_to_remote ‘ajax show’, :url => event_path(@event) %>

      def show
        @event = Event.find(par...
RJS template
<%= link_to_remote ‘ajax show’, :url => event_path(@event) %>

      def show
        @event = Event.find(par...
RJS template
<%= link_to_remote ‘ajax show’, :url => event_path(@event) %>

      def show
        @event = Event.find(par...
Ajax   (2)
inline RJS
def show
  @note = Note.find(params[:id])
  respond_to |format|
    format.js {
      render :update do |page|
...
inline RJS
def show
  @note = Note.find(params[:id])
  respond_to |format|
    format.js {
      render :update do |page|
...
inline RJS
def show
  @note = Note.find(params[:id])
  respond_to |format|
    format.js {
      render :update do |page|
...
js.erb template
 <%=link_to_remote ‘ajax’, :url => posts_path %>



     def index
      ...                    •         ...
respond_to             :
               Graceful Degradation
def index
    @users = User.find(:all)
    respond_to do |for...
respond_to             :
               Graceful Degradation
def index
    @users = User.find(:all)
    respond_to do |for...
respond_to             :
               Graceful Degradation
def index
    @users = User.find(:all)
    respond_to do |for...
respond_to             :
               Graceful Degradation
def index
    @users = User.find(:all)
    respond_to do |for...
respond_to             :
               Graceful Degradation
def index
    @users = User.find(:all)
    respond_to do |for...
Ajax

$.ajax({ async:true,
beforeSend:function(xhr) {xhr.setRequestHeader('Accept', 'text/xml, */*')}, type: “get”,
url: “...
Ajax

$.ajax({ async:true,
beforeSend:function(xhr) {xhr.setRequestHeader('Accept', 'text/json, */*')}, type: “get”,
url: ...
RESTful
   7 actions
               ?
action
event has many attendees
Model design
class Event < ActiveRecord::Base
  has_many :attendees
end

class Attendee < ActiveRecord::Base
  belongs_to ...
nested resources(1)                                     controller
                                                       ...
nested resources(1)                                    controller
                                                       a...
nested resources(1)                                    controller
                                                       a...
nested resources(1)                                     controller
                                                       ...
nested resources(1)                                     controller
                                                       ...
nested resources(2)
<%= link_to ‘show’, event_attendees_path(@event,@attendee) %>
nested resources(2)                        /events/2/attendees/3

<%= link_to ‘show’, event_attendees_path(@event,@attende...
nested resources(2)                             /events/2/attendees/3

<%= link_to ‘show’, event_attendees_path(@event,@at...
nested resources(2)                             /events/2/attendees/3

<%= link_to ‘show’, event_attendees_path(@event,@at...
nested resources(2)                             /events/2/attendees/3

<%= link_to ‘show’, event_attendees_path(@event,@at...
Deep Nesting?
Resources should never be nested more than one level deep.
action
event memberships
Model design
class Event < ActiveRecord::Base
  has_many :memberships
  has_many :users, :through => :memberships
end

cla...
RESTful design 1

map.resources :memberships
RESTful design 1

map.resources :memberships

class MembershipsController < ApplicationController
 # POST /memberships?gro...
RESTful design 2
map.resources :groups do |group|
    group.resources :memberships
end
RESTful design 2
map.resources :groups do |group|
    group.resources :memberships
end


class MembershipsController < App...
action
event has one map
singular resource route
•                resources

    map.resources :events



•                             resource
  ...
singular resource route
•                resources

    map.resources :events



•                             resource
  ...
singular resource route (cont.)
  •           URL helper

  •                index action

  •   show, edit     update    ...
action
operate event state
  (open/closed)
map.resources :events do |event|
    event.resource :closure, :controller => 'event_closures'
end
map.resources :events do |event|
    event.resource :closure, :controller => 'event_closures'
end

class EventClosuresCont...
map.resources :events do |event|
      event.resource :closure, :controller => 'event_closures'
  end

  class EventClosur...
map.resources :events do |event|
      event.resource :closure, :controller => 'event_closures'
  end

  class EventClosur...
why not
PUT closed=1 to /events/2
          Text
why not
PUT closed=1 to /events/2
          Text

 “a separate resource” or “an attribute of event”
action
search event
Extra Collection
               Routes
map.resources :events, :collection => { :search => :get }
Extra Collection
                Routes
map.resources :events, :collection => { :search => :get }

class EventsController ...
Extra Collection
                Routes
 map.resources :events, :collection => { :search => :get }

class EventsController...
action
a event dashboard
Extra Member Routes
map.resources :events, :member => { :dashboard => :get }
Extra Member Routes
 map.resources :events, :member => { :dashboard => :get }

class EventsController < ApplicationControl...
Extra Member Routes
   map.resources :events, :member => { :dashboard => :get }

  class EventsController < ApplicationCon...
Route Customizations
  is not RESTful ??
Route Customizations
  is not RESTful ??

• you can think of it as a sub-resource of
  events resource. (and the sub-resou...
Route Customizations
  is not RESTful ??

• you can think of it as a sub-resource of
  events resource. (and the sub-resou...
action
sorting event
Use query variables
  • Need not new resource
def index
  sort_by = (params[:order] == ‘name’) ? ‘name’ : ‘created_at’
  @...
Use query variables
  • Need not new resource
def index
  sort_by = (params[:order] == ‘name’) ? ‘name’ : ‘created_at’
  @...
action
event admin
namespace
map.namespace :admin do |admin|
  admin.resources :events
end
namespace
 map.namespace :admin do |admin|
   admin.resources :events
 end



# /app/controllers/admin/events_controller.r...
Considerations(1)
Considerations(1)
• a REST resource does not map directly to
  model. It’s high-level abstractions of what’s
  available t...
Considerations(1)
• a REST resource does not map directly to
  model. It’s high-level abstractions of what’s
  available t...
map.resource :session
# This controller handles the login/logout function of the site.
class SessionsController < Applicat...
map.resource :session
# This controller handles the login/logout function of the site.
class SessionsController < Applicat...
Considerations(2)

• a RESTful controller may represent the
  creation or delete of only a concept.
  For example, a Spams...
Considerations(3)
Considerations(3)

• one resources should be associated with
  one controller.
  (well, you can use one controller handle ...
Considerations(3)

• one resources should be associated with
  one controller.
  (well, you can use one controller handle ...
1 attendee Model
2 Resources related
     (2 Controller)
map.resources :attendees




  1 attendee Model
 2 Resources related
        (2 Controller)
map.resources :attendees




  1 attendee Model
 2 Resources related
        (2 Controller)
map.resources :attendees   class AttendeeController < ApplicationController

                             before_filter :m...
for event manager
map.resources :attendees   class AttendeeController < ApplicationController

                           ...
for event manager
map.resources :attendees   class AttendeeController < ApplicationController

                           ...
for event manager
map.resources :attendees   class AttendeeController < ApplicationController

                           ...
for event manager
map.resources :attendees            class AttendeeController < ApplicationController

                  ...
for event manager
map.resources :attendees            class AttendeeController < ApplicationController

                  ...
[namespace]_[custom route]_[parent resource]_event[s]_path( [p,] e)
new, edit
                           ?
[namespace]_[custom route]_[parent resource]_event[s]_path( [p,] e)
new, edit
                                         nested
                           ?
[namespace]_[custom route]_[parent ...
new, edit
                                         nested            ?    ?
                           ?
[namespace]_[cust...
new, edit
                                         nested            ?    ?
                           ?
[namespace]_[cust...
so, what’s REST style?
Nouns
Nouns
 URL
Nouns
 URL
Nouns
        URL



Verb
Nouns
                      URL



            Verb
finite HTTP methods
URL without      Nouns
      action          URL



             Verb
finite HTTP methods
URL without      Nouns
      action          URL



             Verb
finite HTTP methods
URL without      Nouns
      action          URL



             Verb            Content Types
finite HTTP methods
URL without      Nouns
      action          URL



             Verb            Content Types
finite HTTP methods         ...
URL without      Nouns
                                    the resource have
      action          URL          many repre...
URL without      Nouns
                                    the resource have
      action          URL          many repre...
REST end.
Reference books:
• Rails Way(Addison Wesley)


• Advanced Rails    (Pragmatic)


• Code Review PDF        (peepcode.com)

...
Ruby on Rails : RESTful 和 Ajax
Ruby on Rails : RESTful 和 Ajax
Ruby on Rails : RESTful 和 Ajax
Upcoming SlideShare
Loading in...5
×

Ruby on Rails : RESTful 和 Ajax

9,672

Published on

2 Comments
11 Likes
Statistics
Notes
No Downloads
Views
Total Views
9,672
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
197
Comments
2
Likes
11
Embeds 0
No embeds

No notes for slide

Ruby on Rails : RESTful 和 Ajax

  1. 1. Ruby on Rails Part3: RESTful & Ajax ihower@gmail.com http://creativecommons.org/licenses/by-nc/2.5/tw/
  2. 2. Browser MVC Model-View-Control HTTP request GET /users/1 route.rb UsersController Model def show @user = User.find(params[:id]) respond_to do |format| Database format.html format.xml end end #show.html.erb View <html> def index <h1>User Profile</h1> ...... <p><%= @user.nickname %></p> end </html> end
  3. 3. Browser Controller Action MVC Model-View-Control HTTP request GET /users/1 route.rb UsersController Model def show @user = User.find(params[:id]) respond_to do |format| Database format.html format.xml end end #show.html.erb View <html> def index <h1>User Profile</h1> ...... <p><%= @user.nickname %></p> end </html> end
  4. 4. Representational State Transfer ( REST) • Roy Fielding 2000 • SOAP XML-RPC • Web Service API Amazon Yahoo! Google API
  5. 5. Rails RESTful ?
  6. 6. RESTful : designing controller and action is chaos
  7. 7. controller class EventsController < ApplicationController index show new create edit update destroy
  8. 8. controller class EventsController < ApplicationController index watch_list show add_favorite new invite create join edit leave update white_member_list destroy black_member_list feeds deny_user add_comment allow_user show_comment edit_managers destroy_comment set_user_as_manager edit_comment set_user_as_member approve_comment ..... mark_comment_as_spam etc.
  9. 9. controller class EventsController < ApplicationController index watch_list show add_favorite new create controller ??? invite join edit update actions leave ???? white_member_list destroy controller actions black_member_list ??? feeds deny_user add_comment allow_user show_comment edit_managers destroy_comment set_user_as_manager edit_comment set_user_as_member approve_comment ..... mark_comment_as_spam etc.
  10. 10. named routes # routes.rb map.connect '/:controller/:action/:id' <%= link_to ‘text’, :controller => ‘events’, :action => ‘show’, :id => event.id %>
  11. 11. named routes # routes.rb map.connect '/:controller/:action/:id' <%= link_to ‘text’, :controller => ‘events’, :action => ‘show’, :id => event.id %> named route
  12. 12. named routes # routes.rb map.connect '/:controller/:action/:id' <%= link_to ‘text’, :controller => ‘events’, :action => ‘show’, :id => event.id %> named route # routes.rb map.event '/events/:id', :controller => 'event', :action => 'show'
  13. 13. named routes # routes.rb map.connect '/:controller/:action/:id' <%= link_to ‘text’, :controller => ‘events’, :action => ‘show’, :id => event.id %> named route # routes.rb map.event '/events/:id', :controller => 'event', :action => 'show' <%= link_to ‘text’, event_path(event) %>
  14. 14. named routes # routes.rb map.connect '/:controller/:action/:id' <%= link_to ‘text’, :controller => ‘events’, hmm.... named routes :action => ‘show’, event_delete_path? :id => event.id %> event_create_path? named route events_path? # routes.rb events_new_path? map.event '/events/:id', :controller => 'event', :action => 'show' <%= link_to ‘text’, event_path(event) %>
  15. 15. : controllers actions!!
  16. 16. The ideas from CRUD...
  17. 17. HTTP methods (RFC 2616) POST GET PUT DELETE Create Read Update Delete
  18. 18. HTTP methods (RFC 2616) POST GET PUT DELETE Create Read Update Delete GET is defined as a safe method
  19. 19. /events/create /events/show/1 /events/update/1 /events/destroy/1
  20. 20. /events/create Add HTTP method /events/show/1 /events/update/1 /events/destroy/1
  21. 21. /events/create Add HTTP method /events/show/1 /events/update/1 /events/destroy/1
  22. 22. /events/create POST /events Add HTTP method /events/show/1 GET /events/1 /events/update/1 PUT /events/1 /events/destroy/1 DELETE /events/1
  23. 23. /events/create POST /events Add HTTP method /events/show/1 GET /events/1 /events/update/1 PUT /events/1 /events/destroy/1 DELETE /events/1 Remove actions from URL, and we have simple named route.
  24. 24. CRUD-based action names get things simpler create show update delete POST GET PUT DELETE
  25. 25. CRUD-based action names get things simpler create show update delete POST GET PUT DELETE controller CRUD
  26. 26. routes.rb ActionController::Routing::Routes.draw do |map| map.resources :events end named routes actions
  27. 27. routes.rb ActionController::Routing::Routes.draw do |map| map.resources :events end a resource is something with URL named routes actions
  28. 28. routes.rb ActionController::Routing::Routes.draw do |map| map.resources :events end a resource is something with URL named routes actions 4
  29. 29. routes.rb ActionController::Routing::Routes.draw do |map| map.resources :events end a resource is something with URL named routes actions 4 7
  30. 30. routes.rb ActionController::Routing::Routes.draw do |map| map.resources :events end a resource is something with URL named routes actions 4 7
  31. 31. routes.rb ActionController::Routing::Routes.draw do |map| map.resources :events end a resource is something with URL named routes actions 4 7 4 HTTP method
  32. 32. RESTful CRUD
  33. 33. index The default request method is GET <%= link_to ‘event list’, events_path %> class EventsController < ApplicationController def index @events = Event.find(:all) end ... end
  34. 34. show The default request method is GET <%= link_to event.name, event_path(event) %> class EventsController < ApplicationController def show @event = Event.find(params[:id]) end ... end
  35. 35. new/create <%= link_to ‘new event’, new_event_path %> class EventsController < ApplicationController def new @event = Event.new end end
  36. 36. new/create <%= link_to ‘new event’, new_event_path %> class EventsController < ApplicationController def new @event = Event.new end end <% form_for @event, :url => events_path do |f| %> <%= f.text_field :name %> <%= f.submit "Create" %> <% end %>
  37. 37. new/create <%= link_to ‘new event’, new_event_path %> class EventsController < ApplicationController def new @event = Event.new end end <% form_for @event, :url => events_path do |f| %> <%= f.text_field :name %> <%= f.submit "Create" %> <% end %>
  38. 38. new/create <%= link_to ‘new event’, new_event_path %> class EventsController < ApplicationController def new In a form, the default @event = Event.new request method is end POST end <% form_for @event, :url => events_path do |f| %> <%= f.text_field :name %> <%= f.submit "Create" %> <% end %>
  39. 39. new/create <%= link_to ‘new event’, new_event_path %> class EventsController < ApplicationController def new In a form, the default @event = Event.new request method is end POST end <% form_for @event, :url => events_path do |f| %> <%= f.text_field :name %> <%= f.submit "Create" %> <% end %> class EventsController < ApplicationController def create Event.create(params[:id]) end end
  40. 40. edit/update <%= link_to event.name, edit_event_path(event) %> class EventsController < ApplicationController def edit @event = Event.find(params[:id]) end end
  41. 41. edit/update <%= link_to event.name, edit_event_path(event) %> class EventsController < ApplicationController def edit @event = Event.find(params[:id]) end end <% form_for @event, :url => event_path(@event), :method => :put do |f| %> <%= f.text_field :name %> <%= f.submit "Create" %> <% end %>
  42. 42. edit/update <%= link_to event.name, edit_event_path(event) %> class EventsController < ApplicationController def edit @event = Event.find(params[:id]) end end <% form_for @event, :url => event_path(@event), :method => :put do |f| %> <%= f.text_field :name %> <%= f.submit "Create" %> <% end %>
  43. 43. edit/update <%= link_to event.name, edit_event_path(event) %> class EventsController < ApplicationController def edit @event = Event.find(params[:id]) end end <% form_for @event, :url => event_path(@event), :method => :put do |f| %> <%= f.text_field :name %> <%= f.submit "Create" %> the request <% end %> method is PUT
  44. 44. edit/update <%= link_to event.name, edit_event_path(event) %> class EventsController < ApplicationController def edit @event = Event.find(params[:id]) end end <% form_for @event, :url => event_path(@event), :method => :put do |f| %> <%= f.text_field :name %> <%= f.submit "Create" %> the request <% end %> method is PUT class EventsController < ApplicationController def create Event.create(params[:event]) end end
  45. 45. the request destroy method is DELETE <%= link_to @event, event_path(@event), :method => :delete %> class EventsController < ApplicationController def destroy Event.find(params[:id]).destroy end ... end
  46. 46. 4 HTTP methods, 4 URL helper, 7 actions Helper GET POST PUT DELETE /events/1 /events/1 /events/1 event_path(@event) show update destroy /events /events events_path index create /events/1/edit edit_event_path(@event) edit /events/new new_events_path new
  47. 47. Singular and Plural RESTful Routes • show, new, edit, destroy • index, new, create
  48. 48. Singular and Plural RESTful Routes • show, new, edit, destroy • index, new, create event_path(@event)
  49. 49. Singular and Plural RESTful Routes • show, new, edit, destroy • index, new, create event_path(@event) HTTP verb show, update, destroy
  50. 50. Singular and Plural RESTful Routes • show, new, edit, destroy • index, new, create event_path(@event) HTTP verb show, update, destroy events_path
  51. 51. Singular and Plural RESTful Routes • show, new, edit, destroy • index, new, create event_path(@event) HTTP verb show, update, destroy events_path HTTP verb index, create
  52. 52. [custom route]_event[s]_path( event )
  53. 53. new, edit [custom route]_event[s]_path( event )
  54. 54. ? new, edit ? [custom route]_event[s]_path( event )
  55. 55. _path ? _url http://domain/ new, edit ? [custom route]_event[s]_path( event )
  56. 56. _path ? _url http://domain/ new, edit ? [custom route]_event[s]_path( event ) :method => GET | POST | PUT | DELETE
  57. 57. map.connect ':controller/:action/:id'
  58. 58. map.connect ':controller/:action/:id'
  59. 59. map.connect ':controller/:action/:id' link_to event.name, :controller => ‘events’, :action => :show , :id => event.id
  60. 60. map.connect ':controller/:action/:id' link_to event.name, :controller => ‘events’, :action => :show , :id => event.id
  61. 61. map.connect ':controller/:action/:id' link_to event.name, :controller => ‘events’, :action => :show , :id => event.id
  62. 62. map.connect ':controller/:action/:id' link_to event.name, :controller => ‘events’, :action => :show , :id => event.id link_to event.name, event_path(event) resources URL Helper
  63. 63. PUT? DELETE?
  64. 64. The PUT&DELETE Cheat
  65. 65. The PUT&DELETE Cheat • PUT&DELETE method
  66. 66. The PUT&DELETE Cheat • PUT&DELETE method • Rails _method
  67. 67. The PUT&DELETE Cheat • PUT&DELETE method • Rails _method <form id="edit_events_1" method="post" action="/events/1"> <input type="hidden" value="put" name="_method"/> .... </form>
  68. 68. The PUT&DELETE Cheat • PUT&DELETE method • Rails _method <form id="edit_events_1" method="post" action="/events/1"> <input type="hidden" value="put" name="_method"/> .... </form>
  69. 69. The PUT&DELETE Cheat • PUT&DELETE method • Rails _method <form id="edit_events_1" method="post" action="/events/1"> <input type="hidden" value="put" name="_method"/> .... </form> <a onclick="var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute ('name', '_method'); m.setAttribute('value', 'delete'); f.appendChild(m);f.submit ();return false;" href="/events/1">Destroy</a>
  70. 70. The PUT&DELETE Cheat • PUT&DELETE method • Rails _method <form id="edit_events_1" method="post" action="/events/1"> <input type="hidden" value="put" name="_method"/> .... </form> <a onclick="var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute ('name', '_method'); m.setAttribute('value', 'delete'); f.appendChild(m);f.submit ();return false;" href="/events/1">Destroy</a>
  71. 71. The Problem
  72. 72. The Problem • HTML GET/POST HTML forms PUT/DELETE
  73. 73. The Problem • HTML GET/POST HTML forms PUT/DELETE • XmlHttpRequest ( Ajax request) GET/POST/PUT/DELETE/HEAD/OPTIONS
  74. 74. The Problem • HTML GET/POST HTML forms PUT/DELETE • XmlHttpRequest ( Ajax request) GET/POST/PUT/DELETE/HEAD/OPTIONS • Firefox/Safari
  75. 75. The Problem • HTML GET/POST HTML forms PUT/DELETE • XmlHttpRequest ( Ajax request) GET/POST/PUT/DELETE/HEAD/OPTIONS • Firefox/Safari • Opera PUT/DELETE
  76. 76. The Problem • HTML GET/POST HTML forms PUT/DELETE • XmlHttpRequest ( Ajax request) GET/POST/PUT/DELETE/HEAD/OPTIONS • Firefox/Safari • Opera PUT/DELETE • IE DELETE !
  77. 77. As a Ruby On Rails special, Prototype also reacts to other verbs (such as 'put' and 'delete' by actually using 'post' and putting an extra '_method' parameter with the originally requested method in there.)
  78. 78. The type of request to make ("POST" or "GET"), default is "GET". Note: Other HTTP request methods, such as PUT Text and DELETE, can also be used here, but they are not supported by all browsers.
  79. 79. 4 HTTP methods, 4 URL helper, 7 actions Helper GET POST PUT DELETE /events/1 /events/1 /events/1 event_path(@event) show update destroy /events /events events_path index create /events/1/edit edit_event_path(@event) edit /events/new new_events_path new It’s beauty, but RESTful can handle the real & complex world ?
  80. 80. standardization on action name The heart of the Rails’s REST support is a technique for creating bundles of named routes automatically From Rails Way Chap.4
  81. 81. RESTful Scaffold
  82. 82. respond_to ?
  83. 83. One Action, Multiple Response Formats def index @users = User.find(:all) respond_to do |format| format.html # index.html.erb format.xml { render :xml => @user.to_xml } end end
  84. 84. format.html
  85. 85. format.html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <p> ihower at 2008-01-19 </p> </body> </html>
  86. 86. format.html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <p> ihower at 2008-01-19 </p> </body> </html> format.xml
  87. 87. format.html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <p> ihower at 2008-01-19 </p> </body> </html> format.xml <?xml version="1.0" encoding="UTF-8"?> <user> <created-at type="datetime">2008-01-19T09:55:32+08:00</created-at> <id type="integer">2</id> <name>ihower</name> <updated-at type="datetime">2008-01-19T09:55:32+08:00</updated-at> </user>
  88. 88. you don't need this! def show_html @users = User.find(:all) end def show_xml @users = User.find(:all) render :xml => @user.to_xml end def show_json @user = User.find(:all) render :json => @user.to_json end
  89. 89. you don't need this! def show_html @users = User.find(:all) end def show_xml @users = User.find(:all) render :xml => @user.to_xml end def show_json @user = User.find(:all) render :json => @user.to_json end
  90. 90. you don't need this! def show_html @users = User.find(:all) end def show_xml @users = User.find(:all) render :xml => @user.to_xml end def show_json @user = User.find(:all) render :json => @user.to_json end
  91. 91. action Don’t repeat yourself
  92. 92. formats • format.html • format.csv • format.xml • format.xls • format.js • format.yaml • format.json • format.txt • format.atom • more.... • format.rss
  93. 93. http://registrano.com/events/5381ae/attendees
  94. 94. http://registrano.com/events/5381ae/attendees
  95. 95. http://registrano.com/events/5381ae/attendees http://registrano.com/events/5381ae/attendees.csv
  96. 96. http://registrano.com/events/5381ae/attendees http://registrano.com/events/5381ae/attendees.csv http://registrano.com/events/5381ae/attendees.xls
  97. 97. ( UI )
  98. 98. XML API JSON API ( UI )
  99. 99. XML API JSON API ( UI ) Adobe Flex
  100. 100. Rails: how to know?
  101. 101. URL http://localhost:3000/users.xml template <%= link_to ‘User List’, formatted_users_path(:xml) %> <a href=”/users.xml”>User List</a>
  102. 102. HTTP request Headers GET /users HTTP/1.1 Host: localhost:3000 User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; zh-TW; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13 Accept: text/javascript, text/html, application/xml, text/xml, */* Accept-Language: zh-tw,en-us;q=0.7,en;q=0.3 Accept-Encoding: gzip,deflate Accept-Charset: Big5,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive X-Requested-With: XMLHttpRequest X-Prototype-Version: 1.6.0.1
  103. 103. HTTP request Headers GET /users HTTP/1.1 Host: localhost:3000 User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; zh-TW; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13 Accept: text/javascript, text/html, application/xml, text/xml, */* Accept-Language: zh-tw,en-us;q=0.7,en;q=0.3 Accept-Encoding: gzip,deflate Accept-Charset: Big5,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Javascript Ajax X-Requested-With: XMLHttpRequest request X-Prototype-Version: 1.6.0.1
  104. 104. • params[:format] GET /users/1?format=xml
  105. 105. • Controller code class ApplicationController < ActionController::Base before_filter :adjust_format_for_iphone helper_method :iphone_user_agent? protected def adjust_format_for_iphone request.format = :iphone if iphone_user_agent? || iphone_subdomain? end # Request from an iPhone or iPod touch? # (Mobile Safari user agent) def iphone_user_agent? request.env["HTTP_USER_AGENT" ] && request.env["HTTP_USER_AGENT" ][/(Mobile/.+Safari)/] end def iphone_subdomain? return request.subdomains.first == "iphone" end end
  106. 106. custom format # config/initializers/mime_types.rb Mime::Type.register ‘audio/mpeg’, :mp3?Mime::Type.register ‘audio/mpegurl’, :m3u
  107. 107. custom format # config/initializers/mime_types.rb Mime::Type.register ‘audio/mpeg’, :mp3?Mime::Type.register ‘audio/mpegurl’, :m3u def show @mp3 = Mp3.find(params[:id]) respond_to do |format| format.html format.mp3 { redirect_to @mp3.url } format.m3u { render :text => @mp3.url } end end
  108. 108. custom format # config/initializers/mime_types.rb Mime::Type.register ‘audio/mpeg’, :mp3?Mime::Type.register ‘audio/mpegurl’, :m3u http://localhost:3000/mp3s/1.mp3 def show @mp3 = Mp3.find(params[:id]) respond_to do |format| format.html format.mp3 { redirect_to @mp3.url } format.m3u { render :text => @mp3.url } end end
  109. 109. custom format # config/initializers/mime_types.rb Mime::Type.register ‘audio/mpeg’, :mp3?Mime::Type.register ‘audio/mpegurl’, :m3u http://localhost:3000/mp3s/1.mp3 def show @mp3 = Mp3.find(params[:id]) respond_to do |format| format.html format.mp3 { redirect_to @mp3.url } format.m3u { render :text => @mp3.url } end end
  110. 110. template ?
  111. 111. template • format (minetype) template generator (renderer) • Rails2 action.minetype.renderer filename.html.erb
  112. 112. template def index @users = User.find(:all) respond_to do |format| format.html # index.html.erb format.xml # index.xml.builder end end
  113. 113. template def index @users = User.find(:all) respond_to do |format| format.html # index.html.erb format.xml # index.xml.builder end end
  114. 114. erb template • ruby code • HTML ( format.html) <h1><%= @event.name %></h1>
  115. 115. erb template • ruby code • HTML ( format.html) <h1><%= @event.name %></h1>
  116. 116. erb template • ruby code • HTML ( format.html) <h1><%= @event.name %></h1> <h1>OSDC 2008</h1>
  117. 117. erb template • ruby code • HTML ( format.html) <h1><%= @event.name %></h1> show.html.erb <h1>OSDC 2008</h1>
  118. 118. builder template • Ruby XML xml.instruct! xml.title "This is a title" xml.person do xml.first_name "Ryan" xml.last_name "Raaum" end
  119. 119. builder template • Ruby XML xml.instruct! xml.title "This is a title" xml.person do xml.first_name "Ryan" xml.last_name "Raaum" end
  120. 120. builder template • Ruby XML xml.instruct! xml.title "This is a title" xml.person do xml.first_name "Ryan" xml.last_name "Raaum" end <?xml version="1.0" encoding="UTF-8"?> <title>This is a title</title> <person> <first_name>Ryan</first_name> <last_name>Raaum</last_name> </person>
  121. 121. builder template • Ruby XML xml.instruct! xml.title "This is a title" xml.person do xml.first_name "Ryan" xml.last_name "Raaum" end show.xml.builder <?xml version="1.0" encoding="UTF-8"?> <title>This is a title</title> <person> <first_name>Ryan</first_name> <last_name>Raaum</last_name> </person>
  122. 122. builder template (cont.) • Atom feed Rails2 Atom helper atom_feed do |feed| feed.title( @feed_title ) feed.updated((@events.first.created_at)) for event in @events feed.entry(event) do |entry| index.atom.builder entry.title(event.title) entry.content(event.description, :type => 'html') entry.author do |author| author.name( event.creator.nickname ) end end end end
  123. 123. Ajax on Rails
  124. 124. Ajax link_to_remote ‘Terms’, :url => terms_path, :update => ‘content’ <a onclick="$.ajax({async:true, beforeSend:function(xhr) {xhr.setRequestHeader ('Accept', 'text/html, */*')}, complete:function(request){ $("#content").html(request.responseText);}, dataType:'html', type:'get', url:'/terms'}); return false;" href="/terms"> </a> <div id=”content”> #content Browser </div> HTML <h1>ABC</j1> Ajax format.html <ul> <li>1</li> <li>2</li> </ul> Server
  125. 125. Ajax (1)
  126. 126. Ajax <a onclick="$.ajax({async:true, beforeSend:function(xhr) {xhr.setRequestHeader ('Accept', 'text/javascript, text/html, application/xml, text/xml, */*')}, dataType:'script', type:'get', url:'/user/1'}); return false;>User</a> <div id=”content”> </div> Javascript Browser $("#content").html(ʻ blahʼ); Ajax format.js $(“#sidebar”).html(ʻ blahʼ); $("#content").effect("highlight"); Server
  127. 127. RJS template <%= link_to_remote ‘ajax show’, :url => event_path(@event) %>
  128. 128. RJS template <%= link_to_remote ‘ajax show’, :url => event_path(@event) %>
  129. 129. RJS template <%= link_to_remote ‘ajax show’, :url => event_path(@event) %> def show @event = Event.find(params[:id]) respond_to |format| format.js end end
  130. 130. RJS template <%= link_to_remote ‘ajax show’, :url => event_path(@event) %> def show @event = Event.find(params[:id]) • respond_to |format| format.js end Ruby Javascript end
  131. 131. RJS template <%= link_to_remote ‘ajax show’, :url => event_path(@event) %> def show @event = Event.find(params[:id]) • respond_to |format| format.js end Ruby Javascript end # show.js.rjs page.replace_html ‘content’, :partial =>’event’ page.visual_effect :highlight, ‘ content’
  132. 132. RJS template <%= link_to_remote ‘ajax show’, :url => event_path(@event) %> def show @event = Event.find(params[:id]) • respond_to |format| format.js end Ruby Javascript end # show.js.rjs page.replace_html ‘content’, :partial =>’event’ page.visual_effect :highlight, ‘ content’
  133. 133. RJS template <%= link_to_remote ‘ajax show’, :url => event_path(@event) %> def show @event = Event.find(params[:id]) • respond_to |format| format.js end Ruby Javascript end # show.js.rjs page.replace_html ‘content’, :partial =>’event’ page.visual_effect :highlight, ‘ content’
  134. 134. RJS template <%= link_to_remote ‘ajax show’, :url => event_path(@event) %> def show @event = Event.find(params[:id]) • respond_to |format| format.js end Ruby Javascript end # show.js.rjs page.replace_html ‘content’, :partial =>’event’ page.visual_effect :highlight, ‘ content’ try { new Element.update("content", "blah"); new Effect.Highlight("content",{}); } catch (e) { alert('RJS error:nn' + e.toString()); alert('new Element.update ("content", "blah");nnew Effect.Highlight("content",{});'); throw e }
  135. 135. RJS template <%= link_to_remote ‘ajax show’, :url => event_path(@event) %> def show @event = Event.find(params[:id]) • respond_to |format| format.js end Ruby Javascript end # show.js.rjs page.replace_html ‘content’, :partial =>’event’ page.visual_effect :highlight, ‘ content’ Browser Server Javascript code try { new Element.update("content", "blah"); new Effect.Highlight("content",{}); } catch (e) { alert('RJS error:nn' + e.toString()); alert('new Element.update ("content", "blah");nnew Effect.Highlight("content",{});'); throw e }
  136. 136. Ajax (2)
  137. 137. inline RJS def show @note = Note.find(params[:id]) respond_to |format| format.js { render :update do |page| page.replace_html ‘content’, :partial =>’note’ page.visual_effect :highlight, ‘ content’ page << ‘alert(“hello world!”);’ end } end end
  138. 138. inline RJS def show @note = Note.find(params[:id]) respond_to |format| format.js { render :update do |page| page.replace_html ‘content’, :partial =>’note’ page.visual_effect :highlight, ‘ content’ page << ‘alert(“hello world!”);’ end } end end
  139. 139. inline RJS def show @note = Note.find(params[:id]) respond_to |format| format.js { render :update do |page| page.replace_html ‘content’, :partial =>’note’ page.visual_effect :highlight, ‘ content’ page << ‘alert(“hello world!”);’ end } end JavaScript end
  140. 140. js.erb template <%=link_to_remote ‘ajax’, :url => posts_path %> def index ... • JavaScript • Rails 1.x respond_to |format| end format.js hack! end (google MinusMOR plugin) # index.js.erb $j("#foo").html(<%= (render :partial => 'note.html').to_json %>); $j("#foo").Highlight();
  141. 141. respond_to : Graceful Degradation def index @users = User.find(:all) respond_to do |format| format.js { render :update do |page| page.replace_html ‘content’, ‘<p>blah</p>’ end } format.html #index.html.erb end end <a href=”/users” onclick=”$.ajax(...blah...);return false;”>
  142. 142. respond_to : Graceful Degradation def index @users = User.find(:all) respond_to do |format| format.js { render :update do |page| page.replace_html ‘content’, ‘<p>blah</p>’ end } format.html #index.html.erb end end <a href=”/users” onclick=”$.ajax(...blah...);return false;”>
  143. 143. respond_to : Graceful Degradation def index @users = User.find(:all) respond_to do |format| format.js { render :update do |page| page.replace_html ‘content’, ‘<p>blah</p>’ end } format.html #index.html.erb end end Browser Javascript <a href=”/users” onclick=”$.ajax(...blah...);return false;”>
  144. 144. respond_to : Graceful Degradation def index @users = User.find(:all) respond_to do |format| format.js { render :update do |page| page.replace_html ‘content’, ‘<p>blah</p>’ end } format.html #index.html.erb end end Browser Javascript <a href=”/users” onclick=”$.ajax(...blah...);return false;”>
  145. 145. respond_to : Graceful Degradation def index @users = User.find(:all) respond_to do |format| format.js { render :update do |page| page.replace_html ‘content’, ‘<p>blah</p>’ end } format.html #index.html.erb end end Browser Javascript Browser Javascript <a href=”/users” onclick=”$.ajax(...blah...);return false;”>
  146. 146. Ajax $.ajax({ async:true, beforeSend:function(xhr) {xhr.setRequestHeader('Accept', 'text/xml, */*')}, type: “get”, url: “/users”, dataType: “xml”, success: function(xml){ // js blah code // js blah code Browser // js blah code }); XML data DOM <data title=”title”> Ajax format.xml <item>1</item> <item>2</item> <item>3</item> </data> Server
  147. 147. Ajax $.ajax({ async:true, beforeSend:function(xhr) {xhr.setRequestHeader('Accept', 'text/json, */*')}, type: “get”, url: “/users”, dataType: “json”, success: function(json){ // js blah code // js blah code Browser // js blah code }); json data DOM [{"name": "aaaa", "updated_at": "2008/01/19 09:55:32 +0800", "id": 2, Ajax format.json "created_at": "2008/01/19 09:55:32 +0800"}, {"name": "bbbb222", "updated_at": "2008/01/19 09:56:11 +0800", "id": 3, "created_at": "2008/01/19 09:55:40 +0800"}] Server
  148. 148. RESTful 7 actions ?
  149. 149. action event has many attendees
  150. 150. Model design class Event < ActiveRecord::Base has_many :attendees end class Attendee < ActiveRecord::Base belongs_to :event end
  151. 151. nested resources(1) controller attendees map.resources :events do |event| event.resources :attendees, :controller => 'event_attendees' end
  152. 152. nested resources(1) controller attendees map.resources :events do |event| event.resources :attendees, :controller => 'event_attendees' end <%= link_to ‘event attendees’, event_attendees_path(@event) %>
  153. 153. nested resources(1) controller attendees map.resources :events do |event| event.resources :attendees, :controller => 'event_attendees' end <%= link_to ‘event attendees’, event_attendees_path(@event) %>
  154. 154. nested resources(1) controller attendees map.resources :events do |event| event.resources :attendees, :controller => 'event_attendees' end /events/2/attendees <%= link_to ‘event attendees’, event_attendees_path(@event) %>
  155. 155. nested resources(1) controller attendees map.resources :events do |event| event.resources :attendees, :controller => 'event_attendees' end /events/2/attendees <%= link_to ‘event attendees’, event_attendees_path(@event) %> class EventAttendeesController < ApplicationController def index @attendees = Event.find(params[:event_id]).attendees end ... end
  156. 156. nested resources(2) <%= link_to ‘show’, event_attendees_path(@event,@attendee) %>
  157. 157. nested resources(2) /events/2/attendees/3 <%= link_to ‘show’, event_attendees_path(@event,@attendee) %>
  158. 158. nested resources(2) /events/2/attendees/3 <%= link_to ‘show’, event_attendees_path(@event,@attendee) %> class EventAttendeesController < ApplicationController before_filter :find_event def show @attendees = @event.attendees.find(params[:id]) end protected def find_event @event = Event.find(params[:event_id]) end end
  159. 159. nested resources(2) /events/2/attendees/3 <%= link_to ‘show’, event_attendees_path(@event,@attendee) %> class EventAttendeesController < ApplicationController before_filter :find_event def show @attendees = @event.attendees.find(params[:id]) end Q: ?? protected Attendee.find(params[:id]) def find_event @event = Event.find(params[:event_id]) end end
  160. 160. nested resources(2) /events/2/attendees/3 <%= link_to ‘show’, event_attendees_path(@event,@attendee) %> class EventAttendeesController < ApplicationController before_filter :find_event def show @attendees = @event.attendees.find(params[:id]) end Q: ?? protected Attendee.find(params[:id]) def find_event @event = Event.find(params[:event_id]) Ans: end Scope Access end
  161. 161. Deep Nesting? Resources should never be nested more than one level deep.
  162. 162. action event memberships
  163. 163. Model design class Event < ActiveRecord::Base has_many :memberships has_many :users, :through => :memberships end class User < ActiveRecord::Base has_many :memberships has_many :events, :through => :memberships end class Membership < ActiveRecord::Base belongs_to :event belongs_to :user end
  164. 164. RESTful design 1 map.resources :memberships
  165. 165. RESTful design 1 map.resources :memberships class MembershipsController < ApplicationController # POST /memberships?group_id=2&user_id=1 def create end # DELETE /memberships/3 def destroy end end
  166. 166. RESTful design 2 map.resources :groups do |group| group.resources :memberships end
  167. 167. RESTful design 2 map.resources :groups do |group| group.resources :memberships end class MembershipsController < ApplicationController # POST /group/2/memberships/?user_id=1 def create end # DELETE /group/2/memberships/3 def destroy end end
  168. 168. action event has one map
  169. 169. singular resource route • resources map.resources :events • resource map.resources :events do |event| event.resource :map, :controller => ‘event_maps’ end
  170. 170. singular resource route • resources map.resources :events • resource map.resources :events do |event| event.resource :map, :controller => ‘event_maps’ end RESTful controller
  171. 171. singular resource route (cont.) • URL helper • index action • show, edit update URL Helper id <%= link_to ‘Login’, event_map_path(@event) %> <% form_for :event_map, :url => event_map_path(@event) do |f| %>
  172. 172. action operate event state (open/closed)
  173. 173. map.resources :events do |event| event.resource :closure, :controller => 'event_closures' end
  174. 174. map.resources :events do |event| event.resource :closure, :controller => 'event_closures' end class EventClosuresController < ApplicationController # POST /events/3/closure def create Event.find(params[:event_id]).close! end # DELETE /events/3/closure def destroy Event.find(params[:event_id]).open! end end
  175. 175. map.resources :events do |event| event.resource :closure, :controller => 'event_closures' end class EventClosuresController < ApplicationController # POST /events/3/closure def create Event.find(params[:event_id]).close! end # DELETE /events/3/closure def destroy Event.find(params[:event_id]).open! end end <%= link_to ‘close’, event_closure_path(@event), :method => :post %>
  176. 176. map.resources :events do |event| event.resource :closure, :controller => 'event_closures' end class EventClosuresController < ApplicationController # POST /events/3/closure def create Event.find(params[:event_id]).close! end # DELETE /events/3/closure def destroy Event.find(params[:event_id]).open! end end <%= link_to ‘close’, event_closure_path(@event), :method => :post %> <%= link_to ‘open’, event_closure_path(@event), :method => :delete %>
  177. 177. why not PUT closed=1 to /events/2 Text
  178. 178. why not PUT closed=1 to /events/2 Text “a separate resource” or “an attribute of event”
  179. 179. action search event
  180. 180. Extra Collection Routes map.resources :events, :collection => { :search => :get }
  181. 181. Extra Collection Routes map.resources :events, :collection => { :search => :get } class EventsController < ApplicationController def search @events = Event.find_by_keyword(params[:keyword]) end end
  182. 182. Extra Collection Routes map.resources :events, :collection => { :search => :get } class EventsController < ApplicationController def search @events = Event.find_by_keyword(params[:keyword]) end end <%= link_to ‘search’, search_events_path, :keyword => ‘osdc’ %>
  183. 183. action a event dashboard
  184. 184. Extra Member Routes map.resources :events, :member => { :dashboard => :get }
  185. 185. Extra Member Routes map.resources :events, :member => { :dashboard => :get } class EventsController < ApplicationController def dashboard @event = Event.find(params[:id]) end end
  186. 186. Extra Member Routes map.resources :events, :member => { :dashboard => :get } class EventsController < ApplicationController def dashboard @event = Event.find(params[:id]) end end <%= link_to ‘dashboard’, dashboard_event_path(event) %>
  187. 187. Route Customizations is not RESTful ??
  188. 188. Route Customizations is not RESTful ?? • you can think of it as a sub-resource of events resource. (and the sub-resource has only one action)
  189. 189. Route Customizations is not RESTful ?? • you can think of it as a sub-resource of events resource. (and the sub-resource has only one action) • If you have too many extra routes, you should consider another resources.
  190. 190. action sorting event
  191. 191. Use query variables • Need not new resource def index sort_by = (params[:order] == ‘name’) ? ‘name’ : ‘created_at’ @events = Event.find(:all, :order => sort_by) end
  192. 192. Use query variables • Need not new resource def index sort_by = (params[:order] == ‘name’) ? ‘name’ : ‘created_at’ @events = Event.find(:all, :order => sort_by) end <%= link_to ‘search’, events_path, :order => ‘name’ %>
  193. 193. action event admin
  194. 194. namespace map.namespace :admin do |admin| admin.resources :events end
  195. 195. namespace map.namespace :admin do |admin| admin.resources :events end # /app/controllers/admin/events_controller.rb class Admin::EventsController < ApplicationController before_filter :require_admin def index .... end end
  196. 196. Considerations(1)
  197. 197. Considerations(1) • a REST resource does not map directly to model. It’s high-level abstractions of what’s available through your web app. (Not always 1-to-1, maybe 1-to-many or 1-to-zero)
  198. 198. Considerations(1) • a REST resource does not map directly to model. It’s high-level abstractions of what’s available through your web app. (Not always 1-to-1, maybe 1-to-many or 1-to-zero) • You don’t need to use all 7 actions if you don’t need them.
  199. 199. map.resource :session # This controller handles the login/logout function of the site. class SessionsController < ApplicationController def create self.current_user = User.authenticate(params[:login], params[:password]) if logged_in? redirect_back_or_default('/') else render :action => 'new' end end def destroy self.current_user.forget_me if logged_in? cookies.delete :auth_token reset_session redirect_back_or_default('/') end end
  200. 200. map.resource :session # This controller handles the login/logout function of the site. class SessionsController < ApplicationController def create self.current_user = User.authenticate(params[:login], params[:password]) if logged_in? redirect_back_or_default('/') else render :action => 'new' end end def destroy self.current_user.forget_me if logged_in? cookies.delete :auth_token reset_session redirect_back_or_default('/') end end => session
  201. 201. Considerations(2) • a RESTful controller may represent the creation or delete of only a concept. For example, a SpamsController create spam by changing a comment’s status to spam without adding any records to the DB.
  202. 202. Considerations(3)
  203. 203. Considerations(3) • one resources should be associated with one controller. (well, you can use one controller handle more than one resources)
  204. 204. Considerations(3) • one resources should be associated with one controller. (well, you can use one controller handle more than one resources) • offload privileged views into either a different controller or action.
  205. 205. 1 attendee Model 2 Resources related (2 Controller)
  206. 206. map.resources :attendees 1 attendee Model 2 Resources related (2 Controller)
  207. 207. map.resources :attendees 1 attendee Model 2 Resources related (2 Controller)
  208. 208. map.resources :attendees class AttendeeController < ApplicationController before_filter :manager_required def show @person = @event.attendees.find(params[:id]) end end 1 attendee Model 2 Resources related (2 Controller)
  209. 209. for event manager map.resources :attendees class AttendeeController < ApplicationController before_filter :manager_required def show @person = @event.attendees.find(params[:id]) end end 1 attendee Model 2 Resources related (2 Controller)
  210. 210. for event manager map.resources :attendees class AttendeeController < ApplicationController before_filter :manager_required def show @person = @event.attendees.find(params[:id]) end end map.resources :registers 1 attendee Model 2 Resources related (2 Controller)
  211. 211. for event manager map.resources :attendees class AttendeeController < ApplicationController before_filter :manager_required def show @person = @event.attendees.find(params[:id]) end end map.resources :registers 1 attendee Model 2 Resources related (2 Controller)
  212. 212. for event manager map.resources :attendees class AttendeeController < ApplicationController before_filter :manager_required def show @person = @event.attendees.find(params[:id]) end end map.resources :registers class RegistersController < ApplicationController 1 attendee Model before_filter :login_required 2 Resources related def show @person = current_user.registers.find(params[:id]) (2 Controller) end end
  213. 213. for event manager map.resources :attendees class AttendeeController < ApplicationController before_filter :manager_required def show @person = @event.attendees.find(params[:id]) end end map.resources :registers for attendeeing user class RegistersController < ApplicationController 1 attendee Model before_filter :login_required 2 Resources related def show @person = current_user.registers.find(params[:id]) (2 Controller) end end
  214. 214. [namespace]_[custom route]_[parent resource]_event[s]_path( [p,] e)
  215. 215. new, edit ? [namespace]_[custom route]_[parent resource]_event[s]_path( [p,] e)
  216. 216. new, edit nested ? [namespace]_[custom route]_[parent resource]_event[s]_path( [p,] e)
  217. 217. new, edit nested ? ? ? [namespace]_[custom route]_[parent resource]_event[s]_path( [p,] e)
  218. 218. new, edit nested ? ? ? [namespace]_[custom route]_[parent resource]_event[s]_path( [p,] e) :method => GET | POST | PUT | DELETE
  219. 219. so, what’s REST style?
  220. 220. Nouns
  221. 221. Nouns URL
  222. 222. Nouns URL
  223. 223. Nouns URL Verb
  224. 224. Nouns URL Verb finite HTTP methods
  225. 225. URL without Nouns action URL Verb finite HTTP methods
  226. 226. URL without Nouns action URL Verb finite HTTP methods
  227. 227. URL without Nouns action URL Verb Content Types finite HTTP methods
  228. 228. URL without Nouns action URL Verb Content Types finite HTTP methods HTML, XML, JSON....etc
  229. 229. URL without Nouns the resource have action URL many representations Verb Content Types finite HTTP methods HTML, XML, JSON....etc
  230. 230. URL without Nouns the resource have action URL many representations Verb Content Types finite HTTP methods HTML, XML, JSON....etc
  231. 231. REST end.
  232. 232. Reference books: • Rails Way(Addison Wesley) • Advanced Rails (Pragmatic) • Code Review PDF (peepcode.com) • Rails2 PDF (peepcode.com) • RESTful Web Services (O’REILLY)
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×