Книга: Программирование на языке Ruby
19.4.2. Nitro и паттерн MVC
19.4.2. Nitro и паттерн MVC
Вставка кода непосредственно в шаблон удобна для экспериментов и перехода со статических страниц на динамические. Но со временем обнаруживается, что тестировать и сопровождать такое приложение становится все сложнее. Паттерн Модель-Вид-Контроллер позволяет упростить жизнь за счет переноса кода в классы Ruby.
Начнем с создания класса контроллера main.rb
. Если в каталоге приложения есть папка src
, Nitro добавит ее в список путей, по которым ищутся страницы. Следуя принятым в Nitro соглашениям, создадим файл <app_root>/src/controller/book.rb
:
class BookController
def index
@author = "Hal Fulton"
@title = "The Ruby Way"
@last_update = Time.new
end
end
Изменим файл index.xhtml
, включив в него ссылки на эти переменные:
<html>
<head>
<title>Nitro!</title>
</head>
<body>
<h1>#{@title}</h1>
<h2>#{@author}</h2>
<p>Page last updated: #{@last_update}</p>
</body>
</html>
Придется также немного подправить файл run.rb
:
require 'nitro'
require 'controller/book'
Nitro.run(BookController)
Перезапустите сервер WEBrick и перезагрузите страницу, чтобы посмотреть на результат.
Отметим несколько вещей. Файлы шаблонов могут остаться в папке public
; класс контроллера не обязан расширять какой-то специальный базовый класс; класс, переданный методу Nitro.run
, автоматически сопоставляется с корневым URL приложения. Все эти аспекты конфигурируемы.
По умолчанию Nitro ищет шаблоны в каталогах template
и public
. Если вы не хотите помещать шаблоны в каталог public
(скажем, потому, что предпочитаете зарезервировать его только для статических HTML-файлов), то можете создать каталог template
и хранить их там. Предполагается, что пути к шаблонам повторяют пути, заданные в URL, относительно корневой папки шаблонов. Наш файл index.xhtml
мог бы храниться как public/index.xhtml
или template/index.xhtml
. Использование каталога public
упрощает переход от статического сайта к динамическому, но лучше организовать раздельное хранение статических файлов и шаблонов.
Классы контроллеров могут отображаться на пути URL с помощью файла Server.map
. Например, наше демонстрационное приложение может содержать статическую главную страницу со списком литературы (в котором на данный момент присутствует всего одна книга), находящуюся совсем по другому пути. Для этого нужно следующим образом изменить файл run.rb
:
require 'nitro'
require 'controller/book'
Nitro::Server.map = { '/books' => BookController }
Nitro.run()
Соответствующий шаблон нужно будет переместить в другое место, которое соответствовало бы новому пути (template/books/index.xhtml
)
Перезапустите сервер и укажите новый URL:
http://127.0.0.1:9999/books
Сейчас самое время создать настоящую страницу сайта, но ради экономии места подавим это желание. Интереснее добавить механизм для запроса данных о нескольких книгах (сколь бы замечательна ни была та, что вы держите в руках). Добавим файл src/model/book.rb
, содержащий описание модели Book
:
class Book
@@items = {}
attr_accessor :title
attr_accessor :author
attr_accessor :update_time
def initialize( values = {} )
@title, @author = values[:title], values[:author]
@update_time = Time.now
end
def save
@@items[author] = self
end
def self.find(author)
@@items[author]
end
end
Теперь контроллер может запрашивать данные у класса Book
. Изменим метод index
:
def index
book = Book.find_by_author("Mark Twain")
@author = book.author
@title = book.title
@last_update = book.update_time
end
А в файл run.rb
вставим ссылку на модель Book
и загрузим какие-нибудь данные:
require 'model/book'
Book.new(:title => "Life on the Mississippi",
:author => "Mark Twain").save
После перезапуска приложения и перезагрузки страницы мы увидим сведения о книге. А если книг больше? Вместо того чтобы «зашивать» в код данные о них, мы можем попросить контроллер выполнить поиск по имени автора. Добавим в него новый метод find
:
def find(author)
book = Book.find_by_author(author)
@author = book.author
@title = book.title
@last_update = book.update_time
end
Это тот же код, что в методе index
(и для создания соответствующего ему шаблона достаточно было бы переименовать index.xhtml
в find.xhtml
), но он принимает один аргумент — имя автора. Хотя мы разрабатываем Web-приложение, класс контроллера мало чем отличается от класса в любом другом приложении, написанном на Ruby. Самое заметное отличие заключается в том, что методы не возвращают значений, но никаких зависимостей от специальных переменных окружения или каких-то особых объектов нет. (Впрочем, имейте в виду, что Nitro все же добавляет разные «приятности» для программирования Web-приложений; для доступа к ним достаточно унаследовать свой класс от Nitro::Controller.
) Например, Nitro обеспечивает «обстраивание», быструю передачу сообщений от одного запроса другому и хитроумный конвейер конструирования страницы, но все это только если вы сами захотите.
Сделаем еще одно замечание по поводу методов контроллера. Методы, предназначенные для обработки запросов на получение страницы, обычно идут в паре с шаблонами, имеющими схожее имя. Для получения конечного результата Nitro объединяет метод и шаблон. Мы видели, что Nitro может работать без контроллеров, ограничиваясь только видами. Но верно и обратное. Метод контроллера может генерировать всю страницу вообще без шаблона. Запросы на получение страниц обрабатываются в Nitro как действия (actions). Действие — это комбинация вида и методов контроллера. Внутри себя Nitro динамически создает методы действия, объединяющие то и другое. Но если какой-то компоненты недостает, ничего страшного не случится. Если у действия нет шаблона, результатом становится значение, возвращаемое методом контроллера.
Например, некоторый URL может отвечать на запрос, возвращая лишь название книги. Для этого достаточно такого метода контроллера BookController
:
def sparse(author)
@context.content_type = 'text/plain'
book = Book.find_by_author(author)
book.title
end
Если метод контроллера возвращает не HTML-документ, то следует изменить заголовок content-type, отравляемый вместе с ответом в результате присваивания @context.content_type=
. (Кстати, даже если парный шаблон существует, его можно не использовать, переопределив метод render_text
.)
Но откуда метод find
или sparse
получает аргумент? По умолчанию Nitro следует привычному для Web-приложений паттерну, согласно которому сегменты пути URL отображаются на контроллеры, методы и аргументы. Перезапустите приложение и введите такой URL: http://127.0.0.1:9999/books/find/Hal%20Fulton.
Обратите внимание, что %20
— это кодированное представление пробела в имени автора. Nitro производит декодирование еще до вызова метода find
.
В общем случае отображение URL выполняется так: /controller/method/arg1
. Дополнительные аргументы можно передать в виде последующих сегментов пути. Впрочем, в Nitro есть механизм настройки маршрутизации, так что вы не обязаны отражать в URL детали реализации.
- История паттерна проектирования MVC
- Семантика паттернов
- 19.4.4. Решение типичных для Web-приложений задач в Nitro
- Исторический очерк о паттерне создания голубого океана
- Паттерны
- Литературная критика и паттерны проектирования
- 19.4. Разработка Web-приложений с помощью Nitro
- Примеры паттернов
- Комбинация покупательских паттернов и паттернов продвижения
- 19.4.3. Nitro и Og
- 19.4.1. Создание простого приложения Nitro
- Функция key в паттернах