47.24. REST design

47.24.1. Akce řadiče
47.24.2. Vnořené routy
REST (Representational State Transfer)

RESTfull design je přístup k programování webů který popisuje jakým způsobem se k jednotlivým objektům přistupuje. V podstatě logicky mapuje práci s jednotlivými objekty na url.

Pro přechod k REST budeme tedy měnit nejen řadič, ale také routy jenž k tomuto řadiči povedou.

Tabulka 47.5. RESTFull

standardní metodyslovesourlakce
plural_pathGET/notesindex
singular_path(id)GET/notes/1show
new_singular_pathGET/notes/newnew
singular_pathPOST/notescreate
edit_singular_path(id)GET/notes/1;editedit
singular_path(id)PUT/notes/1update
singular_path(id)DELETE/notes/1destroy

    # GET /notes
    # GET /notes.xml
    # ...
    def index
        @notes = Note.find(:all)
        respond_to do |format|
            format.html # index.html.erb
            format.xml  { render :xml => @notes.to_xml }
            format.rss  { render :xml => @notes.to_rss }
        end
    end

Příklad 47.24. Controller Template

class MyController < ApplicationController

    # GET /notes
    # GET /notes.xml
    def index
        @notes = Note.find(:all)
        respond_to do |format|
            format.html # index.html.erb
            format.xml  { render :xml => @notes.to_xml }
        end
    end

    # GET /notes/1
    # GET /notes/1.xml
    def show
        @note = Note.find(params[:id])
        respond_to do |format|
            format.html # index.html.erb
            format.xml  { render :xml => @notes.to_xml }
        end
    end

    # GET /notes/new
    def new
        @mode = 'new'
        @note = Note.new
    end

    def create
        @note = Note.create(params_hash)
        respond_to do |wants|
            wants.html {redirect_to :action=>:index}
            wants.js   {render}
        end
    end

    def edit
        @note = Note.find(params[:id])
    end

    def update
        object = @note, …
        if Note.update_attributes(params_hash)
            respond_to do |wants|
                wants.html {redirect_to :action=>:index}
                wants.js   {render}
            end
        else
            render
        end
    end

    def destroy
        …
    end
end

Routes

ActionController::Routing::Routes.draw do |map|
    map.resources :notes, :sessions
end

V pohledu index.html.erb

<h1>Přehled poznámek</h1>
<table>
    <tr></tr>
  <% for note in @notes %>
    <tr>
        <td></td>
        <td><%= link_to 'Show', note_path(note) %></td>
        <td><%= link_to 'Edit', edit_note_path(note) %></td>
        <td><%= link_to 'Destroy', note_path(note),
                        :confirm => 'Are you sure?',
                        :method => :delete %></td>
    </tr>
  <% end %>
</table>
<br/>
<%= link_to 'Nová poznámka'm new_note_path %>

Místo odkazů link_to můžeme používat tlačítka button_to. Je třeba vědět, že tlačítka tako vytvářená sebou "nesou" celý formulář. Pokud chceme použít AJAX, budou uvedené řádky vypadat

        <td><%= link_to_remote 'Destroy',
                        :url => note_path(note),
                        :confirm => 'Are you sure?',
                        :method => :delete %></td>

Řadič k mazání záznamů (metoda destroy)

    # DELETE /notes/1
    # DELETE /notes/1.xml
    def destroy
        @note = Note.find(params[:id])
        @note.destroy

        respond_to do |format|
            format.html { redirect_to notes_url }  # go to index
            format.js   # run the destroy.rjs template
            format.xml  { render :nothing = > true }
        end
    end

Smazání poznámky z konzole

$ curl -X DELETE -H "Accept: text/xml" http://localhost:3000/notes/2
$ curl -X DELETE -H "Accept: application/javascript" http://localhost:3000/notes/2

Ošetření chyb. Tedy pokoušíme se smazat něco co již neexistuje

    # DELETE /notes/1
    # DELETE /notes/1.xml
    def destroy
        @note = Note.find(params[:id])
        @note.destroy

        respond_to do |format|
            format.html { redirect_to notes_url }  # go to index
            format.js   # run the destroy.rjs template
            format.xml  { render :nothing = > true }
        end
	rescue Exception => e
	    respond_to do |format|
                format.html { render :text => "Record not found", status => 404 }
                format.js   { render(:update) {|page| page.alert("There was an error")}}
                format.xml  { render :nothing = > true, :status => 404 }
            end
    end

K editaci potřebujeme pohled edit.html.erb

<h1>Editace poznámky</h1>
<% form_for(:note,
            :url => note_path(@note),
            :html => { :method => :put }) do |f| %>

  <label for="">Title
    <%= f.text_field :title, :class => 'text_field' %>
  </label>
  <label for="">Content
    <%= f.text_area :content %>
  </label>

  # nebo výš uvedená pole formuláře umístníme do samsotatného souboru _form.hmtl.erb
  # a zde jen uvedeme partial
  <%= render :partial => 'form', :locals => {:f => f} %>

  <p><%= submit_tag "Update" %></p>
<% end %>
<% link_to 'Show', note_path(@note) %> |
<% link_to 'Back', notes_path %>

Pohled pro vytváření novoho záznamu:

<h1>Nová poznámka</h1>
<% form_for(:note, :url => notes_path) do |f| %>
  <%= render :partial => 'form', :locals => {:f => f} %>

  <p><%= submit_tag "Create" %></p>
  
<% end %>
<% link_to 'Back', notes_path %>
$ script/plugin install http://dev.rubyonrails.org/svn/rails/plugins/simply_helpful/

Formáty použité v respond_to do |format| můžeme rozšířit o vlastní formát. V souboru environment.rb zaragistrujeme mime typ.

Mime::Type.register 'application/vnd.blinksale+xml', :api
Mime::Type.register 'application/vnd.visa+xml, :visa

Máme-li model s relací one_to_many, potřebujeme naprogramovat aplikaci tak, aby v rámci RESTFull principu "rozuměla" url jako:

/notes/1/keywords/6

Příklad 47.25. routes.rb pro vnořenou tabulku /notes/:note_id/keywords/:id

ActionController::Routing::Routes.draw do |map|
    …
    # /notes/:note_id/keywords/:id
    map.resources :notes do |notes|
        notes.resources :keywords, :name_prefix => 'note_'
    end
    map.resources :keywords  

Licence Creative Commons
Tento dokument Ruby, jehož autorem je Radek Hnilica, podléhá licenci Creative Commons Uveďte autora-Nevyužívejte dílo komerčně-Zachovejte licenci 3.0 Česká republika .