Rails4.2.6 + Devise + kakurenbo-putiで論理削除を実装

Categories
Tags
Share

私が論理削除で使うライブラリは専らkakurenbo-putiです。 ActiveRecordと疎結合なので、ハマることが少ないです。

今回はDeviseと合わせてみました。

kakurenbo-putiを使えるようにする

Gemfileに以下を追加します。

gem 'kakurenbo-puti'

bundle installしてから、カラムを追加しましょう。

rails g migration AddSoftDestroyedAtToUser soft_destroyed_at:datetime:index

bin/rake db:migrateを実行します。

あとは、Modelにsoft_deletableを入れるだけ。

class User < ActiveRecord::Base
  soft_deletable
end

こうすると、論理削除用のメソッドが生えてきます。

emailのユニーク制約を外す

せっかく論理削除を実装しても、メールアドレスの重複を外せないといけません。 重複を外しましょう!

bin/rails g migration RemoveEmailIndexFromUser

生成されたマイグレーションファイルを修正します。

class RemoveEmailIndexFromUser < ActiveRecord::Migration
  def up
    remove_index :users, :email
    add_index :users, [:email, :soft_destroyed_at], unique: true
  end
  
  def down
    remove_index :users, [:email, :soft_destroyed_at]
    add_index :users, :email, unique: true
  end
end

emailのユニーク制約をやめて、emailとsoft_destroyed_atでユニーク制約つけています。

Userモデルのバリデーションをやり直す

deviseを使うと、なんと問答無用でemailのユニーク制約が付いてしまいます。

元のソースはここ

こうなると、バリデーションをキャンセルしないといけません…。 どうすればいいのかわかってなかったのですが、ググったらいい感じのコードに出会えました。 emailに関わるバリデーションを削除する方法です。

https://gist.github.com/brenes/4503386

まず、emailに関わるバリデーションを削除した後、再度定義しなおせばよさそうです。

class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :confirmable, :timeoutable
  soft_deletable
  
  # Deviseを使うと、問答無用でemailがユニーク扱いになる。
  # それだと論理削除した際に再登録できないので、一旦emailに関する検証を削除する
  # https://gist.github.com/brenes/4503386
  _validators.delete(:email)
  _validate_callbacks.each do |callback|
    if callback.raw_filter.respond_to? :attributes
      callback.raw_filter.attributes.delete :email
    end
  end
  
  # emailのバリデーションを定義し直す
  validates :email, presence: true
  validates_format_of :email, with: Devise.email_regexp, if: :email_changed?
  validates_uniqueness_of :email, scope: :deleted_at, if: :email_changed?
end

これで、削除済みのユーザーが再度同じメールアドレスで登録することができるようになりました。 ここには書きませんが、ちゃんとrspecでテスト書いていますのでちゃんと検証済みです。

論理削除済みのユーザーをログインできなくする

まだ論理削除済みであっても、deviseのログインフォームからログインできてしまいます。 ログインできないようにしましょう。

Userモデルに、以下を追加します。

class User < ActiveRecord::Base
  # (略)

  def self.find_for_authentication(warden_conditions)
    without_soft_destroyed.where(email: warden_conditions[:email]).first
  end
end

このfind_for_authenticationメソッドは、ログイン条件をカスタマイズしたい場合にoverrideしてくれと書いてあったので、 without_soft_destroyedスコープを追加して、論理削除済みユーザーを除外しています。 これを追加した後、論理削除ユーザーでログインしようとしたらできないことを確認しました。

以上で、終わりです。

まとめ

deviseとkakurenbo-putiの組み合わせはいい。


comments powered by Disqus