在 Ruby 中,您可以决定从主方法到 return 还是在调用子方法时继续?
In Ruby, can you decide from a main method to return or continue when calling a submethod?
我正在使用 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
现在的问题是,mane update
方法将继续运行,即使用户是管理员,因为没有 return。如果我包含 return,那么其他方法将永远不会被评估。在 ruby 中有没有一种方法可以做一种“超级return”,这样当用户是管理员时,主要的 update
方法将停止评估?
谢谢!
你可以做的是链式 &&
运算符。
只要一个是 false
,ruby 将不会评估其他的(更新方法将 return 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
看来这样可以达到你的目的,而且更加地道:
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
您可以 short-circuit 布尔语法,在某些情况下,长链接看起来像是糟糕的风格,这里有一个替代方案,但使用 Enumerable#all? See this answer 的想法基本相同 short-circuit s
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
鉴于您的示例和此评论:“...没有本地方法可以在 Ruby 中执行 'super return' 吗?感觉有点像“加薪”但后来有了积极的结果......我可以使用它吗?".
虽然通常有其他方法可以解决被认为“更惯用”的问题,但 ruby 确实有一个 Kernel#throw
and 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
我正在使用 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
现在的问题是,mane update
方法将继续运行,即使用户是管理员,因为没有 return。如果我包含 return,那么其他方法将永远不会被评估。在 ruby 中有没有一种方法可以做一种“超级return”,这样当用户是管理员时,主要的 update
方法将停止评估?
谢谢!
你可以做的是链式 &&
运算符。
只要一个是 false
,ruby 将不会评估其他的(更新方法将 return 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
看来这样可以达到你的目的,而且更加地道:
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
您可以 short-circuit 布尔语法,在某些情况下,长链接看起来像是糟糕的风格,这里有一个替代方案,但使用 Enumerable#all? See this answer 的想法基本相同 short-circuit s
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
鉴于您的示例和此评论:“...没有本地方法可以在 Ruby 中执行 'super return' 吗?感觉有点像“加薪”但后来有了积极的结果......我可以使用它吗?".
虽然通常有其他方法可以解决被认为“更惯用”的问题,但 ruby 确实有一个 Kernel#throw
and 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