Edited at

RailsでActiveRecordとMongoidの共存のさせ方

More than 5 years have passed since last update.


背景

Herokuで適当にスキーマレスなデータをOutput出来る場所がほしい!でも、Herokuはディスクが自由に使えないので、text書き出しができない!!

こんな要求の解決法としては、textタイプのフィールドを作ってserializestoreを使うのが一つの手。ただ、割と書き込みが多い事が想定され、無駄に大切なPostgreSQLサーバーに負荷をかけてしまう。

そんなこんなでスキーマレスな出力場所としてMongoDBを使うというアイデアが持ち上がった。HerokuではMongoHQなら16M、MongoLabなら0.5Gが無料で使える。

なお、基本的に、MongoDBをサブとして使うのでrails g modelなどをした時にmongoidがinvokeされるのは避けたい。


実践

gem 'mongoid'

して、bundle install

$ rails generate mongoid:config

すると、config/mongoid.ymlにdevelopmentとtestの設定が生成されるので、ここにconfigurationを書く。基本的には変更する必要がないが、database, username, passwordあたりの書き換え、書き足しだけチェックしてみればいいのではないだろうか?

productionの設定はHerokuのAdd-onのドキュメント通りにすれば良いと思う。MongoHQのドキュメントの方が詳しく書かれていたのでこっちを参照した。使ったのはMongoLabだけど(笑)

ちゃんと設定したければ、

http://mongoid.org/en/mongoid/docs/installation.html

にそれぞれオプションが詳しく載っている。

mongoidのモデルはrails generate modelでできるようになっている。

$ rails generate model user_mongo name:string

invoke mongoid
create app/models/user_mongo.rb


app/models/user_mongo.rb

class UserMongo

include Mongoid::Document
field :name, type: String
end

ということは、ActiveRecordのmodelを作るとき、active_record:とprefixをつける必要がある。

$ rails generate acrive_record:model

これはmigrationコマンドやgemのdeviseなどでも同様。

これが困る場合、application.rb


config/application.rb

config.generators do |g|

g.orm :active_record
end

と追加すれば良い。このときmongoidのモデルを作るときは

$ rails generate mongoid:model

とする。

最後に、Mongoidの使い方は、適当に自分で調べて使ってみれば意外と簡単に使えると思うが、http://mongoid.org/en/mongoid/docs/relations.html は読んでおくと良いと思う。多分ActiveRecordとはちょっと違っていて難しいであろう、Relation/Associationのあたりの話がちゃんと書いてある。


参考


追記: ロードするclassの設定

mongoidのgemを見ると設定なしではapps/model以下の全てのRubyファイルをロードしてしまう設定になっていた。

これを防ぐためには、optionspreload_modelsにMongoid::DocumentをIncludeしているクラスを全て指定すれば良い。

例えば、data_mongo.rbuser_monbo.rbを作った場合は、


mongoid.yml

production:

# ...
options:
preload_models:
- data_mongo
- user_mongo

というように設定する。

一応、今後解決する可能性もあるので、mongoidの問題となっている部分のを載せておく


mongoid3.0.22/lib/rails/mongoid.rb

    def load_models(app)

app.config.paths["app/models"].each do |path|
preload = ::Mongoid.preload_models
if preload.resizable?
files = preload.map { |model| "#{path}/#{model}.rb" }
else
files = Dir.glob("#{path}/**/*.rb")
end

files.sort.each do |file|
load_model(file.gsub("#{path}/" , "").gsub(".rb", ""))
end
end
end