我最近在我的rails应用程序中使用了两个很棒的gem,state_machine和cancan,但我很好奇将它们干净地集成在一起的最好方法。 目前,我已将状态转换置于按钮上,这些按钮将继续执行控制器授权的操作。这很有效,我可以限制谁可以执行该操作。
我想让用户也能够在编辑表单中更改对象状态。我注意到state_machine将获取散列中的state_event键,以及要执行的操作的值(因此它将遍历所有state_machines回调)。 这可以使用update_attributes中的params哈希传递。奇妙。
但是,只有某些用户应该能够将对象更改为某些状态。我该如何实现呢?这个想法是
params['state_event']=='move_to_x'
应该为某些用户纾困,但允许其他用户。它也让我担心,直到我实现这个是授权部分,一个聪明的用户可以在状态事件中发布任何内容,即使它们也不应该被允许!。
答案 0 :(得分:1)
你可以用两种方式做到这一点。通过在过渡上设置条件。像这样:
transition :from => :parked, :to => :idling, :if => :valid_user
在模型中创建valid_user方法。
def valid_user
if User.current_user.has_role?(xyz)
do baa
end
end
(User.current_user.has_role?(xyz))不是有效的测试 - 您需要自己的测试。
或者您可以使用自定义状态机验证:
state :first_gear, :second_gear do
validate :speed_is_legal
在文档中发现了一个警告:
http://rdoc.info/github/pluginaweek/state_machine/master/StateMachine/Integrations/ActiveModel
这里有另一篇有趣的帖子:
State Machine, Model Validations and RSpec
我们在应用程序中成功使用这两种方法。
- 为大众编辑 -
考虑关于在模型中使用current_user的评论。我们认为这是重新阅读我们的代码。在我们使用这个的一两个例子中,我们意识到我们可以完全删除current_user方法,从而消除任何安全风险。
我们没有调用User.current_user,而是交换到:
self.users.first
这显然假设模型has_many用户。然后,您可以调用此用户的能力
答案 1 :(得分:0)
这实际上并不像我想象的那么糟糕,而且我已经完成了一个让代码相当短的方式,并且将current_user排除在我的模型之外(这么大的优点)。
诀窍是在控制器中再次调用authorize。
基本上
def update
authorize! params[:object][:state_event].to_sym, @object unless params[:object][:state_event].empty?
.... etc
end
这样我就可以在Ability.rb中使用别名。因此,可以执行操作的用户将被授权,而无法获得异常的用户将被授权。这也很棒,因为它与我基于按钮的动作使用的能力相同。
唯一需要注意的是,你不能使用@ object.state_transistions来获取用户可以转换到的可用状态列表,但应该可以通过某种辅助方法来实现。
更新:虽然在像图层这样的视图中获取这些状态很容易
我使用简单的表单,所以我只是一个集合输入
..... collection: @object.status_transistions.select{|t| can? t.event, @object}
这使得select具有对象可以经历的所有转换,并且用户也被授权执行:)。