2016-06-29 62 views
1

在我在我的ActiveRecords使用Ruby的工作流程使用宝石:WorkflowRuby的工作流程问题迁移

现有运行代码包含:

  • 我有一个ActiveRecord:X
  • 我有已迁移两次:
    • (Ref1) CreateX migration(它创建表X)
    • (REF2) CreateInitialEntryInX迁移(在表X创建一个条目)

新变化:

  • 现在,我想补充的工作流程在ActiveRecord X中,因此我做了:
    • (REF3)我增加了工作流代码中的ActiveRecord型号X(提的是:地位,我的工作流场)
    • (至Ref4) AddStatusFieldToX迁移(添加:在表X状态字段)

现在,当我运行rake db:迁移后发生更改时,(Ref2) break 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的模型文件中提到工作流程为:workflow_column :status并且未找到该字段为迁移#3尚未执行。

+0

请张贴一些代码和实际的例外,也是什么原因,没有把正确的顺序迁移? – fabriciofreitag

+0

@onionfeelings,因为早些时候的迁移即Ref1和Ref2已经在生产中,并且自从现在几个月开始运行。这是由于添加要求而正在进行的迁移。这个问题不会在生产中进行,因为只有新的迁移将运行。 但是,如果有人分叉代码并在开发环境中运行,迁移将中断(我想避免) – kattybilly

+0

你可以发布你的代码吗?理论上,如果你这样做: add_column:TABLE_X,:STATUS_ID,:整数 你不应该有任何问题,并且仍然Rails会正确处理的关联,因为你下面的约定 – fabriciofreitag

回答

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 

@斯坦 - brajewski现在,这个代码可以去一个部署。 感谢所有的投入:)

+1

伟大的你做到了。这仍然是一个临时代码的黑客,因为你不希望你的模型来对所有这些属性永远:-) –

+0

你应该接受别人的答案所有这些如果,我投@ 007sumit就像你刚才复制他的答案,你的代码和提出了一个新的答案。您使用007sumit代码作为解决方案,因此请接受007sumit答案。这就是工作原理。 –

+0

所以这不是一个完整的答案。它没有完全解决它。答案是@ 007sumit的答案和帖子中提供的答案的组合:http://stackoverflow.com/questions/200813/how-do-i-force-activerecord-to-reload-a-class。因此我加了正确的答案,这是两者的结合。 M很高兴接受任何人的回答,但他们不完整,因此不能接受。虽然upvoted可用的。 – kattybilly

1

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

理论上最好的方法是让模型代码版本绑定迁移。但我不知道任何允许这个的系统。

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

  1. 生产运行迁移手动,以确保迁移和模型

  2. 之间consitent状态模型临时注释掉工作流程代码运行阻止迁移,再部署,再取消注释工作流代码,并与部署继续前进,下次迁移

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

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

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

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

+0

感谢解决方案,事实上他们是黑客,但即使我做了上述任何解决方案或如上所述,部署在块中,我可能会摆脱生产或其他现有的开发部署,但仍然会遇到的问题是: >如果有人让我的回购构建项目,出于同样的原因,系统上的迁移仍然会中断。 – kattybilly

+0

我已经发布了解决方案。感谢您的宝贵意见@StanBrajewski – kattybilly

+0

@kattybilly当用户克隆新的回购协议和启动项目,他不应该开始迁移,但是从DB/schema.rb创建一个新的数据库 - 它在Rails的文档HTTP说:// edgeguides。 rubyonrails.org/active_record_migrations.html#schema-dumping-and-you“通过重播整个迁移历史记录,不需要(也很容易出错)部署新的应用程序实例,它更简单快捷将当前模式的描述加载到数据库中。“ –

2

您可以通过检查column_name来结束工作流程代码。

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

这将导致只有在'AddStatusToTable'迁移成功运行后,才会运行工作流代码。

+0

那么这样做是: - 迁移运行之前,初始化Rails应用程序,而不会因为它在被绕过了工作流程的'如果条件(你提到)' - 然后,**迁移#2 **移民工程罚款和成功,但是当**迁移#3 **运行时,它不与代码做任何事情:为所加载的模型是不是一个新的列,再加上工作流仍不知何故 'X_MODEL_STATES :: ACTIVE'控制模型(尽管'如果{条件}')。 – kattybilly

+0

所有执行迁移后,观察是: 所有'X Model'条目有':状态= nil'尽管这种变化由你的建议,尽管有**迁移#3 **包含'初始化:status'价值' X_MODEL_STATES :: ACTIVE'这是'1' – kattybilly