serialize-rails gemでJSONをDBに格納するという記事を書いたけど、わざわざgemを使わなくても第2引数に渡すオブジェクトがload
とdump
を実装していれば十分だった。ちなみにActiveRecord 3.2で確認。
たとえばURLをURIのオブジェクトとして扱いたい場合。
# app/models/uri_coder.rb class UriCoder # ActiveRecord::Baseのインスタンスが作られるときに呼ばれる。 # DBから読み出したときにはstringには保存されている値が渡され、 # newされたときにはnilが渡される。 def load(string) URI.parse(string) if string.present? end # DBに書き込むときに呼ばれる。 def dump(uri) uri.to_s end end # app/models/page.rb class Page < ActiveRecord::Base # 第2引数に渡されたオブジェクトのload/dumpが使われる。 serialize :url, UriCoder.new end
実行してみるとこんな感じ:
page = Page.new page.url = URI.parse("http://apple.com") page.save! #=> INSERT INTO "pages" ("url") VALUES ("http://apple.com") # DBに保存するタイミングでdumpが呼ばれてStringに変換される page_id = page.id page = Page.find(page_id) p page.url #=> #<URI::HTTP:0x007fedc8bf4bf0 URL:http://apple.com> # DBから読みだしたタイミングでloadが呼ばれてURIに変換される
以上、下記のTweetを見てserialize
使えないかなーと思って試してみた次第。
@ikm そうそう、それに近い・・・! でもやりたいのはデータ構造の serialize/deserilize ではなくって単純に url カラムのデータを String じゃなくて URI で受け取りたい、ということだったり。自分で作るほうがはやいかもしれない・・・
— Naoya Itoさん (@naoya_ito) 2013年4月27日
ただ上の例だと代入するときにURI
のオブジェクトに事前に変換していてちょっと面倒。直でStringを代入するとちょっと微妙…:
page = Page.new page.url = "http://apple.com" p page.url #=> "http://apple.com" # 代入のタイミングではloadが走らないので文字列のまま。ちょっと不便。 page.save! #=> INSERT INTO "pages" ("url") VALUES ("http://apple.com") # 保存は前述の場合と変わらずURLの文字列がそのまま保存される p page.url #=> #<URI::HTTP:0x007fedcb122df0 URL:http://apple.com> # 保存したタイミングでloadが走るのか、URIに変換されてる。
ここはやっぱりread_attribute
とwrite_attribute
をオーバーライドするのが良さそう。この方法ならupdate_attributesにも使えるし。
@naoya_itoでしたらread/write_attributeでurlカラムだけURIで変換するようオーバーライドすればよいかも。update_attributesの挙動も合うか確認が必要ですし、もっと簡単な方法もありそうですが…
— Masato Ikedaさん (@ikm) 2013年4月27日