Большая и интересная тема маршрутизации в Rails подвигла меня на написание этой статьи. В статье я постараюсь человеческим языком описать основы маршрутизации и ответить на несколько вопросов, которые чаще всего возникают у людей, столкнувшихся со «ШПАЛОЙ» в лице маршрутизации.
Rails предоставляет очень гибкий механизм маршрутизации, основой построения маршрутов является использование ресурсов(resources) и простого задания пути(match) этим двум способам будет уделено наибольшее внимание.
- Match – простое задание пути, использовать этот способ просто и очень часто люди впервые начинающие использовать Rails не утруждают себя детальным изучением маршрутизации и используют match везде. Я считаю это не совсем правильным раз, и два это приводит к избыточному описанию всех возможных вызовов. Например:
Есть у нас контроллер User, который содержит действия {index, new, create, show, edit, update, destroy}. Для описания всех этих вызовов мне понадобиться каждый вызов описать в конструкции match.
match ’/user’=>’user#index’
match ’/user/new’=>’user#new’
match ’/user/create/:id’=>’user#create’
…
и т.д.
В принципе можно, но как-то неудобно, во-первых каждое действие, относящееся к одной сущности надо описать отдельной конструкцией match, во-вторых нам известно, что протокол HTTP поддерживает методы отправки запросов {GET, POST, PUT, DELETE}. Не плохо бы нам воспользоваться этими методами и тут мы переходим ко второй конструкции resources. - Resources – Это конструкци, которая позволяет быстро и компактно указать маршруты используя HTTP методы {GET, POST, PUT, DELETE}. Например следующая запись resources :users создаст следующие маршруты к контроллеру User:
Метод HTTP Путь Действие Использовать GET /users index Показать весь список пользователей GET /users/new new Новый пользователь POST /users create Сохранить/создать нового пользователя GET /users/:id show Показать пользователя с user.id=:id GET /users/:id/edit edit Редактировать пользователя с user.id=:id PUT /users/:id update Обновить/сохранить пользователя с user.id=:id DELETE /users/:id destroy Удалить пользователя с user.id=:id Соответственно и в контроллере User мы должны иметь действия {index, new, create, show, edit, update, destroy}.
Всё вроде нормально, но давайте рассмотрим ситуацию с личным кабинетом/профилем пользователя, нам надо реализовать доступ к профилю для пользователя, который идентифицировался в системе, если взять маршрут полученный объявлением конструкции resources :users, для доступа к действию show необходимо использовать /user/:id, но зачем нам указывать id когда пользователь идентифицировался в системе и его id уже лежит в cookies, да и вообще было бы лучше перейти не по адресу user/:id а допустим по адресу /profile, тут нам приходит на помощь match, после указания resources :users, пишем match ‘/profile’=>’users#show’, теперь по адресу /profile будет вызван метод show у контроллера users. Удобно, но если мы изначально знаем что работать нам придется с пользователем который будет идентифицирован в системе то можно сделать проще, вместо resources :users пишем resources :user маршруты будут отличаться от тех что были у resources :users.
Метод HTTP Путь Действие Использовать GET /user/new new Новый пользователь POST /user create Сохранить/создать нового пользователя GET /user show Показать пользователя GET /user/edit edit Редактировать пользователя PUT /user update Обновить/сохранить пользователя DELETE /user destroy Удалить пользователя Из всего вышесказанного можно сделать вывод что указывать resources :users – надо работая со списком сущности User и resources :user – для работы с одним экземпляром User.
Теперь сделаем шаг в сторону и посмотрим, как нам эти маршруты использовать в наших приложениях. Можно конечно писать конструкции типа <%=link_to ‘Новый пользователь’, :controller=>:user, :action=>:new %> или <%=link_to ‘Новый пользователь’, ‘/user/new’%> а можно использовать хэлперы, для каждого пути существует функция которая возвращает маршрут к действию. Например для resources :users
Действие | Хэлпер |
index | users_path |
new | new_user_path |
create | users_path |
show | user_path(:id) |
edit | edit_user_path(:id) |
update | user_path(:id) |
destroy | user_path(:id) |
Для resources :user
Действие | Хэлпер |
new | new_user_path |
create | user _path |
show | user _path |
edit | edit_user_path |
update | user _path |
destroy | user _path |
Теперь рассмотрим вложенные ресурсы. Начнем с примера, имеем склад в котором лежит товар, связь будет один ко многим.
class Storage < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
belongs_to :storage
end
в маршрут мы напишем следующий
resources :storages do
resources :products
end
Таблица маршрутов вложенных ресурсов
Метод HTTP | Путь | Действие |
GET | /storages/:storage_id/products | index |
GET | /storages/:storage_id/products/new | new |
POST | /storages/:storage_id/products | create |
GET | /storages/:storage_id/products/:id | show |
GET | /storages/:storage_id/products /:id/edit | edit |
PUT | /storages/:storage_id/products /:id | update |
DELETE | /storages/:storage_id/products /:id | destroy |
Всё это очень удобно, но возникает вопрос, а ограничены ли мы этими стандартными действиями {index, new, create, show, edit, update, destroy}, например, я решил расширить действия пользователя, добавить ему license в качестве действия показывающего лицензионное соглашение. Мне надо расширить действия resources :user, это можно сделать используя блок member внутри ресурса
resources :user
member do
get ‘license’
end
end
Таким образом появился новый путь /user/license который вызовет действие license в контроллере User. Так же можно использовать методы Post, Put, Delete. Если в блоке member, как и в примере выше, используется только одна строка расширения то можно использовать сокращенную форму записи
resources :user
get ‘license’, :on=>:member
end
Эта небольшая статья является первой и возможно не последней моей статьей о Routing в Rails, хотелось бы узнать, была ли полезна эта статья и есть ли смысл писать дальше по теме маршрутизации в rails? И конечно конструктивная критика приветствуется!
Автор: Knostan