NAME

Greese - 拡張SELECT表現による汎用レポート生成エンジン


SYNOPSIS

このモジュールは、RDBデータが埋め込まれたレポートを、HTML、XML、LaTeXなどさまざまなフォーマットで作成するものです。簡単なデータ埋め込みの指示をテンプレートに挿入することで、反復、グループ化、ページ分割がプログラミング無しで実現できます。テンプレートはSQLのSELECT文の拡張となっています。

RDBへのアクセスは、データベース独立なインタフェースを提供するDBIモジュールを通じておこなうので、多くのRDBMSに対して使用できます。

  use Greese;
  use DBI;
  $template = Greese::Template::Html->new($templatefile);
  $report = Greese::Report->new($templae);
  $dbhandle = DBI->connect($dbname, $user, $password);
  $report->print(dbihandle => $dbhandle, where => $where, page => $page);


DESCRIPTION


テンプレート

テンプレートは、レポート出力の雛型となるテキストファイルです。レポート出力の固定的な部分はそのまま記述し、データソースを指定したりカラムの値を埋め込んだりレコード毎に繰り返したりする部分は *[ と ]* で囲んだテンプレートコマンドで記述します。テンプレートコマンド以外の部分には何でも書けるので、テキストベースのフォーマットであれば、プレインテキスト、HTML、XML、LaTeXなど好きなフォーマットでレポートを作ることができます。

GreeseのテンプレートはSQLのSELECT文の拡張となっています。例えば、データベース中の「映画」という名前のテーブルからすべてのレコードを取り出し、各レコードの「表題」と「俳優」という名前の二つのカラムの値を順に表示したいとき、SQLのSELECT文は次のようになります。

  SELECT 表題, 俳優 FROM 映画

これに相当することをおこなうGreeseのテンプレートを、テンプレートコマンドだけの骨組みで示せば次のようになります。

  *[select(FROM 映画)]* *[repeat()]* *[表題]* *[俳優]* *[end()]* *[end()]*

カラム名はそのまま *[ と ]* で囲みます。*[select(…)]* から *[end()]* までの間がデータベースからのデータが埋め込まれる部分となります。FROM句以降は *[select(…)]* に入れて指定します。したがってSELECT文とは順序が異なります。また、SELECT文では何も指定しなくてもデータベースの各レコードに対して繰り返してカラムの値が表示されますが、Greeseのテンプレートでは *[repeat()]* と *[end()]* で囲んで明示的に指示した部分が各レコードに対して繰り返されます。*[end()]* が二つありますが、最初のものは *[repeat()]* に対応し、二つ目のものが *[select(…)]* に対応します。

これを骨組みだけでなく、その間に必要な文字や改行などを入れて具体的なテンプレートファイルとすると次のようになります。

プレインテキスト場合:

  テンプレート movie.txt ----------------------
  *[select(FROM 映画)]**[repeat()]**[表題]*, *[俳優]*
  *[end()]**[end()]*
  ---------------------------------------------

HTMLの場合:

  テンプレート movie.html ---------------------
  <HTML><BODY>*[select(FROM 映画)]*
  <TABLE>*[repeat()]*
  <TR><TD>*[表題]*</TD><TD>*[俳優]*</TD></TR>
  *[end()]*</TABLE>
  *[end()]*</BODY></HTML>
  ---------------------------------------------

この例のように、Greeseではテンプレートファイルに特別な拡張子を与えることはせず、そのフォーマットの通常の拡張子、例えばHTMLなら.htmlや.htmを使います。これは、テンプレートファイル自体をそのフォーマットのファイルとして表示したり編集したりできることを意図しています。

ただし、テンプレートコマンドを非空白文字が許されない場所に書かねばならない場合があります。例えば上記の例では <table> と <tr> の間に *[repeat()]* が書かれていますが、このままでは正しいHTMLとして表示や編集ができません。そういう場合は、次のようにコメントにすることでフォーマットの記述ルールを破らないようにします。*[repeat()]* とそれに対応する *[end()]* の両方をコメント化しないと、その間の部分を繰り返したときにコメントの <!-- と --> の対応が崩れるので注意して下さい。

  テンプレート movie.html ---------------------
  <HTML><BODY>*[select(FROM 映画)]*
  <TABLE><!--*[repeat()]*-->
  <TR><TD>*[表題]*</TD><TD>*[俳優]*</TD></TR>
  <!--*[end()]*--></TABLE>
  *[end()]*</BODY></HTML>
  ---------------------------------------------

生成されるレポートでは、*[repeat()]* や *[end()]* 自体は出力されないので、この問題はおきません。あくまでテンプレートファイルでの話です。

この説明書の以下のテンプレート例では、本来はコメントにすべきところもそのまま記述している場合があります。


反復のグループ化

反復でグループ化をおこないたいことがあります。例えば上記の例で、一つの映画の表題に対して複数の俳優があるため、同じ表題が繰り返し現れます。これを一つにまとめたいというような場合です。Greeseでは、反復を入れ子にすることで、外側の反復に含まれるカラム値についてグループ化が自動的におこなわれるようになっています。例えば次のようになります。

  テンプレート -------------------------------
  *[select(FROM 映画)]**[repeat()]**[表題]*
      *[repeat()]**[俳優]*
  *[end()]**[end()]**[end()]*
  レポート出力 -------------------------------
  The Way We Were
      Robert Redford
      Barbra Streisand
  Prince of Tides
      Nick Nolte
      Barbra Streisand
  Life of Brian
      Monty Python
  --------------------------------------------

反復が二重になっていて、外側の反復内のカラム値指定 *[表題]* でグループ化されています。この例では一つのカラムでグループ化していますが、複数のカラムでグループ化することもできます。また反復は何重に入れ子にしても構いません。


反復のページ分割

*[repeat(NUMBER)]* と、ページあたりの反復数 NUMBER を指定すると、反復をページ分割することができます。この指定は一つのテンプレートの中で一度しかできないことに注意してください。例えば次のようになります。

  テンプレート -------------------------------
  *[select(FROM 映画)]**[repeat(3)]**[表題]*, *[俳優]*
  *[end()]**[end()]*
  レポート出力(1ページ目) ------------------
  The Way We Were, Robert Redford
  The Way We Were, Barbra Streisand
  Prince of Tides, Nick Nolte
  レポート出力(2ページ目) ------------------
  Prince of Tides, Barbra Streisand
  Life of Brian, Monty Python
  --------------------------------------------

  テンプレート -------------------------------
  *[select(FROM 映画)]**[repeat()]**[表題]*
      *[repeat(3)]**[俳優]*
  *[end()]**[end()]**[end()]*
  レポート出力(1ページ目) ------------------
  The Way We Were
      Robert Redford
      Barbra Streisand
  Prince of Tides
      Nick Nolte
  レポート出力(2ページ目) ------------------
  Prince of Tides
      Barbra Streisand
  Life of Brian
      Monty Python
  --------------------------------------------

  テンプレート -------------------------------
  *[select(FROM 映画)]**[repeat(2)]**[表題]*
      *[repeat()]**[俳優]*
  *[end()]**[end()]**[end()]*
  レポート出力(1ページ目) ------------------
  The Way We Were
      Robert Redford
      Barbra Streisand
  Prince of Tides
      Nick Nolte
      Barbra Streisand
  レポート出力(2ページ目) ------------------
  Life of Brian
      Monty Python
  --------------------------------------------

何ページ目のレポートを出力するかは、テンプレートからレポートを生成する際にpageという外部パラメーターを与えることで制御します。

ページ分割した場合、ページ番号、総ページ数、前ページ番号、次ページ番号、が必要になるでしょう。これはそれぞれ *[page()]*、*[pages()]*、*[prev()]*、*[next()]* というテンプレートコマンドで挿入することができます。1ページでの *[prev()]* は1に、最終ページでの *[next()]* は最終ページ番号になります。


レコード数

*[count()]* はその位置での繰り返しにおけるレコード数に置き換えられます。例えば次のようになります。

  テンプレート -------------------------------
  *[select(FROM 映画)]**[repeat()]**[表題]* (*[count()]* レコード)
      *[repeat()]**[俳優]*
  *[end()]**[end()]**[end()]*
  レポート出力 -------------------------------
  The Way We Were (2 レコード)
      Robert Redford
      Barbra Streisand
  Prince of Tides (2 レコード)
      Nick Nolte
      Barbra Streisand
  Life of Brian (1 レコード)
      Monty Python
  ---------------------------------------------


反復数

特定の反復の反復数が必要な場合は反復に *[LABEL:repeat()]* とラベルを付けて *[count(LABEL:)]* とそのラベルを指定します。LABEL には任意の語が使えます。例えば次のようになります。

  テンプレート -------------------------------
  *[select(FROM 映画)]**[count(TITLE:)]* 作品あります
  *[TITLE:repeat()]**[表題]*
      *[repeat()]**[俳優]*
  *[end()]**[end()]**[end()]*
  レポート出力 -------------------------------
  3 作品あります
  The Way We Were
      Robert Redford
      Barbra Streisand
  Prince of Tides
      Nick Nolte
      Barbra Streisand
  Life of Brian
      Monty Python
  ---------------------------------------------

ラベル LABEL とページあたりの反復数 NUMBER の両方を指定したい場合は、「*[LABEL:repeat(NUMBER)]*」と書きます。


反復の区切り

反復の各回の間にだけ指定した文字列を挿入することができます。それには、*[separator()]* コマンドを *[repeat()]* と *[end()]* の間に置きます。*[separator()]* から *[end()]* までは区切り文字列(リテラルとして扱われるのでテンプレートコマンドを含めることはできません)となります。

  テンプレート -------------------------------
  *[select(FROM 映画)]**[repeat()]**[表題]*
      *[repeat()]**[俳優]*
  *[end()]**[separator()]*====
  *[end()]**[end()]*
  レポート出力 -------------------------------
  The Way We Were
      Robert Redford
      Barbra Streisand
  ====
  Prince of Tides
      Nick Nolte
      Barbra Streisand
  ====
  Life of Brian
      Monty Python
  --------------------------------------------

区切り文字列を毎回挿入するのでなく一回おきに挿入するなどしたい場合には、*[separator(BASE, SHIFT)]* と指定すると、繰り返し回数(1から始まる)をBASEで割ってSHIFT余る回の後ろで挿入されます。SHIFTを省略すると0とみなされます。

  テンプレート -------------------------------
  *[select(FROM 映画)]**[repeat()]**[俳優]*
  *[separator(2)]*====
  *[separator(2,1)]*----
  *[end()]**[end()]*
  レポート出力 -------------------------------
  Robert Redford
  ----
  Barbra Streisand
  ====
  Nick Nolte
  ----
  Barbra Streisand
  ====
  Monty Python
  --------------------------------------------


レコード選択条件の追加

同じテンプレートに対してレコードを選択する条件を外部から与えることで、異なったレポートを得ることができます。これをおこなうには、テンプレートからレポートを生成するときの外部パラメータwhereとして、SQLの条件式を指定します。*[select(…)]* でもWHERE句を指定していた場合は、AND で結合されます。


*[select()]* への外部パラメータ埋め込み

*[select()]* の()内に %[param(パラメータ名)]% として外部パラメータの値を埋め込むことができます。例えば、*[select(FROM %[param(table)]% ORDER BY %[param(orderby)]%)]* のようにして、外部パラメータtableとorderbyによってテーブル名と順序を決めるカラム名を与えることができます。


複数あるいは入れ子の *[select()]*

一つのテンプレートの中に複数の *[select(…)]*…*[end()]* を指定したり、入れ子にしたりできます。

*[select(…)]*…*[end()]* を入れ子にした場合、内側の *[select(…)]* の()内で %[カラム名]% と書くことで外側でのカラム値に置き換えることができます。例えば、「*[select(FROM 映画 WHERE 俳優='%[俳優]%')]*」のように書けます。

外部パラメータwhereの効果は入れ子の内側の*[select(…)]*へは及びません。


ラベル

テンプレートコマンドにはすべて、*[LABEL:…]* としてラベルを付けることができます。ラベル文字列 LABEL には半角の英数字とアンダースコアからなる任意の語が使えます。

*[select()]*と*[repeat()]*にラベルを付けておき、*[else()]*、*[separator()]*、*[end()]*にもラベルを付けると、対応するコマンドとラベルが正しく対応しているかどうかチェックされます。入れ子になったコマンドによってたくさんの*[end()]*がある時にミスを防ぐために有効です。


*[select()]* のラベルと外部パラメータ

テンプレート中に複数の *[select()]* があって、外部パラメータ page や where がどの *[select()]* に対するものであるか区別したいときは、*[LABEL:select(…)]* とラベルを付け、外部パラメータでは LABEL:page や LABEL:where と指定すると、対応するラベルを持つselect()に対するパラメータとなります。


フィルタとしてのラベル

*[カラム名]* や *[count()]* *[sum()]* *[param()]* など、何らかの値に置き換えられるテンプレートコマンドに付けられたラベルは、フィルタ名の指定とみなされます。これらのテンプレートコマンドの出力では、使用している出力フォーマットでの特殊文字(HTMLであれば、< > & の文字)がエスケープ処理されますが、フィルタを指定することでさらに追加の文字列処理をした上で出力することができます。

フィルタ名が指定されると、使用している出力フォーマットのテンプレートクラスの filter_フィルタ名 というメソッドで値が処理されます。

Greese::Template::Htmlには次のフィルタメソッドが用意されています。

filter_urlescape()

URLエスケープをおこないます。*[urlescape:俳優]* と書けばカラム「俳優」の値がURLエスケープされたものに置き換えられます。

filter_nl2br()

HTMLエスケープした上で、改行を<BR>に置き換えます。*[nl2br:説明文]* と書けばカラム「説明文」の値の改行を<BR>にしたものに置き換えられます。

フィルタとしてラベルはカンマで区切って複数指定でき、先頭から順次適用されます。たとえば *[nl2br,url2link:説明文]* とすれば、カラム「説明文」の値が、まずエスケープ処理され、次に filter_nl2br() が、次に filter_url2link() が適用されます。

rawというフィルタは特殊で、これを先頭に指定したときは、使用している出力フォーマットでの特殊文字のエスケープ処理がおこなわれません。たとえば *[raw,nl2br:説明文]* とすれば、カラム「説明文」の値そのものに filter_nl2br() が適用されます。

テンプレートクラスをサブクラス化して独自のフィルタメソッドを定義すれば、自由にフィルタを追加できます。


他テンプレート出力の埋め込み

テンプレート中に他のテンプレートの出力を埋め込むことができます。 *[embed(テンプレートファイル名,外部パラメータリスト)]* と書きます。外部パラメータリストは、テンプレートに与える外部パラメータの名前と値を組にしてカンマ区切りで並べたものです。このとき、外部パラメータの値の中に %[カラム名]% と書くことでカラム値に置き換えることができます。例えば、「*[embed(sub.html, where, name='%[title]%', page, 2)]*」のように書けます。テンプレートファイル名を「/」で始まらない相対パスで指定した場合、親テンプレートのあるディレクトリを基準とします。

テンプレートファイル名として、「<<END」のように(ENDの部分は任意の英数字列)指定すると、「END」だけの行までが切り出されてテンプレート内容となります。


外部パラメータや式でデータを与える

通常 *[select()]* では *[select(FROM テーブル名 …)]* としてRDBのテーブルからデータを取得しますが、その代わりに外部パラメータで与えた値やPerlの式の値からデータを取得することができます。

外部パラメータの値からデータを取得するには、*[select(param(パラメータ名))]* と書きます。このとき、外部パラメータの値は、配列参照か、配列参照の配列参照か、ハッシュ参照の配列参照でなければなりません。配列参照か配列参照の配列参照の場合は、先頭要素にカラム名を与えます。

例えば、*[select(param(tabledata))]* として、外部パラメータtabledataに次のようなデータを与えます。

  tabledata => [
    ['number', 'name'], 
    [1, 'foo'], 
    [2, 'bar']
  ] 

これはハッシュ参照の配列参照として書けば次の例と同じです。

  tabledata => [
    {number => 1, name => 'foo'}, 
    {number => 2, name => 'bar'}
  ]

フィールドが一つなら次のように単純な配列参照でOKです。

  tabledata => ['number', 1..5]

配列参照の配列参照や、単純な配列参照の場合は、先頭要素にハッシュ参照を与えることで、そのselect内で有効な追加の外部パラメータを指定できます。例えば次のようにして外部パラメータtitleを与えることができます。

  tabledata => [
    {title => 'タイトル'},
    ['number', 'name'], 
    [1, 'foo'], 
    [2, 'bar']
  ] 

Perlの式の値からデータを取得するには、*[select(eval(式))]* と書きます。このとき、式はリストコンテキストで評価されます。その値は、リストか、配列参照のリストか、ハッシュ参照のリストでなければなりません。リストか配列参照のリストの場合は、先頭要素にカラム名を与えます。

例えば上記の例と同じものが次のように書けます。

  *[select(eval(['number', 'name'], [1, 'foo'], [2, 'bar']))]*
  *[select(eval({number => 1, name => 'foo'}, {number => 2, name => 'bar'}))]*
  *[select(eval('number', 1..5))]*
  *[select(eval({title => 'タイトル'}, ['number', 'name'], [1, 'foo'], [2, 'bar']))]*

*[select(param(...))]* や *[select(eval(...))]* でデータを与える場合は、WHERE節以降を書いたりwhereパラメータを与えることはできません。


条件判断

*[if(式)]* *[else()]* *[end()]* によって条件判断による出力の切り替えができます。式の値が真の場合は *[if(式)]* と *[else()]* の間が、偽の場合は *[else()]* と *[end()]* の間が出力されます。*[else()]* はなくてもかまいません。その場合は式の値が真の場合に *[if(式)]* と *[end()]* の間が出力されます。

例えば次のようにすると、

  *[select(eval('num', 1..10))]*
  *[repeat()]**[if(%[num]% % 3 == 0)]*■*[else()]*□*[end()]**[end()]*
  *[end()]*

次のように出力されます。

  □□■□□■□□■□

条件式の評価はPerlの式としておこなわれます。式の中の %[カラム名]% %[param(…)]% などはその値に置き換えられてから評価されます。


該当レコードがない場合の出力

*[select()]* と *[end()]* の間に *[else()]* を書くと、*[select()]* と外部パラメータwhereで指定した条件に該当するレコードがなかった場合には、*[else()]* と *[end()]* の間が出力されます。


Perlの式の埋め込み

*[eval(式)]* でPerlの式を実行できます。*[eval(式)]* 自体は出力されません。

*[reval(式)]* はPerlの式を実行した結果を出力します。

式の中のグローバル変数はそのテンプレート内で有効です。


%[…]%

*[select()]* *[embed()]* *[if()]* *[eval()]* *[reval()]* の()内では、%[ ]% ではさんで、カラム名、param()、count()、page()、pages()、prev()、next()、reval() を書くことができ、それぞれの値に置き換えられます。


RDBアクセス

GreeseはDBIモジュールを通じてデータベースからデータを読みとります。DBIは統一的なRDBへのインタフェースを提供するモジュールです。実際にRDBMSと接続するには、DBIとの仲立ちをするDBDモジュールが必要です。Oracle、SQLServer、PostgreSQL、MySQLなど多くのデータベース用のDBDモジュールがあります。

データベースの種類やデータベースファイルの場所はDBIハンドルをオープンするときに指定されます。Greeseがデータを読みとるときにはオープンされたDBIハンドルを必要とするだけです。つまり、Greeseはデータベースの種類やデータベースファイルの場所を関知しません。

DBIについて詳しくはDBIの説明書をご覧ください。


レポート作成手順

準備

当然ですが、レポート作成の元となるデータベースが必要です。そのデータベース用のDBDドライバモジュールも必要です。

出力のフォーマット(HTML、XML、TeXなど)を選び、テンプレートファイルを作成します。テンプレートコマンドの説明は上にあります。

CGIの例

CGIとしてレポート生成をおこなうスクリプトの骨格は次のようになります。

  # to make one HTML to STDOUT for CGI
  use Greese;
  use DBI;
  use CGI;
  $cgi = CGI->new;
  $selfurl = $cgi->url; 
  $templatefile = $cgi->param('template');
  $page = $cgi->param('page');
  $where = $cgi->param('where');
  $template = Greese::Template::Html->new($templatefile);
  $report = Greese::Report->new($templae);
  $dbhandle = DBI->connect($dbname, $user, $password);
  print $cgi->header(-type => 'text/html', -charset => $charset);
  $report->print(dbihandle => $dbhandle, cgi => $selfurl, where => $where, 
    page => $page);

これをパッケージ化したGreeseCGIというモジュールも用意されています。GreeseCGIの説明書をご覧下さい。


テンプレートコマンド一覧

Greeseのテンプレートに埋め込まれるコマンドはすべて、 *[ と ]* のタグに囲まれています。もし *[ を文字通りに出力したい場合は、 \*[ と書きます。これらのタグとエスケープの文字列は、*[tag(…)]* コマンドでテンプレート内で一時的に変更できるほか、Greese::Templateを継承した独自のクラスを作ることで変更することができます。

select指定: *[LABEL:select(SOURCE_SPEC)]* … *[end()]*

データを埋め込む範囲を示すとともに、FROM句以降を指定します。SOURCE_SPEC には、SQLのSELECT文のFROM句以降をそのまま記述します。一つのテンプレートに複数のselect指定をしたり入れ子にしたりできます。ラベル LABEL: は省略できます。select指定自体は出力レポートには現れません。

SOURCE_SPEC 内の %[COLUMN_NAME]% や %[param(PARAM_NAME)]% はそれぞれカラム値や外部パラメータ値に置き換えられます。

SOURCE_SPEC として、param(PARAM_NAME) という形で外部パラメータ名を指定すると、その外部パラメータ値からデータが取得されます。この場合の外部パラメータ値は、配列参照(先頭要素はカラム名)か、配列参照の配列参照(先頭の配列参照はカラム名)か、ハッシュ参照の配列参照でなければなりません。配列参照か配列参照の配列参照の場合に、先頭要素にハッシュ参照を与えると、追加の外部パラメータを指定できます。

eval(EXPRESSION) という形でPerlの式を指定するとそれを評価した値からデータが取得されます。このとき、式はリストコンテキストで評価されます。その値は、リストか、配列参照のリストか、ハッシュ参照のリストでなければなりません。リストか配列参照のリストの場合は、先頭要素にカラム名を与えます。リストか配列参照のリストの場合に、先頭要素にハッシュ参照を与えると、追加の外部パラメータを指定できます。

例:*[select(FROM movie, actor WHERE movie.actor = actor.name ORDER BY movie.name)]*

例:*[select(param(tabledata))]*

例:*[select(eval('number', 1..5))]*

注:Greeseのグループ化処理は、そのカラムの値の順に並べ替えられていなくてもおこなわれますので、グループ化のために ORDER BY 句を指定する必要はありません。

条件に合致しない場合: *[else()]*

*[select(…)]* と *[end()]* の間に *[else()]* を書くと、*[else()]* から *[end()]* までの間は、抽出されたデータがない場合に出力されます。

*[if(…)]* と *[end()]* の間に *[else()]* を書くと、条件式が偽の場合に出力する内容を指定できます。

カラム値: *[COLUMN_NAME]*

*[COLUMN_NAME]* はカラムの値に置き換えられます。COLUMN_NAME はカラム名で、テーブル名で修飾されていても構いません。テーブル名とカラム名はGreeseが実行するSQL文では自動的にクォートされますので、SQLの予約語と同じ名前でもそのまま使って構いません。

例:*[住所]* *[顧客.住所]* *[注文.数量]* *[order.quantity]*

指定するカラム名は、select指定で指定したデータソースに含まれていなければなりません。

カラムエイリアス: *[alias(AS_SPEC)]*

SQLのSELECT文ではカラムリストの中で「商品.価格 * 注文.数量 as 金額」のようにしてカラム値を使った式に名前を付けてカラムのように扱うことができます。これと同じことをおこない場合、*[alias(商品.価格 * 注文.数量 as 金額)]* と記述します。このコマンド自体はレポート出力には現れません。

反復範囲: *[repeat()]* ... *[end()]*

*[repeat()]* から *[end()]* までの範囲は、各データレコード毎に反復されます。 *[repeat()]* と *[end()]* それ自身は出力には現れません。もし反復範囲が入れ子になっていると外側の反復に含まれるカラム値についてグループ化されます。

ページ分割される反復: *[repeat(NUMBER)]*

*[repeat(NUMBER)]* と数値を指定すると、その反復は指定した数ごとにページ分割されます。この指定は一つのテンプレート内で一度しか使えません。

反復区切り指定: *[separator(BASE, SHIFT)]*

*[repeat()]* と *[end()]* の間に *[separator(...)]* を置くと、*[separator(...)]* から次の *[separator(...)]* または *[end()]* までは区切り文字列(リテラルとみなされる)となります。区切り文字列は反復回数(1から始まる)をBASEで割ってSHIFT余る回の後ろに挿入されます。SHIFTを省略すると0、BASEを省略すると1とみなされます。

ページ番号: *[page()]*

*[page()]* はページ番号に置き換えられます。

総ページ数: *[pages()]*

*[pages()]* は総ページ数に置き換えられます。

前ページ番号: *[prev()]*

*[prev()]* は前ページ番号に置き換えられます。先頭ページでは1になります。

次ページ番号: *[next()]*

*[next()]* は次ページ番号に置き換えられます。最終ページでは最終ページ番号になります。

レコード数: *[count()]*

*[count()]* は、その位置の反復におけるレコード数に置き換えられます。

反復数: *[count(LABEL:)]* *[LABEL:repeat()]*

*[count(LABEL:)]* は、反復 *[LABEL:repeat()]* のその場所での反復数に置き換えられます。LABELには任意の語が使えます。

合計: *[sum(COLUMN_NAME)]*

*[sum(COLUMN_NAME)]* は、その位置の反復における全レコードでの、カラム COLUMN_NAME の値の合計に置き換えられます。カラム COLUMN_NAME は数値型でなければなりません。

最大値: *[max(COLUMN_NAME)]*

sum()と同様にして最大値に置き換えられます。

最小値: *[min(COLUMN_NAME)]*

sum()と同様にして最小値に置き換えられます。

外部パラメータ値: *[param(PARAM_NAME)]*

テンプレートからレポートを生成する際に与えた外部パラメータ PARAM_NAME の値に置き換えられます。

内部で自動的にセットされる特殊なパラメータとして fromwhere があります。これはそのレポートページが生成されたときのSELECT文のFROM句以降となります。テンプレートの *[select()]* で指定した内容だけでなく、外部パラメータのwhereで与えた内容も含まれます。

Perlの式の実行: *[eval(EXPRESSION)]*

Perlの式 EXPRESSION を実行します。何も出力しません。

Perlの式の値: *[reval(EXPRESSION)]*

Perlの式 EXPRESSION を実行し、その結果を出力します。

環境変数値: *[env(ENV_NAME)]*

環境変数 ENV_NAME の値に置き換えられます。

リテラル文字列: *['STRING']*

文字列 STRING そのものが出力されます。テンプレートコマンド以外の部分はそのまま出力されますので、通常はこのコマンドを使う必要はありません。何らかの文字列にフィルタを適用したものを出力したいときに使います。例えば *[urlescape:'俳優']* とすると「俳優」という文字列がURLエスケープされたものが出力されます。(日本語の文字列をURLエスケープするときは文字コードに注意して下さい。)

リテラル範囲: *[LABEL:literal()]* ... *[LABEL:end()]*

*[LABEL:literal()]* から *[LABEL:end()]* までの範囲(LABELは任意の語)は、(その中にテンプレートコマンドにあたる文字列を含んでいても)そのままレポートに出力されます。*[LABEL:literal()]* と *[LABEL:end()]* それ自体はレポートには出力されません。この指定によるリテラル範囲では、開始タグをエスケープする必要はなく、もし \*[ と書くとそのまま出力されます。

タグ変更: *[tag(START,END,ESC)]*

*[tag(START,END,ESC)]* は、それ以降のテンプレートでのタグ文字列を変更します。START が開始タグ、END が終了タグ、ESC がエスケープ文字です。ESC は省略できて、省略するとデフォルトの \ のままとなります。

コメント: *[comment(COMMENT)]*

レポートには出力されないコメントをテンプレートに入れたい時に使用します。コメント文字列 COMMENT 中に改行を含めることはできません。

他テンプレート出力の埋め込み: *[embed(TEMPLATE,PARAM_LIST)]*

他のテンプレートファイルTEMPLATEに外部パラメータPARAM_LISTを与えて得られる出力を、その位置に埋め込みます。PARAM_LISTは、パラメータ名とその値を組にしてカンマで区切って並べたものです。パラメータの値にカラム値を埋め込みたい場合は %[PARAM_NAME]% と記述します。例えば *[embed(sub.html, where, name=%[name]%)]* のようになります。

TEMPLATE として / で始まらない指定すると、親テンプレートファイルのあるディレクトリをカレントディレクトリとした相対パスと見なされます。

TEMPLATE として <<END (ENDは任意の英数字列)と指定すると、次の行から END だけの行までの内容がテンプレートとして使われます。


クラスとメソッド

Greese::Template

テンプレートを表すクラス。Greese::Template::* クラス群のベースクラス。Greese::Template::* オブジェクトはテンプレートファイルの内容を保持する。プレインテキストフォーマットの場合はGreese::Templateクラスそのものを使い、HTML、XML、LaTeXの場合は以下にあげるそれぞれに対応したクラスを使う。Greeseに用意されていないフォーマットを扱う場合は、Greese::Templateを継承したクラスを作成して必要なメソッドをオーバーライドすればよい。

Greese::Template::filter_raw()

デフォルトのエスケープ処理をせずデータそのものを返すフィルタ。

Greese::Template::Html

HTMLフォーマットのためのクラス。Greese::Templateを継承。

Greese::Template::Xml

XMLフォーマットのためのクラス。Greese::Templateを継承。

Greese::Template::LaTeX

LaTeXフォーマットのためのクラス。Greese::Templateを継承。

Greese::Template::Html::filter_urlescape()

URLエスケープをおこなうフィルタ。

Greese::Template::Html::filter_nl2br()

HTMLエスケープの後、改行を<BR>に置き換えるフィルタ。

$templateobj = Greese::Template->new($templatefile)

Greese::Templateオブジェクトを作成して返す。$templatefileはテンプレートファイルのパス名。

Greese::Report

レポートを表すクラス。Greese::Report オブジェクトは、読み込まれたデータをテンプレートに従って保持し、出力ファイルを生成する。

$reportobj = Greese::Report->new($templateobj);

Greese::Reportオブジェクトを生成して返す。$templateobjはGreese::Templateオブジェクト。

$reportobj->print(%param)

出力ファイルを生成する。%paramは外部パラメータとしてテンプレートに与えられる。

特別な意味を持つ外部パラメータは次のとおり。dbihandle は必須。

  dbihandle => $dbh  DBIハンドル
  outfile => $file   出力ファイル。省略すると標準出力
  page => $page      ページ分割の場合にページ番号を指定
  where => $where    *[select()]*にANDで付加されるレコード選択条件
  empty => $empty    真だと、該当するレコードがない場合にも*[select()]*…*[end()]*内を生成
  verbose => $flag   $flagが真なら状況を表示

※emptyオプションを指定しないと、該当するレコードがない場合は*[select()]*…*[end()]*内は何も出力されない。empty => 1 を指定すると、固定文字列部分だけが出力される(カラム値部分や反復部分は出力されない)。


グローバル変数

Greeseの動作を変更するグローバル変数として次のものがある。

$Greese::NOQUOTENAME

通常はGreeseがDBIに送るSELECT文中のテーブル名やカラム名は `` によって囲まれる。$Greese::NOQUOTENAME が真であればこれは行われない。$Greese::NOQUOTENAMEの初期値は0。

$Greese::DEFAULTCOLUMN

カラム名が一つも指定されない時にGreeseがDBIに送るSELECT文のカラム名指定部分はこの変数の値が使われる。$Greese::DEFAULTCOLUMNの初期値は'1'。


カスタマイズ

テンプレートコマンドのタグ文字列 *[ と ]* およびエスケープ文字 \ は、テンプレート中で *[tag(…)]* コマンドで一時的に変更することもできますが、あるフォーマットに対しては常に別のタグを使いたいというような場合は、Greese::Template を継承したそのフォーマット用のクラスを作成して、次のメソッドをオーバーライドしてください。

また、データベースから得られた内容にそのフォーマットではそのまま表示できない特殊な文字(例えばHTMLにおける「<」「>」など)が含まれていた場合の処理、およびレポート出力の生成後実際に出力される前の処理も、各フォーマットに対応したクラスで定義されており、ユーザーが独自のクラスを定義することで変更・拡張することができます。

Greese::Template->esc_char()

開始タグのエスケープ文字(\)を返す。

Greese::Template->start_tag()

開始タグ(*[)を返す。

Greese::Template->end_tag()

終了タグ(]*)を返す。

Greese::Template->escape($string)

引数として文字列を取り、テンプレートフォーマットにおける特殊文字を処理して返す。*[COLUMN_NAME]* を処理するときに呼ばれる。

Greese::Template::escape() は何もしない。Greese::Template::Html::escape() は & < > を処理する。

Greese::Template->postfilter($string)

引数として一つの出力ファイルの内容全体を取り、必要に応じてなんらかの処理をして返す。レポートが最終的に出力される前に呼ばれる。

Greese::Template::postfilter() は何もしない。Greese::Template::Html::postfilter() は「<tr></tr>」というパターンを削除する。


AUTHOR

Sey Nakajima <nakajima@netstock.co.jp>


SEE ALSO

perldoc DBI

perldoc CGI