Skip to content

Instantly share code, notes, and snippets.

@dolphin278
Last active February 12, 2016 19:04
Show Gist options
  • Save dolphin278/5299556 to your computer and use it in GitHub Desktop.
Save dolphin278/5299556 to your computer and use it in GitHub Desktop.
Пример использования собственных языков при написании nodejs-модулей.

Система модулей в nodejs позволяет подключать не только .js и .json - файлы, но и любые другие, если вы зададите для них функцию трансляции.

Зачем это нужно? Можно напихать в эти файлы ваших собственных DSL, которые будут при включении с помощью require() на лету транслироваться в JavaScript. Про то, зачем использовать DSL написано многабукав, и повторять все это тут я не буду.

Итак, допустим у нас есть вот такого содержания файлик (converter.rule):

USD 30
EUR 40

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

require('./converter-translator'); // Тут наш транслятор, см. далее

var converter = require('./converter.rule'),
    roubles = 300;
    
console.log(converter.USD(roubles)); // Должно вернуть 10
console.log(converter.EUR(roubles)); // Должно вернуть 7.5

Как этого добиться? Механизм модулей в node может быть расширен, если объекту require.extensions['расширение файла'] присвоить функцию, трансляции, которая принимает в качестве параметра объект включаемого модуля, и в результате своей работы заполнит поле module.exports (содержимое этого поля собственно и представляет модуль в том коде, который его включает).

Делаем транслятор (converter-translator.js):

var fs = require('fs');
require.extensions['.rule'] = function (module) {
    // Трансляция должна быть синхронной
    var moduleContent = fs.readFileSync(module.filename).toString(),
        // Разбиваем файл на строки
        rules = moduleContent.split('\n');
    
    // Выкидываем пустые строки
    rules = rules.filter(function (rule) { return !!rule; })
    // Для каждой строки добавляем в module.exports функцию конвертации
    rules.forEach(function (rule) {
        
        var ruleParts = rule.split(' '),
            currency = ruleParts[0],
            value = ruleParts[1];
            
        module.exports[currency] = function (roubles) { return roubles / value; };
    });
}

Понятно, что здесь нет никаких проверок и работы с ошибками, но идея должна быть понятна.

var fs = require('fs');
require.extensions['.rule'] = function (module) {
// Трансляция должна быть синхронной
var moduleContent = fs.readFileSync(module.filename).toString(),
// Разбиваем файл на строки
rules = moduleContent.split('\n');
// Выкидываем пустые строки
rules = rules.filter(function (rule) { return !!rule; })
// Для каждой строки добавляем в module.exports функцию конвертации
rules.forEach(function (rule) {
var ruleParts = rule.split(' '),
currency = ruleParts[0],
value = ruleParts[1];
module.exports[currency] = function (roubles) { return roubles / value; };
});
}
require('./converter-translator'); // Тут наш транслятор, см. далее
var converter = require('./converter.rule'),
roubles = 300;
console.log(converter.USD(roubles)); // Должно вернуть 10
console.log(converter.EUR(roubles)); // Должно вернуть 7.5
@dolphin278
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment