Volt — это приятная новая веб-инфраструктура Ruby, целью которой является стирать грань между кодом клиента и сервера. Основная идея этого фреймворка заключается в том, что вы можете написать свой код на стороне клиента (обычно это Javascript) в Ruby, используя Opal, среду выполнения Ruby в Javascript. Кроме того, Volt предоставляет несколько хороших способов передачи данных между клиентской и серверной сторонами. Если вы уже использовали Meteor , Volt — очень похожая идея, но есть много частей Meteor, которых нет у Volt. Я думаю, что у Вольта есть некоторый реальный потенциал. Поскольку веб-приложения становятся все более и более тяжелыми на стороне клиента, становится болезненным переключать ментальный контекст между Javascript и Ruby. Еще сложнее понять, как передавать простые фрагменты данных между клиентом и сервером. Вольт может помочь вам быстро туда добраться.
В этой статье я расскажу, как создать невероятно простое «приложение» для закладок с помощью Volt. Цель этой статьи — познакомить вас с некоторыми основами и понять, как работает разделение между клиентом и сервером в Volt. Давайте доберемся до этого.
Взгляды
Прежде всего, нам нужна копия Вольт:
gem install volt
Если вы уже использовали Rails, то некоторые аргументы командной строки «volt» вам знакомы. Вот как мы создаем новый проект:
volt new volt-example
Вы должны увидеть знакомый (если вы человек из Rails) макет каталога:
Gemfile Gemfile.lock README.md app config config.ru lib spec
Чтобы увидеть, что Volt уже настроил для нас, запустите веб-сервер:
bundle exec volt server
Вы должны увидеть что-то вроде этого:
Хорошо, так где именно эта страница генерируется? Если вы зайдете в каталог приложения, вы должны увидеть «главный» каталог. В отличие от Rails, Volt позволяет разбить ваше веб-приложение на «компоненты» (эта идея похожа на стиль разделения Django). Он автоматически определяет «основной» компонент для вас, так что давайте перейдем к этому каталогу. Теперь вы видите:
assets config controllers models tasks views
Если вы заглядываете в views / main , вы можете видеть представления, связанные с этим компонентом. Макет для представлений, которые являются частью этого компонента, определен в main.html, который выглядит следующим образом:
<:Title> {{ template main_path, "title", {controller_group: 'main'} }} <:Body> <div class="container"> <div class="header"> <ul class="nav nav-pills pull-right"> <:nav href="/" text="Home" /> <:nav href="/about" text="About" /> <:user-templates:menu /> </ul> <h3 class="text-muted">volt-example</h3> </div> <:volt:notices /> {{ template main_path, 'body', {controller_group: 'main'} }} <div class="footer"> <p>© Company 2014</p> </div> </div> <:Nav> <li class="{{ if active_tab? }}active{{ end }}"> <a href="{{ attrs.href }}">{{ attrs.text }}</a> </li>
Странно выглядящий <:body>
, <:title>
и т. д. являются довольно неотъемлемой частью работы представлений в Volt. В основном, если вы видите тег в форме <:tagname>
(обратите внимание на заглавную первую букву), это определение или использование раздела . В макете мы определяем три раздела: <:title>
, <:body>
и <:nav>
которые включают некоторую базовую компоновку, а затем переходят к шаблону действия, которое мы вызвали. В частности, это две строки, которые вызывают шаблоны действий:
{{ template main_path, "title", {controller_group: 'main'} }} ... {{ template main_path, 'body', {controller_group: 'main'} }}
В Volt, где у нас есть код, заключенный в {{
и }}
, он выполняется как Ruby с использованием Opal. В этом случае мы используем некоторые из доступных переменных, чтобы получить шаблон, соответствующий странице, используя этот макет. Хорошо, давайте посмотрим на один из этих шаблонов. Откройте приложение / main / views / main / about.html :
<:Title> About <:Body> <h1>About</h1> <p>About page...</p>
Это выглядит довольно просто! По сути, мы просто заполняем разделы макета. Давайте вернемся к шаблону, чтобы рассмотреть его более подробно. Вы можете заметить, что некоторые теги имеют вид <:tagname>
(обратите внимание на строчную первую букву). Это не разделы; они контроли . В некотором смысле они похожи на помощников по шаблонам в Rails (например, link_to
), поскольку они помогают вам генерировать некоторый HTML. Мы увидим еще несколько таких как мы продолжим.
Маршруты и добавление представления
Хорошо, этого достаточно для времени. Давайте начнем создавать приложение для закладок. Нам понадобится одна страница, на которой пользователь сможет увидеть список закладок и добавить новую. Мы будем разбираться с функциональностью по мере продвижения вперед, но сначала создадим новый вид и маршрут для него. Создать представление просто; просто создайте файл app / main / views / main / bookmarks.html :
<:Title> Bookmarks <:Body> <h1>Bookmarks</h1>
Чтобы добавить маршрут, перейдем к config / rout.rb и следующей строке:
get '/bookmarks', _action: 'bookmarks'
Энн, вот и все! Вам придется перезапустить сервер (автор Volt говорит, что скоро вам это не понадобится), а затем вы должны увидеть красивую страницу по адресу localhost:3000/bookmarks
. Фактически, мы можем сделать действие bookmarks
индексом, изменив последнюю строку config / rout.rb на следующую:
get '/', {_action: 'bookmarks'}
Еще одна приятная вещь о Volt — это то, что он дает вам автоматическую перезагрузку страницы. Если вы попытаетесь изменить содержимое файла app/main/views/main/bookmarks.html
а затем сохраните файл, вы сможете увидеть перезагрузку страницы в браузере.
Основные части
Давайте разберемся, как обрабатывать форму, чтобы мы могли создавать новые закладки. Впоследствии мы хотим красиво отобразить эти закладки на странице. Прежде чем мы это сделаем, невероятно важно понять фундаментальную разницу между Вольт и Рельсы. Volt является своего рода фреймворком MVVM (Model View, View Model), а не фреймворком MVC. С практической точки зрения, представление в Volt призывает контроллер выполнить работу, а не наоборот.
Прежде всего, нам нужна форма. Это довольно просто, просто добавьте следующее в <:body>
из bookmarks.html :
<form e-submit="add_bookmark" role="form"> <div class="form-group"> <label>New Bookmark</label> <input class="form-control" type='text' value="{{ page._new_bookmark._description }}" /> <input class="form-control" type="text" value="{{ page._new_bookmark._url }}" /> <input type="submit" value="Add Bookmark" /> </div> </form>
Хорошо, некоторые части этого кода заурядны, другие нет. У нас есть то, что кажется простым элементом формы. Тем не менее, он имеет некоторые странные атрибуты, например, e-submit
. Здесь e-submit = "add_bookmark"
сообщает Volt, что метод / действие add_bookmark
в контроллере должен вызываться при add_bookmark
данных в этой форме. Две другие странные строки:
<input class="form-control" type="text" value="{{ page._new_bookmark._description }}" /> <input class="form-control" type="text" value="{{ page._new_bookmark._url }}" />
Мы определяем элементы ввода текста, но волшебство происходит в поле value
. В атрибуте value = "..."
мы создаем привязку . Когда значения этих текстовых элементов изменятся, значения переменных page._new_bookmark._description
и page._new_bookmark._url
изменятся. На данный момент эта форма не будет делать ничего важного, так как мы ничего не добавили в add_bookmark
контроллера add_bookmark
. Итак, откройте app/main/controllers/main_controller.rb
и добавьте следующее как метод:
def add_bookmark page._bookmarks << {url: page._new_bookmark._url, description: page._new_bookmark._description} page._new_bookmark._url = 'URL' page._new_bookmark._description = 'Description' end
В Volt эта вещь, называемая page
является моделью . Это не модель в смысле слова Rails — она не обязательно имеет отношение к вашей базе данных. Вместо этого модель — это часть данных, которую могут получить и веб-интерфейс, и серверная часть. Помните, что Volt — это не просто среда Ruby на стороне сервера (такая как Rails или Sinatra). Суть Volt заключается в том, что он позволяет клиентской и серверной сторонам взаимодействовать довольно легко.
Вернуться на page
В форме мы связали переменные page._new_bookmark._description
и page._new_bookmark._url
(обратите внимание на подчеркивание, предшествующее именам переменных; их использование автоматически дает нам пустые модели для ввода данных) с определенными значениями формы. В коде контроллера мы можем взять эти переменные и использовать их! Обновите их в представлении, и изменения этих переменных доступны в контроллере. На первый взгляд это может показаться не таким уж волшебным, но сила идеи заключается в том, что вы можете создать в клиенте любую модель и затем ожидать, что она появится при вызове кода контроллера из представления.
Затем контроллер берет page._bookmarks
и помещает хэш Ruby внутри того, что представляет данные, связанные с одной закладкой в нашем простом веб-приложении. Прежде чем мы сможем увидеть результаты наших усилий, нам нужно немного кода в представлении, которое отображает эти закладки:
<ul class="bookmark-list"> {{ page._bookmarks.each do |bookmark| }} <li> <a href="{{ bookmark._url }}">{{ bookmark._descr }}</a> </li> {{ end }} </ul>
Хорошо, перейдите к «localhost: 3000», вы увидите довольно простую форму, где вы можете ввести описание и URL, нажать Enter и увидеть, что он появляется в списке закладок чуть ниже.
Упорство
Если вы обновите свою страницу после ввода некоторых из этих закладок, вас ждет неприятный сюрприз: они, вероятно, просто исчезли. Нам нужен какой-то способ сохранить эту информацию.
Постоянство в Volt действительно простое, но оно приходит с уловкой: Volt в настоящее время поддерживает MongoDB только в том случае, если вы хотите использовать магию разделения клиент-сервер. Вы можете спросить, почему Volt не поддерживает что-то более «стандартное», такое как Postgres или MySQL. Проблема заключается в том, что перевод объектов JSON в несколько таблиц SQL и из них является головной болью, и в середине нам понадобится ORM. По сути, Вольт является частью лагеря, который считает, что иметь такую трансляционную ORM в веб-фреймворке — плохая идея .
Если у вас нет копии Mongo, ее довольно легко получить. В Mac OS X:
brew install mongodb mongod
Если вы используете Linux, для наших целей хорошо бы использовать версию Mongo, доступную в менеджере пакетов вашего дистрибутива. С Windows я нашел это руководство полезным.
Если вы перейдете в db / config / app.rb , вы найдете следующие строки с комментариями:
config.db_driver = 'mongo' config.db_name = (config.app_name + '_' + Volt.env.to_s) config.db_host = 'localhost' config.db_port = 27017
Просто верни их в существование. Нам нужно создать модель для наших закладок. Поскольку «модели» в Volt на самом деле не связаны с SQL-командой «CREATE TABLE», нам не нужно много указывать. Нам просто нужно создать файл app/main/models/bookmark.rb
и вставить его:
class Bookmark < Volt::Model end
Ваша база данных готова к работе после перезапуска сервера Volt. Нам, однако, нужно немного изменить наш взгляд и контроллер. Помните массив page._bookmarks
мы помещали наши закладки? Чтобы сделать эту синхронизацию с MongoDB, нам просто нужно изменить page._bookmarks
на _bookmarks
. Вольт справится с остальными.
Вот измененные строки:
_bookmarks << {url: page._new_bookmark._url, description: page._new_bookmark._description} {{ _bookmarks.each do |bookmark| }}
Это оно! Обратите внимание, как легко мы получаем данные из браузера в Mongo. Тем не менее, есть несколько проблем с этим подходом. Эта идея синхронизации базы данных между клиентом и сервером вошла в моду с Meteor . Тем не менее, Meteor продвигает соединение клиент-сервер еще дальше с полностью клиентской реализацией Mongo . Кроме того, Meteor позволяет вашему приложению прослушивать изменения в коллекциях на сервере и отправлять их непосредственно в клиентскую базу данных. Этот материал не является частью Volt, насколько можно судить по существующей документации. Таким образом, поддержка постоянства Volt не такая мощная, как у Meteor, когда дело доходит до приложений реального времени с живыми обновлениями данных.
Это упаковка!
Мы только поцарапали саму поверхность Вольт. На самом деле наш менеджер закладок выглядит немного … мусором. Но мы зашли достаточно далеко, чтобы дать вам представление о том, какова основная идея Volt: более тесная и простая интеграция между клиентом и сервером.
Код для этой статьи можно найти на Github .