Ниже приводится небольшая выдержка из нашей книги « Rails: новичок к ниндзя», 3-е издание , написанной Гленном Гудричем и Патриком Ленцем. Это руководство для начинающих по Rails. Члены SitePoint Premium получают доступ к своему членству, или вы можете купить копию в магазинах по всему миру.
Архитектура модель-представление-контроллер (MVC), с которой мы впервые столкнулись в главе 1, не уникальна для Rails. Фактически, он предшествует как Rails, так и языку Ruby на многие годы. Rails, однако, действительно берет идею разделения данных приложения, пользовательского интерфейса и логики управления на совершенно новый уровень.
Давайте посмотрим на концепции, лежащие в основе создания приложения с использованием архитектуры MVC. Как только у нас будет теория, мы увидим, как она переводится в наш код Rails.
MVC в теории
MVC — это образец архитектуры программного приложения. Он разделяет приложение на следующие компоненты:
- Модели для обработки данных и бизнес-логики
- Контроллеры для обработки пользовательского интерфейса и приложения
- Представления для обработки объектов графического интерфейса пользователя и представления
Это разделение приводит к тому, что пользовательские запросы обрабатываются следующим образом:
- Браузер (на клиенте) отправляет запрос на страницу контроллеру на сервере.
- Контроллер получает необходимые данные из модели, чтобы ответить на запрос.
- Контроллер передает полученные данные в представление.
- Представление отображается и отправляется обратно клиенту для отображения в браузере.
Этот процесс показан на рисунке 4-2 ниже.
Разделение программного приложения на эти три отдельных компонента является хорошей идеей по ряду причин, в том числе:
-
улучшенная масштабируемость (способность приложения расти) — например, если ваше приложение начинает испытывать проблемы с производительностью из-за медленного доступа к базе данных, вы можете обновить оборудование, на котором работает база данных, без влияния на другие компоненты
-
простота обслуживания — поскольку компоненты слабо зависят друг от друга, внесение изменений в один (для исправления ошибок или изменения функциональности) не влияет на другой
-
возможность повторного использования — модель может быть повторно использована несколькими представлениями
Если вы изо всех сил пытаетесь понять концепцию MVC, не волнуйтесь. На данный момент важно помнить, что ваше Rails-приложение разделено на три отдельных компонента. Вернитесь к диаграмме MVC, если вам понадобится обратиться к ней позже.
MVC Путь Рельсов
Rails продвигает концепцию, согласно которой модели, представления и контроллеры следует хранить отдельно, храня код для каждого элемента в виде отдельных файлов в отдельных каталогах.
Именно здесь вступает в игру структура каталогов Rails, которую мы создали еще в главе 2. Пришло время немного покопаться в этой структуре. Если вы загляните в каталог app
, показанный на рис. 4-3, вы увидите несколько папок, названия которых могут показаться знакомыми.
Как видите, каждый компонент архитектуры модель-представление-контроллер имеет свое место в подкаталоге app
подкаталогах models
, views
и controllers
. (Мы поговорим об assets
в главе 7, о helpers
в главе 6 и о mailers
в этой главе. jobs
и channels
выходят за рамки этой книги.)
Это разделение продолжается в коде, который содержит сам фреймворк. Классы, которые формируют основную функциональность Rails, находятся в следующих модулях:
-
ActiveRecord
-
ActiveRecord
— это модуль для обработки бизнес-логики и связи с базой данных. Он играет роль модели в нашей архитектуре MVC. Хотя может показаться странным, что вActiveRecord
нет слова «модель» в названии, для этого есть причина: Active Record — это также имя известного шаблона проектирования, который этот компонент реализует для выполнения своей роли. в мире MVC. Кроме того, если бы он называлсяActionModel
, он звучал бы скорее как переплаченная голливудская звезда, чем как программный компонент… -
ActionController
-
ActionController
— это компонент, который обрабатывает запросы браузера и облегчает связь между моделью и представлением. Ваши контроллеры будут наследовать от этого класса. Он является частью библиотекиActionPack
, коллекции компонентов Rails, которую мы подробно рассмотрим в главе 5. -
ActionView
- code> ActionView — это компонент, который обрабатывает представление страниц, возвращаемых клиенту. Представления наследуются от этого класса, который также является частью библиотеки
ActionPack
. - установление соединения с сервером базы данных
- извлечение данных из таблицы
- хранение новых данных в базе данных
- процесс входа на сервер базы данных
- дата расчета
- обработка логических (
true
/false
) данных - эволюция вашей базы данных
- индивидуальные ассоциации
- ассоциации один ко многим
- ассоциации многие ко многим
-
Author
может иметь одинBlog
:class Author < ActiveRecord::Base has_one :weblog end
-
Author
может представить многоStories
:class Author < ActiveRecord::Base has_many :stories end
-
Story
принадлежитAuthor
:class Story < ActiveRecord::Base belongs_to :author end
-
Story
имеет и принадлежит к множеству различных тем:class Story < ActiveRecord::Base has_and_belongs_to_many :topics end class Topic < ActiveRecord::Base has_and_belongs_to_many :stories end
- решить, как обрабатывать конкретный запрос (например, отображать ли полную страницу или только одну ее часть)
- извлечение данных из модели для передачи в представление
- сбор информации из запроса браузера и использование ее для создания или обновления данных в модели
- один контроллер для отображения ссылок на истории, который мы
StoriesController
- другой контроллер для обработки аутентификации пользователя, называемый
SessionsController
- контроллер для отображения пользовательских страниц с именем
UsersController
- контроллер для отображения страниц комментариев с именем
CommentsController
- конечный контроллер для обработки истории голосования, называемый
VotesController
-
Имена классов пишутся в CamelCase (каждое слово начинается с заглавной буквы, без пробелов между словами). На самом деле существует две разновидности CamelCase: одна с заглавной первой буквой (также известная как PascalCase), а другая с заглавной первой буквой. Соглашение Ruby для имен классов требует заглавной первой буквы.
-
Имена файлов пишутся строчными буквами, с подчеркиванием, разделяющим каждое слово.
Давайте подробнее рассмотрим каждый из этих компонентов по очереди.
Модуль ActiveRecord
ActiveRecord
предназначен для обработки всех задач приложения, связанных с базой данных, включая:
ActiveRecord
имеет несколько других хитрых хитростей в рукаве. Давайте посмотрим на некоторые из них сейчас.
База данных Абстракция
ActiveRecord
поставляется с адаптерами базы данных для подключения к SQLite, MySQL и PostgreSQL. Большое количество адаптеров доступно для других популярных пакетов серверов баз данных, таких как Oracle, MongoDB и Microsoft SQL Server, через RubyGems.
Модуль ActiveRecord
основан на концепции абстракции базы данных. Как следует из главы 1, абстракция базы данных — это способ кодирования приложения, чтобы он не зависел от какой-либо одной базы данных. Код, специфичный для конкретного сервера базы данных, надежно скрыт в ActiveRecord
и вызывается по мере необходимости. В результате приложение Rails не связано с каким-либо конкретным программным обеспечением сервера базы данных. Если вам потребуется изменить базовый сервер базы данных позднее, никаких изменений в коде приложения не требуется.
Примечание: жюри на ActiveRecord
Как я уже сказал, ActiveRecord
является реализацией шаблона Active Record. Есть те, кто не согласен с подходом, принятым ActiveRecord
, поэтому вы тоже много об этом услышите. А пока я предлагаю вам узнать, как работает ActiveRecord
, а затем составить свое мнение о реализации по мере изучения.
Вот некоторые примеры кода, которые сильно различаются между поставщиками и которые абстрагируются ActiveRecord
:
Прежде чем я смогу показать вам магию ActiveRecord
в действии, необходимо провести небольшую уборку.
Таблицы базы данных
Таблицы — это контейнеры в реляционной базе данных, которые структурируют наши данные и состоят из строк и столбцов. Строки отображаются на отдельные объекты, а столбцы — на атрибуты этих объектов. Коллекция всех таблиц в базе данных и связи между этими таблицами называются схемой базы данных . Пример таблицы показан на рисунке 4-4.
В Rails именование классов Ruby и таблиц базы данных следует интуитивно понятному шаблону: если у нас есть таблица с именем stories
, состоящая из пяти строк, эта таблица будет хранить данные для пяти объектов Story
. Что хорошо в отображении между классами и таблицами, так это то, что нет необходимости писать код для достижения этого; сопоставление просто происходит, потому что ActiveRecord
выводит имя таблицы из имени класса.
Обратите внимание, что имя нашего класса в Ruby является существительным в единственном числе ( Story
), но имя таблицы во множественном числе ( stories
). Это отношение имеет смысл, если подумать: когда мы ссылаемся на объект Story
в Ruby, мы имеем дело с одной историей. Но таблица SQL содержит множество историй, поэтому ее имя должно быть множественным. Хотя вы можете переопределить эти соглашения — что иногда необходимо при работе с устаревшими базами данных — их гораздо проще придерживаться.
Тесная связь между объектами и таблицами распространяется еще дальше. Если бы в нашей таблице stories
был столбец link
, как в нашем примере на рис. 4-4, данные в этом столбце были бы автоматически сопоставлены с атрибутом link
в объекте Story
. А добавление нового столбца в таблицу приведет к тому, что атрибут с таким же именем станет доступным во всех соответствующих объектах этой таблицы.
Итак, давайте создадим несколько таблиц для хранения историй, которые мы создаем.
В настоящее время мы создадим таблицу с использованием старомодного подхода ввода SQL в консоль SQLite. Вы можете ввести следующие команды SQL, хотя ввод SQL не доставляет удовольствия. Вместо этого я рекомендую вам загрузить следующий скрипт из архива кода, скопировать и вставить его прямо в консоль SQLite, которую вы вызывали с помощью следующей команды в каталоге приложения:
$ sqlite3 db/development.sqlite3
После запуска консоли SQLite вставьте следующее:
CREATE TABLE stories ( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255) DEFAULT NULL, "link" varchar(255) DEFAULT NULL, "created_at" datetime DEFAULT NULL, "updated_at" datetime DEFAULT NULL );
Вам не нужно беспокоиться о запоминании этих команд SQL для использования в ваших собственных проектах; вместо этого, будьте мужественны, зная, что в главе 5 мы рассмотрим миграции. Миграции — это специальные классы Ruby, которые мы можем написать для создания таблиц базы данных для нашего приложения без использования SQL вообще.
Примечание: найдите несколько SQL-умов
Несмотря на то, что Rails абстрагирует SQL, необходимый для создания таблиц и объектов базы данных, вы окажете себе услугу, если познакомитесь с SQL и его синтаксисом. SitePoint опубликовал книгу по изучению SQL, так что проверьте ее.
Использование консоли Rails
Теперь, когда у нас есть таблица stories
, давайте .quit
консоли SQLite (просто введите .quit
) и откроем консоль Rails. Консоль Rails похожа на интерактивную консоль Ruby ( irb
), которую мы использовали в главе 2, но с одним ключевым отличием. В консоли Rails у вас есть доступ ко всем переменным и классам среды, которые доступны вашему приложению во время его работы. Они не доступны в стандартной консоли irb
.
Чтобы войти в консоль Rails, перейдите в папку readit
и введите команду rails console
или rails c
, как показано в следующем коде. Приглашение >>
готово принять ваши команды:
$ cd readit $ rails console Loading development environment (Rails 5.0.0) >>
Сохранение объекта
Чтобы начать использовать ActiveRecord
, просто определите класс, который наследуется от ActiveRecord::Base
. Мы очень кратко коснулись оператора ::
в главе 3, где упоминали, что это был способ вызова методов класса для объекта. Он также может быть использован для ссылки на классы, которые существуют в модуле, что мы и делаем здесь. Вернитесь к разделу об объектно-ориентированном программировании (ООП) в главе 3, если вам нужно освежить информацию о наследовании.
Рассмотрим следующий фрагмент кода:
class Story < ActiveRecord::Base end
Эти две строки кода определяют, казалось бы, пустой класс с именем Story
; однако этот класс далеко не пуст, как мы скоро увидим.
Из консоли Rails давайте создадим этот класс Story
и экземпляр класса под названием story
, введя следующие команды:
>> class Story < ActiveRecord::Base; end => nil >> story = Story.new => #<Story id: nil, name: nil, url: nil, created_at: nil, updated_at: nil> >> story.class => Story(id: integer, name: string, link: string, created_at: datetime, updated_at: datetime)
Как вы можете видеть, синтаксис для создания нового объекта ActiveRecord
идентичен синтаксису, который мы использовали для создания других объектов Ruby в главе 3. На этом этапе мы создали новый объект Story
; однако этот объект существует только в памяти — мы еще не сохранили его в нашей базе данных.
Мы можем подтвердить, что наш объект Story
не был сохранен, проверив возвращаемое значение new_record?
метод:
>> story.new_record? => true
Поскольку объект еще не сохранен, он будет потерян при выходе из консоли Rails. Чтобы сохранить его в базе данных, мы вызываем метод save объекта:
>> story.save => true
Теперь, когда мы сохранили наш объект (возвращаемое значение true
указывает, что метод сохранения был успешным), наша история больше не является новой записью. Ему даже был присвоен уникальный идентификатор:
>> story.new_record? => false >> story.id => 1
Определение отношений между объектами
Помимо основных функциональных возможностей, которые мы только что увидели, ActiveRecord
делает процесс определения отношений (или ассоциаций) между объектами настолько простым, насколько это возможно. Конечно, с некоторыми серверами баз данных возможно определить такие отношения полностью в схеме базы данных. Чтобы пройти ActiveRecord
через его шаги, давайте посмотрим, как он определяет эти отношения внутри Rails.
Отношения объектов могут быть определены различными способами; Основное различие между этими отношениями заключается в количестве записей, указанных в отношениях. Основными типами связи с базой данных являются:
Давайте посмотрим на некоторые примеры каждой из этих ассоциаций. Не стесняйтесь вводить их в консоль Rails, если хотите, ради практики. Помните, что ваши определения классов не будут сохранены, хотя позже я покажу вам, как определить ассоциации в файле.
Предположим, что наше приложение имеет следующие ассоциации:
Вы, несомненно, устали от ввода определений классов в консоль, только чтобы они исчезали, как только вы выходите из консоли. По этой причине мы пока не будем углубляться в связи между нашими объектами — вместо этого мы углубимся в модуль Rails ActiveRecord
более подробно в главе 5.
Библиотека ActionPack
ActionPack
— это имя библиотеки, которая содержит части представления и контроллера архитектуры MVC. В отличие от модуля ActiveRecord
, эти модули имеют более интуитивно понятные названия: ActionController
и ActionView
.
Изучение логики приложения и логики представления в командной строке не имеет большого смысла; В конце концов, представления и контроллеры предназначены для взаимодействия с веб-браузером! Вместо этого я предоставлю краткий обзор компонентов ActionPack
и расскажу о практических ActionPack
в главе 5.
ActionController
(Контроллер)
Контроллер управляет логикой приложения вашей программы, действуя как связующее звено между данными приложения, уровнем представления и веб-браузером. В этой роли контроллер выполняет ряд задач, включая:
Когда мы представили диаграмму MVC на рисунке 4-2 ранее в этой главе, вам, возможно, не пришло в голову, что приложение Rails может состоять из нескольких различных контроллеров. Ну, это может! Каждый контроллер отвечает за определенную часть приложения.
Для нашего приложения Readit мы создадим:
Каждое приложение Rails поставляется с ApplicationController
(который находится в app/controllers/application_controller.rb
), который наследуется от ActionController::Base
. Все наши контроллеры будут наследоваться от ApplicationController
Между этим классом и классом ActionController::Base
будет существовать промежуточный класс; однако это не меняет того факта, что ActionController::Base
является базовым классом, от которого наследуется каждый контроллер. Мы StoriesController
о StoriesController
класса StoriesController
более подробно в главе 5. Но у них будет другая функциональность, которая реализована как методы экземпляра. Вот пример определения класса для класса StoriesController
:
class StoriesController < ApplicationController def index end def show end end
Это простое определение класса устанавливает в StoriesController
два пустых метода: метод index
и метод show
. Мы расширим эти методы в следующих главах.
Каждый контроллер находится в своем собственном файле Ruby (с расширением .rb
), который находится в каталоге app/controllers
. StoriesController
класс StoriesController
который мы только что определили, будет app/controllers/stories_controller.rb
в файле app/controllers/stories_controller.rb
.
Примечание. Соглашения об именах классов и файлов.
Вы уже заметили, что имена классов и файлов следуют различным соглашениям:
Это важная деталь. Если это соглашение не будет соблюдено, Rails будет трудно найти ваши файлы. К счастью, вам не нужно будет называть файлы вручную очень часто, если вообще когда-либо, как вы увидите, когда мы рассмотрим сгенерированный код в главе 5.
ActionView
(вид)
Как обсуждалось ранее, одним из принципов MVC является то, что представление должно содержать только логику представления. Этот принцип гласит, что код в представлении должен выполнять только те действия, которые связаны с отображением страниц в приложении; ни один код в представлении не должен выполнять какую-либо сложную логику приложения, а также сохранять или извлекать какие-либо данные из базы данных. В Rails все, что отправляется в веб-браузер, обрабатывается представлением.
Как и ожидалось, представления хранятся в папке app/views
нашего приложения.
Представление вообще не обязательно должно содержать какой-либо код Ruby — это может быть случай, когда одно из ваших представлений представляет собой простой файл HTML; однако более вероятно, что ваши представления будут содержать комбинацию кода HTML и Ruby, что сделает страницу более динамичной. Код Ruby встроен в HTML с использованием встроенного синтаксиса Ruby (ERb).
ERb позволяет распределять код на стороне сервера по всему HTML-файлу, оборачивая этот код в специальные теги. Например:
<strong><%= 'Hello World from Ruby!' %></strong>
Существует две формы пары тегов ERb: одна включает знак равенства, а другая без него:
-
<%= … %>
- Эта пара тегов предназначена для регулярного вывода. Вывод выражения Ruby между этими тегами будет отображаться в браузере.
-
<% … %>
- Эта пара тегов предназначена для исполнения. Вывод выражения Ruby между этими тегами не будет отображаться в браузере.
Вот пример каждого тега ERb:
<%= 'This line is displayed in the browser' %> <% 'This line executes silently, without displaying any output' %>
Вы можете поместить любой код Ruby — простой или сложный — между этими тегами.
Создание экземпляра вида немного отличается от модели или контроллера. Хотя ActionView::Base
(родительский класс для всех представлений) является одним из базовых классов для представлений в Rails, создание экземпляра представления полностью ActionView
модулем ActionView
. Единственный файл, который разработчик Rails должен изменить, — это шаблон, который содержит код представления для представления. Как вы уже догадались, эти шаблоны хранятся в папке app/views
.
Как и во всем остальном Rails, строгое соглашение касается именования и хранения файлов шаблонов:
- Шаблон имеет однозначное соответствие действию (методу) контроллера. Имя файла шаблона совпадает с именем действия, с которым он сопоставляется.
- Папка, в которой хранится шаблон, названа в честь контроллера.
-
Расширение файла шаблона имеет два аспекта и зависит от его типа и языка, на котором написан шаблон. По умолчанию в Rails есть три типа расширений:
-
html.erb
- Это расширение для стандартных шаблонов HTML, которые содержат теги ERb.
-
xml.builder
- Это расширение используется для шаблонов, которые выводят XML (например, для создания RSS-каналов для вашего приложения).
-
json.builder
- Это расширение используется для шаблонов, которые выводят JSON, который является общей интеграцией данных для API. Мы поговорим больше о JSON в главе 9, посвященной сложным темам.
-
Это соглашение может показаться сложным, но на самом деле оно довольно интуитивно понятно. Например, рассмотрим класс StoriesController
определенный ранее. Вызов метода show
для этого контроллера по умолчанию попытается отобразить шаблон ActionView
который находится в каталоге app/views/stories
ActionView
. Предполагая, что страница была стандартной HTML-страницей (содержащей некоторый код ERb), имя этого шаблона будет show.html.erb
.
Rails также поставляется со специальными шаблонами, такими как макеты и партиалы. Макеты — это шаблоны, которые управляют глобальным макетом приложения, например структуры, которые остаются неизменными между страницами (например, основное меню навигации). Частицы — это специальные подшаблоны (результат разбивки шаблона на отдельные файлы, такие как вторичное навигационное меню или форма), которые могут использоваться в приложении несколько раз. Мы рассмотрим как макеты, так и частичные части в главе 7.
Связь между контроллерами и представлениями происходит через переменные экземпляра, которые заполняются внутри действия контроллера. Давайте расширим наш пример класса StoriesController
чтобы проиллюстрировать этот момент (пока не нужно ничего печатать):
class StoriesController < ActionController::Base def index @variable = 'Value being passed to a view' end end
Как видите, переменной экземпляра @variable
присваивается строковое значение в действии контроллера. Благодаря магии ActionView
, на эту переменную теперь можно ссылаться непосредственно из соответствующего представления, как показано в этом коде:
<p>The instance variable @variable contains: <%= @variable %></p>
Этот подход позволяет выполнять более сложные вычисления вне представления — помните, что он должен содержать только логику представления — и позволяет представлению отображать только конечный результат вычисления.
Rails также предоставляет доступ к специальным контейнерам, таким как params
и хеши session
. Они содержат такую информацию, как текущий запрос страницы и сеанс пользователя. Мы будем использовать эти хеши в следующих главах.