在state_machine gem上持久validation

在state_machine gem转换之前执行validation的正确语法是什么?

我试过以下,

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 

但我得到以下错误,

 undefined method `validate' for # 

我也试过写它,

 state :orange do validate :validate_core end 

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

核心问题是在我的控制器中我有依赖于object.save结果的逻辑。 我对状态机的validation直到初始保存之后才开始,因此保存返回为真,控制器继续逻辑,如果对象无效,则不应该命中。

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

该特定状态机的想法是在状态内嵌入validation声明。

 state :orange do validate :validate_core end 

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

 event :orangify do transition all => :orange end 

我理解您对回滚的关注,但请记住,回滚是在事务中执行的,因此它非常便宜。

 record.orangify! 

此外,请记住,您也可以使用不使用exception的非爆炸版本。

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

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

 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) 

请注意,事务始终处于启动状态,但如果回调处于最开始状态,则可能不会执行任何查询。

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

 before_transition do |object, transaction| object.validate_core end 

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

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

在这种情况下, validate_core应该是一个返回true / false的简单方法。 如果你想使用定义的validation链,那么我想到的是调用valid? 在模型本身。

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

但请注意,您无法在交易范围之外运行交易。 实际上,如果检查代码是否perform ,您将看到回调在事务内部。

 # 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 

要跳过事务,你应该修改state_machine,以便转换方法(例如orangify! )在转换之前检查记录是否有效。

这是你应该实现的一个例子

 # 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 

当然,你不能手动为每个方法做到这一点,这就是为什么你应该修补库来实现这个结果。

您可以通过执行以下操作来尝试取消转换到下一个状态:

 before_transition :apple => :orange do if core.things.blank? errors.add(:core, 'must have one thing') throw :halt end end 

这样,如果core.things为空,则核心将出现错误,并且将取消转换。 我认为它也不会对数据库进行任何更改。 虽然没有尝试过这段代码,但只是阅读了它的来源。 鉴于上面的代码,可能会导致更多代码捕获exception,下面的方法怎么样?

 def orange_with_validation if core.things.blank? && apple? errors.add(:core, 'must have one thing') else #transition to orange state orange end end 

您可以在转换到橙色状态之前在要validation的位置使用上面的代码。 此方法允许您解决state_machine的回调的限制。 在控制器中使用它为向导表单提供动力将阻止您的表单移动到下一步到期,并且在validation失败时将避免任何数据库命中。

我还是新人但不是

  validates 

代替

 validate 

http://edgeguides.rubyonrails.org/active_record_validations.html

也只是阅读你必须在状态中进行validation的文档,我从来没有使用过state_machine,但我认为是这样的:

 state :orange do validates_presence_of :apple end 

Rails正在寻找一种方法’validation’状态。 但validation是一种积极的记录方法。 所有模型都从活动记录inheritance,但状态不inheritance,因此它没有validation方法。 解决这个问题的方法是定义一个类方法并在状态中调用它。 所以,假设您的模型名为Fruit,您可以拥有这样的东西

 class Fruit < ActiveRecord::Base def self.do_the_validation validate :validate_core end before_transition :apple => :orange, :do => :do_the_validation end 

我不确定你是否需要自己。 此外,第二行可能需要:

 self.validate :validate_core 

我认为这应该有效。 话虽如此,是否有任何理由让您在过渡之前进行validation? 为什么不单独进行validation呢? 它应该始终validation。

validate methos是你的模型的类方法,所以你不能从你传递给state_machine类方法的块中调用他,因为你有新的上下文。

试试这个:

 YourModel < AR::B validate :validate_core state_machine :state, :initial => :some_state do before_transition :apple => :orange do |model, transition| model.valid? end end def validate_core if core.things.blank? errors.add(:core, 'must have one thing') end end end 

“停止[ping]状态机从转换为:橙色”的目标听起来就像过渡时的守卫。 state_machine支持以下:if和:除非转换定义上的选项。 与ActiveModelvalidation器一样,这些选项的值可以是lambda或表示要在对象上调用的方法名称的符号。

 event :orangify transition :apple => :orange, :if => lambda{|thing| thing.validate_core } # OR transition :apple => :orange, :if => :validate_core end