-
Notifications
You must be signed in to change notification settings - Fork 10
syntax
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-вызовам.
}
Блок, возвращающий содержимое файла:
'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://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}&'
Некий абстрактный удаленный вызов метода. Аналог вызовам сервантов по корбе. Может быть, это даже и будет вызов по корбе. Или что-то еще.
На деле это все передается в отдельный node-модуль, определяющийся по неймспейсу, этот модуль сам должен что-то сделать (сходить по http или еще что) и вернуть результат.
'ya:auth()' // Вызов метода auth "серванта" ya.
call('ya:auth()', options)
'common.jsx' // Подключить и исполнить файл common.jsx
include('common.jsx', options)
Этот блок позволяет реализовать какую-то более сложную логику.
На вход функция получает 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: function(context) {
var state = context.state;
var request = context.request;
return state.foo || request.foo; // Если значение истинно, то блок выполняется,
// если нет -- вместо блока вернется null.
}
guard: 'state.foo || request.foo' // Тоже самое, что и в предыдущем примере, только короче.
Аналог атрибута xpath
в xscript
:
select: {
'boo' : 'foo[3].bar' // Это что-то вроде xpath. Версия для бедных пока что.
}
В результате в state.boo
будет result.foo[3].bar
(если по такому "пути" что-нибудь есть).
before: function(context) {
// Сделать что-нибудь до вызова блока.
}
after: function(context, result) {
// Сделать что-нибудь после вызова блока.
}
Задать таймаут в милисекундах:
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: { ... } // Конфиг.
}