Rails3で簡単なサンプルを作ってみた スレッド式掲示板です。

レスは追加できるけど、スレッド追加する機能は作り忘れた。

Mongoid版

http://github.com/func09/rails3_sample_bbs/tree/mongoid-20100813

ActiveRecord版

http://github.com/func09/rails3_sample_bbs/tree/activerecord-20100813

それぞれのREADMEに従えば動くはず。 rake db:seed でサンプルデータがインサートされるます。

Rails3だからといって、別段特殊なコードは書けなかった。 よくもわるくもいつもどおり書けた。

http://github.com/func09/rails3_sample_bbs/zipball/mongoid-20100813

http://github.com/func09/rails3_sample_bbs/zipball/activerecord-20100813

Posted in ruby, ruby on rails at 8月 16th, 2010. No Comments.

DBに配列やハッシュのデータを保存したいなと思って調べました。

ActiveRecord.serializeで実に簡単に実現できるんですね。
http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M002229

If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object, then specify the name of that attribute using this method and it will be handled automatically. The serialization is done through YAML. If class_name is specified, the serialized object must be of that class on retrieval or SerializationTypeMismatch will be raised.

このメソッドでオブジェクトとして保存・取得したい属性の名前を指定すると、自動的に処理されるようになる。 YAMLでシリアライズされ、class_nameオプションが指定されると格納できるオブジェクトのクラスを制限できる。あるいはSerializationTypeMismatchの例外となる。

ポイント

  • シリアライズしたいカラムの型をtextにする
  • serializeメソッドでカラムを指定する
  • 格納できるクラスを指定可能
  • 内部的にはYAML

実例

さて、やってみます

まずマイグレーション内で、text型のdataというフィールドを用意する。
ここにシリアライズしたデータが格納されます。

  1. class CreateItems <ActiveRecord::Migration
  2.   def self.up
  3.     create_table :items do |t|
  4.       t.text :data
  5.     end
  6.   end
  7.   def self.down
  8.     drop_table :items
  9.   end
  10. end


そしてモデル内でシリアライズするフィールドを指定。

  1. class Item <ActiveRecord::Base
  2.   # シリアライズされたデータとして扱いたいフィールドを指定する
  3.   serialize :data
  4. end


script/consoleなどをおもむろに起動し、本当にオブジェクトが保存できるのか確認します。

  1. # 配列を代入する
  2. Item.create :data => [1,2,3]
  3.  
  4. item = Item.find(:all).last
  5. p item.data #=>[1,2]
  6. p item.data.class #=>Array


ちゃんと配列として取得できました。めでたしめでたし。

どうなってるの?

DBに格納する際にオブジェクトをYAML化しているようです。

検索したい場合

findしたい場合は、オブジェクトではなく文字列として評価されてしまうので Object#to_yamlを使うことになります。

  1. Item.find_by_params([1,2,3]) # => これではレコードが見つからない
  2. Item.find_by_params([1,2,3].to_yaml) # => 見つかる


使い勝手が悪い。。

格納する型を限定する

このままだとオブジェクトならなんでも格納できてしまいます。 そこで格納するクラスを指定してやります。

  1. class Item <ActiveRecord::Base
  2.   # 配列以外のクラスは格納しない
  3.   serialize :data, Array
  4. end


script/consoleで試してみます

  1. # 配列の格納は成功
  2. >> Item.create :data => ['bob','alice']
  3. => #<item id: 1, data: ["bob", "alice"]>
  4.  
  5. # ハッシュの格納は例外が発生する
  6. >> Item.create :data => {:name => 'bob'}
  7. ActiveRecord::SerializationTypeMismatch: data was supposed to be a Array, but was a Hash


これでかなり使い勝手が良くなりましたけど、配列の中身までは保証できないので、精神的安定のために使う程度でしょうか?

最後に

DBにシリアライズしたデータを保存しなければならない状況は無いにこした事は無いと思いますので 設計レベルで回避できる場合はそちらをおすすめします。

Posted in ruby on rails, 日記 at 5月 21st, 2009. No Comments.