Rails 5 Routing Cookbook: 10 ricette per lo sviluppatore Rails alle prime armi e oltre

Si dovrebbe tenere a mente che Rails è un framework con opinioni e uno dei suoi principi fondamentali è la convenzione sulla configurazione. Questo significa che, a meno che non abbiate una buona ragione, non dovreste infrangere nessuno dei pattern e delle convenzioni di denominazione di cui sopra.

Per esempio, se decidessi di codificare l’azione index come get 'cars/index' o l’azione create come post 'car/create', questo infrangerebbe il comportamento out-of-the-box di Rails per metodi helper come form_for, link_to e redirect_to.

La conseguenza di rompere le convenzioni senza intenzione è il caos e produce una confusione ingarbugliata che vi farà combattere contro il framework per ore e ore.

Foto di Daniele Levis Pelusi su Unsplash

Rotte per risorse singolari

È anche possibile avere rotte per risorse singolari, cioè risorse che possono essere cercate senza specificare un :id, come quelle che si applicano a un current_user o current_account. Questo può essere ottenuto usando le risorse singolari incorporate di Rails.

resource :profile 

Rotte annidate

A volte abbiamo bisogno di annidare risorse dentro altre risorse. Per esempio, se voglio creare una prenotazione per una specifica macchina nel mio garage, potrebbe essere più conveniente prendere il :id per quella macchina dall’URL piuttosto che un campo nascosto del modulo, che potrebbe essere manomesso da un utente malintenzionato. Il modo in cui creiamo risorse annidate in rails è il seguente:

resources :cars do 
resources :bookings
end

Questo crea tutte e sette le azioni CRUD per le prenotazioni annidate nelle auto. Di solito, però, non avete bisogno di tutte e sette le azioni. E anche quando lo fate, certamente non tutti hanno bisogno di essere annidati. Diciamo che ciò di cui abbiamo bisogno è l’azione per creare una prenotazione (assumeremo che il modulo per creare una prenotazione viva nell’azione show dell’auto) e per modificare e aggiornare una prenotazione esistente. Ho bisogno di conoscere il :iddi un’auto per modificare/aggiornare una prenotazione? La risposta è ovviamente no. Pertanto, voglio che la mia applicazione risponda ai seguenti URL:

# to CREATE a booking
POST request to http://my-ruby-garage.com/cars/:id/bookings/create# to EDIT a booking
GET request to http://my-ruby-garage.com/bookings/:id/edit# to UPDATE a booking
PATCH request to http://my-ruby-garage.com/bookings/:id/

Che sono generati dal seguente codice:

resources :cars do 
resources :bookings, only:
end
resources :bookings, only:

Rails ci permette di modificare quali percorsi CRUD standard devono essere generati fornendo un array di simboli o un singolo simbolo all’opzione :only o al suo opposto :except.

Rotte non CRUD

Non siamo limitati ai sette percorsi delle risorse CRUD. Possiamo anche specificare delle rotte che si applicano a una singola risorsa (rotte membro) o a più risorse (rotte collezione).

Rotte membro

Continuando con il nostro esempio RubyGarage, le auto possono essere parcheggiate o rimosse dal garage. Immaginiamo di avere delle azioni del controller in grado di eseguire queste azioni che modificano qualche attributo di un’auto specifica.

resources :cars do 
member do
patch :park
patch :remove
end
end

Quanto sopra ci permette di inviare richieste di patch a http://my-ruby-garage.com/cars/:id/park e http://my-ruby-garage.com/cars/:id/remove e trovare l’auto specifica nel controller prima di modificare la risorsa di conseguenza.

Collection routes

Nello stesso modo, a volte vogliamo eseguire qualche azione su diverse risorse allo stesso tempo. Per esempio, forse abbiamo bisogno di parcheggiare e rimuovere una collezione di auto in una volta sola. Possiamo usare il seguente codice:

resources :cars do 
collection do
post :park, as: :bulk_park
post :remove, as: :bulk_remove
end
end

Qui, impostiamo la nostra applicazione per rispondere a http://my-ruby-garage.com/cars/park e http://my-ruby-garage.com/cars/remove e, rispettivamente, chiamiamo queste azioni bulk_park e bulk_remove. Ricordate che possiamo usare i named path per generare i percorsi URL all’interno della nostra applicazione. Per costruire un percorso di collegamento per parcheggiare una collezione di auto, potremmo usare:

<%= link_to "Park Cars", bulk_park_path, method: :post, class: "button" %>

Namespaced Routes

Namespaced routes prefissa il percorso URL delle risorse all’interno del blocco namespace e cercherà di localizzare i relativi controller sotto un modulo con lo stesso nome del namespace. Usi tipici di questo pattern sono i namespace admin e i namespace api.

namespace :factory do 
resources :cars
end

Questo esempio costruisce le seguenti rotte:

Prefix Verb URI Pattern Controller#Action factory_cars GET /factory/cars(.:format) factory/cars#index
POST /factory/cars(.:format) factory/cars#create factory_car GET /factory/cars/:id(.:format) factory/cars#show
PATCH /factory/cars/:id(.:format) factory/cars#update
PUT /factory/cars/:id(.:format) factory/cars#update
DELETE /factory/cars/:id(.:format) factory/cars#destroy

E anche il controller dovrebbe essere namespaced :

class Factory::CarsController < ApplicationController
# ...
end

Scoped Routes

Il metodo scope ci permette di rimanere DRY e raggruppare regole di routing correlate. Quando è usato senza opzioni, è simile a namespace, ma i relativi controller non devono essere namespace con un modulo.

scope :factory do 
resources :cars
end

Genera i seguenti percorsi:

Prefix Verb URI Pattern Controller#Action factory_cars GET /factory/cars(.:format) cars#index
POST /factory/cars(.:format) cars#create factory_car GET /factory/cars/:id(.:format) cars#show
PATCH /factory/cars/:id(.:format) cars#update
PUT /factory/cars/:id(.:format) cars#update
DELETE /factory/cars/:id(.:format) cars#destroy

Scope supporta tre opzioni: module, path e as.

Diciamo che abbiamo un wizard multi-step per creare una nuova auto in fabbrica che è gestito da un controller che vive in un modulo wizards. Vogliamo che il percorso appaia nel browser come http://my-ruby-garage.com/create-a-car e che si possa fare riferimento a questo percorso all’interno della nostra applicazione come create_car. C’è un modulo Wizard::Car che conosce ogni passo della procedura guidata.

scope module: 'wizards', path: 'create-a-car', as: 'create_car' do
Wizard::Car::STEPS.each do |step|
get step, to: "cars##{step}"
end
post :validate-step, to: 'cars#validate_step'
end

Il codice di cui sopra crea lo stesso schema per ogni passo. Per esempio, il passo1 è accessibile nel browser tramite l’URL http://my-ruby-garage.com/create-a-car/step1, il suo modulo corrispondente invia una richiesta di post a http://my-ruby-garage.com/create-a-car/validate-step, e il percorso può essere richiamato chiamando create_car_step1_path.

Route Redirects

Rails ci permette anche di fare redirects direttamente in routes.rb. Nell’esempio precedente, forse voglio che chiunque atterri su http://my-ruby-garage.com/create-a-car sia automaticamente reindirizzato al primo passo. Questo può essere realizzato con il seguente codice:

get 'create-a-car', 
to: redirect("/create-a-car/#{Wizard::SpotAccount::STEPS.first}")

Route Defaults

Puoi definire parametri predefiniti in una rotta passando un hash per l’opzione :defaults.

resources :cars do
collection do
get :export, defaults: { format: 'csv' }
end
end

Utilizzando questo, la visita http://my-ruby-garage.com/cars/export chiamerà l’azione di esportazione nel controller delle auto e l’azione del controller corrispondente risponderà con un csv per default.

Route Globbing

Utilizzando i segmenti jolly (frammenti preceduti da una stella), possiamo specificare che le parti rimanenti di una rotta devono essere abbinate a un particolare parametro. Questo si chiama route globbing.

get '/rent/*slugs', to: 'cars#index', as: :rent_cars

Questa rotta corrisponde a http://my-ruby-garage.com/rent/lisbon/suv-sedan e imposta i parametri su “lisbon/suv-sedan”. Questo potrebbe poi essere usato in un sistema di ricerca o di filtraggio nel nostro database per trovare auto a Lisbona del tipo suv o sedan.

Sto scrivendo un gem Slugcatcher con questa funzionalità in mente al fine di taggare i modelli Rails che possono essere cercati come route slugs.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.