четверг, 11 октября 2012 г.

Ruby on Rails Tutorial 4

Глава 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, или The Ruby Way.
В этой главе рассматриваются много материала, и это нормально — не понять его весь с первого раза. Я буду часто возвращаться к нему в последующих главах.

4.1 Причины

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

4.1.1 Тitle хелпер

Когда мы в последний раз видели наше новое приложение, мы только что обновили наши, в основном статические, страницы использовав Rails шаблон для устранения дублирования в наших представлениях (Листинг 4.1).
Листинг 4.1. Шаблон сайта Пример приложения.
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Ruby on Rails Tutorial Sample App | <%= @title %></title>
    <%= csrf_meta_tag %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>
Этот шаблон хорошо работает, но есть одна его часть, которую можно немного отполировать. Напомним, что строка заголовка (тайтла)
Ruby on Rails Tutorial Sample App | <%= @title %>
опирается на определение @title в действии (actions), такое как
class PagesController < ApplicationController

  def home
    @title = "Home"
  end
  .
  .
  .
Но что, если мы не определим @title переменную? Это хорошее соглашение - иметь базовый заголовок, который мы используем на каждой странице, с дополнительным переменным заголовком (тайтлом), если мы хотим быть более конкретными. Мы уже почти достигли этого с нашей текущей схемой, с одним маленьким недостатком: как вы можете видеть, если вы удалите назначение @title в одном из действий, в отсутствие @title переменной название будет выглядеть следующим образом:
Ruby on Rails Tutorial Sample App |
Другими словами, есть подходящий базовый заголовок (тайтл), но есть также прицепленная вертикальная черта | в конце заголовка.
Одним принятым способом справиться с этим случаем, является определение helper (помощник, хелпер), который является функцией предназначенной для использования в представлениях. Давайте определим title помощник, который возвращает базовый заголовок, “Ruby on Rails Tutorial Sample App”, если @title переменная не определена и добавляет вертикальную черту перед переменным заголовком, если @title определена (Листинг 4.2).1
Листинг 4.2. Определение title помощника (хелпера).
app/helpers/application_helper.rb
module ApplicationHelper

  # Return a title on a per-page basis.
  def title
    base_title = "Ruby on Rails Tutorial Sample App"
    if @title.nil?
      base_title
    else
      "#{base_title} | #{@title}"
    end
  end
end
Этот код может выглядеть довольно просто для глаз опытного разработчика Rails, но он полон новых идей Ruby: модули, комментарии, назначение локальной переменной, булевые выражения, управление потоком, интерполяция строки и возвращение значений. Мы рассмотрим каждую из этих идей в этой главе.
Теперь у нас есть помощник (хелпер), и мы можем использовать его для упрощения нашего макета, заменив
<title>Ruby on Rails Tutorial Sample App | <%= @title %></title>
на
<title><%= title %></title>
как видно в Листинге 4.3. Обратите внимание, в частности, на переход от переменной экземпляра @title к методу помощника title (без знака @). Используя Autotest или rspec spec/, вы можете убедиться, что тесты из Главы 3 все еще проходят.
Листинг 4.3. Макет сайта Пример приложения.
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <%= csrf_meta_tag %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

4.1.2 Каскадные Таблицы Стилей (CSS)

Это второе дополнение к нашему сайту, оно покажется простым, но добавит несколько новых концепций Ruby: в том числе таблицы стилей в шаблон нашего сайта. Хотя эта книга о веб-разработке, а не о веб-дизайне, мы будем использовать каскадные таблицы стилей (CSS), чтобы придать примеру приложения некоторый минимальный стиль, и мы будем использовать фрэймворк Blueprint CSS в качестве основы для этого стиля.
Для начала, скачайте последнюю версию Blueprint CSS. (Для простоты я буду считать, что вы скачали Blueprint в папку Downloads, но вы можете использовать любую другую.) Используя командную строку либо графический инструмент, скопируйте папку Blueprint CSS blueprint в public/stylesheets - это специальный каталог, в котором Rails держит таблицы стилей. На моем Mac, команды выглядели так, но ваши команды могут отличаться:
$ cp -r ~/Downloads/joshuaclayton-blueprint-css-<version number>/blueprint \
> public/stylesheets/
Здесь cp является командой копирования Unix, и -r — флаг рекурсивного копирования (необходимый для копирования директорий). (Как уже упоминалось вкратце в Разделе 3.2.1.1, тильда ~ означает “home directory” в Unix.) Примечание: вы не должны вставлять символ > в терминал. Если вы вставите первую строку с обратным слэшем (“ \”) и нажмете Enter, вы увидите >, указывающий на продолжение строки. Вам следует вставить вторую строку и снова нажать Enter, чтобы выполнить команду. Отметим также, что вы должны будете заполнить номер версии вручную, т. к. он меняется при обновлении Blueprint. (# я просто скопировал название папки, включающее и номер версии тоже, и заменил им «joshuaclayton-blueprint-css-<номер версии>») Наконец, убедитесь, что вы не вводите
$ cp -r ~/Downloads/joshuaclayton-blueprint-css-<version number>/blueprint/ \
> public/stylesheets/
имеющее косую черту в …/blueprint/. Это вставит содержимое папки Blueprint в каталог public/stylesheets вместо перемещения всей папки.
После того как вы заполучили таблицы стилей в соответствующий каталог, Rails предоставляет помощника для включения их на наших страницах с помощью Embedded (встроенного) Ruby (Листинг 4.4).
Листинг 4.4. Добавление таблиц стилей в макет примера приложения.
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <%= csrf_meta_tag %>
    <%= stylesheet_link_tag 'blueprint/screen', :media => 'screen' %>
    <%= stylesheet_link_tag 'blueprint/print',  :media => 'print' %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>
Обратим внимание на новые строки:
<%= stylesheet_link_tag 'blueprint/screen', :media => 'screen' %>
<%= stylesheet_link_tag 'blueprint/print',  :media => 'print' %>
Они используют встроенный в Rails помощник stylesheet_link_tag, о котором вы можете прочитать больше на Rails API.2 Первая stylesheet_link_tag строка включает таблицу стилей blueprint/screen.css для экранов (например, компьютерных мониторов), а вторая включает в себя blueprint/print.css для печати. (Помощник автоматически добавляет .css расширение к именам файлов, если оно отсутствует, поэтому я опустил его для краткости). Как и с title помощником (хелпером), для опытного Rails разработчика эти строки выглядят просто, но в них есть по крайней мере четыре новых Ruby идеи: встроенные в Rails методы, вызов метода с отсутствующими скобками, символы и хэши. В этой главе мы рассмотрим эти новые идеи. (Мы увидим, HTML произведенный включением этих таблиц стилей в Листинге 4.6 Раздела 4.3.4.)
Кстати, с новыми таблицами стилей сайт не сильно изменился, но это только начало (Рис. 4.1). Мы будем строить на этой основе, начиная с Главы 5.3
home_with_stylesheet
Рисунок 4.1: Home страница с новыми Blueprint таблицами стилей. (полный размер)

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

Нашим основным инструментом для изучения Ruby будет Rails консоль, которая является утилитой командной строки для работы с Rails приложениями. Сама консоль построена на интерактивном Ruby (irb), и таким образом, имеет доступ ко всей мощности Ruby. (Как мы увидим в Разделе 4.4.4, консоль также имеет доступ к среде Rails.) Запуск консоли в командной строке происходит следующим образом:4
$ rails console
Loading development environment (Rails 3.0.9)
>> 
По умолчанию консоль запускается в окружении разработки (development environment), которое является одним из трех отдельных окружений определенных в Rails (другие — тестирование и производство (test и production)). Это различие не будет иметь важного значения в этой главе, мы узнаем больше об окружениях в Разделе 6.3.1.
Консоль это замечательный инструмент обучения, и вы можете чувствовать себя свободно при ее использовании — не волнуйтесь, вы (вероятно) ничего не сломаете. При использовании консоли, нажмите Ctrl-C, если вы застряли, или Ctrl-D для выхода из консоли в целом.
На протяжении оставшейся части этой главы, вы, возможно, найдете полезным консультироваться с Ruby API.5 Она упакована (возможно, даже слишком упакована) информацией, например, чтобы узнать больше о строках Ruby вы можете посмотреть на Ruby API вступление для String класса.

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

Ruby комментарии начинаются со знака фунт # и распространяются до конца строки. Ruby (и, следовательно, Rails) игнорирует коментарии, но они полезны для читателей (в том числе, часто, для самого автора!). В коде
  # Return a title on a per-page basis.
  def title
  .
  .
  .
первая строка является комментарием с указанием цели последующего определения функции.
Обычно, вам не нужно включать комментарии в консольные сессии, но в учебных целях, я в дальнейшем буду включать некоторые коментарии, например:
$ rails console
>> 17 + 42   # Integer addition
=> 59
Если вы, двигаясь по этому разделу, будете набирать или копипастить команды в вашу консоль, вы можете, конечно, опустить комментарии, если хотите; консоль будет игнорировать их в любом случае.

4.2.2 Строки

Строки это, вероятно, наиболее важная структура данных для веб-приложений, так как веб-страницы, в конечном счете, состоят из строк символов отправленных с сервера в браузер. Давайте начнем изучение строк с консолью, в этот раз запустив ее командой rails c, что является сокращением для rails console:
$ rails c
>> ""         # An empty string
=> ""
>> "foo"      # A nonempty string
=> "foo"
Это string literals (буквальная (литеральная) строка) (также, забавно называемая текстовая строка), созданная с использованием двойной кавычки ". Консоль печатает результат вычисления каждой строки, который, в случае буквальной (литеральной) строки, и есть сама строка.
Мы можем также объединить строки + оператором:
>> "foo" + "bar"    # String concatenation
=> "foobar"
Результат вычисления "foo" плюс "bar" это строка "foobar".6
Другой способ создания строк — через интерполяцию с помощью специального синтаксиса #{}:7
>> first_name = "Michael"    # Variable assignment
=> "Michael"
>> "#{first_name} Hartl"     # String interpolation
=> "Michael Hartl"
Здесь мы присвоили значение "Michael" переменной first_name а затем интерполировали ее в строку "#{first_name} Hartl". Мы также можем присвоить имя обеим строкам:
>> first_name = "Michael"
=> "Michael"
>> last_name = "Hartl"
=> "Hartl"
>> first_name + " " + last_name    # Concatenation, with a space in between
=> "Michael Hartl"
>> "#{first_name} #{last_name}"    # The equivalent interpolation
=> "Michael Hartl"
Отметим, что последние два выражения являются эквивалентными, но я предпочитаю интерполированную версию; добавление одного пробела " " кажется мне немного неуклюжим.

Печать

Для того, чтобы напечатать строку, наиболее часто используется Ruby функция puts (произносится как “put ess”, от “put string”):
>> puts "foo"     # put string
foo
=> nil
Puts метод работает с побочным эффектом: выражение puts "foo" выводит строку на экран, а затем возвращает буквально ничего: nil это особое обозначение Ruby для “вообще ничего”. (В дальнейшем, я буду иногда опускать => nil часть для простоты.)
Использование puts автоматически добавляет символ новой строки \n к выводу; связанный print метод — нет:
>> print "foo"    # print string (same as puts, but without the newline)
foo=> nil
>> print "foo\n"  # Same as puts "foo"
foo
=> nil

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

Все примеры до сих пор использовали строки в двойных кавычках, но Ruby также поддерживает строки в одиночных кавычках. Для многих целей, оба типа строк идентично эффективны:
>> 'foo'          # A single-quoted string
=> "foo"
>> 'foo' + 'bar'
=> "foobar"
Хотя есть важное отличие: Ruby не будет интерполировать строки в одиночных кавычках:
>> '#{foo} bar'     # Single-quoted strings don't allow interpolation
=> "\#{foo} bar"
Обратите внимание, как консоль возвращает значения с использованием строк в двойных кавычках, которые требуют обратной косой черты, чтобы маскировать специальные символы, такие как #.
Если строки в двойных кавычках могут делать все то же, что и одиночно закавыченные, и могут интерполировать, какой смысл в одиночных кавычках? Они часто бывают полезны, потому что они действительно буквальные, и хранят в точности такие символы, как вы вводите. Например, “обратный слэш” (бэкслэш) — символ специальный в большинстве систем, например, буквальной новой строке \n. Если вы хотите чтобы переменная содержала буквально обратный слэш, в одиночных кавычках это сделать проще:
>> '\n'       # A literal 'backslash n' combination
=> "\\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        # Passing the "length" message to a string
=> 6
Как правило, сообщения, которые передаются объектам, это методы, которые являются функциями, определенными для этих объектов.8 Строки также реагируют на 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"
Поскольку все в 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?      # Message chaining
=> true
Здесь мы видим, что nil объект сам по себе не реагирует на empty? метод, но nil.to_s реагирует.
Вот специальный метод для проверки на nil-ность, о котором вы могли догадаться:
>> "foo".nil?
=> false
>> "".nil?
=> false
>> nil.nil?
=> true
Если вы посмотрите на Листинг 4.2, вы увидите, что title помощник тестирует, является ли @title — nil, используя nil? метод. Это намек на то, что есть что-то особенное в переменных экземпляра (переменных с @ знаком), что может быть лучше понятым, противопоставлением их обычным переменным. Например, предположим, что мы вводим title и @title переменные в консоли не определив их вначале:
>> title        # Oops! We haven't defined a title variable.
NameError: undefined local variable or method `title' 
>> @title       # An instance variable in the console
=> nil
>> puts "There is no such instance variable." if @title.nil?
There is no such instance variable.
=> nil
>> "#{@title}"  # Interpolating @title when it's nil
=> ""
Вы можете видеть из этого примера, что Ruby жалуется, если мы попытаемся оценить неопределенные локальные переменные, но не жалуется при обращении к переменной экземпляра, вместо этого, переменная экземпляра — nil если она не определена. Это также объясняет, почему код
Ruby on Rails Tutorial Sample App | <%= @title %>
становится
Ruby on Rails Tutorial Sample App |
когда @title является nil: Embedded Ruby вставляет строку, соответствующую данной переменной, а строкой, соответствующей nil является "".
Последний пример также показывает альтернативное использование ключевого слова 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 is false
>> end
=> false
В частности, все другие объекты Ruby являются true, даже 0:
>> if 0
>>   true        # 0 (and everything other than nil and false itself) is true
>> else
>>   false
>> end
=> true

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

Консоль позволяет определять методы точно так же, как мы это делали с home действием из Листинга 3.6 или 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 в обоих местах придает ему приятную симметрию.

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

Теперь мы в состоянии понять title помощника из Листинга 4.2:9
module ApplicationHelper

  # Return a title on a per-page basis.               # Documentation comment
  def title                                           # Method definition
    base_title = "Ruby on Rails Tutorial Sample App"  # Variable assignment
    if @title.nil?                                    # Boolean test for nil
      base_title                                      # Implicit return
    else
      "#{base_title} | #{@title}"                     # String interpolation
    end
  end
end
Все эти элементы — определение функции, присваивание переменной, логические тесты, управление потоком, и интерполяция строки — собраны вместе, чтобы сделать компактный вспомогательный метод для использования в макете нашего сайта. Последним элементом является module ApplicationHelper: код в модулях Ruby может быть подмешан в Ruby классы. При написании обычных Ruby, вы часто пишете модули и сами их явно включаете, но в данном случае Rails делает включение за нас, автоматически. Результатом является то, что title метод автомагически доступен во всех наших представлениях.

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

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

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

Массив это всего лишь список элементов в определенном порядке. Мы еще не обсуждали массивы в Rails Tutorial, но их понимание дает хорошую основу для понимания хэшей (Раздел 4.3.3) и для аспектов Rails моделирования данных (таких как has_many ассоциации, которые мы видели в Разделе 2.3.3 и больше раскроем в Разделе 11.1.2).
Мы потратили много времени на понимание строк, и есть естественный способ перейти от строк к массивам, используя split метод:
>>  "foo bar     baz".split     # Split a string into a three-element array
=> ["foo", "bar", "baz"]
Результатом этой операции является массив из трех строк. По умолчанию, split делит строку на массив путем разделения по пробелу, но вы можете разделить практически по чему угодно:
>> "fooxbarxbazx".split('x')
=> ["foo", "bar", "baz"]
Как это принято в большинстве языков программирования, Ruby массивы — нулевого сдвига, что означает, что первый элемент массива имеет индекс 0, второй имеет индекс 1, и так далее:
>> a = [42, 8, 17]
=> [42, 8, 17]
>> a[0]               # Ruby uses square brackets for array access.
=> 42
>> a[1]
=> 8
>> a[2]
=> 17
>> a[-1]              # Indices can even be negative!
=> 17
Мы видим здесь, что Ruby использует квадратные скобки для доступа к элементам массива. В дополнение к этой скобковой записи, Ruby предлагает синонимы для некоторых часто используемых элементов:10
>> a                  # Just a reminder of what 'a' is
=> [42, 8, 17]
>> a.first
=> 42
>> a.second
=> 8
>> a.last
=> 17
>> a.last == a[-1]    # Comparison using ==
=> true
Последняя строка вводит оператор проверки на равенство ==, который Ruby разделяет со многими другими языками, как и связанные != (“не равно”), и т.д.:
>> x = a.length       # Like strings, arrays respond to the 'length' method.
=> 3
>> x == 3
=> true
>> x == 1
=> false
>> x != 1
=> true
>> x >= 1
=> true
>> x < 1
=> false
В дополнение к length (первая строка в приведенном выше примере), массивы отвечают на множество других методов:
>> a.sort
=> [8, 17, 42]
>> a.reverse
=> [17, 8, 42]
>> a.shuffle
=> [17, 42, 8]
Вы также можете добавлять данные в массивы с помощью 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                       # Join on nothing
=> "428177foobar"
>> a.join(', ')                 # Join on comma-space
=> "42, 8, 17, 7, foo, bar"
Тесно связаны с массивами диапазоны, которые проще всего понять посредством преобразования их в массивы, с помощью to_a метода:
>> 0..9
=> 0..9
>> 0..9.to_a              # Oops, call to_a on 9
ArgumentError: bad value for range
>> (0..9).to_a            # Use parentheses to call to_a on the range
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Хотя 0..9 и является допустимым диапазоном, второе выражение показывает, что нам нужно добавить скобки для вызова метода на нем.
Диапазоны полезны для вытаскивания элементов из массива:
>> a = %w[foo bar baz quux]         # Use %w to make a string array.
=> ["foo", "bar", "baz", "quux"]
>> a[0..2]
=> ["foo", "bar", "baz"]
Диапазоны также работают с буквами:
>> ('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 просто чтобы подчеркнуть, что имя переменной может быть любым.
Если вы существенно не владеете основами программирования, нет короткой дороги к пониманию блоков; просто их нужно много увидеть, и, в конечном итоге, вы привыкнете к ним.11 К счастью, люди довольно хороши на обобщения на основе конкретных примеров; вот еще несколько примеров, в том числе пара с использованием map метода:
>> 3.times { puts "Betelgeuse!" }   # 3.times takes a block with no variables.
"Betelgeuse!"
"Betelgeuse!"
"Betelgeuse!"
=> 3
>> (1..5).map { |i| i**2 }          # The ** notation is for 'power'.
=> [1, 4, 9, 16, 25]
>> %w[a b c]                        # Recall that %w makes string arrays.
=> ["a", "b", "c"]
>> %w[a b c].map { |char| char.upcase }
=> ["A", "B", "C"]
Как вы можете видеть, map метод возвращает результат применения данного блока для каждого элемента в массиве или диапазоне.
Кстати, теперь мы в состоянии понять строку Ruby, которую я вбросил в Разделе 1.4.4 для генерации случайных субдоменов:
('a'..'z').to_a.shuffle[0..7].join
Давайте разберем ее шаг за шагом:
>> ('a'..'z').to_a                     # An alphabet array
=> ["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             # Shuffle it.
=> ["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]       # Pull out the first eight elements.
=> ["f", "w", "i", "a", "h", "p", "c", "x"]
>> ('a'..'z').to_a.shuffle[0..7].join  # Join them together to make one string.
=> "mznpybuj"

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

Хэши, по существу, это объединение массивов: вы можете думать о хэшах в основном, как о массивах, но не ограничиваясь целочисленными индексами. (В самом деле, некоторые языки, особенно Perl, иногда называют хэши associative arrays (ассоциативными массивами) по этой причине.) Вместо этого, хэш-индексами, ключами, могут быть практически любые объекты. Например, мы можем использовать строки в качестве ключей:
>> user = {}                          # {} is an empty hash.
=> {}
>> user["first_name"] = "Michael"     # Key "first_name", value "Michael"
=> "Michael"
>> user["last_name"] = "Hartl"        # Key "last_name", value "Hartl"
=> "Hartl"
>> user["first_name"]                 # Element access is like arrays.
=> "Michael"
>> user                               # A literal representation of the hash
=> {"last_name"=>"Hartl", "first_name"=>"Michael"}
Хэши обозначаются фигурными скобками, содержащими пары ключ-значение; фигурные скобки, без пары ключ-значение, т. е. {} — это пустой хэш. Важно отметить, что фигурные скобки для хэшей не имеют ничего общего с фигурными скобками для блоков. (Да, это может привести к путанице.) Хотя хэши и напоминают массивы, одним важным отличием является то, что хэши не гарантируют сохранность их элементов в определенном порядке.12 Если порядок важен, используйте массив.
Вместо того, чтобы определять хэши попунктно, используя квадратные скобки, проще использовать их буквальное представление:
>> user = { "first_name" => "Michael", "last_name" => "Hartl" }
=> {"last_name"=>"Hartl", "first_name"=>"Michael"}
Здесь я использовал обычную конвенцию Ruby поместив дополнительные пробелы на двух концах хэша — конвенцию, игнорируемую при выводе на консоль. (Не спрашивайте меня, почему пробелы попали в конвенцию; вероятно, раньше какому-то влиятельному Программисту Ruby понравился внешний вид лишних пробелов, и они застряли в конвенции)
До сих пор мы использовали строки в качестве хэш-ключей, но в Rails гораздо чаще вместо них используются символы. Символы выглядят как строки, но с префиксом двоеточие, а не в кавычках. Например, :name это символ. Вы можете думать о символах, в основном, как о строках без дополнительного багажа:13
>> "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]              # Access the value corresponding to :name.
=> "Michael Hartl"
>> user[:password]          # Access the value of an undefined key.
=> nil
Здесь мы видим из последнего примера, что хэш-значение для неопределенного ключа просто nil.
Хеш значениями может быть практически все, даже другие хэши, как видно в Листинге 4.5.
Листинг 4.5. Вложенные хэши.
>> params = {}        # Define a hash called 'params' (short for '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"
Этот вид хэшей-в-хэшах, или вложенных хэшей, интенсивно используется в Рельсах, как мы увидим в Разделе 8.2.
Также как массивы и диапазоны, хэши реагируют на 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            # Put an array as a string.
1
2
3
4
5
>> puts (1..5).to_a.inspect    # Put a literal array.
[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             # Same as 'puts :name.inspect'
:name

4.3.4 Вновь CSS

Пришло время снова посетить строки из Листинга 4.4 используемые в макете для включения каскадных таблиц стилей:
<%= stylesheet_link_tag 'blueprint/screen', :media => 'screen' %>
<%= stylesheet_link_tag 'blueprint/print',  :media => 'print' %>
Теперь мы почти в состоянии это понять. Как уже упоминалось вкратце в Разделе 4.1.2, Rails определяет специальную функцию для включения таблиц стилей и
stylesheet_link_tag 'blueprint/screen', :media => 'screen'
это вызов этой функции. Но есть две тайны. Во-первых, где скобки? В Ruby, они не являются обязательными; эти две строки эквивалентны:
# Parentheses on function calls are optional.
stylesheet_link_tag('blueprint/screen', :media => 'screen')
stylesheet_link_tag 'blueprint/screen', :media => 'screen'
Во-вторых, :media аргумент уверенно выглядит как хэш, но где фигурные скобки? Когда хэш — последний аргумент в вызове функции, фигурные скобки не являются обязательными; эти две строки эквивалентны:
# Curly braces on final hash arguments are optional.
stylesheet_link_tag 'blueprint/screen', { :media => 'screen' }
stylesheet_link_tag 'blueprint/screen', :media => 'screen'
Таким образом, мы видим теперь, что каждая из строк
<%= stylesheet_link_tag 'blueprint/screen', :media => 'screen' %>
<%= stylesheet_link_tag 'blueprint/print',  :media => 'print' %>
вызывает stylesheet_link_tag функцию с двумя аргументами: строкой, указывающей путь к таблице стилей, и хэшем, с указанием типа носителя ( ’screen’ для экрана компьютера и ’print’ для печатной версии). Из-за <%= %> скобок, результаты вставляются в шаблон ERb-ой, и если вы посмотрите исходный код страницы в браузере, вы должны увидеть HTML, необходимый для включении таблиц стилей (Листинг 4.6).14
Листинг 4.6. Исходный код HTML произведенный включением CSS.
<link href="/stylesheets/blueprint/screen.css" media="screen" rel="stylesheet"
type="text/css" />
<link href="/stylesheets/blueprint/print.css" media="print" rel="stylesheet"
type="text/css" />

4.4 Ruby классы

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

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

Мы видели много примеров использования классов для создания экземпляра объекта, но нам еще предстоит сделать это в явном виде. Например, мы экземплировали строку с помощью двойных кавычек, которые являются буквальным конструктором для строк:
>> s = "foobar"       # A literal constructor for strings using double quotes
=> "foobar"
>> s.class
=> String
Мы видим здесь, что строки реагируют на метод class, и просто возвращают класс к которому они принадлежат.
Вместо использования буквального конструктора, можно использовать аналогичный именованный конструктор, что подразумевает вызов new метода на имени класса:15
>> s = String.new("foobar")   # A named constructor for a string
=> "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]            # Try to access the value for the nonexistent key :foo.
=> nil
>> h = Hash.new(0)    # Arrange for nonexistent keys to return 0 instead of nil.
=> {}
>> h[:foo]
=> 0

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

При изучении классов, полезно выяснять иерархию классов, используя superclass метод:
>> s = String.new("foobar")
=> "foobar"
>> s.class                        # Find the class of s.
=> String
>> s.class.superclass             # Find the superclass of String.
=> Object
>> s.class.superclass.superclass  # Ruby 1.9 uses a new BasicObject base class
=> BasicObject 
>> s.class.superclass.superclass.superclass
=> nil
Диаграмма этой иерархии наследования представлена в Рис. 4.2. Мы видим здесь, что суперклассом String является Object , а суперкласс Object это BasicObject, но BasicObject не имеет суперкласса. Эта модель относится к каждому объекту Ruby: можно проследить иерархию классов достаточно далеко, и каждый класс в Ruby в конечном счете наследуется от BasicObject, который не имеет своего суперкласса. Это техническое значение выражения “все в Ruby является объектом”.
string_inheritance_ruby_1_9
Рис. 4.2: Иерархия наследования String класса.
Создание собственного класса незаменимо для более глубокого понимания классов. Давайте сделаем Word класс с palindrome? методом, который возвращает true, если слово можно читать и справа налево и слева направо, сохраняя смысл:
>> class Word
>>   def palindrome?(string)
>>     string == string.reverse
>>   end
>> end
=> nil
Мы можем использовать его следующим образом:
>> w = Word.new              # Make a new Word object.
=> #<Word:0x22d0b20>
>> w.palindrome?("foobar")
=> false
>> w.palindrome?("level")
=> true
Если этот пример поражает вас, как немного надуманный, хорошо; это такой дизайн. Довольно странно создавать новый класс только для того чтобы создать метод принимающий строку в качестве аргумента. Поскольку слово это строка, более естественным решением будет унаследовать наш Word класс от String, как это показано в Листинге 4.7. (Вы должны выйти из консоли и ввести его заново, чтобы убрать старое определение Word.)
Листинг 4.7. Определение Word класса в консоли.
>> class Word < String             # Word inherits from String.
>>   # Return true if the string is its own reverse.
>>   def palindrome?
>>     self == self.reverse        # self is the string itself.
>>   end
>> end
=> nil
Здесь Word < String это Ruby-синтаксис для наследования (кратко обсуждается в Разделе 3.1.2), который гарантирует, что, в дополнение к новому palindrome? методу, words (слова) также имеют все те же методы, что и строки:
>> s = Word.new("level")    # Make a new Word, initialized with "level".
=> "level"                  
>> s.palindrome?            # Words have the palindrome? method.
=> true                     
>> s.length                 # Words also inherit all the normal string methods.
=> 5
Так как Word класс наследует от String , мы можем использовать консоль, чтобы увидеть иерархию классов в явном виде:
>> s.class
=> Word
>> s.class.superclass
=> String
>> s.class.superclass.superclass
=> Object
Эта иерархия показана на Рис. 4.3.
word_inheritance_ruby_1_9
Рисунок 4.3: Иерархия наследования (не встроенного) Word класса из Листинга 4.7.
В Листинге 4.7, обратите внимание, что проверка того, что слово является палиндромом включает в себя вызов слова внутри Word класса. Ruby позволяет нам сделать это, используя ключевое слово self: в Word классе, self является самим объектом, что означает, что мы можем использовать
self == self.reverse
чтобы проверить, является ли слово палиндромом.16

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

Хотя наследование это мощная идея, в случае палиндромов может быть даже более естественно добавить palindrome? метод самому классу String, так что бы (среди прочего) мы могли вызвать palindrome? на буквальную строку, что мы в настоящее время не можем сделать:
>> "level".palindrome?
NoMethodError: undefined method `palindrome?' for "level":String
Несколько удивительно, что Ruby позволяет сделать это; Ruby классы могут быть открыты и изменены, простыми смертными, такими как мы, самостоятельно добавляющими методы к ним:17
>> class String
>>   # Return true if the string is its own reverse.
>>   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 классы в Разделе 9.3.2.

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

Все эти разговоры о классах и наследованиях, возможно, вызвали вспышку узнавания, потому что мы видели их и раньше, в контроллере Pages (Листинг 3.24):
class PagesController < ApplicationController

  def home
    @title = "Home"
  end

  def contact
    @title = "Contact"
  end

  def about
    @title = "About"
  end
end
Теперь вы в состоянии оценить, по крайней мере смутно, что этот код означает: PagesController это класс, который наследует от ApplicationController и он оснащен home, contact и about методами, каждый из которых определяет переменную экземпляра @title. Так как каждый сеанс Rails консоли загружает локальную среду Rails, мы можем даже создать контроллер в явном виде и изучить иерархию его классов:18
>> controller = PagesController.new
=> #<PagesController:0x22855d0>
>> controller.class
=> PagesController
>> 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.4.
pages_controller_inheritance_rails_3
Рисунок 4.4: Иерархия наследования для контроллера Pages.
Мы можем даже вызвать действия контроллера в консоли, которые являются просто методами:
>> controller.home
=> "Home"
Это возвращение значения "Home" происходит от назначения @title = "Home" в home действии.
Но постойте: действия не имеют возвращаемых значений, по крайней мере не действия со знаком вопроса (#булевые). Целью home действия, как мы видели в Главе 3, является визуализация веб-страницы. И я бы точно не забыл, если бы когда-либо вызывал PagesController.new где-нибудь. Что происходит?
Происходит вот что: Rails написан на Ruby, но Rails это не Ruby. Некоторые Rails классы используются как обычные объекты Ruby, но некоторые из них просто льют воду на Рельсовую "волшебную мельницу". Rails это sui generis, и должен быть изучен и понят отдельно от Ruby. Именно поэтому, если ваш основной интерес программирования заключается в написании веб-приложений, я рекомендую изучать вначале Rails, затем изучать Ruby, затем вернуться обратно на рельсы.

4.4.5 Класс user

Мы закончим наше путешествие по Ruby комплектацией собственного класса, User класса, который ожидает User модель которая появится в Главе 6.
До сих пор мы вводили определения классов на консоли, но это быстро стало утомительным, вместо этого создайте файл example_user.rb в корневом каталоге Rails и заполните его содержимым Листинга 4.8. (Напомним, из Раздела 1.1.3 что корневой каталог Rails это корень вашего каталога приложения; например, корневой каталог Rails для моего примера приложения это /Users/mhartl/rails_projects/sample_app.)
Листинг 4.8. Код для примера пользователя.
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 переменные экземпляра.
Первый метод, 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
Давайте запустим консоль, и запросим (require) код примера пользователя, и покрутим наш User класс.
>> require './example_user'     # This is how you load the example_user code.
=> ["User"]
>> example = User.new
=> #<User:0x224ceec @email=nil, @name=nil>
>> example.name                 # nil since attributes[:name] is nil
=> nil
>> example.name = "Example User"           # Assign a non-nil name
=> "Example User"
>> example.email = "user@example.com"      # and a non-nil email address
=> "user@example.com"
>> example.formatted_email
=> "Example User <user@example.com>"
Здесь ’.’ — Unix обозначение для “текущего каталога”, и ’./example_user’ говорит Ruby искать файл примера пользователя относительно этого расположения. Последующий код создает пустого пользователя в качестве примера и затем заполняет имя и адрес электронной почты, присвоением непосредственно к соответствующим атрибутам (присвоение стало возможными благодаря attr_accessor строке в Листинге 4.8). Когда мы пишем
example.name = "Example User"
Ruby создает @name переменную для "Example User" (и аналогично для email атрибута), которую мы затем используем в formatted_email методе.
Ссылаясь на Раздел 4.3.4 мы можем опустить фигурные скобки для последнего хеш аргумента, мы можем создать другого пользователя, передавая хэш initialize методу для создания пользователя с заранее определенными атрибутами:
>> user = User.new(:name => "Michael Hartl", :email => "mhartl@example.com")
=> #<User:0x225167c @email="mhartl@example.com", @name="Michael Hartl">
>> user.formatted_email
=> "Michael Hartl <mhartl@example.com>"
Мы увидим, начиная с Главы 8 что инициализация объектов с использованием хэш аргумента является общепринятой в Rails приложениях.

4.5 Упражнения

  1. Используя Листинг 4.9 как руководство, скомбинировать split, shuffle, и join чтобы написать функцию, которая тасует буквы в данной строке.
  2. Используя Листинг 4.10 как руководство, добавить 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.
Листинг 4.9. Скелет для функции string shuffle.
>> def string_shuffle(s)
>>   s.split('').?.?
>> end
=> nil
>> string_shuffle("foobar")
Листинг 4.10. Скелет для shuffle метода, принадлежащего String классу.
>> class String
>>   def shuffle
>>     self.split('').?.?
>>   end
>> end
=> nil
>> "foobar".shuffle
  1. Если помощник определен для конкретного контроллера, следует поместить его в соответствующий файл помощника; например, помощники для контроллера Pages обычно входят в app/helpers/pages_helper.rb. В нашем случае мы ожидаем, что помощник заголовка будет использоваться на страницах всего сайта, и у Rails есть специальный файл помощника для этого случая: app/helpers/application_helper.rb
  2. Я не даю ссылки на API, потому что у них есть тенденция быстро устаревать. Давайте Google будет вашей путеводной звездой. Кстати, “API” означает “Application Programming Interface (Интерфейс прикладного программирования)”. 
  3. Если вы нетерпеливы, не стесняйтесь проверить Blueprint CSS wiki
  4. Вспомните, что консольное приглашение, вероятно, будет чем-то вроде ruby-1.9.2-head >, но примеры используют >> так как версии Ruby изменяются. 
  5. Как и с Rails API, ссылки Ruby API выходят из моды, хотя и не так быстро. Google по-прежнему всегда готов вам помочь 
  6. Чтобы узнать больше о происхождении “foo” и “bar”—и, в частности возможном не отношении “foobar” к “FUBAR”—см. “foo” в Jargon File. . 
  7. Програмисты, знакомые с Perl или PHP, должны сравнить это с автоматической интерполяцией переменных знака доллара в таких выражениях, как "foo $bar"
  8. Заранее извиняюсь за случайные переключения между функцией и методом повсюду в этой главе; в Ruby они — одно и то же: все методы — функции, и все функции — методы, потому что все — объект. 
  9. Ну, все же осталась одна вещь, которую мы не понимаем — как Rails связывает все это вместе: направление URL к действиям, делание title помощника доступным в представлениях, и т.д. Это интересная тема, и я поощряю вас исследовать ее далее, но точное знание того, как Rails работает, не является необходимым для использования Rails. (Для более глубокого понимания я рекомендую The Rails 3 Way Оби Фернандеса.) 
  10. Second метод, используемый здесь, не является в настоящий момент частью Ruby непосредственно, а скорее добавляется Rails. Это работает в данном случае, потому что консоль Rails автоматически включает Rails расширения в Ruby. 
  11. С другой стороны, искушенные программисты могут извлечь пользу из понимания, что блоки - это замыкания, объединяющие анонимные функции с внешними, по отношению к этим функциям, данными. 
  12. Ruby 1.9 фактически гарантирует, что хеши сохраняют свои элементы в том же самом порядке в каком они вводились, но было бы неблагоразумно когда-либо рассчитывать на определенный порядок. 
  13. В результате наличия меньшего количества багажа символы легче сравнить друг с другом; строки должны быть сравнены посимвольно, в то время как символы могут быть сравнены все одним разом. Это делает их идеальными для использования в качестве хеш ключей. 
  14. Можно увидеть немного пугающие числа, такие как ?1257465942, после имен файла CSS. Они вставляются Rails, чтобы гарантировать, что браузеры перезагружают CSS, когда они изменяются на сервере. 
  15. Эти результаты могут изменятся, в зависимости от версии Ruby, которую вы используете. Этот пример предполагает, что вы используете Ruby 1.9.2. 
  16. Для того чтобы узнать больше о классах Ruby и ключевом слове self, см. RailsTips пост “Class and Instance Variables in Ruby”. 
  17. Для знакомых с JavaScript, эта функциональность сопоставима с использованием встроенного class prototype object для расширения класса. (Спасибо читателю Erik Eldridge за указание на это.) 
  18. Вы не должны знать, что делает каждый класс в этой иерархии. Я не знаю, что они все делают, и я программирую в Ruby on Rails с 2005. Это означает или что (a), я чрезвычайно некомпетентен или (b), можно быть квалифицированным Rails разработчиком, не зная все его внутренности. Для нашей с вами пользы я уповаю на последнее. 

Комментариев нет:

Отправить комментарий