As Sloth As Possible

可能な限りナマケモノでありたい

タグ:DataMapper

#1986@freenodeで、ActiveRecordをRailsの中でじゃなくて単体で使えるか、って聞かれたんだけど、まぁ確かに使えるけどついでにいろんな余計なものが付いてきて、Stringみたいなコアなクラス書き換えたりするから微妙なんだよなー、と思い出してDataMapperを勧めてみた。ARはとっつき易いしRailsで使う分にはいいんだけど、単体で使うには上記の理由もあってあんまり好きじゃない。

でも微妙に変わってんだよね

DataMapperを単体で使う方法については過去の記事で書いたんだけど、半年以上たってるもんでその間にDataMapperも大分バージョンアップして。ということで、同じ挙動をするものを動くように書き直してみた。

require 'dm-core'

DataMapper.setup(:default, {
  :adapter  => 'sqlite3',
  :database => 'dm_test'
})
DataMapper.setup(:second_database, {
  :adapter  => 'sqlite3',
  :database => 'dm_test2'
})

class Rabbit
  include DataMapper::Resource
  property :id,     Integer,  :serial => true
  property :name,   String,   :key => true
  property :color,  String

  # p で出力したときに見にくかったから変えてるだけ
  def inspect
    "<#{self.class}:#{self.id}, #{self.name}, #{self.color}>"
  end
end

Rabbit.auto_migrate!

rabbit        = Rabbit.new
rabbit.name   = 'Jitterbug'
rabbit.color  = 'orange'
rabbit.save

Rabbit.create(:name => 'Mokha', :color => 'chestnut')

puts "default database"
p Rabbit.all
p Rabbit.first(:name => 'Mokha')

puts "-----"

DataMapper.repository(:second_database) do
  Rabbit.auto_migrate!
  Rabbit.create(:name => 'Tango', :color => 'white')
  puts "secnond database"
  p Rabbit.all
end

変わったところは以下の通り。

  • Modelを作るのに前のコードではクラスを継承していたが、今はモジュールをインクルードするようになっている
  • DataMapper.database(:repos_name)ではなくDataMapper.repository(:repos_name)になった

くらいか。もっと詳しく知りたい場合はDataMapperのドキュメント読んでね。まぁやっぱり日本語の情報少ないのは泣きどころ。Merbも1.0がリリースされたことだし、DataMapperも増えてくるかな。

補足・参考

上の例ではsqlite3を使ってるけど、その場合はdo_sqlite3をインストールする必要がある。あったはず。もちろんMySQLとかでやりたければdo_mysqlとかをインストールしてください。

あとこれは既知の問題なんだけど、関連モジュールのaddressableが2.0.0だと2008/11/19現在のgem最新版のDataMapper(0.9.6)が動かないので、addressableをダウングレードするかDataMapperの開発版をgithubから取ってきて下さい。

友達と遊びに行くつもりでいたらなんかみんな都合合わないとかでお流れになりそうな雰囲気なので、せっかくだから久々にMerbでもいじってみることにした。ん?コミケ?行ってないよ。月曜に有給取ったら職場の人に「週末は戦場に行くので週明けに戦士の休息ですね、わかります」的なリアクションをされたんだけど、いや、別にそういうわけじゃ…。わかんないけど。明日暇に耐え兼ねて行くかもしんないけどさっ!うう…。

思い付きでupdateするな俺のバカ

とりあえず以前の記事で軽く動かしてみる程度はやったものの、MerbもDataMapperもしばらく見てない間に更新されてて、深く考えずにgem updateしたらハマった。Merb本体のバージョンが0.9.4に上がってて、Merbをアップデートしたらextlibのバージョンも0.9.4になっちゃったんだけど、DataMapperのバージョンは0.9.3までしか上がってない。gemのdatamapperが依存してるdata_objectsがextlibの0.9.3を要求する(dependencyが'= databjectsのバージョン'になってる)ため、 「extlibを一旦0.9.3にしてからdata_objects、datamapper、merb_datamapperをアップデート」してみた。

入るには入るんだけど、もちろんextlibが0.9.3のままだと最新のMerbが動かないし、extlibを0.9.4にするとこんどはdatamapperがrequireできない。うがー。ActiveRecordやSequelを使うのも考えたけど、そもそもARを使いたくないからDataMapperを試しているのでARは無いし、Sequelも面白そうなんだけど、Merbのプラグイン?で使ってみたいのがARかDataMapperを想定してるのでSequelは使えない。ので却下。ソースをいじるとかも考えたけど、無意味に面倒な上危ないので却下。どうせリスクがあるんならedge版入れちゃえ、そもそも実験だし、てことでgithubからDataMapperの最新版取ってきて入れた。手順はMerb wikiを見ながらやってみた。

$sake -i http://github.com/dkubb/dm-dev/tree/master/dm-dev.sake?raw=true
$ sake dm:clone
$ cd dm
$ sake dm:install

DataMapperの0.9.4があっさり入りやがった。ぬーん。とりあえず今のところ問題なく動いてる様子。DataMapperがMerb本体に深く影響されるのはなんだか納得行かないんだけど、ARだってRailsにべったりだしなぁと考え直すことにする。

なにはともあれdatabase.yml

とりあえずアプリの雛形を作る。

$merb-gen app --orm datamapper sample

ARが本体の一部であるRailsと違って、O/Rマッパーが本体と分離してるMerbはこの時点ではdatabase.ymlとかmodelsディレクトリとかは作られない。

$rake -T | grep dm
rake dm:db:automigrate            # Perform automigration
rake dm:db:autoupgrade            # Perform non destructive automigration
rake dm:db:create                 # Create the database (postgres only)
rake dm:db:database_yaml          # Create a sample database.yml file
rake dm:db:drop                   # Drop the database (postgres only)
rake dm:db:migrate                # Migrate the database to the latest version
rake dm:db:migrate:down[version]  # Migrate down using migrations
rake dm:db:migrate:up[version]    # Migrate up using migrations
rake dm:db:reset                  # Drop the database, and migrate from scr...
rake dm:sessions:clear            # Clears sessions
rake dm:sessions:create           # Perform automigration for sessions
$rake dm:db:database_yaml

これでconfig/database.yml.sampleが出来るので、それをコピーして弄る。中身はRailsでアプリ作ったことある人なら見慣れてるであろうデータベースの設定なので、適当にいじる。

# config/database.yml
---
development: &default
  adapter:  mysql
  encoding: utf8
  database: sample_development
  username: user
  password: password
  host:     localhost

そうしたら、

$merb-gen model user
Generating with model generator:
     [ADDED]  spec/models/user_spec.rb
     [ADDED]  app/models/user.rb

しておいて、app/models/user.rbを、

# app/models/user.rb
class User
  include DataMapper::Resource
  property :id, Integer, serial => true
  property :name, String, nullable => false
end

のようにいじった上で、rake dm:db:automigrateする。これで、DBを適切に設定してあればidとnameというカラムを持ったusersテーブルが出来てるはず。とりあえずここまでできたらmerb -i(Railsで言うところscript/console)とかやっていろいろ遊んでみるといい。

repositoriesってなんぞ

いじってる途中で気付いたんだけど、database.yml.sampleの中にこのような記述が。

development: &defaults
  # These are the settings for repository :default
  adapter:  postgres
  database: sample_development
  username: the_user
  password: secrets
  host:     localhost

  # Add more repositories
  # repositories:
  #   repo1:
  #     adapter:  postgresql
  #     database: sample_development
  #     username: the_user
  #     password: secrets
  #     host:     localhost
  #   repo2:
  #     ...

調べたら、repositories以下に上のと同じ形式でdbの設定を書いて置くとそれを使って接続できる様子。ふむふむ。それなら、とさっきのdatabase.ymlをちょこっと書き換えてみる。

# config/database.yml
---
development: &default
  adapter:  mysql
  encoding: utf8
  database: sample_development
  username: user
  password: password
  host:     localhost

  repositories:
    slave:
      adapter:  mysql
      encoding: utf8
      database: sample_development_slave
      username: user
      password: password
      host:     localhost

これでとりあえずOK。slave、にしたのはdefaultの設定のDBからレプリケーションしてるスレーブのサーバを想定してるんだけど、そもそも全然用途の違う別のDBでもいい。で、使うときはこんなかんじ。

# マスターのサーバから取ってくる
user = User.first
# こっちはスレーブから取ってくる
user_s = User.first(:repository => repository(:slave))

おお。簡単だ。もうちょい説明すると、allとかfirstとかのfindしてくる系統のメソッドに、{:repository => DataMapper::Repositoryのオブジェクト}って引数を渡してやると、そのDB設定で探してくる。database.ymlの設定からDataMapper::Repositoryのオブジェクトを取得するには、repository(設定名)というメソッドを使う。

毎回これをやると面倒なので、参照用のときは必ず:slaveから取ってくるようなメソッドをUserに生やしておくといいかもしれない。逆でもいいな。更新が必要なときだけマスターを見るようにしとくとか。こっちの方が安心か。おそらくモデル単位でどこのリポジトリを使うかを設定できるはずなので、普通のデータは:defaultのDBに入れておいて、あるモデルは:portalに入れておく、とかもできる。DataMapper::Repositoryは動的に生成できるので、分散もそんなに難しくなさそうだ。Rails+ARだと多分プラグインを使わないとできなかったと思うけど、標準でこの機能が付いてるMerb+DataMapperはなかなか素敵。…いや、そう聞いてたからDataMapperを試してみたくなったんだけども。最初からこれが言いたかっただけだったりする。

次はmerb_openidでも

ユーザ認証にOpenIDを使おうと思ってruby-openidを使ってあれこれ試行錯誤してる最中に、merb_openidというgemがあることに気付いた。これを使えば簡単にOpenID認証するコントローラが書けそう。明日あたり試してみよう。

昨夜詳解 Objective-C 2.0が届いてたので、週末はObjective-C漬けになろうと思ってたんだけど、何故かMerbを弄るのにはまってしまった。ということで遅蒔きながらMerb+DataMapperをいろいろ調べた。

ちょっと触ってみた感触としては、良いね。基本はRails+ARで書くのとそんなに変わらない。良くわかんないけど勘で「Railsだったらこうだよな」で書いても大概のものは動いた。ふむ。あとはパフォーマンスがどうなるかだけど、こればっかりはまともなアプリ書いてみないとわかんないな。前に頼まれて作ったとあるイベントの管理システムを試しにMerb化してみるか。FastladderをMerbに移植してみるとか(ry。いいからバグ修正しろって怒られそうなのでやめとこう。ていうか忙しくて止まってたもろもろの修正はやくコミットしないと。

DataMapperは結構気にいった。モデルがそのままスキーマの定義になるのはわかりやすいなぁ。複数DBも簡単に使えそうな印象だけど、Merbでどうやるのかちょっとわかんなかった。調べてみるか。

それにしても、サイトのサンプルコードと最新版の仕様が全然違うのにはちょっと泣けた。Getting Startedに書いてある通りにやろうとしたら、まずインストールするgemの名前からして違うとか!サンプルに書いてあるモジュールも存在すらしないし!えー。日本語の最新の情報が楽に手に入るようになるまでは時間かかりそうだし、APIリファレンスとソース読みつつ試行錯誤するしか無いか。

一応試しに書いてみたコードを曝しておこう。

require 'data_mapper'

DataMapper::Database.setup({
  :adapter => 'sqlite3',
  :database => 'dm_test'
})

DataMapper::Database.setup(:second_database, {
  :adapter => 'sqlite3',
  :database => 'dm_test2'
})

class Rabbit < DataMapper::Base
  property :name,   :string
  property :color,  :string

  # pの出力が見にくかったので
  def inspect
    "<#{self.class}:#{self.id},#{self.name},#{self.color}>"
  end
end

Rabbit.auto_migrate!

rabbit = Rabbit.new
rabbit.name = 'Jitterbug'
rabbit.color = 'orange'
rabbit.save!

Rabbit.create({:name => 'Mokha', :color => 'chestnut'})

puts "default database"
p Rabbit.all
p Rabbit.first(:name => 'Mokha')

puts "-----"

DataMapper.database(:second_database) do
  Rabbit.auto_migrate!
  Rabbit.create({:name => 'Tango', :color => 'white'})
  puts "second database"
  p Rabbit.all
end
$ ls
rabbit.rb
$ ruby rabbit.rb 
default database
[<Rabbit:1,Jitterbug,orange>, <Rabbit:2,Mokha,chestnut>]
<Rabbit:2,Mokha,chestnut>
-----
second database
[<Rabbit:1,Tango,white>]
$ ls
dm_test dm_test2 rabbit.rb

ちなみにコード中のはうちのうさぎ達の名前。一匹だけダンスつながりじゃないのは俺が名付け親じゃないから。


ジルバの写真で和むがいい。

↑このページのトップヘ