Статьи

Панировочные сухари на рельсах с Гретель

Hänsel und Gretel entdecken das Hexenhaus im Wald

Вы, вероятно, знаете, что хлебные крошки — это навигационные элементы, которые помогают пользователям отслеживать свое текущее местоположение на веб-сайте. Панировочные сухари могут перечислять либо ранее посещенные страницы, либо родительские страницы текущей (на иерархических веб-сайтах). Вы также, наверное, знаете, откуда взялся этот термин, но я напомню вам просто для уверенности.

В Википедии говорится, что «Гензель и Гретель» — это известная сказка немецкого происхождения, записанная братьями Гримм и опубликованная в 1812 году. Эта история рассказывает о молодом брате и сестре, которых оставил в лесу их собственный отец, потому что семья нечего есть. К счастью, у Гензеля был кусок хлеба, и он оставил след от хлебных крошек, чтобы они могли найти дорогу домой. К сожалению, все крошки были съедены птицами, и детям приходилось блуждать, в конечном итоге оказавшись лицом к лицу со злой ведьмой. Если вы не знаете, чем заканчивается этот рассказ, прочитайте его некоторое время.

Во всяком случае, термин «панировочные сухари» берет свое начало в этой сказке. Точно так же, как Гензель оставил след крошек, мы представляем пользователю след гиперссылок, чтобы они могли видеть след, пройденный им через сайт. Будем надеяться, что наши хлебные крошки тоже не едят птицы …

Наличие хлебных крошек на больших иерархических веб-сайтах (например, в Mozilla Developer Network ) — хорошая практика, но как мы можем интегрировать ее в наше приложение Rails?

Эта статья создает демонстрационное приложение с сухарями, используя драгоценный камень под названием Гретель. Демонстрация показывает, как можно создавать, настраивать и масштабировать панировочные сухари для больших веб-сайтов.

Исходный код демонстрационного приложения доступен на GitHub .

Рабочую демонстрацию можно найти по адресу http://sitepoint-gretel.herokuapp.com/ .

Начиная путешествие

Rails 4.1.4 используется для этой демонстрации, но такое же решение может быть реализовано с Rails 3.

Хорошо, идем. Мы создадим Music Shop, действительно простой интернет-магазин, продающий музыкальные альбомы. Конечно, мы не будем все реализовывать, но давайте представим, что когда-нибудь это приложение вырастет во что-то большое.

Создайте новое приложение без набора тестов по умолчанию:

$ rails new music_shop -T 

Мы будем использовать Twitter Bootstrap для стиля и макета, поэтому поместите этот гем в ваш Gemfile :

Gemfile

 [...] gem 'bootstrap-sass' 

Бегать

 $ bundle install 

Затем переименуйте application.css в application.css.scss и измените его содержимое на:

таблицы стилей / application.css.scss

 @import 'bootstrap'; @import 'bootstrap/theme'; 

Чтобы воспользоваться магией Bootstrap, измените макет:

макеты / application.html.erb

 [...] <div class="navbar navbar-inverse" role="navigation"> <div class="container"> <div class="navbar-header"> <%= link_to 'Music shop', root_path, class: 'navbar-brand' %> </div> <ul class="nav navbar-nav"> <li><%= link_to 'Albums', albums_path %></li> </ul> </div> </div> <div class="container"> <%= yield %> </div> [...] 

Хорошо, теперь мы можем перейти к модели и соответствующим маршрутам. Для этой демонстрации есть одна модель — Album — со следующими полями (не говоря уже о id умолчанию, created_at , updated_at ):

  • title ( string )
  • description ( text )
  • artist ( text )
  • price ( decimal )

Создайте соответствующую миграцию:

 $ rails g model Album title:string description:text artist:string price:decimal 

Откройте файл миграции и измените эту строку:

xx_create_albums.rb

 [...] t.decimal :price, precision: 6, scale: 2 [...] 

Цена может содержать до 6 цифр с двумя цифрами после запятой. Применить миграцию:

 $ rake db:migrate 

И некоторые маршруты (мы собираемся пропустить update , destroy , create и create на данный момент и делать вид, что они будут созданы на следующей итерации):

routes.rb

 [...] resources :albums, only: [:index, :edit, :show] root to: 'albums#index' [...] 

Теперь создайте соответствующий контроллер:

albums_controller.rb

 class AlbumsController < ApplicationController def index @albums = Album.all end def edit @album = Album.find(params[:id]) end def show @album = Album.find(params[:id]) end end 

Давайте также позаботимся о представлении index :

альбомы / index.html.erb

 <div class="page-header"> <h1>Albums</h1> </div> <table class="table table-hover"> <tr> <th>Title</th> <th>Artist</th> <th>Description</th> <th>Price</th> </tr> <% @albums.each do |album| %> <tr> <td><%= link_to album.title, album_path(album) %></td> <td><%= album.artist %></td> <td><%= album.description %></td> <td><%= number_to_currency album.price, unit: '$' %></td> </tr> <% end %> </table> 

Обратите внимание, что мы используем помощник number_to_currency для правильного форматирования цены (мы предполагаем, что цена введена в долларах США).

Добавление некоторых альбомов

На данный момент у нас есть модель, контроллер и представление, но нет данных. Мы могли бы ввести их вручную, но это было бы очень утомительно. Вместо этого, давайте использовать seeds.rb чтобы загрузить некоторые демонстрационные данные в нашу базу данных.

Создайте несколько случайных названий и цен, используя жемчужину Бенджамина Кертиса. Добавьте фейкер в Gemfile :

Gemfile

 [...] gem 'faker' [...] 

и не забудь бежать

 $ bundle install 

Создайте скрипт для загрузки демонстрационных данных:

seeds.rb

 50.times do Album.create({title: Faker::Lorem.words(2).join(' ').titleize, description: Faker::Lorem.paragraph(3, true, 4), artist: Faker::Name.name, price: Faker::Commerce.price}) end 

words(2) означает, что мы генерируем массив из двух случайных слов. titleize используется для форматирования этих слов как заголовок. paragraph(3, true, 4) создает три абзаца с четырьмя случайными предложениями. name , как вы уже догадались, генерирует случайное имя, а price — цену. Легко, не правда ли?

Загрузите данные:

 $ rake db:seed 

Запустите сервер и посмотрите, как все это выглядит. Мы проделали основную работу и готовы начать интеграцию хлебных крошек.

Получение ломтика хлеба

Точно так же, как у Гензеля был свой кусок хлеба, нам нужен наш собственный инструмент, чтобы начать создание нашего птичьего следа. Познакомьтесь с Gretel , гибким плагином для Rails, созданным Lasse Bunk. Как указано в этом хорошем итоговом изображении , Гретель не связывается с вашим контроллером — все необходимые настройки находятся в другом месте.

Бросьте драгоценный камень в свой Gemfile:

Gemfile

 [...] gem 'gretel' [...] 

и беги

 $ bundle install 

Используйте генератор для создания основного файла конфигурации:

 $ rails generate gretel:install 

Внутри каталога конфигурации вы найдете файл breadcrumbs.rb , содержащий все ваши настройки. Вы, вероятно, думаете: «Это означает, что мне придется перезагружать мой сервер каждый раз, когда я изменяю этот файл?». К счастью, нет.

Начиная с версии 2.1.0, в среде разработки этот файл автоматически перезагружается при каждом изменении. В производственной среде он загружается только один раз.

Теперь мы можем создать нашу первую крошку, используя следующий код:

breadcrumbs.rb

 crumb :root do link "Home", root_path end 

Компоновка и виды также требуют изменений:

макеты / application.html.erb

 <%= breadcrumbs pretext: "You are here: " %> 

Опция pretext указывает текст, который должен быть помещен перед крошками на странице. Вы также можете указать posttext чтобы поместить что-то после крошек.

альбомы / index.html.erb

 <% breadcrumb :root %> [...] 

Эта строка требуется, чтобы указать, какую крошку визуализировать.

Запустите сервер, перейдите на главную страницу и… хлебные крошки не появятся. Это почему???

По умолчанию Гретель не показывает хлебные крошки, если у нее есть только одна ссылка. Чтобы исправить это, breadcrumbs метод breadcrumbs :

макеты / application.html.erb

 <%= breadcrumbs pretext: "You are here: ", display_single_fragment: true %> 

Теперь вы должны увидеть свой блок хлебных крошек. Еще одна проблема — стилизация — сейчас это выглядит довольно некрасиво. К счастью, Гретель уже знает, как создавать панировочные сухари для Bootstrap. Просто предоставьте опцию style следующим образом:

макеты / application.html.erb

 <%= breadcrumbs pretext: "You are here: ", display_single_fragment: true, style: :bootstrap %> 

style может принимать другие значения, такие как ul , ol , foundation5 (отображает панировочные сухари в стиле Foundation ) и inline .

Перезагрузите страницу и вуаля! Bootstrap позаботится о стилизации для нас. Однако существует проблема с разделителем между крошками. Это потому, что стили Bootstrap добавляют / в качестве разделителя, а Гретель использует по умолчанию. Итак, еще раз, breadcrumbs метод breadcrumbs :

макеты / application.html.erb

 <%= breadcrumbs pretext: "You are here: ", display_single_fragment: true, style: :bootstrap, separator: '' %> 

Здорово! Кстати, если вы хотите, чтобы текущая крошка отображалась как ссылка, установите для параметра link_current значение true . Вы также можете использовать семантические хлебные крошки , установив для semantic значение true . Гретель имеет несколько других вариантов, которые можно найти здесь .

Создание тропы

Хорошо, а как насчет немного расширить след? У нас еще есть show и edit представления, которые будут созданы. Начнем с show :

альбомы / show.html.erb

 <div class="page-header"> <h1><%= @album.title %></h1> </div> <p><%= @album.description %></p> <%= link_to 'Edit', edit_album_path(@album), class: 'btn btn-primary' %> 

В реальном приложении вам понадобится какая-то система аутентификации и авторизации, чтобы кнопка «Редактировать» была видна не всем, но это всего лишь демонстрация.

Теперь создайте новую крошку:

breadcrumbs.rb

 crumb :album do |album| link album.title, album parent :root end 

Здесь переменная album используется для получения названия альбома и отображения его на странице. Мы также указываем родителя крошки как root . Кстати, мы могли бы пропустить эту строку, потому что по умолчанию Gretel делает root родителем крошки (это контролируется опцией autoroot ).

Добавьте метод breadcrumb в представление «show»:

альбомы / show.html.erb

 <% breadcrumb :album, @album %> [...] 

Как вы видите здесь, мы не только предоставляем имя мякиша, но и передаем ресурс. На самом деле, мы можем предоставить столько аргументов, сколько захотим. Эта строка также может быть упрощена (крошка будет автоматически выведена):

 <% breadcrumb @album %> 

Наконец, давайте разберемся с видом edit :

альбомы / edit.html.erb

 <% breadcrumb :edit_album, @album %> <div class="page-header"> <h1>Edit <%= @album.title %></h1> </div> <p>Coming in the next version...</p> 

Не забудьте создать новую крошку:

breadcrumbs.rb

 [...] crumb :edit_album do |album| link "Edit #{album.title}", album parent :album, album end 

Опять же, мы используем ресурс Album и указываем крошку album в качестве прямого родителя. Перезагрузите страницу, перейдите к альбому и нажмите «Изменить» — обратите внимание, как меняются хлебные крошки. Это было легко, а?

Расширяя след

Хорошо, давайте создадим еще несколько крошек. Как мы уже обсуждали, реальный онлайн будет иметь какую-то аутентификацию. Мы не собираемся этого делать, но мы создадим страницу профиля пользователя и добавим крошку для нее.

Создайте новый маршрут:

routes.rb

 resources :users, only: [:show] 

и соответствующий контроллер:

users_controller.rb

 class UsersController < ApplicationController end 

Предоставьте ссылку на страницу show (с поддельным идентификатором пользователя):

макеты / application.html.erb

 <div class="navbar navbar-inverse" role="navigation"> <div class="container"> <div class="navbar-header"> <%= link_to 'Music shop', root_path, class: 'navbar-brand' %> </div> <ul class="nav navbar-nav"> <li><%= link_to 'Albums', albums_path %></li> </ul> <ul class="nav navbar-nav navbar-right"> <li><%= link_to 'Profile', user_path(1) %></li> </ul> [...] 

и создайте фактический вид:

пользователи / show.html.erb

 <% breadcrumb :user %> <div class="page-header"> <h1><%= current_user %>'s profile</h1> </div> <p>Coming soon...</p> 

Предположим, у нас есть вспомогательный метод current_user который возвращает имя пользователя. Можем ли мы использовать этот помощник при создании крошки? Давайте попробуем это!

application_helper.rb

 module ApplicationHelper def current_user "Someone" end end 

breadcrumbs.rb

 [...] crumb :user do link "#{current_user}'s profile" end 

Похоже, что помощники могут быть использованы без каких-либо проблем! Мы также можем реализовать условия внутри метода crumb . Предположим, мы хотим включить ссылку на область администратора в крошке, если пользователь является администратором. Добавить этого admin? метод заглушки:

application_helper.rb

 [...] def admin? true end [...] 

И переделай свою крошку

breadcrumbs.rb

 [...] crumb :admin_root do link "Admin's area" parent :root end crumb :user do link "#{current_user}'s profile" if admin? parent :admin_root else parent :root end end 

После перезагрузки страницы вы увидите админку ». Обратите внимание, что второй аргумент не был предоставлен методу link , что приводит к тому, что крошка отображается как простой текст без ссылки.

Хорошо, еще один эксперимент. Представьте, что у нас есть страница поиска, и мы хотим добавить туда также сухари. Можем ли мы показать фактический поисковый термин внутри крошки?

Добавьте еще один маршрут:

routes.rb

 [...] get '/search', to: 'pages#search', as: :search [...] 

Создать контроллер:

pages_controller.rb

 class PagesController < ApplicationController def search @term = params[:q] end end 

Посмотреть:

страницы / search.html.erb

 <% breadcrumb :search, @term %> <% parent_breadcrumb do |link| %> <%= link_to "Back to #{link.text}", link.url %> <% end %> <div class="page-header"> <h1>Searching for <%= @term %></h1> </div> <p>Guess what? Coming soon!</p> 

И форма поиска:

макеты / application.html.erb

 [...] <div class="navbar navbar-inverse" role="navigation"> <div class="container"> <div class="navbar-header"> <%= link_to 'Music shop', root_path, class: 'navbar-brand' %> </div> <ul class="nav navbar-nav"> <li><%= link_to 'Albums', albums_path %></li> </ul> <ul class="nav navbar-nav navbar-right"> <li><%= link_to 'Profile', user_path(1) %></li> </ul> <%= form_tag search_path, method: :get, class: 'navbar-form navbar-right' do %> <%= text_field_tag 'q', nil, placeholder: 'Search' %> <% end %> </div> </div> [...] 

Создайте новую крошку:

breadcrumbs.rb

 [...] crumb :search do |keyword| link "Searching for #{keyword}", search_path(q: keyword) end 

Как видите, помощники по маршрутам могут использоваться для создания ссылки с фактическим поисковым термином, предоставленным пользователем. Иди и попробуй это!

Кстати, вы также можете получить родительскую цепочку и создать ссылку «Вернуться назад», например:

 <% parent_breadcrumb do |link| %> <%= link_to "Back to #{link.text}", link.url %> <% end %> 

Который иногда может пригодиться.

Последнее, что следует упомянуть, это то, что одна крошка может иметь несколько ссылок. Например, мы могли бы сделать это:

breadcrumbs.rb

 crumb :root do link "Music shop" link "Home", root_path end [...] 

Теперь перед ссылкой «Домой» будет отображаться ссылка «Музыкальный магазин».

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

breadcrumbs.rb

 crumb :root do link "Music shop" + (Time.now.monday? ? " (SALES!)" : '') link "Home", root_path end [...] 

Масштабирование тропы

Для больших веб-сайтов файл breadcrumbs.rb может стать очень сложным. К счастью, Gretel позволяет легко разделить конфигурацию на несколько файлов. Для этого создайте новый каталог с именем breacrumbs внутри config и поместите туда файлы .rb вместе с вашим config.

В этой демонстрации я создал album.rb с крошками, связанными с альбомами, и other_stuff.rb с другими крошками. Вы можете удалить файл breadcrumbs.rb или оставить его с другой конфигурацией — он все равно будет загружен.

Еще одна вещь, о которой стоит упомянуть, это то, что хлебные крошки также будут загружаться с любого движка вашего приложения, однако хлебные крошки в основном приложении имеют более высокий приоритет.

Вывод

Это все на сегодня, ребята. Мы взглянули на драгоценный камень Gretel, чтобы легко добавить крошки в ваше приложение. Драгоценный камень является гибким и действительно простым в использовании, поэтому я призываю вас попробовать!

Что вы думаете о сухарях? Они полезны? Вы когда-нибудь реализовывали их в своем приложении? Вы использовали драгоценный камень или вы написали все с нуля? Поделитесь своим опытом!

Спасибо за прочтение!