Ajax on Rails - почему Ajax так хорошо работает с Ruby

Об Ajax (технологии создания высоко интерактивных веб-страниц) говорится очень много. Интегрированная среда Ruby on Rails тоже процветает, частично из-за тесной интеграции с Ajax. Узнайте, что делает комбинацию Ajax on Rails такой мощной.

определение Ajax

Ajax обозначает Asynchronous JavaScript + XML. Этот термин начал использовать в 2005 году Джесс Джеймс Гаррет (Jesse James Garrett), разработчик информационных систем, для описания технологии, применявшейся в некоторых областях почти десятилетие (см. раздел "Ресурсы"). После этого использование Ajax стало повсеместным, появились библиотеки, популярные веб-сайты и соответствующая литература.

Технология Ajax переопределила базовую модель использования браузеров, заключавшуюся в единовременной визуализации страницы. Ajax дала возможность браузеру взаимодействовать с сервером в промежутках между обновлениями страницы. Выигрыш – большее удобство для пользователя, хотя и за счет повышения сложности. Ajax передает XML между сервером и клиентом, используя клиентские JavaScript-библиотеки. Ajax-разработчики могут посылать асинхронные запросы от клиента в любое время, а пользователь может продолжать свою работу в браузере во время обработки запросов. Вот последовательность выполнения Ajax-запроса:

1. Событие (например, нажатие пользователем кнопки мыши или срабатывание программного таймера) инициирует JavaScript-функцию.

2. JavaScript-функция создает запрос для части страницы, а не для всей страницы. Затем JavaScript передает этот запрос на веб-сервер по протоколу HTTP.

3. Этот HTTP-запрос активизирует сценарий на сервере, например, метод Rails-контроллера или Java-сервлет.

4. Серверный сценарий создает XML-документ и возвращает его клиенту.

5. Получив результат, клиент асинхронно создает, обновляет или удаляет часть веб-страницы, например, элемент списка, тег div или изображение. В любом Ajax-приложении используется подход, аналогичный описанному. Например, рассмотрим приложение, позволяющее изменять описания слов в словаре. Старые приложения должны были бы запросить новую страницу для редактирования описания. Ajax позволяет выполнить редактирование на месте, заменяя текст описания полем ввода, а затем заменяя эту форму новым описанием.

Компонентами Ajax являются:

- клиентская JavaScript-библиотека для поддержки асинхронных запросов;

- серверная библиотека для обслуживания входящих запросов и формирования XML-ответов;

- клиентская библиотека для работы с полученным XML;

- библиотека, называемая объектной моделью документа (Document Object Model, DOM), позволяющая обновлять существующую веб-страницу; - вспомогательные подпрограммы для работы с неизбежными проблемами интеграции и UI.

Такая модель событие/запрос/ответ/замещение является базовой моделью большинства Ajax-приложений, хотя новичков в Ajax удивит количество доступных библиотек и большие различия между ними. Появилось много интегрированных Ajax-систем с частым пересечением функциональности, среди которых нет явных победителей. Только в Java используются десятки библиотек, включая Echo, Dojo, DWR, Google Web Toolkit (GWT), Java Web Parts, AjaxAnywhere, AjaxTags, Scriptaculous и Prototype. Эти системы применяют диаметрально различные подходы. Некоторые, например GWT, стремятся полностью скрыть JavaScript, предоставляя Java-библиотеки, генерирующие JavaScript-код. Другие стремятся облегчить использование JavaScript. Некоторые являются полными и универсальными (например, Dom4J), а другие просто хорошо решают отдельную проблему. Как и в случае со многими другими популярными новыми технологиями, на этой шахматной доске решений на первых порах ориентироваться тяжело, а средства отладки, UI-приемы (например, кнопка "Назад") и оптимальные методики разработки будут материализовываться медленно. Сила Ajax-библиотек на платформе Java заключается в их разнообразии. Это также и их слабость, поскольку такое разнообразие порождает неуверенность, проблемы интеграции и сложность. Среда Ruby on Rails значительно отличается от других по двум причинам. Во-первых, Ruby on Rails использует единую платформу веб-разработки - Ruby on Rails. Во-вторых, до сих пор Ajax-разработка на Rails по большей части концентрировалась на двух основных интегрированных средах - Scriptaculous и Prototype. Подход Rails, использующий генерирование кода времени исполнения и пользовательские теги, избавляет от сложностей JavaScript. Настало время увидеть это собственными глазами.

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

простое Rails-приложение без Ajax

Для использования Rails и Ajax мы создадим пустой проект и сгенерируем контроллер с двумя методами. Один управляет простой страницей, второй формирует Ajax-ответ. Введите следующие команды:

rails ajax
cd ajax
script/generate controller ajax show time


В первой и второй строке генерируется Rails-проект и выполняется переход в новый каталог. В третьей строке генерируется контроллер под названием ajax и виды (views) для двух действий: show (отобразить) и time (время). Вот код контроллера:

class AjaxController < ApplicationController

def show
end

def time
end
end


Сначала мы создадим два простых вида без Ajax, а затем соединим их вместе при помощи Ajax. Отредактируйте вид show.rhtml в app/views/ajax так, как показано ниже:

<h1>Ajax show</h1>
Click this link to show the current <%= link_to "time", :action => "time" %>.


Данный код не использует Ajax-поддержку, но мы все равно исследуем его. Прежде всего, взгляните на контроллер. Два пустых метода контроллера обрабатывают входящие HTTP-запросы. Если вид не визуализируется явно (используя метод render), Rails визуализирует вид с тем же названием, что и название метода. Поскольку библиотеки Scriptaculous и Prototype тоже используют HTTP, Rails не должен делать каких-либо различий между стандартными HTTP-методами и Ajax-методами.
Теперь обратим внимание на вид во втором листинге. Большая часть кода - это простой HTML, за исключением link_to во второй строке: <%= link_to "time", :action => "time" %>.

Ruby заменяет код между <%=h и %> значениями выражения. В данном
случае метод link-to генерирует простую HTML-ссылку. Эту ссылку можно увидеть, выполняя код. Запустите сервер при помощи команды script/server и укажите в браузере ссылку http://localhost:3000/ajax/show. Отобразится вид, показанный на рисунке 1.



Рисунок 1. Простой пользовательский интерфейс без Ajax.

В браузере выберите пункт меню для просмотра исходного кода страницы. Вы увидите следующий код:

<h1>Ajax show</h1>
Click this link to show the current <a href="/ajax/time">time</a>.


Обратите внимание на код ссылки. Шаблон защищает Rails-пользователя от громоздкого и подверженного ошибкам HTML-синтаксиса (Ajax работает аналогичным образом: вы используете вспомогательные методы для вставки JavaScript-кода, управляющего вместо вас удаленными запросами и HTML- замещениями). При нажатии ссылки отобразится вид по умолчанию для метода time, но мы его еще не реализовали. Замените метод time в
app/controllers/ajax_controller.rb приведенным ниже. Для простоты я визуализирую вид прямо из контроллера. Позже я подчищу код.

def time
render_text "The current time is #{Time.now.to_s}"
end


Теперь при нажатии ссылки отобразится вид показанный на рисунке 2.



Рисунок 2. Вид без Ajax.

Сразу видна проблема в этом UI. Два вида не размещены на отдельных страницах. Приложение представляет простую концепцию: нажмите ссылку для отображения времени. Для периодического обновления времени нужно каждый раз нажимать ссылку и затем кнопку "Назад". Можно решить эту проблему, поместив ссылку и время на одну и ту же страницу. Но если страница show становится очень большой или сложной, повторное отображение всей страницы занимает много времени.

добавляем Ajax

Ajax позволяет обновлять только фрагмент веб-страницы. Rails-библиотеки делают за вас основную часть работы. Чтобы добавить Ajax к данному приложению, нужно выполнить следующие действия:

- настроить Rails на использование JavaScript;

- изменить ссылку time для отправки JavaScript Ajax-запроса вместо простой визуализации HTML-ссылки;

- указать HTML-фрагмент для обновления;

- подготовить место для обновленного HTML-содержимого;

- создать метод контроллера и, возможно, вид для визуализации Ajax-ответа.

Для начала измените код в app/views/ajax/show.rhtml:

<%= javascript_include_tag :defaults %>
<h1>Ajax show</h1>
Click this link to show the current
<%= link_to_remote "time",
:update => 'time_div',
:url => {:action => "time"} %>.<br/>
<div id='time_div'>
</div>


Я сделал несколько изменений. Во-первых, чтобы пример работал, я просто подключил необходимые JavaScript-библиотеки непосредственно к виду. Обычно (если имеется несколько видов и для устранения дублирования) я включаю JavaScript-файлы в общий Rails-компонент, например, Rails-схему (layout). В этом примере используется только один вид, поэтому я многое упростил.

Во-вторых, я изменил тег link для использования link_to_remote. Скоро будет видно, что делает эта ссылка. Обратите внимание на три параметра: Текст ссылки, который не изменился.

Параметр :update. Если такой синтаксис прежде не встречался, думайте о :update => 'time_div' как о поименованном параметре, где :update - имя, а update_div - значение. Этот код указывает Prototype-библиотеке, что данная ссылка будет обновлять HTML-элемент с именем time_div.

:url => {:action => "time"} - указывает URL, который будет активизировать ссылка. :url принимает значение хэш-карты. На практике хэш-карта имеет только один элемент для действия контроллера - :time. Теоретически URL мог бы также содержать название контроллера и все необязательные параметры, которые могут понадобиться контроллеру.

В листинге 5 имеется также пустой элемент div, который Rails будет обновлять текущим временем.

В браузере загрузите страницу http://localhost:3000/ajax/show. Нажмите кнопку мыши на ссылке. Должна отобразиться страница, показанная на рисунке 3:



Рисунок 3. Вид с Ajax.

Взгляните на исходный код Веб-страницы, чтобы лучше представить, что происходит. Код страницы показан в листинге 6:

Листинг 6. Результат шаблона show с использованием Ajax


<script src="/javascripts/prototype.js?1159113688" type="text/javascript"></script>
<script src="/javascripts/effects.js?1159113688" type="text/javascript"></script>
<script src="/javascripts/dragdrop.js?1159113688" type="text/javascript"></script>
<script src="/javascripts/controls.js?1159113688" type="text/javascript"></script>
<script src="/javascripts/application.js?1159113688" type="text/javascript"></script>
<h1>Ajax show</h1>
Click this link to show the current
<a href="#" onclick="new Ajax.Updater(
'time_div', '/ajax/time', {asynchronous:true, evalScripts:true});
return false;">time</a>.<br/>
<div id='time_div'>

</div>



Обратите внимание на список подключенных JavaScript-файлов. Вспомогательная команда Rails (include_javascript_tags :defaults) создает этот список за вас. Далее, вместо HTML-ссылки указан вызов JavaScript-функции для создания нового объекта Ajax.Updater. Параметр asynchronous установлен в значение true, как и можно было ожидать. Наконец, внутри HTML-тегов div ничего нет, поскольку на первоначальной странице там ничего и не было.



В начало



использование других функций Ajax

Ajax может генерировать разнообразные виды поведения, иногда даже несколько неожиданные. Допустим, пользователь не заметил ссылку на обновленное время. Параметр link_to_remote позволяет легко применить специальные эффекты, так что пользователь заметит результат. Давайте используем некоторые эффекты. Измените link_to_remote в show.rhtml по примеру листинга 7:

Листинг 7. Добавляем эффекты

<%= link_to_remote "time",
:update => 'time_div',
:url => {:action => "time"},
:complete => "new Effect.Highlight('time_div')" %>


Лучшие Ajax-эффекты привлекают внимание, но оно не вечно. Целью должно быть отображение изменений без прерывания работы пользователей. Технические приемы (как данное затенение или содержимое, которое скользит или медленно проявляется) не должны постоянно отвлекать внимание.

Ссылка - это пока единственный триггер, который вы видели. Ajax может предложить несколько вариантов: одни управляются пользователями, другие - программными событиями. Такие элементы, как часы, не обязательно должны требовать вмешательства пользователя. Таймер в Ajax можно обновлять периодически при помощи метода periodically_call_remote. Измените код в show.rhtml так, как показано в листинге 8:

Листинг 8. Периодический вызов удаленного метода

<%= javascript_include_tag :defaults %>
<h1>Ajax show</h1>
<%= periodically_call_remote :update => 'time_div',
:url => {:action => "time"},
:frequency => 1.0 %>
<div id='time_div'>
</div>


На рисунке 4 показан результат: часы, обновляющие свои показания ежесекундно без вмешательства пользователя:



Рисунок 4. Периодически обновляющиеся часы с Ajax.

Хотя код в Rails-виде похож на версию без Ajax, получаемый исходный код страницы значительно отличается: в этой версии используется JavaScript вместо HTML. Код листинга 9 можно увидеть при просмотре исходного кода страницы в браузере:

Листинг 9. Исходный код для periodically_call_remote

<script src="/javascripts/prototype.js?1159113688" type="text/javascript"></script>
<script src="/javascripts/effects.js?1159113688" type="text/javascript"></script>
<script src="/javascripts/dragdrop.js?1159113688" type="text/javascript"></script>
<script src="/javascripts/controls.js?1159113688" type="text/javascript"></script>
<script src="/javascripts/application.js?1159113688" type="text/javascript"></script>
<h1>Ajax show</h1>
<script type="text/javascript">
//<![CDATA[
new PeriodicalExecuter(function() {new Ajax.Updater(
'time_div', '/ajax/time', {asynchronous:true, evalScripts:true})}, 1.0)
//]]>
</script>
<div id='time_div'>
</div>


Обратите особое внимание на то, что здесь происходит. Вместо работы с небольшими фрагментами JavaScript используется более высокий уровень абстракции, и система шаблонов Ruby on Rails делает использование модели довольно естественным.

Как упоминалось ранее, я визуализирую текст прямо из контроллера. Это упрощение облегчает начало работы, но не может использоваться постоянно. Мои виды должны обрабатывать представление, а контроллеры должны группировать и передавать (маршализировать) данные между видом и моделью. Такая технология проектирования, называемая модель-представление-контроллер (model-view-controller - MVC), облегчает изолирование изменений представления или модели. Для использования в этом приложении MVC я могу просто разрешить Rails визуализировать вид по умолчанию, и содержимое, как и ожидается, будет заменять старое содержимое time-div. Измените метод time в app/controllers/ajax_controller.rb, как показано в листинге 10:

Листинг 10. Рефакторинг

def time
@time = Time.now
end


Измените вид в app/views/ajax/time.rhtml, как показано в листинге 11:

Листинг 11. Использование вида для визуализации Ajax-содержимого

<p>The current time is <%=h @time %></p>


Метод контроллера создает экземпляр переменной @time. Поскольку контроллер ничего явно не визуализирует, Rails визуализирует вид time.rhtml. Такая модель использования полностью аналогична модели визуализации вида без Ajax. Опять же, видно, что Rails изолирует разработчиков от различий между приложениями, использующими и не использующими Ajax. Модель использования в традиционных Веб-приложениях и Ajax-приложениях поразительно похожа. Затраты настолько низкие, что все больше Rails-приложений используют преимущества Ajax.



В начало



другие способы использования Ajax в Rails

Технология Ajax в Rails используется широко и глубоко - намного глубже, чем я могу описать в одной статье или даже в серии статей. Ниже приведены некоторые примеры использования Ajax в Rails, показывающие, насколько хорошо Ajax-поддержка в Rails решает некоторые другие проблемы. Подтверждение удаленных форм. Ajax-формы в Rails работают аналогично традиционным формам, за исключением того, что нужно подтверждать их асинхронно. Это означает, что вспомогательные теги Forms в Rails должны разрешать указывать URL для обновления и выполнять визуальные эффекты, точно так же, как в случае с link_to_remote. Rails submit_to_remote расширяет Rails submit так же, как link-to-remote расширяет link_to.

Выполнение сложных сценариев. Rails-разработчикам часто нужно выполнить сложные сценарии, делающие нечто большее, чем просто обновление одного div и визуализация эффектов. Для этой цели Rails предоставляет JavaScript-шаблоны. С JavaScript-шаблоном можно выполнить произвольный JavaScript- сценарий как результат Ajax-запроса. Некоторые часто применяемые для этого шаблоны, называемые RJS-шаблонами, обновляют больше одного div, проверяют правильность заполнения формы и управляют сценариями Ajax-ошибок.

Завершение. Часто хочется предложить пользователям функциональность автоматического завершения на основе элементов, содержащихся в базе данных. Например, если пользователь набирает Bru, хотелось бы, чтобы приложение нашло в базе данных значение "Bruce Tate". Можно использовать Ajax для периодической проверки изменений в поле и передачи пользователю предложений для завершения набираемого текста.

Динамическое создание сложных форм. В бизнес-деятельности часто требуется видеть часть заполненной формы до того, как станет известно, какие поля должен заполнить пользователь. Например, налоговая форма 1040EZ не применяется, если пользователь имеет определенные типы доходов или расходов. С Ajax можно легко обновлять форму в процессе ее заполнения.

Перетащить и оставить (drag and drop). В Rails можно быстро реализовать поддержку техники "перетащить и оставить" с намного меньшими усилиями, чем в большинстве других интегрированных систем.



В начало



заключение

У Ajax есть свои проблемы. Когда все идет хорошо, полные возможности этой технологии могут поразить воображение.Когда дела плохи, обнаруживается, что отладка переходит совершенно в новое измерение, а технологии отладки и инструментальные средства для нее все еще не соответствуют уровню, имеющемуся в других языках программирования. Ruby on Rails имеет одно основное преимущество - простоту. Rails-средства надстройки совместно с фантастической поддержкой сообщества пользователей облегчают погружение в этот новый мир при очень небольших начальных инвестициях. Но Rails-поддержка еще не достаточна. Две интегрированные системы, которые не полностью охватывают все сферы Ajax, удовлетворят не каждого пользователя.

Существует много интегрированных Ajax-систем и подходов для языка программирования Java, из которых есть что выбрать. Можно обнаружить значительную гибкость и фантастическую поддержку. Но за гибкость надо платить. Ведь потребуется не только надежная Ajax-среда, но и среда Веб- разработки. Например, интеграция среды JSF кардинально отличается от интеграции Struts. Новые технологии часто требуют упрощения. Ajax с Rails может решить проблемы, относящиеся к сложности применения Ajax в UI, но не обеспечить продвинутых возможностей корпоративной интеграции, предоставляемых языком Java. В следующий раз я подробнее остановлюсь на JavaScript. А пока продолжайте пересекать границы.



Брюс Тэйт, президент, RapidRed


Сетевые решения. Статья была опубликована в номере 03 за 2007 год в рубрике программирование

©1999-2022 Сетевые решения