Asset Pipeline

Вступление

Вы узнали о Моделях, Вьюхах и Контроллерах. Это немало, но впереди еще множество полезных и интересных вещей, которые делают Rails еще более полезным. В этом уроке мы поговорим об Asset Pipeline и некоторых других темах, которые не совсем подходят для других уроков, но о них лучше сказать, чем не сказать.

Пункты для размышления

Постарайтесь ответить на предложенные вопросы. После выполнения задания попробуйте ответить на них ещё раз

  • Что такое "Asset Pipeline"?
  • Что такое "файлы манифеста"?
  • Зачем помещать файлы стилей в отдельные пространства имен?
  • Что значит "экранировать" HTML?

Asset Pipeline

Ассеты в вашем приложении - это традиционно файлы, которые вызываются браузером после того, как он получит первичный HTML-файл. Они включают в себя такие вещи, как CSS-стили, файлы Javascript, изображения, видео, и так далее. В общем-то все, что требует дополнительного запроса, чтобы быть полученным.

Зачастую для разработки проще всего организовать код во множество различных файлов, среди которых можно с удобством ориентироваться и перемещаться. Но если браузер будет вынужден брать пачки разных CSS-файлов, он будет брать их каждый по отдельности, а значит, процесс загрузки страницы существенно замедлится. Если приложение делает слишком много запросов к серверу, пользовательский опыт (он же user experience) будет не самым приятным.

Существует проблема организации файлов ресурсов, например, изображений. Их удобно хранить отдельно и в отдельной директории, но вы хотите, чтобы на них было легко ссылаться.

Rails предлагает решение всех этих проблем: собрать все воедино и отдавать браузеру по одному css и js-файлу. Процесс называется "конкатенацией" и выполняется как раз через Asset Pipeline. Для ваших CSS это значит, что Rails соберет каждый отдельный .css-файл, а затем последовательно объединит их в один большой файл. Во время этого процесса будет запущена "обфускация" или "минификация" - процесс, который удалит все ненужные пробелы и максимально сожмет ваш файл, чтобы браузеру пришлось скачивать как можно меньший объем данных.

Точно так же Rails поступает и с Javascript-файлами - все они соединяются в один, обфусцируются (минифицируются), и в таком виде уже доступны для получения браузером. Гораздо лучше иметь один относительно большой файл, чем делать несколько полноценных HTTP-запросов.

Manifest-файлы

Rails необходимо знать, какие файлы включать в этот огромный ком, и для этого он (фреймворк) использует так называемые файлы manifest. Вашим manifest-файлом для подключения javascript будет app/assets/javascripts/application.js. Его содержимое выглядит закомментированным, но строки, начинающиеся с //= сообщают Rails, какие файлы следует найти и подключить. Комментарии в файле довольно полезны - они говорят:

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .

Хелпер-метод require_tree просто подключает все файлы, находящиеся в указанной директории (точка, следующая за require_tree указывает на текущую директорию. Вместо неё можно указать путь к иной директории).

Ваш файл-манифест для стилей работает по такому же принципу - он доступен по адресу app/assets/stylesheets/application.css.scss:

/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
 * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
 *
 * 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_tree, которые подключает все файлы, расположенные в указанной директории.

Прочитав комментарии вы так же можете увидеть пару других директорий, которые подразумеваются как "локальные" и из которых так же непринужденно можно подключать файлы: это директории lib/assets и vendor/assets. Иногда, если вы начинаете использовать новый гем (например, гем для подключения Twitter Bootstrap), вам необходимо добавить файлы css и js, относящиеся к Bootstrap в файлы манифестов, чтобы быть уверенными, что приложение действительно подключит их.

Результат

Каким будет финальный результат? Rails соберет все указанные файлы воедино и создаст один новый, название которого будет похоже на что-то вроде application-1fc71ddbb281c144b2ee4af31cf0e308.js. Этот бессмысленный набор символов позволяет различать файлы, если вы вносите любые изменения в css или js-код. Если бы они назывались как-то вроде application.js, то ваш браузер кэшировал бы их и так никогда и не узнал бы, что существует обновленный файл, потому что его название не меняется.

Но постойте, откуда браузер узнает, что нужно запрашивать файл application-1fc71ddbb281c144b2ee4af31cf0e308.js? Об этом мы говорили в предыдущем уроке. Теги для загрузки ассетов позволяют указать браузеру, что он должен запрашивать файл с конкретным названием. Когда вы пишете в макете приложения <%= javascript_include_tag "application" %>, Rails автоматически определяет, какой файл запросить, чтобы получить javascript-файл последней версии.

Учитываем это в вашем коде: пространства имен (Namespacing)

Всё это звучит великолепно, прекрасно и ускоряет ваше приложение, но как Asset Pipeline влияет на то, что вы делаете? Зачастую, вы можете просто забыть о файлах манифестов и продолжать писать код. В самом начале написания приложения вы можете держать все стили и js в одном файле, и ничего в итоге не изменится.

Это становится важно когда, например, у вас есть куча разных страниц и вы хотите использовать для них разные стили. Что, если вы хотите, чтобы класс .container на странице логина вел себя несколько иначе, чем на странице выполнения платежа? С Asset Pipeline Rails объединит файлы в один, и вы не сможете быть уверены, какие свойства .container были перезаписаны другими.

Теоретически, вы можете перезаписывать стили из ваших файлов, размещенных в app/assets/stylesheets при помощи явных тегов <style> или просто в других файлах, но это лишь добавит беспорядка и полностью уничтожит смысл существования внешних стилей, ведь код уже не будет чистым.

Давайте так же предположим, что вам реально нравится пользовательский класс .container, позволяющий удобно упорядочивать ваши <div>-элементы. Решением будет "пространство имен", то есть, вы вкладываете один элемент в другой. Этот принцип будет использоваться ОЧЕНЬ часто, так что важно его понять. Вы увидите его использование в стилях, js, модулях в коде и во множестве других мест.

Основная идея в том, чтобы иметь возможность сказать "весь этт код/css/чтоугодно внутри принадлежит только к XYZ". Вы преодолеете этот барьер. Лучше всего показать на примере:

    # app/views/users/show.html.erb
    <div class="user">
      <div class="container">
        <!-- куча кода для отображения пользователю -->
      </div>
    </div>

Теперь этот контейнер и весь код внутри него вложен в класс .user Таким образом мы можем настроить наши стили так, чтобы они применялись непосредственно к классу .container внутри класса .user:

  # app/assets/stylesheets/user.css.scss
  # Note: I'm not going to use SCSS code because we haven't covered it yet
  .user .container{
    // style stuff
  }

Это хорошо потому, что теперь мы конкретно обозначили контейнер, используемый страницами пользователей.

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

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

Rails в окружении для разработки

Asset pipeline функционирует несколько иначе, когда приложение находится в девелопмент-среде. Если вы посмотрите на ваше Rails-приложение снаружи, когда оно запущено на вашей локальной машине, вы увидите, что оно отправляет браузеру непосредственно все необходимые файлы - пачку css и js-файлов. Это сделано для того, чтобы облегчить процесс отлова ошибок.

Изображения

Для изображений Asset Pipeline имеет отдельную директорию в папке /assets, хотя вы можете помещать их в собственные поддиректории. Используйте image_tag, чтобы избежать проблем, например <%= image_tag "fuzzy_slippers.jpg" %>.

Препроцессоры

Помните препроцессоры, о которых мы говорили в предыдущем уроке, посвященному вьюхам? Такие типы файлов, как ERB, SASS, HAML и CoffeeScript используют препроцессоры как часть Asset Pipeline.

"Разэкранирование" HTML

Допустим, вы создаете блог и хотите иметь возможность писать посты, содержащие HTML-код. Если вы просто напишете что-то вроде это <strong>ТЕЛО</strong> моего поста и затем попытаетесь отобразить это во вьюхе, тег <strong> будет выглядеть как обычный текст... потому что он превратится в '<stong>'. Такое поведение называется "экранированием" символов.

Чтобы отобразить HTML как HTML, вам придется дать Rails понять, что этот код можно безопасно выполнить. С другой стороны, для хакера будет плевым делом внедрить в тег <script> опасный код и поместить его внутрь страницы. Это может быть довольно опасным, поскольку сработает, когда вы попробуете отобразить страницу с вредоносным кодом в браузере.

Чтобы сказать Rails, что строка безопасна для выполнения, просто используйте метод raw в своей вьюхе, например:

<%= raw "<p>hello world!</p>" %>   <!-- this will create real <p> tags -->

Если вы не хотите полагаться на стандартное поведение Rails и хотите быть абсолютно уверенными, что HTML не будет выполняться, используйте метод escapeHTML класса CGI, например:

  CGI::escapeHTML('usage: foo "bar" <baz>')
  # => "Usage: foo &quot;bar&quot; &lt;baz&gt;"

Ваше задание

Полезное и детальное объяснение принципов работы Asset Pipeline:

  1. Прочтите руководство по Asset Pipeline на RusRails с раздела 1 по раздел 3.

Заключение

Asset Pipeline - это не то, о чем вы часто будете думать, особенно, когда будете создавать маленькие пробные приложения, но важно понимать принципы работы этого элемента Rails, когда вы доберетесь до своего первого деплоя. В противном случае вы можете отказаться от использования Asset Pipeline в пользу другого инструмента, решающего подобную задачу.

Дополнительные ресурсы

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

  • Рейлскаст об Asset Pipeline от Райана Бейтса
  • Stack Overflow on Escaping HTML in Rails

Поделиться уроком: