https://github.com/ksss/kor-input-json
https://github.com/ksss/kor-output-json
https://github.com/ksss/kor-input-ltsv
https://github.com/ksss/kor-output-ltsv
https://github.com/ksss/kor-input-yaml
https://github.com/ksss/kor-output-yaml
すべてkorとそのプラグイン。
これでデフォルトを含め、6種類づつinput/output形式ができたので、 csv->csvなどの同形式変換をのぞいて、62 - 6 = 30パターンのテーブル形式変換が可能になった。
つまりこれだけのデータ変換プログラムをkorでまかなうことができる。
irb> puts %w(csv tsv markdown json ltsv yaml).permutation(2).map{|k,v| "#{k} => #{v}"} #=> csv => tsv csv => markdown csv => json csv => ltsv csv => yaml tsv => csv tsv => markdown tsv => json tsv => ltsv tsv => yaml markdown => csv markdown => tsv markdown => json markdown => ltsv markdown => yaml json => csv json => tsv json => markdown json => ltsv json => yaml ltsv => csv ltsv => tsv ltsv => markdown ltsv => json ltsv => yaml yaml => csv yaml => tsv yaml => markdown yaml => json yaml => ltsv
例えば、一昔前にはやった、ltsvをyamlに変換して見やすくするようなプログラムも、korで実現できる。
tail -f access.log | kor --sync ltsv yaml
実装小話
いくつかkorプラグインを書いてみて、input pluginが重要になる設計になっていることがわかった。 例えば、jsonからcsvに変換する場合は、以下のようになることが期待される。
{"foo": 1} {"bar": 2} {"baz": 3}
foo,bar,baz 1,, ,2, ,,3
korでは中間形式として、ヘッダー部はヘッダーのみの配列、値部は値のみの配列。というようにして、 値は一行読み込んだら(input)一行出力(output)する。を基本にしている。*1
このようなjson列の場合、 csvの最初のキー列に、何を表示すればいいかは、json列を最後まで読んでみなければわからない。 ヘッダー部の配列が全データを読み込むまで決まらない状態になる。
全部読んでから全部変換するような単純な実装だと、 tail -fなどのパイプが繋がりっぱなしになるストリームでは延々と読み込み続け、変換できなくなってしまう。
そこで、kor-input-(json|ltsv|yaml)では、guess機能をつけた。*2 これによって、数行読み込んで登場してきたキーを、すべてのキーとして、移行のデータを順次処理できるようにした。
「毎行毎行、keyとvalueのセットを中間形式にすればいいんじゃ?」とも思いついたんだけど、 この場合はoutput部分で、やはりcsvのヘッダーが最後まで決まらず、問題がinput側からoutput側に移ってしまうだけということがわかってやめた。
guess機能があれば、とりあえずは大丈夫そうだ。
また、最初っから「キーはこれだよ」と教えてあげる機能を各pluginにつけているけど、毎回つけるのがめんどくさいので、kor本体でフィルター機能をつけるのもありかもしれない。もちろんguess機能も。
パフォーマンス面も手付かずなので改善の余地はたくさんある。たとえばoutputがI/O負荷でブロックされていても、変換処理は非同期で進めておければ時間短縮になりそう。
そんな感じでどんどんアイデアが出てくるので、しばらくはこれで遊べそう。*3