Spree(Solidus)にみるCancancanの設定方法
権限管理に興味があり、オープンソースのRailsアプリケーションを読んでいます。
Spreeのコードを読んでいる時にCancancanの使いかたでへぇと思ったことがあったので、メモに残します。
概要
Cancancanはinclude CanCan::Ability
したクラスのインスタンスで、can
メソッドを用いて権限を設定します。
class Ability
include CanCan::Ability
def initialize(user)
case
when user.admin?
can :index, User
can :show, User
can :create, User
can :update, User
can :destroy, User
when user.woker?
can :index, User
can :show, User
can :update, User do |target_user|
target_user == user
end
else
can :index, User
end
end
end
権限を管理したいわけですから、必ず分岐が起こります。
しかしこのようにinitialize
で分岐してそのままメソッドを呼ぶなどをすると、将来的にだんだん気まずくなってくる予感がビンビンありますね。
そこでAdminAbility
やWorkerAbility
を個別に用意したうえで、HogeController#current_ability
上で分岐したり、なんとかするのがひとつの方法だと思いますが、
Spreeの場合
Spreeはinclude CanCan::Ability
したクラスはSpree::Ability
ひとつしかありませんし、それを継承したクラスがあるわけでもありません。
solidus/ability.rb at master · solidusio/solidus
そのかわりに、Spree::Ability
インスタンスのcan
メソッドを呼びだすためのSpree::PermissionSets::Base
サブクラスが何種類も(めちゃ多い)用意されています。
solidus/core/lib/spree/permission_sets at master · solidusio/solidus
Spree::Ability#initialize
で必要なSpree::PermissionSets::Base
サブクラスを組みあわせ、それらがSpree::Ability#can
を呼ぶことにより権限を設定しています。
Spree::PermissionSets::Base
solidus/base.rb at master · solidusio/solidus
module Spree
module PermissionSets
class Base
def initialize ability
@ability = ability
end
# Spree::Abilityインスタンスから呼ばれることになるメソッド
def activate!
raise NotImplementedError.new
end
private
attr_reader :ability
delegate :can, :cannot, :user, to: :ability
end
end
end
Spree::PermissionSets
はdelegate :can, :cannot, :user, to: :ability
を行うことによって、シンタックス上ではCancan::Ability
と同じ感覚で設定できるようになっています。
solidus/user_display.rb at master · solidusio/solidus
module Spree
module PermissionSets
class UserDisplay < PermissionSets::Base
# Spree::Abilityインスタンスから呼ばれることになるメソッド
def activate!
can [:display, :admin, :edit, :addresses, :orders, :items], Spree.user_class
can [:display, :admin], Spree::StoreCredit
can :display, Spree::Role
end
end
end
end
この方式ならば、かつてCancanが滅亡したようにCancancanが滅亡しても、切りかえが楽でいいかもしれませんね。