Опубликован: 27.01.2016 | Доступ: свободный | Студентов: 914 / 58 | Длительность: 23:07:00
Лекция 4:

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

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

Хэши, по существу, это массивы которые не ограничены целочисленными индексами. (В самом деле, некоторые языки, особенно 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"}

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

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

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

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

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

>> "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 => "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> user[:name]              # Доступ к значению соответствующему :name.
=> "Michael Hartl"
>> user[:password]          # Доступ к значению неопределенного ключа.
=> nil

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

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

>> h1 = { :name => "Michael Hartl", :email => "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> h2 = { name: "Michael Hartl", email: "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> h1 == h2
=> true

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

{ name: "Michael Hartl", email: "michael@example.com" }

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

{ :name => "Michael Hartl" }

и

{ name: "Michael Hartl" }

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

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

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

Этот вид хэшей-в-хэшах, или вложенных хэшей, интенсивно используется Рельсами, как мы увидим в Разделе 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

Вновь 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 не видит разницы между знаками новой строки и прочими пробелами (в данном контексте).9Знак новой строки это то что идет в конце строки, тем самым начиная новую строку. В коде он представлен знаком \n.Причиной по которой я разбил код на части является моя привычка удерживать длину строк исходного кода в рамках 80 знаков для разборчивости.10Подсчитывание столбцов вручную может свести вас с ума, поэтому многие текстовые редакторы имеют визуальные средства которые могут помочь вам с этим. Например, если вы внимательно посмотрите на Рис. 1.1, то вы увидите маленькую вертикальную черту справа которая помогает удерживать длину строк исходного кода в пределах 80 знаков. (На самом деле она установлена на 78 столбцов, что дает вам небольшой запас на ошибку.) Если вы используете TextMate, вы можете найти эту фичу в View > Wrap Column > 78. В Sublime Text, вы можете использовать View > Ruler > 78 или View > Ruler > 80.

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

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 после их изменения на сервере.)

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

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

Вадим Обозин
Вадим Обозин

Здравствуйте, записался на курс. При этом ставил галочку на "обучаться с тьютором". На email пришло письмо, о том, что записался на самостоятельное изучение курса. Как выбрать тьютора?