obaq/sqlはデータモデルオブジェクトとラッパーオブジェクトを提供します。
データモデルオブジェクトとは、RDBのテーブル定義(フィールド定義)の情報を保持しているオブジェクトです。
以下のクラス群を総称してデータモデルオブジェクトといいます。
- class DataBase
- class Table
- class IntField, class VarCharField等のフィールドオブジェクト
これらによって、次のようなことが可能になります。
スキーマの生成
createtable等のテーブル定義のためのSQLを生成します。
ラッパーオブジェクトの生成
次項で説明するラッパーオブジェクトを生成することができます。
Queryオブジェクトの生成
sqlのselect文を生成するQueryオブジェクトを生成できます。
データ定義とプログラムの分離
データベースのテーブル定義を修正してもプログラムの修正が不要(かごく少量)になります。
単純なフィールドごとのコピー等の処理はほとんど不要になります。
ruby-postgresによってデータベースにアクセスすると、
結果は文字列の配列として返されます。
ほとんどの場合、この配列を数値(Integer)や日付(Date)などの対応するRubyのデータに変換する処理が必要になります。
この処理を自動的に行うのがラッパーオブジェクトです。
ラッパーオブジェクトには、以下の機能があります。
- フィールド名でメンバーにアクセスする
- 対応するデータモデルオブジェクトにアクセスする
- insert文、update文、delete文等のSQLを生成する
ラッパーオブジェクトにMixinされるモジュール。
ラッパーオブジェクトはひとつのクラスでなく、
各テーブルごとに個別のクラスが自動生成されます。
このWrapperMixinモジュールは、全てのラッパーオブジェクトにincludeされます。
すなわち、このモジュールの機能(メソッド)は全てのラッパーオブジェクトが持っています。
WrapperMixin#get_table
-
対応するテーブルのデータモデルオブジェクトを返却します。
WrapperMixin#each_field(&block)
-
対応するフィールドのデータモデルオブジェクトごとにブロックを呼びます。
WrapperMixin#check_data
-
対応するフィールドとテーブルのデータモデルオブジェクトのcheck_dataを呼び出して、
データをチェックします。
WrapperMixin#insert_sql
-
現在、ラッパーオブジェクトに設定されている値をもとに、insert文を生成します。
r = shohin.new_wrapper_object(1, "a", 2)
puts r.insert_sql
# => insert into
Shohin(shohin_code,shohin_name,unit_price)
values(1,'a',2)
nilが設定されているフィールドとSerialFieldは飛ばして処理します。
WrapperMixin#update_sql
-
現在、ラッパーオブジェクトに設定されている値をもとに、update文を生成します。
nilが設定されているフィールドは飛ばして処理します。
WrapperMixin#delete_sql
-
現在、ラッパーオブジェクトに設定されている値をもとに、delete文を生成します。
DataBase#find_table(table_name)
-
テーブルに対応するデータモデルオブジェクトを返却します
DataBase#create_tables
-
テーブル定義のSQL(create table文)を生成します。
返却値は、SQL命令1つの文字列を要素とする配列です。
DataBase#create_dbobjects
-
DBObjectを定義するSQLを生成します。
DataBase#create_all
-
create_table + create_dbobjects
DataBase#drop_tables
DataBase#drop_dbobjects
DataBase#drop_all
DataBase#install_datamodel(mod)
-
module modに各テーブルに対応するデータモデルオブジェクトを定数として定義します。
テーブル名が定数名になります。
DataBase#bind_fields(f1, f2)
-
フィールドの対応を記憶します。
DataBase#each_binded_fields(t1, t2, &block)
-
テーブルt1,t2の中で対応するフィールドのペアごとにblockを実行します。
DataBase#new_wrapper_object(table, *args)
-
テーブルtableからラッパーオブジェクトを生成します。
argsは、データ定義のフィールドの並びどおりに、初期値を渡します。
DataBase#wrapper_object_factory
-
テーブル名と、ラッパーオブジェクト生成クラスの対応を保持しているHashです。
独自のラッパーオブジェクトを使用したい時に、ここにそのラッパーを生成するクラスを登録します。
class MyUriage < Uriage
....
end
db = Uriage.db
db.wrapper_object_factory[Uriage] = MyUriage
r = uriage.new_wrapper_object(1, 'aaa', 2)
# r は MyUriageのインスタンスになっている
テーブル生成時に、Talbe以外のオブジェクトを同時に生成したい場合、
このクラスを使用して、追加のSQLを埋めこみます。
DataBase db1
Table paths
#....
DBObject table_index
create_sql = <<END
create index paths_index on paths
using btree(upper path) text_ops
END
drop_sql = 'drop index paths_index'
SceamaCommandSet
CreateTables
この結果
create table paths ..... ;
create index paths_index .... ;
というSQLが実行されます。
アプリケーションプログラムの実行時には
データモデルオブジェクトとして使用されることはありません。
- DBObject#create_sql
-
そのオブジェクトを生成するために使用するSQL文
- DBObject#drop_sql
-
そのオブジェクトを削除するために使用するSQL文
- DBObject#long_name
-
説明用の名称
- DBObject#description
-
説明文
Table#primary_keys
-
そのテーブルのprimary keyに対応するデータモデルオブジェクトの配列
Table#[field_name]
-
field_nameのフィールドに対応するデータモデルオブジェクト
Table#create_table
-
Tableを定義するSQL文。
命令ひとつを要素とする配列。
Table#drop_table
-
Tableを削除するSQL文
Table#wrapper_object_creator
-
ラッパーオブジェクトを生成するクラスを生成して返却する。
このクラスは、フィールド名をメンバーとするStructであり、
以下のモジュールをincludeしている
- Obaq
- HtmlGen
- ExpandByMember
- WrapperMixin
Table#new_wrapper_object(*args)
-
ラッパーオブジェクトを生成する
argsはラッパーオブジェクトに渡される。
通常は、スキーマで定義されている順番にフィールドの初期値を渡す。
Table#check_data(r)
-
rの内容の正当性をチェックする。
エラーがあるとDBDataErrorをraiseする。
デフォルトでは何もチェックしないので、必要ならばこのメソッドを再定義する
Table#raise_error(msg)
-
DBDataErrorをraiseする
- Field#primary_key
-
このフィールドが主キーである場合にtrueを設定する
- Field#not_null
-
trueであれば、フィールド定義にNOT NULLを付加する。
- Field#unique
-
trueであれば、フィールド定義にUNIQUEを付加する。
- Field#default_value
-
nil以外であれば、フィールド定義にDEFAULT VALUEを付加する。
- Field#long_name
-
説明用の名称
- Field#description
-
説明文
Field#bind_to_field(table_name, field_name)
-
テーブルtable_nameのフィールドfield_nameとこのフィールドを関連づける。
関連づけられたフィールドには、Queryの生成時にwhere x = yの条件が自動的に付加される。
Field#db
-
このフィールドを保持しているDataBaseを返却する
Field#fullname
-
テーブル名.フィールド名
Field#field_def
-
SQLのフィールド定義
Field#==(x)
-
QueryCondオブジェクトを生成する
Field#<=(x)
-
QueryCondオブジェクトを生成する
Field#>=(x)
-
QueryCondオブジェクトを生成する
Field#<(x)
-
QueryCondオブジェクトを生成する
Field#>(x)
-
QueryCondオブジェクトを生成する
Field#---(x)
-
xのクラスによって、対応するQueryCondオブジェクトを生成する
Field#desc
Field#check_data(d)
Field#ruby2sql(x)
-
xの値をSQL形式に変更する(サブクラスで定義)
Field#sql2ruby(x)
-
xの値をRubyとして一般的なタイプに変換する(サブクラスで定義)
連番を表わすフィールドです。
このタイプのフィールドが定義されていると、テーブル定義時に以下のようなSQLによって、
対応するシーケンスとインデックスが生成されます。
create table Shohin (
shohin_code integer default nextval('Shohin_shohin_code_seq') ,
shohin_name varchar(20) ,
unit_price integer );
create sequence Shohin_shohin_code_seq;
create unique index Shohin_shohin_code_index on Shohin (shohin_code)
SerialField#curr_val
-
現在のシーケンス値を取り出すSQL文(select currval('...') )を生成します。
このSQLは、同一セッション内でこのテーブルに対するinsert文かnext_valが実行されていないと、
使用することはできません。(postgreSQLの制限)
SerialField#next_val
列挙値(数種類の決まった値)しか持てないフィールドです。
EnumField shohin_type
.add_enum 1 Foods
.add_enum 2 Drinks
.add_enum 3 Books
このフィールドは、1,2,3のどれかの値(かnull値)しか持てないようになります。
データベース内では整数(integer)のフィールドとして定義され、
整数値を格納します。
Rubyのコードでは、:Foods, :Drinks, :Booksといったシンボル値で処理されます。
r = shohin.new_wrapper_object(1, "a", :Foods)
puts r.insert_sql
# =>"insert into
Shohin(shohin_code,shohin_name,shohin_type)
values(1,'a',1)"
q = Query.new(shohin)
fetch_db(q) |tupl|
r = q.tupl_to_wrapper(tupl)
puts r.shohin_type # => Foods
case r.shohin_type
when :Foods
...
when :Drinks
...
when :Books
...
end
end
EnumField#add_enum(value, enum_name)
-
このフィールドが持ち得る値を追加します。
ここに列挙されてないフィールドの値をDBに格納しようとすると、
insert_sql,update_sql等でDBDataErrorの例外が発生します。
真偽値しか持てないフィールドです。
データベース内では整数フィールドとして定義され、真の場合0、偽の場合1が格納されます。
- BoolField#default_true
このオプションを指定すると、既定値が真になります。
- BoolField#default_false
このオプションを指定すると、既定値が偽になります。
Query#new(table_or_field ... )
-
Queryオブジェクトを生成します。
パラメータはテーブルまたはフィールドに対応するデータモデルオブジェクトです。
テーブルを指定した場合は、そのテーブルの全フィールドに対する問い合わせを生成します。
Query#cond=(c)
-
select文の where句に入る条件を設定します。
Query#ordry_by
-
select文のorder by句に入るフィールドのデータモデルオブジェクトの配列です。
Query#to_s
-
select文を生成します。
Query#tupl_to_wrapper(tupl)
-
この問合せの結果として、
ruby-postgresから返却された配列からラッパーオブジェクトを生成します。
q = Query.new(Uriage)
fetch_db(q) do |tupl|
r = q.tupl_to_wrapper(tupl)
# rはUriageのラッパーオブジェクトになっている
puts r.shohin_code
...
問い合わせに複数のテーブルが関連する時は、
テーブルのデータモデルオブジェクトをキーとして、
そのテーブルのラッパーオブジェクトを値とするHashを返却します。
テーブル名を小文字に変換したものもメンバー名として使用できます。
q = Query.new(Uriage, Shohin)
fetch_db(q) do |tupl|
r = q.tupl_to_wrapper(tupl)
puts r.uriage.dt # r[Uriage].dtも可
puts r.shohin.shohin_code
...
QueryCondはQueryの条件(cond)に設定する条件を表すオブジェクトです。
QueryCond#new(left, op ,right)
-
左辺、演算子、右辺を指定してQueryCondを生成する
QueryCond#to_s
-
条件を文字列として生成する
QueryCond#|(cond)
-
(self) or (cond)という複合条件を生成する
QueryCond#&(cond)
-
(self) and (cond)という複合条件を生成する
RangePair#new(first, last)
-
firstからlastまでの範囲でRangePairを生成します
RangePair#new(x)
-
x[0]からx[1]までの範囲でRangePairを生成します。
RangePair#include?(v)
-
vがfirst以上last以下ならば真。
RangePair#===(v)
-
include?(v)と同じ
RangePair#<=>
RangePair#+(r)
-
selfとrが重なっていなければ、RangeArray.new(self, r)。
重なっていれば、両者の和の範囲を持つRangePairを生成する。
RangeArray#new(*args)
-
argsはRangePairか2要素の配列。
RangeArrayを生成します。
RangeArray#normalize
-
重複無しで同じ範囲を示すRangeArrayを生成します。
RangeArray#[i]
-
i番目のRangePairを返却します。
RangeArray#size
-
含まれるRangePairの数を返却します。
RangeArray#==(ra)
RangeArray#include?(v)
-
vを含むRangePairを要素に含んでいれば真。
RangeArray#===(v)
-
include?(v)と同じ
RangeArray#+(ra)
-
selfとrの両方を示すRangeArrayを生成する。
RangeArray#each(&block)
RangeArray#to_s