Maksym Prokopov personal blog
Idea is a something worth sharing

Что я узнал из работы с Rails assets pipeline и Javascript. KnockoutJS vs AngularJS.

30.10.2014

Reading time: 3 min.

Задумал я оживить нашу систему обработки заявок так, чтобы в realtime подгружались новые обращения, инциденты обновляли SLA и возникали визуальные уведомления при протухании тикетов.

Rails замечательно работает со статикой, лучше всех, пожалуй. Но то, что предлагается использовать для «оживления» страниц подходит только для очень небольшого круга задач. 

vs

Чаще всего Ajax Polling делается через запрос jQuery и подстановку темплейта с рендерингом из JS шаблона. Мне очень нравится писать шаблоны на HAML, и использовать Coffeescript.

Но, к сожалению, браузер не умеет рендерить HAML шаблоны, а изучать/выбирать новый template-язык не хотелось. 

Именно поэтому в поле моего зрения попали движки, которые умеют использовать существующую разметку: AngularJS и KnockoutJS. Вот их я и стал изучать.

Создал две экспериментальные ветки feature в git и стал пробовать Knockout.

Knockout оказался очень хорошим фреймворком для two-way databinding. Классно лег на coffeescript, и HAML. Вот пример вьюконтроллера:

class UnassignedTicketsViewModel extends TicketWithSLAViewModel
  constructor: ->
    super
    @api = ‘/api/tickets/unassigned’

вот HAML партиал. Кстати, это в 4м HAML появилась возможность писать аттрибуты в круглых скобках.

    %td.avatar
      %img.cycle.userpic(data-bind = «attr:{src:avatar.nano.url}, visible: state !== ‘classified’»)
    %td.id_column
      %a(data-bind = «attr:{ href: url }, css: {‘fg-red’: is_major}»)
        %i.icon-fire(data-bind = ‘visible: is_major’)
        %i(data-bind = ‘css: icon()’)
        %span(data-bind = ‘text: id’)
        %i.icon-bus(data-bind = ‘visible: is_outdoor’)
    %td(data-bind = ‘text: author’)

Вот какие выводы у меня появились после использования Knockout с рельсами

Параллельно я вел исследования с AngularJS. Прошел офигительный интерактивный туториал https://www.codeschool.com/courses/shaping-up-with-angular-js

И написал polling тикетов через $resource. И пришел к таким выводам:

И в том и в другом случае было необходимо разработать правильную отдачу JSON объектов при поллинге. Мне нужно было включать ассоциативные поля в модели, поэтому довольно скоро от Model.as_json я перешел к использованию https://github.com/rails-api/active_model_serializers, но поскольку разработчики не смогли придумать кеширование, да и отдачу url_for, пришлось перейти на jbuilder, на котором и остановился. json!.cache отлично сокращает запросы к базе и время рендера.

Для отдачи JSON решил сделать API через отдельные контроллеры и наймспейс ‘/api/ как у Раяна Бейтса

И все было хорошо, пока я не стал думать, как же для разных страниц мне использовать разные биндинги для KnockoutJS и при этом чувствовать себя хорошо. Самый простой и «лобовой» способ это убрать require_tree . из application.js и добавить include_javascript_tag params[:controller] в лейаут. Но здесь то и зарыт главный косяк. Assets pipeline, который склеивает все css и js в один файл будет кидать ексепшены, поскольку не скомпилирует .js для каждого контроллера. И выхода здесь два:

  1. включить компиляцию assets для продакшена.
  2. использовать gem paloma

Более православным мне показался второй путь, поскольку использует assets pipeline и делает всю магию на клиенте. Также позволяет передавать из контрроллера в JS различные параметры. Это же умеет делать gem gon, но если можно гем не использовать, то лучше его не использовать.

Также, мне очень понравился подход с подключением jquery библиотек и используемого мной metro-ui-css фреймворка через интеграцию с bower. В моем случае сделал подключение gem ‘rails-bower’, в Bowerfile записал asset ‘metro-ui-css’, rake bower:install и вуаля, в  /vendor/assets/bower_components загруженные с зависимостями jquery и другие модули. Очень удобно.

Итоги. 

Я остановился на использовании KnockoutJS для моей задачи, и доволен этим фактом, много jQuery кода заменил более компактным и объектно-ориентированным кодом для Knockout. А AngularJS, думаю, стоит использовать вообще отдельным приложением вместе с Node.js, а Rails в качестве исключительно JSON бекенда, что тоже для Rails было бы унизительным.