cookieを保持、参照するplugin
クッキーの値をDBに格納しておき(Visitorというモデル名にする)、
次回同じユーザがアクセスしたときに格納しておいた情報を参照できるようにするというもののPluginを作ってみました。
マイグレーション
まず,cookieを保存しておくテーブルを生成。PluginのRakeで生成できるよう、railsのconfigの接続設定を使ったテーブル生成を行う。
require "active_record" class MigrateVisitors def self.create self.establish_connection MigrateVisitors::CreateVisitors.up end def self.drop self.establish_connection MigrateVisitors::CreateVisitors.down end private def self.establish_connection conf = YAML.load_file(File.dirname(__FILE__) + "/../../../../config/database.yml") env = if ENV['RAILS_ENV'] then ENV['RAILS_ENV'] else 'development' end ActiveRecord::Base.establish_connection(conf[env]) end class CreateVisitors < ActiveRecord::Migration def self.up create_table :visitors do |t| t.column :value, :string, :limit => 32, :null => false t.column :hashed, :string, :limit => 32, :null => false t.column :expired, :date, :null => false t.timestamps end end def self.down drop_table :visitors end end end
カラムとして以下のものを定義しています。
cookieの値
cookieの有効期限
マイグレーションタスクを定義したRakeは以下のとおり。
require 'rake' task :up do require File.dirname(__FILE__) + '/lib/migrate_visitor' require File.dirname(__FILE__) + '/lib/visitor' MigrateVisitors.create end task :down do require File.dirname(__FILE__) + '/lib/migrate_visitor' require File.dirname(__FILE__) + '/lib/visitor' MigrateVisitors.drop end
テーブル生成は、たとえば以下のようにして行う(development環境での例)
rake up ENV=development
Model
lib内にcookie情報のテーブルモデルvisitorを定義。
以下のようにcookie情報を格納するのvisitorモデルを定義。cookieの取得、生成を行うfind_from_cookieメソッドを定義しました。
require "active_record" class Visitor < ActiveRecord::Base # サイト訪問者のcookieを記録するモデル # +value+(クッキーの値), +hashed+(照合用のハッシュ値のクッキー値), +expire+を含むモデル attr_accessor :must_send public # cookie情報をもとに行を検索 # 未作成または、期限まで1ヶ月以内であれば新規生成 # 有効期限が近ければ更新 def self.find_from_cookies(cookies) visitor = if cookies[:visitor] then value = cookies[:visitor] Visitor.find_by_value(value) else nil end new_visitor = if !visitor or visitor.hashed != cookies[:visitor_md] then # 新規(未作成 or ハッシュとの不一致) Visitor.new elsif visitor.expired < Date.today + 30 then # 更新(30日以内に有効期限切れ) visitor else # 変更不要 nil end if new_visitor value = create_cookie_value new_visitor.value = value new_visitor.hashed = Digest::MD5.hexdigest(value+Time::now.strftime("%Y%m%d%H$M$s")) new_visitor.expired = 3.month.from_now new_visitor.must_send = true new_visitor.save new_visitor else visitor.must_send = false visitor end end private # クッキーの値を生成 def self.create_cookie_value() ActiveSupport::SecureRandom.hex(16) end end
includeされるモジュール
controllerにincludeされるためのモジュールを以下のように定義
require "visitor" module ActVisitor attr_reader :visitor def self.included(controller) controller.before_filter :set_visitor_cookie end # Visitorのcookie生成 # 未作成または、期限まで1ヶ月以内であれば新規cookieを生成 def set_visitor_cookie() visitor = Visitor.find_from_cookies(cookies) if visitor.must_send : cookies[:visitor] = {:value => visitor.value, :expires => visitor.expired } cookies[:visitor_md] = {:value => visitor.hashed, :expires => visitor.expired } end @visitor = visitor end end
モジュールがincludeされた時に呼び出されるincludedメソッドをオーバーライド。include元のbefore_filter(前処理)として、上記のcookie送信メソッドが自動的に呼ばれるようにする。
controllerからの使用
各コントローラのベースとなるコントローラーで上記のように作成したActVisitorモジュールをincludeします。
これで,これを継承したコントローラーからvisitorメソッドによりモデルのインスタンスを参照できるようになります。
class Customer::BaseController < ApplicationController include ActVisitor end
使用例
今回は、cartの情報を保持するために利用しました。
以下は、テーブル定義。visitorモデルへのidを含んでいます。
create_table "carts", :force => true do |t| t.integer "customer_id" t.datetime "created_at" t.datetime "updated_at" t.integer "visitor_id" end
以下はCartのモデル。visitorモデルから、cartを取得するメソッドを定義しています。 class Cart < ActiveRecord::Base has_many :cart_items # # カートを返す # 指定したvisitorモデルのidを保持するカートを返す # なければ、nilを返す # def self.find_from_visitor(visitor) if visitor and visitor.id then self.find_by_visitor_id(visitor.id) end end ... ... end
コントローラー側の一覧表示アクションは、簡単な例では、たとえば以下のようになります。
class Customer::CartController < Customer::BaseController # カート一覧の表示 def list @cart = Cart.find_or_create_from_visitor(visitor) end end