読者です 読者をやめる 読者になる 読者になる

Erlang から YAML を扱う

erlang yaml

Erlang から YAML を扱うライブラリに決定的なのが無かったのですが、つい最近出て来ました。

libyaml を NIF(Native Implemented Function) でダイナミックリンクしたものです。

goertzenator/yamler
https://github.com/goertzenator/yamler

使ってみる前に

テストが通らなかったり、色々動かなかったりしたので、色々いじった版がここにあります。

https://github.com/voluntas/yamler

そのうちまとめて pull-request する予定です。

コンパイルとテスト

OS
Mac OS 10.6
Erlang
R15B01
rebar

rebar を入れてないので rebar は自分で入れてください

$ git clone https://github.com/voluntas/yamler
$ ここで rebar を yamler 直下に置く
$ make
==> yamler (compile)
Compiled src/yaml_schema.erl
Compiled src/yaml_schema_json.erl
Compiled src/yaml_schema_failsafe.erl
Compiled src/yaml_schema_erlang.erl
Compiled src/yaml_nif.erl
Compiled src/yaml_schema_core.erl
Compiled src/yaml.erl
Compiled src/yaml_compose.erl
Compiling c_src/yaml_nif.c
c_src/yaml_nif.c: In function ‘cstr_to_binary’:
c_src/yaml_nif.c:50: warning: pointer targets in passing argument 1 of ‘strlen’ differ in signedness
c_src/yaml_nif.c: In function ‘binary_to_libyaml_event_stream_rev’:
c_src/yaml_nif.c:296: warning: pointer targets in passing argument 2 of ‘cstr_to_binary’ differ in signedness
Compiling c_src/libyaml/api.c
Compiling c_src/libyaml/parser.c
Compiling c_src/libyaml/reader.c
Compiling c_src/libyaml/scanner.c
==> yamler (eunit)
Compiled src/yaml_schema.erl
Compiled src/yaml_schema_json.erl
Compiled src/yaml_schema_failsafe.erl
Compiled test/yaml_tests.erl
Compiled src/yaml_schema_erlang.erl
Compiled src/yaml_schema_core.erl
Compiled src/yaml_nif.erl
Compiled src/yaml.erl
Compiled src/yaml_compose.erl
======================== EUnit ========================
module 'yaml'
  module 'yaml_tests'
    yaml_tests: pairs_test_ ()...[0.018 s] ok
    [done in 0.021 s]
  [done in 0.027 s]

=INFO REPORT==== 1-Jun-2012::14:01:10 ===
    application: yamler
    exited: stopped
    type: temporary
module 'yaml_compose'
module 'yaml_nif'
module 'yaml_schema'
module 'yaml_schema_core'
module 'yaml_schema_erlang'
module 'yaml_schema_failsafe'
module 'yaml_schema_json'
=======================================================
  Test passed.
Cover analysis: /private/tmp/yamler/.eunit/index.html

動かしてみる

もともと test ディレクトリにサンプル YAML が入っているのでそれを使いましょう。

% erl -pa ebin
Erlang R15B01 (erts-5.9.1) [source] [64-bit] [smp:2:2] [async-threads:0] [kernel-poll:false] [dtrace]

Eshell V5.9.1  (abort with ^G)
1> yaml:load_file("test/yaml/demo1.yaml").
{ok,[[{<<"specialDelivery">>,
       <<"Follow the Yellow Brick Road to the Emerald City. Pay no attention to the man behind the cur"...>>},
      {<<"items">>,
       [[{<<"part_no">>,<<"A4786">>},
         {<<"price">>,1.47},
         {<<"quantity">>,4},
         {<<"descrip">>,<<"Water Bucket (Filled)">>}],
        [{<<"part_no">>,<<"E1628">>},
         {<<"size">>,8},
         {<<"price">>,100.27},
         {<<"quantity">>,1},
         {<<"descrip">>,<<"High Heeled \"Ruby\" Slippers">>}]]},
      {<<"date">>,<<"2007-08-06">>},
      {<<"customer">>,
       [{<<"family">>,<<"Gale">>},{<<"given">>,<<"Dorothy">>}]},
      {<<"receipt">>,<<"Oz-Ware Purchase Invoice">>},
      {<<"bill_to">>,
       [{<<"state">>,<<"KS">>},
        {<<"street">>,<<"123 Tornado Alley\nSuite 16\n">>},
        {<<"city">>,<<"East Centerville">>}]},
      {<<"ship_to">>,
       [{<<"state">>,<<"KS">>},
        {<<"street">>,<<"123 Tornado Alley\nSuite 16\n">>},
        {<<"city">>,<<"East Centerville">>}]}]]}

file:open あたりはラッパー関数が用意されています。まぁ単なる file:file_read/1 ですけど。

オススメスキーマ

YAML 1.2 にはオススメスキーマというのがあるみたいで、yamler にはそれが実装されています。

http://www.yaml.org/spec/1.2/spec.html#Schema

yaml にはもう一つ erlang スキーマが用意されています。スキーマを使うときは以下のオプションを指定します。

2> yaml:load_file("test/yaml/demo1.yaml", [{schema, yaml_schema_failsafe}]).
{ok,[[{<<"specialDelivery">>,
       <<"Follow the Yellow Brick Road to the Emerald City. Pay no attention to the man behind the cur"...>>},
      {<<"items">>,
       [[{<<"part_no">>,<<"A4786">>},
         {<<"price">>,<<"1.47">>},
         {<<"quantity">>,<<"4">>},
         {<<"descrip">>,<<"Water Bucket (Filled)">>}],
        [{<<"part_no">>,<<"E1628">>},
         {<<"size">>,<<"8">>},
         {<<"price">>,<<"100.27">>},
         {<<"quantity">>,<<"1">>},
         {<<"descrip">>,<<"High Heeled \"Ruby\" Slippers">>}]]},
      {<<"date">>,<<"2007-08-06">>},
      {<<"customer">>,
       [{<<"family">>,<<"Gale">>},{<<"given">>,<<"Dorothy">>}]},
      {<<"receipt">>,<<"Oz-Ware Purchase Invoice">>},
      {<<"bill_to">>,
       [{<<"state">>,<<"KS">>},
        {<<"street">>,<<"123 Tornado Alley\nSuite 16\n">>},
        {<<"city">>,<<"East Centerville">>}]},
      {<<"ship_to">>,
       [{<<"state">>,<<"KS">>},
        {<<"street">>,<<"123 Tornado Alley\nSuite 16\n">>},
        {<<"city">>,<<"East Centerville">>}]}]]}

yaml_schema_failsafe を使うと値の変換を自動でしません。数値や小数も binary で返ってきます。

感想

libyaml がとても良く出来ていて、さらにシンプルにラッピングされていて使いやすいです。今まで Erlang で設定ファイルといえば file:consult/1 が常識でしたが、それを覆してくれそうです。

出来れば少しずつコミットしていければなと、思っています。