Należy pamiętać, że Rails jest opiniotwórczym frameworkiem, a jedną z jego podstawowych zasad jest przewaga konwencji nad konfiguracją. Oznacza to, że o ile nie masz bardzo dobrego powodu, nie powinieneś łamać żadnego z powyższych wzorców i konwencji nazewnictwa.
Na przykład, jeśli zdecydowałbym się zakodować akcję index jako get 'cars/index'
lub akcję create jako post 'car/create'
, złamałoby to zachowanie out-of-the-box Rails dla metod pomocniczych takich jak form_for, link_to, i redirect_to.
Konsekwencją łamania konwencji bez zamiaru jest chaos i produkuje on splątany bałagan, który sprawi, że będziesz walczył z frameworkiem przez wiele godzin.
Singular Resource Routes
Możliwe jest również posiadanie tras dla pojedynczych zasobów, czyli zasobów, które mogą być wyszukane bez podawania :id
, takich jak te odnoszące się do current_user
lub current_account
. Można to osiągnąć za pomocą wbudowanych w Railsy zasobów singularnych.
resource :profile
Ruty zagnieżdżone
Czasami potrzebujemy zagnieżdżać zasoby wewnątrz innych zasobów. Na przykład, jeśli chcę utworzyć rezerwację dla konkretnego samochodu w moim garażu, wygodniej jest pobrać :id
dla tego samochodu z adresu URL, niż z ukrytego pola formularza, które mogłoby zostać zmanipulowane przez złośliwego użytkownika. Sposób w jaki tworzymy zagnieżdżone zasoby w rails jest następujący:
resources :cars do
resources :bookings
end
To tworzy wszystkie siedem akcji CRUD dla rezerwacji zagnieżdżonych w samochodach. Zazwyczaj jednak nie potrzebujesz wszystkich siedmiu tras. A nawet jeśli tak jest, z pewnością nie wszystkie z nich muszą być zagnieżdżone. Załóżmy, że potrzebujemy akcji do tworzenia rezerwacji (zakładamy, że formularz do tworzenia rezerwacji znajduje się w akcji pokaż samochód) oraz do edycji i aktualizacji istniejącej rezerwacji. Czy muszę znać :id
samochodu, aby edytować/aktualizować rezerwację? Odpowiedź brzmi oczywiście nie. Dlatego chcę, aby moja aplikacja odpowiadała na następujące adresy 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/
, które są generowane przez następujący kod:
resources :cars do
resources :bookings, only:
end
resources :bookings, only:
Rails pozwala nam modyfikować, które ze standardowych tras CRUD powinny być generowane przez dostarczenie tablicy symboli lub pojedynczego symbolu do opcji :only
lub jej przeciwieństwa :except
.
Trasy nie-CRUD
Nie jesteśmy ograniczeni do siedmiu tras zasobów CRUD. Możemy również określić trasy, które odnoszą się do pojedynczego zasobu (member routes) lub do kilku zasobów (collection routes).
Member routes
Kontynuując nasz przykład RubyGarage, samochody mogą być parkowane w garażu lub z niego usuwane. Wyobraźmy sobie, że mamy akcje kontrolera, które są w stanie wykonać te akcje, które modyfikują jakiś atrybut konkretnego samochodu.
resources :cars do
member do
patch :park
patch :remove
end
end
Powyższe pozwala nam wysłać żądania poprawek do http://my-ruby-garage.com/cars/:id/park
i http://my-ruby-garage.com/cars/:id/remove
i znaleźć konkretny samochód w kontrolerze przed odpowiednią modyfikacją zasobu.
Collection routes
W ten sam sposób, czasami chcemy wykonać jakąś akcję na kilku zasobach w tym samym czasie. Na przykład, może potrzebujemy zaparkować i usunąć kolekcję samochodów na raz. Możemy użyć następującego kodu:
resources :cars do
collection do
post :park, as: :bulk_park
post :remove, as: :bulk_remove
end
end
Tutaj ustawiamy naszą aplikację tak, aby reagowała na http://my-ruby-garage.com/cars/park
i http://my-ruby-garage.com/cars/remove
i odpowiednio nazywamy te akcje bulk_park i bulk_remove. Pamiętaj, że możemy użyć nazwanych ścieżek do wygenerowania ścieżek URL wewnątrz naszej aplikacji. Aby zbudować link ścieżki do parkowania kolekcji samochodów, moglibyśmy użyć:
<%= link_to "Park Cars", bulk_park_path, method: :post, class: "button" %>
Namespaced Routes
Namespaced routes poprzedzają ścieżkę URL zasobów wewnątrz bloku przestrzeni nazw i spróbują zlokalizować odpowiednie kontrolery w module o tej samej nazwie co przestrzeń nazw. Typowe zastosowania dla tego wzorca to przestrzenie nazw adminów i przestrzenie nazw api.
namespace :factory do
resources :cars
end
Ten przykład buduje następujące trasy:
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
A kontroler też musiałby być nazwany :
class Factory::CarsController < ApplicationController
# ...
end
Scoped Routes
Metoda scope
pozwala nam zachować DRY i połączyć razem powiązane reguły routingu. Gdy jest używana bez opcji, jest podobna do namespace
, ale odpowiednie kontrolery nie muszą być nazwane modułem.
scope :factory do
resources :cars
end
Generuje następujące trasy:
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 obsługuje trzy opcje: moduł, ścieżka i as.
Powiedzmy, że mamy wieloetapowy kreator do tworzenia nowego samochodu w fabryce, który jest obsługiwany przez kontroler żyjący w module wizards. Chcemy, aby ścieżka pojawiała się w przeglądarce jako http://my-ruby-garage.com/create-a-car
i abyśmy mogli odwoływać się do tej trasy wewnątrz naszej aplikacji jako create_car
. Istnieje moduł Wizard::Car
, który zna każdy krok kreatora.
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
Powyższy kod tworzy ten sam wzorzec dla każdego kroku. Na przykład, krok 1 jest dostępny w przeglądarce przez URL http://my-ruby-garage.com/create-a-car/step1
, odpowiadający mu formularz wysyła żądanie post do http://my-ruby-garage.com/create-a-car/validate-step
, a ścieżka może być przywołana przez wywołanie create_car_step1_path
.
Route Redirects
Railsy pozwalają nam również robić przekierowania bezpośrednio w routes.rb
. W poprzednim przykładzie, być może chcę, aby każdy kto wyląduje na http://my-ruby-garage.com/create-a-car
został automatycznie przekierowany do pierwszego kroku. Można to osiągnąć za pomocą następującego kodu:
get 'create-a-car',
to: redirect("/create-a-car/#{Wizard::SpotAccount::STEPS.first}")
Route Defaults
Możesz zdefiniować domyślne parametry w trasie, przekazując hash dla opcji :defaults
.
resources :cars do
collection do
get :export, defaults: { format: 'csv' }
end
end
Używając tego, odwiedzenie http://my-ruby-garage.com/cars/export
wywoła akcję eksportu w kontrolerze samochodów, a odpowiednia akcja kontrolera odpowie domyślnie z csv.
Route Globbing
Używając segmentów wieloznacznych (fragmentów poprzedzonych gwiazdką), możemy określić, że pozostałe części trasy powinny być dopasowane do konkretnego parametru. Nazywa się to globalizacją trasy.
get '/rent/*slugs', to: 'cars#index', as: :rent_cars
Ta trasa dopasowałaby http://my-ruby-garage.com/rent/lisbon/suv-sedan
i ustawiła parametry na „lisbon/suv-sedan”. Mogłoby to być użyte w systemie wyszukiwania lub filtrowania w naszej bazie danych w celu znalezienia samochodów w Lizbonie typu suv lub sedan.
Piszę gem Slugcatcher z tą funkcjonalnością w celu oznaczenia modeli Rails, które mogą być wyszukiwane jako route slugs.
.