with_scopeとalias_method_chainを使って論理削除の実装
注意
このエントリーは、railsで論理削除を実装しようとするときに、プラグインとか使わなかった、使えなかった、今更使うの?みたいな感じになったときに論理削除をサポートしていくための、最初のステップを書いています。まずは
- プラグインを使えないのか?、
- 論理削除を実装しなければならないかどうか?
検討してみてくださいね。
参考:論理削除をサポートしてくれるrailsのプラグイン(一例として)
- acts_as_paranoid http://ar-paranoid.rubyforge.org/
- http://d.hatena.ne.jp/fujisan3776/20080912/1221234453
- とても分かりやすかったです。id:fujisan3776++
論理削除を手動で実装
例えば、学校(schools)と学生(students)というclassを作る。んで、学生には論理削除をつけましょう。
DB
schools :name, :string ... students :family_name, :string :first_name, :string ... :status, :enum, :limit => [:active, :inactive], :default => :active # 今回はenumカラムを使って、inactiveというステータスを持っていたら、論理削除されている、ということにします。
関係は学校 has_many 学生かな
Model
class School < ActiveRecord::Base has_many :students # 中略 end class Student < ActiveRecord::Base belongs_to :schools # 中略 def destroy self.status = :inactive self.save end end
このときに便利なActiveRecordのおかげで、以下のような参照関係を得られます。
school.students student.school
例えば、東大に、山田君と佐藤さんがいたとすると、
school #=> 東大 school.students #=> [山田君, 佐藤さん]
みたいに得られるはず。
ここで山田君、東大を退学しました
yamadakun.destory #=> yamadakun.status == :inactive
また東大の学生を参照すると、
school.students #=> [山田君, 佐藤さん]
山田君まだいる( ゜Д゜)!?
ということで、手動で実装すると、論理削除はめんどくなります。
論理削除に合わせるためにfindなどのDB参照系のメソッドを上書き
しょうが無いので、findやらなんやら上書きしていきます。
基本的に論理削除がある StudentClassのメソッドを上書きします。
このときに使うのが、with_scopeとalias_method_chain
- 参考alias_method_chain:
- 参考with_scope:
- http://d.hatena.ne.jp/ichiro_tanaka/20090210/1234234223
- このエントリーは、ここから学ばせていただきました。
class << self def find_with_active(*args) with_scope(:find => {:conditions =>["status = ?", :active]}) do find_without_active(*args) end end alias_method_chain :find, :active def count_with_active(*args) with_scope(:count => {:conditions =>["status = ?", :active]}) do count_without_active(*args) end end alias_method_chain :count, :active end
必要なものは必要なだけ書かなきゃいけない。
ちなみにfind_by_sqlとかには対応できない。
(その時はちゃんとstatusも含めて書いてね、ということで)
ここまでしてあげれば、さっきの山田君もちゃんと論理削除されて見えます。
school.students #=> [佐藤さん]
テスト
必要なものを必要なだけテストしてください。
注意点はfixturesの読み込み。
当然のようにfixturesを読み込むときには、findメソッドが使われているわけで、
fixturesの中で、論理削除されたレコードを作っておくと、そのレコードが読み込めないためERRORを起こします。
テストの中で初期化するような部分とかで、ちゃんと論理削除してあげてください。