Skip to content
Sergei Nikitin edited this page Aug 29, 2011 · 2 revisions

Синтаксис

DSL, описывающий структуру и способ получения данных. Является аналогом xml-файлов, использующихся в xscript'е.

Блоки описываются при помощи обычных javascrip'овых сущностей. Объекты и массивы задают структуру результата. А сами источники данных описываются через file-, http- и другие "примитивные" блоки.

Например:

{
    hello: 'world!', // Обычный текст, попадет в результат как есть.

    count: 42, // Равно как и number, boolean, null.

    // Некоторые строки воспринимаются особым образом:

    common: 'common.jsx', // include-блок -- замена xi:include.

    content: {
        info: 'http://{config.host}/info/?id={id}', // http-блок.

        data: [
            '../data/foo.json', // file-блок.
            '../data/bar.json',
        ]
    },

    auth: 'ya:auth()', // call-блок -- замена corba-вызовам.
}

Примитивы

file-блок

Блок, возвращающий содержимое файла:

'foo.json' // json, который можно (по запросу, см. ниже) распарсить в js-объект.

'foo.txt' // содержимое не-json файлов является просто строкой.
'foo.xml'

'/foo.json' // абсолютный путь.
'../foo/bar.json' // относительный путь.

file('foo.json', options) // полный вариант, с возможностью задать дополнительные параметры блока.

Про возможные значения options см. ниже.

Абсолютные пути откладываются от config.rootdir, а относительные -- от местоположения jsx-файла, содержащего описание блока. При этом относительные пути не могут выходить за пределы "корневой" папки.

В имени файла можно использовать параметры вида {...}:

'info/{id}.json' // параметр id из реквеста.
'info/{state.json}' // параметр json из state'а.
'{config.datadir}/foo.json' // параметр datadir из конфига.

http-блок

Блок, выполняющий http-запрос:

'http://foo.com/'
'http://foo.com/?id=42'

http('http://foo.com/', options)

В урле могут быть использованы {...} параметры:

'http://foo.com/api/{login}/item/{id}/'
'http://{config.host}/?id={id}'

Если урл заканчивается на ?, то к урлу добавятся параметры, переданные в блок.

'http://foo.com/bar/?'

Если же урле уже есть какие-то параметры, то завершающий & говорит, что опять-таки к урлу нужно добавить параметры из реквеста, переопределив, если нужно параметры из урла.

'http://foo.com/bar/?id={id}&'

call-блок

Некий абстрактный удаленный вызов метода. Аналог вызовам сервантов по корбе. Может быть, это даже и будет вызов по корбе. Или что-то еще.

На деле это все передается в отдельный node-модуль, определяющийся по неймспейсу, этот модуль сам должен что-то сделать (сходить по http или еще что) и вернуть результат.

'ya:auth()' // Вызов метода auth "серванта" ya.

call('ya:auth()', options)

include-блок

'common.jsx' // Подключить и исполнить файл common.jsx

include('common.jsx', options)

function-блок

Этот блок позволяет реализовать какую-то более сложную логику. На вход функция получает context (см. ниже), возвращаемое значение компилируется и исполняется.

function(context) {
    var state = context.state;
    if (state.foo) {
        return 'http://foo.yandex.ru';
    } else {
        return 'foo.json';
    }
}

Параметры блоков

Для того, чтобы задать блоку дополнительные параметры, нужно использовать специальные функции: file, http, call, include, object и array:

file('foo.json', options)

В объекте options можно задать следующие параметры:

guard

Условие, которое "включает" или "выключает" блок:

guard: function(context) {
    var state = context.state;
    var request = context.request;

    return state.foo || request.foo; // Если значение истинно, то блок выполняется,
                                     // если нет -- вместо блока вернется null.
}

guard: 'state.foo || request.foo' // Тоже самое, что и в предыдущем примере, только короче.

select

Аналог атрибута xpath в xscript:

select: {
    'boo' : 'foo[3].bar' // Это что-то вроде xpath. Версия для бедных пока что.
}

В результате в state.boo будет result.foo[3].bar (если по такому "пути" что-нибудь есть).

before & after

before: function(context) {
    // Сделать что-нибудь до вызова блока.
}

after: function(context, result) {
    // Сделать что-нибудь после вызова блока.
}

timeout

Задать таймаут в милисекундах:

timeout: 5000

Если блок не успевает отработать за это время, то в результат в соответствующем месте будет ошибка.

Приоритеты блоков

По-дефолту все блоки выполняются параллельно. Но можно каждому блоку задать приоритет, позволяющий произвольным образом задавать порядок выполнения блоков:

{
    bar: [
        'third.jsx'
        'also-second.jsx' +25
    ],

    foo: [
        'first.jsx' +50,
        'second.jsx' +25
    ]

}

В этом случае порядок выполнения будет такой: сперва first.jsx, после него одновременно second.jsx и also-second.jsx. И после того, как оба этих блока отработают -- third.jsx. На порядок выполнения никак не влияет положение блока в дереве -- только его приоритет.

Приоритет задается прибавлением "цифирок" к определению блока. Для блоков, заданных строкой, так задавать приоритет можно только для file-блоков (хотя для file-блоков это довольно бессмысленно) и include-блоков.

Для всех остальных нужно использовать "функциональный" вариант:

{
    foo: http('http://foo.com/?login={login}', { // Сперва выполняем этот блок.
        select: { // Из ответа вытаскиваем какой-то id.
            'id': 'foo.bar.id'
        }
    }) +10,

    bar: http('http://bar.com?id={state.id}') // Передаем этот id в следующий блок.
}

Для задания приоритета массиву или объекту нужно использовать соответствующие функции:

{
    foo: object({
        bar: 'bar.jsx',
        boo: 'boo.jsx'
    }) +20,

    bar: array([
        'foo.jsx'
    ]) +10,

    baz: 'baz.jsx'
}

Контекст

На каждый запрос создается объект context, который передается во все блоки и другие места.

context = {
    state: {}, // Изначально пустой объект, который можно использовать для передачи данных между вызовами блоков.
    request: { ... }, // query-параметры http-реквеста.
    headers: { ... }, // Заголовки.
    cookies: { ... }, // Куки.
    config: { ... } // Конфиг.
}