背景
Herokuで適当にスキーマレスなデータをOutput出来る場所がほしい!でも、Herokuはディスクが自由に使えないので、text書き出しができない!!
こんな要求の解決法としては、textタイプのフィールドを作ってserialize
やstore
を使うのが一つの手。ただ、割と書き込みが多い事が想定され、無駄に大切な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
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.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のあたりの話がちゃんと書いてある。
参考
- http://stackoverflow.com/questions/6372626/using-active-record-generators-after-mongoid-installation
- http://mongoid.org/en/mongoid/
- https://devcenter.heroku.com/articles/mongohq
追記: ロードするclassの設定
mongoidのgemを見ると設定なしではapps/model
以下の全てのRubyファイルをロードしてしまう設定になっていた。
これを防ぐためには、options
のpreload_models
にMongoid::DocumentをIncludeしているクラスを全て指定すれば良い。
例えば、data_mongo.rb
とuser_monbo.rb
を作った場合は、
production:
# ...
options:
preload_models:
- data_mongo
- user_mongo
というように設定する。
一応、今後解決する可能性もあるので、mongoidの問題となっている部分のを載せておく
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