Поскольку мы очень активно используем opensource решения в своей деятельности, вполне естественным является и обратный процесс — публикация под свободными лицензиями библиотек и компонент, созданных в нашей компании.
В этот раз мы публикуем библиотеку сериализации в JSON типов данных Erlang, авторства si14 под BSD 2-clause license. Те проекты, для которых написана эта библиотека, ещё не готовы (ждите анонсов к осени), но библиотека уже стала вполне самостоятельной и может применяться в множестве других случаев. Традиционно, рассчитываем на кооперацию в совершенствовании, с интересом услышим о применении в других проектах.
Описание конкретного record'а представляет из себя словарь с названием поля в качестве ключа и описанием данного поля в качестве значения.
Описание поля представляет из себя словарь с обязательным ключём type, содержащим спецификацию типа, и опциональным ключём default, заданным, если в спецификации record'а для поля указано значение по умолчанию.
Спецификация типа представляет из себя словарь с ключём, равным названию типа, и значением, равным списку аргументов типа (который также может содержать спецификации типов).
По умолчанию все типы полей в record'ах определяются как union их заданного типа и атома undefined. Это не всегда удобно, поэтому текущая реализация принимает параметр ignore_undefined, игнорируя при его наличии undefined.
Пример использования в качестве post-compile hook'а rebar'а:
Результат трансляции в .json (с ignore_undefined):
То же, но без ignore_undefined:
Автор библиотеки: si14.
Традиционно, спасибо akme за согласие на BSD-license.
В этот раз мы публикуем библиотеку сериализации в JSON типов данных Erlang, авторства si14 под BSD 2-clause license. Те проекты, для которых написана эта библиотека, ещё не готовы (ждите анонсов к осени), но библиотека уже стала вполне самостоятельной и может применяться в множестве других случаев. Традиционно, рассчитываем на кооперацию в совершенствовании, с интересом услышим о применении в других проектах.
В дебри Erlang'а
В отличие от многих динамических языков, в Erlang'е есть опциональные аннотации типов для функций и record'ов. На текущий момент они используются минимум 3 утилитами: edoc (формирует документацию из исходников; пример получаемой документации можно увидеть, например, здесь), что более важно, dialyzer (анализирует существующую информацию о типах и сообщает об ошибках несоответствия типов, в том числе несоответствия декларируемого и выведенного типов) и PropEr (система автоматической генерации тестов на основании информации о типах и декларативно задаваемых свойств функций). Использование этих деклараций стало правилом хорошего тона, поэтому почти все качественные проекты на Erlang'е имеют их. Нельзя ли использовать информацию о типах где-либо ещё?JANE
В процессе разработки одного из проектов возникла идея: почему бы не использовать существующую информацию о типах прямо в JS (например, для отрисовки форм или валидации данных)? Блиц-опрос знакомых разработчиков подтвердил, что идея «висит в воздухе», но стандартного решения нет. Тогда появился JANE: попытка описать стандарт кодирования информации о record'ах с помощью JSON, с которым достаточно удобно работать из JS. Особенно хорошо JANE сочетается с BERT, позволяя почти прозрачно работать в JS с Erlang'овскими термами.Формат и текущая реализация
Текущая реализация формата представляет из себя исполняемый escript, принимающий пути к .hrl файлам с описаниями record'ов и записывающий результирующие .json файлы в папку priv/records. Каждое определение record'а в файле кодируется как элемент словаря верхнего уровня с ключём, равным имени record'а и словарём, описывающим данный record, в качестве значения.Описание конкретного record'а представляет из себя словарь с названием поля в качестве ключа и описанием данного поля в качестве значения.
Описание поля представляет из себя словарь с обязательным ключём type, содержащим спецификацию типа, и опциональным ключём default, заданным, если в спецификации record'а для поля указано значение по умолчанию.
Спецификация типа представляет из себя словарь с ключём, равным названию типа, и значением, равным списку аргументов типа (который также может содержать спецификации типов).
По умолчанию все типы полей в record'ах определяются как union их заданного типа и атома undefined. Это не всегда удобно, поэтому текущая реализация принимает параметр ignore_undefined, игнорируя при его наличии undefined.
Пример использования в качестве post-compile hook'а rebar'а:
{post_hooks, [{'compile', './priv/recordparser ignore_undefined include/test.hrl'}]}.
Примеры
Определение record'а:
-record(params_ping, {host :: nonempty_string()}).
-record(params_tcp, {host :: list(atom()),
port = 80 :: pos_integer(),
timeout :: pos_integer()}).
Результат трансляции в .json (с ignore_undefined):
{
"params_ping": {
"host": {
"type": {
"nonempty_string": []
}
}
},
"params_tcp": {
"host": {
"type": {
"list": [
{
"atom": []
}
]
}
},
"port": {
"type": {
"pos_integer": []
},
"default": 80
},
"timeout": {
"type": {
"pos_integer": []
}
}
}
}
То же, но без ignore_undefined:
{
"params_ping": {
"host": {
"type": {
"union": [
{
"atom": [
"undefined"
]
},
{
"nonempty_string": []
}
]
}
}
},
"params_tcp": {
"host": {
"type": {
"union": [
{
"atom": [
"undefined"
]
},
{
"nonempty_string": []
}
]
}
},
"port": {
"type": {
"pos_integer": []
},
"default": 80
},
"timeout": {
"type": {
"union": [
{
"atom": [
"undefined"
]
},
{
"pos_integer": []
}
]
}
}
}
}
Ссылки и люди
Код библиотеки в нашем репозитории на Github'е: github.com/selectel/janeАвтор библиотеки: si14.
Традиционно, спасибо akme за согласие на BSD-license.