読者です 読者をやめる 読者になる 読者になる

波打際のブログさん

主に、プログラミング備忘録など。

論理削除gemを1年ほど保守してみて。重大な欠点にやっと気づいたポエム。

はじめに

 kakurenboというgemはご存知でしょうか?paranoiaの欠点を克服すべく1年ほど前に私が開発を始めたgemです。(参考:Rails4と3で論理削除を行うためのGem Kakurenbo の紹介と今更論理削除Gemを実装した理由。 - 波打際のブログさん)

issueやpullrequestを送信してくださる善意のコミッターの方々に支えられながら1年ほど保守をしてきました。その上で薄々は気がついていたのですが、どうしても認められなかった重大な欠点をハッキリと認識させられたのでポエムにしました。

論理削除gemの起源

 kakurenboもparanoiaも、廃れてしまった acts_as_paranoid を再実装したものです。

これらのgemは導入するだけで、いつも使っているdestroyメソッドが論理削除メソッドに早変わりする素晴らしいgem...になるはずだったのです。

destroyメソッドを置き換えるということ

 ActiveRecordは様々な事情で成り立っているので、destroyメソッドをオーバーライドすれば論理削除gemが完成するわけではありません。コールバックにアソシエーション、destroy_allにdelete_allなどのメソッドから、find時のスコープまで様々な箇所に関係があります。

ActiveRecordCRUDを提供しているので、CRUDのDを変更する論理削除gemはActiveRecordの深くにまで関わらなければいけなくなるのは当然です。

深く関わっているのでkakurenboのバージョンアップ対応中にActiveRecordのバグを発見するなんてこともありました。(default scopes should break the cache on singulur_association. by alfa-jpn · Pull Request #17559 · rails/rails · GitHub)

paranoia

 paranoiaは保守性を優先して、ActiveRecordと深く関わらない代わりに不完全な対応のままgemをリリースしています。今となっては一理あるなと思えるものの、delete_allメソッドを呼び出すと、全て物理削除されてしまう挙動には、あまりにも整合性が保てていないと思います。(というか事故る可能性すらあります。)

kakurenbo

 ということでActiveRecordの削除に関わる部分を完全に置き換えたkakurenboが生まれたわけですが、railsのパッチアップデートですら動かなくなることがありました。マイナーアップデートならほぼ確実。メジャーアップデートならacts_as_paranoidのようにプロダクトが廃れるのではないかとすら思います。

destroyという崇高なメソッドをオーバーライドするとrails様の祟りが下るのかもしれません。

なぜ、acts_as_paranoidがメンテされなくなってしまったのか、公開1年後にしてやっと理解できたような気がします。

acts_as_paranoidの呪縛から開放

 悩んだ末に私が出した答えがこれです。destroyのオーバーライドをやめ、素直にsoft_destroyというメソッドを設けることです。冷静に考えるとdestroyで論理削除できなくても不便なことなんてありません。

ただし、kakurenboでそんな大幅な改変を行うのは無理があります。設計思想の根底から違うのですから。そこで、新たなgemの開発に取り組みました。

kakurenbo-putiリリース

 もちろんkakurenboの保守は続けます。...が保守性を考慮して一貫性のある論理削除機能を追加可能な kakurenbo-puti をリリースしました。


alfa-jpn/kakurenbo-puti · GitHub

思い切って呪縛を立ち切れたgemです。コアファイルは70行以下です。

使っているメソッドrailsのレールを脱線していないメソッドばかりなので、何も修正せずにメジャーアップデート対応できる気すらします!

kakurenbo-putiにできること

  • 論理削除
  • 復元
  • 親が削除されている場合に子を削除扱いにすること

具体的な使い方はこちら

まとめ

 kakurenboの重大な欠点はActiveRecordのdestroyを完全に置き換えようとして、ActiveRecordの深い箇所まで改変し、保守が高コスト・高頻度になっていることです。

rails既存の機能を置き換えるgemならこの問題を多かれ少なかれ持っており、やがて耐えられなくなるとacts_as_paranoidのように廃れてしまいます。

そこで、今後railsで開発を行う際には、

  • rails既存の機能を置き換えるgemは地雷だと考える
  • rails既存の機能を置き換えるコードを絶対に書かない

という2点に留意すると、同じ轍を踏まずに素晴らしいrailsライフを送れると思います。