Ruby on Rails Tutorial
Изучение Веб Разработки на Rails
Michael Hartl
Содержание
- Предисловие к русскому изданию
- Глава 1 От нуля к развертыванию
- Глава 2 demo app
- Глава 3 В основном статические страницы
- Глава 4 Rails-приправленный Ruby
- Глава 5 Заполнение шаблона
- Глава 6 Моделирование пользователей
- Глава 7 Регистрация
- Глава 8 Войти, выйти
- Глава 9 Обновление, демонстрация и удаление пользователей
- Глава 10 Микросообщения пользователей
- Глава 11 Слежение за сообщениями пользователей
Foreword
Моя компания (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
В настоящее время: основатель Thoughts Ltd.
Благодарности
Ruby On Rails Учебник во многом обязан моей предыдущей книге по Rails, RailsSpace и, следовательно, моему соавтору Aurelius Prochazka. Я хотел бы поблагодарить Aure как за работу, которую он проделал над прошлой книгой, так и за поддержку этой. Я также хотел бы поблагодарить Debra Williams Cauley, редактора обеих книг RailsSpace и Rails Tutorial; до тех пор, пока она не прекратит брать меня на бейсбол, я буду продолжать писать книги для нее.
Я хотел бы поблагодарить огромное количество Рубистов Rubyists учивших и вдохновлявших меня на протяжении многих лет: 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) 2012 Michael Hartl
Данная лицензия разрешает лицам, получившим копию данного программного
обеспечения и сопутствующей документации (в дальнейшем именуемыми
«Программное Обеспечение»), безвозмездно использовать Программное
Обеспечение без ограничений, включая неограниченное право на использование,
копирование, изменение, добавление, публикацию, распространение,
сублицензирование и/или продажу копий Программного Обеспечения, также
как и лицам, которым предоставляется данное Программное Обеспечение,
при соблюдении следующих условий:
Указанное выше уведомление об авторском праве и данные условия должны быть
включены во все копии или значимые части данного Программного Обеспечения.
ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО
ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ
ГАРАНТИЯМИ ТОВАРНОЙ ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И
ОТСУТСТВИЯ НАРУШЕНИЙ ПРАВ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ
НЕСУТ ОТВЕТСТВЕННОСТИ ПО ИСКАМ О ВОЗМЕЩЕНИИ УЩЕРБА, УБЫТКОВ ИЛИ ДРУГИХ
ТРЕБОВАНИЙ ПО ДЕЙСТВУЮЩИМ КОНТРАКТАМ, ДЕЛИКТАМ ИЛИ ИНОМУ, ВОЗНИКШИМ ИЗ,
ИМЕЮЩИМ ПРИЧИНОЙ ИЛИ СВЯЗАННЫМ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ ИЛИ ИСПОЛЬЗОВАНИЕМ
ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ИНЫМИ ДЕЙСТВИЯМИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.
/*
* ----------------------------------------------------------------------------
* "ПИВНАЯ ЛИЦЕНЗИЯ" (Ревизия 42):
* Весь код написан Майклом Хартлом. До тех пор пока вы осознаете это,
* вы можете делать с ним все что захотите. Если мы когда нибудь
* встретимся, и если это того стоило, вы можете купить мне
* пиво в ответ.
* ----------------------------------------------------------------------------
*/
Глава 2 demo app
В этой главе мы разработаем простое демонстрационное приложение, чтобы показать мощь Rails. Цель - получить общее представление о программировании на Ruby on Rails (и веб-разработке в целом), быстро сгенерировав приложение, используя scaffold generators. Как обсуждалось в Блоке 1.1, остальная часть книги будет применять противоположный подход, постепенно разрабатывая приложение с объяснением всех новых понятий по мере их появления, но для быстрого обзора (и некоторого быстрорастворимого удовлетворения) нет ничего лучше чем scaffolding. Конечное демонстрационное приложение позволит нам взаимодействовать с ним через свои URI, дав нам понимание структуры Rails приложений, включая первый пример REST архитектуры предпочитаемой Rails.
Как и предстоящий пример приложения, демонстрационное приложение будет состоять из пользователей и связанных с ними микросообщений (образуя минималистичное приложение в стиле Twitter). Функциональность будет совершенно слаборазвита и многие из шагов будут походить на волшебство, но не переживайте: полный пример приложения разработает подобное же приложение с нуля, начиная с Главы 3, и я дам многочисленные ссылки на более поздний материал. Тем временем, имейте терпение и немного веры - все таки основная цель данного учебника - удержать вас вне этого поверхностного, управляемого scaffold подхода, для достижения более глубокого понимания Rails.
2.1 Планирование приложения
В этом Разделе мы обрисуем в общих чертах наши планы относительно демонстрационного приложения. Как и в Разделе 1.2.3, мы начнем с создания скелета приложения, используя команду rails
:
$ cd ~/rails_projects
$ rails new demo_app
$ cd demo_app
Затем, мы используем текстовый редактор, чтобы обновить Gemfile
необходимый Bundler, содержанием из Листинга 2.1.
Gemfile
для демонстрационного приложения.
source 'https://rubygems.org'
gem 'rails', '3.2.13'
group :development do
gem 'sqlite3', '1.3.5'
end
# Gems used only for assets and not required
# in production environments by default.
group :assets do
gem 'sass-rails', '3.2.5'
gem 'coffee-rails', '3.2.2'
gem 'uglifier', '1.2.3'
end
gem 'jquery-rails', '2.0.2'
group :production do
gem 'pg', '0.12.2'
end
Отмечу, что Листинг 2.1 аналогичен Листингу 1.9
Как и в Разделе 1.4.1, мы установим локальные гемы и предотвратим установку production гемов с помощью опции --without production:
$ bundle update
$ bundle install --without production
Наконец, мы поместим наш код в систему контроля версий. Вспомним что rails
команда генерирует дефолтный .gitignore
файл, но, в зависимости от вашей системы, вы можете найти расширенный файл из Листинга 1.7 гораздо более удобным. Затем инициализируем Git репозиторий и делаем первый коммит:
$ git init
$ git add .
$ git commit -m "Initial commit"
Можно также дополнительно создать новый репозиторий (Рис. 2.1) и отправить его на GitHub:
$ git remote add origin [email protected]:<username>/demo_app.git
$ git push -u origin master
(Как и в случае с первым приложением, позаботьтесь о том чтобы не инициализировать репозиторий GitHub с файлом README
.)
Теперь мы готовы начать делать само приложение. Типичный первый шаг, при разработке приложений это создание модели данных, которая является изображением структур, необходимых нашему приложению. В нашем случае demo app будет упрощенным микроблогом: только пользователи и короткие (микро) сообщения. Таким образом, мы начнем с модели для пользователей приложения (Раздел 2.1.1), затем мы добавим модель для микросообщений (Раздел 2.1.2).
2.1.1 Моделирование пользователей
Вариантов для модели данных пользователя так же много, как много различных форм регистрации в сети; мы пойдем отчетливо минималистичным путем. У пользователей нашего demo app будет уникальный целочисленный
идентификатор называемый id
, публично видимое имя
(тип - string (строка)
), и email
адрес (также string
) совпадающий с именем пользователя. Итоговая модель данных для пользователей представлена на Рис. 2.2.
Как мы увидим в Разделе 6.1.1, табличка users
в Рис. 2.2 соответствует таблице в базе данных и id
, name
, и email
атрибуты это столбцы этой таблицы.
2.1.2 Моделирование микросообщений
Ядро модели данных микросообщений даже проще чем для пользователей: микросообщение имеет только id
и поле content
для текста микросообщения (тип- string
).1 Есть дополнительная сложность: мы хотим связать каждое микросообщение с определенным пользователем; мы выполним это, записывая user_id
владельца сообщения. Результаты показаны на Рис. 2.3.
Мы увидим в Разделе 2.3.3 (и более полно в Главе 10) как этот user_id
атрибут позволит нам кратко реализовать идею, что у пользователя потенциально есть много связанных с ним микросообщений.
2.2 Ресурс Users
В этом разделе мы реализуем модель данных пользователей из Раздела 2.1.1, наряду с веб-интерфейсом к этой модели. Эта комбинация образует ресурс Users, который позволит нам думать о пользователях как об объектах, которые могут быть созданы, считаны, обновлены, и удалены через сеть с использованием HTTP протокола. Как обещалось во введении, наш ресурс Users будет создаваться программой scaffold generator, которая стандартно поставляется с каждым проектом Rails. Я настоятельно прошу вас не всматриваться в генерируемый код; на этом этапе он лишь запутает вас.
Rails scaffolding запускается передачей scaffold
команды скрипту rails generate
. Аргумент команды scaffold
это имя ресурса в единственном числе (в данном случае, User
), вместе с дополнительными параметрами для атрибутов модели данных:2
$ rails generate scaffold User name:string email:string
invoke active_record
create db/migrate/20111123225336_create_users.rb
create app/models/user.rb
invoke test_unit
create test/unit/user_test.rb
create test/fixtures/users.yml
route resources :users
invoke scaffold_controller
create app/controllers/users_controller.rb
invoke erb
create app/views/users
create app/views/users/index.html.erb
create app/views/users/edit.html.erb
create app/views/users/show.html.erb
create app/views/users/new.html.erb
create app/views/users/_form.html.erb
invoke test_unit
create test/functional/users_controller_test.rb
invoke helper
create app/helpers/users_helper.rb
invoke test_unit
create test/unit/helpers/users_helper_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/users.js.coffee
invoke scss
create app/assets/stylesheets/users.css.scss
invoke scss
create app/assets/stylesheets/scaffolds.css.scss
Добавив name:string
и email:string
, мы добились того, что модель User приобрела форму соответствующую Рис. 2.2. (Отметьте, что нет надобности включать параметр для id
; он создается Rails автоматически для использования в качестве primary key в базе данных.)
Чтобы продолжить с демонстрационным приложением, мы сначала должны migrate (мигрировать, переместить) базу данных, используя Rake (Блок 2.1):
$ bundle exec rake db:migrate
== CreateUsers: migrating ====================================================
-- create_table(:users)
-> 0.0017s
== CreateUsers: migrated (0.0018s) ===========================================
Это просто обновляет базу данных с нашей новой моделью данных users
. (Мы узнаем больше о миграциях баз данных в Разделе 6.1.1.) Отметьте, что для того, чтобы обеспечить использование версии Rake, соответствующей нашему Gemfile
, нам необходимо запустить rake
используя bundle exec
.
Теперь мы можем запустить локальный веб-сервер, используя rails s
, что является сокращением для rails server
:
$ rails s
При этом демонстрационное приложение уже должно быть готово по адресу //localhost:3000/.
В традиции Unix, make утилита играет важную роль в сборке исполняемых программ из исходного кода; множество компьютерных хакеров зафиксировали в мышечной памяти строку
$ ./configure && make && sudo make install
обычно используемую, для компиляции кода на Unix системах (включая Linux и Mac OS X).
Rake это Ruby make, make-подобный язык написанный на Ruby. Rails использует Rake, в значительной степени, специально для неисчислимых небольших административных задач, необходимых при разработке поддерживаемых базой данных веб-приложений. rake db:migrate
команда, вероятно, наиболее распространена, но есть и многие другие; можно увидеть список задач базы данных, используя -T db
:
$ bundle exec rake -T db
Чтобы увидеть все доступные Rake задачи, запустите
$ bundle exec rake -T
Список, вероятно, будет подавляющим, но не волнуйтесь, вы не должны знать все (или даже большую часть) этих команд. К концу Учебника Rails, вы будете знать все самые важные.
2.2.1 Обзор пользователя
При посещении корневого url //localhost:3000/ мы видим ту же самую дефолтную страницу Rails, что и на Рис. 1.3, но при генерировании ресурса Users scaffolding-ом мы также создали большое количество страниц для управления пользователями. Например, страницу для отображения списка всех пользователей на /users и страницу для создания нового пользователя на /users/new. Остальная часть этого раздела, выделяется предпринятию ураганного тура через эти страницы пользователя. В процессе этого тура для вас может оказаться полезным иногда поглядывать на Таблицу 2.1, которая показывает соответствие между страницами их URI.
URI | Действие | Назначение |
---|---|---|
/users | index | страница, для отображения списка всех пользователей |
/users/1 | show | страница показывающая пользователя с id 1 |
/users/new | new | страница для создания нового пользователя |
/users/1/edit | edit | страница для редактирования пользователя с id 1 |
Мы начнем со страницы показывающей всех пользователей в нашем приложении, называемой index; как вы могли бы ожидать, изначально там нет никаких пользователей (Рис. 2.4).
Для того, чтобы создать нового пользователя мы посещаем new страницу, как показано на Рис. 2.5. (Поскольку //localhost:3000 часть адреса явно будет повторятся до тех пор пока мы разрабатываем локально, я буду обычно опускать ее с этого момента.) В Главе 7 это станет страницей регистрации пользователя.
Мы можем создать пользователя, введя значения имени и адреса электронной почты в текстовые поля и нажав затем кнопку Create User. Результат - страница show как видно на Рис. 2.6. (Зеленое приглашающее сообщение выполняется с использованием флэш, о котором мы узнаем в Разделе 7.4.2.) Отметим, что в URI /users/1; как вы могли бы подозревать, цифра 1
это просто атрибут id
пользователя из Рис. 2.2. В Разделе 7.1эта страница станет профилем пользователя.
Для того, чтобы изменить информацию пользователя, мы посещаем страницу edit (Рис. 2.7). Изменяя информацию о пользователе и нажимая кнопку Update User, мы изменяем информацию о пользователе в демонстрационном приложении (Рис. 2.8). (Как мы увидим в Главе 6, эти данные пользователя хранятся в бэкэнде базы данных.) Мы добавим функции edit/update (редактировать/обновить) пользователя к примеру приложения в Разделе 9.1.
Теперь мы создадим второго пользователя, повторно посетив new страницу и предоставив второй набор информации о пользователе; получившаяся страница index показана на Рис. 2.9. Раздел 7.1 преобразует список пользователей в более отполированную страницу для демонстрации всех пользователей.
Показав как создавать, отображать и редактировать пользователей, мы переходим, наконец, к их удалению (Рис. 2.10). Следует проверить, что клик по ссылке показанной на Рис. 2.10 уничтожает второго пользователя, приводя к index странице с одним пользователем. (Если это не работает, убедитесь, что JavaScript включен в вашем браузере; Rails использует JavaScript, чтобы выдать запрос, который должен уничтожить пользователя.) Раздел 9.4 добавляет удаление пользователя к демонстрационному приложению, заботясь об ограничении его использования специальным классом административных пользователей.
2.2.2 MVC в действии
Теперь, когда мы завершили быстрый обзор ресурса Пользователи (Users), давайте исследуем одну его определенную часть в контексте паттерна Модель-Представление-Контроллер (MVC) частично представленного в Разделе 1.2.6. Наша стратегия будет состоять в том, чтобы описать результаты типичного запроса браузера: посещение index страницы пользователя на /users—в терминах MVC (Рис. 2.11).
- Браузер выдает запрос на URI /users.
- Rails направляет /users к
index
действию в контроллере Users. Index
действие просит, чтобы модель User получила всех пользователей (User.all
).- Модель User вытягивает всех пользователей из базы данных.
- Модель User возвращает список пользователей в контроллер.
- Контроллер получает пользователей в
@users
переменной, которую он передаетindex
представлению. - Представление использует Embedded (Встроенный) Ruby, чтобы визуализировать страницу как HTML.
- Контроллер возвращает HTML в браузер.3
Мы начинаем с запроса, выданного браузером — то есть, с результата ввода URI в адресной строке или клика по ссылке (Шаг 1 в Рис. 2.11). Этот запрос вызывает Rails маршрутизатор (Шаг 2), который отправляет к надлежащему, действию контроллера, основываясь на URI (и, как мы увидим в Блоке 3.1, на типе запроса). Код создающий перенаправление URI пользователя к действиям контроллера для ресурса Users представлен в Листинге 2.2; этот код фактически устанавливает таблицу пар URI/Действие, которые мы видели в Таблице 2.1. (Странная нотация :users
это symbol, о котором мы узнаем в Разделе 4.3.3.)
config/routes.rb
DemoApp::Application.routes.draw do
resources :users
.
.
.
end
Страницы из Раздела 2.2.1 соответствуют действиям в контроллере Users, который является набором связанных действий; контроллер, сгенерированный scaffolding, схематично показан в Листинге 2.3. Отметьте запись class UsersController < ApplicationController
; это пример Ruby класса с наследованием. (Мы вкратце обсудим наследование в Разделе 2.3.4 и раскроем обе темы более подробно в Разделе 4.4.)
app/controllers/users_controller.rb
class UsersController < ApplicationController
def index
.
.
.
end
def show
.
.
.
end
def new
.
.
.
end
def create
.
.
.
end
def edit
.
.
.
end
def update
.
.
.
end
def destroy
.
.
.
end
end
Вы могли заметить, что действий больше чем страниц; index
, show
, new
, и edit
действия - все они соответствуют страницам из Раздела 2.2.1, но есть также дополнительные create
(создать), update
(обновить), и destroy
(разрушить) действия. Эти действия обычно не визуализируют страниц (хотя иногда могут); вместо этого, их основная цель состоит в том, чтобы изменять информацию о пользователях в базе данных. Этот полный комплект действий контроллера, сведенный в Таблицу 2.2, представляет реализацию архитектуры REST в Rails (Блок 2.2), которая опирается на идеи передачи состояния представления идентифицированные и названые ученым Roy Fielding.4 Отметьте в Таблице 2.2 что есть некоторое наложение в URIs; например, и show
и update
действиям соответствует URI /users/1. Различие между ними в методе запроса HTTP на который они отвечают. Мы узнаем больше о методах запроса HTTP в Разделе 3.2.1.
HTTP запрос | URI | Действие | Назначение |
---|---|---|---|
GET | /users | index | страница со списком всех пользователей |
GET | /users/1 | show | страница показывающая пользователя с id 1 |
GET | /users/new | new | страница для создания нового пользователя |
POST | /users | create | создание нового пользователя |
GET | /users/1/edit | edit | страница для редактирования пользователя с id 1 |
PUT | /users/1 | update | обновление пользователя с id 1 |
DELETE | /users/1 | destroy | удаление пользователя с id 1 |
Если вы много читали о веб-разработке на Ruby on Rails, вы видели много ссылок на “REST”, являющуюся акронимом для REpresentational State Transfer («передача состояния представления»). REST - архитектурный стиль для разработки распределенных, сетевых систем и приложений, таких как World Wide Web и веб-приложения. Хотя теория REST довольно абстрактна, в контексте Rails приложений REST означает, что большинство компонентов приложения (таких как пользователи и микросообщения) моделируются как ресурсы, которые могут быть созданы (Сreated), прочитаны (Read), обновлены (Updated), и удалены (Deleted) - операции, которые соответствуют и операциям CRUD, реляционных баз данных и четырем фундаментальным методам запроса HTTP: POST, GET, PUT, и DELETE. (Мы узнаем больше о запросах HTTP в Разделе 3.2.1 и особенно в Блоке 3.1.)
Как разработчику Rails приложений, RESTful стиль разработки помогает вам определиться какие контроллеры и действия писать: вы просто структурируете приложение, используя ресурсы, которые создаются, читаются, обновляются, и удаляются. В случае пользователей и микросообщений, этот процесс является простым, так как они - натуральные ресурсы по определению. В Главе 11мы увидим пример, где принципы REST позволяют нам моделировать более тонкую проблему “Слежения за сообщениями пользователей” естественным и удобным способом.
Чтобы исследовать отношения между контроллером Users и моделью User, давайте сосредоточимся на упрощенной версии index
действия, показанной в Листинге 2.4. (Код scaffold уродливый и запутанный, так что я опустил большую его часть.)
app/controllers/users_controller.rb
class UsersController < ApplicationController
def index
@users = User.all
end
.
.
.
end
У этого index
действия есть строка @users = User.all
(Шаг 3), которая просит, чтобы модель User получила список всех пользователей из базы данных (Шаг 4), и затем помещает их в переменную @users
(произносится “at-users”) (Шаг 5). Сама модель User появляется в Листинге 2.5; хотя она довольно проста, к ней прилагается большое количество функций из-за наследования (Раздел 2.3.4 и Раздел 4.4). В частности, за счет использования Rails библиотеки Active Record, код в Листинге 2.5 заставляет User.all
возвратить всех пользователей. (Мы узнаем о строке attr_accessible
в Разделе 6.1.2.2. Примечание: Эта строка не появляется если вы ипользуете Rails 3.2.2 или более старые версии.)
app/models/user.rb
class User < ActiveRecord::Base
attr_accessible :email, :name
end
Как только @users
переменная определена, контроллер вызывает представление (Шаг 6), показанное в Листинге 2.6. Переменные, которые начинаются со знака @
, называемые переменными экземпляра, автоматически доступны в представлении; в данном случае, представление index.html.erb
в Листинге 2.6 перебирает список @users
и выводит строку HTML для каждого. (Помните, вы не должны пытаться понять этот код прямо сейчас. Он показан только для иллюстрации.)
app/views/users/index.html.erb
<h1>Listing users</h1>
<table>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
<th></th>
<th></th>
</tr>
<% @users.each do |user| %>
<tr>
<td><%= user.name %></td>
<td><%= user.email %></td>
<td><%= link_to 'Show', user %></td>
<td><%= link_to 'Edit', edit_user_path(user) %></td>
<td><%= link_to 'Destroy', user, method: :delete,
data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New User', new_user_path %>
Представление преобразует свое содержимое в HТML (Шаг 7), который затем возвращается контроллером в браузер для отображения (Шаг 8).
2.2.3 Недостатки данного Users ресурса
Несмотря на полезность для общего обзора Rails, scaffold-ный ресурс Users имеет много серьезных недостатков.
- Нет валидации данных. Наша модель User безропотно принимает такие данные как пустые имена и недопустимые адреса электронной почты.
- Нет аутентификации. У нас нет понятия регистрации, и нет способа воспрепятствовать тому, чтобы любой пользователь выполнил любую операцию.
- Нет тестов. Что, технически, не вполне верно — scaffolding включает рудиментарные тесты — но сгенерированные тесты уродливы и негибки, и они не тестируют на подтверждение правильности данных, аутентификацию, или какие нибудь другие пользовательские потребности.
- Нет макета. Нет сколь-нибудь связанного стиля сайта или навигации.
- Нет реального понимания. Если вы понимаете код scaffold-а, вам, вероятно, не стОит читать эту книгу.
2.3 Ресурс Microposts
Сгенерировав и исследовав ресурс Users, мы поворачиваемся теперь к связанному с ним ресурсу Microposts. На протяжении этого раздела я рекомендую сравнивать элементы ресурса Microposts с аналогичными элементами User из Раздела 2.2; вы должны увидеть, что эти два ресурса во многом параллельны друг другу. RESTful структура Rails приложений лучше всего усваивается посредством наблюдения за подобными повторяющимися формами; действительно, наблюдение параллельной структуры Users и Microposts даже на этой ранней стадии является одной из причин написания этой главы. (Как мы увидим, написание приложения, более устойчивого, нежели игрушечный пример в этой главе, требует значительных усилий — мы не увидим ресурс Microposts до Главы 10 — и я не хотел так надолго откладывать его первое появление.)
2.3.1 Микрообзор микросообщений
Как и с ресурсом Users, мы генерируем код scaffold для ресурса Microposts, используя rails generate scaffold
, в этом случае реализуя модель данных из Рис. 2.3:5
$ rails generate scaffold Micropost content:string user_id:integer
invoke active_record
create db/migrate/20111123225811_create_microposts.rb
create app/models/micropost.rb
invoke test_unit
create test/unit/micropost_test.rb
create test/fixtures/microposts.yml
route resources :microposts
invoke scaffold_controller
create app/controllers/microposts_controller.rb
invoke erb
create app/views/microposts
create app/views/microposts/index.html.erb
create app/views/microposts/edit.html.erb
create app/views/microposts/show.html.erb
create app/views/microposts/new.html.erb
create app/views/microposts/_form.html.erb
invoke test_unit
create test/functional/microposts_controller_test.rb
invoke helper
create app/helpers/microposts_helper.rb
invoke test_unit
create test/unit/helpers/microposts_helper_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/microposts.js.coffee
invoke scss
create app/assets/stylesheets/microposts.css.scss
invoke scss
identical app/assets/stylesheets/scaffolds.css.scss
Чтобы обновить нашу базу данных с новой моделью данных, мы должны запустить миграцию как в Разделе 2.2:
$ bundle exec rake db:migrate
== CreateMicroposts: migrating ===============================================
-- create_table(:microposts)
-> 0.0023s
== CreateMicroposts: migrated (0.0026s) ======================================
Теперь мы имеем возможность создавать микропосты тем же образом, каким мы создавали пользователей в Разделе 2.2.1. Как вы могли бы предположить, генератор scaffold добавил в файл маршрутов Rails правило для ресурса Microposts, как видно в Листинге 2.7.6 Как и с users, resources :microposts
маршрутное правило направляет URL микросообщений на действия в контроллере Microposts, как видно в Таблице 2.3.
config/routes.rb
DemoApp::Application.routes.draw do
resources :microposts
resources :users
.
.
.
end
HTTP запрос | URI | Действие | Назначение |
---|---|---|---|
GET | /microposts | index | страница со списком всех микросообщений |
GET | /microposts/1 | show | страница показывающая микросообщение с id 1 |
GET | /microposts/new | new | страница для создания нового микросообщения |
POST | /microposts | create | создание нового микросообщения |
GET | /microposts/1/edit | edit | страница для редактирования микросообщения с id 1 |
PUT | /microposts/1 | update | обновление микросообщения с id 1 |
DELETE | /microposts/1 | destroy | удаление микросообщения с id 1 |
Сам Microposts контроллер в схематичной форме представлен в Листинге 2.8. Отметьте, что, кроме наличия MicropostsController
вместо UsersController
, Листинг 2.8 идентичен коду в Листинге 2.3. Это отражение REST архитектуры, характерной для обоих ресурсов.
app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
def index
.
.
.
end
def show
.
.
.
end
def new
.
.
.
end
def create
.
.
.
end
def edit
.
.
.
end
def update
.
.
.
end
def destroy
.
.
.
end
end
для того чтобы сделать несколько настоящих микросообщений, мы вводим информацию на new странице микросообщений, /microposts/new как видно на Рис. 2.12.
Теперь создайте одно - два микросообщения, удостоверьтесь, чтобы по крайней мере у одного из них был user_id
= 1
для сопоставления с id первого пользователя, созданного в Разделе 2.2.1. Результат должен быть похож на Рис. 2.13.
2.3.2 Помещение микро в микросообщения
Каждое микросообщение, достойное своего имени, должно иметь некоторые средства предписания длины сообщения. Реализация этого ограничения в Rails легка с проверками допустимости (валидациями); чтобы принять микросообещния длиной не более 140 символов (а-ля Twitter), мы используем проверку допустимости (валидацию) длины. В этой точке следует открыть файл app/models/micropost.rb
в вашем текстовом редакторе или IDE и заполнить его содержимым Листинга 2.9. (Использование валидации из Листинга 2.9 характерно для Rails 3; если вы ранее работали с Rails 2.3, следует сравнить это с использованием validates_length_of
.)
app/models/micropost.rb
class Micropost < ActiveRecord::Base
attr_accessible :content, :user_id
validates :content, :length => { :maximum => 140 }
end
Код в Листинге 2.9 может выглядеть довольно таинственно — мы рассмотрим валидацию более тщательно в Разделе 6.2 — но его эффект станет вполне очевиден, если мы перейдем в новую страницу микросообщений и введем больше чем 140 символов для контента сообщения. Как видно на Рис. 2.14, Rails выдает сообщение об ошибке указывающее на то, что контент микросообщения является слишком длинным. (Мы узнаем больше о сообщениях об ошибках в Разделе 7.3.2.)
2.3.3 A user has_many
microposts (Пользователь имеет_много
микросообщений)
Одна из наиболее мощных функций Rails – возможность формировать связи между различными моделями данных. В случае нашей модели User у каждого пользователя потенциально есть много микросообщений. Мы можем выразить это в коде, обновив модели User и Micropost как это показано в Листинге 2.10 и Листинге 2.11.
app/models/user.rb
class User < ActiveRecord::Base
attr_accessible :email, :name
has_many :microposts
end
app/models/micropost.rb
class Micropost < ActiveRecord::Base
attr_accessible :content, :user_id
belongs_to :user
validates :content, :length => { :maximum => 140 }
end
Мы можем увидеть результат применения этой ассоциации на Рис. 2.15. Из-за user_id
столбца в таблице microposts
Rails (используя Active Record) может вывести микросообщения, связанные с каждым пользователем.
В Главе 10 и Главе 11, мы будем использовать ассоциацию пользователей и микросообщений и чтобы вывести на экран все микросообщения пользователя, и чтобы создать Twitter-подобную подачу микросообщений. Пока, мы можем исследовать применение связи микросообщения-пользователь с помощью консоли, которая является полезным инструментом для взаимодействия с Rails приложениями. Сначала мы вызовем консоль командой rails console
в командной строке, а затем получим первого пользователя из базы данных, используя User.first
(поместив результаты в переменную first_user
):7
$ rails console
>> first_user = User.first
=> #<User id: 1, name: "Michael Hartl", email: "[email protected]",
created_at: "2011-11-03 02:01:31", updated_at: "2011-11-03 02:01:31">
>> first_user.microposts
=> [#<Micropost id: 1, content: "First micropost!", user_id: 1, created_at:
"2011-11-03 02:37:37", updated_at: "2011-11-03 02:37:37">, #<Micropost id: 2,
content: "Second micropost", user_id: 1, created_at: "2011-11-03 02:38:54",
updated_at: "2011-11-03 02:38:54">]
>> exit
(Я включил последнюю строку только чтобы продемонстрировать, как выйти из консоли, и на большинстве систем вы можете использовать Ctrl-d с этой же целью.) Здесь мы получили доступ к микросообщениям пользователя, используя код first_user.microposts
: с этим кодом Active Record автоматически возвращает все микросообщения с user_id
равным id first_user
(в данном случае, 1
). Мы узнаем намного больше о возможностях ассоциаций (связей) Active Record в Главе 10 и Главе 11.
2.3.4 Иерархия наследования
Мы закончим наше обсуждение демонстрационного приложения кратким описанием иерархии классов контроллеров и моделей в Rails. Это обсуждение будет иметь много смысла, только если у вас был некоторый опыт объектно-ориентированного программирования (ООП); если вы не изучали ООП, не стесняйтесь пропустить этот раздел. В частности, если вы не знакомы с классами (обсуждаемыми в Разделе 4.4), я предлагаю вернуться к этому разделу позже.
Мы начнем со структуры наследования для моделей. Сравнивая Листинг 2.12 с Листингом 2.13, мы видим, что и модель User и модель Micropost наследовались (через левую угловую скобку <
) от ActiveRecord::Base
, который является базовым классом для моделей, предоставляемых библиотекой ActiveRecord; схема, резюмирующая это отношение, представлена на Рис. 2.16. Благодаря наследованию от ActiveRecord::Base
наши объекты модели получают возможность связываться с базой данных, обрабатывать столбцы базы данных как Ruby атрибуты и так далее.
User
, с наследованием. app/models/user.rb
class User < ActiveRecord::Base
.
.
.
end
Micropost
с наследованием. app/models/micropost.rb
class Micropost < ActiveRecord::Base
.
.
.
end
Структура наследования для контроллеров лишь немногим более сложная. Сравнивая Листинг 2.14 с Листингом 2.15, мы видим, что и контроллер User и контроллер Micropost наследовались от Application controller. Исследуя Листинг 2.16, мы видим, что сам ApplicationController
наследует от ActionController::Base
; это базовый класс для контроллеров, обеспеченных Rails библиотекой Action Pack. Отношения между этими классами иллюстрируются в Рис. 2.17.
UsersController
, с наследованием. app/controllers/users_controller.rb
class UsersController < ApplicationController
.
.
.
end
MicropostsController
, с наследованием. app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
.
.
.
end
ApplicationController
, с наследованием. app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
.
.
.
end
Как и с наследованием моделей, будучи, в конечном счете, наследниками ActionController::Base
и Users и Microposts контроллеры имеют большое количество функций, таких как возможность управлять объектами модели, фильтровать входящие запросы HTTP, и визуализировать представления как HTML. Так как все контроллеры Rails наследовались от ApplicationController
, правила, определенные в ApplicationController автоматически, применяются к каждому действию в приложении. Например, в Разделе 8.2.1 мы увидим как включить хелперы для входа и выхода во все контроллеры примера приложения.
2.3.5 Развертывание демонстационного приложения
Закончив ресурс Microposts, мы можем отправить репозиторий на GitHub:
$ git add .
$ git commit -a -m "Finish demo app"
$ git push
Обычно вам следует делать более частые коммиты, но для этой главы единственный большой коммит в конце - это вполне нормально.
Можно также развернуть demo app на Heroku как и в Разделе 1.4:
$ heroku create --stack cedar
$ git push heroku master
Наконец, запустите миграции для продакшен базы данных (см. ниже если вы получили deprecation warning):
$ heroku run rake db:migrate
Это обновит базу данных на Heroku с необходимой моделью данных. Вы можете получить "deprecation warning" относящееся к assets в vendor/plugins
, которое вам следует игнорировать, поскольку в этой директории нет никаких плагинов.
2.4 Заключение
Мы подошли к концу 9 144 метрового (30,000-foot ) обзора Rails приложения. У demo app, разработанного в этой главе, есть несколько достоинств и масса недостатков.
Достоинства
- Общее представление о Rails
- Введение в MVC
- Первый вкус архитектуры REST
- Начальное моделирование данных
- Живое, поддерживаемое базой данных веб-приложение в production
Недостатки
- Нет собственного макета или стиля
- Нет статических страниц (таких как "Главная" или "О нас")
- Нет паролей пользователя
- Нет изображений пользователя
- Нет регистрации
- Нет безопасности
- Нет автоматической связи пользователь/микросообщение
- Нет понятий “following” или “followed”
- Нет потока (ленты) микросообщений
- Нет разработки через тестирование
- Нет реального понимания
Остальная часть этого учебника посвящена усилению достоинств и устранению недостатков.
- Моделируя более длинные сообщения, для нормального (не микро) блога, следует использовать тип
text
вместоstring
. ↑ - Имя scaffold следует за соглашением в соответствии с которым, модели имеют единственное число, в отличие от ресурсов и контроллеров, которые используются во множественном числе. Таким образом, у нас есть User вместо Users. ↑
- Некоторые источники утверждают, что представление возвращает HTML непосредственно браузеру (через веб-сервер, такой как Apache или Nginx). Независимо от деталей реализации, я предпочитаю думать о контроллере как о центральном хабе через который проходят информационные потоки всего приложения. ↑
- Fielding, Roy Thomas. Architectural Styles and the Design of Network-based Software Architectures. Doctoral dissertation, University of California, Irvine, 2000. ↑
- Как и с User scaffold, генератор scaffold для микросообщений следует сингулярному соглашению для моделей Rails; таким образом, мы имеем,
generate Micropost
, а неgenerate MicropostS
. ↑ - У кода scaffold могут быть дополнительные новые строки по сравнению с Листингом 2.7. Это не повод для беспокойства, поскольку Ruby игнорирует дополнительные новые строки. ↑
- Ваше приглашение командной строки, вероятно, будет чем-то вроде
ruby-1.9.3-head >
, но я буду использовать >> поскольку версии Ruby могут отличаться. ↑