友達と遊びに行くつもりでいたらなんかみんな都合合わないとかでお流れになりそうな雰囲気なので、せっかくだから久々に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認証するコントローラが書けそう。明日あたり試してみよう。