こんにちは。ikedaです。
Rails 4系で論理削除を実装する際に使われているgem paranoiaのソースを見る機会がありましたので、紹介とソースで気になった点を書いていきたいと思います。
論理削除
論理削除をざっくり説明すると、データベースのレコードからは削除せずに、削除済みであると判断できるように値を更新しておくことです。
実装のロジックは簡単でレコードが削除されているかいないかを表すカラムを追加しておき、削除する際にそのカラムを更新するというのが一般的です。
gem paranoia
その論理削除を実装することがparanoiaでできます。
- gem: paranoia
ちなみに, Rails3アプリケーション開発で良く使うgemまとめのスライドの中にも紹介されています。スライドで紹介されているのはacts_as_paranoidですね。
paranoiaのgithubのREADMEにも記載されていますが、paranoiaは、acts_as_paranoidよりコードを減らしているといっています。
Paranoia is a re-implementation of acts_as_paranoid for Rails 3, using much, much, much less code.
コードをみていきます
ソースコードを抜粋していくので前後のソースコードが省かれていることがあります。
ちなみに私が確認したときにはbranch: rails4 2014/04/11が最新のコミットになっていました
# paranoia
class ActiveRecord::Base
def self.acts_as_paranoid(options={})
alias :destroy! :destroy
alias :delete! :delete
# (省略)
include Paranoia
end
destroy
, delete
メソッドでdestroy!
, delete!
を再定義しています。
gemを使わずに開発者が論理削除を実現しようとすると,カラムを追加してupdate_attributes
などのメソッドを利用しなければなりませんが、paranoiaでは、destroy
, delete
で論理削除ができるようになります。
私感ですが、論理削除であってもdestroy
, delete
が扱えるので直感的に迷わないと思います。
self.paranoia_column = options[:column] || :deleted_at
デフォルトではdeleted_at
のカラムを利用するようですが、options[:column]
を設定することによりカラムを指定することもできます。
- 例: paranoiaで使われるカラムをdefaultの
deleted_at
ではなくて、suspended_at
というカラムを利用するようにします。
class Student < ActiveRecord::Base
acts_as_paranoid column: :suspended_at
end
これで、paranoiaが利用するカラムがdeleted_at
からsuspended_at
に切り替わります。
# paranoia
# touch paranoia column.
# insert time to paranoia column.
# @param with_transaction [Boolean] exec with ActiveRecord Transactions.
def touch_paranoia_column(with_transaction=false)
# This method is (potentially) called from really_destroy
# The object the method is being called on may be frozen
# Let's not touch it if it's frozen.
unless self.frozen?
if with_transaction
with_transaction_returning_status { touch(paranoia_column) }
else
touch(paranoia_column)
end
end
end
コメントにもしっかり書かれていますが、suspended_at
にはtimestampが入力されます。
# paranoia
default_scope { where(paranoia_column => nil) }
ActiveRecordクラスからacts_as_paranoidが呼ばれると、default_scope { where(:paranoia_column => nil) }
が呼ばれるので、削除されていないレコードが呼ばれます。
論理削除したレコードもみたいという場合はunscope
を利用することでとれますが、READMEを見るとwith_deleted
やonly_deleted
メソッドなどが準備されていますからこちらを利用しましょう。
paranoiaは200行程度の小さなgemですので、読むのは比較的に楽だと思います。
gemのコードを読んでおくことでより理解が深まりますので、頻繁に利用するgemなどは読んでみると良いかもしれません。
古めの記事になりますが、こちらの記事に紹介されているgemを読んでみるのもいいと思います。