Опубликован: 27.01.2016 | Уровень: для всех | Доступ: платный
Лекция 2:

demo app

< Лекция 1 || Лекция 2 || Лекция 3 >

В этой главе мы разработаем простое демонстрационное приложение, чтобы показать мощь 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"
Создание репозитория demo app на GitHub.

Рис. 2.1. Создание репозитория demo app на GitHub.

Можно также дополнительно создать новый репозиторий ( рис. 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.

Модель данных для пользователей.

Рис. 2.2. Модель данных для пользователей.

Как мы увидим в Разделе 6.1.1, табличка users в рис. 2.2 соответствует таблице в базе данных и id, name, и email атрибуты это столбцы этой таблицы

Моделирование микросообщений

Ядро модели данных микросообщений даже проще чем для пользователей: микросообщение имеет только id и поле content для текста микросообщения (тип - string).1Моделируя более длинные сообщения, для нормального (не микро) блога, следует использовать тип text вместо string. Есть дополнительная сложность: мы хотим связать каждое микросообщение с определенным пользователем; мы выполним это, записывая user_id владельца сообщения. Результаты показаны на рис. 2.3.

Модель данных для микросообщений.

Рис. 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 задачи, запустите

$ bundle exec rake -T

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

Обзор пользователя

При посещении корневого url http://localhost:3000/ мы видим ту же самую дефолтную страницу Rails, что и на рис. 1.3, но при генерировании ресурса Users scaffolding-ом мы также создали большое количество страниц для управления пользователями. Например, страницу для отображения списка всех пользователей на /users и страницу для создания нового пользователя на /users/new. Остальная часть этого раздела посвящена ураганному туру по этим страницам. В процессе этого тура для вас может оказаться полезным иногда поглядывать на таблица 2.1, которая показывает соответствие между страницами и их URL.

Таблица 2.1. Cоответствие между страницами и URL для ресурса Users.
URL Действие Назначение
/users index страница, для отображения списка всех пользователей
/users/1 show страница показывающая пользователя с id
/users/new new страница для создания нового пользователя
/users/1/edit edit страница для редактирования пользователя с id

Мы начнем со страницы показывающей всех пользователей в нашем приложении, называемой index; как вы могли бы ожидать, изначально там нет никаких пользователей ( рис. 2.4).

Начальная index страница для ресурса Users (/users).

Рис. 2.4. Начальная index страница для ресурса Users (/users).

Для того, чтобы создать нового пользователя мы посещаем new страницу, как показано на рис. 2.5 (Поскольку http://localhost:3000 часть адреса явно будет повторятся до тех пор пока мы разрабатываем локально, я буду обычно опускать ее с этого момента.) В Главе 7 это станет страницей регистрации пользователя.

Страница создания нового пользователя (/users/new)

Рис. 2.5. Страница создания нового пользователя (/users/new)

Мы можем создать пользователя, введя значения имени и адреса электронной почты в текстовые поля и нажав затем кнопку Create User. Результат - страница show как видно на рис. 2.6 (Зеленое приглашающее сообщение выполняется с использованием флэш, о котором мы узнаем в Разделе 7.4.2.) Отметим, что в URL /users/1; как вы могли бы подозревать, цифра 1 это просто атрибут id пользователя из рис. 2.2 В Разделе 7.1 эта страница станет профилем пользователя.

Страница показывающая пользователя (/users/1).

Рис. 2.6. Страница показывающая пользователя (/users/1).

Для того, чтобы изменить информацию пользователя, мы посещаем страницу edit ( рис. 2.7). Изменяя информацию о пользователе и нажимая кнопку Update User, мы изменяем информацию о пользователе в демонстрационном приложении ( рис. 2.8). (Как мы увидим в Главе 6, эти данные пользователя хранятся в бэкэнде базы данных.) Мы добавим функции edit/update (редактировать/обновить) пользователя к примеру приложения в Разделе 9.1.

Страница редактирования пользователя (/users/1/edit).

Рис. 2.7. Страница редактирования пользователя (/users/1/edit).
Пользователь с обновленной информацией.

Рис. 2.8. Пользователь с обновленной информацией.

Теперь мы создадим второго пользователя, повторно посетив new страницу и предоставив второй набор информации о пользователе; получившаяся страница index показана на рис. 2.9. Раздел 7.1 преобразует список пользователей в более отполированную страницу для демонстрации всех пользователей.

Страница-список пользователей (/users) с вторым пользователем.

Рис. 2.9. Страница-список пользователей (/users) с вторым пользователем.

Показав как создавать, отображать и редактировать пользователей, мы переходим, наконец, к их удалению ( рис. 2.10). Следует проверить, что клик по ссылке показанной на рис. 2.10 уничтожает второго пользователя, приводя к index странице с одним пользователем. (Если это не работает, убедитесь, что JavaScript включен в вашем браузере; Rails использует JavaScript, чтобы выдать запрос, который должен уничтожить пользователя.) Раздел 9.4 добавляет удаление пользователя к демонстрационному приложению, заботясь об ограничении его использования специальным классом административных пользователей.

Уничтожение пользователя.

Рис. 2.10. Уничтожение пользователя.

MVC в действии

Теперь, когда мы завершили быстрый обзор ресурса Пользователи (Users), давайте исследуем одну его определенную часть в контексте паттерна Модель-Представление-Контроллер (MVC) частично представленного в Разделе 1.2.6. Наша стратегия будет состоять в том, чтобы описать результаты типичного запроса браузера: посещение index страницы пользователя на /users—в терминах MVC ( рис. 2.11).

Подробная схема MVC в Rails.

Рис. 2.11. Подробная схема MVC в Rails.
  1. Браузер выдает запрос на URL /users.
  2. Rails направляет /users к index действию в контроллере Users.
  3. Index действие просит, чтобы модель User получила всех пользователей (User.all).
  4. Модель User вытягивает всех пользователей из базы данных.
  5. Модель User возвращает список пользователей в контроллер.
  6. Контроллер получает пользователей в @users переменной, которую он передает index представлению.
  7. Представление использует Embedded (Встроенный) Ruby, чтобы визуализировать страницу как HTML.
  8. Контроллер возвращает 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.
                    Маршруты Rails, с правилом для ресурса Users. config/routes.rb

Страницы из Раздела 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.

Таблица 2.2. RESTful маршруты предоставленные ресурсом Users в Листинге 2.2.
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
Таблица 2.3. RESTful маршруты, обеспеченные ресурсом Microposts в Листинге 2.7.
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.

Страница для создания микросообщений (/microposts/new).

Рис. 2.12. Страница для создания микросообщений (/microposts/new).

Теперь создайте одно - два микросообщения, удостоверьтесь, чтобы по крайней мере у одного из них был user_id = 1 для сопоставления с id первого пользователя, созданного в Разделе 2.2.1. Результат должен быть похож на рис. 2.13.

Страница списка микросообщений (/microposts).

Рис. 2.13. Страница списка микросообщений (/microposts).

Помещение микро в микросообщения

Каждое микросообщение, достойное своего имени, должно иметь некоторые средства предписания длины сообщения. Реализация этого ограничения в 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.)

Сообщение об ошибке для неудачно созданного микросообщения.

Рис. 2.14. Сообщение об ошибке для неудачно созданного микросообщения.

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) может вывести микросообщения, связанные с каждым пользователем.

Связь между микросообщениями и пользователями.

Рис. 2.15. Связь между микросообщениями и пользователями.

В Главе 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
Иерархия наследования для моделей User и Micropost.

Рис. 2.16. Иерархия наследования для моделей User и Micropost.

Структура наследования для контроллеров лишь немногим более сложная. Сравнивая Листинг 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
Иерархия наследования для контроллеров Users и Microposts.

Рис. 2.17. Иерархия наследования для контроллеров Users и Microposts.

Как и с наследованием моделей, будучи, в конечном счете, наследниками 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"
  • Нет потока (ленты) микросообщений
  • Нет разработки через тестирование
  • Нет реального понимания

Остальная часть этого учебника посвящена усилению достоинств и устранению недостатков.

< Лекция 1 || Лекция 2 || Лекция 3 >
Вадим Обозин
Вадим Обозин

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

Акбар Ахвердов
Акбар Ахвердов
Россия, г. Москва
Артём Зайцев
Артём Зайцев
Украина, ДНР