我將Pundit gem 用于我的授權類,其中每個控制器操作都根據模型策略檢查,以查看用戶是否允許操作。
這些方法有時會變得非常臃腫且難以閱讀,因為我正在為某些物件檢查相當多的東西。
現在我正在考慮重構這些方法,并將每個“驗證”放在它自己的方法中:
以前的:
class PostPolicy < ApplicationPolicy
def update
return true if @user.has_role? :admin
return true if @object.owner == user
return true if 'some other reason'
false
end
end
現在理想情況下,我想將其重構為:
class PostPolicy < ApplicationPolicy
def update
allow_if_user_is_admin
allow_if_user_owns_record
allow_for_some_other_reason
false
end
private
def allow_if_user_is_admin
# this would go in the parent class, as the logic is the same for other objects
return true if @user.has_role? :admin
end
end
現在的問題是,即使用戶是管理員,maneupdate方法也會繼續運行,因為沒有回傳。如果我會包含一個回報,那么其他方法將永遠不會被評估。ruby 有沒有辦法做一種“超級回報”,這樣當用戶是管理員時,主要update方法會停止評估?
謝謝!
uj5u.com熱心網友回復:
鑒于您的示例和此評論:“......沒有原生方式在 Ruby 中實作“超級回報”?感覺就像是一種“加薪”,但結果是積極的......我可以使用它嗎? " .
雖然通常有其他方法可以解決可能被認為“更慣用”的問題,但 ruby?? 確實有一個Kernel#throw和Kernel#catch實作,當導航通過眾多且可能不同的方法和操作時,它對于控制流非常有用。
throw和對應的catch將短路塊的結果,這似乎是您正在尋找的語法。
非常基本的例子:
class PostPolicy
def initialize(n)
@n = n
end
def update
catch(:fail) do
stop_bad_actor!
catch(:success) do
allow_if_user_is_admin
allow_if_user_owns_record
stop_bad_actor!(2)
allow_for_some_other_reason
false
end
end
end
private
def allow_if_user_is_admin
puts "Is User Admin?"
throw(:success, true) if @n == 1
end
def allow_if_user_owns_record
puts "Is User Owner?"
throw(:success,true) if @n == 2
end
def allow_for_some_other_reason
puts "Is User Special?"
throw(:success,true) if @n == 3
end
def stop_bad_actor!(m=1)
puts "Is a Bad Actor?"
throw(:fail, false) if @n == 6 || @n ** m == 64
end
end
示例輸出:
PostPolicy.new(1).update
# Is a Bad Actor?
# Is User Admin?
#=> true
PostPolicy.new(2).update
# Is a Bad Actor?
# Is User Admin?
# Is User Owner?
#=> true
PostPolicy.new(3).update
# Is a Bad Actor?
# Is User Admin?
# Is User Owner?
# Is a Bad Actor?
# Is User Special?
#=> true
PostPolicy.new(4).update
# Is a Bad Actor?
# Is User Admin?
# Is User Owner?
# Is a Bad Actor?
# Is User Special?
#=> false
PostPolicy.new(6).update
# Is a Bad Actor?
#=> false
PostPolicy.new(8).update
# Is a Bad Actor?
# Is User Admin?
# Is User Owner?
# Is a Bad Actor?
#=> false
uj5u.com熱心網友回復:
你可以做的是連鎖&&經營者。
只要一個是false,ruby 就不會評估其他的(并且更新方法將回傳false)。
class PostPolicy < ApplicationPolicy
def update
allow_if_user_is_admin &&
allow_if_user_owns_record &&
allow_for_some_other_reason &&
end
private
def allow_if_user_is_admin
# this would go in the parent class, as the logic is the same for other objects
return true if @user.has_role? :admin
end
end
uj5u.com熱心網友回復:
似乎這將實作您的目標并且更加慣用:
class PostPolicy < ApplicationPolicy
def update
user_has_admin_role? ||
user_owns_object? ||
some_other_reason?
end
private
def user_has_admin_role?
@user.has_role? :admin
end
def user_owns_object?
@object.owner == user
end
def some_other_reason?
'some other reason'
end
end
uj5u.com熱心網友回復:
您可以短路布爾語法,在某些情況下,長鏈接看起來很糟糕,這是一個替代方案,但使用Enumerable#all? 的想法基本相同? 請參閱此答案以了解它是如何短路的
class PostPolicy < ApplicationPolicy
def update
deny?
end
private
def deny?
[
user_is_admin?,
user_owns_record,
allow_for_some_other_reason?,
thing1?,
thing2?
].any?
end
def user_is_admin?
@user.has_role? :admin
end
def user_owns_record?
@user.owns_record?
end
def allow_for_some_other_reason?
@user.has_cheezebuerger?
end
def thing1?
@user.thing1
end
def thing2?
@user.thing2
end
end
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/424102.html
