Jednym z ważniejszych plików konfiguracyjnych aplikacji napisanej w Ruby on Rails jest plik routes.rb
. Plik zawiera mapowania do wszystkich zasobów aplikacji, aby były (lub nie były) dostępne przez URL. W rails 3 wprowadzono sporo zmian w tej kwestii, chciałbym po krótce opisać chociaż najważniejsze z nich.
Po pierwsze zniknęło słowo map
, dobrze znane z poprzedniej wersji frameworka. Uprościło to trochę zapis. Np. w poprzedniej wersji taki zapis:
1 2 3 4 |
map.resources :posts do map.resource :comments end |
można teraz zapisać jako:
1 2 3 4 |
resources :posts do resources :comments end |
Routing podstawowy i nazwany
map.connect
został zastąpiony w rails 3 metodą match
. Dodatkowo w routingach nie podaje się już oddzielnie nazwy kontrolera i akcji. Zamiast tego oddziela się je znakiemm #
. Dla przykładu routing z poprzedniej wersji frameworka wyglądający w ten sposób:
1 2 |
map.connect 'posts/:id', :controller => "posts", :action => "view" |
przyjmie taką postać:
1 2 |
match 'posts/:id', :to => 'posts#view' |
Jak widać zapis jest o wiele czytelniejszy. Pojawił się także nowy paramert :to
, który został wprowadzony po połączeniu :action i :controller
.
Metoda match
została także użyta przy nazwanych routingach.
Routingi nazwane są to takie, które tworzą automagicznie helpery do wyświetlania odpowiednich adresów URL. Dla przykładu zapis:
1 2 |
map.logout '/logout', :controller => 'sessions', :action => 'destroy' |
będzie wyglądał teraz tak:
1 2 |
match 'logout', :to => 'sessions#destroy', :as => 'logout' |
Parametr :as
oznacza nazwę, która zostanie użyta podczas tworzenia helperów. W powyższym przypadku: logout_path i logout_url
.
Inny przykład użycia :as
:
1 2 3 4 |
match 'users/show/:id', :as => 'profile' # zapis w kontrolerze: profile_path(current_user.id) |
Drugim parametrem, którego można użyć wraz z match
jest :via
. Parametrem tym, określamy, które metody żądania HTTP są dostępne dla danego routingu.
1 2 |
match 'account/setup', :via => [:post, :get] |
W przypadku, gdy ograniczamy wejście tylko do jednej metody, możemy użyć jej nazwy zamiast match
1 2 3 4 |
match 'account/setup', :via => :get #jest jednoznaczne z: get 'account/setup' |
Rails 3 wprowadził także kilka skrótów, które możemy wykorzystać w jednoznacznych sytuacjach. Dla przykładu, możemy pominąć wspomniany wyżej parametr :to
zamieniając oba parametry w mapę.
1 2 3 |
match 'logout', :to => 'sessions#destroy' match 'logout' => 'sessions#destroy' |
Oba powyższe przypadki są jednoznaczne.
Jeszcze większy skrót jest gdy mapowany url jest analogiczny do nazwy kontrolera i akcji, wtedy wogóle nie musimy określać punktu docelowego.
1 2 3 4 |
match 'sessions/destroy', :to => 'sessions#destroy' match 'sessions/destroy' => 'sessions#destroy' match 'sessions/destroy' |
Wszystkie powyższe zapisy są analogiczne. Kierują wejście na sessions/destroy
do kontrolera SessionsController
i akcji destroy
.
Route domyślny i segmenty opcjonalne
Ciekawostką wprowadzoną w Rails 3 są opcjonalne segmenty w routingu. Są to takie fragmenty adresu URL, które mogą wystąpić lecz nie muszą. Opcjonalne fragmenty są w zapisie umieszczone w nawiasach.
Najlepszym przykładem ich użycia jest route domyślny, który zmienił postać z:
1 2 3 |
map.connect ':controller/:action/:id' map.connect ':controller/:action/:id.:format' |
na:
1 2 |
match ':controller(/:action(/:id(.:format)))' |
Jak widać powyżej parametry :action, :id i :format są opcjonalne.
Dodatkową przydatną informacją na temat domyślnego routingu jest fakt, że został on domyślnie wyłączony w Rails 3. Jeżeli chcemy użyć tego domyślnego mapowania musimy je odkomentować jednak nie jest to zalecane.
Routing główny
Routing główny jest często określany także pustym. Jest to mapowanie, które jest wyświetlane po wpisaniu w przeglądarce samej nazwy domeny, bez żadnych parametrów.
W rails 2 takie mapowanie jest określane za pomocą metody map.root
. W obecnej wersji, pominięto map
, więc wystarczy użycie metody root
.
Stary zapis:
1 2 |
map.root :controller => "posts", :action => "index" |
można w nowej wersji przedstawić w ten sposób:
1 2 |
root :to => 'posts#index' |
Ponownie widać uproszczenie zapisu.
Zagnieżdżone zasoby
Niektóre modele w tworzonej aplikacji posiadają referencje do innych, referencje te są zdefiniowane poprzez użycie has_many
lub has_one
w definicji modelu. Na przykład:
1 2 3 4 5 6 7 8 |
class Post < ActiveRecord::Base has_many :comments end class Comment < ActiveRecord::Base belongs_to :post end |
Zagnieżdżony routing pozwala na dostęp do „niższych” zasbów bezpośrednio z URL’a, na przykład: /posts/2/comments/3
dostarczy komentarz o ID=3, który należy do postu o id = 2.
Aby taki routing był możliwy musimy go zdefiniować. Robimy t w ten sposób:
1 2 3 4 |
resources :posts do resources :comments end |
Po takiej deklaracji zostaną także utworzone odpowiednie helpery, np.:post_comments_path, edit_post_comment_path
.
W przypadku jednak gdy helpery te są za długie, lub nie chcemy korzystać z modelu nadrzędnego możemy użyć opcji shallow
, która pozwoli nam na bezpośredni dostęp do zagnieżdżonych zasobów.
1 2 3 4 5 6 |
resources :posts do shallow do resources :comments end end |
Od tej pory możemy używać /comments/2
zamiast /posts/2/comments/2
.
Dla przypomnienia w rails 2 z tej opcji korzystało się w ten sposób:
1 2 3 4 |
map.resources :posts, :shallow => true do |post| post.resources :comments end |
Zasoby
Od czasów wprowadzenia definicji resources
do routes, zaleca się z korzystania z tej metody. Tworzy ona helpery oraz zarządza odpowiednimi RESTowymi połączeniami z zasobami.
Aplikacja mapuje odpowiednio wszystkie CRUD’owe metody dla takiego zasobu(index, new, create, delete, update, show, edit).
W rails 3 możemy zrezygnować z niektórych (except), lub wskazać tylko te z których chcemy skorzystać (only):
1 2 3 |
resources :comments, :except => [:index] resources :sessions, :only => [:new, :create, :destroy] |
Więcej niż REST
Czasem wprowadzamy do naszych kontrolerów inne akcje, do których chcielibyśmy mieć dostęp z zewnątrz. Dodatkowe akcje definiujemy za pomocą metod collections i member
zależnie czy chcemy manipulować zbiorem obiektów czy jedną sztuką.
W rails 2 pisaliśmy coś takiego:
1 2 3 4 5 6 7 8 9 |
map.resources :orders, :collection => { :only_new => :get, :accepted => :get }, :member => { :change_status => :put } |
teraz możemy pisać tak:
1 2 3 4 5 6 7 8 9 |
resources :posts do collection do get :only_new, :accepted end member do put :change_status end end |
lub tak:
1 2 3 4 5 6 |
resources :posts do get :only_new, :on => :collection get :accepted, :on => :collection put :change_status, :on => :member end |
Zmiana nazwy parametru żądania
W niektórych przypadkach, zwłaszcza w aplikacjach nieanglojęzycznych przydatna jest opcja zmiany nazwy akcji. Np. Chcielibyśmy ze względu na SEO aby w URL było używane „aktualizuj” zamiast „edit”. Możemy tego dokonać za pośrednictwem opcji path_names
1 2 |
resources :users, :path_names => {:edit => 'aktualizuj'} |
Przekierowanie żądania
W rails 3 jest możliwe przekierowanie wejścia z jednego żądania na inne
1 2 3 4 |
match '/profile', :to => redirect('/users/show') #albo match '/profile/:username' => redirect('/users/show/%{username}) |
Ograniczenia
W routes.rb jest możliwość wprowadzenia ograniczeń co do żądania, np. ID może składać się tylko z pięciu cyfr lub podobne.
1 2 3 4 5 6 7 |
resources :users, :constraints => { :id => /\d{5}/ } #lub w przypadku gdy takie ograniczenia są wspólne dla kilku zasobów: constraints(:id => /\d{5}/) do resources :users resources :posts end |
To nie wszystkie nowości, które zostały wprowadzone w Rails 3, pozostały do opisania np. uruchamianie aplikacji bezpośrednio na racku lub inne perełki. Mam nadzieję jednak, że da to ogólny wgląd w nadchodzące zmiany.
.