Сборка приложения Ruby on Rails

Rails – это фреймворк для разработки веб-приложений, написанный на Ruby. Согласно его подходу к разработке приложений, разработчикам лучше всего заранее установить соглашения, чтобы достичь общей цели. Поэтому Rails предлагает соглашения для обработки маршрутизации, stateful данных, управления активами и многого другого – это обеспечивает базовую функциональность, которая нужна большинству веб-приложений.

Rails следует архитектурному шаблону модель-представление-контроллер (MCV), который отделяет логику приложения, расположенную в моделях, от маршрутизации и представления информации о приложении. Эта структура (наряду с другими соглашениями, которые позволяют разработчикам извлекать код в хелперы (helpers) и частичные шаблоны (или партиалы, partials)) предотвращает ненужное дублирование кода.

В этом мануале мы создадим приложение Rails, которое позволит пользователям размещать свою информацию. Это простое приложение станет хорошим стартом для более сложной разработки.

Требования

  • Локальная машина Ubuntu 18.04. Ваша машина разработки должна иметь не-root аккаунт с привилегиями администратора и базовую настройку ufw. Все инструкции можно найти здесь.
  • Предварительно установленные на локальную машину Node.js и npm. В данном мануале мы используем версию Node.js 10.16.3 и npm 6.9.0. Инструкции по установке этих компонентов есть в мануале Установка Node.js в Ubuntu 18.04 (раздел «Установка с помощью PPA»).
  • Ruby, rbenv и Rails на локальной машине. Мы используем версии Ruby 2.5.1, rbenv 1.1.2 и Rails 5.2.0. Инструкции можно найти в разделах 1-4 в мануале Установка Ruby on Rails с помощью rbenv в Ubuntu 18.04.

1: Установка SQLite3

Перед созданием приложения Rails нужно убедиться, что у нас есть БД для хранения пользовательских данных. Rails по умолчанию использует СУБД SQLite – она хорошо подходит для разработки. Поскольку данные приложения не требуют программной расширяемости высокого уровня, SQLite на данном этапе вполне сработает.

Сначала обновите индекс пакетов:

sudo apt update

Затем установите пакеты sqlite3 и libsqlite3-dev:

sudo apt install sqlite3 libsqlite3-dev

Эта команда установит SQLite другие необходимые файлы для разработки.

Запросите версию, чтобы убедиться, что установка прошла успешно:

sqlite3 --version
3.22.0 2018-01-22 18:45:57 0c55d179733b46d8d0ba4d88e01a25e10677046ee3da1d5b1581e86726f2alt1

Установив SQLite, вы можете приступать к разработке приложения.

2: Создание нового проекта Rails

Итак, теперь мы можем создать новый проект Rails и взглянуть на стандартный код, который Rails предоставляет с помощью команды rails new.

Создайте проект по имени sharkapp с помощью следующей команды:

rails new sharkapp

Вы увидите довольно широкий вывод, который сообщит вам, что Rails создает новый проект. Ниже мы выбрали наиболее важные фрагменты этого вывода:

create
. . .
create  Gemfile
. . .
create  app
. . .
create  app/controllers/application_controller.rb
. . .
create  app/models/application_record.rb
. . .
create  app/views/layouts/application.html.erb
. . .
create  config
create  config/routes.rb
create  config/application.rb
. . .
create  config/environments
create  config/environments/development.rb
create  config/environments/production.rb
create  config/environments/test.rb
. . .
create  config/database.yml
create  db
create  db/seeds.rb
. . .
run  bundle install
. . .
Bundle complete! 18 Gemfile dependencies, 78 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
. . .
* bin/rake: Spring inserted
* bin/rails: Spring inserted

Вот о чем говорят эти фрагменты:

  • Gemfile: это файл, в котором перечислены все гемы и зависимости вашего приложения. Гем – это программный пакет Ruby. Файл Gemfile позволяет управлять этими пакетами и зависимостями вашего проекта.
  • app: этот каталог хранит главный код приложения. Он включает в себя модели, контроллеры и представления, активы, хелперы и почтовые программы, которые составляют само приложение. Rails предоставляет шаблон уровня приложения для модели MCV, который можно запустить в таких файлах, как app/models/application_record.rb, app/controllers/application_controller.rb, and app/views/layouts/application.html.erb.
  • config: этот каталог содержит настройки приложения. В config/routes.rb находятся объявления маршрутов вашего приложения. В config/application.rb хранятся общие настройки для компонентов вашего приложения.
  • config/environments: в этом каталоге находятся конфигурации сред. По умолчанию Rails включает в себя три среды: development, production и test (разработка, производство и тестирование).
  • config/database.yml: в этом файле находятся конфигурации базы данных. Файл разбит на четыре раздела: default, development, production и test. Благодаря Gemfile, поставляемому с командой rails new, которая включает гем sqlite3, файл config/database.yml уже имеет параметр adapter со значением sqlite3 (то есть мы можем использовать базу данных SQLite с этим приложением).
  • db: эта папка включает каталог для миграции баз данных под названием migrate, а также файлы schema.rb и seeds.rb files. Файл schema.db содержит данные о вашей БД, а seeds.rb хранит seed данные БД.

В конце Rails запускает команду bundle install для установки зависимостей, перечисленных в Gemfile.

Когда все будет настроено, перейдите в каталог sharkapp:

cd sharkapp

Теперь вы можете запустить сервер Rails, чтобы убедиться, что ваше приложение работает. Это можно сделать с помощью команды rails server. Если вы работаете на локальном компьютере, введите:

rails server

Rails по умолчанию привязан к localhost, поэтому вы можете получить доступ к своему приложению, открыв в браузере locahost:3000. Здесь вы увидите следующую страницу:

Yay! You’re on Rails!

Если вы находитесь на сервере разработки, сначала убедитесь, что брандмауэр пропускает соединения через порт 3000:

sudo ufw allow 3000

Затем запустите сервер с флагом —binding, чтобы привязать IP-адрес вашего сервера:

rails server --binding=your_server_ip

Перейдите по адресу http://your_server_ip:3000 в браузере, где вы увидите приветственное сообщение Rails.

Убедившись, что все работает, вы можете остановить сервер, нажав CTRL+C.

Теперь приложение готово, и вы можете начать сборку шаблона Rails для создания уникального приложения.

3: Скаффолдинг приложения

Чтобы собрать простое приложение с информацией об акулах, нам нужно создать модель для управления данными этого приложения, представление для обработки взаимодействий пользователя с этими данными и контроллер для управления связью между моделью и представлениями. Для создания всех этих компонентов можно использовать команду rails generate scaffold. Она предоставит модель, выполнит миграцию базы данных (чтобы настроить схему базы данных), контроллер, полный набор представлений для управления CRUD-операциями (Create, Read, Update, Delete) приложения, а также частичные шаблоны, хелперы и тесты.

Поскольку команда generate scaffold выполняет за нас очень много работы, мы более подробно рассмотрим ресурсы, которые она создает, чтобы изнутри разобраться, как работает Rails.

Итак, команда generate scaffold включает имя модели и поля, которые должны присутствовать в таблице базы данных. Для управления отношениями между данными приложения, созданными в виде объектов с моделями, и базой данных приложения Rails использует Active Record. Каждая из наших моделей является классом Ruby, а также наследуется от класса ActiveRecord::Base. Это означает, что мы можем работать с классом модели так же, как работали бы с классом Ruby, а также извлекать методы из Active Record. Active Record подтверждает, что каждый класс связан с таблицей в БД, а каждый экземпляр этого класса – со строкой в ​​этой таблице.

Введите следующую команду, чтобы сгенерировать модель Shark, контроллер и связанные представления:

rails generate scaffold Shark name:string facts:text

Строки name:string и facts:text передает Rails информацию о полях, которые мы хотели бы видеть в нашей таблице БД, и о типе данных, которые они должны принимать. И то и другое даст нам возможность ввести свои данные, а text позволит ввести больше символов.

Когда вы введете эту команду, вы снова увидите длинный вывод, который объясняет все, что генерирует Rails. Вот некоторые из наиболее важных фрагментов для нашей установки:

invoke  active_record
create    db/migrate/20190804181822_create_sharks.rb
create    app/models/shark.rb
. . .
invoke  resource_route
route    resources :sharks
invoke  scaffold_controller
create    app/controllers/sharks_controller.rb
invoke    erb
create      app/views/sharks
create      app/views/sharks/index.html.erb
create      app/views/sharks/edit.html.erb
create      app/views/sharks/show.html.erb
create      app/views/sharks/new.html.erb
create      app/views/sharks/_form.html.erb
. . .

Rails создал модель app/models/shark.rb и и миграцию БД: db/migrate/20190804181822_create_sharks.rb. Ваша метка времени в файле миграции будет отличаться от этого примера.

Также Rails создал контроллер app/controllers/sharks_controller.rb и представления, связанные с операциями CRUD, собранные в app/views/sharks. Среди этих представлений есть частичный шаблон _form.html.erb, который содержит код, используемый в представлениях.

Затем Rails добавил новый маршрут resources :sharks в config/routes.rb. Он позволяет маршрутизатору Rails сопоставлять входящие HTTP-запросы с контроллером sharks и связанными с ним представлениями.

Как видите, Rails проделал большую часть работы по созданию кода приложения. Теперь нам стоит взглянуть на некоторые файлы, чтобы понять, что вообще происходит.

Сначала давайте посмотрим на файл контроллера. Это можно сделать с помощью следующей команды:

cat app/controllers/sharks_controller.rb
class SharksController < ApplicationController
before_action :set_shark, only: [:show, :edit, :update, :destroy]
# GET /sharks
# GET /sharks.json
def index
@sharks = Shark.all
end
# GET /sharks/1
# GET /sharks/1.json
def show
end
# GET /sharks/new
def new
@shark = Shark.new
end
# GET /sharks/1/edit
def edit
end
# POST /sharks
# POST /sharks.json
def create
@shark = Shark.new(shark_params)
respond_to do |format|
if @shark.save
format.html { redirect_to @shark, notice: 'Shark was successfully created.' }
format.json { render :show, status: :created, location: @shark }
else
format.html { render :new }
format.json { render json: @shark.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /sharks/1
# PATCH/PUT /sharks/1.json
def update
respond_to do |format|
if @shark.update(shark_params)
format.html { redirect_to @shark, notice: 'Shark was successfully updated.' }
format.json { render :show, status: :ok, location: @shark }
else
format.html { render :edit }
format.json { render json: @shark.errors, status: :unprocessable_entity }
end
end
end
# DELETE /sharks/1
# DELETE /sharks/1.json
def destroy
@shark.destroy
respond_to do |format|
format.html { redirect_to sharks_url, notice: 'Shark was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_shark
@shark = Shark.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def shark_params
params.require(:shark).permit(:name, :facts)
end
end

Контроллер отвечает за управление сбором информации и передачей ее в связанную модель. Также он управляет тем, как она связывается с конкретными представлениями. Контроллер sharks включает в себя ряд методов, которые соответствуют стандартным операциям CRUD. Но методов больше, чем функций CRUD (чтобы обеспечить эффективную обработку ошибок).

Давайте рассмотрим метод create:

. . .
def create
@shark = Shark.new(shark_params)
respond_to do |format|
if @shark.save
format.html { redirect_to @shark, notice: 'Shark was successfully created.' }
format.json { render :show, status: :created, location: @shark }
else
format.html { render :new }
format.json { render json: @shark.errors, status: :unprocessable_entity }
end
end
end
. . .

Если новый экземпляр класса Shark успешно сохранен, redirect_to создаст новый запрос, который затем будет направлен в контроллер. Это будет запрос GET, и он будет обработан методом show, который покажет пользователю только что добавленную информацию.

Если произойдет сбой, Rails снова отобразит шаблон app/views/sharks/new.html.erb, а не отправит другой запрос к маршрутизатору, предоставив пользователям еще один шанс отправить свои данные.

В дополнение к контроллеру sharks, Rails предоставил нам шаблон для представления index, который связан с методом index в контроллере. Мы будем использовать его в качестве корневого представления приложения, поэтому стоит взглянуть на него подробнее.

Введите следующую команду, чтобы вывести файл:

cat app/views/sharks/index.html.erb
<p id="notice"><%= notice %></p>
<h1>Sharks</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Facts</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @sharks.each do |shark| %>
<tr>
<td><%= shark.name %></td>
<td><%= shark.facts %></td>
<td><%= link_to 'Show', shark %></td>
<td><%= link_to 'Edit', edit_shark_path(shark) %></td>
<td><%= link_to 'Destroy', shark, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Shark', new_shark_path %>

Представление index перебирает экземпляры класса Shark, которые были связаны с таблицей sharks в БД. Используя шаблон ERB, представление выводит каждое поле из таблицы, которая связана с отдельным экземпляром: name и facts.

Затем представление использует хелпер link_to для создания гиперссылки, где пользовательский ввод идет в качестве текста для ссылки, а предоставленный путь – в качестве пункта назначения. Сами пути стали возможны благодаря хелперам, к которым мы получили доступ, когда определили маршрут sharks с помощью команды rails generate scaffold.

Кроме представления index, мы также можем изучить представление new, чтобы понять, как Rails использует в представлениях частичные шаблоны. Введите следующую команду, чтобы вывести шаблон app/views/sharks/new.html.erb:

cat app/views/sharks/new.html.erb
<h1>New Shark</h1>
<%= render 'form', shark: @shark %>
<%= link_to 'Back', sharks_path %>

Хотя этот шаблон может выглядеть так, как будто в нем отсутствуют поля ввода для новой записи shark, ссылка на render ‘form’ говорит нам, что шаблон извлекает частичные данные _form.html.erb, которые извлекают код, повторяемый во всех представлениях.

Просмотр этого файла даст нам полное представление о том, как создается новый экземпляр shark:

cat app/views/sharks/_form.html.erb
<%= form_with(model: shark, local: true) do |form| %>
<% if shark.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(shark.errors.count, "error") %> prohibited this shark from being saved:</h2>
<ul>
<% shark.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<div class="field">
<%= form.label :facts %>
<%= form.text_area :facts %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>

Этот шаблон использует хелпер форм form_with. Хелперы форм упрощают создание новых объектов из пользовательского ввода с помощью полей и диапазона конкретных моделей. Здесь form_with принимает model: shark  в качестве аргумента, а новый созданный им объект сборки форм имеет входные поля, соответствующие полям в таблице sharks. Таким образом пользователи получают форму с полями, чтобы ввести название акулы и факты о ней.

Отправка этой формы создаст ответ JSON с пользовательскими данными, к которым остальная часть вашего приложения сможет получить доступ через метод params. Этот метод создает объект ActionController::Parameters с этими данными.

Теперь вы знаете, что делает команда rails generate scaffold. Можно приступать к настройке корневого представления для вашего приложения.

4: Создание корневого представления приложения и тестирование функций

В идеале целевая страница вашего приложения должна отображаться в корне приложения, чтобы пользователи могли сразу понять его цель.

Вы можете сделать это несколькими способами: например, можно создать контроллер Welcome и связанное с ним представление index, которое предоставит пользователям посадочную страницу, откуда можно ссылаться на различные части приложения. Но в нашем случае достаточно будет поместить пользователей в представление index.

Для этого нужно изменить настройки маршрутизации в config/routes.rb и указать корневой каталог приложения.

Откройте файл config/routes.rb в текстовом редакторе:

nano config/routes.rb

Он выглядит так:

Rails.application.routes.draw do
esources :sharks
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

Если не указать что-то более конкретное, страницей приветствия Rails по умолчанию будет стандартное представление по адресу http://localhost:3000 or http://your_server_ip:3000.

Чтобы связать корневое представление приложения с представлением index контроллера sharks, вам необходимо добавить в файл следующую строку:

Rails.application.routes.draw do
resources :sharks
root 'sharks#index'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

Теперь, когда пользователи перейдут в корневой каталог вашего приложения, они увидят полный список акул и получат возможность создать новую запись об акуле, просмотреть существующие записи, а также отредактировать или удалить их.

Сохраните файл и выйдите из редактора. Если вы использовали nano, вы можете нажать CTRL + X, Y, затем ENTER.

Теперь можно запустить миграцию с помощью команды:

rails db:migrate

Вы увидите вывод, подтверждающий миграцию.

Запустите свой сервер Rails еще раз. Если вы работаете локально, введите:

rails s

На сервере разработки нужно ввести:

rails s --binding=your_server_ip

Перейдите на localhost:3000, если вы работаете локально, или на http://your_server_ip:3000, если вы работаете на сервере разработки.

Целевая страница вашего приложения будет выглядеть так:

Sharks
Name Facts
New Shark

Чтобы создать новую запись об акуле, нажмите на ссылку New Shark в нижней части страницы, что проведет вас по маршруту sharks/new:

New Shark
Name:
Facts:
Create Shark
Back

Давайте добавим какую-то информацию, чтобы протестировать наше приложение. Введите Great White в поле Name и Scary в поле Facts.

Нажмите на кнопку Create, чтобы создать запись.

Это направит вас к маршруту show, который устанавливается с помощью метода set_shark благодаря фильтру before_action. Метод получает id только что созданной записи об акуле:

class SharksController < ApplicationController
before_action :set_shark, only: [:show, :edit, :update, :destroy]
. . .
def show
end
. . .
private
# Use callbacks to share common setup or constraints between actions.
def set_shark
@shark = Shark.find(params[:id])
end
. . .

Вы получите:

Shark was successfully created.
Name: Great White
Facts: Scary
Edit | Back

Теперь вы можете протестировать функцию редактирования, нажав кнопку Edit. Это приведет вас к маршруту edit для этой записи:

Editing Shark
Name:
Great White
Facts:
Scary
Update Shark
Show | Back

Измените facts о Great White: введите Large вместо Scary и нажмите Update Shark. Это вернет вас к маршруту show:

Shark was successfully updated.
Name: Great White
Facts: Large
Edit | Back

Нажав кнопку Back, вы перейдете к обновленному представлению index:

Sharks
Name         Facts
Great White  Large   Show   Edit   Destroy
New Shark

Теперь, когда вы проверили основные функциональные возможности вашего приложения, вы можете добавить пару проверок, чтобы сделать приложение более надежным.

5: Добавление проверок

Ваше приложение может принимать входные данные от пользователей. А теперь представьте себе случай: пользователь пытается создать запись об акуле, не добавляя факты, или создает запись, которая уже есть в базе данных. Вы можете создать механизмы для проверки данных до того, как они будут введены в БД, добавив проверки в ваши модели. Поскольку логика приложения находится в его моделях, проверка ввода данных в этом компоненте имеет больше смысла, чем в других компонентах приложения.

Обратите внимание: мы не будем рассматривать написание тестов в этом руководстве. Вы можете узнать больше о тестировании в документации Rails.

Если вы еще не остановили сервер, сделайте это, нажав CTRL+C.

Откройте файл модели shark.rb:

nano app/models/shark.rb

В настоящее время файл сообщает, что класс Shark наследуется от ApplicationRecord, который, в свою очередь, наследуется от ActiveRecord::Base:

class Shark < ApplicationRecord
end

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

class Shark < ApplicationRecord
validates :name, presence: true, uniqueness: true
end

Затем добавьте проверку для поля facts, чтобы убедиться, что оно также заполнено:

class Shark < ApplicationRecord
validates :name, presence: true, uniqueness: true
validates :facts, presence: true
end

Уникальность фактов здесь не так важна, поскольку факты связаны с уникальными записями.

Сохраните и закройте файл.

Запустите свой сервер еще раз с помощью rails s или rails s —binding=your_server_ip, в зависимости от того, работаете ли вы локально или на удаленном сервере.

Перейдите в корневой каталог приложения по адресу http://localhost:3000 или http://your_server_ip:3000.

Нажмите на New Shark. Введите Great White в поле Name и Big Teeth в поле Facts, а затем нажмите Create Shark. Вы должны увидеть следующее предупреждение:

1 error prohibited this shark from being saved:
Name has already been taken

Теперь давайте протестируем, как работает вторая проверка. Нажмите Back, чтобы вернуться на домашнюю страницу, а затем снова выберите New Shark. В новой форме введите имя Tiger Shark и оставьте поле  Facts пустым. Нажмите Create Shark, и это вызовет следующее предупреждение:

1 error prohibited this shark from being saved:
Facts can’t be blank

Теперь в приложении есть методы проверки для обеспечения согласованности данных, сохраненных в БД. Пора обратить внимание на пользователей вашего приложения и определить, кто может изменять данные.

6: Настройка аутентификации

Если вы не хотите, чтобы все пользователи добавляли данные в базу, нужно настроить аутентификацию, и тогда только доверенные пользователи смогут добавлять информацию об акулах. Для этого мы используем метод http_basic_authenticate_with, который создаст комбинацию из имени пользователя и пароля для аутентификации пользователей.

Существует несколько способов аутентификации пользователей с помощью Rails: обычно это делается либо через гемы bcrypt, либо через devise. Однако сейчас мы добавим в контроллер метод, который будет применяться к действиям в приложении. Это удобно, если в будущем вы планируете добавить больше контроллеров.

Остановите сервер, нажав CTRL+C.

Откройте файл, который определяет ваш ApplicationController:

nano app/controllers/application_controller.rb

Внутри вы увидите определение класса ApplicationController, от которого наследуются другие контроллеры в вашем приложении:

class ApplicationController < ActionController::Base
end

Для аутентификации пользователей мы будем использовать имя и пароль, жестко закодированные с помощью метода http_basic_authenticate_with. Добавьте следующий код в файл:

class ApplicationController < ActionController::Base
http_basic_authenticate_with name: '8host', password: 'blog', except: [:index, :show]
end

Итак, мы ввели имя пользователя и пароль, а также ограничили аутентификацию, указав маршруты, где она не требуется: index и show. Еще один способ сделать это – написать only: [:create, :update, :destroy]. Таким образом, все пользователи смогут просматривать все названия, но читать факты только об определенных акулах. Но когда дело доходит до изменения содержимого сайта, пользователям необходимо будет доказать, что у них есть доступ.

В более надежной настройке жестко кодировать значения не нужно, но в целях демонстрации в нашем простом приложении это позволит вам увидеть, как можно включить аутентификацию для маршрутов вашего приложения. Также вы узнаете, как Rails хранит данные сессий по умолчанию в файлах cookie: после того, как вы пройдете аутентификацию, вам больше не потребуется проходить ее снова в текущей сессии.

Сохраните и закройте app/controllers/application_controller.rb, когда закончите редактирование. Теперь вы можете проверить аутентификацию в действии.

Запустите сервер с помощью команд  rails s или rails s —binding=your_server_ip и перейдите к своему приложению по адресу http://localhost:3000 или http://your_server_ip:3000.

На посадочной странице нажмите кнопку New Shark. Это вызовет окно аутентификации.

Если вы введете комбинацию из имени пользователя и пароля, которую вы добавили в app/controllers/application_controller.rb, вы сможете создать новую запись.

Теперь у вас есть работающее приложение, которое может принимать пользовательский ввод, обрабатывать ошибки и поддерживает аутентификацию.

Заключение

Приложение Rails, которое вы создали в этом мануале, является хорошей базой для дальнейшего обучения и разработки. Если вы хотите узнать об экосистеме Rails больше, начните с документации проекта.

Если вы хотите изучить различные варианты баз данных, вы также можете почитать мануал Настройка приложения Ruby on Rails на PostgreSQL в Ubuntu 18.04 и настроить в приложении PostgreSQL вместо SQLite.

Tags: , , , ,