Ruby on Rails中seuros state machine能否跨模型传参?
关于state_machine gem传入外部模型实例的实现方案
state_machine gem支持在转换条件中使用与宿主模型无直接关联的外部实例(比如你的场景中的current_user),具体可以通过以下两种方式实现:
方法一:调用转换方法时直接传入参数
你可以在定义转换条件时使用接受参数的方法,触发状态转换时直接传入所需的外部实例。
模型层代码(Task模型)
class Task < ActiveRecord::Base state_machine :state, initial: :in_progress do # 定义转换,绑定接受参数的条件方法 transition :in_progress => :completed, :if => :user_is_owner? def user_is_owner?(user) user.id == self.requester_id end end end
控制器层调用
在控制器中触发状态转换时,将current_user作为参数传入:
class TasksController < ApplicationController def complete @task = Task.find(params[:id]) # 调用转换方法时传入current_user if @task.completed!(current_user) redirect_to @task, notice: '任务已完成' else redirect_to @task, alert: '无权限完成此任务' end end end
方法二:通过上下文(Context)传递参数
如果需要传递多个参数,或者希望参数以结构化方式传递,可以使用state_machine的上下文功能。
模型层代码(Task模型)
class Task < ActiveRecord::Base state_machine :state, initial: :in_progress do # 基于上下文做条件判断 transition :in_progress => :completed, :if => :user_is_owner_via_context? def user_is_owner_via_context?(context) context[:user].id == self.requester_id end end end
控制器层调用
触发转换时通过:context选项传入包含current_user的哈希:
class TasksController < ApplicationController def complete @task = Task.find(params[:id]) if @task.completed!(context: { user: current_user }) redirect_to @task, notice: '任务已完成' else redirect_to @task, alert: '无权限完成此任务' end end end
补充说明
你提到的「授权逻辑交给框架其他部分,状态机仅做状态转换接口」的思路是合理的。实际开发中,也可以先在控制器层面完成用户权限校验,再触发状态转换,让状态机专注于状态流转规则,职责更清晰:
def complete @task = Task.find(params[:id]) # 先在控制器做授权校验 unless current_user.id == @task.requester_id redirect_to @task, alert: '无权限完成此任务' return end # 再触发状态转换 if @task.completed! redirect_to @task, notice: '任务已完成' end end
内容的提问来源于stack exchange,提问作者Thomas
相关产品推荐
相关产品推荐

