Створення простого застосунку з архітектурою MVC за допомогою Sinatra та Active Record

Цього тижня я працював над створенням простої програми на основі Sinatra, яка також використовує Active Record. Я вирішив створити додаток під назвою PetCareTodoApp, який допомагає користувачам вести список завдань для своїх домашніх улюбленців.

Я використав gem corneal для налаштування файлової структури додатка, однак я вирішив самостійно написати моделі, контролери (application controllers) та міграції, щоб переконатися, що я повністю розумію свою схему та зв’язки між моделями. Мій додаток базувався на архітектурі MVC (Model, View, Controller). У цій архітектурі моделі (models) обробляють всю логіку програми, часто представляючи її компоненти — у моєму випадку це були Користувачі (Users), Домашні улюбленці (Pets) і Завдання (Todos). Представлення (views) відповідають за інтерфейс користувача, усе, що бачить і з чим взаємодіє користувач, — у моєму випадку це були файли erb. Контролери (controllers) забезпечують взаємодію між моделями та представленнями, передаючи дані між браузером і додатком.

Коли я налаштував файлову структуру, настав час створити схему (schema) та моделі. Кожна з моїх моделей відповідала таблиці в базі даних. У мене були моделі Користувачів (User), Домашніх улюбленців (Pet), Завдань (Todo) і PetTodo (для зв’язку). Модель Користувача виглядала так:

class User < ActiveRecord::Basehas_many :petshas_many :todosvalidates :email, :presence => true, :uniqueness => truehas_secure_passwordend

Ця модель представляла користувача, який зареєструвався у додатку. Ви бачите, що у користувача є багато домашніх улюбленців (hasmany :pets) і завдань (hasmany :todos). Моделі для цих сутностей повинні відображати цей зв’язок. Модель Домашніх улюбленців виглядала так:

class Pet < ActiveRecord::Basevalidates :name, :presence => :truebelongs_to :userhas_many :pet_todoshas_many :todos, through: :pet_todosend

Тут ви бачите belongs_to :user, що є другою частиною зв’язку hasmany/belongsto. Також видно, що у моделі багато завдань (hasmany :todos) через таблицю pettodos. Таблиця pet_todos є таблицею зв’язку, яка містить лише зовнішні ключі (foreign keys) для домашніх улюбленців і завдань. Це було необхідно, оскільки я вирішив зробити зв’язок "багато до багатьох" між домашніми улюбленцями та завданнями. Я хотів, щоб у одного домашнього улюбленця могло бути багато завдань, а одне завдання могло належати кільком домашнім улюбленцям. Модель Завдань виглядала так:

class Todo < ActiveRecord::Basehas_many :pet_todoshas_many :pets, through: :pet_todosbelongs_to :userend

Тут ви бачите іншу сторону зв’язку hasmany/belongsto з Користувачем, а також іншу частину зв’язку "багато до багатьох" із Домашніми улюбленцями. Нарешті, ось модель для PetTodos:

class PetTodo < ActiveRecord::Basebelongs_to :petbelongs_to :todoend

Як згадувалося раніше, таблиця pettodos і відповідно модель PetTodo існують лише для представлення зв’язку між домашніми улюбленцями та завданнями. Це через те, що в таблиці немає стовпців із типом "hasmany"; тільки той, хто належить до чогось (belongs_to), відповідає за відстеження цього зв’язку. Тому, якщо у вас є зв’язок "багато до багатьох", вам потрібна таблиця зв’язку для його відстеження.

Ось приклад мого файлу schema.rb, який представляє таблиці, пов’язані з цими моделями:

ActiveRecord::Schema.define(version: 20200605165927) docreate_table "pet_todos", force: :cascade do |t|t.integer "pet_id"t.integer "todo_id"endcreate_table "pets", force: :cascade do |t|t.integer "user_id"t.string "name"t.string "species"t.string "breed"endcreate_table "todos", force: :cascade do |t|t.string "name"t.text "description"t.datetime "datetime"t.boolean "complete", default: falset.integer "user_id"endcreate_table "users", force: :cascade do |t|t.string "password_digest"t.string "name"t.string "email"endend

Ви можете побачити зв’язок belongs_to у будь-якому місці, де є стовпець, який є зовнішнім ключем у таблиці.
ЗАУВАЖЕННЯ: Файл schema.rb — це лише відображення вашої бази даних, структура вашої бази даних та таблиць насправді налаштовується через міграції (Active Record migrations). Будь-які зміни, внесені до цього файлу, не відобразяться у вашій базі даних.

Тепер, коли ми розглянули моделі (Models), настав час пояснити представлення (Views). Оскільки я створював додаток на основі Sinatra, мої представлення були написані у форматі erb, що означає "embedded ruby" (вбудований Ruby). Тобто я писав HTML-код і додавав теги erb, коли потрібно було обробити логіку Ruby. У представленнях потрібно розміщувати все, що ви хочете показати користувачеві або дати йому змогу взаємодіяти. У моєму додатку було багато форм, які користувач мав заповнювати, щоб я міг отримати інформацію для створення нових екземплярів Користувачів, Домашніх улюбленців, Завдань тощо. Ці форми створювалися за допомогою файлів .erb у папці views. Наприклад, (надзвичайно спрощене) представлення для головної сторінки сайту може виглядати так:


Hello!

Please Log In or Sign Up  

Це б показало посилання користувачеві, коли він заходить на головну сторінку сайту. Далі він міг би натиснути на одне з цих посилань, що перенаправило б його до маршруту (route) у вашому контролері (controller), який, у свою чергу, ймовірно, відобразив би представлення форми входу або реєстрації. Якщо дотримуватися конвенцій, то для кожної з ваших моделей (принаймні тих, які потребують представлень) слід створити окремі папки для представлень. Мій додаток дотримувався 7 RESTful маршрутів (routes) для домашніх улюбленців та завдань, тому папки представлень pets і todos містили:

  • Файл index.erb, який показував користувачеві всіх його домашніх улюбленців/завдання.
  • Файл show.erb, який показував користувачеві один екземпляр домашнього улюбленця/завдання.
  • Файл new.erb, який відображав форму для створення нового екземпляра домашнього улюбленця/завдання.
  • Файл edit.erb, який відображав форму для редагування екземпляра домашнього улюбленця/завдання.

Відповідальність за визначення, коли і яке представлення потрібно відобразити, лежить на контролері (controller).

Отже, ми розглянули моделі (Models) і представлення (Views). Тепер поговоримо про контролери (Controllers). У файлі config.ru вашого додатка слід запускати один контролер. Якщо ви використали gem corneal для налаштування, то у вас уже має бути створений Application Controller, і у файлі config.ru буде записано ‘run ApplicationController.rb’. За конвенцією, краще створювати окремі контролери для кожної з ваших моделей. Для кожного з них у файлі config.ru потрібно буде додати ‘use [modelcontrollername].rb’.

У Ruby ваш контролер (Controller) складається з набору маршрутів (routes), які обробляють запити з браузера. Наприклад, головна сторінка вашого сайту чи додатка буде маршрутом '/'. У вашому Application Controller це може виглядати так:

get '/' do  
end

Між do та end потрібно розміщувати логіку, яка виконується, коли користувач заходить на головну сторінку. У вашому додатку ви спрямовуватимете користувача до різних маршрутів у ваших контролерах, і у цих маршрутах (get/post/patch/put/delete) ви будете додавати логіку, яка визначає, що має відбуватися, коли користувач потрапляє на цей маршрут. Наприклад, якщо у вас є маршрут get '/signup', ви, можливо, захочете відобразити представлення з формою, яку користувач має заповнити для реєстрації. Після заповнення форми його можуть перенаправити до маршруту post '/users', який оброблятиме логіку створення нового користувача, використовуючи логіку з моделі (model) та інформацію з форми, а потім перенаправлятиме його до сторінки профілю.

Ви можете побачити, як ваші контролери діють як посередники між представленнями (views) та моделями (models).

ЗАУВАЖЕННЯ: Rack — це інтерфейс, який дозволяє вашій програмі взаємодіяти з веб-серверами, і він підтримує лише HTTP-запити GET та POST. Для використання PUT, PATCH або DELETE потрібно додати middleware під назвою Rack::MethodOverride.

Отже, це був базовий огляд створення веб-додатка на основі Sinatra, який використовує архітектуру MVC.
Хоча gem corneal дозволяє швидше створювати моделі (models) та контролери (controllers), я радий, що вирішив написати все самостійно, адже тепер я краще розумію, як усі частини мого додатка працюють разом. Проте, коли я почну працювати з Rails, я впевнений, що багато завдань, які цей фреймворк спрощує, дозволять мені зосередитися на створенні більш складних додатків. Я сподіваюся, що зможу глибше зануритися у створення привабливого та ще більш функціонального додатка для свого наступного проєкту.

Перекладено з: Creating a Simple App with a MVC Framework with Sinatra and Active Record

Leave a Reply

Your email address will not be published. Required fields are marked *