#203 Routing in Rails 3
- Download:
- source codeProject Files in Zip (89 KB)
- mp4Full Size H.264 Video (21.1 MB)
- m4vSmaller H.264 Video (14.3 MB)
- webmFull Size VP8 Video (41.3 MB)
- ogvFull Size Theora Video (28.9 MB)
Contiunuiamo il nostro viaggio alla scoperta delle nuove funzionalità di Rails 3 in questo episodio, concentrandoci sul routing. Rails 3 ha una nuova API per definire le regole di routing ed alcune nuove funzionalità. Per mostrarvi come funzionano, creiamo una nuova applicazione chiamata detour e definiamo alcuni instradamenti per quest’ultima:
rails detour
Creata l’applicazione, diamo un’occhiata al file config/routes.rb
. Il file generato contiene un po’ di documentazione e alcuni esempi di nuove API: non fa male darci uno sguardo. Ora cambieremo il contenuto di default con un esempio di alcuni vecchi instradamenti stile Rails 2 per vedere come cambiano in Rails 3:
Detour::Application.routes.draw do |map| map.resources :products, :member => { :detailed => :get } <<<<<<< HEAD map.resources :forums, :collection => { :sortable => :get, :sort => :put } do |forums| forums.resources :topics end map.root :controller => "home", :action => "index" ======= map.resources :forums, :collection => { :sortable => :get, :sort => :put } do |forums| forums.resources :topics end map.root :controller => "home", :action => "index" >>>>>>> b97aa6197477c8dd0eb373f63b133400a451886f map.about "/about", :controller => "info", :action => "about" end
Gli instradamenti vecchio stile che stiamo per convertire.
Partiamo dal primo instradamento:
map.resources :products, :member = { :detailed => :get }
Questo instradamento ha una risorsa products
e una action specifica member
chiamata detailed
che viene invocata mediante una richiesta GET.
Il primo cambiamento da notare nel meccanismo di routing di Rails 3 è che non trattiamo più con l’oggetto map
, ma piuttosto chiamiamo direttamente resources
nel blocco routes.draw
. Le azioni :member
e :collection
dentro a resources sono definite dentro un blocco. Il route può perciò essere riscritto in questo modo:
resources :products do get :detailed, :on => :member end
Ora guardiamo un instradamento più complicato:
map.resources :forums, :collection => { :sortable => :get, :sort => :put } do |forums| forums.resources :topics end
In questo route abbiamo una risorsa forum con due utleriori elementi collezioni e una risorsa innestata topics. Con la nuova API questo instradamento può essere scritto così:
resources :forums do collection do get :sortable put :sort end resources :topics end
Ancora, usiamo resources
anzichè map.resources
e passiamo un blocco. Abbiamo due azioni collection nel route. Benchè potremmo definire quelle allo stesso modo in cui abbiamo fatto con la azione detailed nel primo route, facendo uso dell’argomento :on
, in questo caso definiremo un blocco collection
(i membri possono essere trattati allo stesso modo che in un blocco member
) e ogni instradamento definito dentro la blocco agirà sulla collezione di forum. Nel nostro blocco abbiamo definito due nuove action: sortable come richiesta di tipo GET e sort come richiesta di tipo PUT.
Per la risorsa innestata topics, chiamiamo nuovamente solo resources
, innestando la risorsa topics dentro a forums.
Il prossimo route che vediamo è un route che definisce il controller e l’action a cui deve puntare l’URL di root:
map.root :controller => "home", :action => "index"
Qui chiamiamo semplicemente root
e usiamo un argomento :to
a cui passiamo una stringa che contiene il nome del controller e dell’action separati dal carattere #.
root :to => "home#index"
Questa possibilità di specificare sia il controller, sia la action in un’unica stringa è una nuova feature di Rails 3. Possiamo fare qualcosa di simile per un instradamento con nome:
map.about "/about", :controller => "info", :action => "about"
Con l’API di Rails 3 tutto ciò può essere riscritto come:
match "/about" => "info#about", :as => :about
Senza l’argomento :as
l’instradamento di sopra sarebbe stato solo un instradamento generico. L’aggiunta di :as
lo rende un instradamento con nome, in modo tale che in seguito si possano usare riferimenti ad URL del tipo about_path
o about_url
.
Nuove funzionalità
Come si può vedere dagli esempi sopra, non è difficile convertire gli instradamenti dalla vecchia API alla nuova, ma ciò che rende realmente interessante la nuova API di routing sono le nuove funzionalità che mette a disposizione e che vedremo ora nel resto dell’episodio.
Parametri opzionali
<<<<<<< HEADI parametri opzionali erano gi&agravi; supportati dalle precedenti versioni di Rails, ma la sintassi era un po’ scomoda. Vediamo cosa è cambiato invece in Rails 3.
=======I parametri opzionali erano già supportati dalle precedenti versioni di Rails, ma la sintassi era un po’ scomoda. Vediamo cosa è cambiato invece in Rails 3.
>>>>>>> b97aa6197477c8dd0eb373f63b133400a451886fSarà più semplice dimostrare l’utilizzo dei parametri opzionali se la nostra applicazione ha un controller, per cui creiamone uno. Creiamo un info
controller con una action about
. Si noti che in Rails 3 possiamo usare rails g
come scorciatoia per il comando rails generate
:
rails g controller info about
Possiamo avviare il server con un altro nuovo shortcut:
rails s
Ora, se visitiamo http://localhost:3000/about vedremo la action info#about
come è stato imposto dall’instradamento che abbiamo scritto poc’anzi.
Una volta sistemato questo, vogliamo ora fornire una versione PDF per questa action, ma se visitiamo l’URL http://localhost:3000/about.pdf otteniamo un errore di routing poichè la nostra applicazione non sa come risolvere questo instradamento.
Nel file di route possiamo cambiare l’instradamento di about
per accettare un parametro di formato, aggiungendo un .
e un :format
:
match "/about.:format" => "info#about", :as => :about
Se ricarichiamo la vista PDF ora, il route sarà risolto, ma avremo un errore per mancanza di template, in quanto la nostra applicazione non sa come renderizzare quella action come PDF.
Sembra che abbiamo risolto il problema, ma il formato che abbiamo appena aggiunto alle regole di routing non è opzionale, per cui quando torniamo indietro alla vista di default per quella pagina, otteniamo un errore di routing. Fortunatamente è facile rendere una parte del route opzionale: tutto ciò che occorre è racchiudere la parte opzionale fra parentesi, in questo modo:
match "/about(.:format)" => "info#about", :as => :about
Ora possiamo visitare sia http://localhost:3000/about, sia http://localhost:3000/about.pdf senza vedere errori di routing.
<<<<<<< HEADOra vi mostreremo come usare parametri opzionali pi&ugravi; complessi. Ipotizziamo che la nostra applicazione abbia un certo numero di articoli di blog che vorremmo filtrare specificando un anno con un mese opzionale (o mese e giorno) nell’URL.
=======Ora vi mostreremo come usare parametri opzionali più complessi. Ipotizziamo che la nostra applicazione abbia un certo numero di articoli di blog che vorremmo filtrare specificando un anno con un mese opzionale (o mese e giorno) nell’URL.
>>>>>>> b97aa6197477c8dd0eb373f63b133400a451886fPossiamo fare ciò definendo un instradamento tipo questo, dirigendo ogni instradamento che fa match alla nostra action info#about
. Si noti come sia possibile innestare le parentesi quando si creano parametri opzionali:
match "/:year(/:month(/:day))" => "info#about"
Nel codice della vista aggiungiamo un po’ di codice di debug in modo tale da poter vedere quali parametri vengano passati:
<h1>Info#about</h1> <p>Find me in app/views/info/about.html.erb</p> <%= debug params %>
Ora, se indichiamo un anno nell’URL, sarà passato alla action about e analogamente, se specifichiamo un anno, un mese e un giorno, tutti e tre i parametri verranno passati.
Questo instradamento è comunque abbastanza generico e se volessimo visitare, per esempio, http://localhost:3000/foo/bar, anche questo URL sarebbe servito dalla action about, anche se noi in realtà vorremmo che solo i parametri che sembrino date siano passati a tale action. Possiamo vare ciò che vorremmo con i vincoli.
Vincoli
I vincoli sono disponibili già da Rails 2, conosciuti col nome di requirements. Possiamo passare un opzione :constraints
al nostro route con un hash di valori ed i vincoli che ciascun valore dovrebbe rispettare. Vincoliamo dunque il route in modo tale che faccia match solo se il parametro anno è di quattro numeri ed il mese e il giorno sono di due numeri:
match "/:year(/:month(/:day))" => "info#about", :constraints => { :year => /\d{4}/, :month => /\d{2}/, :day => /\d{2}/ }
Con questo vincolo, quando visitiamo il percorso /foo/bar
, ora otterremo un errore di routing, dal momento che, come volevamo, il percorso specificato non fa più match con i vincoli sulle date.
Tutto ciò che abbiamo fatto fino ad ora con i vincoli era possibile farlo anche in Rails 2 con i requirements, ma con i vincoli di Rails 3 possiamo in relatà fare molto di più. Per esempio, possiamo usare un parametro user_agent
per restringere il route solo a determinati browser, in questo caso Firefox:
match "/secret" => "info#about", :constraints => { :user_agent => /Firefox/ }
Se visitiamo http:/localhost:3000/secret da Safari, Chrome o Opera, vedremo un errore di routing, ma se lo visitiamo da Firefox, o fingiamo con un altro browser di essere Firefox (modificando di fatto lo user-agent nell’head della richiesta HTTP), allora lo user agent passato dal browser farà match col vincolo e la pagina verrà mostrata:
Potremmo anche aggiungere un vincolo per qualcosa di un po’ più utile, per esempio l’host:
match "/secret" => "info#about", :constraints => { :host => /localhost/ }
Questo vincolo significa che possiamo visitare http://localhost:3000/secret e vedere la pagina, ma otteniamo un errore di routing se proviamo a visitare la pagina usando l’indirizzo IP anzichè localhost( http://127.0.0.1/secret ) nonostante che entrambi siano logicamente equivalenti. Questo trucco può essere utilizzato per restringere determinati instradamenti solo a specifici sottodomini. Al momento il modo per fare ciò è un po’ scomodo, ma in una prossima versione di Rails saremo in grado di passare una opzione :subdomain
per definire questo genere di vincoli.
Se abbiamo un numero di instradamenti che devono fare match con un certo vincolo, potremmo ritrovarci con un po’ di duplicazioni di codice nel file di route. Si può ovviare al problema, in questi casi, usando il metodo constraints e inserendo in un blocco gli instradamenti che devono fare match:
constraints :host => /localhost/ do match "/secret" => "info#about" match "/topsecret" => "info#about" end
Ci sono molte altre cose che si possono fare con l’opzione constraint, ma non esauriremo tutte le potenzialità del comando qui.
Instradamenti con Rack
C’è molto di più sul routing in Rails 3 di quanto non abbiamo il tempo di far vedere in questo episodio, ma ci ritorneremo su negli episodi futuri. Un’ultima funzionalità a cui diamo uno sguardo è il modo in cui il routing di Rails 3 si concilia con Rack. Normalmente passiamo un controller e il nome di una action ad un route, ma possiamo anche passare una applicazione Rack. Per dimostrarlo, creiamo un instradamento che punti ad una semplice applicazione Rack.
match "/hello" => proc { |env| [200, {}, "Hello Rack!"] }
Se non conoscete Rack, guardatevi l’episodio 151. Tutto ciò che stiamo facendo qui, è semplicemente di passare un codice di ritorno, un hash (vuoto) di headers e un semplice body.
Visitando /hello
dal browser ora, vediamo “Hello Rack!”, che significa che la nostra applicazione Rack sta funzionando e generando la risposta. Questo apre la strada ad una enorme potenzialità e flessibilità del modo in cui si definiscono gli instradamenti e li si inoltrano verso applicazioni differenti. E’ora semplice l’instradamento verso una applicazione Sinatra, per esempio.
Le nuove funzionalità di routing di Rails 3 forniscono una serie di interessanti possibilità. Anche se qui abbiamo fornito solo uno sguardo panoramico del potenziale, torneremo più in dettaglio sui singoli argomenti negli episodi futuri.
<<<<<<< HEADPer maggior documentazione sul routing di Rails 3, date un’occhiata al blog di Yehuda Katz e alle RailsGuides.
=======Per maggior documentazione sul routing di Rails 3, date un’occhiata al blog di Yehuda Katz e alle RailsGuides.
>>>>>>> b97aa6197477c8dd0eb373f63b133400a451886f