Ruby 迁移期间的工作流问题
Ruby Workflow Issue During Migration
我在使用 Gem 的 ActiveRecords 中使用 Ruby 工作流:Workflow
现有运行代码包含:
- 我有一个 ActiveRecord:X
- 我已经进行了两次迁移:
- (Ref1) CreateX 迁移(创建 table X)
- (Ref2) CreateInitialEntryInX 迁移(在 table X 中创建一个条目)
新变化:
- 现在我想在 ActiveRecord X 中添加工作流,因此我做了:
- (Ref3) 我在 ActiveRecord Model X 中添加了工作流代码(提及 :status 作为我的工作流字段)
- (Ref4) AddStatusFieldToX 迁移(在 table X 中添加 :status 字段)
现在,当我 运行 rake db:migrate 更改后,(Ref2) 中断 cos Migration 查找 :status 字段,如中所述工作流部分中的 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
因此,当 Migration#2 运行s 时,它会尝试查找要写入的 :status 字段,其初始值为 X_MODEL_STATES::ACTIVE
在 Active Record 模型文件工作流程中提到:workflow_column :status
并且没有找到该字段,因为 Migration#3 尚未执行。
这很痛苦,因为您的模型需要在迁移时保持一致。我不知道有任何自动解决方案。
理论上最好的方法是将模型代码版本与迁移绑定。但我不知道有任何系统允许这样做。
每次重构大型模型时,我都会遇到这个问题。这种情况的可能解决方案。
运行 手动迁移生产以确保迁移和模型之间的一致状态
暂时注释掉模型中的工作流代码以运行阻止迁移,然后部署,然后取消注释工作流代码并继续部署和下一次迁移
分支上的版本迁移和模型更改,因此它们是一致的。部署到生产环境并 运行 按块迁移
在模型代码中包含临时解决方法,并在部署生产迁移后将其从源代码中删除。
为了向后兼容,迁移代码中的猴子补丁模型。在您的情况下,这将是从模型代码中动态删除 'workflow',这可能很难,然后 运行 migration
所有的解决方案都是一些肮脏的黑客,但要对迁移和模型代码进行版本控制并不容易。最好的方法是分块部署,或者如果需要一次全部部署,请在模型中使用一些临时代码并在生产部署后将其删除。
您可以通过检查 column_name.
来结束您的工作流代码
if self.attribute_names.include?('status')
include Workflow
workflow_column :status
workflow do
...
end
end
只有在 'AddStatusToTable' 迁移 运行 成功后,才会产生 运行 工作流代码。
谢谢大家
我找到了解决这个问题的方法,我把它贴在这里。这里要发布的问题是:
- 将
: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:
x.reset_column_information
x.all.each do |x_instance|
x.status = X_MODEL_STATES::ACTIVE
x.save!
end
end
end
@stan-brajewski 现在,这段代码可以一次部署。
感谢大家的投入:)
我在使用 Gem 的 ActiveRecords 中使用 Ruby 工作流:Workflow
现有运行代码包含:
- 我有一个 ActiveRecord:X
- 我已经进行了两次迁移:
- (Ref1) CreateX 迁移(创建 table X)
- (Ref2) CreateInitialEntryInX 迁移(在 table X 中创建一个条目)
新变化:
- 现在我想在 ActiveRecord X 中添加工作流,因此我做了:
- (Ref3) 我在 ActiveRecord Model X 中添加了工作流代码(提及 :status 作为我的工作流字段)
- (Ref4) AddStatusFieldToX 迁移(在 table X 中添加 :status 字段)
现在,当我 运行 rake db:migrate 更改后,(Ref2) 中断 cos Migration 查找 :status 字段,如中所述工作流部分中的 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
因此,当 Migration#2 运行s 时,它会尝试查找要写入的 :status 字段,其初始值为 X_MODEL_STATES::ACTIVE
在 Active Record 模型文件工作流程中提到:workflow_column :status
并且没有找到该字段,因为 Migration#3 尚未执行。
这很痛苦,因为您的模型需要在迁移时保持一致。我不知道有任何自动解决方案。
理论上最好的方法是将模型代码版本与迁移绑定。但我不知道有任何系统允许这样做。
每次重构大型模型时,我都会遇到这个问题。这种情况的可能解决方案。
运行 手动迁移生产以确保迁移和模型之间的一致状态
暂时注释掉模型中的工作流代码以运行阻止迁移,然后部署,然后取消注释工作流代码并继续部署和下一次迁移
分支上的版本迁移和模型更改,因此它们是一致的。部署到生产环境并 运行 按块迁移
在模型代码中包含临时解决方法,并在部署生产迁移后将其从源代码中删除。
为了向后兼容,迁移代码中的猴子补丁模型。在您的情况下,这将是从模型代码中动态删除 'workflow',这可能很难,然后 运行 migration
所有的解决方案都是一些肮脏的黑客,但要对迁移和模型代码进行版本控制并不容易。最好的方法是分块部署,或者如果需要一次全部部署,请在模型中使用一些临时代码并在生产部署后将其删除。
您可以通过检查 column_name.
来结束您的工作流代码if self.attribute_names.include?('status')
include Workflow
workflow_column :status
workflow do
...
end
end
只有在 'AddStatusToTable' 迁移 运行 成功后,才会产生 运行 工作流代码。
谢谢大家 我找到了解决这个问题的方法,我把它贴在这里。这里要发布的问题是:
- 将
: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:
x.reset_column_information
x.all.each do |x_instance|
x.status = X_MODEL_STATES::ACTIVE
x.save!
end
end
end
@stan-brajewski 现在,这段代码可以一次部署。 感谢大家的投入:)