Ruby on Rails Tutorial

Изучение Rails на Примерах

Майкл Хартл

Содержание

  1. Предисловие к русскому изданию
  2. Глава 1 От нуля к развертыванию
    1. 1.1 Введение
      1. 1.1.1 Комментарии для разных читателей
      2. 1.1.2 “Масштабирование” Rails
      3. 1.1.3 Соглашения в этой книге
    2. 1.2 За работу
      1. 1.2.1 Среда разработки
        1. Интегрированные Среды Разработки
        2. Текстовые редакторы и инструменты командной строки
        3. Браузеры
        4. Примечание об инструментах
      2. 1.2.2 Ruby, RubyGems, Rails и Git
        1. Rails Installer (Windows)
        2. Установка Git
        3. Установка Ruby
        4. Установка RubyGems
        5. Установка Rails
      3. 1.2.3 Первое приложение
      4. 1.2.4 Bundler
      5. 1.2.5 rails server
      6. 1.2.6 Модель-представление-контроллер (MVC)
    3. 1.3 Управление версиями с Git
      1. 1.3.1 Установка и настройка
        1. Первоначальная настройка системы
        2. Первоначальная настройка репозитория
      2. 1.3.2 Добавление и фиксация
      3. 1.3.3 Что хорошего Git делает для вас?
      4. 1.3.4 GitHub
      5. 1.3.5 Ветвление, редактирование, фиксация, объединение
        1. Ветвление
        2. Редактирование
        3. Фиксация
        4. Объединение
        5. Отправка
    4. 1.4 Развертывание
      1. 1.4.1 Установка Heroku
      2. 1.4.2 Развертывание на Heroku, шаг первый
      3. 1.4.3 Развертывание на Heroku, шаг второй
      4. 1.4.4 Команды Heroku
    5. 1.5 Заключение
  3. Глава 2 demo app
    1. 2.1 Планирование приложения
      1. 2.1.1 Моделирование пользователей
      2. 2.1.2 Моделирование микросообщений
    2. 2.2 Ресурс Users
      1. 2.2.1 Обзор пользователя
      2. 2.2.2 MVC в действии
      3. 2.2.3 Недостатки данного ресурса Users
    3. 2.3 Ресурс Microposts
      1. 2.3.1 Микрообзор микросообщений
      2. 2.3.2 Помещение micro в микросообщения
      3. 2.3.3 Пользователь has_many микросообщений
      4. 2.3.4 Иерархия наследования
      5. 2.3.5 Развертывание демонстрационного приложения
    4. 2.4 Заключение
  4. Глава 3 В основном статические страницы
    1. 3.1 Статические страницы
    2. 3.2 Наши первые тесты
      1. 3.2.1 Разработка через тестирование
      2. 3.2.2 Добавление страницы
        1. Красный
        2. Зеленый
        3. Refactor (реорганизация)
    3. 3.3 Немного динамические страницы
      1. 3.3.1 Тестирование изменения заголовка
      2. 3.3.2 Прохождение тестов заголовка
      3. 3.3.3 Встроенный Ruby
      4. 3.3.4 Устранение дублирования шаблонами
    4. 3.4 Заключение
    5. 3.5 Упражнения
    6. 3.6 Продвинутые настройки
      1. 3.6.1 Избавляемся от bundle exec
        1. RVM Bundler интеграция
        2. binstubs
      2. 3.6.2 Автоматизируем тесты с Guard
      3. 3.6.3 Ускоряем тесты с помощью Spork
        1. Guard и Spork
      4. 3.6.4 Запускаем тесты внутри Sublime Text
  5. Глава 4 Rails-приправленный Ruby
    1. 4.1 Причины
    2. 4.2 Строки и методы
      1. 4.2.1 Комментарии
      2. 4.2.2 Строки
        1. Вывод на экран
        2. Строки в одинарных кавычках
      3. 4.2.3 Объекты и передача сообщений
      4. 4.2.4 Определение методов
      5. 4.2.5 Возвращение к title хелперу
    3. 4.3 Другие структуры данных
      1. 4.3.1 Массивы и диапазоны
      2. 4.3.2 Блоки
      3. 4.3.3 Хэши и символы
      4. 4.3.4 Вновь CSS
    4. 4.4 Ruby классы
      1. 4.4.1 Конструкторы
      2. 4.4.2 Наследование классов
      3. 4.4.3 Изменение встроенных классов
      4. 4.4.4 Класс контроллер
      5. 4.4.5 Класс User
    5. 4.5 Заключение
    6. 4.6 Упражнения
  6. Глава 5 Заполнение шаблона
    1. 5.1 Добавление некоторых структур
      1. 5.1.1 Навигация по сайту
      2. 5.1.2 Bootstrap и кастомные CSS
      3. 5.1.3 Частичные шаблоны (partials)
    2. 5.2 Sass и файлопровод (asset pipeline)
      1. 5.2.1 Файлопровод
        1. Директории ассетов
        2. Файлы-манифесты
        3. Препроцессоры
        4. Производительность в продакшен
      2. 5.2.2 Синтаксически обалденные таблицы стилей
        1. Вложение
        2. Переменные
    3. 5.3 Ссылки в шаблоне
      1. 5.3.1 Тестирование маршрутов
      2. 5.3.2 Rails маршруты
      3. 5.3.3 Именованные маршруты
      4. 5.3.4 Приятный RSpec
    4. 5.4 Регистрация пользователей: Первый шаг
      1. 5.4.1 Контроллер Users
      2. 5.4.2 URL для регистрации
    5. 5.5 Заключение
    6. 5.6 Упражнения
  7. Глава 6 Моделирование пользователей
    1. 6.1 Модель User
      1. 6.1.1 Миграции базы данных
      2. 6.1.2 Файл модели
      3. 6.1.3 Создание объектов user
      4. 6.1.4 Поиск объектов user
      5. 6.1.5 Обновление объектов user
    2. 6.2 Валидации User
      1. 6.2.1 Начальные тесты для пользователей
      2. 6.2.2 Валидация наличия
      3. 6.2.3 Валидация длины
      4. 6.2.4 Валидация формата
      5. 6.2.5 Валидация уникальности
        1. Предостережение уникальности
    3. 6.3 Добавление безопасного пароля
      1. 6.3.1 Зашифрованный пароль
      2. 6.3.2 Пароль и подтверждение
      3. 6.3.3 Аутентификация пользователя
      4. 6.3.4 У пользователя есть безопасный пароль
      5. 6.3.5 Создание пользователя
    4. 6.4 Заключение
    5. 6.5 Упражнения
  8. Chapter 7 Регистрация
    1. 7.1 Демонстрация пользователей
      1. 7.1.1 Отладка и окружения Rails
      2. 7.1.2 A Ресурс Users
      3. 7.1.3 Тестирование страницы показывающей пользователя (с фабриками)
      4. 7.1.4 Изображение Gravatar и боковая панель
    2. 7.2 Форма регистрации
      1. 7.2.1 Тесты для регистрации пользователя
      2. 7.2.2 Применение form_for
      3. 7.2.3 HTML формы
    3. 7.3 Провальная регистрация
      1. 7.3.1 Рабочая форма
      2. 7.3.2 Строгие параметры
      3. 7.3.3 Сообщения об ошибках при регистрации
    4. 7.4 Успешная регистрация
      1. 7.4.1 Завершенная форма регистрации
      2. 7.4.2 Флэш
      3. 7.4.3 Первая регистрация
      4. 7.4.4 Развертывание приложения на сервере с SSL
    5. 7.5 Заключение
    6. 7.6 Упражнения
  9. Глава 8 Войти, выйти
    1. 8.1 Сессии и провальный вход
      1. 8.1.1 Sessions контроллер
      2. 8.1.2 Тестирование входа
      3. 8.1.3 Форма для входа
      4. 8.1.4 Обзор отправки формы
      5. 8.1.5 Рендеринг с флэш сообщением
    2. 8.2 Успешный вход
      1. 8.2.1 Запомнить меня
      2. 8.2.2 Рабочий метод sign_in
      3. 8.2.3 Текущий пользователь
      4. 8.2.4 Изменение ссылок шаблона
      5. 8.2.5 Вход после регистрации
      6. 8.2.6 Выход
    3. 8.3 Введение в Cucumber (опционально)
      1. 8.3.1 Установка и настройка
      2. 8.3.2 Фичи и шаги
      3. 8.3.3 Контрапункт: кастомные проверки RSpec
    4. 8.4 Заключение
    5. 8.5 Упражнения
  10. Глава 9 Обновление, демонстрация и удаление пользователей
    1. 9.1 Обновление пользователей
      1. 9.1.1 Форма для редактирования
      2. 9.1.2 Провальное редактирование
      3. 9.1.3 Успешное редактирование
    2. 9.2 Авторизация
      1. 9.2.1 Требование входа пользователей
      2. 9.2.2 Требование правильного пользователя
      3. 9.2.3 Дружелюбная переадресация
    3. 9.3 Отображение всех пользователей
      1. 9.3.1 Список пользователей
      2. 9.3.2 Образцы пользователей
      3. 9.3.3 Пагинация
      4. 9.3.4 Частичный рефакторинг
    4. 9.4 Уничтожение пользователей
      1. 9.4.1 Административные пользователи
        1. Возвращение к строгим параметрам
      2. 9.4.2 Destroy действие
    5. 9.5 Заключение
    6. 9.6 Упражнения
  11. Глава 10 Микросообщения пользователей
    1. 10.1 Модель Micropost
      1. 10.1.1 Базовая модель
      2. 10.1.2 Первая валидация
      3. 10.1.3 Ассоциации Пользователь/Микросообщения
      4. 10.1.4 Улучшение микросообщений
        1. Дефолтное пространство (scope)
        2. Dependent: destroy
      5. 10.1.5 Валидации контента
    2. 10.2 Просмотр микросообщений
      1. 10.2.1 Дополнение страницы показывающей пользователя
      2. 10.2.2 Образцы микросообщений
    3. 10.3 Манипулирование микросообщениями
      1. 10.3.1 Контроль доступа
      2. 10.3.2 Создание микросообщений
      3. 10.3.3 Предварительная реализация потока сообщений
      4. 10.3.4 Уничтожение микросообщений
    4. 10.4 Заключение
    5. 10.5 Упражнения
  12. Глава 11 Слежение за сообщениями пользователей
    1. 11.1 Модель Relationship
      1. 11.1.1 Проблема с моделью данных (и ее решение)
      2. 11.1.2 Ассоциации пользователь/взаимоотношение
      3. 11.1.3 Валидации
      4. 11.1.4 Читаемые пользователи
      5. 11.1.5 Читатели пользователя
    2. 11.2 Веб-интерфейс для читаемых пользователей
      1. 11.2.1 Образцы данных
      2. 11.2.2 Статистика и форма для слежения за сообщениями пользователя
      3. 11.2.3 Страницы с читаемыми и читателями
      4. 11.2.4 Стандартный способ реализации кнопки "читать" (follow)
      5. 11.2.5 Реализация кнопки "читать" (follow) с Ajax
    3. 11.3 Поток сообщений
      1. 11.3.1 Мотивация и стратегия
      2. 11.3.2 Первая реализация потока сообщений
      3. 11.3.3 Подзапросы
      4. 11.3.4 Новый поток сообщений
    4. 11.4 Заключение
      1. 11.4.1 Расширения к примеру приложения
        1. Реплики
        2. Обмен сообщениями
        3. Уведомления о новых читателях
        4. Напоминание пароля
        5. Подтверждение регистрации
        6. RSS канал
        7. REST API
        8. Поиск
      2. 11.4.2 Руководство по дальнейшим ресурсам
    5. 11.5 Упражнения

Предисловие

Моя компания (CD Baby) была одной из первых громко перешедших на Ruby on Rails, а затем еще громче вернувшейся обратно на PHP (Google расскажет вам об этой драме). Эту книгу, написанную Майклом Хартлом так высоко рекомендовали, что я должен был попробовать её, и Ruby on Rails Tutorial это всё, что я использовал, чтобы вернуться к Rails.

Хотя я уже прошел через много книг по Rails, это одна из немногих, что, наконец, зацепила меня. Было много написано книг типа «Путь Rails» — после которых я чувствовал себя неестественно, но после этой книги я наконец почувствовал себя естественно. Это также единственная книга по Rails, которая соблюдает методику «разработка через тестирование» на всем своем протяжении, этот подход строго рекомендуется специалистами, но он никогда не был так чётко продемонстрирован ранее. Наконец, Git, GitHub и Heroku присутствуют в демо-примерах, автор действительно дает вам почувствовать, что он хотел сделать реальный проект. Учебный код примеров не изолирован.

Линейное повествование — отличный формат. Лично я прошел Rails Tutorial в течении трёх долгих дней, делая все примеры и задачи в конце каждой главы. Делайте всё от начала до конца, не прыгая, и вы получите максимальную пользу.

Наслаждайтесь!

Derek Sivers (sivers.org)
Основатель CD Baby

Благодарности

Ruby On Rails Учебник во многом обязан моей предыдущей книге по Rails, RailsSpace и, следовательно, моему соавтору Aurelius Prochazka. Я хотел бы поблагодарить Aure как за работу, которую он проделал над прошлой книгой, так и за поддержку этой. Я также хотел бы поблагодарить Debra Williams Cauley, редактора обеих книг RailsSpace и Rails Tutorial; до тех пор, пока она не прекратит брать меня на бейсбол, я буду продолжать писать книги для нее.

Я хотел бы поблагодарить огромное количество Рубистов учивших и вдохновлявших меня на протяжении многих лет: David Heinemeier Hansson, Yehuda Katz, Carl Lerche, Jeremy Kemper, Xavier Noria, Ryan Bates, Geoffrey Grosenbach, Peter Cooper, Matt Aimonetti, Gregg Pollack, Wayne E. Seguin, Amy Hoy, Dave Chelimsky, Pat Maddox, Tom Preston-Werner, Chris Wanstrath, Chad Fowler, Josh Susser, Obie Fernandez, Ian McFarland, Steven Bristol, Pratik Naik, Sarah Mei, Sarah Allen, Wolfram Arnold, Alex Chaffee, Giles Bowkett, Evan Dorn, Long Nguyen, James Lindenbaum, Adam Wiggins, Tikhon Bernstam, Ron Evans, Wyatt Greene, Miles Forrest, хороших людей из Pivotal Labs, команду Heroku, thoughtbot ребят, и команду GitHub. Наконец, многих, многих читателей - слишком много чтобы перечислять их здесь - внёсших большое количество предложений по улучшению и сообщивших об ошибках во время написания этой книги, и я с благодарностью признаю их помощь в написании ее настолько хорошей, насколько это было возможно.

Об авторе

Майкл Хартл – автор Ruby on Rails Tutorial, лидирующего введения в веб разработку на Ruby on Rails. Его предыдущий опыт включает в себя написание и разработку RailsSpace - чрезвычайно устаревшего учебника по Rails и разработку Insoshi - некогда популярной, а ныне устаревшей платформы для социальных сетей написанной на Ruby on Rails. В 2011, Майкл получил Ruby Hero Award за его вклад в Ruby сообщество. Он закончил Harvard College, имеет степень Кандидата Физических Наук присвоенную в Caltech и является выпускником предпринимательских курсов Y Combinator.

Копирайт и лицензия

Ruby on Rails Tutorial: Learn Web Development with Rails. Copyright © 2012 by Michael Hartl. Весь исходный код в Ruby on Rails Tutorial доступен под MIT License и Beerware License.

Лицензия MIT

Copyright (c) 2013 Michael Hartl

Данная лицензия разрешает лицам, получившим копию данного программного
обеспечения и сопутствующей документации (в дальнейшем именуемыми
«Программное Обеспечение»), безвозмездно использовать Программное
Обеспечение без ограничений, включая неограниченное право на использование,
копирование, изменение, добавление, публикацию, распространение,
сублицензирование и/или продажу копий Программного Обеспечения, также
как и лицам, которым предоставляется данное Программное Обеспечение,
при соблюдении следующих условий:

Указанное выше уведомление об авторском праве и данные условия должны быть
включены во все копии или значимые части данного Программного Обеспечения.

ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО
ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ
ГАРАНТИЯМИ ТОВАРНОЙ ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И
ОТСУТСТВИЯ НАРУШЕНИЙ ПРАВ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ
НЕСУТ ОТВЕТСТВЕННОСТИ ПО ИСКАМ О ВОЗМЕЩЕНИИ УЩЕРБА, УБЫТКОВ ИЛИ ДРУГИХ
ТРЕБОВАНИЙ ПО ДЕЙСТВУЮЩИМ КОНТРАКТАМ, ДЕЛИКТАМ ИЛИ ИНОМУ, ВОЗНИКШИМ ИЗ,
ИМЕЮЩИМ ПРИЧИНОЙ ИЛИ СВЯЗАННЫМ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ ИЛИ ИСПОЛЬЗОВАНИЕМ
ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ИНЫМИ ДЕЙСТВИЯМИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.
/*
 * ----------------------------------------------------------------------------
 * "ПИВНАЯ ЛИЦЕНЗИЯ" (Ревизия 42):
 * Весь код написан Майклом Хартлом. До тех пор пока вы осознаете это,
 * вы можете делать с ним все что захотите. Если мы когда нибудь
 * встретимся, и если это того стоило, вы можете купить мне
 * пиво в ответ.
 * ----------------------------------------------------------------------------
 */

Глава 4 Rails — приправленный Ruby

Основанная на примерах из Главы 3, эта глава рассматривает некоторые элементы Ruby важные для Rails. Ruby многогранен, но, к счастью, нам нужна относительно малая его часть, чтобы быть продуктивным Rails-разработчиком. Более того, эта малая часть отличается от той, которая вам понадобится в Ruby для обычных задач, поэтому, если вашей целью является создание динамических веб-приложений, я рекомендую изучать Rails первым, собирая биты Ruby на этом пути. Чтобы стать экспертом Rails, вы должны понимать Ruby более глубоко, и эта книга дает вам хорошую основу для развития. Как отмечалось в Разделе 1.1.1, после окончания Rails Tutorial я советую почитать книги о чистом Ruby, такие как Beginning Ruby, The Well-Grounded Rubyist, or The Ruby Way.

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

4.1 Причины

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

Когда мы в последний раз видели наше новое приложение, мы только что обновили наши, в основном статические, страницы использовав Rails шаблон для устранения дублирования в наших представлениях, как показано в Листинге 4.1 (который является слегка переформатированной версией Листинга 3.26).

Листинг 4.1. Шаблон сайта Пример приложения.
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title>
    <%= stylesheet_link_tag "application", media: "all",
                                           "data-turbolinks-track" => true %>
    <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
    <%= csrf_meta_tags %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

Давайте сфокусируемся на одной строке в Листинге 4.1:

<%= stylesheet_link_tag "application", media: "all",
                                       "data-turbolinks-track" => true %>

Этот код использует встроенную Rails функцию stylesheet_link_tag (о которой вы можете узнать более подробно из Rails API) для включения application.css для всех медиа типов (включая мониторы компьютеров и принтеры). Для опытного Rails разработчика эта строка выглядит просто, но в ней есть по крайней мере четыре Руби идеи которые могут сбить с толку: встроенные Rails-методы, вызов метода без скобок, символы и хэши. Мы раскроем все эти идеи в этой главе.

Помимо того, что Rails поставляются с огромным количеством встроенных функций для использования в представлениях, они также позволяют создавать новые. Такие функции называются хелперы; для того, чтобы посмотреть как создается собственный хелпер, давайте для начала рассмотрим строку с тайтлом из Листинг 4.1:

Ruby on Rails Tutorial Sample App | <%= yield(:title) %>

Этот код опирается на определение заголовка страницы (используя provide) в каждом представлении:

<% provide(:title, 'Home') %>
<h1>Sample App</h1>
<p>
  This is the home page for the
  <a href="https://railstutorial.org/">Ruby on Rails Tutorial</a>
  sample application.
</p>

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

Ruby on Rails Tutorial Sample App |

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

Для решения проблемы отсутствующего заголовка страницы, мы определим кастомный хелпер с названием full_title. Хелпер full_title будет возвращать базовый заголовок, “Ruby on Rails Tutorial Sample App”, в случае если заголовок страницы не определен и добавлять вертикальную черту перед заголовком страницы в противном случае (Листинг 4.2).1

Листинг 4.2. Определение хелпера full_title.
app/helpers/application_helper.rb
module ApplicationHelper

  # Returns the full title on a per-page basis.
  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
end

Теперь, когда у нас есть хелпер, мы можем упростить наш шаблон заменив

<title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title>

на

<title><%= full_title(yield(:title)) %></title>

как видно в Листинге 4.3.

Листинг 4.3. Шаблон сайта Пример приложения.
app/views/layouts/application.html.erb
<!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 %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

Для того чтобы заставить наш хелпер работать, мы можем удалить ненужное слово “Home” из Home страницы, позволив ей вернуться к базовому заголовку. Мы сделаем это вначале обновив наш тест с кодом из Листинга 4.4, который обновляет предыдущий тест тайтла и добавляет один тест на отсутствие кастомной строки ’Home’ в заголовке. (Примечание: Если вы выполнили упражнение соотоветствующее Листингу 3.31, вам следует сохранить выражение let определяющее base_title в первом блоке describe.)

Листинг 4.4. Обновленные тесты для заголовка Home страницы.
spec/requests/static_pages_spec.rb
require 'spec_helper'

describe "Static pages" do

  describe "Home page" do

    it "should have the content 'Sample App'" do
      visit '/static_pages/home'
      expect(page).to have_content('Sample App')
    end

    it "should have the base title" do
      visit '/static_pages/home'
      expect(page).to have_title("Ruby on Rails Tutorial Sample App")
    end

    it "should not have a custom page title" do
      visit '/static_pages/home'
      expect(page).not_to have_title('| Home')
    end
  end
  .
  .
  .
end

Посмотрим, сможете ли вы понять почему мы добавили новый тест вместо того чтобы просто преобразовать существующий. (Подсказка: ответ кроется в Разделе 3.3.1.)

Давайте запустим набор тестов чтобы проверить что один тест провальный:

$ bundle exec rspec spec/requests/static_pages_spec.rb

Чтобы получить прохождение набора тестов мы удаляем строку provide из представления страницы как это показано в Листинге 4.5.

Листинг 4.5. Home страница без кастомного заголовка страницы.
app/views/static_pages/home.html.erb
<h1>Sample App</h1>
<p>
  This is the home page for the
  <a href="https://railstutorial.org/">Ruby on Rails Tutorial</a>
  sample application.
</p>

В этой точке тесты должны пройти:

$ bundle exec rspec spec/requests/static_pages_spec.rb

Как и со строкой для включения стилей приложения, код в Листинге 4.2 может выглядеть просто для глаз опытного Rails разработчика, но он полон новых Руби идей которые могут сбить с толку: модули, комментарии, назначение локальных переменных, булевые, контроль потока, интерполяция строк и возвращение значений. Все эти идеи тоже будут раскрыты в этой главе.

4.2 Строки и методы

Нашим основным инструментом для изучения Ruby будет Rails консоль, которая является утилитой командной строки для работы с Rails приложениями, мы впервые видели ее в Разделе 2.3.3. Сама консоль построена на интерактивном Ruby (irb), и таким образом, имеет доступ ко всей мощности языка Ruby. (Как мы увидим в Разделе 4.4.4, консоль также имеет доступ и к Rails окружению.) Запустите консоль в командной строке следующим образом:

$ rails console
Loading development environment
>>

По умолчанию консоль запускается в окружении разработки (development environment), которое является одним из трех отдельных окружений определенных в Rails (другие — тестирование и производство (test и production)). Это различие не будет иметь важного значения в этой главе, мы узнаем больше об окружениях в Разделе 7.1.1.

Консоль это замечательный инструмент обучения, и вы можете чувствовать себя свободно при ее использовании — не волнуйтесь, вы (вероятно) ничего не сломаете. При использовании консоли, нажмите Ctrl-C, если вы застряли, или Ctrl-D для выхода из консоли в целом. На протяжении оставшейся части этой главы, вы, возможно, найдете полезным консультироваться с Ruby API. Ruby API. Она упакована (возможно, даже слишком упакована) информацией, например, чтобы узнать больше о строках Ruby вы можете посмотреть в Ruby API вступление для String класса.

4.2.1 Комментарии

Ruby комментарии начинаются со знака фунт # (также называемого“знаком хэша” или, более поэтично, “октоторпом”) и распространяются до конца строки. Ruby (и, следовательно, Rails) игнорирует комментарии, но они полезны для читателей (а зачастую и для самого автора!). В коде

  # Returns the full title on a per-page basis.
  def full_title(page_title)
    .
    .
    .
  end

первая строка является комментарием с указанием цели последующего определения функции.

Обычно, вам не нужно включать комментарии в консольные сессии, но в учебных целях, я в дальнейшем буду включать некоторые комментарии, например:

$ rails console
>> 17 + 42   # Сложение целых чисел
=> 59

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

4.2.2 Строки

Строки это, вероятно, наиболее важная структура данных для веб-приложений, так как веб-страницы, в конечном счете, состоят из строк символов отправленных с сервера на браузер. Давайте начнем изучение строк с консолью, в этот раз запустив ее командой rails c, что является сокращением для rails console:

$ rails c
>> ""         # Пустая строка
=> ""
>> "foo"      # Не пустая строка
=> "foo"

Это string literals (буквальная (литеральная) строка) (также, забавно называемая текстовая строка), созданная с использованием двойной кавычки ". Консоль печатает результат вычисления каждой строки, который, в случае буквальной (литеральной) строки, и есть сама строка.

Мы можем также объединить строки + оператором:

>> "foo" + "bar"    # Конкатенация строк
=> "foobar"

Здесь результатом оценки выражения "foo" plus "bar" является строка "foobar".2

Другой способ создания строк — через интерполяцию с помощью специального синтаксиса #{}:3

>> first_name = "Michael"    # Присвоение переменной
=> "Michael"
>> "#{first_name} Hartl"     # Интерполяция строки
=> "Michael Hartl"

Здесь мы присвоили значение "Michael" переменной first_name а затем интерполировали ее в строку "#{first_name} Hartl". Мы также можем присвоить имя обеим строкам:

>> first_name = "Michael"
=> "Michael"
>> last_name = "Hartl"
=> "Hartl"
>> first_name + " " + last_name    # Конкатенация с пробелом
=> "Michael Hartl"
>> "#{first_name} #{last_name}"    # Эквивалентная интерполяция
=> "Michael Hartl"

Отметим, что последние два выражения являются эквивалентными, но я предпочитаю интерполированную версию; добавление одного пробела " " кажется мне немного неуклюжим.

Вывод на экран

Для того чтобы вывести на экран (print, напечатать) строку обычно используют Ruby функцию puts (произносится “put ess”, от “put string”):

>> puts "foo"     # вывести на экран строку
foo
=> nil

Метод puts работает с побочным эффектом: выражение puts "foo" выводит строку на экран, а затем возвращает буквально ничего: nil это особое обозначение Ruby для “вообще ничего”. (В дальнейшем, я буду иногда опускать => nil часть для простоты.)

Использование puts автоматически добавляет символ новой строки \n к выводу; связанный print метод — нет:

# выводит на экран строку (тоже что и puts, но без символа новой строки):
>> print "foo"
foo=> nil
>> print "foo\n"  # То же что и puts "foo"
foo
=> nil

Строки в одинарных кавычках

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

>> 'foo'          # Строка в одиночных кавычках
=> "foo"
>> 'foo' + 'bar'
=> "foobar"

Хотя есть важное отличие; Ruby не будет интерполировать строки в одиночных кавычках:

>> '#{foo} bar'     # Строки в одиночных кавычках не позволяют делать интерполяцию
=> "\#{foo} bar"

Обратите внимание, как консоль возвращает значения с использованием строк в двойных кавычках, которые требуют обратной косой черты, чтобы маскировать специальные символы, такие как #.

Если строки в двойных кавычках могут делать все то же, что и одиночно закавыченные, и могут интерполировать, какой смысл в одиночных кавычках? Они часто бывают полезны, потому что они действительно буквальные, и хранят в точности такие символы, как вы вводите. Например, “обратный слэш” (бэкслэш) — символ специальный в большинстве систем, например, буквальной новой строке \n. Если вы хотите чтобы переменная содержала буквально обратный слэш, в одиночных кавычках это сделать проще:

>> '\n'       # Буквальная комбинация 'бэкслэш n'
=> "\\n"

Как и с символом # в нашем предыдущем примере, Ruby необходимо маскировать обратный слэш; посредством дополнительного бэкслэша, внутри строки в двойных кавычках, буквальный бэкслэш представлен двумя бэкслэшами. Для небольшого примера, как этот, это небольшое спасение, но если есть много элементов, которые нужно маскировать, это может реально помочь:

>> 'Newlines (\n) and tabs (\t) both use the backslash character \.'
=> "Newlines (\\n) and tabs (\\t) both use the backslash character \\."

4.2.3 Объекты и передача сообщений

Все в Ruby, включая строки и даже nil, является объектом. Мы увидим технический смысл этого выражения в Разделе 4.4.2, но я не думаю, что кто-нибудь когда-нибудь понял объекты, прочитав определение в книге, вы должны создать свое интуитивное понимание объектов, видя множество примеров.

Проще описать, что объекты делают, на какие сообщения реагируют. Объект, типа строки, например, может реагировать на сообщение length, которое возвращает количество символов в строке:

>> "foobar".length        # Передача сообщения "length" строке
=> 6

Как правило, сообщения, которые передаются объектам, это методы, которые являются функциями, определенными для этих объектов.4 Строки также реагируют на empty? метод:

>> "foobar".empty?
=> false
>> "".empty?
=> true

Обратите внимание на знак вопроса в конце empty? метода. Это конвенция Ruby обозначающая, что возвращаемое значение — boolean (булево, логика): true (истина) или false (ложь). Булевые особенно полезны для управления потоком:

>> s = "foobar"
>> if s.empty?
>>   "The string is empty"
>> else
>>   "The string is nonempty"
>> end
=> "The string is nonempty"

Булевы также могут быть объединены с помощью && (“и”), || (“или”), и ! (“не”) операторов:

>> x = "foo"
=> "foo"
>> y = ""
=> ""
>> puts "Both strings are empty" if x.empty? && y.empty?
=> nil
>> puts "One of the strings is empty" if x.empty? || y.empty?
"One of the strings is empty"
=> nil
>> puts "x is not empty" if !x.empty?
"x is not empty"
=> nil

Поскольку все в Ruby является объектом, следовательно, nil тоже является объектом, поэтому он тоже может отвечать на методы. Одним из примеров является to_s метод, который может конвертировать практически любой объект в строку:

>> nil.to_s
=> ""

Это, конечно, кажется, пустой строкой, что мы можем проверить, цепочкой сообщений, передаваемых к nil:

>> nil.empty?
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.empty?
>> nil.to_s.empty?      # Сцепление сообщений
=> true

Здесь мы видим, что nil объект сам по себе не реагирует на empty? метод, но nil.to_s реагирует.

Вот специальный метод для проверки на nil-ность, о котором вы могли догадаться:

>> "foo".nil?
=> false
>> "".nil?
=> false
>> nil.nil?
=> true

Код

puts "x is not empty" if !x.empty?

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

>> string = "foobar"
>> puts "The string '#{string}' is nonempty." unless string.empty?
The string 'foobar' is nonempty.
=> nil

Стоит отметить, что nil объект уникален, тем, что это единственный объект Ruby, который является ложью в булевом контексте, кроме, непосредственно, false:

>> if nil
>>   true
>> else
>>   false        # nil является false
>> end
=> false

В частности, все другие объекты Ruby являются true, даже 0:

>> if 0
>>   true        # 0 (и все остальные кроме nil и самой false) является true
>> else
>>   false
>> end
=> true

4.2.4 Определение методов

Консоль позволяет определять методы точно так же, как мы это делали с home действием из Листинга 3.6 или full_title хелпером из Листинга 4.2. (Определение методов в консоли - немного громоздкое мероприятие, и обычно вы будете использовать файл, но это удобно для демонстрационных целей.) Например, давайте определим функцию string_message которая принимает один аргумент и возвращает сообщение в зависимости от того, пустой аргумент или нет:

>> def string_message(string)
>>   if string.empty?
>>     "It's an empty string!"
>>   else
>>     "The string is nonempty."
>>   end
>> end
=> nil
>> puts string_message("")
It's an empty string!
>> puts string_message("foobar")
The string is nonempty.

Обратите внимание, что Ruby функции имеют неявное возвращение (скрытый return) то есть, они возвращают последнее оцененное утверждение — в данном случае, одну из двух строк сообщения, в зависимости от того, пуст или нет аргумент string. Ruby также имеет явный вариант возвращения (явный return); следующие функции эквивалентны приведенным выше:

>> def string_message(string)
>>   return "It's an empty string!" if string.empty?
>>   return "The string is nonempty."
>> end

(Внимательный читатель может заметить в этой точке, что второе return здесь фактически ненужно — состояние последнего выражения в функции, т.е. строки "The string is nonempty." будет возвращено независимо от ключевого слова return, но использование return в обоих местах придает ему приятную симметрию.)

Также важно понимать что название аргумента функции не имеет значения. Другими словами, в первом примере показанном выше можно заменить string на любое валидное название переменной, такое как the_function_argument и он будет работать совершенно аналогично:

>> def string_message(the_function_argument)
>>   if the_function_argument.empty?
>>     "It's an empty string!"
>>   else
>>     "The string is nonempty."
>>   end
>> end
=> nil
>> puts string_message("")
It's an empty string!
>> puts string_message("foobar")
The string is nonempty.

4.2.5 Возвращение к title хелперу

Теперь мы в состоянии понять full_title хелпер из Листинга 4.2:5

module ApplicationHelper

  # Возвращает полный заголовок зависящий от страницы # Документирующий коментарий
  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
end

Все эти элементы — определение функции, присваивание переменной, логические тесты, управление потоком, и интерполяция строки — собраны вместе, чтобы сделать компактный вспомогательный метод для использования в шаблоне нашего сайта. Последний элемент это module ApplicationHelper: модули позволяют нам складывать вместе связанные методы, затем они (модули) могут быть подмешаны в Ruby-классы с помощью include. Если вы пишете на обычном Ruby, вы обычно пишете модули и сами их явно включаете, но в случае с хелпер-модулем Rails делает включение за нас. Результатом является то, что full_title метод автомагически доступен во всех наших представлениях.

4.3 Другие структуры данных

Хотя веб-приложения, в конечном счете это строки, фактически, для изготовления этих строк требуется также использование других структур данных. В этом разделе мы узнаем о некоторых структурах данных Ruby, важных для написания Rails приложений.

4.3.1 Массивы и диапазоны

Массив это всего лишь список элементов в определенном порядке. Мы еще не обсуждали массивы в Rails Tutorial, но их понимание дает хорошую основу для понимания хэшей (Раздел 4.3.3) и для аспектов Rails моделирования данных (таких как has_many ассоциации, которые мы видели в Разделе 2.3.3 и больше раскроем в Разделе 10.1.3).

Мы потратили много времени на понимание строк, и есть естественный способ перейти от строк к массивам, используя split метод:

>>  "foo bar     baz".split     # Разбиение строки на трех-элементный массив.
=> ["foo", "bar", "baz"]

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

>> "fooxbarxbazx".split('x')
=> ["foo", "bar", "baz"]

Как это принято в большинстве языков программирования, Ruby массивы — нулевого сдвига, что означает, что первый элемент массива имеет индекс 0, второй имеет индекс 1, и так далее:

>> a = [42, 8, 17]
=> [42, 8, 17]
>> a[0]               # Ruby использует квадратные скобки для доступа к массиву.
=> 42
>> a[1]
=> 8
>> a[2]
=> 17
>> a[-1]              # Индексы могут быть даже отрицательными!
=> 17

Мы видим здесь что Ruby использует квадратные скобки для доступа к элементам массива. В дополнение к этой скобковой записи, Ruby предлагает синонимы для некоторых часто используемых элементов:6

>> a                  # Просто напоминание о том что такое 'a'
=> [42, 8, 17]
>> a.first
=> 42
>> a.second
=> 8
>> a.last
=> 17
>> a.last == a[-1]    # Сравнение с помощью ==
=> true

Последняя строка вводит оператор проверки на равенство ==, который Ruby разделяет со многими другими языками, наряду со связанным != (“не равно”), и т.д.:

>> x = a.length       # Как и строки, массивы отвечают на метод 'length'.
=> 3
>> x == 3
=> true
>> x == 1
=> false
>> x != 1
=> true
>> x >= 1
=> true
>> x < 1
=> false

В дополнение к length (первая строка в приведенном выше примере), массивы отвечают на множество других методов:

>> a
=> [42, 8, 17]
>> a.sort
=> [8, 17, 42]
>> a.reverse
=> [17, 8, 42]
>> a.shuffle
=> [17, 42, 8]
>> a
=> [42, 8, 17]

обратите внимание на то что ни один из вышеприведенных методов не меняет саму a. Для того, чтобы изменить массив, используется соответствующие “бэнг” методы (названные так потому что восклицательный знак обычно произносится как “бэнг” в этом контексте):

>> a
=> [42, 8, 17]
>> a.sort!
=> [8, 17, 42]
>> a
=> [8, 17, 42]

Вы также можете добавлять данные в массивы с помощью push (# отправить, толкнуть) метода или эквивалентного ему оператора, <<:

>> a.push(6)                  # Отправка 6 в массив
=> [42, 8, 17, 6]
>> a << 7                     # Отправка 7 в массив
=> [42, 8, 17, 6, 7]
>> a << "foo" << "bar"        # Сцепление отправляемых в массив данных
=> [42, 8, 17, 6, 7, "foo", "bar"]

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

Прежде мы видели что split преобразовывает строку в массив. Мы также можем пойти другим путем с join методом:

>> a
=> [42, 8, 17, 7, "foo", "bar"]
>> a.join                       # Объединение без ничего.
=> "428177foobar"
>> a.join(', ')                 # Объединение через запятую.
=> "42, 8, 17, 7, foo, bar"

Тесно связаны с массивами диапазоны, которые проще всего понять посредством преобразования их в массивы, с помощью to_a метода:

>> 0..9
=> 0..9
>> 0..9.to_a              # Упс, вызвали to_a на 9.
NoMethodError: undefined method `to_a' for 9:Fixnum
>> (0..9).to_a            # Использование круглых скобок для вызова to_a на диапазоне.
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Хотя 0..9 и является допустимым диапазоном, второе выражение показывает, что нам нужно добавить скобки для вызова метода на нем.

Диапазоны полезны для вытаскивания элементов массива:

>> a = %w[foo bar baz quux]         # Применение %w для создания массива строк.
=> ["foo", "bar", "baz", "quux"]
>> a[0..2]
=> ["foo", "bar", "baz"]

Особенно полезным трюком является использование индекса -1 в конце диапазона для выборки каждого элемента от заданной точки до конца массива без необходимости явно использовать длину массива:

>> a = (0..9).to_a
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>> a[2..(a.length-1)]               # Явное использование длины массива.
=> [2, 3, 4, 5, 6, 7, 8, 9]
>> a[2..-1]                         # Использование трюка с индексом -1.
=> [2, 3, 4, 5, 6, 7, 8, 9]

Диапазоны также работают с буквами:

>> ('a'..'e').to_a
=> ["a", "b", "c", "d", "e"]

4.3.2 Блоки

И массивы и диапазоны отвечают на множество методов, которые принимают блоки, которые одновременно являются и самыми мощными и одними из самых непонятных элементов Руби:

>> (1..5).each { |i| puts 2 * i }
2
4
6
8
10
=> 1..5

Этот код вызывает each метод на диапазоне (1..5) и передает ему блок { |i| puts 2 * i }. Вертикальные линии вокруг имени переменной в |i| являются Ruby синтаксисом для блоковых переменных и это позволяет методу узнать, что делать с блоком; в данном случае диапазонный each метод может обрабатывать блок с одной локальной переменной, которую мы называли i и он просто выполняет блок для каждого значения в диапазоне.

Фигурные скобки это один из способов обозначить блок, но есть также второй способ:

>> (1..5).each do |i|
?>   puts 2 * i
>> end
2
4
6
8
10
=> 1..5

Блоки часто могут быть более чем из одной строки. В Rails Tutorial мы будем следовать общей конвенции использования фигурных скобок только для коротких однострочных блоков и использовать do..end синтаксис для длинных однострочных и многострочных блоков:

>> (1..5).each do |number|
?>   puts 2 * number
>>   puts '--'
>> end
2
--
4
--
6
--
8
--
10
--
=> 1..5

Здесь я использовал number вместо i просто чтобы подчеркнуть, что имя переменной может быть любым.

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

>> 3.times { puts "Betelgeuse!" }   # 3.times принимает блок без переменных.
"Betelgeuse!"
"Betelgeuse!"
"Betelgeuse!"
=> 3
>> (1..5).map { |i| i**2 }          # Нотация ** обозначает 'степень'.
=> [1, 4, 9, 16, 25]
>> %w[a b c]                        # Вспомните что %w создает массив строк.
=> ["a", "b", "c"]
>> %w[a b c].map { |char| char.upcase }
=> ["A", "B", "C"]
>> %w[A B C].map { |char| char.downcase }
=> ["a", "b", "c"]

Как вы можете видеть, map метод возвращает результат применения данного блока для каждого элемента в массиве или диапазоне.

Кстати, теперь мы в состоянии понять строку Ruby, которую я вбросил в Разделе 1.4.4 для генерации случайных субдоменов:

('a'..'z').to_a.shuffle[0..7].join

Давайте построим ее шаг за шагом:

>> ('a'..'z').to_a                     # Массив букв в алфавитном порядке
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
>> ('a'..'z').to_a.shuffle             # Перемешиваем его.
=> ["c", "g", "l", "k", "h", "z", "s", "i", "n", "d", "y", "u", "t", "j", "q",
"b", "r", "o", "f", "e", "w", "v", "m", "a", "x", "p"]
>> ('a'..'z').to_a.shuffle[0..7]       # Вытаскиваем первые восемь элементов.
=> ["f", "w", "i", "a", "h", "p", "c", "x"]
# Объединяем их вместе чтобы сделать одну строку.
>> ('a'..'z').to_a.shuffle[0..7].join
=> "mznpybuj"

4.3.3 Хэши и символы

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

>> user = {}                          # {} это пустой хэш.
=> {}
>> user["first_name"] = "Michael"     # Ключ "first_name", значение "Michael"
=> "Michael"
>> user["last_name"] = "Hartl"        # Ключ "last_name", значение "Hartl"
=> "Hartl"
>> user["first_name"]                 # Доступ к элементам как в массивах.
=> "Michael"
>> user                               # Буквальное представление хэша
=> {"last_name"=>"Hartl", "first_name"=>"Michael"}

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

Вместо того, чтобы определять хэши поэлементно, используя квадратные скобки, проще использовать их буквальное представление с ключами и значениями разделенными  =>, который называют “hashrocket”:

>> user = { "first_name" => "Michael", "last_name" => "Hartl" }
=> {"last_name"=>"Hartl", "first_name"=>"Michael"}

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

До сих пор мы использовали строки в качестве хэш-ключей, но в Rails гораздо чаще вместо них используются символы. Символы выглядят как строки, но с префиксом двоеточие, а не в кавычках. Например, :name это символ. Вы можете думать о символах, в основном, как о строках без дополнительного багажа:9

>> "name".split('')
=> ["n", "a", "m", "e"]
>> :name.split('')
NoMethodError: undefined method `split' for :name:Symbol
>> "foobar".reverse
=> "raboof"
>> :foobar.reverse
NoMethodError: undefined method `reverse' for :foobar:Symbol

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

В терминах символов как хэш-ключей, мы можем определить user хэш следующим образом:

>> user = { :name => "Michael Hartl", :email => "[email protected]" }
=> {:name=>"Michael Hartl", :email=>"[email protected]"}
>> user[:name]              # Доступ к значению соответствующему :name.
=> "Michael Hartl"
>> user[:password]          # Доступ к значению неопределенного ключа.
=> nil

Здесь мы видим из последнего примера, что хэш-значение для неопределенного ключа просто nil.

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

>> h1 = { :name => "Michael Hartl", :email => "[email protected]" }
=> {:name=>"Michael Hartl", :email=>"[email protected]"}
>> h2 = { name: "Michael Hartl", email: "[email protected]" }
=> {:name=>"Michael Hartl", :email=>"[email protected]"}
>> h1 == h2
=> true

Второй синтаксис заменяет комбинацию символ/hashrocket на имя за которым следует двоеточие и ключ:

{ name: "Michael Hartl", email: "[email protected]" }

Эта конструкция более близка к нотации хэшей используемой в других языках (таких как JavaScript) и наслаждается возрастающей популярностью в Rails сообществе. Поскольку оба варианта синтаксиса все еще часто используются, совершенно необходимо уметь распознавать их. К сожалению это может привести к путанице, поскольку :name является валидным сам по себе (как отдельно взятый символ) но name: сам по себе не имеет значения. Суть в том, что :name => и name: являются фактически одинаковыми только внутри литеральных хэшей, таким образом

{ :name => "Michael Hartl" }

и

{ name: "Michael Hartl" }

эквивалентны, но в противном случае вы должны использовать :name (с двоеточием впереди) для обозначения символа.

Хеш значениями может быть практически все, даже другие хэши, как видно в Листинге 4.6.

Листинг 4.6. Вложенные хэши.
>> params = {} # Определение хэша с именем 'params' (сокращение для 'parameters').
=> {}
>> params[:user] = { name: "Michael Hartl", email: "[email protected]" }
=> {:name=>"Michael Hartl", :email=>"[email protected]"}
>> params
=> {:user=>{:name=>"Michael Hartl", :email=>"[email protected]"}}
>>  params[:user][:email]
=> "[email protected]"

Этот вид хэшей-в-хэшах, или вложенных хэшей, интенсивно используется Рельсами, как мы увидим в Разделе 7.3.

Также как массивы и диапазоны, хэши реагируют на each метод. Рассмотрим, например, хэш с именем flash с ключами для двух условий :success и :error:

>> flash = { success: "It worked!", error: "It failed." }
=> {:success=>"It worked!", :error=>"It failed."}
>> flash.each do |key, value|
?>   puts "Key #{key.inspect} has value #{value.inspect}"
>> end
Key :success has value "It worked!"
Key :error has value "It failed."

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

Последний пример использует полезный inspect метод, который возвращает строку с буквальным представлением объекта на котором он был вызван:

>> puts (1..5).to_a            # Вывести массив как строку.
1
2
3
4
5
>> puts (1..5).to_a.inspect    # Вывести буквальный массив.
[1, 2, 3, 4, 5]
>> puts :name, :name.inspect
name
:name
>> puts "It worked!", "It worked!".inspect
It worked!
"It worked!"

Кстати, использование inspect для печати объекта достаточно обычное явление, для этого даже есть специальное сокращение - p функция:

>> p :name             # Тоже что и 'puts :name.inspect'
:name

4.3.4 Вновь CSS

Пришло время еще раз навестить строку из Листинга 4.1, используемую в шаблоне для включения каскадных таблиц стилей:

<%= stylesheet_link_tag "application", media: "all",
                                       "data-turbolinks-track" => true %>

Теперь мы почти в состоянии это понять. Как уже упоминалось вкратце в Разделе 4.1, Rails определяет специальную функцию для включения таблиц стилей и

stylesheet_link_tag "application", media: "all",
                                   "data-turbolinks-track" => true

это вызов этой функции. Но есть несколько вопросов. Во-первых, где скобки? В Ruby, они не являются обязательными; эти два выражения эквивалентны:

# Круглые скобки необязательны при вызове функции.
stylesheet_link_tag("application", media: "all",
                                   "data-turbolinks-track" => true)
stylesheet_link_tag "application", media: "all",
                                   "data-turbolinks-track" => true

Во-вторых, media аргумент определенно выглядит как хэш, но где фигурные скобки? Когда хэш — последний аргумент в вызове функции, фигурные скобки не являются обязательными; эти два выражения эквивалентны:

# Фигурные скобки необязательны если хэш является последним аргументом функции.
stylesheet_link_tag "application", { media: "all",
                                     "data-turbolinks-track" => true }
stylesheet_link_tag "application", media: "all",
                                   "data-turbolinks-track" => true

Далее. Почему data-turbolinks-track пара ключ/значение использует старый синтаксис хэшей? Это связано с тем что использование нового синтаксиса для написания

data-turbolinks-track: true

приведет к попытке создания символа :data-turbolinks-track, который (оказывается) невалиден из-за дефисов. Это вынуждает нас использовать старый синтаксис приводящий к

"data-turbolinks-track" => true

Наконец, почему Ruby корректно интерпретирует строки

stylesheet_link_tag "application", media: "all",
                                   "data-turbolinks-track" => true

даже с разрывом строки между конечными элементами? Ответ заключается в том что Ruby не видет разницы между знаками новой строки и прочими пробелами (в данном контексте).10 Причиной по которой я разбил код на части является моя привычка удерживать длину строк исходного кода в рамках 80 знаков для разборчивости.11

Таким образом, мы видим теперь, что строка

stylesheet_link_tag "application", media: "all",
                                   "data-turbolinks-track" => true

вызывает stylesheet_link_tag функцию с двумя аргументами: строкой, указывающей путь к таблице стилей и хэшем, с двумя элементами, указывающими медиа тип и говорящими Rails о необходимости использовать фичу turbolinks (появилась в Rails 4). (Turbolinks будут более детально описаны в следующем издании учебника.) Из-за <%= %> скобок, результаты вставляются в шаблон ERb-ом и если вы посмотрите исходный код страницы в браузере, вы должны увидеть HTML, необходимый для включении таблиц стилей (Листинг 4.7). (Вы можете увидеть некоторые дополнительные вещи вроде ?body=1, после названий файлов CSS. Они вставляются Rails для того чтобы браузеры перегружали CSS после их изменения на сервере.)

Листинг 4.7. Исходный код HTML произведенный включением CSS.
<link data-turbolinks-track="true" href="/assets/application.css" media="all"
rel="stylesheet" />

Если вы посмотрите на сам CSS файл перейдя на //localhost:3000/assets/application.css, вы увидите что он (за исключением нескольких коментариев) пуст. Мы это изменим в Главе 5.

4.4 Ruby классы

Мы говорили, что все в Ruby является объектами, и в этом разделе мы, наконец, определим несколько собственных классов. Ruby, как и многие другие объектно-ориентированные языки, использует классы чтобы организовать методы; эти классы, затем экземплируются для создания объектов. Если вы новичок в объектно-ориентированном программировании, это может звучать как бред, так что давайте рассмотрим несколько конкретных примеров.

4.4.1 Конструкторы

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

>> s = "foobar"   # Буквальный конструктор для строк использующий двойные кавычки
=> "foobar"
>> s.class
=> String

Мы видим здесь, что строки реагируют на метод class, и просто возвращают класс к которому они принадлежат.

Вместо использования буквального конструктора, можно использовать аналогичный именованный конструктор, что подразумевает вызов new метода на имени класса:12

>> s = String.new("foobar")   # Именованный конструктор для строки
=> "foobar"
>> s.class
=> String
>> s == "foobar"
=> true

Это эквивалентно буквальному конструктору, но это более четко говорит о том, что мы делаем.

Массивы в этом контексте работают так же как строки:

>> a = Array.new([1, 3, 2])
=> [1, 3, 2]

Хэши работают по другому. Если конструктор массива Array.new принимает начальное значение для массива, Hash.new принимает дефолтное значение для хэша, которое является значением хэша с несуществующим ключом:

>> h = Hash.new
=> {}
>> h[:foo]          # Попытка обратиться к значению несуществующего ключа :foo.
=> nil
>> h = Hash.new(0)  # Теперь несуществующие ключи будут возвращать 0 вместо nil.
=> {}
>> h[:foo]
=> 0

Когда метод вызывается на самом классе, как это происходит в случае с new, он называется методом класса. Результатом вызова new на классе является объект этого класса, также называемый экземпляром класса. Метод вызываемый на экземпляре, такой как length, называется методом экземпляра.

4.4.2 Наследование классов

При изучении классов, полезно выяснять иерархию классов, используя superclass метод:

>> s = String.new("foobar")
=> "foobar"
>> s.class                        # Поиск класса к которому принадлежит s.
=> String
>> s.class.superclass             # Поиск суперкласса String.
=> Object
# Ruby 1.9 использует новый базовый класс BasicObject:
>> s.class.superclass.superclass
=> BasicObject
>> s.class.superclass.superclass.superclass
=> nil

Диаграмма этой иерархии наследования представлена на Рис. 4.1. Мы видим здесь, что суперклассом String является Object , а суперкласс Object это BasicObject, но BasicObject не имеет суперкласса. Эта модель относится к каждому объекту Ruby: можно проследить иерархию классов достаточно далеко, и каждый класс в Ruby в конечном счете наследуется от BasicObject, который не имеет своего суперкласса. Это техническое значение выражения “все в Ruby является объектом”.

string_inheritance_ruby_1_9
Рис. 4.1: Иерархия наследования String класса.

Создание собственного класса незаменимо для более глубокого понимания классов. Давайте сделаем Word класс с palindrome? методом, который возвращает true, если слово можно читать и справа налево и слева направо, сохраняя смысл:

>> class Word
>>   def palindrome?(string)
>>     string == string.reverse
>>   end
>> end
=> nil

Мы можем использовать его следующим образом:

>> w = Word.new              # Создание нового Word объекта.
=> #<Word:0x22d0b20>
>> w.palindrome?("foobar")
=> false
>> w.palindrome?("level")
=> true

Если этот пример поражает вас, как немного надуманный, хорошо; это такой дизайн. Довольно странно создавать новый класс только для того чтобы создать метод принимающий строку в качестве аргумента. Поскольку слово это строка, более естественным решением будет унаследовать наш Word класс от String, как это показано в Листинге 4.8. (Вы должны выйти из консоли и ввести его заново, чтобы убрать старое определение Word.)

Листинг 4.8. Определение Word класса в консоли.
>> class Word < String             # Word наследует от String.
>>   # Возвращает true если строка соответствует своему перевертышу.
>>   def palindrome?
>>     self == self.reverse        # self это сама строка.
>>   end
>> end
=> nil

Здесь Word < String это Ruby-синтаксис для наследования (кратко обсуждается в Разделе 3.1), который гарантирует, что, в дополнение к новому palindrome? методу, words (слова) также имеют все те же методы, что и строки:

>> s = Word.new("level") # Создать новый объект Word, инициализирует его с "level".
=> "level"
>> s.palindrome?         # Слова имеют palindrome? метод.
=> true
>> s.length              # Слова также наследуют все нормальные методы строк.
=> 5

Так как Word класс наследует от String , мы можем использовать консоль, чтобы увидеть иерархию классов в явном виде:

>> s.class
=> Word
>> s.class.superclass
=> String
>> s.class.superclass.superclass
=> Object

Эта иерархия показана на Рис. 4.2.

word_inheritance_ruby_1_9
Рис. 4.2: Иерархия наследования (не встроенного) Word класса из Листинга 4.8.

В Листинге 4.8, обратите внимание, что проверка того, что слово является палиндромом включает в себя вызов слова внутри Word класса. Ruby позволяет нам сделать это, используя ключевое слово self: в Word классе, self является самим объектом, что означает, что мы можем использовать

self == self.reverse

чтобы проверить, является ли слово палиндромом.13

4.4.3 Изменение встроенных классов

Хотя наследование это мощная идея, в случае палиндромов может быть даже более естественно добавить palindrome? метод самому классу String, так чтобы (среди прочего) мы могли вызвать palindrome? на буквальную строку, что мы в настоящее время не можем сделать:

>> "level".palindrome?
NoMethodError: undefined method `palindrome?' for "level":String

Несколько удивительно, что Ruby позволяет сделать это; Ruby классы могут быть открыты и изменены простыми смертными, такими как мы, самостоятельно добавляющими методы к ним:14

>> class String
>>   # Возвращает true если строка соответствует своему перевертышу.
>>   def palindrome?
>>     self == self.reverse
>>   end
>> end
=> nil
>> "deified".palindrome?
=> true

(Я и не знаю что круче: то, что Ruby позволяет добавлять методы во встроенные классы или то, что слово "deified" является палиндромом.)

Изменение встроенных классов является мощной техникой, но с большой властью приходит большая ответственность и считается дурным тоном добавлять методы к встроенным классам, не имея действительно хорошей причины для этого. Rails имеет несколько хороших причин (чтобы не делать этого); например, в веб-приложениях вы часто не хотите, чтобы переменные были пустыми; например, имя пользователя не должно быть пустым или состоять из одних пробелов—и Rails добавляет blank? метод к Ruby. Rails консоль автоматически включает Rails расширения, и мы можем увидеть это, например, здесь (это не будет работать в простом irb (Интерактивном Руби)):

>> "".blank?
=> true
>> "      ".empty?
=> false
>> "      ".blank?
=> true
>> nil.blank?
=> true

Мы видим, что строка пробелов не является пустой, но она чистая (blank). Отметим также, что nil является чистым, так как nil не является строкой, это намек, на то, что Rails фактически добавляет blank? к базовому классу String, которым (как мы видели в начале этого раздела) является сам Object. Мы увидим некоторые другие примеры Rails дополнений в Ruby классы в Разделе 8.2.1.

4.4.4 Класс контроллер

Все эти разговоры о классах и наследованиях, возможно, вызвали вспышку узнавания, потому что мы видели их и раньше, в контроллере StaticPages (Листинг 3.16):

class StaticPagesController < ApplicationController

  def home
  end

  def help
  end

  def about
  end
end

Теперь вы в состоянии оценить, по крайней мере смутно, что этот код означает: StaticPagesController это класс, который наследует от ApplicationController и он оснащен home, help и about методами. Так как каждый сеанс Rails консоли загружает локальную среду Rails, мы можем даже создать контроллер в явном виде и изучить иерархию его классов:15

>> controller = StaticPagesController.new
=> #<StaticPagesController:0x22855d0>
>> controller.class
=> StaticPagesController
>> controller.class.superclass
=> ApplicationController
>> controller.class.superclass.superclass
=> ActionController::Base
>> controller.class.superclass.superclass.superclass
=> ActionController::Metal
>> controller.class.superclass.superclass.superclass.superclass
=> AbstractController::Base
>> controller.class.superclass.superclass.superclass.superclass.superclass
=> Object

Диаграмма этой иерархии представлена на Рис. 4.3.

static_pages_controller_inheritance
Рис. 4.3: Иерархия наследования для контроллера StaticPages.

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

>> controller.home
=> nil

Здесь возвращаемое значение это nil поскольку home действие пустое.

Но постойте: действия не имеют возвращаемых значений, по крайней мере не действия со знаком вопроса (#булевые). Целью home действия, как мы видели в Главе 3, является визуализация веб-страницы. И я бы точно не забыл, если бы когда-либо вызывал StaticPagesController.new где-нибудь. Что происходит?

Происходит вот что: Rails написан на Ruby, но Rails это не Ruby. Некоторые Rails классы используются как обычные объекты Ruby, но некоторые из них просто льют воду на Рельсовую "волшебную мельницу". Rails это sui generis и должен быть изучен и понят отдельно от Ruby. Именно поэтому, если ваш основной интерес программирования заключается в написании веб-приложений, я рекомендую изучать вначале Rails, затем изучать Ruby, затем вернуться обратно на рельсы.

4.4.5 Класс User

Мы закончим наше путешествие по Ruby комплектацией собственного класса, User класса, который ожидает User модель, которая появится в Главе 6.

До сих пор мы вводили определения классов на консоли, но это быстро стало утомительным, вместо этого создайте файл example_user.rb в корневом каталоге приложения и заполните его содержимым Листинга 4.9.

Листинг 4.9. Код для примера пользователя.
example_user.rb
class User
  attr_accessor :name, :email

  def initialize(attributes = {})
    @name  = attributes[:name]
    @email = attributes[:email]
  end

  def formatted_email
    "#{@name} <#{@email}>"
  end
end

Здесь довольно много чего происходит, так что давайте разбираться шаг за шагом. Первая строка,

  attr_accessor :name, :email

создает атрибуты доступа соответствующие имени пользователя и адресу электронной почты. Это создает “получатель” (“getter”) и “назначатель” (“setter”) методы, которые позволяют нам получать (get) и назначать (set) @name и @email переменные экземпляра, которые мы вкратце упоминали в Разделе 2.2.2. В Rails, принципиальная важность переменных экземпляра заключается в том что они автоматически доступны в представлениях, но в общем случае они используются для переменных которые должны быть доступны в Ruby классе повсеместно. (Мы вскоре расскажем об этом более подробно.) Переменные экземпляра всегда начинаются со знака @, и являются nil если они не определены.

Первый метод, initialize, специальный в Ruby: этот метод вызывается, когда мы выполняем User.new. Конкретно этот initialize принимает один аргумент, attributes:

  def initialize(attributes = {})
    @name  = attributes[:name]
    @email = attributes[:email]
  end

Здесь attributes переменная имеет значение по умолчанию равное пустому хэшу, так что мы можем определить пользователя без имени или адреса электронной почты (напомним, из Раздела 4.3.3 , что хэши возвращают nil на несуществующие ключи, поэтому attributes[:name] будет nil, если нет :name ключа, и так же для attributes[:email]).

Наконец, наш класс определяет метод, называемый formatted_email который использует значения присваиваемые @name и @email переменным для создания отформатированной версии адреса электронной почты пользователя, с помощью интерполяции строки (Раздел 4.2.2):

  def formatted_email
    "#{@name} <#{@email}>"
  end

Переменные @name и @email автоматически доступны в методе formatted_email из-за того что они являются переменными экземпляра (на что указывает знак @).

Давайте запустим консоль, и запросим (require) код примера пользователя, и покрутим наш User класс.

>> require './example_user'           # Так вы загружаете код example_user.
=> ["User"]
>> example = User.new
=> #<User:0x224ceec @email=nil, @name=nil>
>> example.name                   # nil поскольку attributes[:name] является nil
=> nil
>> example.name = "Example User"      # Назначение не-nil имени
=> "Example User"
>> example.email = "[email protected]" # и не-nil email адреса
=> "[email protected]"
>> example.formatted_email
=> "Example User <[email protected]>"

Здесь ’.’ — Unix обозначение для “текущего каталога” и ’./example_user’ говорит Ruby искать файл примера пользователя относительно этого расположения. Последующий код создает пустого пользователя в качестве примера и затем заполняет имя и адрес электронной почты, присвоением непосредственно к соответствующим атрибутам (присвоение стало возможными благодаря attr_accessor строке в Листинге 4.9). Когда мы пишем

example.name = "Example User"

Ruby создает @name переменную для "Example User" (и аналогично для email атрибута), которую мы затем используем в formatted_email методе.

Ссылаясь на Раздел 4.3.4 мы можем опустить фигурные скобки для последнего хеш аргумента, мы можем создать другого пользователя, передавая хэш initialize методу для создания пользователя с заранее определенными атрибутами:

>> user = User.new(name: "Michael Hartl", email: "[email protected]")
=> #<User:0x225167c @email="[email protected]", @name="Michael Hartl">
>> user.formatted_email
=> "Michael Hartl <[email protected]>"

Мы увидим, начиная с Главы 7 что инициализация объектов с использованием хэш аргумента является общепринятой в Rails приложениях.

4.5 Заключение

На этом мы завершаем обзор языка Ruby. В Главе 5 мы начнем применять полученные знания при разработке примера приложения.

Файл example_user.rb из Раздела 4.4.5 нам больше не пригодится, поэтому я рекомендую удалить его:

$ rm example_user.rb

Затем зафиксируйте остальные изменения в основном репозитории проекта:

$ git add .
$ git commit -m "Add a full_title helper"

4.5 Упражнения

  1. Заменяя знаки вопроса в Листинге 4.10 на соответствующие методы, скомбинируйте split, shuffle и join для того чтобы написать функцию которая тасует буквы в данной строке.
  2. Используя Листинг 4.11 как руководство, добавить shuffle метод к String классу.
  3. Создайте три хэша, назовите их person1, person2, и person3, С именем (first name) и фамилией (last name) под ключами :first и :last. Затем создайте params хэш с тем, чтобы params[:father] являлся person1, params[:mother] являлся person2, и params[:child] являлся person3 (father, mother, child это отец, мать, и ребенок соответственно). Убедитесь, что, например, params[:father][:first] имеет правильное значение.
  4. Найдите онлайн версию Ruby API и почитайте о Hash методе merge.
  5. Пройдите Ruby Koans16 для того чтобы достичь просветления в Ruby.
Листинг 4.10. Скелет для функции тасующей строку.
>> def string_shuffle(s)
>>   s.split('').?.?
>> end
=> nil
>> string_shuffle("foobar")
Листинг 4.11. Скелет для shuffle метода, принадлежащего String классу.
>> class String
>>   def shuffle
>>     self.split('').?.?
>>   end
>> end
=> nil
>> "foobar".shuffle
  1. Если хелпер специфичен для конкретного контроллера, вам следует помещать его в соответствующий файл; например, хелперы для контроллера StaticPages обычно хранятся в app/helpers/static_pages_helper.rb. В нашем случае, мы ожидаем что хелпер full_title будет использоваться на всех страницах сайта и у Rails есть специальный файл хелпера для таких случаев: app/helpers/application_helper.rb
  2. Для того чтобы узнать больше о происхождении “foo” и “bar”—и, в частности, возможной не-связанности “foobar” с “FUBAR”—см. Jargon File entry on “foo”
  3. Программисты, знакомые с Perl или PHP могут сравнить это с автоматической интерполяцией переменных со знаком доллара в выражениях вроде "foo $bar"
  4. Заранее извиняюсь за случайные переключения между функцией и методом на протяжении этой главы; в Ruby они представляют одно и то же: все методы это функции, а все функции это методы, поскольку все является объектом. 
  5. Ну, все же осталась одна вещь, которую мы не понимаем — как Rails связывает все это вместе: направляет URL к действиям, делает full_title хелпер доступным в представлениях и т.д.. Это интересная тема, и я поощряю вас исследовать ее далее, но точное знание того, как Rails работает, не является необходимым для использования Rails. (Для более глубокого понимания я рекомендую The Rails 4 Way Оби Фернандеса.) 
  6. Second метод, используемый здесь, не является в настоящий момент частью Ruby непосредственно, а скорее добавляется Rails. Это работает в данном случае, потому что консоль Rails автоматически включает Rails расширения в Ruby. 
  7. С другой стороны, искушенные программисты могут извлечь пользу из понимания, что блоки - это замыкания, объединяющие анонимные функции с внешними, по отношению к этим функциям, данными. 
  8. Ruby 1.9 фактически гарантирует, что хеши сохраняют свои элементы в том же самом порядке в каком они вводились, но было бы неблагоразумно когда-либо рассчитывать на определенный порядок. 
  9. В результате наличия меньшего количества багажа символы легче сравнить друг с другом; строки должны быть сравнены посимвольно, в то время как символы могут быть сравнены все одним разом. Это делает их идеальными для использования в качестве хеш ключей. 
  10. Знак новой строки это то что идет в конце строки, тем самым начиная новую строку. В коде он представлен знаком \n
  11. Подсчитывание столбцов вручную может свести вас с ума, поэтому многие текстовые редакторы имеют визуальные средства которые могут помочь вам с этим. Например, если вы внимательно посмотрите на Рис. 1.1, то вы увидите маленькую вертикальную черту справа которая помогает удерживать длину строк исходного кода в пределах 80 знаков. (На самом деле она установлена на 78 столбцов, что дает вам небольшой запас на ошибку.) Если вы используете TextMate, вы можете найти эту фичу в View > Wrap Column > 78. В Sublime Text, вы можете использовать View > Ruler > 78 или View > Ruler > 80
  12. Эти результаты могут изменятся, в зависимости от версии Ruby, которую вы используете. Этот пример предполагает, что вы используете Ruby 1.9.3. 
  13. Для того чтобы узнать больше о классах Ruby и ключевом слове self, см. RailsTips пост “Class and Instance Variables in Ruby”. 
  14. Для знакомых с JavaScript, эта функциональность сопоставима с использованием встроенного class prototype object для расширения класса. (Спасибо читателю Erik Eldridge за указание на это.) 
  15. Вы не должны знать, что делает каждый класс в этой иерархии. Я не знаю, что они все делают и я программирую на Ruby on Rails с 2005. Это означает или что или (a) я чрезвычайно некомпетентен или (b) можно быть квалифицированным Rails разработчиком не зная всех его внутренностей. Для нашей с вами пользы я уповаю на последнее. 
  16. http://rubykoans.com/