Rails権限管理ライブラリ cancancan gem バージョンアップ(2.x => 3.x)の際の対応と注意点

Ruby on rails の権限管理ライブラリの中で一番ダウンロード数が多いGemであるcancancanのv3.0が2019年4月にリリースされました。

仕事のプロジェクトで使っているcancancanのバージョンは随分前にv3.0に上げていたのですが、結構大きな変更が入っていることを認識できていなかったことに最近気が付き、これからアップグレードをする方や新規にCancancanを導入する方に向けて、どんなところに注意したら良いのかをまとめてみることにしました。

注意点を認識していないと、気がつかない間に

  • N+1(パフォーマンス問題)が発生する
  • SQLクエリ実行時にエラーが発生する

といった問題が発生する可能性があります。

Cancancan公式のドキュメントのURLも載せておきます。
Migrating from CanCanCan 2.x to 3.0 · CanCanCommunity/cancancan Wiki · GitHub

4つの注意点(Breaking changes)

注意点(Breaking changes)は4点ありますが、特に2番目、3番目を意識すると問題の発生を避けられそうです。

①権限を定義するアビリティにSubjectを定義しなくてはいけなくなった

例えば、ダッシュボードに対する権限を指定する場合

can :dashboard

のように書くことができなくなりました。具体的に

can :read, :dashboard

のように、ダッシュボードのリード権限があるという書き方をする必要があります。

  • 以前よりDefining AbilitiesのWikiページ で、後者の書き方が載っていたので、あまり前者の書き方をしているプロジェクトはない
  • 前者の書き方をしているとエラーが発生するようになった

ため、この注意点に関してはあまり意識しておかなくても問題ないと思います。

しかし

  • 権限のテストをあまり書いていないプロジェクト
  • アップグレード後に手動でも動作確認をしていない

場合だと、気がつかずにプロダクションに反映する恐れがあるので注意が必要になりそうです。

②Eager loadingを自動的に行わなくなった。

具体的には、このPRの差分になるのですが、今まではモデルに :has_many などで紐付いているモデルは includes して読み込んでいたのですが、left_joinsに変わったのでN+1が発生する可能性がでてきました。

具体的には、Book(本)モデルの :has_many にAuthor(著者)モデルがあるような場合で、書籍一覧(Booksのindexページ)で、著者名(author.name)を表示するようなケースが該当します。

なので

といった対応を行った方が良さそうです。

③Distinctを使うようになった

②と同じ差分で、クエリでdistinctを行うようになったことがわかります。

例えば、load_resourceを読み込んだ結果(ActiveRecord::Relation)に対してordergroup byを付けて以下のようなクエリになっていた場合は、PostgreSQLの場合クエリ実行時にエラーになります。

SELECT
  DISTINCT users.id
FROM 
  users
order by 
  users.email

=>  ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list

このような場合、selectの値を見直し、users.emailを含めるようにするといった対応が必要になります。

④アビリティファイルをマージした際、エイリアスもマージされるようになった

権限が複雑になってくるとアビリティファイルを分けたり、それらのアビリティをマージしたくなってきます。

cancancanのアビリティには、mergeというメソッドが定義されていて以前からマージを行うことができるようになっていましたが、エイリアスはマージされないといった問題があったようです。

cancancan 3.0でエイリアスもマージできるようになったようですが、Railsプロジェクトはそこまで複雑になる案件には向かないと思うので

  • アビリティのマージをしている
  • エイリアスも使っている

というのはあまり多くはなさそう。ということでこの4点目は比較的重要度は低そうです。