О некоторых моментах при разработке дополнения для EmberJS

Немного слов о вещах, смежных с разработкой дополнений для Emberjs.

  • Установка ember-cli
  • Инициализация дополнения и создание репозитория на GitHub
  • Базовая структура дополнения.

    • Добавление шаблонов
    • Добавление helper’ов и включение их в приложение
    • Что такое EXTEND_PROTOTYPES
  • Тестирование
  • Демо-приложение для gh-pages
  • Публикация в npm и выкатка новых версий
  • ReadMe и бейджики (увы, без них никак)

Работать с EmberJS без ember-cli сейчас вообще не имеет смысла. Инициализация приложения, генерация элементов (компонентов, моделей, представлений и т.д.), сборка приложения, запуск тестов — все это делается одной cli-утилитой.

Установка ember-cli

npm install -g ember-cli bower phantomjs

Так просто, если у вас node.js 0.12+ или же io.js. Иначе, вначале придется обновить/установить нужное ПО.

Инициализация дополнения и создание репозитория на GitHub

Создания дополнения для EmberJS начинается с команды:

ember addon ember-cli-my-cool-addon

В текущем каталоге будет создан подкаталог ember-cli-my-cool-addon, в котором и будет дополнение. В самой документации достаточно подробно расписано, что и как делать для создания аддона (ссылка). Далее необходимо связать каталог аддона с репозиторием на github’e. Для начале создаем сам репозиторий (тут без консольных команд — делайте прямиком на самом сайте). Теперь из хорошего мануала отбираем только лучшие зерна нужные команды, а именно — git remote add origin MY_AWESOME_URL и git push origin master. Почему пропущено начальные команды? Потому что ember-cli при наличии git’a сам сделает инициализацию и первый коммит (см. по ссылке). Если же каким-то чудом этого не случилось, то необходимо самому выполнить все, что указано в инструкции на github’e.

Базовая структура дополнения

Сразу после создания структура дополнения выглядит так, как указано в оф. документации. Менять там что-либо не надо. Впишите в package.json правильные description, repository, keywords, author и можете сделать еще один коммит. Рекомендую далее ознакомиться с инструкциями до пункта «Default Blueprint».

Добавление шаблонов

Нам говорили, что все наше дополнение должно быть в папке addon. Ну или почти все. Шаблоны точно будут лежать в папке app/templates. Иными словами, если мы разрабатываем компонент под названием modal-window (со своим шаблоном), то файловая структура будет такой:

/
├── addon
│   ├── components
│   │   └── modal-window.js
└── app
     └── templates
          └── components
                └── modal-window.hbs

А ведь еще надо не забыть, что нам компонент надо «пропроксировать» в само приложение (см. пример по ссылке). То есть, структура будет уже такой:

/
├── addon
│   ├── components
│   │   └── modal-window.js
└── app
     ├── components
     │   └── modal-window.js
     └── templates
          └── components
                └── modal-window.hbs

Кстати, если пользователь, который установил дополнение с вышеуказанным компонентом, в неожиданный момент создаст в своем приложении шаблон templates/components/modal-window.hbs, то он перезатрет шаблон из аддона. Такие дела.

Добавление helper’ов и включение их в приложение

Что такое helper? Идем и читаем на сайт HandleBarsJS. А почему не на HtmlBars? Потому что определение и базовое понятие можно получить и там. Так же материалы доступны на сайте EmberJS. Начинаем с Templates/Links и читаем до конца раздела Templates.

На первый взгляд кажется, что helper’ов у Эмбера предостаточно. И да, и нет. Простая ситуация — есть одна переменная с объектом (obj) и вторая переменная (key), в которой находится строка с именем поля объекта. Надо на страничке вывести результат Ember.get(obj, key). А нет такого helper’а. Хотя написать его самый топорный вариант — это дело двух минут:

// helpers/get.js
import Ember from 'ember';
 
function get(obj, prop) {
  return Ember.get(obj, prop);
}
export default Ember.Handlebars.makeBoundHelper(get);

И все. Теперь в шаблонах можно смело писать (при условии, что obj не null):

{{get obj prop}}

Да, в сам метод helper’a еще можно добавить проверки на кол-во аргументов и т.д. В результате получится что-то похожее на аддон ember-get-helper. Как видим, проблема весьма насущная 🙂

Как же правильно добавить helper в дополнение? В папке addon/helper создается код, который экспортирует функцию, которая позже вставляется в Ember.Handlebars.makeBoundHelper. Как-то сложно. Лучше на примере. Для начала файловая структура:

/
├── addon
│   ├── helpers
│   │   └── get.js
└── app
     └── helpers
          └── get.js

Опять два файла? Да, снова два файла.

// addon/helpers/get.js
import Ember from 'ember';
 
export function get(obj, key) {
    if (arguments.length === 0) {
        return;
    }
    return Ember.get(obj, key);
}
 
export default get;
// app/helpers/get.js
 
import Ember from 'ember';
import get from 'YOUR_COOL_ADDON_NAME/helpers/get';
 
export default Ember.Handlebars.makeBoundHelper(get);

Теперь helper доступен как в дополнении, так и в приложении, где это дополнение используется. И, опять-таки, пользователь может случайно или специально переопределить helper.

Что такое EXTEND_PROTOTYPES

Данный параметр настройки Ember-приложения отвечает за то, будут ли в прототипы String, Array, Function добавлены дополнительные Эмберовские методы. В обычном Ember-приложении эта опция включена по умолчанию. А вот для addon’ов она выключена. Сделано это с одной целью — ваше дополнение должно работать с любой настройкой Эмбера. По началу немного непривычно без [].compact(), function (){}.observes(..) и т.д. Но потом быстро привыкаешь и вместо var a = []; пишешь var a = Ember.A([]); и т.д. В официальной документации Эмбера есть раздел про Life Without Prototype Extension. Настоятельно рекомендую прочитать.

Да, при наличии качественных тестов, вы сразу узнаете места, в которых ваш код ведет себя так, как будто EXTEND_PROTOTYPES включен.

Тестирование

В документации к ember-cli под тестирование выделен целый раздел. И этот раздел расписан очень подробно и доступно. Однако есть моменты, которые приходится гуглить. Один из них — это подключение специфичного шаблона. Допустим, у нас есть компонент addon/components/super-table.js. И ему может передаваться имя файла с шаблоном, который он отображает через {{partial ...}}. И вот этот рендеринг мы и хотим протестировать. Наш спец. шаблон будет лежать в test/dummy/app/templates/custom/tpl.hbs. В тестах, если просто взять переменную со значением custom/tpl и попытаться передать ее в partial, то «кина не будет», а будет ошибка вида Unable to find partial with name .... Как же быть? Ответ скрывается (по другому и сказать не могу) по ссылке. Кому не интересно читать весь диалог, перескажу основную часть — «Ember is doing «weird» things for partials» и «The factory that Ember (and ember-qunit) is looking for template:path/to/partial does not match the actual module path». Это ответ Роберта Джексона (rwjblue@github). Далее все же он дает решение данной проблемы — в файле с тестами сделать импорт из helpers/resolver:

// some-file-test.js
 
import resolver from '../../helpers/resolver';

Далее в секции setup необходимо указать Ember’у, где брать требуемый шаблон:

// some-file-test.js
moduleForComponent('my-component', 'MyComponent', {
 
  setup: function () {
    this.container.register('template:custom/tpl', resolver.resolve('template:custom/tpl'));
  }
 
});

После этого никаких ошибок, связанных с «Unable to find partial», не будет. Возможно, это описано где-то в документации, но мне не попадалось.

Ваши helper’ы тоже нуждаются в покрытии тестами. Тут есть два пути: тестировать только саму функцию helper’а или тестировать рендеринг шаблонов с helper’ом. В первом случае в файле с тестами достаточно сделать импорт функции из addon/helpers/get.js (был описан выше) и тестировать ее как обычную функцию. Во втором случае, помимо импорта функции, ее надо зарегистрировать как helper, потом сделать какое-то представление (view), в шаблон которого включить helper. Далее надо сделать render для view и смотреть ее содержимое путем view.$().text(). Наверно, лучше с примерами. Случай первый:

// test/unit/helpers/custom-test.js
 
import {
  moduleForComponent,
    test
} from 'ember-qunit';
import Ember from 'ember';
import get from 'YOUR_COOL_ADDON_NAME/helpers/get';
 
module('myCoolModule', function () {});
 
test('Just a test', function(assert) {
    assert.equal(get({a: 1}, 'a'), 1);
});

И второй вариант:

// test/unit/helpers/custom2-test.js
 
import Em from 'ember';
import { module, test } from 'qunit';
import get from 'YOUR_COOL_ADDON_NAME/helpers/get';
 
module('myCoolModule', function () {
    setup(function () {
        Ember.HTMLBars.registerHelper('get', get);
    });
});
 
test('Just a test', function(assert) {
    var view = Em.View.create({
        obj: {
            a: 1,
            b: 2
        },
        key: a,
        template: Ember.HTMLBars.compile("{{get obj key}}/{{get obj 'a'}}"),
    });
    Ember.run(function () {
        view.render();
    });
    assert.equal(view.$().text(), '1/1');
});

Пример для второго случая взят из репозитория jmurphyau/ember-get-helper (плагин не мой, я просто разместил объяву ссылку).

Демо-приложение для gh-pages

Известный факт — на картинках/примерах информация воспринимается куда лучше, чем в «plain text». Вот и для созданного дополнения неплохо бы сделать демку. Для этих целей есть скелет приложения в папке test/dummy/app. Оно генерируется еще при инициализации дополнения (команда ember addon MY_ADDON_NAME) и может быть запущено командой ember server. Компоненты, миксины («примеси», если угодно), хэлперы вашего дополнения так же будут доступны в этом тестовом приложении. Вроде бы, все круто. Но вот пользователь чаще всего ленив и не захочет клонировать проект к себе, устанавливать все зависимости и т.д. Для демонстрационных целей на github’е есть gh-pages. Задача — подложить туда тестовое приложение. Реализуется за пару действий. Первое — немного поменяем config/environment.js:

// config/environment.js
 
if (environment === 'production') {
    ENV.baseURL = '/GITHUB_PROJECT_NAME/'; // добавилась эта строка
}

Зачем это нужно? Демо на gh-pages имеет адрес вида: https://GH_LOGIN.github.io/GITHUB_PROJECT_NAME. Ember-приложению использует параметр ENV.baseURL для задания атрибута href тэга base. Таким образом, браузер будет искать assets приложения в правильной папке. Теперь надо правильно собрать приложение в правильной ветке (gh-pages). Тут на помощь приходит маленький sh-скрипт, который (как обычно) сделает все за вас:

#!/bin/bash
 
git branch -D gh-pages
git push origin --delete gh-pages
git checkout -b gh-pages
ember build --environment production
git rm -rf app addon config tests
git rm -rf Brocfile.js bower.json package.json testem.json
git rm -rf .bowerrc .editorconfig .jshintrc .travis.yml
mv dist/* .
rm -rf dist
git add .
git commit -m "Publishing to github pages"
git push origin gh-pages
git checkout master

В нем удаляется существующая ветка gh-pages. Потом создается новая gh-pages. Далее выполняется сборка приложения с ключом environment = production. Затем из ветки удаляется все ненужное. Содержимое dist переносится на уровень выше (сама папка уже пустая и удаляется). Остается только добавить все новые файлы в git, сделать commit и push. Для удобства в последней строке скрипта выполняется переключение на ветку master.

Этот скрипт необходимо запускать только тогда, когда вы реально готовы обновить демо. Если же Вы просто хотите обновить тестовое приложение в master, то скрипт запускать не надо. Рекомендую закоммиттить его в master, дабы всегда он был под рукой.

Будет не лишним в демку добавить «уголок» от GitHub (см. по ссылке). Теперь из демки всегда можно попасть на страницу с дополнением.

Публикация в npm и выкатка новых версий

При публикации новых версий вашего дополнения главное — не забыть поменять версию в package.json. И не забывайте, что версия должна соответствовать semver (ну хотя бы иметь формат X.Y.Z). Публикация дополнения выполняется командой:

npm publish

Если при этом у вас возникают какие-то ошибки (вариант, что вы не авторизовались в npm не берем), то необходимо обновить npm (саму nodejs стоит обновить только, если у вас версия совсем древняя). Совет немного капитановский, но он работает 🙂

Выкатка новых версий делается все той же командой npm publish. После обновления должно пройти немного времени (до получаса), перед тем как на https://www.npmjs.com/package/YOUR_PACKAGE_NAME появится новая версия.

ReadMe и бейджики (увы, без них никак)

Удивительно, но бейджики в ReadMe вашего дополнения (а без ReadMe сейчас вообще никак и никуда) увеличивают «продаваемость» вашего кода. Какие бейджики можно добавить себе:

  • Travis CI status
  • coverall.io
  • Codacy
  • Version Eye
  • Badge Fury for JS
  • Shields.io

Пройдемся подробнее.

Travis CI. Сервис по Continuous Integration. На любой commit/pull-request будет выполнен сценарий из .travis.yml в вашем репозитории. При начальной генерации скелета дополнения ember-cli создает и этот файл. В нем уже есть все для запуска тестов. Остается только на самом github’e включить service-hook для travis. Бейджик о том, что сборка проекта проходит удачно является хорошим подспорьем для того, чтоб установить дополнение.

Coveralls — сервис, который работает в связке с Travis и показывает процент покрытия кода тестами. Требует доп. настроек (см. мануал). Хорошо оттестированный код — это должно быть нормой, а не какой-то диковинкой. Бейджик будет содержать процент покрытия.

Codacy — Automated Code Review. Покажет проблемные места в коде (как кажется самому сервису) и поставит оценку качеству кода. Бегом исправлять все, что скажет сервис, не надо. Но просмотреть «проблемные» места стоит обязательно. Бейджик с оценкой так же будет хорошо смотреться в ReadMe. Чуть не забыл — сервис также позволяет настроить проверку покрытия кода тестами (лично не проверял).

Version Eye — сервис, который нужен, если вы хотите быть впереди планеты всей в плане версий зависимостей для своего продукта. Для JS позволяет отслеживать как node.js, так и обычные js-зависимости. На каждую группу свой бейджик. Перед использованием хорошо подумается, реально ли оно вам надо.

Badge Fury — дает бейджик для последней на текущий момент версии npm-пакета. Вообще, умеет работать не только с npm, но и с Ruby, Python, Bower, GitHub, NuGet, PHP и т.д. Пользователю будет проще увидеть актуальную версия пакета (хотя никто ему не запрещает ту же информацию посмотреть в package.json).

Shields.io нужен для случая, если вы хотите, чтоб в Readme была информация о лицензии проекта, кол-ву открытых тикетов на github’e, а так же stars/forks все на том же github’e. С одной стороны кажется, что идет дублирование информации (в плане, что Readme и так лежит на гитхабе), но это не так. Readme из проекта будет выводиться и на npmjs.

Как заключение для раздела про бейджики — они (бейджики) не цель, а всего лишь средство. Используйте его с умом.

Заключение

Создание программного продукта — это не только написание кода, но и немало вспомогательной работы.

,

Оставить комментарий

Top ↑ | Main page | Back