лучшая практика для драгоценных камней, таких как рабочий процесс или AASM - PullRequest
8 голосов
/ 05 июля 2011

Я хотел бы знать, как вы, ребята, используете рабочий процесс или гем AASM в контроллере, если вы хотите обновить все атрибуты, но также для правильного запуска обратных вызовов рабочего процесса / AASM.

В настоящее время я использую это так:

  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

что не правильно, что будет лучшим решением?

Ответы [ 4 ]

4 голосов
/ 05 июля 2011

Я обычно определяю несколько действий, которые обрабатывают переход из одного состояния в другое и имеют явные имена.В вашем случае я бы предложил вам добавить действие 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

2 голосов
/ 20 декабря 2013

Вы можете переопределить установщик aasm_state моделей (или статус в моем примере), чтобы он мог принимать имена событий.Затем мы проверяем, действительно ли это событие, а затем проверяем, является ли переход действительным.Если это не так, мы добавляем правильное сообщение об ошибке.

Спецификация запроса

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

Модель Spec

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
0 голосов
/ 22 января 2013

Я хотел, чтобы моя модель возвращала новое состояние после обновления, и это был самый простой способ сделать это без большого количества "жира" в контроллерах, и это облегчит работу в будущем, если ваш рабочий процесс изменится :

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
0 голосов
/ 05 июля 2011

Это небольшая вещь, но хеш возвращает ноль, если ее нет, так что вы можете удалить вызов для представления?

Я понимаю, что вы, конечно, не об этом.Один из вариантов - установить в модели фильтр до и выполнить там проверку состояния.Это оставляет ваш контроллер слепым к основному хранилищу вашего статуса.

Кстати, мы здесь используем AASM, и мне это нравится:)

...