DataMapper を使う

ActiveRecord はかなり便利な O/R mapper ですが,さらに上(?)がありました.

なお,このエントリは 0.9.11 をベースに書いています.

このエントリの目次と他エントリへのリンク

概要

ActiveRecord とは異なり,スキーマ定義は全てモデルに記述する (ActiveRecord だとマイグレーションとして記述).
とにかく,すべての定義がモデル定義に集中する.

class Post
  include DataMapper::Resource
  property :id, Integer, :serial => true
  property :title, String
  property :body, Text
  property :created_at, DateTime
end


「なぜ DataMapper なのか?」については以下を参照してください (微妙な訳ですが...).
 → http://d.hatena.ne.jp/KrdLab/20090503/1241331628

接続

といっても,以下のコードが即 DB に接続するわけではない (Adapter を作成するだけ).

DataMapper.setup(:default, "mysql://localhost/dm_test")

詳細設定の場合は以下の通り.

DataMapper.setup(:default, {
  :adapter  => "mysql",
  :database => "データベース名",
  :username => "ユーザ",
  :password => "パスワード",
  :host     => "localhost"
})

マイグレーション

if __FILE__ == $0 then
  DataMapper.auto_migrate!
end

他にも,auto_upgrade 等がある.

Logger

DataObjects::Mysql.logger = DataObjects::Logger.new('log/dm.log', 0)

ってやると,こんな感じでログがとれる.

Sun, 05 Jul 2009 08:36:07 GMT ~ debug ~ (0.028012) INSERT INTO `people` (`age`, `name`) VALUES (10, 'aaa')
Sun, 05 Jul 2009 08:36:07 GMT ~ debug ~ (0.000021) INSERT INTO `people` (`age`, `name`) VALUES (20, 'bbb')
Sun, 05 Jul 2009 08:36:07 GMT ~ debug ~ (0.000018) INSERT INTO `people` (`age`, `name`) VALUES (30, 'ccc')
Sun, 05 Jul 2009 08:36:07 GMT ~ debug ~ (0.000017) INSERT INTO `people` (`age`, `name`) VALUES (31, 'ddd')

Property 定義

↓こっちに書いた.
http://d.hatena.ne.jp/KrdLab/20090531/1243768985

Validation 指定

↓こっちに書いた.
http://d.hatena.ne.jp/KrdLab/20090614/1244987529

作成

Post.create(:title => '作り方 1',
            :body => 'create した.')

post = Post.new
post.title = '作り方 2'
post.body  = 'new してから save した.'
post.save

post = Post.first_or_create(:title => '作り方 1')
p post

結果は以下の通り.

mysql> select * from posts;
+----+-------------+---------------------------------+---------------------+
| id | title       | body                            | created_at          |
+----+-------------+---------------------------------+---------------------+
|  1 | 作り方 1    | create した.                   | 2009-05-03 00:14:34 |
|  2 | 作り方 2    | new してから save した.        | 2009-05-03 00:14:34 |
+----+-------------+---------------------------------+---------------------+

読み取り

主キーによる検索.

Post.get(100)    # 見つからないときは nil
Post.get!(100)   # 見つからないときは ObjectNotFoundError
# ↑#<DataMapper::ObjectNotFoundError: Could not find Post with key [100]> とかいわれる


すべて取得する.

Post.all
Post.all.each {|r|
  puts "#{r.title}, #{r.body}"
}


ActiveRecord っぽくもある.

Post.first(:created_at.lt => Time.now)
Post.find(:first, :conditions => ['created_at < ?', Time.now])


演算子が使える.
http://datamapper.org/doku.php?id=docs:finders

  • gt: よりも大きい
  • lt: よりも小さい
  • gte: 以上
  • lte: 以下
  • not: 等しくない
  • like: マッチ (LIKE 指定)
  • in: を含む (引数に配列を渡すと自動的にこれを指定したことになる)

Post.all(:title.like => 'title%', :id => [*1..5])

更新

attribute の変更方法.

post.title = '変更したよ'
post.attributes = {:title => '変更したよ'}
post.attribute_set(:title, '変更したよ')
post.save

これも OK.

post.update_attributes(:title => '変更したよ')


オブジェクトが変更されていることを確認.
変更されていれば true が返ってくる.

post.dirty?
post.attribute_dirty?(:title)

削除

post = Post.first
post.destroy

消えちゃいます.

アソシエーション

1:N とか 1:N:1 とか.
以下,基本的な使い方.

class Post
  include DataMapper::Resource
  property :id, Integer, :serial => true
  property :title, String
  property :body, Text
  property :created_at, DateTime

  has n, :comments
end

class Comment
  include DataMapper::Resource
  property :id, Serial
  property :posted_by, String
  property :email, String
  property :url, String
  property :body, Text

  belongs_to :post
end

comments テーブルは以下の通り (post_id が追加される).

mysql> describe comments;
+-----------+-------------+------+-----+---------+----------------+
| Field     | Type        | Null | Key | Default | Extra          |
+-----------+-------------+------+-----+---------+----------------+
| id        | int(11)     | NO   | PRI | NULL    | auto_increment |
| posted_by | varchar(50) | YES  |     | NULL    |                |
| email     | varchar(50) | YES  |     | NULL    |                |
| url       | varchar(50) | YES  |     | NULL    |                |
| body      | text        | YES  |     | NULL    |                |
| post_id   | int(11)     | YES  |     | NULL    |                |
+-----------+-------------+------+-----+---------+----------------+

さっそく作成してみる.

post = Post.get(3)

comment = post.comments.build(:posted_by => 'krdlab', :body => 'This is the comment.')
comment.save

レコードが追加された.

mysql> select * from comments;
+----+-----------+-------+------+----------------------+---------+
| id | posted_by | email | url  | body                 | post_id |
+----+-----------+-------+------+----------------------+---------+
|  1 | krdlab    | NULL  | NULL | This is a comment.   |       3 |
+----+-----------+-------+------+----------------------+---------+


別の方法.

comment = Comment.create(:posted_by => 'krdlab', :body => 'hogehoge.')

post = Post.get(3)
post.comments << comment
post.save

またしても追加された.

mysql> select * from comments;
+----+-----------+-------+------+----------------------+---------+
| id | posted_by | email | url  | body                 | post_id |
+----+-----------+-------+------+----------------------+---------+
|  1 | krdlab    | NULL  | NULL | This is a comment.   |       3 |
|  2 | krdlab    | NULL  | NULL | hogehoge.            |       3 |
+----+-----------+-------+------+----------------------+---------+

ところで

ActiveRecord と DataMapper ですが,これらはシステム設計におけるパーシステンス層の設計パターン名そのままですね.