迁移期间的Ruby工作流问题

时间:2016-06-29 13:48:57

标签: ruby-on-rails ruby activerecord workflow rails-migrations

我使用Gem:Workflow

在我的ActiveRecords中使用Ruby Workflow

现有的运行代码包含:

  • 我有一个ActiveRecord:X
  • 我已经进行了两次迁移:
    • (Ref1) CreateX迁移(创建表X)
    • (Ref2) CreateInitialEntryInX迁移(在表X中创建一个条目)

新变化:

  • 现在我想在ActiveRecord X中添加工作流程,因此我做了:
    • (参考3)我在ActiveRecord Model X中添加了工作流代码(提到:状态为我的工作流程字段)
    • (参考4) AddStatusFieldToX迁移(添加:表X中的状态字段)

现在,当我在更改后运行rake db:migrate时,(Ref2)中断cos迁移查找:状态字段,如“工作流”部分的ActiveRecord模型中所述,但是:status字段由于迁移(Ref4)尚未执行,因此尚未添加。

因此,当所有迁移按顺序运行时,所有构建都会失败,对此有何解决方案? 我不想重新排序任何迁移或编辑任何旧的现有迁移。

我的模型看起来像:

  class BaseModel < ActiveRecord::Base
      #
      # Workflow to define states of Role
      #
      # Initial State => Active
      #
      # # State Diagram::
      #   Active  --soft_delete--> Deleted
      #   Deleted
      #
      # * activate, soft_delete are the event which triggers the state transition
      #
      include Workflow
      workflow_column :status
      workflow do
        state :active, X_MODEL_STATES::ACTIVE do
          event :soft_delete, transitions_to: :deleted
        end
        state :deleted, X_MODEL_STATES::DELETED

        on_transition do |from, to, event, *event_args|
          self.update_attribute(:status, to)
        end
      end

      def trigger_event(event)
        begin
          case event.to_i
            when X_MODEL_EVENTS::ACTIVATE
              self.activate!
            when X_MODEL_EVENTS::SOFT_DELETE
              self.soft_delete!
          end
        rescue ....
      end
  end

  class X_MODEL_STATES
    ACTIVE      = 1
    DELETED     = 2
  end

  class X_MODEL_EVENTS
    ACTIVATE      = 1
    SOFT_DELETE   = 2
  end

# Migrations(posting Up functions only - in correct sequence)
#--------------------------------------------------

#1st: Migration - This is already existing migration
CreateX < ActiveRecord::Migration
  def up
    create_table :xs do |t|
      t.string :name
      t.timestamps null: false
    end
  end
end

#2nd: Migration - This is already existing migration
CreateInitialX < ActiveRecord::Migration
  def up
    X.create({:name => 'Kartik'})
  end
end

#3rd: Migration - This is a new migration
AddStatusToX < ActiveRecord::Migration
  def up
    add_column :xs, :status, :integer
    x.all.each do |x_instance|
      x.status = X_MODEL_STATES::ACTIVE
      x.save!
    end
  end
end

因此,当迁移#2 运行时,它会尝试查找:要写入的状态字段的初始值为X_MODEL_STATES::ACTIVE,因为它在Active Record Model文件工作流中提到:{ {1}}并且未找到迁移#3 尚未执行的字段。

3 个答案:

答案 0 :(得分:2)

您可以通过检查column_name来包装工作流代码。

if self.attribute_names.include?('status')
  include Workflow
  workflow_column :status
  workflow do
    ...
  end
end

这将导致仅在“AddStatusToTable&#39;”之后运行工作流程代码。迁移成功运行。

答案 1 :(得分:1)

这很痛苦,因为您的模型需要与迁移保持一致。我不知道任何汽车解决方案。

理论上最好的方法是将模型代码版本与迁移结合起来。但我不知道任何允许这种情况的系统。

每次我做大模型重构我都会遇到这个问题。这种情况的可能解决方案。

  1. 手动运行生产迁移以确保迁移和模型之间的合作状态

  2. 暂时注释掉模型中的工作流代码以运行阻止迁移,然后部署,然后取消注释工作流代码,继续部署和下次迁移

  3. 分支上的版本迁移和模型更改,因此它们是一致的。部署到生产并按块运行迁移

  4. 在模型代码中包含临时解决方法,并在部署生产迁移后从源代码中删除它们。

  5. 迁移代码中的Monkey-patch模型,用于向后兼容。在您的情况下,这将是从模型代码中动态删除“工作流程”,这可能很难,然后运行迁移

  6. 所有解决方案都是某种肮脏的黑客攻击,但是迁移和模型代码版本化并不容易。最好的方法是以块的形式进行部署,或者如果需要一次部署所有部分,请在模型中使用一些临时代码,并在生产部署后将其删除。

答案 2 :(得分:0)

感谢所有 我找到了解决方案,我在这里发布。这里要问的问题是:

  • :status字段添加到ActiveRecord Model X而不注释掉工作流程代码,并且不允许工作流程在迁移期间禁止在Table X中创建实例。
  • 其次,按@ 007sumit。
  • 的指定添加if条件
  • 第三,能够使用Model X
  • 更新的column_information在迁移中重新加载模型

在此编写完整的代码解决方案:

  class BaseModel < ActiveRecord::Base
      #
      # Workflow to define states of Role
      #
      # Initial State => Active
      #
      # # State Diagram::
      #   Active  --soft_delete--> Deleted
      #   Deleted
      #
      # * activate, soft_delete are the event which triggers the state transition
      #

      # if condition to add workflow only if :status field is added
      if self.attribute_names.include?('status')
        include Workflow
        workflow_column :status
        workflow do
          state :active, X_MODEL_STATES::ACTIVE do
            event :soft_delete, transitions_to: :deleted
          end
          state :deleted, X_MODEL_STATES::DELETED

        end
      end

      def trigger_event(event)
        ...
      end
  end

  class X_MODEL_STATES
    ...
  end

  class X_MODEL_EVENTS
    ...
  end

# Migrations(posting Up functions only - in correct sequence)
#--------------------------------------------------

#1st: Migration - This is already existing migration
CreateX < ActiveRecord::Migration
  def up
    create_table :xs do |t|
      t.string :name
      t.timestamps null: false
    end
  end
end

#2nd: Migration - This is already existing migration
CreateInitialX < ActiveRecord::Migration
  def up
    X.create({:name => 'Kartik'})
  end
end

#3rd: Migration - This is a new migration to add status field and 
# modify status value in existing entries in X Model
AddStatusToX < ActiveRecord::Migration
  def up
    add_column :xs, :status, :integer

    # This resets Model Class before executing this migration and 
    # Workflow is identified from the if condition specified which was 
    # being skipped previously without this line as Model Class is  
    # loaded only once before all migrations run.
    # Thanks to post: http://stackoverflow.com/questions/200813/how-do-i-force-activerecord-to-reload-a-class

    x.reset_column_information
    x.all.each do |x_instance|
      x.status = X_MODEL_STATES::ACTIVE
      x.save!
    end
  end
end

@ stan-brajewski现在,此代码可以在一个部署中进行。 感谢所有人的投入:)