LL言語に最適なシンプルなソフトウェア構成
単に「LL言語からも使える帳票開発ツール」とするのではなく,「LL言語用の帳票開発ツール」とはどうあるべきかを徹底的に追求しました。
対応プログラミング言語
現在はRuby, Python, Perl, PHPが主要なLL言語と思われますが,将来的には新しい言語が主流になる可能性もあります。
また,実際にLL言語により業務システムを開発している会社では,用途や目的に応じて復数の言語を使い分けている傾向があるようです。
そこで,LL言語用の帳票開発ツールを開発するにあたっては特定の言語への対応だけでは不十分で,復数の言語への対応が必須であると考えました。
さらに,以下の要件を満たす必要があると考えました。
- 復数の言語で同じような使い勝手を実現すること(各言語においてAPIの差異が少ない)。
- 他の言語への展開が容易であること(移植性が良い)。
APIの設計
APIセット
言語によって差異が少ないAPIをどう設計すべきでしょうか?
まず考えられるのは,オブジェクト指向ベースのクラスライブラリとして帳票生成機能を提供することですが,以下のような課題があります。
- LL言語のオブジェクト指向システムは各言語での仕様の差異が大きく,統一したI/Fの設計は困難。
- ドキュメント・ページ・テキスト・画像・フォントといった細かい単位でクラスを定義していくと,コード量が多くなり移植コストが増える。
そこで,「多様なLL言語で共通の要素は何だろうか?」ということを考えた時に,
LL言語御用達のデータ交換フォーマットであるJSONの活用に思い至りました。
JSONで帳票定義用DSLを設計し,帳票生成に必要な情報はすべてそのDSLで記述するのです(これをレンダリング・パラメータと呼びます)。
そして,帳票生成時に一括してAPIを呼び出す形式とすれば,APIの数をかなり減らすことができそうです。
実際に用意したAPIは,わずか5つで済みました。
version() |
バージョン番号を取得します。 |
set_log_level() |
ログ出力のレベルを設定します。 |
set_defaults() | レンダリング・パラメータのデフォルト値を設定します。 |
renders(param) | レンダリング・パラメータparamを元にレンダリングを実行し, 結果をバイト文字列として返します。 |
render(param, filename) | レンダリング・パラメータparamを元にレンダリングを実行します。処理結果は,ファイルに出力されます。 |
LL言語から使用する場合の使用例
JSONのデータ構造はほとんどのLL言語のデータ構造と1体1対応しますので,容易に各言語のコードに置き換えて記述することができます。
実際にRubyで使用した場合のプログラム記述例を以下に示します。
#!/usr/bin/env ruby
# coding: utf-8
require 'rubygems'
require 'field/reports'
$param = {
# テンプレート
"template" => [
{"header" => "./hyousi.pdf"},
{"body" => "./mitumori.pdf"}
],
# フィールド値
"context" => {
"header" => {
"date" => "${NOW}",
"number" => "10R0001",
"to" => "△△△惣菜株式会社",
"title" => "肉じゃがの材料",
"delivery_date" => "2011-03-01",
"delivery_place" => "貴社指定場所",
"payment_terms" => "銀行振込",
"expiration_date" => "発行から3ヶ月以内",
"total" => 840
},
"body" => {
"date" => "${NOW}",
"number" => "10R0001",
"to" => "△△△惣菜株式会社",
"title" => "肉じゃがの材料",
"delivery_date" => "2011-03-01",
"delivery_place" => "貴社指定場所",
"payment_terms" => "銀行振込",
"expiration_date" => "発行から3ヶ月以内",
"stamp1" => {"icon" => "./stamp.png"},
"table" => [
["1", "N001", "牛肉(切り落とし)", "200g", 250, 500],
["2", "Y001", "じゃがいも(乱切り)", "3個", 30, 90],
["3", "Y002", "にんじん(乱切り)", "1本", 40, 40],
["4", "Y003", "たまねぎ(くし切り)", "1個", 50, 50],
["5", "Y004", "しらたき", "1袋", 80, 80],
["6", "Y005", "いんげん", "1袋", 40, 40]
],
"sub_total" => 800,
"tax" => 40,
"total" => 840
}
},
# スタイル指定
"style" => [
{"*.date" => {"datetime" => "GGE年M月D日"}},
{"*.delivery_date" => {"datetime" => "GGE年M月D日"}},
{"*.total" => {"format" => "###,###円"}},
{"*.sub_total" => {"format" => "###,###円"}},
{"*.tax" => {"format" => "###,###円"}},
{"body.table.*.[4:6]" => {"format" => "###,###円"}}
]
}
if ARGV.length == 1 then
Field::Reports.set_log_level(3)
Field::Reports.render($param, ARGV[0])
else
p "usage: %s <outfile>" % $0
end
ソフトウェア構成
復数の言語への展開を容易にし移植性を良くするために,以下のようなソフトウェア構成としました。
- PDF帳票レンダリング機能の本体は共有ライブラリとして実装する。
- 各言語用の拡張ライブラリを用意し,拡張ライブラリから共有ライブラリのAPIを呼び出す(この部分を言語Bridgeと呼ぶ)。
LL言語で組み立てたレンダリング・パラメータは,APIの引数として共有ライブラリへ渡されます。
生成されたPDF帳票のデータは,バイナリ文字列として呼び出し側のLL言語へ返されます。
PDFテンプレートや画像ファイル・フォントなどのリソースは,基本的にはローカルファイルとしてサーバに置きますが,URL指定により任意の場所から取得することもできます。また,data URI scheme文字列としてインラインで受け渡すことも可能です。
言語Bridgeのソースを添付
LL言語では,各バージョンで拡張モジュールの形式が異なり,再ビルドが必要な場合が多々あります。また,言語自体をビルドした際のオプション指定によっては,バイナリレベルの互換性が失われる場合もあります。
標準的なオプションでビルドした最新の2~3のバージョンについては,言語Bridgeの実行モジュールをバイナリ提供しますが,過去のバージョンや特殊なビルド・オプションには対応しきれません。
そこで,言語Bridge部分に関してはソースでも提供することとし,さらにBSDライセンスとしました。
これで,バイナリ提供対象外の環境であっても,ソースを再ビルドしていただければ,利用できるようになるはずです。
また,言語Bridgeを提供していない言語をお使いの場合でも既存の言語Bridgeのソースを参考にしていただければ,拡張モジュールを自作することも可能なはずです。
コマンドラインプログラムも提供
- JSON文字列を組み立てることができること。
- 外部プロセスを呼び出すことができること。
Field Reports試用版は下記ページよりダウンロードしてください。