【问题标题】:Check if state is past another state in aasm?检查状态是否超过aasm中的另一个状态?
【发布时间】:2014-10-31 21:43:28
【问题描述】:

假设有一个对象有 4 个状态

:new
:in_process
:done
:verified

还有一个方法应该只在对象的状态大于:in_process时才执行

我该如何进行这项检查?我想这可能是什么

def some_action
  return unless my_object.state > :in_process
  #do some work
end

但这只是比较字符串。

我是否遗漏了什么,或者是否有执行此类检查的实际方法?

谢谢。

【问题讨论】:

    标签: ruby-on-rails aasm


    【解决方案1】:

    忽略非线性状态机的问题,我发现以下方法可以很好地满足我在一些具有简单状态机的项目中的需求:

    # Check if the stage is in or before the supplied stage (stage_to_check).
    def in_or_before_stage?(stage_to_check)
      if stage_to_check.present? && self.stage.present?
        STAGES_IN_ORDER.reverse.lazy.drop_while { |stg| stg != stage_to_check }.include?(self.stage)
      else
        false
      end
    end
    

    有时也需要其他检查:

    # Check if the stage is in or after the supplied stage (stage_to_check).
    def in_or_after_stage?(stage_to_check)
      if stage_to_check.present? && self.stage.present?
        # Get all the stages that are in and after the stage we want to check (stage_to_check),
        # and then see if the stage is in that list (well, technically in a lazy enumerable).
        STAGES_IN_ORDER.lazy.drop_while { |stg| stg != stage_to_check }.include?(self.stage)
      else
        false
      end
    end
    

    其中“STAGES_IN_ORDER”只是一个数组,其中的阶段按从初始到最终的顺序排列。

    我们只是从列表中删除项目,然后检查对象的当前阶段是否在结果列表中。如果我们想知道它是在某个阶段还是在某个阶段之前,我们会删除后面的阶段,直到我们到达我们提供的测试阶段;如果我们想知道它是否在某个给定阶段之后,我们会从列表的前面删除项目。

    我知道你可能不再需要这个答案了,但希望它对某人有所帮助 =]

    【讨论】:

      【解决方案2】:

      这里的问题是您在状态机中没有订单。您需要提供并声明一个。

      我会坚持这个解决方案:

      1. 首先在模型中声明常量,包含状态(按顺序!),所以: STATES = [:new, :in_process, :done, :verified]

      2. 然后,在您的模型内部:

       

      def current_state_index
        return state_index(self.state)
      end
      
      def state_index(state)
        return STATES.index(state)
      end
      
      def some_action
        return unless current_state_index > state_index(:in_process)
        #do some work end
      end
      

      【讨论】:

      • 有趣的解决方案。我认为它适用于我的特殊情况,但有一些事情需要考虑。 1)您正在复制声明状态的位置;一次在 aasm 声明中,另一个在数组中。 2)一个状态机可能有多个“分支”,这只是一个分支的情况。
      • 1) 是的 - 我知道,但是您需要以某种方式声明订单。 2)当有分支时,可能会出现一些实际上无法比较的状态。考虑一下:faculty.kutztown.edu/rieksts/225/study/spring07/test3-ans_files/… 哪个元素大于 8 或 6?实际上——我们不知道。我相信这里需要线性排序假设才能考虑这样的问题。
      【解决方案3】:

      如果在 AASM 中定义正确的顺序并确保 覆盖任何状态(例如,指定额外的选项),则可以使用它们。

      下面的 mixin 定义了像 Model.done_or_beforeModel.in_process_or_after 这样的作用域,以及像 m.done_or_before? 这样的方法。

      module AASMLinearity
        def self.included(base)
          base.extend(ClassMethods)
        end
      
        module ClassMethods
          def aasm(*args, &block)
            r = super(*args, &block)
            if block
              states = r.state_machine.states.map(&:name)
              column = r.attribute_name
              states.each_with_index do |state, i|
                scope "#{state}_or_after", ->{ where(column => states[i..-1]) }
                scope "#{state}_or_before", ->{ where(column => states[0..i]) }
      
                define_method "#{state}_or_after?", ->{ states[i..-1].include? read_attribute(column).to_sym }
                define_method "#{state}_or_before?", ->{ states[0..i].include? read_attribute(column).to_sym }
              end
            end
            r
          end
        end
      end
      

      你可以把它放在像app/models/concerns/aasm_linearity.rbinclude AASMLinearity这样的地方include AASM之后,但是在状态机定义之前。

      【讨论】:

        猜你喜欢
        • 2022-07-21
        • 1970-01-01
        • 2020-03-02
        • 2020-11-19
        • 1970-01-01
        • 2022-06-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多