こんにちは。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を読んでみるのもいいと思います。