Railsのアプリケーションを作っているのですが、ユーザーの使用履歴とかを一覧表示したい、という仕様がありまして。
ユーザーがどのページを表示したとか、何を更新したとか等の情報を逐一保存しておきたいと。
DBはMySQLを使っているのですが、行動履歴みたいな情報をRDBにいちいち格納するのはパフォーマンス的にもちょっと心配だし、
削除もしない、どんどん溜めてくデータなので、KVSに入れるのはどうかな、と思いつきました。
で、やってみました
環境
- MacOSX Lion
- Ruby 2.0
- Rails 4.0
MongoDBのインストール
手元のMacOSにMongoDBをインストールします。
外部のサーバーにインストールする場合は、こちらの記事をどうぞ。
CentOSにMongoDBをインストールして外部から接続できるようにする
MacOSにはHomebrewでインストールします。
brew install mongodb
# インストール完了後のメッセージに従って、以下のコマンドを実行
ln -sfv /usr/local/opt/mongodb/*.plist ~/Library/LaunchAgents
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mongodb.plist
mongo --version
=> MongoDB shell version: 2.6.4
RailsアプリからMongoDBに更新する
RailsにMongoDB用gemを追加
bson_extを入れないと、後の"config.mongoid.logger"でundefined methodエラーが出ました。
gem "mongoid"
gem "bson_ext"
インストール
bundle install --path .bundle
設定ファイルを作成。中身はそのままでOKです。
rails g mongoid:config
=>create config/mongoid.yml
設定保存用モデルを定義
デフォルトのDBがRDBになっている場合、MongoDBへ接続するmodelをgenerateするには下記のようにします。
rails g mongoid:model action_log
modelを編集して必要なfieldを定義します。
class ActionLog
include Mongoid::Document
field :datetime, type: DateTime
field :user_id, type: String
field :user_name, type: String
field :controller, type: String
field :method, type: String
field :body, type: String
field :delete, type: Boolean
end
mongoDBだとdb:migrateする必要が無いのが嬉しいですね。
早速更新してみましょー。
まずはrails consoleからテスト。
irb(main):002:0> ActionLog.create!(datetime:Time.now, user_id:0, user_name:"jacoyutorius", body:"test")
=> #<ActionLog _id: 543df2987975746196000000, datetime: 2014-10-15 04:05:43 UTC, user_id: "0", user_name: "jacoyutorius", controller: nil, method: nil, body: "test", delete: nil>
無事更新できました。
ユーザーの各アクションを残したい
さて。
元々の仕様を思い出すと、「ユーザーの行動履歴を採りたい!」というのが始まりでした。
今回はcontrollerの各アクションが呼ばれる際のパラメータやログインユーザーをMongoDBに保存することで対応しようと思います。
各コントローラにずらずらと同じコードを書くのはDRYでないので、concernsを使います。
module LogMonger
extend ActiveSupport::Concern
included do
before_action :save_log
def save_log
ActionLog.create!(
datetime: Time.now,
user_id: 1,
user_name: "jacoyutorius",
method: params[:method],
controller: params[:controller],
body: params,
delete: false
)
end
end
end
controllerに飛ばされてくるparamsもまるまるbodyに格納してます。
あとは各コントローラでincludeします。
class UsersController < ApplicationController
include LogMonger
# -(省略)-
end
あとはRailsアプリを起動して適当にいじってみましょう。
MongoDBにはこんな感じで保存されます。
irb(main):005:0> ActionLog.first
=> #<ActionLog _id: 543deed77975745d73000000, datetime: 2014-10-15 03:49:43 UTC, user_id: "1", user_name: "jacoyutorius", controller: "admin/top", method: nil, body: "{\"controller\"=>\"top\", \"action\"=>\"index\"}", delete: false>
できました!
(2014.10.17 追記)
ActiveRecordとMongoidを併用している場合、ActiveRecordでmigrationコマンドを実行した場合に、"error mongoid [not found]"というエラーが出るようになりました。
この場合は、
rails g active_record:migration AddHoge
ActiveRecordを使うよう明示的に指定してやるとmigrationが作成できます。
こちらの記事を参考に解決しました。
mongoidとactiverecordが併存する場合のmigration
(さらに追記)
上記の対応だと、scaffoldの時に同じくmongoidが使われてしまう。
最終的にはapplication.rbでgeneratorの設定を定義しておくのが良いみたい。
class Application < Rails::Application
config.generators do |g|
g.orm :active_record
end
end
参考
バッドノウハウとありますが、特定の用途(今回でいえばログ取得)に絞った使い方であれば問題はないかなーと思っていますねー。