Je moet in gedachten houden dat Rails een opiniërend framework is en een van de onderliggende principes is conventie boven configuratie. Dit betekent dat, tenzij je een zeer goede reden hebt, je geen van de bovenstaande patronen en naamgevingsconventies moet doorbreken.
Als ik bijvoorbeeld zou besluiten om de index-actie als get 'cars/index'
of de create-actie als post 'car/create'
te coderen, zou dit het out-of-the-box Rails-gedrag voor helpermethoden zoals form_for, link_to en redirect_to doorbreken.
Het gevolg van het breken van conventies zonder opzet is chaos en het produceert een warrige puinhoop die je urenlang tegen het raamwerk zal laten vechten.
Singular Resource Routes
Het is ook mogelijk om routes voor singuliere bronnen te hebben, dat wil zeggen, bronnen die kunnen worden opgezocht zonder een :id
te specificeren, zoals degene die van toepassing zijn op een current_user
of current_account
. Dit kan worden bereikt door gebruik te maken van Rails’ ingebouwde enkelvoudige resources.
resource :profile
Nested Routes
Soms moeten we resources in andere resources nesten. Als ik bijvoorbeeld een boeking wil maken voor een specifieke auto in mijn garage, kan het handiger zijn om de :id
voor die auto uit de URL te halen in plaats van een verborgen veld in het formulier, waarmee door een kwaadwillende gebruiker geknoeid zou kunnen worden. De manier waarop we geneste bronnen maken in rails is als volgt:
resources :cars do
resources :bookings
end
Dit creëert alle zeven CRUD acties voor boekingen genest in auto’s. Meestal heb je echter niet alle zeven routes nodig. En zelfs als je ze nodig hebt, hoeven ze zeker niet allemaal genest te zijn. Laten we zeggen dat wat we nodig hebben de actie is om een boeking te maken (we nemen aan dat het formulier om een boeking te maken in de show actie van de auto staat) en om een bestaande boeking te bewerken en bij te werken. Moet ik de :id
van een auto kennen om een boeking te kunnen wijzigen/bijwerken? Het antwoord is natuurlijk nee. Daarom wil ik dat mijn applicatie reageert op de volgende URL’s:
# 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/
Wie worden gegenereerd door de volgende code:
resources :cars do
resources :bookings, only:
end
resources :bookings, only:
Rails laat ons wijzigen welke van de standaard CRUD routes moeten worden gegenereerd door het leveren van een array van symbolen of een enkel symbool aan de optie :only
of zijn tegengestelde :except
.
Non-CRUD Routes
We zijn niet beperkt tot de zeven CRUD resource routes. We kunnen ook routes specificeren die van toepassing zijn op een enkele resource (member routes) of op meerdere resources (collection routes).
Member routes
Doorgaand op ons RubyGarage voorbeeld, auto’s kunnen worden geparkeerd in of verwijderd uit de garage. Laten we ons voorstellen dat we controller-acties hebben die in staat zijn om deze acties uit te voeren die een of ander attribuut van een specifieke auto wijzigen.
resources :cars do
member do
patch :park
patch :remove
end
end
Het bovenstaande stelt ons in staat om patch-verzoeken naar http://my-ruby-garage.com/cars/:id/park
en http://my-ruby-garage.com/cars/:id/remove
te sturen en de specifieke auto in de controller te vinden voordat de resource dienovereenkomstig wordt gewijzigd.
Collection routes
Op dezelfde manier willen we soms een of andere actie op meerdere resources tegelijkertijd uitvoeren. Bijvoorbeeld, misschien moeten we een verzameling auto’s in één keer parkeren en verwijderen. We kunnen de volgende code gebruiken:
resources :cars do
collection do
post :park, as: :bulk_park
post :remove, as: :bulk_remove
end
end
Hier stellen we onze applicatie in om te reageren op http://my-ruby-garage.com/cars/park
en http://my-ruby-garage.com/cars/remove
en, respectievelijk, deze acties bulk_park en bulk_remove te noemen. Onthoud dat we named paths kunnen gebruiken om de URL paden binnen onze applicatie te genereren. Om een padlink te maken om een verzameling auto’s te parkeren, zouden we kunnen gebruiken:
<%= link_to "Park Cars", bulk_park_path, method: :post, class: "button" %>
Namespaced Routes
Namespaced routes geven een voorvoegsel aan het URL pad van de bronnen binnen het namespace blok en zullen proberen om de relevante controllers te lokaliseren onder een module met dezelfde naam als de namespace. Typisch gebruik voor dit patroon zijn admin namespaces en api namespaces.
namespace :factory do
resources :cars
end
Dit voorbeeld bouwt de volgende routes:
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
En de controller zou ook namespaced moeten zijn :
class Factory::CarsController < ApplicationController
# ...
end
Scoped Routes
De scope
methode staat ons toe om DRY te blijven en gerelateerde routing regels te bundelen. Wanneer het zonder opties wordt gebruikt, is het vergelijkbaar met namespace
, maar de relevante controllers hoeven geen namespaced te zijn met een module.
scope :factory do
resources :cars
end
Genereert de volgende routes:
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 ondersteunt drie opties: module, pad en als.
Laten we zeggen dat we een multi-step wizard hebben om een nieuwe auto in de fabriek te maken die wordt afgehandeld door een controller die in een wizards module zit. We willen dat het pad in de browser verschijnt als http://my-ruby-garage.com/create-a-car
en dat we in onze applicatie naar deze route kunnen verwijzen als create_car
. Er is een module Wizard::Car
die elke stap van de wizard kent.
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
De bovenstaande code creëert hetzelfde patroon voor elke stap. Bijvoorbeeld, stap1 is toegankelijk in de browser via de URL http://my-ruby-garage.com/create-a-car/step1
, het bijbehorende formulier stuurt een post request naar http://my-ruby-garage.com/create-a-car/validate-step
, en het pad kan worden opgeroepen door create_car_step1_path
aan te roepen.
Route Redirects
Rails laat ons ook direct redirects doen in routes.rb
. In het voorgaande voorbeeld, wil ik misschien dat iedereen die landt op http://my-ruby-garage.com/create-a-car
automatisch wordt doorgestuurd naar de eerste stap. Dit kan worden bereikt met de volgende code:
get 'create-a-car',
to: redirect("/create-a-car/#{Wizard::SpotAccount::STEPS.first}")
Route Defaults
U kunt standaard parameters in een route definiëren door een hash voor de :defaults
optie door te geven.
resources :cars do
collection do
get :export, defaults: { format: 'csv' }
end
end
Door dit te gebruiken, zal een bezoek aan http://my-ruby-garage.com/cars/export
de export actie in de auto’s controller aanroepen en de corresponderende controller actie zal standaard reageren met een csv.
Route Globbing
Met behulp van wildcard segmenten (fragmenten voorafgegaan door een ster), kunnen we aangeven dat de resterende delen van een route moeten worden gematched met een bepaalde parameter. Dit heet route globbing.
get '/rent/*slugs', to: 'cars#index', as: :rent_cars
Deze route zou overeenkomen met http://my-ruby-garage.com/rent/lisbon/suv-sedan
en de params instellen op “lisbon/suv-sedan”. Dit zou dan gebruikt kunnen worden in een lookup of filter systeem tegen onze database om auto’s te vinden in Lissabon van het type suv of sedan.
Ik ben een gem Slugcatcher aan het schrijven met deze functionaliteit in gedachten om Rails modellen te taggen die opgezocht kunnen worden als route slugs.