Здравствуйте, записался на курс. При этом ставил галочку на "обучаться с тьютором". На email пришло письмо, о том, что записался на самостоятельное изучение курса. Как выбрать тьютора? |
demo app
В этой главе мы разработаем простое демонстрационное приложение, чтобы показать мощь Rails. Цель - получить общее представление о программировании на Ruby on Rails (и веб-разработке в целом), быстро сгенерировав приложение, используя scaffold generators. Как обсуждалось в Блоке 1.2, остальная часть книги будет применять противоположный подход, постепенно разрабатывая приложение с объяснением всех новых понятий по мере их появления, но для быстрого обзора (и некоторого быстрорастворимого удовлетворения) нет ничего лучше чем scaffolding. Конечное демонстрационное приложение позволит нам взаимодействовать с ним через свои URL, дав нам понимание структуры Rails приложений, включая первый пример REST архитектуры предпочитаемой Rails.
Как и предстоящий пример приложения, демонстрационное приложение будет состоять из пользователей и связанных с ними микросообщений (образуя минималистичное приложение в стиле Twitter). Функциональность будет совершенно слаборазвита и многие из шагов будут походить на волшебство, но не переживайте: полный пример приложения разработает подобное же приложение с нуля, начиная с Главы 3, и я дам многочисленные ссылки на более поздний материал. Тем временем, имейте терпение и немного веры - все таки основная цель данного учебника - удержать вас вне этого поверхностного, управляемого scaffold подхода, для достижения более глубокого понимания Rails.
Планирование приложения
В этом Разделе мы обрисуем в общих чертах наши планы относительно демонстрационного приложения. Как и в Разделе 1.2.3, мы начнем с создания скелета приложения, используя команду rails:
$ cd ~/rails_projects $ rails new demo_app $ cd demo_app
Затем, мы используем текстовый редактор, чтобы обновить Gemfile необходимый Bundler, содержанием из Листинга 2.1.
source 'https://rubygems.org' ruby '2.0.0' #ruby-gemset=railstutorial_rails_4_0 gem 'rails', '4.0.2' group :development do gem 'sqlite3', '1.3.8' end gem 'sass-rails', '4.0.1' gem 'uglifier', '2.1.1' gem 'coffee-rails', '4.0.1' gem 'jquery-rails', '3.0.4' gem 'turbolinks', '1.1.1' gem 'jbuilder', '1.0.2' group :doc do gem 'sdoc', '0.3.20', require: false end group :production do gem 'pg', '0.15.1' gem 'rails_12factor', '0.0.2' endЛистинг 2.1. Gemfile для демонстрационного приложения.
Отмечу, что Листинг 2.1 аналогичен Листингу 1.9
Как и в Разделе 1.4.1, мы установим локальные гемы и предотвратим установку production гемов с помощью опции --without production:
$ bundle install --without production $ bundle update $ bundle install
(Напомним что если Bundler выдает ошибку связанную с readline, стоит попробовать добавить gem ’rb-readline’ в ваш Gemfile.)
Наконец, мы поместим наш код в систему контроля версий. Вспомним что rails команда генерирует дефолтный .gitignore файл, но, в зависимости от вашей системы, вы можете найти расширенный файл из Листинга 1.7 гораздо более удобным. Затем инициализируем Git репозиторий и делаем первый коммит:
$ git init $ git add . $ git commit -m "Initial commit"
Можно также дополнительно создать новый репозиторий ( рис. 2.1) и отправить его на GitHub:
$ git remote add origin https://github.com/<username>/demo_app.git $ git push -u origin master
(Как и в случае с первым приложением, позаботьтесь о том чтобы не инициализировать репозиторий GitHub с файлом README.)
Теперь мы готовы начать делать само приложение. Типичный первый шаг, при разработке приложений это создание модели данных, которая является изображением структур, необходимых нашему приложению. В нашем случае demo app будет упрощенным микроблогом: только пользователи и короткие (микро) сообщения. Таким образом, мы начнем с модели для пользователей приложения (Раздел 2.1.1), затем мы добавим модель для микросообщений (Раздел 2.1.2).
Моделирование пользователей
Вариантов для модели данных пользователя так же много, как много различных форм регистрации в сети; мы пойдем отчетливо минималистичным путем. У пользователей нашего demo app будет уникальный целочисленный идентификатор называемый id, публично видимое имя (тип - string (строка)), и email адрес (также string) совпадающий с именем пользователя. Итоговая модель данных для пользователей представлена на рис. 2.2.
Как мы увидим в Разделе 6.1.1, табличка users в рис. 2.2 соответствует таблице в базе данных и id, name, и email атрибуты это столбцы этой таблицы
Моделирование микросообщений
Ядро модели данных микросообщений даже проще чем для пользователей: микросообщение имеет только id и поле content для текста микросообщения (тип - string).1Моделируя более длинные сообщения, для нормального (не микро) блога, следует использовать тип text вместо string. Есть дополнительная сложность: мы хотим связать каждое микросообщение с определенным пользователем; мы выполним это, записывая user_id владельца сообщения. Результаты показаны на рис. 2.3.
Мы увидим в Разделе 2.3.3 (и более полно в Главе 10) как этот user_id атрибут позволит нам кратко реализовать идею, что у пользователя потенциально есть много связанных с ним микросообщений.
Ресурс Users
В этом разделе мы реализуем модель данных пользователей из Раздела 2.1.1, наряду с веб-интерфейсом к этой модели. Эта комбинация образует ресурс Users, который позволит нам думать о пользователях как об объектах, которые могут быть созданы, считаны, обновлены, и удалены через сеть с использованием HTTP протокола. Как обещалось во введении, наш ресурс Users будет создаваться программой scaffold generator, которая стандартно поставляется с каждым проектом Rails. Я настоятельно прошу вас не всматриваться в генерируемый код; на этом этапе он лишь запутает вас.
Rails scaffolding запускается передачей scaffold команды скрипту rails generate. Аргумент команды scaffold это имя ресурса в единственном числе (в данном случае, User), вместе с дополнительными параметрами для атрибутов модели данных:2Имя scaffold следует за соглашением в соответствии с которым, модели имеют единственное число, в отличие от ресурсов и контроллеров, которые используются во множественном числе. Таким образом, у нас есть User вместо Users.
$ rails generate scaffold User name:string email:string invoke active_record create db/migrate/20130305221714_create_users.rb create app/models/user.rb invoke test_unit create test/models/user_test.rb create test/fixtures/users.yml invoke resource_route route resources :users invoke jbuilder_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/controllers/users_controller_test.rb invoke helper create app/helpers/users_helper.rb invoke test_unit create test/helpers/users_helper_test.rb invoke jbuilder exist app/views/users create app/views/users/index.json.jbuilder create app/views/users/show.json.jbuilder 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 в базе данных.)
$ 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. (Если вы, в соответствии с рекомендациями в Разделе 1.2.2.3, используете RVM, вы можете опускать bundle exec. Для того чтобы узнать об альтернативных способах избавления от bundle exec, см. Section 3.6.1.)
Теперь мы можем запустить локальный веб-сервер, используя rails s, что является сокращением для rails server:
$ rails s
При этом демонстрационное приложение уже должно быть готово по адресу http://localhost:3000/.
Блок 2.1.Rake
В традиции 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 задачи, запустите
Список, вероятно, будет подавляющим, но не волнуйтесь, вы не должны знать все (или даже большую часть) этих команд. К концу Учебника Rails, вы будете знать все самые важные
Обзор пользователя
При посещении корневого url http://localhost:3000/ мы видим ту же самую дефолтную страницу Rails, что и на рис. 1.3, но при генерировании ресурса Users scaffolding-ом мы также создали большое количество страниц для управления пользователями. Например, страницу для отображения списка всех пользователей на /users и страницу для создания нового пользователя на /users/new. Остальная часть этого раздела посвящена ураганному туру по этим страницам. В процессе этого тура для вас может оказаться полезным иногда поглядывать на таблица 2.1, которая показывает соответствие между страницами и их URL.
URL | Действие | Назначение |
---|---|---|
/users | index | страница, для отображения списка всех пользователей |
/users/1 | show | страница показывающая пользователя с id |
/users/new | new | страница для создания нового пользователя |
/users/1/edit | edit | страница для редактирования пользователя с id |
Мы начнем со страницы показывающей всех пользователей в нашем приложении, называемой index; как вы могли бы ожидать, изначально там нет никаких пользователей ( рис. 2.4).
Для того, чтобы создать нового пользователя мы посещаем new страницу, как показано на рис. 2.5 (Поскольку http://localhost:3000 часть адреса явно будет повторятся до тех пор пока мы разрабатываем локально, я буду обычно опускать ее с этого момента.) В Главе 7 это станет страницей регистрации пользователя.
Мы можем создать пользователя, введя значения имени и адреса электронной почты в текстовые поля и нажав затем кнопку Create User. Результат - страница show как видно на рис. 2.6 (Зеленое приглашающее сообщение выполняется с использованием флэш, о котором мы узнаем в Разделе 7.4.2.) Отметим, что в URL /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 добавляет удаление пользователя к демонстрационному приложению, заботясь об ограничении его использования специальным классом административных пользователей.
MVC в действии
Теперь, когда мы завершили быстрый обзор ресурса Пользователи (Users), давайте исследуем одну его определенную часть в контексте паттерна Модель-Представление-Контроллер (MVC) частично представленного в Разделе 1.2.6. Наша стратегия будет состоять в том, чтобы описать результаты типичного запроса браузера: посещение index страницы пользователя на /users—в терминах MVC ( рис. 2.11).
- Браузер выдает запрос на URL /users.
- Rails направляет /users к index действию в контроллере Users.
- Index действие просит, чтобы модель User получила всех пользователей (User.all).
- Модель User вытягивает всех пользователей из базы данных.
- Модель User возвращает список пользователей в контроллер.
- Контроллер получает пользователей в @users переменной, которую он передает index представлению.
- Представление использует Embedded (Встроенный) Ruby, чтобы визуализировать страницу как HTML.
- Контроллер возвращает HTML в браузер.3Некоторые источники утверждают, что представление возвращает HTML непосредственно браузеру (через веб-сервер, такой как Apache или Nginx). Независимо от деталей реализации, я предпочитаю думать о контроллере как о центральном хабе через который проходят информационные потоки всего приложения.
Мы начинаем с запроса, выданного браузером — то есть, с результата ввода URL в адресной строке или клика по ссылке (Шаг 1 в рис. 2.11). Этот запрос вызывает Rails маршрутизатор (Шаг 2), который отправляет к надлежащему действию контроллера, основываясь на URL (и, как мы увидим в Блоке 3.3, на типе запроса). Код, создающий перенаправление URL пользователя к действиям контроллера для ресурса Users представлен в Листинге 2.2; этот код фактически устанавливает таблицу пар URL/Действие, которые мы видели в Таблице 2.1. (Странная нотация :users это symbol, о котором мы узнаем в Разделе 4.3.3.)
Страницы из Раздела 2.2.1 соответствуют действиям в контроллере Users, который является набором связанных действий; контроллер, сгенерированный scaffolding, схематично показан в Листинге 2.3. Отметьте запись class UsersController < ApplicationController; это пример Ruby класса с наследованием. (Мы вкратце обсудим наследование в Разделе 2.3.4 и раскроем обе темы более подробно в Разделе 4.4.)
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Листинг 2.3. Контроллер Users в схематичной форме.app/controllers/users_controller.rb
Вы могли заметить, что действий больше чем страниц; index, show, new, и edit действия - все они соответствуют страницам из Раздела 2.2.1, но есть также дополнительные create (создать), update (обновить), и destroy (разрушить) действия. Эти действия обычно не визуализируют страниц (хотя иногда могут); вместо этого, их основная цель состоит в том, чтобы изменять информацию о пользователях в базе данных. Этот полный комплект действий контроллера, сведенный в таблица 2.2, представляет реализацию архитектуры REST в Rails (Блок 2.2), которая опирается на идеи передачи состояния представления идентифицированные и названые ученым Roy Fielding.4Fielding, Roy Thomas. Architectural Styles and the Design of Network-based Software Architectures. Doctoral dissertation, University of California, Irvine, 2000. Отметьте в таблица 2.2 что есть некоторое наложение в URLs; например, и show и update действиям соответствует URL /users/1. Различие между ними в методе запроса HTTP на который они отвечают. Мы узнаем больше о методах запроса HTTP в Разделе 3.2.1.
HTTP запрос | URL | Действие | Назначение |
---|---|---|---|
GET | /users | index | страница со списком всех пользователей |
GET | /users/1 | show | страница показывающая пользователя с id 1 |
GET | /users/new | new | страница для создания нового пользователя |
POST | /users | create | создание нового пользователя |
GET | /users/1/edit | edit | страница для редактирования пользователя с id 1 |
PATCH | /users/1 | update | обновление пользователя с id 1 |
DELETE | /users/1 | destroy | удаление пользователя с id 1 |
Блок 2.2.REpresentational State Transfer (REST, "передача состояния представления")
Если вы много читали о веб-разработке на Ruby on Rails, вы видели много ссылок на "REST", являющуюся акронимом для REpresentational State Transfer ("передача состояния представления"). REST - архитектурный стиль для разработки распределенных, сетевых систем и приложений, таких как World Wide Web и веб-приложения. Хотя теория REST довольно абстрактна, в контексте Rails приложений REST означает, что большинство компонентов приложения (таких как пользователи и микросообщения) моделируются как ресурсы, которые могут быть созданы (Сreated), прочитаны (Read), обновлены (Updated), и удалены (Deleted) - операции, которые соответствуют и операциям CRUD, реляционных баз данных и четырем фундаментальным методам запроса HTTP: POST, GET, PATCH, и DELETE. (Мы узнаем больше о запросах HTTP в Разделе 3.2.1 и особенно в Блоке 3.3.)
Как разработчику Rails приложений, RESTful стиль разработки помогает вам определиться какие контроллеры и действия писать: вы просто структурируете приложение, используя ресурсы, которые создаются, читаются, обновляются, и удаляются. В случае пользователей и микросообщений, этот процесс является простым, так как они - натуральные ресурсы по определению. В Главе 11 мы увидим пример, где принципы REST позволяют нам моделировать более тонкую проблему "Слежения за сообщениями пользователей" естественным и удобным способом.
Чтобы исследовать отношения между контроллером Users и моделью User, давайте сосредоточимся на упрощенной версии index действия, показанной в Листинге 2.4. (Код scaffold уродливый и запутанный, так что я опустил большую его часть.)
class UsersController < ApplicationController . . . def index @users = User.all end . . . endЛистинг 2.4. Упрощенное index действие для демонстрационного приложения. app/controllers/users_controller.rb
У этого 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 возвратить всех пользователей.
class User < ActiveRecord::Base endЛистинг 2.5. Модель User для демонстрационного приложения. app/models/user.rb
Как только @users переменная определена, контроллер вызывает представление (Шаг 6), показанное в Листинге 2.6. Переменные, которые начинаются со знака @, называемые переменными экземпляра, автоматически доступны в представлении; в данном случае, представление index.html.erb в Листинге 2.6 перебирает список @users и выводит строку HTML для каждого. (Помните, вы не должны пытаться понять этот код прямо сейчас. Он показан только для иллюстрации.)
<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 %>Листинг 2.6. Представление для index. app/views/users/index.html.erb
Представление преобразует свое содержимое в HТML (Шаг 7), который затем возвращается контроллером в браузер для отображения (Шаг 8).
Недостатки данного Users ресурса
Несмотря на полезность для общего обзора Rails, scaffold-ный ресурс Users имеет много серьезных недостатков.
- Нет валидации данных. Наша модель User безропотно принимает такие данные как пустые имена и недопустимые адреса электронной почты
- Нет аутентификации. У нас нет понятия регистрации, и нет способа воспрепятствовать тому, чтобы любой пользователь выполнил любую операцию.
- Нет тестов. Что, технически, не вполне верно — scaffolding включает рудиментарные тесты — но сгенерированные тесты уродливы и негибки, и они не тестируют на подтверждение правильности данных, аутентификацию, или какие нибудь другие пользовательские потребности.
- Нет макета. Нет сколь-нибудь связанного стиля сайта или навигации.
- Нет реального понимания. Если вы понимаете код scaffold-а, вам, вероятно, не стОит читать эту книгу.
Ресурс Microposts
Сгенерировав и исследовав ресурс Users, мы поворачиваемся теперь к связанному с ним ресурсу Microposts. На протяжении этого раздела я рекомендую сравнивать элементы ресурса Microposts с аналогичными элементами User из Раздела 2.2; вы должны увидеть, что эти два ресурса во многом параллельны друг другу. RESTful структура Rails приложений лучше всего усваивается посредством наблюдения за подобными повторяющимися формами; действительно, наблюдение параллельной структуры Users и Microposts даже на этой ранней стадии является одной из причин написания этой главы. (Как мы увидим, написание приложения, более устойчивого, нежели игрушечный пример в этой главе, требует значительных усилий — мы не увидим ресурс Microposts до Главы 10 — и я не хотел так надолго откладывать его первое появление.)
Микрообзор микросообщений
Как и с ресурсом Users, мы генерируем код scaffold для ресурса Microposts, используя rails generate scaffold, в этом случае реализуя модель данных из рис. 2.3:5Как и с User scaffold, генератор scaffold для микросообщений следует сингулярному соглашению для моделей Rails; таким образом, мы имеем, generate Micropost, а не generate MicropostS.
$ rails generate scaffold Micropost content:string user_id:integer invoke active_record create db/migrate/20130307005528_create_microposts.rb create app/models/micropost.rb invoke test_unit create test/models/micropost_test.rb create test/fixtures/microposts.yml invoke resource_route route resources :microposts invoke jbuilder_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/controllers/microposts_controller_test.rb invoke helper create app/helpers/microposts_helper.rb invoke test_unit create test/helpers/microposts_helper_test.rb invoke jbuilder exist app/views/microposts create app/views/microposts/index.json.jbuilder create app/views/microposts/show.json.jbuilder 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У кода scaffold могут быть дополнительные новые строки по сравнению с Листингом 2.7. Это не повод для беспокойства, поскольку Ruby игнорирует дополнительные новые строки. Как и с users, resources :microposts маршрутное правило направляет URL микросообщений на действия в контроллере Microposts, как видно в таблица 2.3.
DemoApp::Application.routes.draw do resources :microposts resources :users . . . endЛистинг 2.7. Маршруты Rails с новым правилом для ресурсов Microposts. config/routes.rb
HTTP запрос | URL | Действие | Назначение |
---|---|---|---|
GET | /microposts | index | страница со списком всех микросообщений |
GET | /microposts/1 | show | страница показывающая микросообщение с id 1 |
GET | /microposts/new | new | страница для создания нового микросообщения |
POST | /microposts | create | создание нового микросообщения |
GET | /microposts/1/edit | edit | страница для редактирования микросообщения с id 1 |
PATCH | /microposts/1 | update | обновление микросообщения с id 1 |
DELETE | /microposts/1 | destroy | даление микросообщения с id 1 |
Сам Microposts контроллер в схематичной форме представлен в Листинге 2.8. Отметьте, что, кроме наличия MicropostsController вместо UsersController, Листинг 2.8 идентичен коду в Листинге 2.3.. Это отражение REST архитектуры, характерной для обоих ресурсов.
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Листинг 2.8. Контроллер Microposts в схематичной форме. app/controllers/microposts_controller.rb
для того чтобы сделать несколько настоящих микросообщений, мы вводим информацию на new странице микросообщений, /microposts/new как видно на рис. 2.12.
Теперь создайте одно - два микросообщения, удостоверьтесь, чтобы по крайней мере у одного из них был user_id = 1 для сопоставления с id первого пользователя, созданного в Разделе 2.2.1. Результат должен быть похож на рис. 2.13.
Помещение микро в микросообщения
Каждое микросообщение, достойное своего имени, должно иметь некоторые средства предписания длины сообщения. Реализация этого ограничения в Rails легка с проверками допустимости (валидациями); чтобы принять микросообещния длиной не более 140 символов (а-ля Twitter), мы используем проверку допустимости (валидацию) длины. В этой точке следует открыть файл app/models/micropost.rb в вашем текстовом редакторе или IDE и заполнить его содержимым Листинга 2.9. (Использование валидации из Листинга 2.9 характерно для Rails 3; если вы ранее работали с Rails 2.3, следует сравнить это с использованием validates_length_of.)
class Micropost < ActiveRecord::Base validates :content, length: { maximum: 140 } endЛистинг 2.9. Ограничение микросообщений максимумом в 140 знаков посредством валидации длины. app/models/micropost.rb
Код в Листинге 2.9 может выглядеть довольно таинственно — мы рассмотрим валидацию более тщательно в Разделе 6.2 — но его эффект станет вполне очевиден, если мы перейдем в новую страницу микросообщений и введем больше чем 140 символов для контента сообщения. Как видно на рис. 2.14, Rails выдает сообщение об ошибке указывающее на то, что контент микросообщения является слишком длинным. (Мы узнаем больше о сообщениях об ошибках в Разделе 7.3.3.)
A user has_many microposts (Пользователь имеет_много микросообщений)
Одна из наиболее мощных функций Rails – возможность формировать связи между различными моделями данных. В случае нашей модели User у каждого пользователя потенциально есть много микросообщений. Мы можем выразить это в коде, обновив модели User и Micropost как это показано в Листинге 2.10 и Листинге 2.11.
class User < ActiveRecord::Base has_many :microposts endЛистинг 2.10. У пользователя есть много микросообщений (user has many microposts). app/models/user.rb
class Micropost < ActiveRecord::Base belongs_to :user validates :content, length: { maximum: 140 } endЛистинг 2.11. Микросообщения принадлежат пользователю (micropost belongs to a user). app/models/micropost.rb
Мы можем увидеть результат применения этой ассоциации на рис. 2.15. Из-за user_id столбца в таблице microposts Rails (используя Active Record) может вывести микросообщения, связанные с каждым пользователем.
В Главе 10 и Главе 11, мы будем использовать ассоциацию пользователей и микросообщений и чтобы вывести на экран все микросообщения пользователя, и чтобы создать Twitter-подобную подачу микросообщений. Пока, мы можем исследовать применение связи микросообщения-пользователь с помощью консоли, которая является полезным инструментом для взаимодействия с Rails приложениями. Сначала мы вызовем консоль командой rails console в командной строке, а затем получим первого пользователя из базы данных, используя User.first (поместив результаты в переменную first_user):7Ваше приглашение командной строки, вероятно, будет чем-то вроде ruby-2.0.0-head >, но я буду использовать >> поскольку версии Ruby могут отличаться
$ rails console >> first_user = User.first => #<User id: 1, name: "Michael Hartl", email: "michael@example.org", created_at: "2013-03-06 02:01:31", updated_at: "2013-03-06 02:01:31"> >> first_user.microposts => [#<Micropost id: 1, content: "First micropost!", user_id: 1, created_at: "2013-03-06 02:37:37", updated_at: "2013-03-06 02:37:37">, #<Micropost id: 2, content: "Second micropost", user_id: 1, created_at: "2013-03-06 02:38:54", updated_at: "2013-03-06 02:38:54">] >> exit
(Я включил последнюю строку только чтобы продемонстрировать, как выйти из консоли, и на большинстве систем вы можете использовать Ctrl-d с этой же целью.) Здесь мы получили доступ к микросообщениям пользователя, используя код first_user.microposts: с этим кодом Active Record автоматически возвращает все микросообщения с user_id равным id first_user (в данном случае, 1). Мы узнаем намного больше о возможностях ассоциаций (связей) Active Record в Главе 10 и Главе 11.
Иерархия наследования
Мы закончим наше обсуждение демонстрационного приложения кратким описанием иерархии классов контроллеров и моделей в Rails. Это обсуждение будет иметь много смысла, только если у вас был некоторый опыт объектно-ориентированного программирования (ООП); если вы не изучали ООП, не стесняйтесь пропустить этот раздел. В частности, если вы не знакомы с классами (обсуждаемыми в Разделе 4.4), я предлагаю вернуться к этому разделу позже.
Мы начнем со структуры наследования для моделей. Сравнивая Листинг 2.12 с Листингом 2.13, мы видим, что и модель User и модель Micropost наследовались (через левую угловую скобку <) от ActiveRecord::Base, который является базовым классом для моделей, предоставляемых библиотекой ActiveRecord; схема, резюмирующая это отношение, представлена на рис. 2.16. Благодаря наследованию от ActiveRecord::Base наши объекты модели получают возможность связываться с базой данных, обрабатывать столбцы базы данных как Ruby атрибуты и так далее.
class User < ActiveRecord::Base . . . endЛистинг 2.12. Класс User, с наследованием. app/models/user.rb
class Micropost < ActiveRecord::Base . . . endЛистинг 2.13. Класс Micropost с наследованием. app/models/micropost.rb
Структура наследования для контроллеров лишь немногим более сложная. Сравнивая Листинг 2.14 с Листингом 2.15, мы видим, что и контроллер User и контроллер Micropost наследовались от Application controller. Исследуя Листинг 2.16, мы видим, что сам ApplicationController наследует от ActionController::Base; это базовый класс для контроллеров, обеспеченных Rails библиотекой Action Pack. Отношения между этими классами иллюстрируются в рис. 2.17.
class UsersController < ApplicationController . . . endЛистинг 2.14. Класс UsersController, с наследованием. app/controllers/users_controller.rb
class MicropostsController < ApplicationController . . . endЛистинг 2.15. Класс MicropostsController, с наследованием. app/controllers/microposts_controller.rb
class ApplicationController < ActionController::Base . . . endЛистинг 2.16. Класс ApplicationController, с наследованием. app/controllers/application_controller.rb
Как и с наследованием моделей, будучи, в конечном счете, наследниками ActionController::Base и Users и Microposts контроллеры имеют большое количество функций, таких как возможность управлять объектами модели, фильтровать входящие запросы HTTP, и визуализировать представления как HTML. Так как все контроллеры Rails наследовались от ApplicationController, правила, определенные в ApplicationController автоматически, применяются к каждому действию в приложении. Например, в Разделе 8.2.1 мы увидим как включить хелперы для входа и выхода во все контроллеры примера приложения.
Развертывание демонстационного приложения
Закончив ресурс Microposts, мы можем отправить репозиторий на GitHub:
$ git add . $ git commit -a -m "Finish demo app" $ git push
Обычно вам следует делать более частые коммиты, но для этой главы единственный большой коммит в конце - это вполне нормально.
Можно также развернуть demo app на Heroku как и в Разделе 1.4:
$ heroku create $ git push heroku master
(Как было отмечено в Разделе 1.4.1, некоторые читатели сообщали о необходимости прекомпиляции ассетов при помощи команды
# Это должно быть использовано только если вы не можете задеплоить на Heroku. $ rake assets:precompile $ git add . $ git commit -m "Add precompiled assets for Heroku" $ git push heroku master
Шаг прекомпиляции ассетов по идее не нужен и я не смог воспроизвести ситуацию в которой он был бы необходим, но количество получаемых сообщений об ошибках таково что я вынужден упомянуть здесь о решении.)
Также необходимо запустить миграции продакшен базы данных:
$ heroku run rake db:migrate
Это обновит базу данных на Heroku с необходимой моделью данных.
Заключение
Мы подошли к концу 9 144 метрового (30,000-foot ) обзора Rails приложения. У demo app, разработанного в этой главе, есть несколько достоинств и масса недостатков.
Достоинства
- Общее представление о Rails
- Введение в MVC
- Первый вкус архитектуры REST
- Начальное моделирование данных
- Живое, поддерживаемое базой данных веб-приложение в production
Недостатки
- Нет собственного макета или стиля
- Нет статических страниц (таких как "Главная" или "О нас")
- Нет паролей пользователя
- Нет изображений пользователя
- Нет регистрации
- Нет безопасности
- Нет автоматической связи пользователь/микросообщение
- Нет понятий "following" или "followed"
- Нет потока (ленты) микросообщений
- Нет разработки через тестирование
- Нет реального понимания
Остальная часть этого учебника посвящена усилению достоинств и устранению недостатков.