В процессе краткого обзора Ruby в Главе 4 мы узнали о подключении таблиц стилей к примеру приложения, но, как было отмечено в Разделе 4.3.4, эти таблицы стилей в настоящий момент пусты. В этой главе мы это изменим, включив фреймворк Bootstrap в наше приложение и добавив затем немного своих стилей.
Rails Учебник это книга по веб-разработке, а не по веб-дизайну, но работа над приложением, которое выглядит как полное дерьмо - удручает, поэтому в этом разделе мы добавим некоторые структуры в шаблон и придадим ему минимальный дизайн с CSS. В дополнение к нашим собственным CSS правилам, мы будем использовать Bootstrap - CSS фреймворк с открытым исходным кодом от Twitter. Мы также придадим нашему коду немного стиля, так сказать, используя частичные шаблоны почистим шаблон, поскольку он немного загроможден.
При создании веб-приложений, часто бывает полезным получить общий вид пользовательского интерфейса как можно раньше. Таким образом, на протяжении остальной части книги, я буду часто использовать макеты (в контексте Веб часто называемыми каркасами), которые являются грубыми набросками того, как приложение, возможно, будет выглядеть.
(рис 5.1) Каркас Home страницы примера приложения.
Как обычно, если вы используете Git для управления версиями, то сейчас самое время создать новую ветку:
$ git checkout -b filling-in-layout
В качестве первого шага к добавлению ссылок и стилей к примеру приложения, мы добавим в файл шаблона сайта . (Примечание: это (пока) не очень приятно.)
<!DOCTYPE html>
<html>
<head>
<title><%= full_title(yield(:title)) %></title>
<%= stylesheet_link_tag "application", media: "all",
"data-turbolinks-track" => true %>
<%= javascript_include_tag "application", "data-turbolinks-track" => true %>
<%= csrf_meta_tags %>
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<header class="navbar navbar-fixed-top navbar-inverse">
<div class="navbar-inner">
<div class="container">
<%= link_to "sample app", '#', id: "logo" %>
<nav>
<ul class="nav pull-right">
<li><%= link_to "Home", '#' %></li>
<li><%= link_to "Help", '#' %></li>
<li><%= link_to "Sign in", '#' %></li>
</ul>
</nav>
</div>
</div>
</header>
<div class="container">
<%= yield %>
</div>
</body>
</html>
Давайте просмотрим на новые элементы в Листинге 5.1 сверху вниз. Как вкратце отмечалось в Разделе 3.3.2, Rails 4 по умолчанию использует HTML5 (о чем говорит doctype <!DOCTYPE html>); т.к. HTML5 — новый стандарт и некоторые браузеры (особенно старые версии Internet Explorer) пока не полностью его поддерживают, мы включим JavaScript код (известный как "HTML5 shim") чтобы обойти это затруднение:
<!--[if lt IE 9]> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <![endif]-->
Немного странный синтаксис
<!--[if lt IE 9]>
включает закомментированную строку, только если версия Microsoft Internet Explorer (IE) ниже чем 9 (if lt IE 9). Чудной [if lt IE 9] синтаксис не является частью Rails; это условный комментарий поддерживаемый браузерами Internet Explorer специально для подобных ситуаций. Это хорошая вещь, хотя бы потому, что это означает, что мы можем включить дополнительную таблицу стилей только для IE браузеров версии менее 9, не затрагивая такие браузеры как Firefox, Chrome и Safari.
Следующий раздел включает header для (текстового) логотипа сайта, несколько разделителей (использующих div тег) и список элементов с навигационными ссылками:
<header class="navbar navbar-fixed-top navbar-inverse">
<div class="navbar-inner">
<div class="container">
<%= link_to "sample app", '#', id: "logo" %>
<nav>
<ul class="nav pull-right">
<li><%= link_to "Home", '#' %></li>
<li><%= link_to "Help", '#' %></li>
<li><%= link_to "Sign in", '#' %></li>
</ul>
</nav>
</div>
</div>
</header>
Здесь тег navbar, navbar-inverse и navbar-fixed-top, разделенных пробелом:
<header class="navbar navbar-fixed-top navbar-inverse">
Всем HTML элементам могут быть назначены как классы, так и id (идентификаторы); это всего лишь ярлыки и они используются для стилевания сайта с помощью CSS (Раздел 5.1.2). Основное различие между классами и идентификаторами, в том, что классы могут быть использованы несколько раз на странице, а идентификаторы могут быть использованы лишь один раз. В данном случае, и navbar и navbar-fixed-top классы имеют особое значение для фреймворка Bootstrap, который мы установим и начнем использовать в Разделе 5.1.2.
Внутри тега header мы видим несколько div тегов:
<div class="navbar-inner"> <div class="container">
Тег div это разделитель; он не делает ничего, кроме разделения документа на отдельные части. В старом стиле HTML, div теги использовали почти для всех разделов сайта, но HTML5 добавил header, nav и section элементы для разделов, общих для многих приложений. В данном случае каждый div снабжен CSS классом. Как и в случае с тегом header, эти классы имеют особое значение для Bootstrap.
После дивов мы неожиданно наталкиваемся на встроенный Ruby:
<%= link_to "sample app", '#', id: "logo" %>
<nav>
<ul class="nav pull-right">
<li><%= link_to "Home", '#' %></li>
<li><%= link_to "Help", '#' %></li>
<li><%= link_to "Sign in", '#' %></li>
</ul>
</nav>
Здесь используется Rails хелпер link_to для создания ссылок (которые мы создавали непосредственно с якорным (анкорным) тегом a в Разделе 3.3.2); первый аргумент в link_to это текст ссылки, а второй это URL. Мы заменим URL на именованные маршруты в Разделе 5.3.3, но сейчас мы используем заглушки URL ’#’ обычно применяемые в веб-дизайне. Третий аргумент это хэш опций, в данном случае добавление CSS id logo для ссылки на пример приложения. (У остальных трех ссылок хэш опций отсутствует, и это нормально поскольку это необязательный аргумент.) Rails хелперы часто принимают хэш опций подобным образом, что дает нам возможность добавлять произвольные HTML опции оставаясь в рамках Rails.
Второй элемент внутри дивов это список навигационных ссылок, созданный с помощью тега ненумерованного списка ul, совместно с тегом элемента списка li:
<nav>
<ul class="nav pull-right">
<li><%= link_to "Home", '#' %></li>
<li><%= link_to "Help", '#' %></li>
<li><%= link_to "Sign in", '#' %></li>
</ul>
</nav>
Тег nav, хотя формально здесь и не нужен, служит для обозначения того что это именно навигационные ссылки. Классы nav и pull-right тега ul имеют особое значение для Bootstrap. После того как Rails обработает этот шаблон и оценит встроенный Ruby, список будет выглядеть следующим образом:
<nav>
<ul class="nav pull-right">
<li><a href="#">Home</a></li>
<li><a href="#">Help</a></li>
<li><a href="#">Sign in</a></li>
</ul>
</nav>
Завершающая часть шаблона это div для основного контента:
<div class="container"> <%= yield %> </div>
Как и прежде, класс container имеет специальное значение для Bootstrap. Как мы узнали в Разделе 3.3.4, метод yield вставляет содержимое каждой страницы в шаблон сайта.
Если не брать в расчет подвал сайта, который мы добавим в Разделе 5.1.3, наш шаблон теперь завершен, и мы можем посмотреть на результаты, посетив, например, Home страницу. Для того чтобы извлечь пользу из ожидающих своего часа элементов стиля, мы добавим несколько дополнительных элементов к home.html.erb представлению (Листинг 5.2).
<div class="center hero-unit">
<h1>Welcome to the Sample App</h1>
<h2>
This is the home page for the
<a href="http://railstutorial.org/">Ruby on Rails Tutorial</a>
sample application.
</h2>
<%= link_to "Sign up now!", '#', class: "btn btn-large btn-primary" %>
</div>
<%= link_to image_tag("rails.png", alt: "Rails"), 'http://rubyonrails.org/' %>
В ожидании добавления пользователей к нашему сайту в Главе 7, первый link_to создает ссылку-заглушку вида
<a href="#" class="btn btn-large btn-primary">Sign up now!</a>
В теге div CSS класс hero-unit предназначен для Bootstrap, также как и btn, btn-large и btn-primary классы в кнопке signup
Второй
<img alt="Rails" src="/assets/rails.png" />
Alt атрибут, это то, что будет отображаться, если нет изображения, также его используют читатели экрана для слабовидящих. Хотя люди иногда пренебрежительно относятся к включению атрибута alt для изображений, на самом деле его включение требуется HTML стандартом. К счастью, Rails включает дефолтный атрибут alt; если вы не укажете его явно при вызове image_tag, Rails пропишет в него название файла изображения (за вычетом расширения). В данном случае мы все же явно установили alt текст для того чтобы капитализировать "Rails"
В предыдущих версиях Rails логотип ) и поместить его в директорию app/assets/images/. (Вам, возможно, также потребуется создать эту директорию, или командой mkdir или с помощью графического менеджера файлов.) Поскольку мы использовали хелпер image_tag в Листинге 5.2, Rails автоматически будет находить любые изображения в этой директории с помощью файлопровода (Раздел 5.2).
Теперь мы, наконец, готовы увидеть плоды наших трудов (рис 5.2). Говорите "Не впечатляет"? Может быть. К счастью, однако, мы проделали хорошую работу, прописав для наших HTML элементов внятные классы и идентификаторы, которые позволят нам отстилить наш сайт с помощью CSS.
(рис 5.2) Страница Home (/static_pages/home) без кастомного CSS.
В Разделе 5.1.1 мы связали многие HTML элементы с CSS классами, которые дают нам большую гибкость при построении шаблона с помощью CSS. Как отмечалось в Разделе 5.1.1, многие из этих классов специфичны для Bootstrap - фреймворка от Twitter который делает легким создание приятного веб дизайна и элементов пользовательского интерфейса для HTML5 приложений. В этом разделе мы будем комбинировать Bootstrap с небольшим количеством кастомных CSS правил для того чтобы придать некоторый стиль примеру приложения.
Нашим первым шагом будет добавление Bootstrap, что может быть сделано в Rails приложениях с помощью bootstrap-sass гема, как это показано в Листинге 5.3. Фреймворк Bootstrap изначально использует язык LESS CSS для создания динамических таблиц стилей, но файлопровод в Rails по умолчанию поддерживает (очень похожий) язык Sass (Раздел 5.2), bootstrap-sass конвертирует LESS в Sass и делает все необходимые файлы Bootstrap доступными для текущего приложения.
source 'https://rubygems.org' ruby '2.0.0' #ruby-gemset=railstutorial_rails_4_0 gem 'rails', '4.0.2' gem 'bootstrap-sass', '2.3.2.0' . . .
Для установки Bootstrap мы, как обычно запускаем bundle install:
$ bundle install
Затем рестартуем веб-сервер для того чтобы изменения вступили в силу. (На большинстве систем рестарт сервера подразумевает нажатие Ctrl-C и последующий запуск rails server.) Наконец, (для Rails 4.0) нам нужно добавить одну строку в config/application.rb для того чтобы сделать bootstrap-sass совместимым с файлопроводом, как это показано в Листинге 5.4.
require File.expand_path('../boot', __FILE__)
.
.
.
module SampleApp
class Application < Rails::Application
.
.
.
config.assets.precompile += %w(*.png *.jpg *.jpeg *.gif)
end
end
Первый шаг в добавлении кастомных CSS к нашему приложению это создание файла для их хранения:
app/assets/stylesheets/custom.css.scss
Здесь важны как название директории так и название файла. Директория
app/assets/stylesheets
является частью файлопровода (Раздел 5.2) и любые таблицы стилей в этой директории будут автоматически включены как часть файла application.css включенного в шаблон сайта. Кроме того, название файла custom.css.scss включает расширение .css, которое обозначает файл CSS, и расширение .scss которое указывает на то что это "Sassy CSS" файл и говорит файлопроводу что этот файл необходимо обрабатывать с помошью Sass. (Мы не будем использовать Sass до Раздела 5.2.2, но сейчас он необходим bootstrap-sass гему.)
Внутри файла для кастомных CSS мы можем использовать функцию @import для включения Bootstrap, как это показано в Листинге 5.5.
@import "bootstrap";
Эта единственная строка включает весь Bootstrap CSS фреймворк, результат показан на рис 5.3 (Вам, возможно, придется рестартовать веб сервер. Стоит также отметить что скриншоты используют Bootstrap 2.0, в то время как учебник использует Bootstrap 2.3, так что могут быть небольшие отличия во внешнем виде, но это не повод для беспокойства.) Текст расположен плохо и логотип оказался неотстиленым, но цвета и кнопка регистрации выглядят многообещающе.
(рис 5.3) Пример приложения с Bootstrap CSS.
Затем мы добавим немного CSS который будет использован для стилизации шаблона и каждой отдельной страницы, как это показано в Листинге 5.6. В Листинге 5.6 довольно много правил; для того чтобы понять что делает CSS правило, часто бывает полезным закомментировать его с помощью CSS комментариев, т.e., поместив его внутрь .
@import "bootstrap";
/* universal */
html {
overflow-y: scroll;
}
body {
padding-top: 60px;
}
section {
overflow: auto;
}
textarea {
resize: vertical;
}
.center {
text-align: center;
}
.center h1 {
margin-bottom: 10px;
}
(рис 5.4) Добавление нескольких отступов и прочих универсальных стилей.
Обратите внимание что CSS в Листинге 5.6 имеют последовательный вид. В целом, CSS правила относятся либо к классу, id, HTML тегу или к какой либо их комбинации за которыми идет список стилевых команд. Например,
body {
padding-top: 60px;
}
задает отступ в 60 пикселей от верхнего края страницы. Благодаря классу navbar-fixed-top в теге header, Bootstrap фиксирует панель навигации вверху страницы, таким образом отступ служит для разделения основного текста и навигации. (Поскольку дефолтный цвет навигационной панели в Boostrap 2.1 отличается от того что ранее использовался в версии 2.0, нам пришлось добавить класс navbar-inverse для того чтобы сделать его черным вместо (теперь) дефолтного белого.) Между тем, CSS в правиле
.center {
text-align: center;
}
связывает класс center со свойством text-align: center. Другими словами, точка . в .center указывает на то, что правило стилизует класс. (Как мы увидим в Листинге 5.8, знак фунта # указывает на правило для стилизации CSS id.) Это означает что этот элемент внутри любого тега (такого как div) с классом center будет отцентрован на странице. (Мы увидим пример этого класса в Листинге 5.2.)
Хотя Bootstrap поставляется с CSS правилами для приятной типографики, мы также добавим немного кастомных правил для представления текста на нашем сайте, как это показано в Листинге 5.7. (Не все эти правила применяются к странице Home page, но каждое правило будет использовано в какой-то части примера приложения.) Результат Листинга 5.7 показан на рис 5.7" type="example">Листинге 5.7. (Не все эти правила применяются к странице Home page, но каждое правило будет использовано в какой-то части примера приложения.) Результат Листинга 5.7 показан на @import "bootstrap";
.
.
.
/* typography */
h1, h2, h3, h4, h5, h6 {
line-height: 1;
}
h1 {
font-size: 3em;
letter-spacing: -2px;
margin-bottom: 30px;
text-align: center;
}
h2 {
font-size: 1.2em;
letter-spacing: -1px;
margin-bottom: 30px;
text-align: center;
font-weight: normal;
color: #999;
}
p {
font-size: 1.1em;
line-height: 1.7em;
}
Наконец, мы добавим несколько правил для придания стиля логотипу сайта, который представляет собой текст "sample app". CSS в Листинге 5.8 конвертирует текст в верхний регистр и изменяет его размер, цвет и положение. (Мы использовали CSS id потому что предполагаем что логотип сайта будет на странице в единственном экземпляре, но вы можете использовать класс вместо него.) Здесь .
(рис 5.5) Добавление нескольких стилей для типографики
@import "bootstrap";
.
.
.
/* header */
#logo {
float: left;
margin-right: 10px;
font-size: 1.7em;
color: #fff;
text-transform: uppercase;
letter-spacing: -1px;
padding-top: 9px;
font-weight: bold;
line-height: 1;
}
#logo:hover {
color: #fff;
text-decoration: none;
}
(рис 5.6) Пример приложения с приятно отстиленным логотипом.
Хотя шаблон в Листинге 5.1 и выполняет свою работу, но он становится немного суматошным. HTML shim занимает три строки и использует странный IE-специфичный синтаксис, и было бы хорошо вынести его в какое нибудь отдельное место. К тому же заголовок формирует отдельную логическую единицу и он весь должен быть упакован в одном месте. Мы можем достигнуть этого, используя удобное Rails-приспособление называемое partials (# партиалы, частичные шаблоны). Давайте сначала посмотрим, как шаблон выглядит после определения партиалов (Листинг 5.9).
<!DOCTYPE html>
<html>
<head>
<title><%= full_title(yield(:title)) %></title>
<%= stylesheet_link_tag "application", media: "all",
"data-turbolinks-track" => true %>
<%= javascript_include_tag "application", "data-turbolinks-track" => true %>
<%= csrf_meta_tags %>
<%= render 'layouts/shim' %>
</head>
<body>
<%= render 'layouts/header' %>
<div class="container">
<%= yield %>
</div>
</body>
</html>
В Листинге 5.9 мы заменили HTML shim строки одним вызовом Rails помощника называемого render:
<%= render 'layouts/shim' %>
Эта строка приводит к поиску файла, называемого
Конечно, чтобы заполучить частичные шаблоны на работу, мы должны заполнить их чем-нибудь; в случае shim-партиала это просто три строки shim-кода из Листинга 5.1; результат представлен в Листинге 5.10.
Аналогичным образом, мы можем переместить материал header-а в частичный шаблон показанный в Листинге 5.11 и вставить его в шаблон другим вызовом render.
<header class="navbar navbar-fixed-top navbar-inverse">
<div class="navbar-inner">
<div class="container">
<%= link_to "sample app", '#', id: "logo" %>
<nav>
<ul class="nav pull-right">
<li><%= link_to "Home", '#' %></li>
<li><%= link_to "Help", '#' %></li>
<li><%= link_to "Sign in", '#' %></li>
</ul>
</nav>
</div>
</div>
</header>
Теперь, когда мы знаем как сделать частичные шаблоны, давайте добавим подвал (footer) для каждой страницы сайта, чтобы он шел вместе с шапкой (header). К этому моменту вы, вероятно, догадались, что мы будем называть его
<footer class="footer">
<small>
<a href="http://railstutorial.org/">Rails Tutorial</a>
by Michael Hartl
</small>
<nav>
<ul>
<li><%= link_to "About", '#' %></li>
<li><%= link_to "Contact", '#' %></li>
<li><a href="http://news.railstutorial.org/">News</a></li>
</ul>
</nav>
</footer>
Как и в шапке (header), в подвале (footer) мы использовали link_to для внутренних ссылок на About и Contact страницы и заглушили URL-адреса символом ’#’ до лучших времен. (Как и header, footer - новый, HTML5, тег.)
Мы можем вставить партиал подвала в шаблон тем же образом, что и shim и header партиалы (Листинг 5.13).
<!DOCTYPE html>
<html>
<head>
<title><%= full_title(yield(:title)) %></title>
<%= stylesheet_link_tag "application", media: "all",
"data-turbolinks-track" => true %>
<%= javascript_include_tag "application", "data-turbolinks-track" => true %>
<%= csrf_meta_tags %>
<%= render 'layouts/shim' %>
</head>
<body>
<%= render 'layouts/header' %>
<div class="container">
<%= yield %>
<%= render 'layouts/footer' %>
</div>
</body>
</html>
Конечно, подвал будет уродливым, если его немного не отстилить (Листинг 5.14). Результаты представлены на рис 5.14" type="example">Листинг 5.14). Результаты представлены на .
.
.
/* footer */
footer {
margin-top: 45px;
padding-top: 5px;
border-top: 1px solid #eaeaea;
color: #999;
}
footer a {
color: #555;
}
footer a:hover {
color: #222;
}
footer small {
float: left;
}
footer ul {
float: right;
list-style: none;
}
footer ul li {
float: left;
margin-left: 10px;
}
Одним из наиболее значитильных нововведений в последних версиях Rails является файлопровод (asset pipeline), который значительно улучшает создание и управление статическими файлами ( Файлопровод включает в себя множество подкапотных изменений в Rails, но с точки зрения типичного Rails разработчика есть три принципиальных момента в которых нужно разобраться: директории ассетов (активов, файлов), файлы-манифесты и препроцессоры. Директории ассетов В версиях Rails до 3.0 включительно, статические ассеты жили в директории public/ следующим образом: Файлы в этих директориях (даже в версиях "после 3.0") автоматически выдаются через запрос к http://example.com/stylesheets, и т.д.. Начиная с Rails 3.1 и продолжая в Rails 4, существуют три каноничные директории для статических ассетов, каждая со своим собственным назначением: Как вы можете догадаться, каждая из этих директорий имеет субдиректории для каждого класса ассетов, например, Теперь мы в состоянии понять причины, по которым мы поместили файл custom.css.scss в директорию app/assets в Разделе 5.1.2: custom.css.scss специфичен для примера приложения, поэтому он отправляется в app/assets/stylesheets. Файлы-манифесты После того как вы поместили ваши ассеты в предназначенные им директории, вы можете использовать файлы-манифесты для того, чтобы сказать Rails (с помошью гема Sprockets) как скомбинировать их для того чтобы сформировать один единственный файл. (Это относится к CSS и JavaScript но не к изображениям.) В качестве примера, давайте взглянем на дефолтный манифест файл для таблиц стилей приложения (Листинг 5.15). Ключевыми строками здесь являются CSS комментарии, но именно они используются Sprockets для включения правильных файлов: Здесь обеспечивает включение всех CSS файлов из директории app/assets/stylesheets (в том числе вложенные субдиректории) в CSS приложения. Строка указывает где в загрузочной последовательности будет включен CSS код самого application.css файла. Rails поставляется с вполне вменяемыми дефолтными манифест-файлами и в Rails Tutorial мы не будем изменять их, но в случае если вам понадобится их изменить, см. rus)Rails Guides введение в asset pipeline чтобы узнать об этом подробнее. Препроцессоры После того как вы собрали ваши активы, Rails подготавливает их для шаблона сайта, прогоняя их через несколько препроцессоров и комбинирует их с помощью манифест-файлов для доставки в браузер. Мы говорим Rails какой препроцессор использовать с помощью расширения файла; наиболее употребительными являются .scss для Sass, .coffee для CoffeeScript и .erb для встроенного Ruby (ERb). Мы впервые рассказывали о ERb в Разделе 3.3.3 и о Sass в Разделе 5.2.2. В этом учебнике нам не понадобится CoffeeScript. Если вам интересно узнать об этом элегантном маленьком языке который компилируется в JavaScript, советую начать с RailsCast on CoffeeScript basics. Препроцессоры могут быть сцеплены, например прогоняется через препроцессор CoffeeScript, а прогоняется и через CoffeeScript и через ERb (справа налево, т.е. CoffeeScript первым). Производительность в продакшен Одно из самых замечательных свойств файлопровода заключается в том, что он автоматически приводит к повышению производительности в продакшен приложении. Традиционный подход к организации CSS и JavaScript подразумевает разбиение функциональности между отдельными файлами и использование приятного глазу форматирования (с большим количеством отступов). Что является удобным для программистов, но неэффективно в продакшен; включение множества крупноразмерных файлов может серьезно увеличить время загрузки страниц (а это один из наиболее значимых факторов влияющих на впечатление получаемое пользователями от сайта). С файлопроводом, в продакшен все таблицы стилей приложения заворачиваются в один CSS файл (application.css), весь JavaScript код приложения заворачивается в один JavaScript файл (javascripts.js) и все подобные файлы (включая и те что хранятся в lib/assets и vendor/assets) минимизируются для удаления ненужных пробелов увеличивающих размер файла. В результате мы
получаем лучшее для обоих миров: множество хорошо отформатированных файлов для удобства программистов и объединенные в один, оптимизированные файлы для продакшен. Как было вкратце отмечено в Разделе 5.1.2, Sass поддерживает формат называемый SCSS (обозначается расширением файла Вложение Общепринятым паттерном в написании таблиц стилей являются правила применяемые ко вложенным элементам. Например, в Листинге 5.6 у нас есть правило применяемое и для В Sass мы можем заменить это на Здесь вложенное правило Здесь есть второй кандидат на вложение, который требует несколько иного синтаксиса. В Листинге 5.8, мы имеем код Здесь Sass заменяет Обе эти техники вложения элементов применяются в CSS подвала в Листинге 5.14, который может быть трансформирован в следующее: Конвертирование Листинга 5.14 вручную это хорошее упражнение и вы должны убедиться что CSS по прежнему работает как надо после преобразования. Переменные Sass позволяет нам определять переменные для устранения дублирования и написания более выразительного кода. Для примера, взгляните на Листинг 5.7 и Листинг 5.14, мы видим, что здесь есть повторяющийся вызов одного и того же цвета: В данном случае, Это позволяет нам переписать наш SCSS следующим образом: Поскольку названия переменных, такие как Это означает, что, благодаря гему Применение Sass вложения и определения переменных ко всему SCSS файлу дает нам файл представленный в Листинге 5.16. Это использует Sass переменные (как определенные в Bootstrap) так и встроенные названия цветов (т.е., Sass дает нам гораздо больше способов для упрощения наших таблиц стилей, но код в Листинге 5.16 использует наиболее важные из них и дает нам хороший старт. Более подробно об этом см. на сайте Sass. Теперь, когда мы закончили шаблон сайта с достойным стилем, пришло время приступить к заполнению ссылок которые мы заглушили символом ’#’. Конечно, мы могли бы просто захардкодить ссылки: но это не Rails Way. Было бы хорошо, если бы URL для about страницы был /about, а не /pages/about. Причем, Rails конвенционально использует named routes (именованные маршруты), которые включают в себя код, подобный Таким образом код обретает более прозрачный смысл и, к тому же, это более гибкий подход, поскольку мы можем изменить определение Полный список наших запланированных ссылок представлен в Таблице 5.1, вместе с соответствием URL и маршрутов. Со временем, мы реализуем все ссылки, но предпоследнюю мы добавим только в конце этой главы, а последняя будет создана лишь в Главе 8.
(рис 5.7) Home страница (/static_pages/home) с добавленным подвалом.
Sass и файлопровод (asset pipeline)
# assets, активы) такими как CSS, JavaScript и изображения. Этот раздел даст вам общее понимание файлопровода, а затем покажет как использовать удивительный инструмент для создания CSS называемый Sass, теперь уже по умолчанию включенный в Rails как часть файлопровода.Файлопровод
$ ls app/assets/
images javascripts stylesheets
/*
* This is a manifest file that'll automatically include all the stylesheets
* available in this directory and any sub-directories. You're free to add
* application-wide styles to this file and they'll appear at the top of the
* compiled file, but it's generally better to create a new file per style
* scope.
*= require_self
*= require_tree .
*/
/*
.
.
.
*= require_self
*= require_tree .
*/
*= require_tree .
*= require_self
foobar.js.coffeefoobar.js.erb.coffeeСинтаксически обалденные таблицы стилей
.center и для .center h1:.center {
text-align: center;
}
.center h1 {
margin-bottom: 10px;
}
.center {
text-align: center;
h1 {
margin-bottom: 10px;
}
}
h1 наследует контекст правила .center.#logo {
float: left;
margin-right: 10px;
font-size: 1.7em;
color: #fff;
text-transform: uppercase;
letter-spacing: -1px;
padding-top: 9px;
font-weight: bold;
line-height: 1;
}
#logo:hover {
color: #fff;
text-decoration: none;
}
id логотипа #logo используется дважды, в первый раз сам по себе и второй раз с атрибутом hover (который отвечает за его внешний вид при наведении курсора). Для того чтобы вложить второе правило нам необходимо сослаться на родительский элемент #logo; в SCSS это достигается с помощью символа следующим образом:#logo {
float: left;
margin-right: 10px;
font-size: 1.7em;
color: #fff;
text-transform: uppercase;
letter-spacing: -1px;
padding-top: 9px;
font-weight: bold;
line-height: 1;
:hover {
color: #fff;
text-decoration: none;
}
}
:hover на #logo:hover при конвертации из SCSS в CSS.footer {
margin-top: 45px;
padding-top: 5px;
border-top: 1px solid #eaeaea;
color: #999;
a {
color: #555;
:hover {
color: #222;
}
}
small {
float: left;
}
ul {
float: right;
list-style: none;
li {
float: left;
margin-left: 10px;
}
}
}
h2 {
.
.
.
color: #999;
}
.
.
.
footer {
.
.
.
color: #999;
}
#999 это светло серый цвет, и мы можем дать ему имя, определив переменную следующим образом:$lightGray: #999;
$lightGray: #999;
.
.
.
h2 {
.
.
.
color: $lightGray;
}
.
.
.
footer {
.
.
.
color: $lightGray;
}
$lightGray являются более говорящими, нежели #999, часто бывает полезным определять переменные даже для не повторяющихся значений. Действительно, фреймворк Bootstrap определяет большое количество переменных для цветов, вы можете ознакомиться с ними онлайн на странице LESS variables. Эта страница определяеет переменные используя LESS, а не Sass, но гем bootstrap-sass обеспечивает нас их Sass эквивалентами. Угадать соответствие несложно; там где LESS использует знак "at" ( @), Sass использует знак доллара $. Глядя на страницу Bootstrap с переменными, мы видим, что переменная для светло-серого цвета уже существует:@grayLight: #999;
bootstrap-sass должна существовать соответствующая SCSS переменная $grayLight. Мы можем использовать ее для замены нашей кастомной переменной, $lightGray, что дает намh2 {
.
.
.
color: $grayLight;
}
.
.
.
footer {
.
.
.
color: $grayLight;
}
white для #fff). Обратите внимание, в частности, на кардинальное улучшение в правилах для тега footer.@import "bootstrap";
/* mixins, variables, etc. */
$grayMediumLight: #eaeaea;
/* universal */
html {
overflow-y: scroll;
}
body {
padding-top: 60px;
}
section {
overflow: auto;
}
textarea {
resize: vertical;
}
.center {
text-align: center;
h1 {
margin-bottom: 10px;
}
}
/* typography */
h1, h2, h3, h4, h5, h6 {
line-height: 1;
}
h1 {
font-size: 3em;
letter-spacing: -2px;
margin-bottom: 30px;
text-align: center;
}
h2 {
font-size: 1.2em;
letter-spacing: -1px;
margin-bottom: 30px;
text-align: center;
font-weight: normal;
color: $grayLight;
}
p {
font-size: 1.1em;
line-height: 1.7em;
}
/* header */
#logo {
float: left;
margin-right: 10px;
font-size: 1.7em;
color: white;
text-transform: uppercase;
letter-spacing: -1px;
padding-top: 9px;
font-weight: bold;
line-height: 1;
:hover {
color: white;
text-decoration: none;
}
}
/* footer */
footer {
margin-top: 45px;
padding-top: 5px;
border-top: 1px solid $grayMediumLight;
color: $grayLight;
a {
color: $gray;
:hover {
color: $grayDarker;
}
}
small {
float: left;
}
ul {
float: right;
list-style: none;
li {
float: left;
margin-left: 10px;
}
}
}
Ссылки в шаблоне
<a href="/static_pages/about">About</a>
<%= link_to "About", about_path %>
about_path и URL изменятся везде где используется about_path.
| Страница | URL | Именованный маршрут |
|---|---|---|
| Home | / | root_path |
| About | /about | about_path |
| Help | /help | help_path |
| Contact | /contact | contact_path |
| Sign up | /signup | signup_path |
| Sign in | /signin | signin_path |
Прежде чем двигаться дальше, давайте добавим страницу Contact (было оставлено в качестве упражнения в Главе 3). Тесты представлены в Листинге 5.17; они просто следуют модели из Листинга 3.19.
require 'spec_helper'
describe "Static pages" do
.
.
.
describe "Contact page" do
it "should have the content 'Contact'" do
visit '/static_pages/contact'
expect(page).to have_content('Contact')
end
it "should have the title 'Contact'" do
visit '/static_pages/contact'
expect(page).to have_title("Ruby on Rails Tutorial Sample App | Contact")
end
end
end
Для того чтобы убедиться что оба теста в Листинге 5.17 не проходят, нам нужно закомментировать "Contact" ссылку в подвале, как это показано в Листинге 5.18.
<footer class="footer">
<small>
<a href="http://railstutorial.org/">Rails Tutorial</a>
by Michael Hartl
</small>
<nav>
<ul>
<li><%= link_to "About", '#' %></li>
<li><%#= link_to "Contact", '#' %></li>
<li><a href="http://news.railstutorial.org/">News</a></li>
</ul>
</nav>
</footer>
В Листинге 5.18 мы видим как выглядят коментирование кода в Embedded Ruby, что подразумевает помещение знака Ruby-комментария # сразу за знаком процента:
<%#= link_to "Contact", '#' %>
В этой точке тест из Листинга 5.17 должен быть провальным:
$ bundle exec rspec spec/requests/static_pages_spec.rb
Код приложения параллелен коду для добавления страницы About в Разделе 3.2.2: вначале мы обновляем маршруты (Листинг 5.19), затем добавляем contact действие в контроллер StaticPages (Листинг 5.20) и, наконец, мы создаем представление Contact (Листинг 5.21).
SampleApp::Application.routes.draw do get "static_pages/home" get "static_pages/help" get "static_pages/about" get "static_pages/contact" . . . end
class StaticPagesController < ApplicationController . . . def contact end end
<% provide(:title, 'Contact') %> <h1>Contact</h1> <p> Contact Ruby on Rails Tutorial about the sample app at the <a href="http://railstutorial.org/contact">contact page</a>. </p>
Теперь убедитесь что тесты проходят:
$ bundle exec rspec spec/requests/static_pages_spec.rb
В этой точке, пройдя через Красный-Зеленый цикл для того чтобы убедиться что мы тестируем правильные вещи, нам следует раскомментировать ссылку "Contact" в подвале (Листинг 5.22). Это далеко не идеальное решение так как теперь наши тесты не смогут отловить регрессию если мы случайно удалим тег h1 из Листинга 5.22. Исправление этой ошибки, требующее наприсания теста специально для содержимого h1 оставлено в качестве упражнения (Раздел 5.6).
<footer class="footer">
<small>
<a href="http://railstutorial.org/">Rails Tutorial</a>
by Michael Hartl
</small>
<nav>
<ul>
<li><%= link_to "About", '#' %></li>
<li><%= link_to "Contact", '#' %></li>
<li><a href="http://news.railstutorial.org/">News</a></li>
</ul>
</nav>
</footer>
С работой которую мы проделали при написании интеграционных тестов для статических страниц, написание тестов для маршрутов довольно простая задача: мы просто заменяем каждое упоминание жестко прописанных адресов на соответствующие именованные маршруты из Таблицы 5.1. Другими словами, мы меняем
visit '/static_pages/about'
на
visit about_path
и так далее для остальных страниц. Результат представлен в Листинге 5.23.
require 'spec_helper'
describe "Static pages" do
describe "Home page" do
it "should have the h1 'Sample App'" do
visit root_path
expect(page).to have_content('Sample App')
end
it "should have the base title" do
visit root_path
expect(page).to have_title("Ruby on Rails Tutorial Sample App")
end
it "should not have a custom page title" do
visit root_path
expect(page).not_to have_title('| Home')
end
end
describe "Help page" do
it "should have the h1 'Help'" do
visit help_path
expect(page).to have_content('Help')
end
it "should have the title 'Help'" do
visit help_path
expect(page).to have_title("Ruby on Rails Tutorial Sample App | Help")
end
end
describe "About page" do
it "should have the h1 'About Us'" do
visit about_path
expect(page).to have_content('About Us')
end
it "should have the title 'About Us'" do
visit about_path
expect(page).to have_title("Ruby on Rails Tutorial Sample App | About Us")
end
end
describe "Contact page" do
it "should have the content 'Contact'" do
visit contact_path
expect(page).to have_content('Contact')
end
it "should have the title 'Contact'" do
visit contact_path
expect(page).to have_title("Ruby on Rails Tutorial Sample App | Contact")
end
end
end
Как обычно, вам следует проверить, что тесты сейчас покраснели:
$ bundle exec rspec spec/requests/static_pages_spec.rb
Кстати, если код в Листинге 5.23 удивляет вас своими повторениями и многословностью, вы не одиноки. Мы отрефакторим эту грязь в красивую драгоценность в Разделе 5.3.4.
Теперь, когда у нас есть нужные нам тесты для URL, пришло время заставить их работать. Как отмечалось в Разделе 3.1, Rails использует для маршрутов файл config/routes.rb. Если вы посмотрите на дефолтный, файл routes.rb, вы увидите беспорядок, но это полезный беспорядок — полный закомментированных примеров маршрутов. Я советую почитать его как-нибудь, а также взглянуть на статью о маршрутах в (rus)Rails Guides для более углубленного изучения маршрутов.
Для того чтобы определить именованные маршруты нам необходимо заменить такие маршруты как
get 'static_pages/help'
на
match '/help', to: 'static_pages#help', via: 'get'
Это дает нам два варианта возможных варианта написания пути к странице help: /help (отвечающую на GET запросы) и именованный маршрут help_path который возвращает путь к этой странице. (На самом деле использование get вместо match даст тот же именованнный маршрут, но использование match более принято.)
Применение этого паттерна к остальным статическим страницам приводит нас к Листингу 5.24. Единственным исключением здесь является страница Home, о которой мы позаботимся в Листинге 5.26.
SampleApp::Application.routes.draw do match '/help', to: 'static_pages#help', via: 'get' match '/about', to: 'static_pages#about', via: 'get' match '/contact', to: 'static_pages#contact', via: 'get' . . . end
Если вы внимательно почитаете код в Листинге 5.24, вы возможно сможете понять что он делает; например, вы можете увидеть что
match '/about', to: 'static_pages#about', via: 'get'
устанавливает соответствие между GET запросом к ’/about’ и именованным маршрутом, а также направляет его в about действие контроллера StaticPages. Прежде это было более очевидно: мы использовали
get 'static_pages/about'
для того чтобы попасть в то же место, но /about более краток. В дополнение, как отмечалось выше, код match ’/about’ также автоматически создает именованные маршруты для использования в контроллерах и представлениях:
about_path -> '/about' about_url -> 'http://localhost:3000/about'
Обратите внимание, что , для развернутого сайта). Как обсуждалось в Разделе 5.3, для того чтобы получить просто /about, вы используете about_path. (Rails Tutorial использует path форму по соглашению, но разница редко имеет значение на практике (т.е также возможно применение _url формы).)
После определения этих маршрутов тесты для About, Contact, и Help страниц должны пройти:
$ bundle exec rspec spec/requests/static_pages_spec.rb
Это оставляет один провальный тест для страницы Home.
Для маршрута Home страницы мы могли бы использовать код вроде этого:
match '/', to: 'static_pages#home', via: 'get'
Однако делать этого не придется; Rails имеет специальный маршрут для корневого URL / ("слэш") расположенный внизу файла (Листинг 5.25).
SampleApp::Application.routes.draw do . . . # You can have the root of your site routed with "root" # root 'welcome#index' . . . end
Используя Листинг 5.25 как модель, мы приходим к Листингу 5.26 чтобы направить корневой URL / к Home странице.
SampleApp::Application.routes.draw do root 'static_pages#home' match '/help', to: 'static_pages#help', via: 'get' match '/about', to: 'static_pages#about', via: 'get' match '/contact', to: 'static_pages#contact', via: 'get' . . . end
Этот код направляет корневой URL / к /static_pages/home, а это означает что http://localhost:3000/ наконец то покажет нечто иное нежели дефолтную Rails страницу из Этот код направляет корневой URL / к /static_pages/home, а это означает что http://localhost:3000/ наконец то покажет нечто иное нежели дефолтную Rails страницу из рис 1.3. Он также дает нам следующие URL хелперы:. Он также дает нам следующие URL хелперы:
root_path -> '/' root_url -> 'http://localhost:3000/'
При этом, все маршруты для статических страниц работают и тесты должны пройти:
$ bundle exec rspec spec/requests/static_pages_spec.rb
Теперь нам осталось просто заполнить ссылки в макете.
Давайте применим именованные маршруты, созданные в Разделе 5.3.2. Это повлечет за собой заполнение второго аргумента link_to функции правильным именованным маршрутом. Например, мы конвертируем
<%= link_to "About", '#' %>
в
<%= link_to "About", about_path %>
и так далее.
Мы начнем с партиала _header.html.erb (Листинг 5.27), который содержит ссылки на Home и Help страницы. Пока мы здесь, мы также, следуя общей конвенции Сети, свяжем логотип с Home страницей.
<header class="navbar navbar-fixed-top navbar-inverse">
<div class="navbar-inner">
<div class="container">
<%= link_to "sample app", root_path, id: "logo" %>
<nav>
<ul class="nav pull-right">
<li><%= link_to "Home", root_path %></li>
<li><%= link_to "Help", help_path %></li>
<li><%= link_to "Sign in", '#' %></li>
</ul>
</nav>
</div>
</div>
</header>
У нас не будет именованного маршрута для "Sign in" ссылки, до Главы 8, поэтому мы пока оставили ее в виде ’#’.
Другим местом со ссылками является партиал подвала, _footer.html.erb, в котором есть ссылки для About и Contact страниц (Листинг 5.28).
<footer class="footer">
<small>
<a href="http://railstutorial.org/">Rails Tutorial</a>
by Michael Hartl
</small>
<nav>
<ul>
<li><%= link_to "About", about_path %></li>
<li><%= link_to "Contact", contact_path %></li>
<li><a href="http://news.railstutorial.org/">News</a></li>
</ul>
</nav>
</footer>
Теперь наш макет имеет ссылки на все статические страницы созданные в Главе 3, так, например, ).
Кстати, стоит отметить, что мы на самом деле не протестировали на наличие ссылок в шаблоне, наши тесты будут провальными, только если маршруты не определены. Вы можете проверить это, закомментировав один из маршрутов в Листинге 5.24 и запустив ваш набор тестов. Метод тестирования который на самом деле обеспечивает уверенность что ссылки существуют и отправляют в правильные места, см. в Разделе 5.6.
(рис 5.8) About страница на /about.
Мы отметили в Разделе 5.3.1 что тесты для статических страниц стали немного многословными и наполнились повторениями (Листинг 5.23). В этом разделе мы применим самые последние фишки RSpec для того чтобы сделать наши тесты более компактными и элегантными.
Давайте взглянем на пару примеров чтобы увидеть как они могут быть улучшены:
describe "Home page" do
it "should have the content 'Sample App'" do
visit root_path
expect(page).to have_content('Sample App')
end
it "should have the base title" do
visit root_path
expect(page).to have_title("Ruby on Rails Tutorial Sample App")
end
it "should not have a custom page title" do
visit root_path
expect(page).not_to have_title('| Home')
end
end
Здесь стоит отметить одну вещь - все три примера включают посещение root_path. Мы можем устранить это дублирование с помощью блока before:
describe "Home page" do
before { visit root_path }
it "should have the content 'Sample App'" do
expect(page).to have_content('Sample App')
end
it "should have the base title" do
expect(page).to have_title("Ruby on Rails Tutorial Sample App")
end
it "should not have a custom page title" do
expect(page).not_to have_title('| Home')
end
end
Здесь используется строка
before { visit root_path }
для того чтобы посещать root_path перед каждым примером. (Метод before можно также вызвать спомощью синонима before(:each).)
Есть еще один источник повторений в каждом примере; у нас есть
it "should have the content 'Sample App'" do
и
expect(page).to have_content('Sample App')
которые, по сути, одно и то же. К тому же, оба примера ссылаются на переменную page. Мы можем устранить это дублирование сказав RSpec-y что page это субъект тестирования с помощью
subject { page }
а затем применив вариант метода it для того чтобы собрать код и описание (теста) в одну строку:
it { should have_content('Sample App') }
Благодаря subject { page }, вызов should автоматически использует переменную page предоставленную гемом Capybara (Раздел 3.2.1).
Применение этих изменений дает нам гораздо более компактные тесты для страницы Home:
subject { page }
describe "Home page" do
before { visit root_path }
it { should have_content('Sample App') }
it { should have_title("Ruby on Rails Tutorial Sample App") }
it { should_not have_title('| Home') }
end
Этот код выглядит лучше, но тест заголовка по-прежнему длинноват. Кроме того, большая часть теста заголовка в Листинге 5.23 содержит длиный текст заголовка вида
"Ruby on Rails Tutorial Sample App | About"
Упражнение в Разделе 3.5 предлагает вам устранить часть этого дублирования посредством определения переменной base_title и интерполяции строк (Листинг 3.31). Мы можем сделать еще лучше, определив full_title, который является параллелью хелпера full_title из Листинга 4.2. Мы сделаем это создав директорию spec/support и файл utilities.rb для RSpec-утилит (Листинг 5.29).
def full_title(page_title)
base_title = "Ruby on Rails Tutorial Sample App"
if page_title.empty?
base_title
else
"#{base_title} | #{page_title}"
end
end
Конечно, это, по сути, дублирование хелпера из Листинга 4.2, но наличие двух независимых методов позволит нам отловить возможные опечатки в базовом заголовке. Однако это все же довольно сомнительная реализация и лучший (немного более продвинутый) подход, который непосредственно тестирует оригинальный хелпер full_title, будет представлен в упражнениях (Раздел 5.6).
Файлы в spec/support директории автоматически включаются RSpec, это означает что мы можем написать тесты для Home страницы следующим образом:
subject { page }
describe "Home page" do
before { visit root_path }
it { should have_content('Sample App') }
it { should have_title(full_title('')) }
it { should_not have_title('| Home') }
end
Теперь мы можем упростить тесты для страниц Help, About и Contact используя теже методы что и для страницы Home. Результаты представлены в Листинге 5.30.
require 'spec_helper'
describe "Static pages" do
subject { page }
describe "Home page" do
before { visit root_path }
it { should have_content('Sample App') }
it { should have_title(full_title('')) }
it { should_not have_title('| Home') }
end
describe "Help page" do
before { visit help_path }
it { should have_content('Help') }
it { should have_title(full_title('Help')) }
end
describe "About page" do
before { visit about_path }
it { should have_content('About') }
it { should have_title(full_title('About Us')) }
end
describe "Contact page" do
before { visit contact_path }
it { should have_content('Contact') }
it { should have_title(full_title('Contact')) }
end
end
Теперь необходимо убедиться что тесты по-прежнему проходят:
$ bundle exec rspec spec/requests/static_pages_spec.rb
Стиль RSpec из Листинга 5.30 намного более лаконичен нежели стиль в Листинге 5.23 — однако, он может быть еще более лаконичным (Раздел 5.6). При дальнейшей разработке примера приложения мы будем использовать этот более компактный стиль везде, где это только будет возможно.
В качестве кульминации нашей работы над макетом и маршрутами, в этом разделе мы сделаем маршрут для страницы регистрации (signup), что будет означать создание второго контроллера. Это первый важный шаг к предоставлению пользователям возможности регистрироваться на нашем сайте, мы сделаем следующий шаг, моделирование пользователя, в Главе 6 и мы закончим работу в Главе 7.
Мы создали наш первый контроллер - StaticPages - еще в Разделе 3.1. Пришло время создать второй — контроллер Users. Как и прежде, мы будем использовать generate для создания простейшего контроллера, который отвечает нашим текущим потребностям, а именно, с одной страницей-заглушкой для регистрации новых пользователей. Следуя конвенции REST архитектуры предпочитаемой Рельсами, мы назовем действие для новых пользователей, new и передадим его в качестве аргумента в generate controller для создания его (действия) автоматически (Листинг 5.31).
$ rails generate controller Users new --no-test-framework
create app/controllers/users_controller.rb
route get "users/new"
invoke erb
create app/views/users
create app/views/users/new.html.erb
invoke helper
create app/helpers/users_helper.rb
invoke assets
invoke coffee
create app/assets/javascripts/users.js.coffee
invoke scss
create app/assets/stylesheets/users.css.scss
Это создает контроллер Users с new действием (Листинг 5.32) и заглушку представления (Листинг 5.33).
class UsersController < ApplicationController def new end end
<h1>Users#new</h1> <p>Find me in app/views/users/new.html.erb</p>
С кодом из Раздела 5.4.1 у нас уже есть рабочая страница для создания новых пользователей на /users/new, но вспомним из Таблицы 5.1, что вместо этого нам необходим URL /signup. Как и в Разделе 5.3, мы начнем с написания интеграционных тестов, которые мы сейчас сгенерируем:
$ rails generate integration_test user_pages
Затем, следуя модели спеков для статических страниц в Листинге 5.30, мы заполняем тесты для страниц пользователей кодом тестирующим содержимое h1 и title тегов как это показано в Листинге 5.34.
require 'spec_helper'
describe "User pages" do
subject { page }
describe "signup page" do
before { visit signup_path }
it { should have_content('Sign up') }
it { should have_title(full_title('Sign up')) }
end
end
Мы можем запустить эти тесты командой rspec как обычно:
$ bundle exec rspec spec/requests/user_pages_spec.rb
Стоит отметить что мы также можем запустить все интеграционные тесты передав в качестве параметра всю директорию вместо одного файла:
$ bundle exec rspec spec/requests/
Опираясь на этот паттерн вы можете догадаться как запустить все спеки:
$ bundle exec rspec spec/
Обычно мы будем использовать именно этот способ запуска тестов в остальной части учебника. Кстати, стоит отметить, что вы можете также запускать набор тестов с помощью Rake задачи spec:
$ bundle exec rake spec
(Фактически, вы можете просто набрать rake; дефолтное поведение rake это запуск набора тестов.) Единственное 'но' - использование rake для запуска тестов примера приложения в его текущем состоянии вызовет ошибку т.к. этой команде требуется правильно подготовленная тестовая база данных; этот шаг мы отложили до Раздела 6.2.1.
По конструкции, контроллер Users уже имеет new действие, так что все что нам необходимо для прохождения тестов это правильные маршрут и контент представления. Мы будем следовать примерам из Листинга 5.24 и добавим match ’/signup’ правило для URL регистрации (Листинг 5.35).
SampleApp::Application.routes.draw do get "users/new" root 'static_pages#home' match '/signup', to: 'users#new', via: 'get' match '/help', to: 'static_pages#help', via: 'get' match '/about', to: 'static_pages#about', via: 'get' match '/contact', to: 'static_pages#contact', via: 'get' . . . end
Обратите внимание, мы сохранили правило get "users/new", которое было сгенерировано автоматически при создании Users контроллера в Листинге 5.31. В настоящее время это правило необходимо для работы адреса ’users/new’, но оно не соответствует REST конвенции (Таблица 2.2) и мы избавимся от него в Разделе 7.1.2.
Теперь все что нам теперь необходимо для прохождения тестов это представление с заголовком браузера и заголовком первого уровня "Sign up" (Листинг 5.36).
<% provide(:title, 'Sign up') %> <h1>Sign up</h1> <p>Find me in app/views/users/new.html.erb</p>
Теперь тест страницы регистрации из Листинга 5.34 должен пройти. Нам осталось лишь добавить соответствующую ссылку к кнопке на Home странице. Как и с прочими маршрутами, match ’/signup’ дает нам именованный маршрут signup_path, который мы применяем в Листинге 5.37.
<div class="center hero-unit">
<h1>Welcome to the Sample App</h1>
<h2>
This is the home page for the
<a href="http://railstutorial.org/">Ruby on Rails Tutorial</a>
sample application.
</h2>
<%= link_to "Sign up now!", signup_path, class: "btn btn-large btn-primary" %>
</div>
<%= link_to image_tag("rails.png", alt: "Rails"), 'http://rubyonrails.org/' %>
С этим работу над ссылками и именованными маршрутами можно считать законченной, по крайней мере до тех пор, пока мы не добавим маршрут для входа на сайт (Глава 8). Получившаяся страница для регистрации пользователей (на URL /signup) представлена на рис 5.9.
(рис 5.9) Страница регистрации (/signup).
В этой точке тесты должны проходить:
$ bundle exec rspec spec/
В этой главе мы привели шаблон нашего приложение в чувство и отполировали маршруты. Остальная часть книги посвящена конкретизации примера приложения: во-первых, путем добавления пользователей, которые могут зарегистрироваться, войти в систему и выйти; затем добавлением пользовательских микросообщений и, наконец, добавлением возможности следить за микросообщениями других пользователей.
Если вы используете Git, вам следует объединить изменения с мастер-веткой:
$ git add . $ git commit -m "Finish layout and routes" $ git checkout master $ git merge filling-in-layout
Также вы можете отправить изменения на GitHub:
$ git push
Наконец, вы можете развернуть приложение на Heroku:
$ git push heroku
Результатом должно стать работающий пример приложения на продакшен сервере:
$ heroku open
Если столкнулись с проблемами, попробуйте выполнить
$ heroku logs
чтобы узнать об ошибках с помощью логов Heroku.
shared examples предназначенную для устранения такого вида дублирования. Следуя примеру в Листинге 5.39, заполните пропущенные тесты для страниц Help, About и Contact. Обратите внимание - команда let кратко обсуждаемая в Листинге 3.31, создает локальную переменную с данным значением по запросу (т.е. при использовании переменной), в отличие от переменной экземпляра, которая создается в момент назначения. (Листинг 5.39 также использует have_selector из Листинга 5.38.)visit и click_link внутри RSpec интеграционного теста. Дополните код из Листинга 5.40 чтобы проверить что все ссылки шаблона правильно определены.spec/helpers и файл application_helper_spec.rb.) Затем include его в тест с помощью кода в Листинге 5.42. Проверьте, запустив тесты, что новый код работает. Примечание: Листинг 5.41 использует регулярные выражения о которых мы узнаем больше в Разделе 6.2.4. (Спасибо Alex Chaffee за совет и код используемый в этом упражнении.)require 'spec_helper'
describe "Static pages" do
.
.
.
describe "Contact page" do
before { visit contact_path }
it { should have_selector('h1', text: 'Contact') }
it { should have_title(full_title('Contact')) }
end
end
require 'spec_helper'
describe "Static pages" do
subject { page }
shared_examples_for "all static pages" do
it { should have_selector('h1', text: heading) }
it { should have_title(full_title(page_title)) }
end
describe "Home page" do
before { visit root_path }
let(:heading) { 'Sample App' }
let(:page_title) { '' }
it_should_behave_like "all static pages"
it { should_not have_title('| Home') }
end
describe "Help page" do
.
.
.
end
describe "About page" do
.
.
.
end
describe "Contact page" do
.
.
.
end
end
require 'spec_helper'
describe "Static pages" do
.
.
.
it "should have the right links on the layout" do
visit root_path
click_link "About"
expect(page).to have_title(full_title('About Us'))
click_link "Help"
expect(page).to # заполнить
click_link "Contact"
expect(page).to # заполнить
click_link "Home"
click_link "Sign up now!"
expect(page).to # заполнить
click_link "sample app"
expect(page).to # заполнить
end
end
require 'spec_helper'
describe ApplicationHelper do
describe "full_title" do
it "should include the page title" do
expect(full_title("foo")).to match(/foo/)
end
it "should include the base title" do
expect(full_title("foo")).to match(/^Ruby on Rails Tutorial Sample App/)
end
it "should not include a bar for the home page" do
expect(full_title("")).not_to match(/\|/)
end
end
end
include ApplicationHelper
В процессе краткого обзора Ruby в Главе 4 мы узнали о подключении таблиц стилей к примеру приложения, но, как было отмечено в Разделе 4.3.4, эти таблицы стилей в настоящий момент пусты. В этой главе мы это изменим, включив фреймворк Bootstrap в наше приложение и добавив затем немного своих стилей.
Rails Учебник это книга по веб-разработке, а не по веб-дизайну, но работа над приложением, которое выглядит как полное дерьмо - удручает, поэтому в этом разделе мы добавим некоторые структуры в шаблон и придадим ему минимальный дизайн с CSS. В дополнение к нашим собственным CSS правилам, мы будем использовать Bootstrap - CSS фреймворк с открытым исходным кодом от Twitter. Мы также придадим нашему коду немного стиля, так сказать, используя частичные шаблоны почистим шаблон, поскольку он немного загроможден.
При создании веб-приложений, часто бывает полезным получить общий вид пользовательского интерфейса как можно раньше. Таким образом, на протяжении остальной части книги, я буду часто использовать макеты (в контексте Веб часто называемыми каркасами), которые являются грубыми набросками того, как приложение, возможно, будет выглядеть.
(рис 5.1) Каркас Home страницы примера приложения.
Как обычно, если вы используете Git для управления версиями, то сейчас самое время создать новую ветку:
$ git checkout -b filling-in-layout
В качестве первого шага к добавлению ссылок и стилей к примеру приложения, мы добавим в файл шаблона сайта . (Примечание: это (пока) не очень приятно.)
<!DOCTYPE html>
<html>
<head>
<title><%= full_title(yield(:title)) %></title>
<%= stylesheet_link_tag "application", media: "all",
"data-turbolinks-track" => true %>
<%= javascript_include_tag "application", "data-turbolinks-track" => true %>
<%= csrf_meta_tags %>
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<header class="navbar navbar-fixed-top navbar-inverse">
<div class="navbar-inner">
<div class="container">
<%= link_to "sample app", '#', id: "logo" %>
<nav>
<ul class="nav pull-right">
<li><%= link_to "Home", '#' %></li>
<li><%= link_to "Help", '#' %></li>
<li><%= link_to "Sign in", '#' %></li>
</ul>
</nav>
</div>
</div>
</header>
<div class="container">
<%= yield %>
</div>
</body>
</html>
Давайте просмотрим на новые элементы в Листинге 5.1 сверху вниз. Как вкратце отмечалось в Разделе 3.3.2, Rails 4 по умолчанию использует HTML5 (о чем говорит doctype <!DOCTYPE html>); т.к. HTML5 — новый стандарт и некоторые браузеры (особенно старые версии Internet Explorer) пока не полностью его поддерживают, мы включим JavaScript код (известный как "HTML5 shim") чтобы обойти это затруднение:
<!--[if lt IE 9]> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <![endif]-->
Немного странный синтаксис
<!--[if lt IE 9]>
включает закомментированную строку, только если версия Microsoft Internet Explorer (IE) ниже чем 9 (if lt IE 9). Чудной [if lt IE 9] синтаксис не является частью Rails; это условный комментарий поддерживаемый браузерами Internet Explorer специально для подобных ситуаций. Это хорошая вещь, хотя бы потому, что это означает, что мы можем включить дополнительную таблицу стилей только для IE браузеров версии менее 9, не затрагивая такие браузеры как Firefox, Chrome и Safari.
Следующий раздел включает header для (текстового) логотипа сайта, несколько разделителей (использующих div тег) и список элементов с навигационными ссылками:
<header class="navbar navbar-fixed-top navbar-inverse">
<div class="navbar-inner">
<div class="container">
<%= link_to "sample app", '#', id: "logo" %>
<nav>
<ul class="nav pull-right">
<li><%= link_to "Home", '#' %></li>
<li><%= link_to "Help", '#' %></li>
<li><%= link_to "Sign in", '#' %></li>
</ul>
</nav>
</div>
</div>
</header>
Здесь тег navbar, navbar-inverse и navbar-fixed-top, разделенных пробелом:
<header class="navbar navbar-fixed-top navbar-inverse">
Всем HTML элементам могут быть назначены как классы, так и id (идентификаторы); это всего лишь ярлыки и они используются для стилевания сайта с помощью CSS (Раздел 5.1.2). Основное различие между классами и идентификаторами, в том, что классы могут быть использованы несколько раз на странице, а идентификаторы могут быть использованы лишь один раз. В данном случае, и navbar и navbar-fixed-top классы имеют особое значение для фреймворка Bootstrap, который мы установим и начнем использовать в Разделе 5.1.2.
Внутри тега header мы видим несколько div тегов:
<div class="navbar-inner"> <div class="container">
Тег div это разделитель; он не делает ничего, кроме разделения документа на отдельные части. В старом стиле HTML, div теги использовали почти для всех разделов сайта, но HTML5 добавил header, nav и section элементы для разделов, общих для многих приложений. В данном случае каждый div снабжен CSS классом. Как и в случае с тегом header, эти классы имеют особое значение для Bootstrap.
После дивов мы неожиданно наталкиваемся на встроенный Ruby:
<%= link_to "sample app", '#', id: "logo" %>
<nav>
<ul class="nav pull-right">
<li><%= link_to "Home", '#' %></li>
<li><%= link_to "Help", '#' %></li>
<li><%= link_to "Sign in", '#' %></li>
</ul>
</nav>
Здесь используется Rails хелпер link_to для создания ссылок (которые мы создавали непосредственно с якорным (анкорным) тегом a в Разделе 3.3.2); первый аргумент в link_to это текст ссылки, а второй это URL. Мы заменим URL на именованные маршруты в Разделе 5.3.3, но сейчас мы используем заглушки URL ’#’ обычно применяемые в веб-дизайне. Третий аргумент это хэш опций, в данном случае добавление CSS id logo для ссылки на пример приложения. (У остальных трех ссылок хэш опций отсутствует, и это нормально поскольку это необязательный аргумент.) Rails хелперы часто принимают хэш опций подобным образом, что дает нам возможность добавлять произвольные HTML опции оставаясь в рамках Rails.
Второй элемент внутри дивов это список навигационных ссылок, созданный с помощью тега ненумерованного списка ul, совместно с тегом элемента списка li:
<nav>
<ul class="nav pull-right">
<li><%= link_to "Home", '#' %></li>
<li><%= link_to "Help", '#' %></li>
<li><%= link_to "Sign in", '#' %></li>
</ul>
</nav>
Тег nav, хотя формально здесь и не нужен, служит для обозначения того что это именно навигационные ссылки. Классы nav и pull-right тега ul имеют особое значение для Bootstrap. После того как Rails обработает этот шаблон и оценит встроенный Ruby, список будет выглядеть следующим образом:
<nav>
<ul class="nav pull-right">
<li><a href="#">Home</a></li>
<li><a href="#">Help</a></li>
<li><a href="#">Sign in</a></li>
</ul>
</nav>
Завершающая часть шаблона это div для основного контента:
<div class="container"> <%= yield %> </div>
Как и прежде, класс container имеет специальное значение для Bootstrap. Как мы узнали в Разделе 3.3.4, метод yield вставляет содержимое каждой страницы в шаблон сайта.
Если не брать в расчет подвал сайта, который мы добавим в Разделе 5.1.3, наш шаблон теперь завершен, и мы можем посмотреть на результаты, посетив, например, Home страницу. Для того чтобы извлечь пользу из ожидающих своего часа элементов стиля, мы добавим несколько дополнительных элементов к home.html.erb представлению (Листинг 5.2).
<div class="center hero-unit">
<h1>Welcome to the Sample App</h1>
<h2>
This is the home page for the
<a href="http://railstutorial.org/">Ruby on Rails Tutorial</a>
sample application.
</h2>
<%= link_to "Sign up now!", '#', class: "btn btn-large btn-primary" %>
</div>
<%= link_to image_tag("rails.png", alt: "Rails"), 'http://rubyonrails.org/' %>
В ожидании добавления пользователей к нашему сайту в Главе 7, первый link_to создает ссылку-заглушку вида
<a href="#" class="btn btn-large btn-primary">Sign up now!</a>
В теге div CSS класс hero-unit предназначен для Bootstrap, также как и btn, btn-large и btn-primary классы в кнопке signup
Второй
<img alt="Rails" src="/assets/rails.png" />
Alt атрибут, это то, что будет отображаться, если нет изображения, также его используют читатели экрана для слабовидящих. Хотя люди иногда пренебрежительно относятся к включению атрибута alt для изображений, на самом деле его включение требуется HTML стандартом. К счастью, Rails включает дефолтный атрибут alt; если вы не укажете его явно при вызове image_tag, Rails пропишет в него название файла изображения (за вычетом расширения). В данном случае мы все же явно установили alt текст для того чтобы капитализировать "Rails"
В предыдущих версиях Rails логотип ) и поместить его в директорию app/assets/images/. (Вам, возможно, также потребуется создать эту директорию, или командой mkdir или с помощью графического менеджера файлов.) Поскольку мы использовали хелпер image_tag в Листинге 5.2, Rails автоматически будет находить любые изображения в этой директории с помощью файлопровода (Раздел 5.2).
Теперь мы, наконец, готовы увидеть плоды наших трудов (рис 5.2). Говорите "Не впечатляет"? Может быть. К счастью, однако, мы проделали хорошую работу, прописав для наших HTML элементов внятные классы и идентификаторы, которые позволят нам отстилить наш сайт с помощью CSS.
(рис 5.2) Страница Home (/static_pages/home) без кастомного CSS.
В Разделе 5.1.1 мы связали многие HTML элементы с CSS классами, которые дают нам большую гибкость при построении шаблона с помощью CSS. Как отмечалось в Разделе 5.1.1, многие из этих классов специфичны для Bootstrap - фреймворка от Twitter который делает легким создание приятного веб дизайна и элементов пользовательского интерфейса для HTML5 приложений. В этом разделе мы будем комбинировать Bootstrap с небольшим количеством кастомных CSS правил для того чтобы придать некоторый стиль примеру приложения.
Нашим первым шагом будет добавление Bootstrap, что может быть сделано в Rails приложениях с помощью bootstrap-sass гема, как это показано в Листинге 5.3. Фреймворк Bootstrap изначально использует язык LESS CSS для создания динамических таблиц стилей, но файлопровод в Rails по умолчанию поддерживает (очень похожий) язык Sass (Раздел 5.2), bootstrap-sass конвертирует LESS в Sass и делает все необходимые файлы Bootstrap доступными для текущего приложения.
source 'https://rubygems.org' ruby '2.0.0' #ruby-gemset=railstutorial_rails_4_0 gem 'rails', '4.0.2' gem 'bootstrap-sass', '2.3.2.0' . . .
Для установки Bootstrap мы, как обычно запускаем bundle install:
$ bundle install
Затем рестартуем веб-сервер для того чтобы изменения вступили в силу. (На большинстве систем рестарт сервера подразумевает нажатие Ctrl-C и последующий запуск rails server.) Наконец, (для Rails 4.0) нам нужно добавить одну строку в config/application.rb для того чтобы сделать bootstrap-sass совместимым с файлопроводом, как это показано в Листинге 5.4.
require File.expand_path('../boot', __FILE__)
.
.
.
module SampleApp
class Application < Rails::Application
.
.
.
config.assets.precompile += %w(*.png *.jpg *.jpeg *.gif)
end
end
Первый шаг в добавлении кастомных CSS к нашему приложению это создание файла для их хранения:
app/assets/stylesheets/custom.css.scss
Здесь важны как название директории так и название файла. Директория
app/assets/stylesheets
является частью файлопровода (Раздел 5.2) и любые таблицы стилей в этой директории будут автоматически включены как часть файла application.css включенного в шаблон сайта. Кроме того, название файла custom.css.scss включает расширение .css, которое обозначает файл CSS, и расширение .scss которое указывает на то что это "Sassy CSS" файл и говорит файлопроводу что этот файл необходимо обрабатывать с помошью Sass. (Мы не будем использовать Sass до Раздела 5.2.2, но сейчас он необходим bootstrap-sass гему.)
Внутри файла для кастомных CSS мы можем использовать функцию @import для включения Bootstrap, как это показано в Листинге 5.5.
@import "bootstrap";
Эта единственная строка включает весь Bootstrap CSS фреймворк, результат показан на рис 5.3 (Вам, возможно, придется рестартовать веб сервер. Стоит также отметить что скриншоты используют Bootstrap 2.0, в то время как учебник использует Bootstrap 2.3, так что могут быть небольшие отличия во внешнем виде, но это не повод для беспокойства.) Текст расположен плохо и логотип оказался неотстиленым, но цвета и кнопка регистрации выглядят многообещающе.
(рис 5.3) Пример приложения с Bootstrap CSS.
Затем мы добавим немного CSS который будет использован для стилизации шаблона и каждой отдельной страницы, как это показано в Листинге 5.6. В Листинге 5.6 довольно много правил; для того чтобы понять что делает CSS правило, часто бывает полезным закомментировать его с помощью CSS комментариев, т.e., поместив его внутрь .
@import "bootstrap";
/* universal */
html {
overflow-y: scroll;
}
body {
padding-top: 60px;
}
section {
overflow: auto;
}
textarea {
resize: vertical;
}
.center {
text-align: center;
}
.center h1 {
margin-bottom: 10px;
}
(рис 5.4) Добавление нескольких отступов и прочих универсальных стилей.
Обратите внимание что CSS в Листинге 5.6 имеют последовательный вид. В целом, CSS правила относятся либо к классу, id, HTML тегу или к какой либо их комбинации за которыми идет список стилевых команд. Например,
body {
padding-top: 60px;
}
задает отступ в 60 пикселей от верхнего края страницы. Благодаря классу navbar-fixed-top в теге header, Bootstrap фиксирует панель навигации вверху страницы, таким образом отступ служит для разделения основного текста и навигации. (Поскольку дефолтный цвет навигационной панели в Boostrap 2.1 отличается от того что ранее использовался в версии 2.0, нам пришлось добавить класс navbar-inverse для того чтобы сделать его черным вместо (теперь) дефолтного белого.) Между тем, CSS в правиле
.center {
text-align: center;
}
связывает класс center со свойством text-align: center. Другими словами, точка . в .center указывает на то, что правило стилизует класс. (Как мы увидим в Листинге 5.8, знак фунта # указывает на правило для стилизации CSS id.) Это означает что этот элемент внутри любого тега (такого как div) с классом center будет отцентрован на странице. (Мы увидим пример этого класса в Листинге 5.2.)
Хотя Bootstrap поставляется с CSS правилами для приятной типографики, мы также добавим немного кастомных правил для представления текста на нашем сайте, как это показано в Листинге 5.7. (Не все эти правила применяются к странице Home page, но каждое правило будет использовано в какой-то части примера приложения.) Результат Листинга 5.7 показан на рис 5.7" type="example">Листинге 5.7. (Не все эти правила применяются к странице Home page, но каждое правило будет использовано в какой-то части примера приложения.) Результат Листинга 5.7 показан на @import "bootstrap";
.
.
.
/* typography */
h1, h2, h3, h4, h5, h6 {
line-height: 1;
}
h1 {
font-size: 3em;
letter-spacing: -2px;
margin-bottom: 30px;
text-align: center;
}
h2 {
font-size: 1.2em;
letter-spacing: -1px;
margin-bottom: 30px;
text-align: center;
font-weight: normal;
color: #999;
}
p {
font-size: 1.1em;
line-height: 1.7em;
}
Наконец, мы добавим несколько правил для придания стиля логотипу сайта, который представляет собой текст "sample app". CSS в Листинге 5.8 конвертирует текст в верхний регистр и изменяет его размер, цвет и положение. (Мы использовали CSS id потому что предполагаем что логотип сайта будет на странице в единственном экземпляре, но вы можете использовать класс вместо него.) Здесь . Хотя шаблон в Листинге 5.1 и выполняет свою работу, но он становится немного суматошным. HTML shim занимает три строки и использует странный IE-специфичный синтаксис, и было бы хорошо вынести его в какое нибудь отдельное место. К тому же заголовок формирует отдельную логическую единицу и он весь должен быть упакован в одном месте. Мы можем достигнуть этого, используя удобное Rails-приспособление называемое partials (# партиалы, частичные шаблоны). Давайте сначала посмотрим, как шаблон выглядит после определения партиалов (Листинг 5.9). В Листинге 5.9 мы заменили HTML shim строки одним вызовом Rails помощника называемого Эта строка приводит к поиску файла, называемого Конечно, чтобы заполучить частичные шаблоны на работу, мы должны заполнить их чем-нибудь; в случае shim-партиала это просто три строки shim-кода из Листинга 5.1; результат представлен в Листинге 5.10. Аналогичным образом, мы можем переместить материал header-а в частичный шаблон показанный в Листинге 5.11 и вставить его в шаблон другим вызовом Теперь, когда мы знаем как сделать частичные шаблоны, давайте добавим подвал (footer) для каждой страницы сайта, чтобы он шел вместе с шапкой (header). К этому моменту вы, вероятно, догадались, что мы будем называть его Как и в шапке (header), в подвале (footer) мы использовали Мы можем вставить партиал подвала в шаблон тем же образом, что и shim и header партиалы (Листинг 5.13). Конечно, подвал будет уродливым, если его немного не отстилить (Листинг 5.14). Результаты представлены на рис 5.14" type="example">Листинг 5.14). Результаты представлены на .
.
.
/* footer */
footer {
margin-top: 45px;
padding-top: 5px;
border-top: 1px solid #eaeaea;
color: #999;
}
footer a {
color: #555;
}
footer a:hover {
color: #222;
}
footer small {
float: left;
}
footer ul {
float: right;
list-style: none;
}
footer ul li {
float: left;
margin-left: 10px;
}
Одним из наиболее значитильных нововведений в последних версиях Rails является файлопровод (asset pipeline), который значительно улучшает создание и управление статическими файлами ( Файлопровод включает в себя множество подкапотных изменений в Rails, но с точки зрения типичного Rails разработчика есть три принципиальных момента в которых нужно разобраться: директории ассетов (активов, файлов), файлы-манифесты и препроцессоры. Директории ассетов В версиях Rails до 3.0 включительно, статические ассеты жили в директории public/ следующим образом: Файлы в этих директориях (даже в версиях "после 3.0") автоматически выдаются через запрос к http://example.com/stylesheets, и т.д.. Начиная с Rails 3.1 и продолжая в Rails 4, существуют три каноничные директории для статических ассетов, каждая со своим собственным назначением: Как вы можете догадаться, каждая из этих директорий имеет субдиректории для каждого класса ассетов, например, Теперь мы в состоянии понять причины, по которым мы поместили файл custom.css.scss в директорию app/assets в Разделе 5.1.2: custom.css.scss специфичен для примера приложения, поэтому он отправляется в app/assets/stylesheets. Файлы-манифесты После того как вы поместили ваши ассеты в предназначенные им директории, вы можете использовать файлы-манифесты для того, чтобы сказать Rails (с помошью гема Sprockets) как скомбинировать их для того чтобы сформировать один единственный файл. (Это относится к CSS и JavaScript но не к изображениям.) В качестве примера, давайте взглянем на дефолтный манифест файл для таблиц стилей приложения (Листинг 5.15). Ключевыми строками здесь являются CSS комментарии, но именно они используются Sprockets для включения правильных файлов: Здесь обеспечивает включение всех CSS файлов из директории app/assets/stylesheets (в том числе вложенные субдиректории) в CSS приложения. Строка указывает где в загрузочной последовательности будет включен CSS код самого application.css файла. Rails поставляется с вполне вменяемыми дефолтными манифест-файлами и в Rails Tutorial мы не будем изменять их, но в случае если вам понадобится их изменить, см. rus)Rails Guides введение в asset pipeline чтобы узнать об этом подробнее. Препроцессоры После того как вы собрали ваши активы, Rails подготавливает их для шаблона сайта, прогоняя их через несколько препроцессоров и комбинирует их с помощью манифест-файлов для доставки в браузер. Мы говорим Rails какой препроцессор использовать с помощью расширения файла; наиболее употребительными являются .scss для Sass, .coffee для CoffeeScript и .erb для встроенного Ruby (ERb). Мы впервые рассказывали о ERb в Разделе 3.3.3 и о Sass в Разделе 5.2.2. В этом учебнике нам не понадобится CoffeeScript. Если вам интересно узнать об этом элегантном маленьком языке который компилируется в JavaScript, советую начать с RailsCast on CoffeeScript basics. Препроцессоры могут быть сцеплены, например прогоняется через препроцессор CoffeeScript, а прогоняется и через CoffeeScript и через ERb (справа налево, т.е. CoffeeScript первым). Производительность в продакшен Одно из самых замечательных свойств файлопровода заключается в том, что он автоматически приводит к повышению производительности в продакшен приложении. Традиционный подход к организации CSS и JavaScript подразумевает разбиение функциональности между отдельными файлами и использование приятного глазу форматирования (с большим количеством отступов). Что является удобным для программистов, но неэффективно в продакшен; включение множества крупноразмерных файлов может серьезно увеличить время загрузки страниц (а это один из наиболее значимых факторов влияющих на впечатление получаемое пользователями от сайта). С файлопроводом, в продакшен все таблицы стилей приложения заворачиваются в один CSS файл (application.css), весь JavaScript код приложения заворачивается в один JavaScript файл (javascripts.js) и все подобные файлы (включая и те что хранятся в lib/assets и vendor/assets) минимизируются для удаления ненужных пробелов увеличивающих размер файла. В результате мы
получаем лучшее для обоих миров: множество хорошо отформатированных файлов для удобства программистов и объединенные в один, оптимизированные файлы для продакшен. Как было вкратце отмечено в Разделе 5.1.2, Sass поддерживает формат называемый SCSS (обозначается расширением файла Вложение Общепринятым паттерном в написании таблиц стилей являются правила применяемые ко вложенным элементам. Например, в Листинге 5.6 у нас есть правило применяемое и для В Sass мы можем заменить это на Здесь вложенное правило Здесь есть второй кандидат на вложение, который требует несколько иного синтаксиса. В Листинге 5.8, мы имеем код Здесь Sass заменяет Обе эти техники вложения элементов применяются в CSS подвала в Листинге 5.14, который может быть трансформирован в следующее: Конвертирование Листинга 5.14 вручную это хорошее упражнение и вы должны убедиться что CSS по прежнему работает как надо после преобразования. Переменные Sass позволяет нам определять переменные для устранения дублирования и написания более выразительного кода. Для примера, взгляните на Листинг 5.7 и Листинг 5.14, мы видим, что здесь есть повторяющийся вызов одного и того же цвета: В данном случае, Это позволяет нам переписать наш SCSS следующим образом: Поскольку названия переменных, такие как Это означает, что, благодаря гему Применение Sass вложения и определения переменных ко всему SCSS файлу дает нам файл представленный в Листинге 5.16. Это использует Sass переменные (как определенные в Bootstrap) так и встроенные названия цветов (т.е., Sass дает нам гораздо больше способов для упрощения наших таблиц стилей, но код в Листинге 5.16 использует наиболее важные из них и дает нам хороший старт. Более подробно об этом см. на сайте Sass. Теперь, когда мы закончили шаблон сайта с достойным стилем, пришло время приступить к заполнению ссылок которые мы заглушили символом ’#’. Конечно, мы могли бы просто захардкодить ссылки: но это не Rails Way. Было бы хорошо, если бы URL для about страницы был /about, а не /pages/about. Причем, Rails конвенционально использует named routes (именованные маршруты), которые включают в себя код, подобный Таким образом код обретает более прозрачный смысл и, к тому же, это более гибкий подход, поскольку мы можем изменить определение Полный список наших запланированных ссылок представлен в Таблице 5.1, вместе с соответствием URL и маршрутов. Со временем, мы реализуем все ссылки, но предпоследнюю мы добавим только в конце этой главы, а последняя будет создана лишь в Главе 8.
(рис 5.5) Добавление нескольких стилей для типографики
@import "bootstrap";
.
.
.
/* header */
#logo {
float: left;
margin-right: 10px;
font-size: 1.7em;
color: #fff;
text-transform: uppercase;
letter-spacing: -1px;
padding-top: 9px;
font-weight: bold;
line-height: 1;
}
#logo:hover {
color: #fff;
text-decoration: none;
}
(рис 5.6) Пример приложения с приятно отстиленным логотипом.
Частичные шаблоны (partials)
<!DOCTYPE html>
<html>
<head>
<title><%= full_title(yield(:title)) %></title>
<%= stylesheet_link_tag "application", media: "all",
"data-turbolinks-track" => true %>
<%= javascript_include_tag "application", "data-turbolinks-track" => true %>
<%= csrf_meta_tags %>
<%= render 'layouts/shim' %>
</head>
<body>
<%= render 'layouts/header' %>
<div class="container">
<%= yield %>
</div>
</body>
</html>
render:<%= render 'layouts/shim' %>
render.<header class="navbar navbar-fixed-top navbar-inverse">
<div class="navbar-inner">
<div class="container">
<%= link_to "sample app", '#', id: "logo" %>
<nav>
<ul class="nav pull-right">
<li><%= link_to "Home", '#' %></li>
<li><%= link_to "Help", '#' %></li>
<li><%= link_to "Sign in", '#' %></li>
</ul>
</nav>
</div>
</div>
</header>
<footer class="footer">
<small>
<a href="http://railstutorial.org/">Rails Tutorial</a>
by Michael Hartl
</small>
<nav>
<ul>
<li><%= link_to "About", '#' %></li>
<li><%= link_to "Contact", '#' %></li>
<li><a href="http://news.railstutorial.org/">News</a></li>
</ul>
</nav>
</footer>
link_to для внутренних ссылок на About и Contact страницы и заглушили URL-адреса символом ’#’ до лучших времен. (Как и header, footer - новый, HTML5, тег.)<!DOCTYPE html>
<html>
<head>
<title><%= full_title(yield(:title)) %></title>
<%= stylesheet_link_tag "application", media: "all",
"data-turbolinks-track" => true %>
<%= javascript_include_tag "application", "data-turbolinks-track" => true %>
<%= csrf_meta_tags %>
<%= render 'layouts/shim' %>
</head>
<body>
<%= render 'layouts/header' %>
<div class="container">
<%= yield %>
<%= render 'layouts/footer' %>
</div>
</body>
</html>
(рис 5.7) Home страница (/static_pages/home) с добавленным подвалом.
Sass и файлопровод (asset pipeline)
# assets, активы) такими как CSS, JavaScript и изображения. Этот раздел даст вам общее понимание файлопровода, а затем покажет как использовать удивительный инструмент для создания CSS называемый Sass, теперь уже по умолчанию включенный в Rails как часть файлопровода.Файлопровод
$ ls app/assets/
images javascripts stylesheets
/*
* This is a manifest file that'll automatically include all the stylesheets
* available in this directory and any sub-directories. You're free to add
* application-wide styles to this file and they'll appear at the top of the
* compiled file, but it's generally better to create a new file per style
* scope.
*= require_self
*= require_tree .
*/
/*
.
.
.
*= require_self
*= require_tree .
*/
*= require_tree .
*= require_self
foobar.js.coffeefoobar.js.erb.coffeeСинтаксически обалденные таблицы стилей
.center и для .center h1:.center {
text-align: center;
}
.center h1 {
margin-bottom: 10px;
}
.center {
text-align: center;
h1 {
margin-bottom: 10px;
}
}
h1 наследует контекст правила .center.#logo {
float: left;
margin-right: 10px;
font-size: 1.7em;
color: #fff;
text-transform: uppercase;
letter-spacing: -1px;
padding-top: 9px;
font-weight: bold;
line-height: 1;
}
#logo:hover {
color: #fff;
text-decoration: none;
}
id логотипа #logo используется дважды, в первый раз сам по себе и второй раз с атрибутом hover (который отвечает за его внешний вид при наведении курсора). Для того чтобы вложить второе правило нам необходимо сослаться на родительский элемент #logo; в SCSS это достигается с помощью символа следующим образом:#logo {
float: left;
margin-right: 10px;
font-size: 1.7em;
color: #fff;
text-transform: uppercase;
letter-spacing: -1px;
padding-top: 9px;
font-weight: bold;
line-height: 1;
:hover {
color: #fff;
text-decoration: none;
}
}
:hover на #logo:hover при конвертации из SCSS в CSS.footer {
margin-top: 45px;
padding-top: 5px;
border-top: 1px solid #eaeaea;
color: #999;
a {
color: #555;
:hover {
color: #222;
}
}
small {
float: left;
}
ul {
float: right;
list-style: none;
li {
float: left;
margin-left: 10px;
}
}
}
h2 {
.
.
.
color: #999;
}
.
.
.
footer {
.
.
.
color: #999;
}
#999 это светло серый цвет, и мы можем дать ему имя, определив переменную следующим образом:$lightGray: #999;
$lightGray: #999;
.
.
.
h2 {
.
.
.
color: $lightGray;
}
.
.
.
footer {
.
.
.
color: $lightGray;
}
$lightGray являются более говорящими, нежели #999, часто бывает полезным определять переменные даже для не повторяющихся значений. Действительно, фреймворк Bootstrap определяет большое количество переменных для цветов, вы можете ознакомиться с ними онлайн на странице LESS variables. Эта страница определяеет переменные используя LESS, а не Sass, но гем bootstrap-sass обеспечивает нас их Sass эквивалентами. Угадать соответствие несложно; там где LESS использует знак "at" ( @), Sass использует знак доллара $. Глядя на страницу Bootstrap с переменными, мы видим, что переменная для светло-серого цвета уже существует:@grayLight: #999;
bootstrap-sass должна существовать соответствующая SCSS переменная $grayLight. Мы можем использовать ее для замены нашей кастомной переменной, $lightGray, что дает намh2 {
.
.
.
color: $grayLight;
}
.
.
.
footer {
.
.
.
color: $grayLight;
}
white для #fff). Обратите внимание, в частности, на кардинальное улучшение в правилах для тега footer.@import "bootstrap";
/* mixins, variables, etc. */
$grayMediumLight: #eaeaea;
/* universal */
html {
overflow-y: scroll;
}
body {
padding-top: 60px;
}
section {
overflow: auto;
}
textarea {
resize: vertical;
}
.center {
text-align: center;
h1 {
margin-bottom: 10px;
}
}
/* typography */
h1, h2, h3, h4, h5, h6 {
line-height: 1;
}
h1 {
font-size: 3em;
letter-spacing: -2px;
margin-bottom: 30px;
text-align: center;
}
h2 {
font-size: 1.2em;
letter-spacing: -1px;
margin-bottom: 30px;
text-align: center;
font-weight: normal;
color: $grayLight;
}
p {
font-size: 1.1em;
line-height: 1.7em;
}
/* header */
#logo {
float: left;
margin-right: 10px;
font-size: 1.7em;
color: white;
text-transform: uppercase;
letter-spacing: -1px;
padding-top: 9px;
font-weight: bold;
line-height: 1;
:hover {
color: white;
text-decoration: none;
}
}
/* footer */
footer {
margin-top: 45px;
padding-top: 5px;
border-top: 1px solid $grayMediumLight;
color: $grayLight;
a {
color: $gray;
:hover {
color: $grayDarker;
}
}
small {
float: left;
}
ul {
float: right;
list-style: none;
li {
float: left;
margin-left: 10px;
}
}
}
Ссылки в шаблоне
<a href="/static_pages/about">About</a>
<%= link_to "About", about_path %>
about_path и URL изменятся везде где используется about_path.
| Страница | URL | Именованный маршрут |
|---|---|---|
| Home | / | root_path |
| About | /about | about_path |
| Help | /help | help_path |
| Contact | /contact | contact_path |
| Sign up | /signup | signup_path |
| Sign in | /signin | signin_path |
Прежде чем двигаться дальше, давайте добавим страницу Contact (было оставлено в качестве упражнения в Главе 3). Тесты представлены в Листинге 5.17; они просто следуют модели из Листинга 3.19.
require 'spec_helper'
describe "Static pages" do
.
.
.
describe "Contact page" do
it "should have the content 'Contact'" do
visit '/static_pages/contact'
expect(page).to have_content('Contact')
end
it "should have the title 'Contact'" do
visit '/static_pages/contact'
expect(page).to have_title("Ruby on Rails Tutorial Sample App | Contact")
end
end
end
Для того чтобы убедиться что оба теста в Листинге 5.17 не проходят, нам нужно закомментировать "Contact" ссылку в подвале, как это показано в Листинге 5.18.
<footer class="footer">
<small>
<a href="http://railstutorial.org/">Rails Tutorial</a>
by Michael Hartl
</small>
<nav>
<ul>
<li><%= link_to "About", '#' %></li>
<li><%#= link_to "Contact", '#' %></li>
<li><a href="http://news.railstutorial.org/">News</a></li>
</ul>
</nav>
</footer>
В Листинге 5.18 мы видим как выглядят коментирование кода в Embedded Ruby, что подразумевает помещение знака Ruby-комментария # сразу за знаком процента:
<%#= link_to "Contact", '#' %>
В этой точке тест из Листинга 5.17 должен быть провальным:
$ bundle exec rspec spec/requests/static_pages_spec.rb
Код приложения параллелен коду для добавления страницы About в Разделе 3.2.2: вначале мы обновляем маршруты (Листинг 5.19), затем добавляем contact действие в контроллер StaticPages (Листинг 5.20) и, наконец, мы создаем представление Contact (Листинг 5.21).
SampleApp::Application.routes.draw do get "static_pages/home" get "static_pages/help" get "static_pages/about" get "static_pages/contact" . . . end
class StaticPagesController < ApplicationController . . . def contact end end
<% provide(:title, 'Contact') %> <h1>Contact</h1> <p> Contact Ruby on Rails Tutorial about the sample app at the <a href="http://railstutorial.org/contact">contact page</a>. </p>
Теперь убедитесь что тесты проходят:
$ bundle exec rspec spec/requests/static_pages_spec.rb
В этой точке, пройдя через Красный-Зеленый цикл для того чтобы убедиться что мы тестируем правильные вещи, нам следует раскомментировать ссылку "Contact" в подвале (Листинг 5.22). Это далеко не идеальное решение так как теперь наши тесты не смогут отловить регрессию если мы случайно удалим тег h1 из Листинга 5.22. Исправление этой ошибки, требующее наприсания теста специально для содержимого h1 оставлено в качестве упражнения (Раздел 5.6).
<footer class="footer">
<small>
<a href="http://railstutorial.org/">Rails Tutorial</a>
by Michael Hartl
</small>
<nav>
<ul>
<li><%= link_to "About", '#' %></li>
<li><%= link_to "Contact", '#' %></li>
<li><a href="http://news.railstutorial.org/">News</a></li>
</ul>
</nav>
</footer>
С работой которую мы проделали при написании интеграционных тестов для статических страниц, написание тестов для маршрутов довольно простая задача: мы просто заменяем каждое упоминание жестко прописанных адресов на соответствующие именованные маршруты из Таблицы 5.1. Другими словами, мы меняем
visit '/static_pages/about'
на
visit about_path
и так далее для остальных страниц. Результат представлен в Листинге 5.23.
require 'spec_helper'
describe "Static pages" do
describe "Home page" do
it "should have the h1 'Sample App'" do
visit root_path
expect(page).to have_content('Sample App')
end
it "should have the base title" do
visit root_path
expect(page).to have_title("Ruby on Rails Tutorial Sample App")
end
it "should not have a custom page title" do
visit root_path
expect(page).not_to have_title('| Home')
end
end
describe "Help page" do
it "should have the h1 'Help'" do
visit help_path
expect(page).to have_content('Help')
end
it "should have the title 'Help'" do
visit help_path
expect(page).to have_title("Ruby on Rails Tutorial Sample App | Help")
end
end
describe "About page" do
it "should have the h1 'About Us'" do
visit about_path
expect(page).to have_content('About Us')
end
it "should have the title 'About Us'" do
visit about_path
expect(page).to have_title("Ruby on Rails Tutorial Sample App | About Us")
end
end
describe "Contact page" do
it "should have the content 'Contact'" do
visit contact_path
expect(page).to have_content('Contact')
end
it "should have the title 'Contact'" do
visit contact_path
expect(page).to have_title("Ruby on Rails Tutorial Sample App | Contact")
end
end
end
Как обычно, вам следует проверить, что тесты сейчас покраснели:
$ bundle exec rspec spec/requests/static_pages_spec.rb
Кстати, если код в Листинге 5.23 удивляет вас своими повторениями и многословностью, вы не одиноки. Мы отрефакторим эту грязь в красивую драгоценность в Разделе 5.3.4.
Теперь, когда у нас есть нужные нам тесты для URL, пришло время заставить их работать. Как отмечалось в Разделе 3.1, Rails использует для маршрутов файл config/routes.rb. Если вы посмотрите на дефолтный, файл routes.rb, вы увидите беспорядок, но это полезный беспорядок — полный закомментированных примеров маршрутов. Я советую почитать его как-нибудь, а также взглянуть на статью о маршрутах в (rus)Rails Guides для более углубленного изучения маршрутов.
Для того чтобы определить именованные маршруты нам необходимо заменить такие маршруты как
get 'static_pages/help'
на
match '/help', to: 'static_pages#help', via: 'get'
Это дает нам два варианта возможных варианта написания пути к странице help: /help (отвечающую на GET запросы) и именованный маршрут help_path который возвращает путь к этой странице. (На самом деле использование get вместо match даст тот же именованнный маршрут, но использование match более принято.)
Применение этого паттерна к остальным статическим страницам приводит нас к Листингу 5.24. Единственным исключением здесь является страница Home, о которой мы позаботимся в Листинге 5.26.
SampleApp::Application.routes.draw do match '/help', to: 'static_pages#help', via: 'get' match '/about', to: 'static_pages#about', via: 'get' match '/contact', to: 'static_pages#contact', via: 'get' . . . end
Если вы внимательно почитаете код в Листинге 5.24, вы возможно сможете понять что он делает; например, вы можете увидеть что
match '/about', to: 'static_pages#about', via: 'get'
устанавливает соответствие между GET запросом к ’/about’ и именованным маршрутом, а также направляет его в about действие контроллера StaticPages. Прежде это было более очевидно: мы использовали
get 'static_pages/about'
для того чтобы попасть в то же место, но /about более краток. В дополнение, как отмечалось выше, код match ’/about’ также автоматически создает именованные маршруты для использования в контроллерах и представлениях:
about_path -> '/about' about_url -> 'http://localhost:3000/about'
Обратите внимание, что , для развернутого сайта). Как обсуждалось в Разделе 5.3, для того чтобы получить просто /about, вы используете about_path. (Rails Tutorial использует path форму по соглашению, но разница редко имеет значение на практике (т.е также возможно применение _url формы).)
После определения этих маршрутов тесты для About, Contact, и Help страниц должны пройти:
$ bundle exec rspec spec/requests/static_pages_spec.rb
Это оставляет один провальный тест для страницы Home.
Для маршрута Home страницы мы могли бы использовать код вроде этого:
match '/', to: 'static_pages#home', via: 'get'
Однако делать этого не придется; Rails имеет специальный маршрут для корневого URL / ("слэш") расположенный внизу файла (Листинг 5.25).
SampleApp::Application.routes.draw do . . . # You can have the root of your site routed with "root" # root 'welcome#index' . . . end
Используя Листинг 5.25 как модель, мы приходим к Листингу 5.26 чтобы направить корневой URL / к Home странице.
SampleApp::Application.routes.draw do root 'static_pages#home' match '/help', to: 'static_pages#help', via: 'get' match '/about', to: 'static_pages#about', via: 'get' match '/contact', to: 'static_pages#contact', via: 'get' . . . end
Этот код направляет корневой URL / к /static_pages/home, а это означает что http://localhost:3000/ наконец то покажет нечто иное нежели дефолтную Rails страницу из Этот код направляет корневой URL / к /static_pages/home, а это означает что http://localhost:3000/ наконец то покажет нечто иное нежели дефолтную Rails страницу из рис 1.3. Он также дает нам следующие URL хелперы:. Он также дает нам следующие URL хелперы:
root_path -> '/' root_url -> 'http://localhost:3000/'
При этом, все маршруты для статических страниц работают и тесты должны пройти:
$ bundle exec rspec spec/requests/static_pages_spec.rb
Теперь нам осталось просто заполнить ссылки в макете.
Давайте применим именованные маршруты, созданные в Разделе 5.3.2. Это повлечет за собой заполнение второго аргумента link_to функции правильным именованным маршрутом. Например, мы конвертируем
<%= link_to "About", '#' %>
в
<%= link_to "About", about_path %>
и так далее.
Мы начнем с партиала _header.html.erb (Листинг 5.27), который содержит ссылки на Home и Help страницы. Пока мы здесь, мы также, следуя общей конвенции Сети, свяжем логотип с Home страницей.
<header class="navbar navbar-fixed-top navbar-inverse">
<div class="navbar-inner">
<div class="container">
<%= link_to "sample app", root_path, id: "logo" %>
<nav>
<ul class="nav pull-right">
<li><%= link_to "Home", root_path %></li>
<li><%= link_to "Help", help_path %></li>
<li><%= link_to "Sign in", '#' %></li>
</ul>
</nav>
</div>
</div>
</header>
У нас не будет именованного маршрута для "Sign in" ссылки, до Главы 8, поэтому мы пока оставили ее в виде ’#’.
Другим местом со ссылками является партиал подвала, _footer.html.erb, в котором есть ссылки для About и Contact страниц (Листинг 5.28).
<footer class="footer">
<small>
<a href="http://railstutorial.org/">Rails Tutorial</a>
by Michael Hartl
</small>
<nav>
<ul>
<li><%= link_to "About", about_path %></li>
<li><%= link_to "Contact", contact_path %></li>
<li><a href="http://news.railstutorial.org/">News</a></li>
</ul>
</nav>
</footer>
Теперь наш макет имеет ссылки на все статические страницы созданные в Главе 3, так, например, ).
Кстати, стоит отметить, что мы на самом деле не протестировали на наличие ссылок в шаблоне, наши тесты будут провальными, только если маршруты не определены. Вы можете проверить это, закомментировав один из маршрутов в Листинге 5.24 и запустив ваш набор тестов. Метод тестирования который на самом деле обеспечивает уверенность что ссылки существуют и отправляют в правильные места, см. в Разделе 5.6.
(рис 5.8) About страница на /about.
Мы отметили в Разделе 5.3.1 что тесты для статических страниц стали немного многословными и наполнились повторениями (Листинг 5.23). В этом разделе мы применим самые последние фишки RSpec для того чтобы сделать наши тесты более компактными и элегантными.
Давайте взглянем на пару примеров чтобы увидеть как они могут быть улучшены:
describe "Home page" do
it "should have the content 'Sample App'" do
visit root_path
expect(page).to have_content('Sample App')
end
it "should have the base title" do
visit root_path
expect(page).to have_title("Ruby on Rails Tutorial Sample App")
end
it "should not have a custom page title" do
visit root_path
expect(page).not_to have_title('| Home')
end
end
Здесь стоит отметить одну вещь - все три примера включают посещение root_path. Мы можем устранить это дублирование с помощью блока before:
describe "Home page" do
before { visit root_path }
it "should have the content 'Sample App'" do
expect(page).to have_content('Sample App')
end
it "should have the base title" do
expect(page).to have_title("Ruby on Rails Tutorial Sample App")
end
it "should not have a custom page title" do
expect(page).not_to have_title('| Home')
end
end
Здесь используется строка
before { visit root_path }
для того чтобы посещать root_path перед каждым примером. (Метод before можно также вызвать спомощью синонима before(:each).)
Есть еще один источник повторений в каждом примере; у нас есть
it "should have the content 'Sample App'" do
и
expect(page).to have_content('Sample App')
которые, по сути, одно и то же. К тому же, оба примера ссылаются на переменную page. Мы можем устранить это дублирование сказав RSpec-y что page это субъект тестирования с помощью
subject { page }
а затем применив вариант метода it для того чтобы собрать код и описание (теста) в одну строку:
it { should have_content('Sample App') }
Благодаря subject { page }, вызов should автоматически использует переменную page предоставленную гемом Capybara (Раздел 3.2.1).
Применение этих изменений дает нам гораздо более компактные тесты для страницы Home:
subject { page }
describe "Home page" do
before { visit root_path }
it { should have_content('Sample App') }
it { should have_title("Ruby on Rails Tutorial Sample App") }
it { should_not have_title('| Home') }
end
Этот код выглядит лучше, но тест заголовка по-прежнему длинноват. Кроме того, большая часть теста заголовка в Листинге 5.23 содержит длиный текст заголовка вида
"Ruby on Rails Tutorial Sample App | About"
Упражнение в Разделе 3.5 предлагает вам устранить часть этого дублирования посредством определения переменной base_title и интерполяции строк (Листинг 3.31). Мы можем сделать еще лучше, определив full_title, который является параллелью хелпера full_title из Листинга 4.2. Мы сделаем это создав директорию spec/support и файл utilities.rb для RSpec-утилит (Листинг 5.29).
def full_title(page_title)
base_title = "Ruby on Rails Tutorial Sample App"
if page_title.empty?
base_title
else
"#{base_title} | #{page_title}"
end
end
Конечно, это, по сути, дублирование хелпера из Листинга 4.2, но наличие двух независимых методов позволит нам отловить возможные опечатки в базовом заголовке. Однако это все же довольно сомнительная реализация и лучший (немного более продвинутый) подход, который непосредственно тестирует оригинальный хелпер full_title, будет представлен в упражнениях (Раздел 5.6).
Файлы в spec/support директории автоматически включаются RSpec, это означает что мы можем написать тесты для Home страницы следующим образом:
subject { page }
describe "Home page" do
before { visit root_path }
it { should have_content('Sample App') }
it { should have_title(full_title('')) }
it { should_not have_title('| Home') }
end
Теперь мы можем упростить тесты для страниц Help, About и Contact используя теже методы что и для страницы Home. Результаты представлены в Листинге 5.30.
require 'spec_helper'
describe "Static pages" do
subject { page }
describe "Home page" do
before { visit root_path }
it { should have_content('Sample App') }
it { should have_title(full_title('')) }
it { should_not have_title('| Home') }
end
describe "Help page" do
before { visit help_path }
it { should have_content('Help') }
it { should have_title(full_title('Help')) }
end
describe "About page" do
before { visit about_path }
it { should have_content('About') }
it { should have_title(full_title('About Us')) }
end
describe "Contact page" do
before { visit contact_path }
it { should have_content('Contact') }
it { should have_title(full_title('Contact')) }
end
end
Теперь необходимо убедиться что тесты по-прежнему проходят:
$ bundle exec rspec spec/requests/static_pages_spec.rb
Стиль RSpec из Листинга 5.30 намного более лаконичен нежели стиль в Листинге 5.23 — однако, он может быть еще более лаконичным (Раздел 5.6). При дальнейшей разработке примера приложения мы будем использовать этот более компактный стиль везде, где это только будет возможно.
В качестве кульминации нашей работы над макетом и маршрутами, в этом разделе мы сделаем маршрут для страницы регистрации (signup), что будет означать создание второго контроллера. Это первый важный шаг к предоставлению пользователям возможности регистрироваться на нашем сайте, мы сделаем следующий шаг, моделирование пользователя, в Главе 6 и мы закончим работу в Главе 7.
Мы создали наш первый контроллер - StaticPages - еще в Разделе 3.1. Пришло время создать второй — контроллер Users. Как и прежде, мы будем использовать generate для создания простейшего контроллера, который отвечает нашим текущим потребностям, а именно, с одной страницей-заглушкой для регистрации новых пользователей. Следуя конвенции REST архитектуры предпочитаемой Рельсами, мы назовем действие для новых пользователей, new и передадим его в качестве аргумента в generate controller для создания его (действия) автоматически (Листинг 5.31).
$ rails generate controller Users new --no-test-framework
create app/controllers/users_controller.rb
route get "users/new"
invoke erb
create app/views/users
create app/views/users/new.html.erb
invoke helper
create app/helpers/users_helper.rb
invoke assets
invoke coffee
create app/assets/javascripts/users.js.coffee
invoke scss
create app/assets/stylesheets/users.css.scss
Это создает контроллер Users с new действием (Листинг 5.32) и заглушку представления (Листинг 5.33).
class UsersController < ApplicationController def new end end
<h1>Users#new</h1> <p>Find me in app/views/users/new.html.erb</p>
С кодом из Раздела 5.4.1 у нас уже есть рабочая страница для создания новых пользователей на /users/new, но вспомним из Таблицы 5.1, что вместо этого нам необходим URL /signup. Как и в Разделе 5.3, мы начнем с написания интеграционных тестов, которые мы сейчас сгенерируем:
$ rails generate integration_test user_pages
Затем, следуя модели спеков для статических страниц в Листинге 5.30, мы заполняем тесты для страниц пользователей кодом тестирующим содержимое h1 и title тегов как это показано в Листинге 5.34.
require 'spec_helper'
describe "User pages" do
subject { page }
describe "signup page" do
before { visit signup_path }
it { should have_content('Sign up') }
it { should have_title(full_title('Sign up')) }
end
end
Мы можем запустить эти тесты командой rspec как обычно:
$ bundle exec rspec spec/requests/user_pages_spec.rb
Стоит отметить что мы также можем запустить все интеграционные тесты передав в качестве параметра всю директорию вместо одного файла:
$ bundle exec rspec spec/requests/
Опираясь на этот паттерн вы можете догадаться как запустить все спеки:
$ bundle exec rspec spec/
Обычно мы будем использовать именно этот способ запуска тестов в остальной части учебника. Кстати, стоит отметить, что вы можете также запускать набор тестов с помощью Rake задачи spec:
$ bundle exec rake spec
(Фактически, вы можете просто набрать rake; дефолтное поведение rake это запуск набора тестов.) Единственное 'но' - использование rake для запуска тестов примера приложения в его текущем состоянии вызовет ошибку т.к. этой команде требуется правильно подготовленная тестовая база данных; этот шаг мы отложили до Раздела 6.2.1.
По конструкции, контроллер Users уже имеет new действие, так что все что нам необходимо для прохождения тестов это правильные маршрут и контент представления. Мы будем следовать примерам из Листинга 5.24 и добавим match ’/signup’ правило для URL регистрации (Листинг 5.35).
SampleApp::Application.routes.draw do get "users/new" root 'static_pages#home' match '/signup', to: 'users#new', via: 'get' match '/help', to: 'static_pages#help', via: 'get' match '/about', to: 'static_pages#about', via: 'get' match '/contact', to: 'static_pages#contact', via: 'get' . . . end
Обратите внимание, мы сохранили правило get "users/new", которое было сгенерировано автоматически при создании Users контроллера в Листинге 5.31. В настоящее время это правило необходимо для работы адреса ’users/new’, но оно не соответствует REST конвенции (Таблица 2.2) и мы избавимся от него в Разделе 7.1.2.
Теперь все что нам теперь необходимо для прохождения тестов это представление с заголовком браузера и заголовком первого уровня "Sign up" (Листинг 5.36).
<% provide(:title, 'Sign up') %> <h1>Sign up</h1> <p>Find me in app/views/users/new.html.erb</p>
Теперь тест страницы регистрации из Листинга 5.34 должен пройти. Нам осталось лишь добавить соответствующую ссылку к кнопке на Home странице. Как и с прочими маршрутами, match ’/signup’ дает нам именованный маршрут signup_path, который мы применяем в Листинге 5.37.
<div class="center hero-unit">
<h1>Welcome to the Sample App</h1>
<h2>
This is the home page for the
<a href="http://railstutorial.org/">Ruby on Rails Tutorial</a>
sample application.
</h2>
<%= link_to "Sign up now!", signup_path, class: "btn btn-large btn-primary" %>
</div>
<%= link_to image_tag("rails.png", alt: "Rails"), 'http://rubyonrails.org/' %>
С этим работу над ссылками и именованными маршрутами можно считать законченной, по крайней мере до тех пор, пока мы не добавим маршрут для входа на сайт (Глава 8). Получившаяся страница для регистрации пользователей (на URL /signup) представлена на рис 5.9.
(рис 5.9) Страница регистрации (/signup).
В этой точке тесты должны проходить:
$ bundle exec rspec spec/
В этой главе мы привели шаблон нашего приложение в чувство и отполировали маршруты. Остальная часть книги посвящена конкретизации примера приложения: во-первых, путем добавления пользователей, которые могут зарегистрироваться, войти в систему и выйти; затем добавлением пользовательских микросообщений и, наконец, добавлением возможности следить за микросообщениями других пользователей.
Если вы используете Git, вам следует объединить изменения с мастер-веткой:
$ git add . $ git commit -m "Finish layout and routes" $ git checkout master $ git merge filling-in-layout
Также вы можете отправить изменения на GitHub:
$ git push
Наконец, вы можете развернуть приложение на Heroku:
$ git push heroku
Результатом должно стать работающий пример приложения на продакшен сервере:
$ heroku open
Если столкнулись с проблемами, попробуйте выполнить
$ heroku logs
чтобы узнать об ошибках с помощью логов Heroku.
shared examples предназначенную для устранения такого вида дублирования. Следуя примеру в Листинге 5.39, заполните пропущенные тесты для страниц Help, About и Contact. Обратите внимание - команда let кратко обсуждаемая в Листинге 3.31, создает локальную переменную с данным значением по запросу (т.е. при использовании переменной), в отличие от переменной экземпляра, которая создается в момент назначения. (Листинг 5.39 также использует have_selector из Листинга 5.38.)visit и click_link внутри RSpec интеграционного теста. Дополните код из Листинга 5.40 чтобы проверить что все ссылки шаблона правильно определены.spec/helpers и файл application_helper_spec.rb.) Затем include его в тест с помощью кода в Листинге 5.42. Проверьте, запустив тесты, что новый код работает. Примечание: Листинг 5.41 использует регулярные выражения о которых мы узнаем больше в Разделе 6.2.4. (Спасибо Alex Chaffee за совет и код используемый в этом упражнении.)require 'spec_helper'
describe "Static pages" do
.
.
.
describe "Contact page" do
before { visit contact_path }
it { should have_selector('h1', text: 'Contact') }
it { should have_title(full_title('Contact')) }
end
end
require 'spec_helper'
describe "Static pages" do
subject { page }
shared_examples_for "all static pages" do
it { should have_selector('h1', text: heading) }
it { should have_title(full_title(page_title)) }
end
describe "Home page" do
before { visit root_path }
let(:heading) { 'Sample App' }
let(:page_title) { '' }
it_should_behave_like "all static pages"
it { should_not have_title('| Home') }
end
describe "Help page" do
.
.
.
end
describe "About page" do
.
.
.
end
describe "Contact page" do
.
.
.
end
end
require 'spec_helper'
describe "Static pages" do
.
.
.
it "should have the right links on the layout" do
visit root_path
click_link "About"
expect(page).to have_title(full_title('About Us'))
click_link "Help"
expect(page).to # заполнить
click_link "Contact"
expect(page).to # заполнить
click_link "Home"
click_link "Sign up now!"
expect(page).to # заполнить
click_link "sample app"
expect(page).to # заполнить
end
end
require 'spec_helper'
describe ApplicationHelper do
describe "full_title" do
it "should include the page title" do
expect(full_title("foo")).to match(/foo/)
end
it "should include the base title" do
expect(full_title("foo")).to match(/^Ruby on Rails Tutorial Sample App/)
end
it "should not include a bar for the home page" do
expect(full_title("")).not_to match(/\|/)
end
end
end
include ApplicationHelper
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.