text:
ENB
===

[![NPM version](http://img.shields.io/npm/v/enb.svg?style=flat)](http://www.npmjs.org/package/enb) [![Build Status](http://img.shields.io/travis/enb-make/enb/master.svg?style=flat&label=tests)](https://travis-ci.org/enb-make/enb) [![Build status](http://img.shields.io/appveyor/ci/blond/enb.svg?style=flat&label=windows)](https://ci.appveyor.com/project/blond/enb) [![Coverage Status](https://img.shields.io/coveralls/enb-make/enb.svg?style=flat)](https://coveralls.io/r/enb-make/enb?branch=master) [![Dependency Status](http://img.shields.io/david/enb-make/enb.svg?style=flat)](https://david-dm.org/enb-make/enb)

Сборщик проектов. С помощью ENB можно собрать любой проект, который строится на модели node / target.

**ВАЖНО:** Конфигуратор для упрощения создания конфига: [http://enb-make.info/config/](http://enb-make.info/config/).

**Возможности**

* Сборка проекта и конкретных таргетов в разных режимах (с помощью ENV-переменной `YENV`).
* Режим сервера как в виде самостоятельного express-сервера, так и в виде express-middleware.

**Зачем нужен этот проект, если есть bem-tools?**

ENB работает гораздо быстрее, чем bem-tools. Причем, как в режиме сборки, так и в режиме сервера.

**Почему?**

* Гибкая система кэширования.
* Обмен промежуточными данными в процессе сборки.
* Разбиение технологий на более мелкие (избавляемся от повторной работы).
* Используются более быстрые библиотеки (например, https://github.com/dfilatov/jspromise вместо Q).
* Нет порядка выполнения технологий (технологии зависят от таргетов, а не друг от друга), технологии зачастую выполняются параллельно.
* Тяжелые синхронные технологии выделяются в субпроцессы.

**Некоторые отличия от bem-tools**

* ENB (как платформа) свободна от идеологии BEM. Сбор префиксов не является частью платформы, а реализуется с помощью одной из технологий.
* Технологии в ENB не ограничены в том, каким образом они будут собирать те или иные таргеты.
* Все технологии настраиваемые (в большей или меньшей степени).
* ENB сложнее настроить для проекта. В нем нет готовых шаблонов make-файлов.
* В рамках ENB одна и та же технология может быть использована с разными опциями. Например, можно построить несколько разных `deps.js` в рамках одной ноды на основе различных `bemdecl.js`.

**Как потестить?**

Специально для этого я подготовил сборку project-stub на ENB: https://github.com/mdevils/project-stub

**Пакеты для ENB**

* Сборка BEMHTML: https://github.com/enb-make/enb-bemhtml
* Модульность для нового bem-core: https://github.com/enb-make/enb-modules
* Интеграция для grunt: https://github.com/megatolya/grunt-enb

Благодарности
-------------

* Дмитрию Филатову (@dfilatov). За `vow`, `vow-fs`, `inherit`, советы, поддержку и мотивацию.
* Дмитрию Ковалю (@smith). За помощь в сборке тестов, production-режима и здоровый скептицизм.
* Александру Тармолову (@hevil). За помощь с `git`, `modules`, поддержку и полезные ссылки.
* Вениамину Клещенкову (@benjamin). За помощь в отладке и доработке ENB, поддержку, советы и ревью.
* Сергею Бережному (@veged). За `borschik`, советы и правильные вопросы.
* Команде разработчиков bem-tools. За часть заимствованного кода.
* Егору Блинову (@escaton). За пулл-реквесты, идеи.
* Андрею Абрамову (@blond). За пулл-реквесты.

Использование из командной строки
---------------------------------

Предполагается, что вы установили `npm`-пакет `enb` и находитесь в корне проекта.

Сборка всех страниц проекта:
```
./node_modules/.bin/enb make
```

Сборка всех страниц проекта со сбросом кэша:
```
./node_modules/.bin/enb make --no-cache
```

Сборка всех страниц проекта с построением графа сборки:
```
./node_modules/.bin/enb make --graph
```

Сборка конкретной страницы проекта:
```
./node_modules/.bin/enb make pages/index
```

Сборка конкретного файла:
```
./node_modules/.bin/enb make pages/index/index.html
```

Запуск в режиме сервера:
```
./node_modules/.bin/enb server
```

Отключение цветового форматирования при выводе прогресса в консоль:
```
NOCOLOR=1 ./node_modules/.bin/enb make
```

Установка лимита открытых файлов для асинхронных операций. Правильно выбранный лимит позволяет избежать `EMFILE`-ошибок:
```
ENB_FILE_LIMIT=100 ./node_modules/.bin/enb make
```

Терминология
------------

* Target (таргет) — это цель для сборки. Например, таргетом может быть `index.js` в рамках ноды `pages/index`..
* Node (нода) — это папка, в которой находятся те или иные таргеты. Например, `pages/index`.
* Suffix (суффикс) — это расширение исходного или конечного файла. Например, `js`.
* Masked Target (замаскированный таргет) — это имя таргета, которое может содержать `?`. Знак `?` заменяется на имя ноды в процессе настройки технологии, а с помощью подстроки `{lang}` можно создать несколько копий технологии для каждого из языков, где `{lang}` заменится на аббревиатуру языка в каждой из копий технологии. Например, таргет `?.js` заменяется на `search.js`, если нода — `pages/search`. Такой подход удобен, когда мы настраиваем несколько нод через `nodeMask`.
* Make-файл — файл, в котором конфигурируется ENB для сборки проекта. Находится в папке `<project_root>/.enb/make.js`.
* Билдить — собирать, компилировать (используется в отношении таргетов).

Процесс сборки
--------------

1. Какие таргеты необходимо билдить `ENB` узнаёт из команды `enb make [target]`. Если вы запустили `enb make` без указания конкретного таргета, `ENB` будет собирать все таргеты, определенные в `make.js`.
2. `ENB` инициализирует ноды, участвующие в сборке указанных таргетов. В это время каждая нода спрашивает у технологий (которые регистрировались внутри ноды) список таргетов.
3. Запускаются технологии, которые отвечают за те таргеты, которые необходимо билдить.
4. В процессе выполнения технология может потребовать у ноды другие таргеты, необходимые для сборки (с помощью метода `requireSources`). В таком случае технология приостанавливается, нода запускает технологии, отвечающие за требуемые таргеты (если они не запущены) и после того, как технологии заканчивают сборку нужных таргетов, продолжает свою работу искомая технология.
5. После того, как технология выполнила свою работу по сборке таргета, она оповещает об этом ноду (с помощью метода `resolveTarget`).
6. Сборка завершается после того, как все необходимые таргеты собраны.

Как собрать проект - пошаговое руководство
==========================================

1. Прописать в `package.json` проекта зависимость от пакета `enb` (желательно в виде ">=последняя_версия").
2. Выполнить `npm install`.
3. Проверить, что `ENB` установлен. Команда `node_modules/.bin/enb` должна выполниться без ошибок.
4. Создать make-файл `.bem/enb-make.js` вида:

  ```javascript
  module.exports = function(config) {
  };
  ```
5. Проверить, что `ENB` работает. Команда `node_modules/.bin/enb make` должна выполниться без ошибок.
6. Теперь нужно настроить ноды. Для примера, я приведу вариант настройки ноды `pages/index`.

  ```javascript
  module.exports = function(config) {
      config.node('pages/index', function(nodeConfig) {
      });
  };
  ```
  Так объявляется нода в рамках make-платформы. В данный момент она не настроена, а лишь объявлена.
7. Объявим таргеты, которые надо собрать для ноды:

  ```javascript
  module.exports = function(config) {
      config.node('pages/index', function(nodeConfig) {
        nodeConfig.addTargets(['_?.js', '_?.css']);
      });
  };
  ```
  Таргеты объявлены, но при попытке выполнить `node_modules/.bin/enb make` будет ошибка, т.к. не зарегистрированы технологии, которые могут предоставить таргеты.
8. Зарегистрируем базовые технологии:
  ```javascript
  module.exports = function(config) {
      config.node('pages/index', function(nodeConfig) {
        nodeConfig.addTechs([
          [ require('enb/techs/levels'), { levels: getLevels(config) } ],
          [ require('enb/techs/file-provider'), { target: '?.bemdecl.js' } ],
          require('enb/techs/deps-old'),
          require('enb/techs/files')
        ]);

        nodeConfig.addTargets(['_?.js', '_?.css']);
      });
  };

  function getLevels(config) {
      return [
        {path: 'bem-bl/blocks-common', check: false},
        {path: 'bem-bl/blocks-desktop', check: false},
        {path: 'lego/blocks-common', check: false},
        {path: 'lego/blocks-desktop', check: false},
        'common.blocks',
        'desktop.blocks'
      ].map(function(levelPath) { return config.resolvePath(levelPath); });
  }
  ```
  Чтобы не засорять конфиг ноды, функцию `getLevels` мы выносим в нижнюю часть файла.

  Рассмотрим каждую технологию:

  **enb/techs/levels** — собирает информацию об уровнях переопределения проекта. Результат выполнения этой технологии необходим технологиям `enb/techs/deps`, `enb/techs/deps-old` и `enb/techs/files`. Для каждой ноды по умолчанию добавляется уровень `<путь_к_ноде>/blocks`. Например, для ноды `pages/index` — `pages/index/blocks`.

  **enb/techs/file-provider** — сообщает make-платформе, что таргет (переданный в опции `target`) уже готов. В нашем случае, исходным файлом для сборки является `index.bemdecl.js`. Он лежит в репозитории и отдельная сборка для него не требуется.

  **enb/techs/deps-old** — собирает `?.deps.js` (`index.deps.js`) на основе `index.bemdecl.js` и `index.levels`. Почему `deps-old`? В lego не хватает ряда зависимостей, поэтому ваш проект может не собраться с более быстрый технологией `deps` без модификации lego. Технология `deps-old` повторяет алгоритм сборки из `bem-tools` и нехватка зависимостей становится незаметной, как раньше.

  **enb/techs/files** — собирает полный список файлов со всех уровней переопределения в том порядке, в котором они идут в финальном `index.deps.js`. Результат этой технологии может использоваться, например, в технологии `enb/techs/js`.

9. Регистрируем технологии, необходимые для сборки js и css.
  ```javascript
  module.exports = function(config) {
      config.node('pages/index', function(nodeConfig) {
        nodeConfig.addTechs([
          [ require('enb/techs/levels'), { levels: getLevels(config) } ],
          [ require('enb/techs/file-provider'), { target: '?.bemdecl.js' } ],
          require('enb/techs/deps-old'),
          require('enb/techs/files'),
          require('enb/techs/js'),
          require('enb/techs/css')
        ]);

        nodeConfig.addTargets(['_?.js', '_?.css']);
      });
  };

  function getLevels(config) {
      return [
        {path: 'bem-bl/blocks-common', check: false},
        {path: 'bem-bl/blocks-desktop', check: false},
        {path: 'lego/blocks-common', check: false},
        {path: 'lego/blocks-desktop', check: false},
        'common.blocks',
        'desktop.blocks'
      ].map(function(levelPath) { return config.resolvePath(levelPath); });
  }
  ```
  Теперь файлы `index.js` и `index.css` могут собираться с помощью технологий `enb/techs/js` и `enb/techs/css` соответственно.
  Но мы регистрировали иные таргеты: `_?.js` (`_index.js`) и `_?.css` (`_index.css`). Для их сборки воспользуемся технологией `enb/techs/file-copy`.
  ```javascript
  module.exports = function(config) {
      config.node('pages/index', function(nodeConfig) {
        nodeConfig.addTechs([
          [ require('enb/techs/levels'), { levels: getLevels(config) } ],
          [ require('enb/techs/file-provider'), { target: '?.bemdecl.js' } ],
          require('enb/techs/deps-old'),
          require('enb/techs/files'),
          require('enb/techs/js'),
          require('enb/techs/css'),
          [ require('enb/techs/file-copy'), { sourceTarget: '?.js', destTarget: '_?.js' } ],
          [ require('enb/techs/file-copy'), { sourceTarget: '?.css', destTarget: '_?.css' } ]
        ]);

        nodeConfig.addTargets(['_?.js', '_?.css']);
      });
  };

  function getLevels(config) {
      return [
        {path: 'bem-bl/blocks-common', check: false},
        {path: 'bem-bl/blocks-desktop', check: false},
        {path: 'lego/blocks-common', check: false},
        {path: 'lego/blocks-desktop', check: false},
        'common.blocks',
        'desktop.blocks'
      ].map(function(levelPath) { return config.resolvePath(levelPath); });
  }
  ```
  Теперь можно выполнить команду `node_modules/.bin/enb make` и в папке `pages/index` будут столь нужные нам `_index.js` и `_index.css`.
  Окей, мы получили результат, с которым можно работать. Но как же production-режим?

10. Разделяем сборку финальных файлов для разных режимов.
  ```javascript
  module.exports = function(config) {
      config.node('pages/index', function(nodeConfig) {
        nodeConfig.addTechs([
          [ require('enb/techs/levels'), { levels: getLevels(config) } ],
          [ require('enb/techs/file-provider'), { target: '?.bemdecl.js' } ],
          require('enb/techs/deps-old'),
          require('enb/techs/files'),
          require('enb/techs/js'),
          require('enb/techs/css')
        ]);

        nodeConfig.mode('development', function(nodeConfig) {
          nodeConfig.addTechs([
            [ require('enb/techs/file-copy'), { sourceTarget: '?.js', destTarget: '_?.js' } ],
            [ require('enb/techs/file-copy'), { sourceTarget: '?.css', destTarget: '_?.css' } ]
          ]);
        });

        nodeConfig.mode('production', function(nodeConfig) {
          nodeConfig.addTechs([
            [ require('enb/techs/borschik'), { sourceTarget: '?.js', destTarget: '_?.js', minify: true } ],
            [ require('enb/techs/borschik'), { sourceTarget: '?.css', destTarget: '_?.css', minify: true } ]
          ]);
        });

        nodeConfig.addTargets(['_?.js', '_?.css']);
      });
  };

  function getLevels(config) {
      return [
        {path: 'bem-bl/blocks-common', check: false},
        {path: 'bem-bl/blocks-desktop', check: false},
        {path: 'lego/blocks-common', check: false},
        {path: 'lego/blocks-desktop', check: false},
        'common.blocks',
        'desktop.blocks'
      ].map(function(levelPath) { return config.resolvePath(levelPath); });
  }
  ```
  Теперь для production-режима конечные файлы обрабатываются [Борщиком](https://github.com/bem/borschik). Production-режим запускается командой `YENV=production node_modules/.bin/enb make`

11. Сборка `js` и `css` работает. Если в вашем проекте присутствуют другие цели или мультиязычность, то можно продолжить чтение данной документации в поисках информации о небходимых технологиях.
12. Собираем `node_modules/.bin/enb make`.

Настройка сборки
================

```javascript
module.exports = function(config) {

  // Языки для проекта.
  config.setLanguages(['ru', 'en']);

  // Добавление набора нод в сборку.
  config.nodes('pages/*');

  // Добавление ноды в сборку + конфигурирование ноды.
  config.node('pages/index', function(nodeConfig) {
    // Переопределение языков для конкретной ноды.
    nodeConfig.setLanguages(['ru']);

    // Добавление одной технологии с опциями.
    nodeConfig.addTech([ require('enb/techs/file-provider'), { target: '?.bemdecl.js' } ]);

    // Добавление нескольких технологий.
    nodeConfig.addTechs([
      [ require('enb/techs/levels'), {
        levels: [
          'common.blocks',
          'desktop.blocks'
        ].map(function(config) { return config.resolvePath(level); }) // Резолвинг путей от корня проекта.
      }],
      require('enb/techs/deps'),
      require('enb/techs/files'),

      // Добавление технологии с опциями
      [ require('enb/techs/js'), { target: '?.new.js' } ],
      require('enb/techs/css')
    ]);

    // Добавление одного таргета.
    nodeConfig.addTarget('?.css');

    // Добавление нескольких таргетов.
    nodeConfig.addTargets(['?.css', '?.js']);
  });

  // Настройки для режима development.
  config.mode('development', function() {
    // Настройка нод по маске (regex).
    config.nodeMask(/pages\/.*/, function(nodeConfig) {
      nodeConfig.addTechs([
        [ require('enb/techs/file-copy'), { sourceTarget: '?.css', destTarget: '_?.css'} ],
        [ require('enb/techs/file-copy'), { sourceTarget: '?.js', destTarget: '_?.js'} ]
      ]);
    });
  });

  // Настройки для режима production.
  config.mode('production', function() {
    // Настройка нод по маске (regex).
    config.nodeMask(/pages\/.*/, function(nodeConfig) {
      nodeConfig.addTechs([
        [ require('enb/techs/borschik'), { sourceTarget: '?.css', destTarget: '_?.css'} ],
        [ require('enb/techs/borschik'), { sourceTarget: '?.js', destTarget: '_?.js'} ]
      ]);
    });
  });

  // Регистрация таска.
  config.task('i18n.get', function(task) {
    // Выполнение shell-команды.
    return task.shell('./blocks/lego/tools/get-tanker.js');
  });

  // Установка переменных среды для shell-команд.
  config.setEnv({
      PRJ_ROOT        : config.resolvePath(), // Получение абсолютного пути к папке с проектом.
      TANKER_HOST     : 'tanker-test.yandex-team.ru',
      TANKER_PRJ      : 'super-project',
      TANKER_PRJ_REV  : 'master'
  });
};
```

Автоматизация с помощью express
===============================

При разработке `nodejs`-приложений на базе `express` можно сильно упростить использование `enb` в `development`-режиме.

Суть в том, что можно забыть о пересборке проекта, о других портах для статики и т.п. Можно просто отправлять в `ENB`
запросы на сборку тогда, когда это необходимо. То есть, когда вы открываете в браузере свой проект.

Для этого можно воспользоваться `express`-совместимым `middleware`. Его возвращает метод `createMiddleware` модуля
`lib/server/server-middleware`.

```javascript
/**
 * @param {Object} options
 * @param {String} options.cdir Корневая директория проекта.
 * @param {Boolean} options.noLog Не логгировать в консоль процесс сборки.
 * @returns {Function}
 */
module.exports.createMiddleware = function(options) { /* ... */ };
```

Пример использования:

```javascript
app
    .use(require('enb/lib/server/server-middleware').createMiddleware())
    .get('/', function (req, res) {
        /* ... */
    });
```

Сборка по требованию
====================

Помимо упрощения сборки статики в `dev`-режиме с помощью `ENB` в `express`-приложениях,
можно собирать по требованию различные ресурсы, например, шаблоны.

Если `nodejs` приложению в процессе работы требуется собирать шаблоны или локализацию (или что-нибудь еще),
то можно воспользоваться методом `createBuilder` модуля `lib/server/server-middleware`.

```javascript
/**
 * @param {Object} options
 * @param {String} options.cdir Корневая директория проекта.
 * @param {Boolean} options.noLog Не логгировать в консоль процесс сборки.
 * @returns {Function}
 */
module.exports.createBuilder = function(options) { /* ... */ };
```

Пример использования:

```javascript
var enbBuilder = require('enb/lib/server/server-middleware').createBuilder();
var dropRequireCache = require('enb/lib/fs/drop-require-cache');
app
    .get('/', function (req, res, next) {
        var bemhtmlFilePath = 'pages/index/index.bemhtml.js';
        enbBuilder(bemhtmlFilePath).then(function() {
            var bemhtmlAbsFilePath = process.process.cwd() + '/' + bemhtmlFilePath;
            dropRequireCache(require, bemhtmlAbsFilePath);
            var bemhtml = require(bemhtmlAbsFilePath);
            res.end(bemhtml.BEMHTML.apply({block: 'b-page', content: 'Hello World'}));
            next();
        }, next);
    });
```

Сборка merged (common) бандла
-----------------------------

>  Merged бандл — это бандл, который объединяет в себе декларации всех бандлов уровня. Соответственно по такой объединенной декларации собираются и объединенные конечные файлы. Например, css будет включать в себе все стили, используемые всеми бандлами.
>
>  Merged бандл может быть полезен, например, если вы хотите использовать общие файлы статики (js, css) для нескольких страниц проекта.
>  (c) bem.info

Одним из решений может быть:

1. Проход по всем нодам и копирование deps в общую (`common`) папку (см. `deps-provider`);

2. Мердж всех депсов в один (см. `deps-merge`).

Разберем на примере:

Предположим, есть 3 ноды:

* `pages/index`
* `pages/search`
* `pages/order`

У каждой страницы свои уникальными стили и скрипты. Нам нужно собрать общий `js` и `css` с этих страниц и положить их внутрь `pages/common/` как `common.js` и `common.css` соответственно.

```javascript
// Пробегаемся по всем директориям внутри "pages"
// ...
config.nodeMask(/pages\/.*/, function (nodeConfig) {
    // Если текущая нода common
    if (nodeConfig.getPath() === 'pages/common') {
        nodeConfig.addTechs([
            [ require("enb/techs/levels"), { levels: getLevels() } ],
            require("enb/techs/files"),

            // Копируем депсы с каждоый страницы внутрь текущей ноды (pages/common)
            [ require('enb/techs/deps-provider'), { sourceNodePath: 'pages/index', depsTarget: 'index.deps.js' } ],
            [ require('enb/techs/deps-provider'), { sourceNodePath: 'pages/search', depsTarget: 'search.deps.js' } ],
            [ require('enb/techs/deps-provider'), { sourceNodePath: 'pages/order', depsTarget: 'order.deps.js' } ],

            // Склеиваем наши депсы в один (common.deps.js)
            [ require('enb/techs/deps-merge'), { depsSources: ['index.deps.js', 'search.deps.js', 'order.deps.js'] } ],

            require("enb/techs/js"),
            require("enb/techs/css"),
            [ require("enb/techs/css"), {target: '?.ie9.css', sourceSuffixes: ['css', 'ie9.css'] } ]
        ]);
        // Собираем необходимые файлы
        nodeConfig.addTargets(["_?.js", "_?.css", "_?.ie9.css"]);
    } else {
        nodeConfig.addTechs([
            [ require("enb/techs/levels"), { levels: getLevels() } ],
            [ require("enb/techs/file-provider"), { target: "?.bemjson.js" } ],
            require("enb/techs/bemdecl-from-bemjson"),
            require("enb/techs/deps-old"),
            require("enb/techs/files"),
            require("enb-bemhtml/techs/bemhtml"),
            require("enb/techs/html-from-bemjson"),
            require("enb/techs/js"),
            [ require("enb/techs/i18n-merge-keysets"), { lang: "all" }],
            [ require("enb/techs/i18n-merge-keysets"), { lang: "{lang}" }],
            [ require("enb/techs/i18n-lang-js"), { lang: "all" } ],
            [ require("enb/techs/i18n-lang-js"), { lang: "{lang}" } ],
            [ require("enb/techs/js-i18n"), { lang: "{lang}" } ],
            require("enb/techs/css"),
            [ require("enb/techs/css"), {target: '?.ie9.css', sourceSuffixes: ['css', 'ie9.css'] } ]
        ]);
        nodeConfig.addTargets(["_?.js", "_?.css", "_?.ie9.css", "?.html"]);
    }

    function getLevels() {
        return [
            {"path":"bem-bl/blocks-common","check":false},
            {"path":"bem-bl/blocks-touch","check":false},
            {"path":"blocks","check":true}
        ].map(function(l) { return config.resolvePath(l); });
    }
});
// ...
```

Обратите внимание, директория `pages/common` должна существовать. Её можно создавать динамически.
```javascript
// ...
  // Создание директории common
  if (!fs.existsSync('pages/common')) {
      fs.mkdirSync('pages/common');
  }
// ...
```

Конечно, если у вас много страниц и постоянно добавляются новые, то лучше обрабатывать это динамически:

Необходимо подключать модуль `fs`
```javascript
var fs = require('fs');
//...
if (nodeConfig.getPath() === 'touch.bundles/common') {
    var pagesDeps = [],
        addTechsAttrs = [
            [ require("enb/techs/levels"), { levels: getLevels() } ],
            require("enb/techs/files"),
            require("enb/techs/js"),
            require("enb/techs/css"),
            require("enb/techs/css-ie9")
        ];

    // Проходимся по существующим страницам
    fs.readdirSync('touch.bundles').map(function (page) {
        if (page !== 'common') {
            // Копируем депсы с каджой страницы внутрь common
            addTechsAttrs.push([ require('enb/techs/deps-provider'), { sourceNodePath: 'touch.bundles/' + page, depsTarget: page + '.deps.js' } ]);

            pagesDeps.push(page + '.deps.js');
        }
    });

    // Мерджим все полученные депмы в один - common.deps.js
    addTechsAttrs.push([ require('enb/techs/deps-merge'), { depsSources: pagesDeps } ]);

    // прокидываем атрибуты
    nodeConfig.addTechs(addTechsAttrs);
    nodeConfig.addTargets(["_?.js", "_?.css", "_?.ie9.css"]);
}
//...
```

Подробное описание актуальных технологий
========================================

В алфавитном порядке.

Все технологии, включенные в пакет `ENB`, находятся в папке `techs` пакета. Подключаются из make-файла с помощью `require('enb/techs/<tech-name>')`. Например, `require('enb/techs/js')`. Подключаются к ноде указанием класса и опций: `nodeConfig.addTech([ require('enb/techs/<tech-name>'), {/* [options] */} ]);`, либо без опций: `nodeConfig.addTech(require('enb/techs/<tech-name>'));`.

Если при настройке технологии в опциях указана подстрока `{lang}`, то будет создано столько копий технологии, сколько языков установлено для ноды или проекта (если у ноды не указаны языки).

Например:

```javascript
nodeConfig.setLanguages(['ru', 'en', 'tk']);
nodeConfig.addTech([require('js-i18n'), { target: '?.{lang}.js', lang: '{lang}' }]);
```

Эквивалентно:

```javascript
nodeConfig.addTech([require('js-i18n'), { target: '?.ru.js', lang: 'ru' }]);
nodeConfig.addTech([require('js-i18n'), { target: '?.en.js', lang: 'en' }]);
nodeConfig.addTech([require('js-i18n'), { target: '?.tk.js', lang: 'tk' }]);
```

bemdecl-from-bemjson
--------------------

Технология переехала в пакет `enb-bem-techs`.

bemdecl-from-deps-by-tech
-------------------------

Технология переехала в пакет `enb-bem-techs`.

bemdecl-merge
-------------

Технология переехала в пакет `enb-bem-techs`.

bemdecl-provider
----------------

Технология переехала в пакет `enb-bem-techs`.

borschik
--------

Технология переехала в пакет `enb-borschik`.

browser-js
----------

Технология переехала в пакет `enb-diverse-js`.

css
---

Склеивает *css*-файлы по deps'ам, обрабатывает инклуды и ссылки, сохраняет в виде `?.css`.

**Опции**

* *String* **target** — Результирующий таргет. По умолчанию `?.css`.
* *String* **filesTarget** — files-таргет, на основе которого получается список исходных файлов (его предоставляет технология `files`). По умолчанию — `?.files`.
* *String* **sourceSuffixes** — суффиксы файлов, по которым строится `files`-таргет. По умолчанию — `'css'`.

**Пример**

```javascript
nodeConfig.addTech(require('enb/techs/css'));
```

css-borschik-chunks
-------------------

Технология переехала в пакет `enb-borschik`.

css-chunks
----------

Технология переехала в пакет `enb-bembundle`.

css-ie
------

Технология устарела. Используйте технологию `css` с опцией `sourceSuffixes`.

css-ie6
-------

Технология устарела. Используйте технологию `css` с опцией `sourceSuffixes`.

css-ie7
-------

Технология устарела. Используйте технологию `css` с опцией `sourceSuffixes`.

css-ie8
-------

Технология устарела. Используйте технологию `css` с опцией `sourceSuffixes`.

css-ie9
-------

Технология устарела. Используйте технологию `css` с опцией `sourceSuffixes`.

css-ie-includes
---------------

Технология устарела. Используйте технологию `css-includes` с опцией `sourceSuffixes`.

css-includes
------------

Собирает *css*-файлы по deps'ам инклудами, сохраняет в виде `?.css`. Может пригодиться в паре с ycssjs (как fastcgi-модуль).

**Опции**

* *String* **target** — Результирующий таргет. По умолчанию `?.css`.
* *String* **filesTarget** — files-таргет, на основе которого получается список исходных файлов (его предоставляет технология `files`). По умолчанию — `?.files`.
* *String* **sourceSuffixes** — суффиксы файлов, по которым строится `files`-таргет. По умолчанию — `'css'`.

**Пример**

```javascript
nodeConfig.addTech(require('enb/techs/css-includes'));
```

css-less
--------

Технология устарела и будет удалена.

css-stylus
----------

Технология переехала в пакет `enb-stylus`.

css-stylus-with-nib
-------------------

Технология переехала в пакет `enb-stylus`.

deps
----

Технология переехала в пакет `enb-bem-techs`.

deps-merge
----------

Технология переехала в пакет `enb-bem-techs`.

deps-old
--------

Технология переехала в пакет `enb-bem-techs`.

deps-provider
-------------

Технология переехала в пакет `enb-bem-techs`.

deps-subtract
-------------

Технология переехала в пакет `enb-bem-techs`.

file-copy
---------

Копирует один таргет в другой.
Может, например, использоваться для построения `_?.css` из `?.css` для development-режима.

**Опции**

* *String* **sourceTarget** — Исходный таргет. Обязательная опция.
* *String* **destTarget** — Результирующий таргет. Обязательная опция.

**Пример**

```javascript
nodeConfig.addTech([ require('enb/techs/file-copy'), {
  sourceTarget: '?.css',
  destTarget: '_?.css'
} ]);
```

file-merge
----------

Склеивает набор файлов в один.

**Опции**

* *String[]* **sources** — Список исходных таргетов. Обязательная опция.
* *String* **target** — Результирующий таргет. Обязательная опция.
* *String* **divider** — Строка для склеивания файлов. По умолчанию — "\n".
* *Boolean* **sourcemap** — Построение карт кода (source maps) с информацией об исходных файлах.

**Пример**

```javascript
nodeConfig.addTech([ require('enb/techs/file-merge'), {
    sources: ['?.bemhtml', '?.pre.js']
    target: '?.js'
} ]);
```

file-provider
-------------

Предоставляет существующий файл для make-платформы.
Может, например, использоваться для предоставления исходного *bemdecl*-файла.

**Опции**

* *String* **target** — Таргет. Обязательная опция.

**Пример**

```javascript
nodeConfig.addTech([ require('enb/techs/file-provider'), { target: '?.bemdecl.js' } ]);
```

files
-----

Технология переехала в пакет `enb-bem-techs`.

html-from-bemjson
-----------------

Технология устарела. Используйте технологию из пакета вашего шаблонизатора: `enb-xjst`, `enb-bemxjst`, `enb-bh`.

html-from-bemjson-i18n
----------------------

Технология устарела. Используйте технологию из пакета вашего шаблонизатора: `enb-xjst`, `enb-bemxjst`, `enb-bh`.

i18n-keysets-xml
----------------

Технология переехала в пакет `enb-bem-i18n`.

i18n-lang-js
------------

Технология переехала в пакет `enb-bem-i18n`.

i18n-lang-js-chunks
-------------------

Технология переехала в пакет `enb-bembundle`.

i18n-merge-keysets
------------------

Технология переехала в пакет `enb-bem-i18n`.

js
--

Склеивает *js*-файлы по deps'ам, сохраняет в виде `?.js`.

**Опции**

* *String* **target** — Результирующий таргет. По умолчанию — `?.js`.
* *String* **filesTarget** — files-таргет, на основе которого получается список исходных файлов (его предоставляет технология `files`). По умолчанию — `?.files`.
* *String* **sourceSuffixes** — суффиксы файлов, по которым строится `files`-таргет. По умолчанию — `'js'`.

**Пример**

```javascript
nodeConfig.addTech(require('enb/techs/js'));
```

js-bundle-component
-------------------

Технология переехала в пакет `enb-bembundle`.

js-bembundle-component-i18n
---------------------------

Технология переехала в пакет `enb-bembundle`.

js-bundle-page
--------------

Технология переехала в пакет `enb-bembundle`.

js-bembundle-page-i18n
----------------------

Технология переехала в пакет `enb-bembundle`.

js-chunks
---------

Технология переехала в пакет `enb-bembundle`.

js-expand-includes
------------------

Обрабатывает инклуды в исходном `js`-файле и собирает результирующий файл. При раскрытии инклудов, если имя подключенного файла является таргетом, то ждет его выполнения.

**Опции**

* *String* **sourceTarget** — Исходный JS-таргет. Обязательная опция.
* *String* **destTarget** — Результирующий JS-таргет. Обязательная опция.

**Пример**

```javascript
nodeConfig.addTech([ require('enb/techs/js-expand-includes'), { sourceTarget: '?.run-tests.js', destTarget: '_?.run-tests.js' } ]);
```

js-i18n
-------

Собирает `js`-файл по deps'ам и добавляет в результат таргет `?.lang.<язык>.js`. Используется с технологией `i18n-lang-js`.

**Опции**

* *String* **filesTarget** — files-таргет, на основе которого получается список исходных файлов (его предоставляет технология `files`). По умолчанию — `?.files`.
* *String* **sourceSuffixes** — суффиксы файлов, по которым строится `files`-таргет. По умолчанию — `'js'`.
* *String* **target** — Результирующий таргет. По умолчанию — `?.{lang}.js`.
* *String* **lang** — Язык, для которого необходимо собрать файл.

**Пример**

```javascript
nodeConfig.addTech([ require('enb/techs/js-i18n'), { lang: '{lang}' } ]);
```

js-includes
-----------

Собирает *js*-файлы по deps'ам инклудами, сохраняет в виде `?.js`. Может пригодиться в паре с ycssjs (как fastcgi-модуль).

**Опции**

* *String* **filesTarget** — files-таргет, на основе которого получается список исходных файлов (его предоставляет технология `files`). По умолчанию — `?.files`.
* *String* **sourceSuffixes** — суффиксы файлов, по которым строится `files`-таргет. По умолчанию — `'js'`.

**Пример**

```javascript
nodeConfig.addTech(require('enb/techs/js-includes'));
```

levels
------

Технология переехала в пакет `enb-bem-techs`.

node-js
-------

Технология переехала в пакет `enb-diverse-js`.

priv-js
-------

Технология переехала в пакет `enb-priv-js`.

priv-js-i18n
------------

Технология переехала в пакет `enb-priv-js`.

priv-js-i18n-all
----------------

Технология переехала в пакет `enb-priv-js`.

pub-js-i18n
-----------

Технология переехала в пакет `enb-priv-js`.

symlink
-------

Создает симлинк из одного таргета в другой. Может, например, использоваться для построения `_?.css` из `?.css` для development-режима.

**Опции**

* *String* **fileTarget** — Исходный таргет. Обязательная опция.
* *String* **symlinkTarget** — Результирующий таргет. Обязательная опция.

**Пример**

```javascript
nodeConfig.addTech([ require('enb/techs/symlink'), {
  fileTarget: '?.css',
  symlinkTarget: '_?.css'
} ]);
```

vanilla-js
----------

Технология переехала в пакет `enb-diverse-js`.

xsl
---

Технология переехала в пакет `enb-lego-xml`.

xsl-2lego
---------

Технология переехала в пакет `enb-lego-xml`.

xsl-convert2xml
---------------

Технология переехала в пакет `enb-lego-xml`.

xsl-html5
---------

Технология переехала в пакет `enb-lego-xml`.

xsl-html5-i18n
--------------

Технология переехала в пакет `enb-lego-xml`.

xslt
----

Технология переехала в пакет `enb-lego-xml`.

## Как написать свою технологию

С версии 0.8 технологии рекомендуется писать с использованием хэлпера `BuildFlow`.

Исходный код хэлпера: https://github.com/enb-make/enb/blob/master/lib/build-flow.js

В данном руководстве охвачены не все возможности `BuildFlow`. Полный перечень методов с описанием находится
в JSDoc файла `build-flow.js`.

### Теория

Цель технологии — собирать таргет в ноде. Например, технология `css` может собрать `index.css` в ноде `pages/index`
на основе `css`-файлов по уровням переопределения.

Каждая технология умеет принимать настройки.
Хэлпер `BuildFlow` способствует тому, чтобы максимальное количество параметров было настраиваемым.

Технологии могут использовать результат выполнения других технологий. Например, список исходных `css`-файлов
строится с помощью технологии `files`.

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

### Технология для склеивания файлов по суффиксу

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

```javascript
module.exports = require('enb/lib/build-flow').create() // Создаем инстанцию BuildFlow
    .name('js') // Выбираем имя для технологии
    .target('target', '?.js') // Имя опции для задания имени результирующего файла и значение по умолчанию
    .useFileList('js') // Указываем, какие суффиксы нас интересуют при сборке
    .justJoinFilesWithComments() // Еще один хэлпер. Склеивает результат, обрамляя комментариями вида /* ... */
                                 // в которых указывается путь к исходному файлу, из которого был сформирован фрагмент.
    .createTech(); // Создаем технологию с помощью хэлпера
```

Этот пример, конечно очень общий и слишком упрощенный.

Рассмотрим аналог этой технологии без использования `justJoinFilesWithComments`:

```javascript
var Vow = require('vow'); // Используемая в ENB библиотека промисов
var vowFs = require('vow-fs'); // Работа с файловой системой на основе Vow

module.exports = require('enb/lib/build-flow').create()
    .name('js')
    .target('target', '?.js')
    .useFileList('js')
    .builder(function(jsFiles) { // Будем возвращать промис, чтобы ENB ждал выполнения асинхронной технологии
        var node = this.node; // Сохраняем ссылку на инстанцию класса `Node`.
        return Vow.all(jsFiles.map(function(file) { // Ждем выполнения всех промисов
            return vowFs.read(file.fullname, 'utf8').then(function(data) { // Читаем каждый исходный файл
                var filename = node.relativePath(file.fullname); // Получаем путь относительно ноды
                // Строим фрагменты из содержимого исходных файлов
                return '/* begin: ' + filename + ' *' + '/\n' + data + '\n/* end: ' + filename + ' *' + '/';
            });
        })).then(function(contents) { // Получили результат обработки всех исходных файлов
            return contents.join('\n'); // Объединяем полученные фрагменты с помощью перевода строки
        });
    })
    .createTech();
```

Так как мы использовали метод `useFileList`, в `builder` пришел аргумент со списком файлов по указанному суффиксу.
Каждый `use`-метод добавляет аргумент в `builder`. Тип и содержимое аргументов зависят от того, какой `use`-метод был
использован.

Добавим к получившейся технологии файлы интернационализации:

```javascript
var Vow = require('vow'); // Используемая в ENB библиотека промисов
var vowFs = require('vow-fs'); // Работа с файловой системой на основе Vow

module.exports = require('enb/lib/build-flow').create()
    .name('js')
    .target('target', '?.js')
    .defineRequiredOption('lang') // Определяем обязательную опцию lang для задания языка
    .useFileList('js')

    .useSourceText('allLangTarget', '?.lang.all.js') // Подключаем общую для всех языков интернационализацию,
                                                     // используя метод useSourceText, который добавляет в
                                                     // builder содержимое указанного файла в виде аргумента

    .useSourceText('langTarget', '?.lang.{lang}.js') // Подключаем кейсеты конкретного языка;
                                                     // здесь используется значение опции lang для того,
                                                     // чтобы сформировать значение по умолчанию

    .builder(function(jsFiles, allLangText, langText) {
        var node = this.node;
        return Vow.all(jsFiles.map(function(file) {
            return vowFs.read(file.fullname, 'utf8').then(function(data) {
                var filename = node.relativePath(file.fullname);
                return '/* begin: ' + filename + ' *' + '/\n' + data + '\n/* end: ' + filename + ' *' + '/';
            });
        })).then(function(contents) {
            return contents
                .concat([allLangText, langText]) // Добавляем фрагменты содержимого файлов интернационализации
                .join('\n');
        });
    })
    .createTech();
```

### Технология для склеивания нескольких целей

Рассмотрим готовый пример:

```javascript
// В данном примере строится локализованный priv.js
module.exports = require('enb/lib/build-flow').create()
    .name('priv-js-i18n')
    .target('target', '?.{lang}.priv.js')
    .defineRequiredOption('lang')

    // Все эти цели подготавливаются другими технологиями:

    .useSourceFilename('allLangTarget', '?.lang.all.js') // Устанавливаем зависимость от имени файла
                                                         // общей интернационализации

    .useSourceFilename('langTarget', '?.lang.{lang}.js') // Устанавливаем зависимость от имени файла
                                                         // конкретного языка

    .useSourceFilename('privJsTarget', '?.priv.js') // Устанавливаем зависимость от имени файла
                                                    // priv-js файла

    .justJoinFilesWithComments() // Пользуемся хэлпером для склеивания

    .createTech();
```

Реализуем склеивание без хэлпера:

```javascript
module.exports = require('enb/lib/build-flow').create()
    .name('priv-js-i18n')
    .target('target', '?.{lang}.priv.js')
    .defineRequiredOption('lang')
    .useSourceFilename('allLangTarget', '?.lang.all.js')
    .useSourceFilename('langTarget', '?.lang.{lang}.js')
    .useSourceFilename('privJsTarget', '?.priv.js')

    .builder(function(allLangFilename, langFilename, privJsFilename) {
        var node = this.node;
        // Перебираем исходные файлы
        return Vow.all([allLangFilename, langFilename, privJsFilename].map(function(absoluteFilename) {
            // Читаем каждый исходный файл
            return vowFs.read(absoluteFilename, 'utf8').then(function(data) {
                // Получаем относительный путь к файлу
                var filename = node.relativePath(absoluteFilename);

                // Формируем фрагмент
                return '/* begin: ' + filename + ' *' + '/\n' + data + '\n/* end: ' + filename + ' *' + '/';
            });
        })).then(function(contents) {
            return contents.join('\n'); // Склеиваем фрагменты
        });
    })

    .createTech();
```

### Зависимости от файлов, не входящих в сборку

Например, нам надо добавить модульную систему в начало какого-нибудь файла и сохранить результат под новым именем:

```javascript
var vowFs = require('vow-fs'); // Подключаем модуль для работы с файловой системой
var path = require('path'); // Подключаем утилиты работу с путями

module.exports = require('enb/lib/build-flow').create()
    .name('prepend-modules')
    .target('target', '?.js')
    .defineRequiredOption('source') // Указываем обязательную опцию
    .useSourceText('source', '?') // Устанавливаем зависимость от содержимого цели, задаваемой опцией source
    .needRebuild(function(cache) { // Указываем дополнительную проверку кэша
        // В данном случае модульная система не находится в исходных уровнях переопределения,
        // но ее можно найти в пакете ym; для того, чтобы пересборка правильно работало в случае
        // изменения содержимого файла modules.js, добавляем проверку
        this._modulesFile = path.join(__dirname, '..', 'node_modules', 'ym', 'modules.js'); // Формируем путь
        return cache.needRebuildFile( // Проверяем, изменился ли файл
            'modules-file', // Ключ для кэширования данных о файле; должен быть уникален в рамках технологии
            this._modulesFile // Путь к файлу, для которого необходимо проверить кэш
        );
    })
    .saveCache(function(cache) { // Сохраняем в кэш данные об использованном файле
        cache.cacheFileInfo( // Сохраняем в кэш информацию о файле
            'modules-file', // Ключ для кэширования данных о файле; должен быть уникален в рамках технологии
            this._modulesFile // Путь к файлу, для которого необходимо проверить кэш
        );
    })
    .builder(function(preTargetSource) {
        // Считываем содержимое файла модульной системы
        return vowFs.read(this._modulesFile, 'utf8').then(function(modulesRes) {
            return modulesRes + preTargetSource; // Объединяем результаты
        });
    })
    .createTech();
```

### То же самое, но чуть-чуть иначе

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

Например, нам нравится, как работает технология `css`:

```javascript
module.exports = require('enb/lib/build-flow').create()
    .name('css')
    .target('target', '?.css')
    .useFileList('css')
    .builder(function(cssFiles) {
        // ...
    })
    .methods({
        // ...
    })
    .createTech();
```

В каждой технологии, сделанной с помощью `BuildFlow`, есть метод `buildFlow()`, который можно вызвать для того,
чтобы создать новую технологию на основе функционала существующей.

В какой-то момент нам понадобилось вместе с суффиксами `css` собирать еще и `light.css`.
Для этого надо написать новую технологию, заимствуя функционал старой:

```javascript
module.exports = require('enb/techs/css').buildFlow()
    .name('css-light') // Изменяем имя
    .useFileList(['css', 'light.css']) // Изменяем нужные параметры
    .createTech();
```

## Node API

Каждой технологии в `init` приходит инстанция ноды, для которой необходимо собирать таргеты.
Через ноду технология взаимодействует с процессом сборки.

Основные методы класса Node:

node.getTargetName
------------------

```javascript
// Возвращает имя таргета ноды без суффикса. Например, для ноды 'pages/index' результат — index.
String Node::getTargetName()
// Возвращает имя таргета ноды с суффиксом. Например, для ноды 'pages/index' с суффиксом 'js' результат — 'index.js'.
String Node::getTargetName(String suffix)
```

node.unmaskTargetName
---------------------

```javascript
// Демаскирует имя таргета ноды. Например, для ноды 'pages/index' и maskedTargetName='?.css', результат — 'index.css'.
String Node::unmaskTargetName(String maskedTargetName)
```

node.resolvePath
----------------

```javascript
// Возвращает абсолютный путь к таргету.
String Node::resolvePath(String targetName)
```

**Пример**

```javascript
var fs = require('fs');
fs.writeFileSync(this.node.resolvePath(this.node.getTargetName('js')), 'alert("Hello World!");', 'utf8');
```

node.resolveTarget
------------------

```javascript
// Оповещает ноду о том, что таргет собран. Опционально принимает результат сборки.
// Результатом может быть любой объект, который может быть полезен другим технологиям для продолжения сборки.
undefined Node::resolveTarget(String targetName[, Object result])
```

**Примеры**

```javascript
// #1
this.node.resolveTarget('index.css');

// #2 Получаем имя таргета динамически с помощью суффикса.
this.node.resolveTarget(this.node.getTargetName('css'));

// #3 Получаем имя таргета путем демаскирования таргета.
this.node.resolveTarget(this.node.unmaskTargetName('?.css'));

// #4 Передаем значение.
var target = this.node.unmaskTargetName('?.deps.js'),
    targetPath = this.node.resolvePath(target);
delete require.cache[targetPath]; // Избавляемся от кэширования в nodejs.
this.node.resolveTarget(target, require(targetPath));
```

node.rejectTarget
------------------

```javascript
// Оповещает ноду о том, что таргет не может быть собран из-за ошибки.
undefined Node::rejectTarget(String targetName, Error error)
```

**Примеры**

```javascript
// #1
this.node.rejectTarget('index.css', new Error('Could not find CSS Tools.'));

// #2 Получаем имя таргета динамически с помощью суффикса.
this.node.rejectTarget(this.node.getTargetName('css'), new Error('Could not find CSS Tools.'));
```

node.requireSources
-------------------

```javascript
// Требует у ноды таргеты для дальнейшей сборки, возвращает промис.
// Промис выполняется, возвращая массив результатов, которыми резолвились требуемые таргеты.
// ВАЖНО: Не все технологии резолвят таргеты с результатом.
// В данный момент резолвят с результатом технологии: levels, deps*, files.
Promise(Object[]) Node::requireSources(String[] targetNames)
```

**Пример**

Например, нам надо объединить в один файл `index.css` и `index.ie.css` и записать в `index.all.css`.

```javascript
var vowFs = require('vow-fs');
// ...
  build: function() {
    var _this = this;
    return this.node.requireSources(['index.css', 'index.ie.css']).then(function() {
      return Vow.all([vowFs.read(_this.node.resolvePath('index.css'), 'utf8'), vowFs.read(_this.node.resolvePath('index.ie.css'), 'utf8')]).then(function(res) {
        return vowFs.write(_this.node.resolvePath('index.all.css'), res.join('\n'), 'utf8').then(function() {
          _this.node.resolveTarget('index.all.css');
        });
      });
    });
  }
// ...
```

Пример использования: [Технология deps](/mdevils/enb/blob/master/techs/deps.js#L31)

node.relativePath
-----------------

```javascript
// Возвращает относительный путь к таргету относительно ноды.
String Node::relativePath(String targetName)
```

Пример использования: [Технология css-includes](/mdevils/enb/blob/master/techs/css-includes.js#L16)

node.getDir
-----------

```javascript
// Возвращает полный путь к папке ноды.
String Node::getDir()
```

node.getRootDir
---------------

```javascript
// Возвращает полный путь к корневой папке проекта.
String Node::getRootDir()
```

node.getLogger
--------------

[Logger](/mdevils/enb/blob/master/lib/logger.js)

```javascript
// Возвращает инстанцию логгера для ноды.
Logger Node::getLogger()
```

**Пример**

```javascript
this.node.getLogger().log('Hello World');
```

Пример использования: [Технология deps](/mdevils/enb/blob/master/techs/deps.js#L79)

node.getNodeCache
-----------------

[Cache](/mdevils/enb/blob/master/lib/cache/cache.js)

```javascript
// Возвращает инстанцию кэша для таргета ноды.
Cache Node::getNodeCache(String targetName)
```

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

С помощью методов `Boolean needRebuildFile(String cacheKey, String filePath)` и `Boolean needRebuildFileList(String cacheKey, FileInfo[] files)` производится валидация кэша.

С помощью методов `undefined cacheFileInfo(String cacheKey, String filePath)` и `undefined cacheFileList(String cacheKey, FileInfo[] files)` производится сохранение информации о файлах в кэш.

Пример использования:

* Валидация кэша: [Технология deps](/mdevils/enb/blob/master/techs/deps.js#L33)
* Кэширование результатов сборки: [Технология deps](/mdevils/enb/blob/master/techs/deps.js#L73)

node.getSharedResources
-----------------------

[SharedResources](lib/shared-resources/index.js)

Набор ресурсов, которые могут быть использованы в технологиях:
- [JobQueue](lib/shared-resources/job-queue/index.js) - пул дочерних процессов для выполнения "тяжелых" задач

**Пример**

Контент файла `some-processor.js`:
```js
module.exports = function(arg1, arg2) {
    var res = null;
    // Здесь какая-то нагруженная работа, возможно с использованием промисов
    return res;
}
```
В технологии:
```js
var jobQueue = this.node.getSharedResources().jobQueue;
// Выполнить таску в отдельном процессе, возвращается промис с результатом
return jobQueue.push(require.resolve('./path/to/processor'), arg1, arg2);
```


text:
Herzlich Willkommen bei AnFeSys

	<alert data-ng-show="alert" close="alert=''">

{{alert}}

	</alert>

			<button class="btn btn-primary" ng-click="go('/newSurvey')">Neue Umfrage erstellen</button>

{{s[0]}} {{s[1].length}}

Keine Umfragen vorhanden

							Letzte Änderung
								Start
								Start

							Ende
							Titel

							Teilnahme

							{{survey.data.start}}

							{{survey.data.end}}
							{{survey.data.title}}

								<progressbar max="!survey.open ? survey.data.countRecipients : survey.data.countAnswers " value="survey.data.countAnswers" style="text-color:black">{{survey.data.countAnswers}} {{survey.open ? '' : ' / ' + survey.data.countRecipients}}
								</progressbar>

										<button class="btn btn-default" ng-Click="edit($index)">Bearbeiten</button>
										<button class="btn btn-info" ng-Click="activate($index)">Freischalten</button>
										<button class="btn btn-danger" ng-Click="delete(0, $index)">Löschen</button>

										<input type="button" class="btn btn-default" ng-Click="close($index)" value="Beenden" />
										<input type="button" class="btn btn-danger" ng-Click="delete(1, $index)" value="Löschen" />

										<public-link data-ng-show="survey.data.keepAfterUse" url="baseUrl + survey.data.token"></public-link>
									  	<input type="button" class="btn btn-default" data-ng-show="survey.data.keepAfterUse"ng-click="gotoLinks(survey.data.token)" value="Kurz-Links"/>

										<button class="btn btn-default" ng-Click="viewResults($index)">Auswertung</button>
										<button class="btn btn-success" ng-Click="restart($index)">Wiederverwenden</button>
										<button class="btn btn-danger" ng-Click="delete(2, $index)">Löschen</button>

redpajama_set_name: RedPajamaGithub


text:
package moe.tristan.easyfxml.fxkit.form.defaults;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javafx.beans.value.ObservableValue;

import moe.tristan.easyfxml.fxkit.form.FormFieldController;

public abstract class StringFormFieldController extends FormFieldController<String> {

    private static final Logger LOGGER = LoggerFactory.getLogger(StringFormFieldController.class);

    @Override
    public void initialize() {
        // by default, every change is made to run through validation
        validateValueOnChange();
    }

    public abstract ObservableValue<String> getObservableValue();

    @Override
    public String getFieldValue() {
        return getObservableValue().getValue();
    }

    protected boolean isNullOrBlank() {
        final String fieldValue = getFieldValue();
        return fieldValue == null || fieldValue.isBlank();
    }

    private void validateValueOnChange() {
        getObservableValue().addListener((o, prev, cur) -> {
            LOGGER.debug("Validating field [{}] -> \"{}\"", getFieldName(), getFieldValue());
            validate(cur);
        });
    }

}


text:
package org.goldonship.designpattern.behavioral.templatemethod;

public class TemplateMethod {
    private Game game;

    public TemplateMethod(Game game) {
        this.game = game;
    }

    public void setGame(Game game) {
        this.game = game;
    }

    public void play(){
        this.game.play();
    }
}


text:
package org.scijava.jupyter.kernel.evaluator;

import com.twosigma.beakerx.jvm.object.SimpleEvaluationObject;
import com.twosigma.beakerx.mimetype.MIMEContainer;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;

import org.scijava.Context;
import org.scijava.convert.ConvertService;
import org.scijava.log.LogService;
import org.scijava.module.ModuleException;
import org.scijava.module.ModuleRunner;
import org.scijava.module.process.PostprocessorPlugin;
import org.scijava.module.process.PreprocessorPlugin;
import org.scijava.notebook.converter.output.NotebookOutput;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.PluginService;
import org.scijava.script.ScriptInfo;
import org.scijava.script.ScriptLanguage;
import org.scijava.script.ScriptModule;
import org.scijava.util.ClassUtils;

public class Worker implements Runnable {

    @Parameter
    private LogService log;

    @Parameter
    private Context context;

    @Parameter
    private PluginService pluginService;

    @Parameter
    private ConvertService convertService;

    private final Map<String, ScriptEngine> scriptEngines;
    private final Map<String, ScriptLanguage> scriptLanguages;
    private String languageName;

    SimpleEvaluationObject seo = null;
    String code = null;

    Worker(Context context, Map<String, ScriptEngine> scriptEngines, Map<String, ScriptLanguage> scriptLanguages) {
	context.inject(this);
	this.scriptEngines = scriptEngines;
	this.scriptLanguages = scriptLanguages;
    }

    public void setup(SimpleEvaluationObject seo, String code, String languageName) {
	this.seo = seo;
	this.code = code;
	this.languageName = languageName;
    }

    @Override
    public void run() {

	ScriptLanguage scriptLanguage = this.scriptLanguages.get(this.languageName);
	ScriptEngine scriptEngine = this.scriptEngines.get(this.languageName);

	final Reader input = new StringReader(this.code);
	final ScriptInfo info = new ScriptInfo(context, "dummy.py", input);
	info.setLanguage(scriptLanguage);
	this.seo.setOutputHandler();

	try {
	    // create the ScriptModule instance
	    final ScriptModule module = info.createModule();
	    context.inject(module);

	    // HACK: Inject our cached script engine instance, rather
	    // than letting the ScriptModule instance create its own.
	    final Field f = ClassUtils.getField(ScriptModule.class, "scriptEngine");
	    ClassUtils.setValue(f, module, scriptEngine);

	    // execute the code
	    final List<PreprocessorPlugin> pre = pluginService.createInstancesOfType(PreprocessorPlugin.class);
	    final List<PostprocessorPlugin> post = pluginService.createInstancesOfType(PostprocessorPlugin.class);
	    final ModuleRunner runner = new ModuleRunner(context, module, pre, post);
	    runner.run();

	    // accumulate the outputs into an ordered map
	    final Map<String, Object> outputTable = new LinkedHashMap<>();
	    info.outputs().forEach(output -> {
		final String name = output.getName();
		final Object value = output.getValue(module);
		if (value != null) {
		    outputTable.put(name, value);
		}
	    });

	    // convert result into a notebook-friendly form
	    Object output = null;
	    try {
		if (outputTable.size() == 0) {
		    output = null;

		} else if (outputTable.size() == 1) {
		    output = outputTable.values().toArray()[0];
		    if (!(output instanceof MIMEContainer)) {
			output = convertService.convert(output, NotebookOutput.class);
			if (output == null) {
			    log.warn("[WARNING] No suitable converter found");
			    output = outputTable.values().toArray()[0];
			}
		    }

		} else {
		    output = convertService.convert(outputTable,
			    NotebookOutput.class);
		    if (output == null) {
			log.warn("[WARNING] No suitable converter found");
			output = outputTable;
		    }
		}
	    } catch (Exception e) {
		e.printStackTrace();
		output = "[ERROR]";
	    } finally {
		this.seo.finished(output);
	    }

	    this.syncBindings(scriptEngine, scriptLanguage);
	} catch (final ThreadDeath ex) {
	    seo.error("Execution canceled");
	    log.error(ex);
	} catch (final ModuleException t) {
	    seo.error(t.getMessage());
	    log.error(t);
	}

	this.seo.clrOutputHandler();
	this.seo.executeCodeCallback();
    }

    private void syncBindings(ScriptEngine scriptEngine, ScriptLanguage scriptLanguage) {

	Bindings currentBindings = scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE);
	this.scriptEngines.forEach((String name, ScriptEngine engine) -> {
	    Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
	    currentBindings.keySet().forEach((String key) -> {
		bindings.put(key, scriptLanguage.decode(currentBindings.get(key)));
	    });
	});

    }

}


text:
ACCEPTED

#### According to
Index Fungorum

#### Published in
null

#### Original name
Cladonia degenerans var. ramosa Nyl.

### Remarks
null


text:
L.Handler.Draw = L.Handler.extend({
	includes: L.Mixin.Events,

	initialize: function (map, options) {
		this._map = map;
		this._container = map._container;
		this._overlayPane = map._panes.overlayPane;
		this._popupPane = map._panes.popupPane;

		// Merge default shapeOptions options with custom shapeOptions
		if (options && options.shapeOptions) {
			options.shapeOptions = L.Util.extend({}, this.options.shapeOptions, options.shapeOptions);
		}
		L.Util.extend(this.options, options);
	},

	enable: function () {
		this.fire('activated');
		this._map.fire('drawing', { drawingType: this.type });
		L.Handler.prototype.enable.call(this);
	},

	disable: function () {
		this._map.fire('drawing-disabled', { drawingType: this.type });
		L.Handler.prototype.disable.call(this);
	},

	addHooks: function () {
		if (this._map) {
			L.DomUtil.disableTextSelection();

			this._label = L.DomUtil.create('div', 'leaflet-draw-label', this._popupPane);
			this._singleLineLabel = false;

			L.DomEvent.addListener(this._container, 'keyup', this._cancelDrawing, this);
		}
	},

	removeHooks: function () {
		if (this._map) {
			L.DomUtil.enableTextSelection();

			this._popupPane.removeChild(this._label);
			delete this._label;

			L.DomEvent.removeListener(this._container, 'keyup', this._cancelDrawing);
		}
	},

	_updateLabelText: function (labelText) {
		labelText.subtext = labelText.subtext || '';

		// update the vertical position (only if changed)
		if (labelText.subtext.length === 0 && !this._singleLineLabel) {
			L.DomUtil.addClass(this._label, 'leaflet-draw-label-single');
			this._singleLineLabel = true;
		}
		else if (labelText.subtext.length > 0 && this._singleLineLabel) {
			L.DomUtil.removeClass(this._label, 'leaflet-draw-label-single');
			this._singleLineLabel = false;
		}

		this._label.innerHTML =
			(labelText.subtext.length > 0 ? '' + labelText.subtext + '' + '
' : '') +
			'' + labelText.text + '';
	},

	_updateLabelPosition: function (pos) {
		L.DomUtil.setPosition(this._label, pos);
	},

	// Cancel drawing when the escape key is pressed
	_cancelDrawing: function (e) {
		if (e.keyCode === 27) {
			this.disable();
		}
	}
});


text:
 package afk.ge.tokyo.ems.factories;

import afk.ge.ems.FactoryRequest;

/**
 *
 * @author daniel
 */
public class TextLabelFactoryRequest implements FactoryRequest
{

    protected String text;
    protected Integer top, right, bottom, left;

    public TextLabelFactoryRequest(String text, int x, int y)
    {
        this(text,y,null,null,x);
    }

    public TextLabelFactoryRequest(String text, Integer top, Integer right,
            Integer bottom, Integer left)
    {
        this.text = text;
        this.top = top;
        this.right = right;
        this.bottom = bottom;
        this.left = left;
    }

    public TextLabelFactoryRequest withText(String text)
    {
        return new TextLabelFactoryRequest(text, top, right, bottom, left);
    }

    public TextLabelFactoryRequest atPosition(int x, int y)
    {
        return new TextLabelFactoryRequest(text, x, y);
    }

    public TextLabelFactoryRequest atPosition(Integer top,
            Integer right, Integer bottom, Integer left)
    {
        return new TextLabelFactoryRequest(text, top, right, bottom, left);
    }
}


text:
import {Buffer} from 'node:buffer';
import transformStream from 'easy-transform-stream';
import PluginError from 'plugin-error';

const BOM = Buffer.from('\uFEFF');

export default function gulpBom() {
	return transformStream({objectMode: true}, async file => {
		if (file.isNull()) {
			return file;
		}

		if (file.isStream()) {
			throw new PluginError('gulp-bom', 'Streaming not supported');
		}

		file.contents = Buffer.concat([BOM, file.contents]);

		return file;
	});
}


text:
FactoryGirl.define do
  factory :user do
    first_name 'Sancho'
    last_name 'Sample'

    case Squash::Configuration.authentication.strategy
    when 'password'
      sequence(:username) { |i| "user-#{i}" }
      sequence(:email_address) { |i| "default-email-#{i}@example.com" }
      password 'correct horse battery staple'

    when 'ldap'
      sequence(:username) { |i| "user-#{i}" }

    when 'google'
      sequence(:google_auth_data) do |i|
        { "email" => "email-#{i}@example.com",
          "sub" => "uid-#{i}"
        }
      end
    end
  end
end


text:
layout: v2Section
title:  Content
section: Content
---


text:
<?php

namespace PHPPromTests\Storage;

use PHPProm\Storage\DBAL;

class DBALPostgreSQLTest extends AbstractDBALTest {

    protected function setUp() {
        $connectionParams = array(
            'url' => 'postgresql://localhost:5432/postgres',
        );
        $this->connectToDatabase($connectionParams);
        $this->esc = '"';

        $sql = 'DROP TABLE IF EXISTS phpprom';
        $this->database->executeUpdate($sql);
        $sql = 'CREATE TABLE public.phpprom (
              key VARCHAR(255) PRIMARY KEY NOT NULL,
              value DOUBLE PRECISION NOT NULL
            )';
        $this->database->executeUpdate($sql);
        $sql = 'CREATE UNIQUE INDEX phpprom_key_uindex ON public.phpprom (key);';
        $this->database->executeUpdate($sql);

        $this->storage = new DBAL($this->database);
    }

}


text:
package org.HUDSystem;

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

import org.resources.ImagePack;
import org.resources.VisibleObject;
import org.rooms.Room;

public class HUD extends VisibleObject {
	public static final BufferedImage menuBack = ImagePack.getImage("menu stuff/menuback3.png"), planets = ImagePack
			.getImage("backgrounds/planets.png");
	private volatile ArrayList<VisibleObject> parts;
	public HUD(int W, int H) {
		x = 0;
		y = 0;
		w = W;
		h = H;
		parts = new ArrayList<VisibleObject>();
	}
	public HUD(int W, int H, ArrayList<VisibleObject> p) {
		this(W, H);
		parts.addAll(p);
	}
	public void add(VisibleObject v) {
		parts.add(v);
	}
	public void remove(VisibleObject v) {
		parts.remove(v);
	}
	public BufferedImage image() {
		return image();
	}
	public void reload() {
		image = new BufferedImage(w, h, 2);
		Graphics g = image.createGraphics();
		for (VisibleObject v : parts) {
			v.draw(g);
		}
		g.dispose();
	}
	public void draw(Graphics g) {
		for (VisibleObject v : parts) {
			v.draw(g);
		}
	}
}


text:
package com.amazonaws.services.licensemanager.model;

import javax.annotation.Generated;

/**
 * <p>
 * There are no entitlements found for this license, or the entitlement maximum count is reached.
 * </p>
 */
@Generated("com.amazonaws:aws-java-sdk-code-generator")
public class NoEntitlementsAllowedException extends com.amazonaws.services.licensemanager.model.AWSLicenseManagerException {
    private static final long serialVersionUID = 1L;

    /**
     * Constructs a new NoEntitlementsAllowedException with the specified error message.
     *
     * @param message
     *        Describes the error encountered.
     */
    public NoEntitlementsAllowedException(String message) {
        super(message);
    }

}


text:
package com.arondor.common.reflection.gwt.client.presenter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import com.arondor.common.reflection.bean.config.PrimitiveConfigurationBean;
import com.arondor.common.reflection.gwt.client.presenter.fields.EnumTreeNodePresenter;
import com.arondor.common.reflection.gwt.client.presenter.fields.ListTreeNodePresenter;
import com.arondor.common.reflection.gwt.client.presenter.fields.ListTreeNodePresenter.ListRootDisplay;
import com.arondor.common.reflection.gwt.client.presenter.fields.MapTreeNodePresenter;
import com.arondor.common.reflection.gwt.client.presenter.fields.PrimitiveTreeNodePresenter;
import com.arondor.common.reflection.gwt.client.presenter.fields.PrimitiveTreeNodePresenter.PrimitiveDisplay;
import com.arondor.common.reflection.gwt.client.presenter.fields.StringListTreeNodePresenter;
import com.arondor.common.reflection.gwt.client.service.GWTReflectionServiceAsync;
import com.arondor.common.reflection.model.config.PrimitiveConfiguration;
import com.arondor.common.reflection.model.java.AccessibleAnnotation;
import com.arondor.common.reflection.model.java.AccessibleClass;
import com.arondor.common.reflection.model.java.AccessibleField;
import com.arondor.common.reflection.util.PrimitiveTypeUtil;
import com.google.gwt.user.client.rpc.AsyncCallback;

public class TreeNodePresenterFactory
{
    private final static Logger LOG = Logger.getLogger(TreeNodePresenterFactory.class.getName());

    private static final String ANNOTATION_SCRIPT_SOURCE = "com.arondor.common.management.mbean.annotation.ScriptSource";

    public TreeNodePresenter createChildNodePresenter(GWTReflectionServiceAsync rpcService,
            ObjectReferencesProvider objectReferencesProvider, TreeNodePresenter.ChildCreatorDisplay display,
            AccessibleField accessibleField)
    {
        String fieldName = accessibleField.getName();
        String fieldClassName = accessibleField.getClassName();
        boolean isMandatory = accessibleField.isMandatory();
        String defaultValue = accessibleField.getDefaultValue();
        String placeholder = accessibleField.getPlaceholder();
        String classDeclaredIn = accessibleField.getDeclaredInClass();
        boolean isEnumProperty = accessibleField.isEnumProperty();
        List<String> genericTypes = accessibleField.getGenericParameterClassList();

        TreeNodePresenter childPresenter = null;
        if (PrimitiveTypeUtil.isPrimitiveType(fieldClassName))
        {
            LOG.finest("Field " + fieldName + " is primitive type, class=" + fieldClassName + ", defaultValue="
                    + defaultValue);
            PrimitiveDisplay primitiveDisplay;
            String scriptType = isScriptFieldType(accessibleField);
            if (scriptType != null)
                primitiveDisplay = display.createScriptChild(scriptType, isMandatory);
            else
                primitiveDisplay = display.createPrimitiveChild(fieldClassName, isMandatory);
            childPresenter = new PrimitiveTreeNodePresenter(primitiveDisplay);
            if (defaultValue != null && !defaultValue.equals(""))
            {
                ((PrimitiveTreeNodePresenter) childPresenter).setDefaultValue(defaultValue);
            }
            if (placeholder != null && !placeholder.equals(""))
            {
                ((PrimitiveTreeNodePresenter) childPresenter).setPlaceholder(placeholder);
            }
            PrimitiveConfiguration primitiveConfiguration = new PrimitiveConfigurationBean();
            childPresenter.setElementConfiguration(primitiveConfiguration);
        }
        else if (isEnumProperty)
        {
            LOG.finest("Field " + fieldName + " is associated with enum type");
            childPresenter = createEnumListPresenter(rpcService, display, fieldName, fieldClassName, classDeclaredIn);
        }
        else if (isStringListField(fieldClassName, genericTypes))
        {
            LOG.finest("Field " + fieldName + " is a string list");
            childPresenter = new StringListTreeNodePresenter(fieldName, display.createStringListChild(isMandatory));
            if (defaultValue != null && !defaultValue.equals(""))
            {
                ((StringListTreeNodePresenter) childPresenter)
                        .setDefaultValue(Arrays.asList(defaultValue.trim().split("\n")));
            }
            if (placeholder != null && !placeholder.equals(""))
            {
                ((StringListTreeNodePresenter) childPresenter)
                        .setPlaceholder(Arrays.asList(placeholder.trim().split("\n")));
            }
        }
        else if (fieldClassName.equals("java.util.Map") && genericTypes != null && genericTypes.size() == 2)
        {
            LOG.finest("Field " + fieldName + " is a Map<" + genericTypes.get(0) + ", " + genericTypes.get(1) + ">");
            childPresenter = new MapTreeNodePresenter(rpcService, objectReferencesProvider, genericTypes,
                    display.createMapChild(isMandatory));
        }
        else if (fieldClassName.equals("java.util.List") && genericTypes != null && genericTypes.size() == 1)
        {
            String genericType = genericTypes.get(0);
            LOG.finest("Field " + fieldName + " is a List<" + genericType + ">");
            ListRootDisplay listView = display.createListChild(isMandatory);
            childPresenter = new ListTreeNodePresenter(rpcService, objectReferencesProvider, genericType, listView);
        }
        else
        {
            LOG.finest("Field " + fieldName + " is an object " + fieldClassName);
            childPresenter = new ClassTreeNodePresenter(rpcService, objectReferencesProvider, fieldClassName,
                    isMandatory, display.createClassChild(isMandatory));
        }

        setNodeNameAndDescription(fieldName, accessibleField, childPresenter);
        return childPresenter;
    }

    private String isScriptFieldType(AccessibleField accessibleField)
    {
        if (accessibleField.getClassName().equals(String.class.getName()))
        {
            if (accessibleField.getAnnotations() != null)
            {
                AccessibleAnnotation annot = accessibleField.getAnnotations().get(ANNOTATION_SCRIPT_SOURCE);
                if (annot != null)
                {
                    return annot.getValue();
                }
            }
        }
        return null;
    }

    private TreeNodePresenter createEnumListPresenter(GWTReflectionServiceAsync rpcService,
            TreeNodePresenter.ChildCreatorDisplay display, String fieldName, final String fieldClassName,
            final String classDeclaredIn)
    {
        final EnumTreeNodePresenter childPresenter = new EnumTreeNodePresenter(fieldName,
                display.createEnumListChild(false));

        if (classDeclaredIn != null)
        {
            rpcService.getAccessibleClass(classDeclaredIn, new AsyncCallback<AccessibleClass>()
            {
                @Override
                public void onSuccess(AccessibleClass result)
                {
                    LOG.finest("Retrieve enum class : " + result.getClassBaseName());
                    Map<String, List<String>> enumValuesMap = result.getAccessibleEnums();
                    if (enumValuesMap == null || enumValuesMap.isEmpty())
                    {
                        LOG.warning("No enum type defined for class : " + fieldClassName);
                        return;
                    }
                    List<String> enumVals = enumValuesMap.get(fieldClassName);
                    if (enumVals == null || enumVals.isEmpty())
                    {
                        LOG.warning("Impossible to get enumtype values for " + fieldClassName);
                        return;
                    }
                    final List<String> enumValues = new ArrayList<String>();
                    enumValues.addAll(enumVals);

                    if (enumValues.isEmpty())
                    {
                        LOG.warning("Impossible to get values for enum : " + fieldClassName);
                    }
                    else
                    {
                        childPresenter.setEnumValues(enumValues);
                    }
                }

                @Override
                public void onFailure(Throwable caught)
                {
                }
            });

        }
        return childPresenter;
    }

    private void setNodeNameAndDescription(String fieldName, AccessibleField accessibleField,
            TreeNodePresenter childPresenter)
    {
        childPresenter.getDisplay().setNodeName(fieldName);
        if (accessibleField.getDescription() != null)
        {
            childPresenter.getDisplay().setNodeDescription(accessibleField.getDescription());
        }
        else
        {
            childPresenter.getDisplay().setNodeDescription(fieldName);
        }
        if (accessibleField.getLongDescription() != null)
        {
            childPresenter.getDisplay().setNodeLongDescription(accessibleField.getLongDescription());
        }
        if (accessibleField.isPassword())
        {
            childPresenter.getDisplay().setIsPassword();
        }
    }

    private boolean isStringListField(String fieldClassName, List<String> genericTypes)
    {
        return fieldClassName.equals("java.util.List") && genericTypes != null && genericTypes.size() == 1
                && genericTypes.get(0).equals("java.lang.String");
    }

    private final static TreeNodePresenterFactory INSTANCE = new TreeNodePresenterFactory();

    public static TreeNodePresenterFactory getInstance()
    {
        return INSTANCE;
    }

}


text:
'''
This module is to create model of Course
'''
from openerp import api, fields, models, _

class Course(models.Model):
    '''
    This class create model of course
    '''
    _name = 'openacademy.course'  # Model odoo name

    name = fields.Char(string="Title", required=True)
    # Field reserved to identified name rec
    description = fields.Text(string="Description")
    responsible_id = fields.Many2one('res.users',
                                     ondelete='set null',
                                     string="Responsible", index=True)
    session_ids = fields.One2many('openacademy.session', 'course_id',
                                  string="Sessions")

    _sql_constraints = [
        ('name_description_check',
         'CHECK(name != description)',
         _("The title of the course should not be the description")),

        ('name_unique',
         'UNIQUE(name)',
         _("The course title must be unique")),
    ]

    @api.multi
    def copy(self, default=None):
        default = dict(default or {})
        if default is None:
            default = {}
        copied_count = self.search_count(
            [('name', '=like', _(u"Copy of {}%").format(self.name))])
        if not copied_count:
            new_name = _(u"Copy of {}").format(self.name)
        else:
            new_name = _(u"Copy of {} ({})").format(self.name, copied_count)

        default['name'] = new_name
        return super(Course, self).copy(default)


text:
    <md-toolbar>

        e-vento

        login

    </md-toolbar>
    <md-content class="md-padding" layout="column" class="md-padding">
      <md-input-container>
        email
        <input ng-model="vm.email" type="email"/>
      </md-input-container>
      <md-input-container>
        senha
        <input ng-model="vm.password" type="password"/>
      </md-input-container>

        <md-button class="md-raised md-primary" ng-click="vm.login(vm.email, vm.password)">login</md-button>
        <md-button class="md-raised md-primary" ng-click="vm.loginProvider(vm.email, vm.password)">login provedor</md-button>

        <md-button md-no-ink="md-no-ink" ng-click="vm.create()">criar conta</md-button>
        <md-button md-no-ink="md-no-ink" ng-click="vm.requestAccount()">requisitar conta</md-button>

    </md-content>


text:
// Copyright (c) Lex Li. All rights reserved.
//
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Runtime.InteropServices;

namespace Microsoft.ApplicationHost
{
    [ComVisible(true), Guid("0c86c5ea-5976-46f7-af2d-e2101f4ab029")]
    public interface IListenerAdapter
    {
        [DispId(1)]
        string Name
        {
            get;
        }
        [DispId(2)]
        string Identity
        {
            get;
        }
        [DispId(3)]
        string ProtocolManagerDll
        {
            get;
        }
        [DispId(4)]
        string ProtocolManagerDllInitFunction
        {
            get;
        }
    }
}


text:
package com.ansorgit.plugins.bash.editor.inspections.quickfix;

import com.ansorgit.plugins.bash.BashLightQuickfixParametrizedTest;
import com.ansorgit.plugins.bash.editor.inspections.inspections.SimpleVarUsageInspection;

public class ReplaceVarWithParamExpansionQuickfixTest extends BashLightQuickfixParametrizedTest {
    public ReplaceVarWithParamExpansionQuickfixTest() {
        super(SimpleVarUsageInspection.class);
    }

    @Override
    protected String getBasePath() {
        return "/quickfixes/replaceVarWithParamExpansionQuickfix";
    }

}


text:
"""
===============================================================
Trial Program for PE 67

Goal: Find the greatest path-sum.
        https://projecteuler.net/problem=67

Note: The program uses FILE IO
===============================================================
"""
_FILE_NAME = "data.pe"

def extract(fileName):
    f = open(fileName, "r")
    data = f.readlines()
    number = []
    for line in data:
        row = line.split()
        number.append(row)
    count = 0
    while (count < len(number)):
        number[count] = [int(i) for i in number[count]]
        count += 1
    f.close()
    return number

def maxPathSum(number):
    numOfRows = len(number) - 1
    count = numOfRows - 1
    while (count > -1):
        curCol = 0
        while (curCol < len(number[count])):
            if (number[count + 1][curCol] > number[count + 1][curCol + 1]):
                number[count][curCol] += number[count + 1][curCol]
            else:
                number[count][curCol] += number[count + 1][curCol + 1]
            curCol += 1
        count -= 1
    return number[0][0]

def main():
    number = extract(_FILE_NAME)
    answer = maxPathSum(number)
    print(answer)

main()