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が滅亡しても、切りかえが楽でいいかもしれませんね。