【问题标题】:best practice for gems like workflow or AASM工作流或 AASM 等 gem 的最佳实践
【发布时间】:2011-07-05 10:59:35
【问题描述】:

如果您想更新所有属性,但还需要工作流/AASM 回调正确触发,我想知道你们如何使用控制器中的工作流或 AASM gem。

目前,我是这样使用它的:

  class ModelController < ApplicationController
    def update
      @model = model.find(params[:id])

      if params[:application]['state'].present?
        if params[:application]['state'] == "published"
          @model.publish!
        end
      end
      if @model.update_attributes(params[:application]); ... end
    end
  end

感觉不对,有什么更好的解决方案?

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 aasm


    【解决方案1】:

    我通常定义多个动作来处理从一种状态到另一种状态的转换,并具有明确的名称。在您的情况下,我建议您添加 publish 操作:

    def publish
      # as the comment below states: your action 
      # will have to do some error catching and possibly
      # redirecting; this goes only to illustrate my point
      @story = Story.find(params[:id])
      if @story.may_publish?
        @story.publish!
      else
       # Throw an error as transition is not legal
      end
    end
    

    在您的routes.rb 中声明:

    resources :stories do
      member do
        put :publish
      end
    end
    

    现在您的路线准确地反映了故事发生的情况:/stories/1234/publish

    【讨论】:

    • 请注意,在这种情况下,您可能没有从状态“x”到“已发布”的转换,AASM 将引发异常。否则,听起来很合理。男孩,今天早上我是个吹毛求疵的人:P
    • 对,这是伪代码。这只是为了说明一般模式。
    【解决方案2】:

    您可以覆盖模型 aasm_state 设置器(或我的示例中的状态),以便它可以接受事件名称。然后我们检查它是否是一个有效的事件,然后检查转换是否有效。如果不是,我们添加正确的错误消息。

    请求规范

    it "should cancel" do
      put "/api/ampaigns/#{@campaign.id}", {campaign: {status: "cancel"}, format: :json}, valid_session
      response.code.should == "204"
    end
    

    模型规范

    it "should invoke the cancel method" do
      campaign.update_attribute(:status, "cancel")
      campaign.canceled?.should be_true
    end
    it "should add an error for illegal transition" do
      campaign.update_attribute(:status, "complete")
      campaign.errors.should include :status
      campaign.errors[:status].should == ["status cannot transition from pending to complete"]
    end
    it "should add an error for invalid status type" do
      campaign.update_attribute(:status, "foobar")
      campaign.errors.should include :status
      campaign.errors[:status].should == ["status of foobar is not valid.  Legal values are pending, active, canceled, completed"]
    end
    

    型号

    class Campaign < ActiveRecord::Base
      include AASM
      aasm column: :status do
        state :pending, :initial => true
        state :active
        state :canceled
        state :completed
        # Events
        event :activate do
          transitions from: :pending, to: :active
        end
        event :complete do
          transitions from: :active, to: [:completed]
        end
        event :cancel do
          transitions from: [:pending, :active], to: :canceled
        end
      end
      def status=(value)
        if self.class.method_defined?(value)
          if self.send("may_#{value}?")
            self.send(value)
          else
            errors.add(:status, "status cannot transition from #{status} to #{value}")
          end
    
        else
          errors.add(:status, "status of #{value} is not valid.  Legal values are #{aasm.states.map(&:name).join(", ")}")
        end
      end
    end
    

    【讨论】:

      【解决方案3】:

      这是一件小事,但如果该事物不存在,则哈希返回 nil,因此您可以删除对 present 的调用?

      我知道这当然不是你要问的。一种替代方法是在模型中放置一个前置过滤器并在那里检查状态。这会使您的控制器对您的状态的底层存储视而不见。

      顺便说一句,我们在这里使用 AASM,我喜欢它 :)

      【讨论】:

        【解决方案4】:

        我希望我的模型在更新后返回新状态,这是我能想到的最简单的方法,而控制器中没有太多“脂肪”,而且如果你的工作流程发生变化,它会让你更容易前进:

        class Article < ActiveRecord::Base
          include Workflow
          attr_accessible :workflow_state, :workflow_event # etc
          validates_inclusion_of :workflow_event, in: %w(submit approve reject), allow_nil: true
          after_validation :send_workflow_event
        
          def workflow_event
            @workflow_event
          end
        
          def workflow_event=(workflow_event)
            @workflow_event = workflow_event
          end
        
          # this method should be private, normally, but I wanted to 
          # group the meaningful code together for this example
          def send_workflow_event
            if @workflow_event && self.send("can_#{@workflow_event}?")
              self.send("#{@worklow_event}!")
            end
          end
        
          # I pulled this from the workflow website, to use that example instead.
          workflow do
            state :new do
              event :submit, :transitions_to => :awaiting_review
            end
            state :awaiting_review do
              event :review, :transitions_to => :being_reviewed
            end
            state :being_reviewed do
              event :accept, :transitions_to => :accepted
              event :reject, :transitions_to => :rejected
            end
            state :accepted
            state :rejected
          end
        end
        

        【讨论】:

          猜你喜欢
          • 2016-10-19
          • 1970-01-01
          • 2010-12-02
          • 1970-01-01
          • 2016-11-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-22
          相关资源
          最近更新 更多