Pull to refresh

Routes. The Beginning

Reading time 7 min
Views 18K
Роуты в рельсах очень важная вещь. Но до поры до времени можно даже не обращать внимание на них. Особенно если вы пользуетесь командой scaffold, которая автоматически все прописывает. Но в какой-то момент появляется необходимость создавать нестандартные роуты. Это значит самое время залезать в файл routes.rb в папке config вашего проекта.

Что такое роуты


Роуты — это система маршрутов (путей, url'ов) на вашем сайте. Благодаря роутам мы можем иметь красивые и ясные для пользователей ссылки. Введя ссылку вроде mysite.ru/articles/2008/november/13 мы получим все статьи за 13 ноября 2008 года, а по ссылке mysite.ru/shop/shoes получим каталог обуви из вашего магазина. При всем при этом, структура каталогов сайта никак не изменяется. В любой момент мы можем изменить роуты не трогая расположение самих файлов. Но чтобы все это работало нам необходимо настроить роуты.

К практике


Давайте создадим тестовый проект, с которым мы будем шаманить. (Если вы это делает впервые, то можно обсудить в комментариях процесс установки рельс и создания приложения).

rails routes
cd routes


Окей. Проект создан и мы вошли в рабочую папку. Сразу набросимся на роуты:

rake routes

эта команда вам выдаст две строчки стандартных роутов.
/:controller/:action/:id
/:controller/:action/:id.:format


Это значит, что любой урл сейчас будет парситься по этим двум правилам.
:controller — это Контроллер =). Это компонент MVC, который чаще всего выступает как посредник между Представлением (HTML) и Моделью (база данных, скажем). Дальше будет яснее, но скорее всего вы итак знаете, что это такое.
:action — это вызываемый метод контроллера. У контроллера обычно много методов.
:id — если я вно не указывать запрет на создание id, то по умолчанию любая модель (таблица БД) создается с полем id. Поэтому любой элемент модели имеет id. И когда вы хотите удалить/редактировать/что угодно делать с каким-то конкретным элементом модели вы обязаны передать в контроллер этот самый id.

Окей. Давайте мы создадим новостной журнал. Для этого нам нужны:
— Таблица news в нашей базе данных (Модель). В БД мы будем хранить заголовок статьи (title), автора статьи (author) и собственно саму статью (article)
— Набор методов для работы с БД (Контроллер)
— HTML формы для ввода, редактирования, чтения новостей (Представление)
Мы можем создавать все это по отдельности. Но сейчас мы упростим себе задачу и используем функцию scaffold для генерации пачки готовых файлов.

./script/generate scaffold Magazine title:string author:string article:text

Мы только что создали все выше перечисленное (а также Helpers, о которых как-нибудь в другой раз). Также команда scaffold сама создала необходимые роуты. Еще раз наберите команду rake routes и вывалится кипа новых роутов

magazines		              GET      /magazines                       {:controller=>"magazines", :action=>"index"}
formatted_magazines	      GET      /magazines.:format               {:controller=>"magazines", :action=>"index"}
			                      POST    /magazines                       {:controller=>"magazines", :action=>"create"}
			                      POST    /magazines.:format               {:controller=>"magazines", :action=>"create"}
new_magazine		      GET       /magazines/new                   {:controller=>"magazines", :action=>"new"}
formatted_new_magazine    GET       /magazines/new.:format           {:controller=>"magazines", :action=>"new"}
edit_magazine		              GET       /magazines/:id/edit              {:controller=>"magazines", :action=>"edit"}
formatted_edit_magazine     GET       /magazines/:id/edit.:format      {:controller=>"magazines", :action=>"edit"}
magazine		                      GET       /magazines/:id                   {:controller=>"magazines", :action=>"show"}
formatted_magazine	      GET       /magazines/:id.:format           {:controller=>"magazines", :action=>"show"}
			                      PUT       /magazines/:id                   {:controller=>"magazines", :action=>"update"}
			                      PUT       /magazines/:id.:format           {:controller=>"magazines", :action=>"update"}
			                      DELETE /magazines/:id                   {:controller=>"magazines", :action=>"destroy"}
			                      DELETE /magazines/:id.:format           {:controller=>"magazines", :action=>"destroy"}

Теперь мы запустим сервер и поиграемся с журналом. Но сперва мы создадим нашу базу данных и запустим миграции.

rake db:create
rake db:migrate
./script/server


Наш журнал теперь доступен по адресу localhost:3000/magazines
Создайте пару новых статей.

Вернемся к таблице роутов выше. Первый столбец — это именные роуты. Они очень удобны. Есть несколько вариантов сейчас сделать ссылку на создание новой статьи. Откройте файл app/views/magazines/index.html.erb — это представление для метода index в контроллере magazines_controller.
В самом низу давайте допишем немного кода.

<hr>
<%= link_to 'Новая статья', :action => 'new' %><br />
<%= link_to 'Новая статья', '/magazines/new' %><br />
<%= link_to 'Новая статья', new_magazine_path %><br />
<%= link_to 'Новая статья', new_magazine_url %><br />


Самым правильным будет использование последних двух методов. Разница в том, что url возвращает полную ссылку (http://localhost:3000/magazines/new), а path только путь (/magazines/new). Почему лучше пользоваться именными роутами? Именной роут это переменная, изменив которую вы меняете все ссылки, которые пользуются этим роутом. Писать пути от руки вобще не рекомендуется, если приспичило, то лучше написать :action => 'new' (зачастую именных роутов на все случаи жизне не хватает, поэтому этот вариант очень распространен).

Второй столбец таблицы — это метод запроса. Одна и таже ссылка, но с разным методом ведет на разные методы контроллера. К примеру, в том же app/views/magazines/index.html.erb:
<%= link_to 'Show', magazine %>
<%= link_to 'Destroy', magazine, :method => :delete %>

В первой ссылке исполняется дефолтный GET запрос (можно не указывать в ссылке :method=>:get), а во второй отправляется метод :delete. А ссылка magazine остается в обоих случаях одинаковая.

Третий столюбец — это собственно ссылки, которые мы получим в HTML. Последний столбец — это соответствие ссылок контроллеру и методу. Как уже выше писалось, любую ссылку можно представить в виде пары :controller, :action (ну и иногда :method).

<%= link_to 'Ссылка на другой контроллер из контроллера magazines', :controller => 'blogs', :action=>'show', :id=>'1', :method=>'GET' %>
Так мы получим ссылку на блог с индексом 1. (Тут метод :get можно было и не указывать)
<%= link_to 'Ссылка на родной метод контроллера', :action => 'show', :id => '1' %>
Ссылка на статью с индексом 1. Контроллер и HTTP метод в данном случае указывать не надо, так как GET исполняется по умолчанию, а контроллер, если не указан, выполняется тот же.

Тепрь откройте файл config/routes.rb (можете удалить весь закомментированный текст)

map.resources :magazines
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'


Первую строчку вставила команда scaffold. Эта строчка и добавила нам пачку роутов, которую мы наблюдали выше.

Если вы сейчас наберете просто localhost:3000 вы попадете на приветственную страницу. Давайте это исправим.

map.root :controller => 'magazines'

Теперь из папки public удалите index.html и зайдя на localhost:3000 вы попадете напрямую куда надо =). Кроме того если вы просмотрите все роуты занова (rake routes), то увидите новый именной роут root. И в меню сможете сделать ссылку на «Главную» вида:

<%= link_to 'Главная', root %>

И вы всегда без ущерба ссылкам сможете изменить домашнюю страницу, скажем, на ваш магазин map.root :controller => 'shop'

II уровень


Собственно создав root вы создали первый именной роут своими руками.
Давайте создадим именной роут «localhost:3000/zhurnal». Не хотим мы буржуйский 'magazines', хотим 'zhurnal'!

map.zhurnal '/klevi_zhurnal/:id', :controller => 'magazines', :id => nil

Итак, мы создали именной роут zhurnal, урл которого будет выглядеть как localhost:3000/klevi_zhurnal, а контент он будет получать от контроллера magazines. Если мы попробуем прочесть статью теперь вроде localhost:3000/klevi_zhurnal/1 — то мы обламаемся. Внесем в наш роут немного изменений:

map.zhurnal '/klevi_zhurnal/:action/:id', :controller => 'magazines', :action => 'index', :id => nil

Что все это значит:
— урл вида /klevi_zhurnal/ будет отработан :controller => 'magazines', :action => 'index', :id => 'nil' — то есть мы получим индексовую страницу (index.html.erb)
— /klevi_zhurnal/1 выплюнет ошибку, что action '1' не существует (посмотрите на последовательность передачи аргумента в роуте)
— /klevi_zhurnal/show скажет, что ID не указано
— /klevi_zhurnal/show/1 — выдаст вам статью с ID=1 (если она конечно существует)
— /klevi_zhurnal/edit/1 — выдаст форму редактирования этой статьи

Правда теперь несколько тяжелее выглядят сами ссылки:
Вместо <%= link_to 'Все статьи', magazines_path %> будет <%= link_to 'Все статьи', zhurnal_path %>
Вместо <%= link_to 'Статья номер 1', magazine_path('1') %> будет <%= link_to 'Статья номер 1', zhurnal_path(:action=>'show', :id=>'1') %>

Обратите внимание на то, что для лучшего понимания роутов введена система множественного/единственного числа:
показать все статьи magazines_path,
показать отдельную статью: magazine_path.
Чорт — не самое правильное слово вообще выбрал =). Если бы у нас все называлось Article:
index => articles_path, show => article_path(:id)

Теперь давайте создадим новый метод.
Откройте app/controllers/magazines_controller.rb
Добавьте туда метод

def random
offset = rand(Magazine.count :all)
@magazine = Magazine.find(:first, :offset => offset)
render :action => 'show'
end


Этот метод просто возвращает рандомно статью. Давайте попробуем его вызвать: localhost:3000/magazines/random
Получаем ошибку — требует от нас ID. Почему? Потому что стандартный роут продразумевает роут вида :controller/:action/:id.
Давайте попробуем вызвать роут по правилам:
localhost:3000/magazines/random/1230492
Записи с таким ID не существует — но все работает! Так как мы в нашем методе не используем ID вообще — то для нас и не принципиально какую ерунду мы там напишем.
Давайте теперь все же попробуем сделать корректный роут вида localhost:3000/magazines/random/
Для этого существует опция :collection => { :action => :HTTP_method }
Наш :action это :random, метод — :get
получаем

map.resources :magazines, :collection => { :random => :get }

теперь все работает! =)

На этом вводная часть заканчивается. Дальше нас ждут более изощренные методы извращения ). Но не сегодня.
Спасибо, за потраченное на чтение статьи время ).

API для желающих
Скринкасты Райана Бейтса
Tags:
Hubs:
+35
Comments 34
Comments Comments 34

Articles