http://d.hatena.ne.jp/KrdLab/20090503/1241331627 の補足.
モデル定義についてです.
一部のサンプルは公式サイトから拝借しています.
プロパティの宣言
モデルクラス内部で property メソッドを呼び出す.
class Post include DataMapper::Resource property :id, Serial # primary serial key property :title, String, :nullable => false # null 値不可 property :published, Boolean, :default => false # デフォルト値 = false end
DataMapper.auto_migrate! すると以下のようになる.
+-----------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+-------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | title | varchar(50) | NO | | NULL | | | published | tinyint(4) | YES | | 0 | | +-----------+-------------+------+-----+---------+----------------+
主キー (primary key)
primary key は,ActiveRecord の様に自動的には作成されない.
また,最低でも 1 つの key property を設定しなければならない.
例えば,
class Dummy include DataMapper::Resource property :name, String end
と定義し,使ってみると,以下のようなエラーが発生する.
/var/lib/gems/1.8/gems/dm-core-0.9.11/lib/dm-core/resource.rb:622:in `assert_valid_model': Dummy must have a key. (DataMapper::IncompleteResourceError)
DataMapper は Identity Map のインデックスとして,モデルの primary key を使う.
primary key としてよく用いられる auto-incrementing な整数は,以下のように指定できる.
property :id, Serial
id の様な,レコードの一意性を確保するための人為的なキー (人工キー) ではなく,入力データとして与えられる値を主キー (自然キー (natural key)) にする場合は,property 定義にオプションとして :key => true を渡せばよい.
property :name, String, :key => true
複合キー (composite key)
複数の property に :key 設定すればよい.
class Post include DataMapper::Resource property :old_id, Integer, :key => true property :new_id, Integer, :key => true end
デフォルト値の指定
プロパティに対して :default を指定すれば,デフォルト値を設定できる.
static な値だけではなく,Proc によるデフォルト値を指定することもできる.
Proc は次の 2 引数をとる.
- プロパティが設定されるインスタンス自身 (引数 "r")
- プロパティ自身 (引数 "p")
(※ ブロック内部で "p" を参照すると,stack level too deep (SystemStackError) となる.そりゃそうか.でも何に使うんだろう?)
class Post include DataMapper::Resource property :id, Serial property :title, String, :nullable => false property :published, Boolean, :default => false property :md5, String, :default => Proc.new {|r, p| Digest::MD5.hexdigest(r.title) if r.title } end
で,呼び出すと,
Post.create(:title => 'ほげほげ')
+----+--------------+-----------+----------------------------------+ | id | title | published | md5 | +----+--------------+-----------+----------------------------------+ | 1 | ほげほげ | 0 | 69e285bae22889ad148c8e81dfeaabdd | +----+--------------+-----------+----------------------------------+
md5 は,Post を new したり create したりするタイミングで設定される.
(new/create で :title を指定しないと NULL になる)
遅延ロード (lazy loading)
プロパティは遅延ロードさせることができる.
遅延ロードするプロパティは,レコードを取得した段階ではロードされず,そのプロパティが最初に呼ばれたときロードされる.
テキストフィールドはデフォルトで遅延ロードになる (←しないようにすることもできる).
class Post include DataMapper::Resource property :id, Integer, :serial => true property :title, String property :body, Text # 遅延ロードする (デフォルト) property :notes, Text, :lazy => false # 遅延ロードしない end
複数のプロパティをグループ指定することで,一括して遅延ロードさせることもできる.
class Post include DataMapper::Resource property :id, Integer, :serial => true property :title, String property :subtitle, String, :lazy => [:show] property :body, Text, :lazy => [:show] property :views, Integer, :lazy => [:show] property :summary, Text end
例えば,データがこんなのだったとする.
+----+---------------------------+----------------------+--------------------------------+-------+--------------+ | id | title | subtitle | body | views | summary | +----+---------------------------+----------------------+--------------------------------+-------+--------------+ | 1 | KrdLab の不定期日記 | DataMapper を使う | 使い方とかほげほげ. | 2 | 概要です | +----+---------------------------+----------------------+--------------------------------+-------+--------------+
上の例では,Post.get(1) したとき,id と title だけがロードされる (summary(Text) はデフォルトで遅延ロード).
#<Article id=1 title="KrdLab \343\201\256\344\270\215\345\256\232\346\234\237\346\227\245\350\250\230" subtitle=<not loaded> body=<not loaded> views=<not loaded> summary=<not loaded>>
subtitle/body/views のどれか 1 つにアクセスすると,:show グループに属する 3 つの値は一括してロードされる.
summary は,それ自体を呼ばない限りロードされない.
#<Article id=1 title="KrdLab \343\201\256\344\270\215\345\256\232\346\234\237\346\227\245\350\250\230" subtitle="DataMapper \343\202\222\344\275\277\343\201\206" body="\344\275\277\343\201\204\346\226\271\343\201\250\343\201\213\343\201\273\343\201\222\343\201\273\343\201\222\357\274\216" views=2 summary=<not loaded>>
summary にアクセすればロードされる.
#<Article id=1 title="KrdLab \343\201\256\344\270\215\345\256\232\346\234\237\346\227\245\350\250\230" subtitle="DataMapper \343\202\222\344\275\277\343\201\206" body="\344\275\277\343\201\204\346\226\271\343\201\250\343\201\213\343\201\273\343\201\222\343\201\273\343\201\222\357\274\216" views=2 summary="\346\246\202\350\246\201\343\201\247\343\201\231">
アクセス制限
プロパティの accessor はデフォルトで public になる.
:accessor オプションを用いることで private や protected にすることができる.
class Post property :title, String, :accessor => :private # reader/writer ともに private property :body, Text, :accessor => :protected # reader/writer ともに protected end
:writer / :reader オプションを用いることで,reader は public,writer は private,といった指定をすることができる.
class Post property :title, String, :writer => :private # reader は public,writer は private property :tags, String, :reader => :protected # reader は protected,writer は public end
accessor の override
プロパティのあとに accessor の定義を追加すれば良い.
class Post property :name, String def name=(new_name) raise ArgumentError if new_name != 'KrdLab' attribute_set(:name, new_name) # attribute_set を使うと dirty? で判定できるようになる end end
利用可能な型
DM-Core は,次のデータ型をサポートする.
TrueClass, Boolean, String, Text, Float, Fixnum, Integer, BigDecimal, DateTime, Date, Time, Object, (marshalled) Discriminator (all you need for Single Table Inheritance, actually)
DM-Types を include した場合は,次のデータ型がサポートされる.
Csv, Enum, EpochTime, FilePath, Flag, IPAddress, URI, Yaml, Json, BCryptHash, Regex
そのほかの型については以下のページを参照.
http://datamapper.org/doku.php?id=dm-more:dm-types