如何给状态机加钩子stateful_enum的before/after事件回调与参数传递完全教程【免费下载链接】stateful_enumA very simple state machine plugin built on top of ActiveRecord::Enum项目地址: https://gitcode.com/gh_mirrors/st/stateful_enum状态机在状态切换的一瞬间往往还伴随着发送通知、记录时间、填写操作人等一连串业务动作。stateful_enum 是基于 ActiveRecord::Enum 构建的轻量级状态机插件它原生支持before与after事件回调让你在状态机事件的关键时刻挂上钩子并把业务参数原样传入回调。本文用最短的路径讲清楚钩子怎么写、参数怎么传、执行顺序是什么、有哪些坑要避。状态机为什么需要事件钩子状态本身的变更只是一次整数更新但业务真正关心的是变更前后要发生什么变更前记录解决时间、校验前置条件、填写操作人变更后发送通知、写审计日志、触发下游流程如果把这些逻辑散落在各个 Controller 里很快就会失控。用before/after事件回调可以把副作用集中收敛到状态机定义处——改状态的地方就是改业务逻辑的地方。三步给状态机加上 before/after 钩子第 1 步在 Gemfile 中安装插件。gem stateful_enum第 2 步用enum定义状态并在块里声明事件。第 3 步在event块内写before/after回调写法与 README.md 的示例一致class Bug ApplicationRecord enum :status, {unassigned: 0, assigned: 1, resolved: 2, closed: 3} do event :resolve do before do self.resolved_at Time.zone.now end transition [:unassigned, :assigned] :resolved end event :close do after do Notifier.notify Bug##{id} has been closed. end transition all - [:closed] :closed end end end两个要点回调通过instance_exec执行运行在模型实例的上下文中所以self.resolved_at、id等模型方法都能直接使用一个事件里可以写多个before或after按代码中出现的先后顺序依次执行钩子参数传递事件方法的参数会原样转发这是 stateful_enum 钩子最有用的能力事件方法接收的所有参数位置参数和关键字参数会原封不动地转发给每一个回调块。event :close do before do |closed_by:, reason: nil| self.closed_by closed_by self.close_reason reason end after do |closed_by:, **| Notifier.notify Bug##{id} was closed by #{closed_by.name} end transition all - [:closed] :closed end触发时直接传参bug.close(closed_by: current_user, reason: Duplicate)参数传递的完整规则写法说明\|closed_by:\|必填关键字参数\|reason: nil\|带默认值的关键字参数\|**, \|吸收并忽略其余所有参数位置参数同样被转发如\|arg1, arg2\|带!的事件方法如bug.close!与不带!的版本一样转发参数区别只在于状态不合法时一个抛异常、一个返回false。测试用例里reopen(reason: not fixed)的完整验证见 test/dummy/app/models/bug.rb。执行顺序before → 状态变更 → after事件方法被调用时实际发生的顺序是可对照 lib/stateful_enum/machine.rb 中Event类的实现步骤动作①校验当前状态是否存在合法迁移含:if/:unless条件②依次执行所有before回调参数完整传入③执行真正的状态变更内部调用 ActiveRecord::Enum 生成的方法④依次执行所有after回调参数完整传入两个值得注意的细节⚠️迁移不合法时钩子不会执行事件方法直接返回false!版本抛出Invalid transition。所以 before 回调可以放心当作前置校验 数据准备使用 想在触发事件前先确认是否合法可以用自动生成的can_谓词如bug.can_close?3 个常见坑位不会自动保存数据库事件方法只修改内存中的对象属性不会调用save。需要持久化时请自行保存常见做法是在after回调里save或在使用侧显式bug.savebefore 里做重活要谨慎此时状态还没变更如果 before 中抛异常状态将保持不变——这既是风险也是免费的回滚机会同一状态不要重复定义迁移同一个事件里一个来源状态只能有一条迁移重复定义会直接报错测试用例见 test/mechanic_machine_test.rb调试技巧快速查看当前可用事件排查钩子问题时可以先看看当前状态下哪些事件可触发Bug.new(status: :assigned).stateful_enum.possible_event_names # [:resolve, :close]该功能由 lib/stateful_enum/state_inspection.rb 提供还可以用possible_states查看迁移后的状态列表。总结钩子执行时机典型用途before状态变更之前记录时间、填写操作人、数据准备after状态变更之后发送通知、写日志、持久化事件方法的位置参数与关键字参数会全部转发给每个回调签名灵活迁移不合法时回调不执行配合can_x?谓词可做前置检查钩子运行在模型实例上下文中直接访问模型属性与业务方法掌握这套 before/after 事件回调 参数传递机制你的状态机就能在最正确的时机做最正确的事。【免费下载链接】stateful_enumA very simple state machine plugin built on top of ActiveRecord::Enum项目地址: https://gitcode.com/gh_mirrors/st/stateful_enum创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考