本文介绍了在 state_machine gem 上持久化之前的验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

state_machine gem 中的转换之前执行验证的正确语法是什么?>

我尝试了以下方法,

before_transition :apple =>:橙色做验证:validate_core结尾def validate_core如果core.things.blank?errors.add(:core, '必须有一个东西')结尾结尾

但我收到以下错误,

未定义的方法`validate' for #

我也试过把它写成,

state :orange do验证:validate_core结尾

但这会导致记录保存后回滚,不太理想.我想首先阻止状态机转换为 :orange.

核心问题是,在我的控制器中,我的逻辑依赖于 object.save 的结果.我对状态机的验证直到初始保存后才开始,因此 save 返回为 true 并且控制器继续执行逻辑,如果对象无效,它不应命中.

除了检查保存之外,我还通过手动测试有效性来解决这个问题,但感觉应该有一种方法可以在对象保存之前触发验证.

解决方案

那个特定状态机的想法是在状态中嵌入验证声明.

state :orange do验证:validate_core结尾

上述配置将在对象转换为橙色时执行:validate_core 验证.

event :orangify do过渡全部 =>:橙子结尾

我理解您对回滚的担忧,但请记住,回滚是在事务中执行的,因此成本非常低.

record.orangify!

此外,请记住,您也可以使用不使用异常的非 bang 版本.

>c.整理(0.3ms) 开始(0.3ms) 回滚=>错误的

也就是说,如果您想使用基于 before 转换的不同方法,那么您只需要知道如果回调返回 false,则转换将停止.

before_transition 做错误的结尾>c.整理!(0.2ms) 开始(0.2ms) 回滚StateMachine::InvalidTransition:无法通过 :cancel from :purchased 转换状态(原因:转换停止)

请注意,事务总是开始的,但如果回调在最开始,则可能不会执行任何查询.

before_transaction 接受一些参数.您可以生成对象和事务实例.

before_transition do |对象,事务|object.validate_core结尾

实际上你可以通过事件来限制它

before_transition all =>:orange do |对象,事务|object.validate_core # =>错误的结尾

在这种情况下,validate_core 应该是一个返回真/假的简单方法.如果您想使用定义的验证链,那么我想到的是在模型本身上调用 valid? .

before_transition all =>:orange do |对象,事务|对象.有效?结尾

但是,请注意,您不能在事务范围之外运行事务.事实上,如果你检查 perform 的代码,你会看到回调在事务内部.

# 并行运行每个集合的转换.## 所有的转换都会经过以下步骤:# 1. 回调之前# 2. 持久化状态# 3. 调用动作# 4. 回调之后(如果已配置)# 5. 回滚(如果操作不成功)## 如果一个块被传递给这个方法,那个块将被调用# 调用每个转换的动作.def执行(&block)重启如果有效?如果use_event_attributes?&&!block_given?每个都做|过渡|transition.transient = truetransition.machine.write(object, :event_transition, transition)结尾运行动作别的inside_transaction 做catch(:halt) { run_callbacks(&block) }除非成功回滚?结尾结尾结尾# ...结尾

要跳过事务,您应该对 state_machine 进行猴子补丁,以便转换方法(例如 orangify!)在转换前检查记录是否有效.

这是您应该实现的目标的示例

# 覆盖 orangify!状态机动作# 如果记录有效,则执行实际转换,# 否则提前返回.def orangify!(*args)除非self.valid,否则返回false?极好的结尾

当然,您不能为每个方法手动执行此操作,这就是您应该对库进行修补以实现此结果的原因.

What is the correct syntax for performing a validation on before a transition in the state_machine gem?

I've tried the following,

before_transition :apple => :orange do
  validate :validate_core
end

def validate_core
  if core.things.blank?
    errors.add(:core, 'must have one thing')
  end
end

But I get the following error,

undefined method `validate' for #<StateMachine::Machine:0x007ffed73e0bd8>

I've also tried writing it as,

state :orange do
  validate :validate_core
end

But this causes a rollback after the record is saved, which is less than ideal. I'd like to stop the state machine from transitioning into :orange in the first place.

The core problem is that in my controller I have logic that relies on the result of object.save. The validation I have for my state machine doesn't kick in until after the initial save, so save gets returned as true and the controller goes on to logic it shouldn't hit if the object isn't valid.

I've worked around this by testing the validity manually in addition to checking the save, but it feels like there should be a way to have the validation fire before the object saves.

解决方案

The idea of that particular state machine is to embed validation declaration inside the state.

state :orange do
  validate :validate_core
end

The configuration above will perform the validation :validate_core whenever the object is transitioning to orange.

event :orangify do
  transition all => :orange
end

I understand your concern about the rollback, but keep in mind that the rollback is performed in a transaction, thus it's quite cheap.

record.orangify!

Moreover, remember you can also use the non bang version that don't use exceptions.

> c.orangify
   (0.3ms)  BEGIN
   (0.3ms)  ROLLBACK
 => false

That said, if you want to use a different approach based on the before transition, then you only have to know that if the callback returns false, the transition is halted.

before_transition do
  false
end

> c.orangify!
   (0.2ms)  BEGIN
   (0.2ms)  ROLLBACK
StateMachine::InvalidTransition: Cannot transition state via :cancel from :purchased (Reason(s): Transition halted)

Note that a transaction is always started, but it's likely no query will be performed if the callback is at the very beginning.

The before_transaction accepts some params. You can yield the object and the transaction instance.

before_transition do |object, transaction|
  object.validate_core
end

and indeed you can restrict it by event

before_transition all => :orange do |object, transaction|
  object.validate_core # => false
end

In this case, validate_core however is supposed to be a simple method that returns true/false. If you want to use the defined validation chain, then what comes to my mind is to invoke valid? on the model itself.

before_transition all => :orange do |object, transaction|
  object.valid?
end

However, please note that you can't run a transaction outside the scope of a transaction. In fact, if you inspect the code for perform, you will see that callbacks are inside the transaction.

# Runs each of the collection's transitions in parallel.
#
# All transitions will run through the following steps:
# 1. Before callbacks
# 2. Persist state
# 3. Invoke action
# 4. After callbacks (if configured)
# 5. Rollback (if action is unsuccessful)
#
# If a block is passed to this method, that block will be called instead
# of invoking each transition's action.
def perform(&block)
  reset

  if valid?
    if use_event_attributes? && !block_given?
      each do |transition|
        transition.transient = true
        transition.machine.write(object, :event_transition, transition)
      end

      run_actions
    else
      within_transaction do
        catch(:halt) { run_callbacks(&block) }
        rollback unless success?
      end
    end
  end

  # ...
end

To skip the transaction, you should monkey patch state_machine so that transition methods (such as orangify!) check whether the record is valid before transitioning.

Here's an example of what you should achieve

# Override orangify! state machine action
# If the record is valid, then perform the actual transition,
# otherwise return early.
def orangify!(*args)
  return false unless self.valid?
  super
end

Of course, you can't do that manually for each method, that's why you should monkey patch the library to achieve this result.

这篇关于在 state_machine gem 上持久化之前的验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-15 13:38