しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

Railsで論理削除をするacts_as_paranoidを試してみた

論理削除を実現してくれるgem、acts_as_paranoidを試してみました。

環境

Ruby2.1.5
Rails4.2.0

インストール

まずはGemfileに以下のように追加してbundle install。

gem 'acts_as_paranoid'

しかしWEBrickを起動しようとすると下記エラー。
どうやらrails4系に対応してない模様。

undefined method `validate_find_options' for class `Class' (NameError)

こちらリポジトリでメンテナンスされているようなのでGemfileを下のように書き換えて再度bundle install。
これで無事にWEBrickが起動してくれるようになりました。

gem 'acts_as_paranoid', github: 'ActsAsParanoid/acts_as_paranoid'

設定

acts_as_paranoidを使う設定は下の2ステップになります。
1. 論理削除をしたいテーブルにdeleted_atカラムを追加する。
2. 以下のように論理削除したいテーブルでacts_as_paranoidを呼び出す。

class Task < ActiveRecord::Base
  acts_as_paranoid
end

acts_as_paranoidの挙動

具体的なacts_as_paranoidの挙動は下記の通りです。

destroyメソッドで物理削除されなくなる

delete, destroy, delete_all, destroy_allを試してみましたが、全て物理削除からdeleted_atに現在時刻を入れる挙動に変わっていました。

2.1.5 :047 > Task.delete_all
  SQL (1.3ms)  UPDATE `tasks` SET deleted_at = '2015-01-04 13:51:26.703429' WHERE (`tasks`.`deleted_at` IS NULL)

find系で削除したものを取得しないようにする

find, all, first, last等、検索をかける際は全てdeleted_atがnullのものを取得する挙動に変わっていました。

2.1.5 :046 > Task.all
  Task Load (0.3ms)  SELECT `tasks`.* FROM `tasks` WHERE (`tasks`.`deleted_at` IS NULL)
 => #<ActiveRecord::Relation [#<Task id: 2, name: "aaaaaa", deleted_at: nil, created_at: "2015-01-04 13:20:49", updated_at: "2015-01-04 13:48:36">]>

コードを見たところdefault_scopeを使っているようです。

# Magic!
default_scope { where(paranoid_default_scope_sql) }
def paranoid_default_scope_sql
  if string_type_with_deleted_value?
    self.all.table[paranoid_column].eq(nil).
      or(self.all.table[paranoid_column].not_eq(paranoid_configuration[:deleted_value])).
      to_sql
  else
    self.all.table[paranoid_column].eq(nil).to_sql
  end
end

削除されたものを取得できるメソッドが追加

削除されたレコードを取得するonly_deletedや削除済も未削除も全て取るwith_deletedが追加されています。
あとは削除時間を元に取得するdeleted_after_timeやdeleted_before_timeもありました。
しかしデフォルトスコープが効いているため、with_deletedと併用しないと取ってこれないようです。

Task.deleted_before_time(Time.now) # これだと取ってこれない
Task.with_deleted.deleted_before_time(Time.now) # これなら取ってこれる

削除されたものを復活させるメソッドが追加

削除されたものを復活する為のrecoverメソッドが追加されています。
あとrecover時に呼ばれるコールバックのbefore_recoverとafter_recoverも追加されています。

その他

ドキュメントを読んだ所、削除情報はDatetime型でなくString型やBoolean型で持つ事もできるようです。

まとめ

取得や削除を置き換えてくれるので使う側で意識する点が少ない所が良さそうです。
関連テーブルの挙動とかも機会あれば追いかけてみようと思います。