本を読む

読書やコンピュータなどに関するメモ

Bash on Railsを作る(7) db/schema.sh

 bashの内蔵コマンドだけでいかにRuby on Railsっぽいことをやるかというパロディ企画です。「Webアプリケーションをなめるな」とDISってもらえる日を夢みています。特にそのための行動をする気はありませんが。

 今回は、前回の予告どおり、モデルのスキーマ定義であるdb/schema.shファイルについて、メタプログラミングでDSLする方法を解説します。

db/schema.shの中身

 第1回で紹介したように、db/schema.shファイルの中身は以下のようになっています。

create_table members t
      t.column name string
      t.column mailaddress string
      t.column comment string
elbat_etaerc

 これをシェルスクリプトとして解釈します。

 なお、いまのところdb/schema.shを手書きしていますが、そのうちscript/generateなどの生成スクリプトを用意しようと思ってます。

基本的なアイデア

 前回にも書いたように、bashはオンメモリーのデータ構造を操作するのは苦手です。そこで、db/schema.shをシェルスクリプトとして読み込んだときに、できるだけその場で処理を実行することにします。

 db/schema.shは、make db-schema-loadでテーブルを作るときと、ActiveRecordもどきのO/Rマッパーを起動するときの2箇所で使います。そこで、同じ名前のコマンド(関数)を定義したスクリプトファイルを、2通り用意します。用途に応じて違うスクリプトファイルを読み込み(sourceし)、そのあとにdb/schema.shを読み込めば、設定が反映されます。

テーブルを作る場合

 まず、前回のとおりconfig/database.shを反映しておきます。そして、connection_adapters/${dbconf_adapter}_adapter.shを読み込んで、RDBMS固有の処理をラップした共通関数を定義します。

 create_table関数は、以下のように実装します。

function create_table() {
    table=$1
    prefix=$2
    columns_sql="id INTEGER PRIMARY KEY AUTOINCREMENT"

    eval "function ${prefix}.column() {
              local column=\$1
              local datatype=\$(type2dbtype \$2)
              string_push columns_sql \", \$column \$datatype\"
          }"
}

 bashの関数は「.」を含んでいいというのがポイントです。第2引数で渡された文字列を見て、${文字列}.columnという関数を定義しています。これで、Ruby on Railsのモデル定義にブロック変数を使うのを真似てみました。定義するコマンドは、column_sqlという変数に引数のテキストを追加するものです。

 コード中、type2dbtypeは、Bash on Railsのデータ型を引数にとり、対応するRDBMSのデータ型名を返すユーティリティ関数です(connection_adapters/*.shで定義)。また、string_pushは第1引数に変数名を、第2引数に文字列をとり、変数に入っている文字列の末尾に新しく文字列を追加するユーティリティ関数です(ほかのシェルスクリプトで定義)。

 elbat_etaerc(create_tableの逆綴り)のほうは、こんな実装です。

function elbat_etaerc() {
    echo "-- create_table($table)"
    do_create_table "$dbconf_database" "$table" "$columns_sql"
}

 一連の${文字列}.column関数で追加してきたcolumns_sqlを元にSQL文を作り、実行する処理です。do_create_tableも、connection_adapters/*.shで定義しています。

 これで単にdb/schema.shをsourceしてもいいのですが、変数をグローバルにしないために関数に封じ込めます。

function load_schema() {
    local table prefix columns_sql
    . db/schema.sh
    unset -f ${prefix}.column
}

 db/schema.shを読んだ後は、${文字列}.columnという関数も未定義に戻します。

O/Rマッパーの定義として読み込む場合

 次に、O/Rマッパー(もどき)の定義としてdb/schema.shを読み込む場合です。

 読み込んだ結果、table_listという変数にテーブルの一覧が、${テーブル名}_columnsという変数にカラム名の一覧が、${テーブル名}_columns_typeという変数に各カラムのデータ型が入る仕様とします。

 最初に、テーブルを作るときと同じく、config/database.shとconnection_adapters/*.shを読み込みます。

 create_table関数は、以下のように実装します。

function create_table() {
    table=$1
    prefix=$2

    string_push table_list " $table"
    unset ${table}_columns ${table}_columns_type

    eval "function ${prefix}.column() {
              local column=\$1
              local datatype=\$2
              string_push \${table}_columns \" \$column\"
              string_push \${table}_columns_type \" \$datatype\"
          }"
}

 elbat_etaerc関数のほうは、何もしません。

function elbat_etaerc() {
    true        # dummy
}

 そして、db/schema.shをsourceします。

function load_schema() {
    local table prefix
    unset table_list
    . db/schema.sh
    unset -f "${prefix}.column"
}

まとめ

 スキーマ定義をシェルスクリプトとして読み込む方法を解説しました。ブロック引数風の文字列から関数名を作るのは、単に見た目をRuby on Railsっぽくしたかっただけです。

 データベース固有の処理を抽象化している関数は、まだきれいに抽象化できてはいないので、気が向いたらいろいろ変えるかもしれません。

 次回は、bashでOO風のプログラミングを実現するしくみを紹介しようかと思います。

注意:Bash on Railsは、実用性を無視したパロディソフトです。誤解のないようお願いします。

コメント

コメントの投稿

管理者にだけ表示を許可する

トラックバック

http://emasaka.blog65.fc2.com/tb.php/353-64a6e294

 | HOME | 

Categories

Recent Entries

Recent Comments

Recent Trackbacks

Appendix

emasaka

emasaka

フリーター。
連絡先はこのへん

Monthly


FC2Ad