mlins-active_migration 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +16 -0
- data/MIT-LICENSE +20 -0
- data/README +169 -0
- data/Rakefile +35 -0
- data/VERSION.yml +4 -0
- data/lib/active_migration/base.rb +237 -0
- data/lib/active_migration/callbacks.rb +114 -0
- data/lib/active_migration/dependencies.rb +53 -0
- data/lib/active_migration/key_mapper.rb +117 -0
- data/lib/active_migration/version.rb +9 -0
- data/lib/active_migration.rb +48 -0
- data/lib/activemigration.rb +1 -0
- data/spec/base_spec.rb +223 -0
- data/spec/callbacks_spec.rb +52 -0
- data/spec/dependencies_spec.rb +36 -0
- data/spec/fixtures/product_eight_migration.rb +13 -0
- data/spec/fixtures/product_five_migration.rb +11 -0
- data/spec/fixtures/product_four_migration.rb +9 -0
- data/spec/fixtures/product_nine_migration.rb +13 -0
- data/spec/fixtures/product_one_migration.rb +11 -0
- data/spec/fixtures/product_seven_migration.rb +13 -0
- data/spec/fixtures/product_six_migration.rb +10 -0
- data/spec/fixtures/product_ten_migration.rb +12 -0
- data/spec/fixtures/product_three_migration.rb +11 -0
- data/spec/fixtures/product_two_migration.rb +11 -0
- data/spec/key_mapper_spec.rb +122 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +16 -0
- metadata +32 -3
data/CHANGELOG
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
*1.0.3 (November 22, 2008)*
|
2
|
+
|
3
|
+
* Fixed gemspec problems
|
4
|
+
|
5
|
+
*1.0.2 (November 22, 2008)*
|
6
|
+
|
7
|
+
* Renamed gem back to active_migration
|
8
|
+
|
9
|
+
*1.0.1 (November 22, 2008)*
|
10
|
+
|
11
|
+
* Renamed gem to activemigration
|
12
|
+
* Fixed dependencies
|
13
|
+
|
14
|
+
*1.0.0 (November 22, 2008)*
|
15
|
+
|
16
|
+
* Initial Release
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Matt Lins
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
= ActiveMigration
|
2
|
+
|
3
|
+
Github[http://github.com/mlins/active_migration/]
|
4
|
+
|
5
|
+
ActiveMigration is a library to assist with the migration of data from legacy databases. This library was not designed
|
6
|
+
for speed so much as it was designed to maintain data integrity. By default ActiveMigration runs all data through your
|
7
|
+
ActiveRecord validators and callbacks. It can be extended to run faster with the ar-extenstions library if speed is
|
8
|
+
more important than data integrity.
|
9
|
+
|
10
|
+
ActiveMigration was written by: Matt Lins.
|
11
|
+
|
12
|
+
You'll probably want to use ActiveMigration with the Godwit[http://github.com/mlins/godwit] framework for migrating
|
13
|
+
databases.
|
14
|
+
|
15
|
+
== Installation
|
16
|
+
|
17
|
+
$ gem sources -a http://gems.github.com
|
18
|
+
|
19
|
+
$ sudo gem install mlins-active_migration
|
20
|
+
|
21
|
+
== Terms
|
22
|
+
|
23
|
+
I use a couple of terms throughout the codebase and documentation that could be confusing. I use the term *legacy* to
|
24
|
+
refer to the old data that you'll be migrating from. I use the term *active* to refer the new data that you'll be
|
25
|
+
migrating to. This can be confusing because ActiveMigration makes use of ActiveRecord. You'll see *active*
|
26
|
+
and *legacy* used to refer to:
|
27
|
+
|
28
|
+
- databases
|
29
|
+
- models
|
30
|
+
- records
|
31
|
+
- fields
|
32
|
+
|
33
|
+
Other terms used:
|
34
|
+
|
35
|
+
- *PK* - Primary Key
|
36
|
+
- *FK* - Foreign Key
|
37
|
+
|
38
|
+
== Usage
|
39
|
+
|
40
|
+
Once you have written your migration, you can run it like this:
|
41
|
+
|
42
|
+
MyMigration.new.run
|
43
|
+
|
44
|
+
ActiveMigration::Base is intended to be subclassed and defines a simple DSL similar to ActiveRecord. ActiveMigration
|
45
|
+
assumes you have an ActiveRecord class defined for both the legacy model and the active model. Godwit namespaces
|
46
|
+
legacy models with the Legacy module. You can then map fields with a muli-dimensional array. Each element of the
|
47
|
+
array represents one field mapping. Within the element array the first element is the legacy field and the second
|
48
|
+
element is the active field.
|
49
|
+
|
50
|
+
A simple example:
|
51
|
+
|
52
|
+
class PostMigration < ActiveMigration::Base
|
53
|
+
|
54
|
+
set_active_model 'Post'
|
55
|
+
|
56
|
+
set_legacy_model 'Legacy::Post'
|
57
|
+
|
58
|
+
map [['old_field_name', 'new_field_name']]
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
ActiveMigration::Callbacks provides callback support via the ActiveSupport library. You can use callbacks exactly
|
63
|
+
as you would in your ActiveRecord models. This example below also illustrates accessing the record instance
|
64
|
+
variables. You can access both the legacy and active records at anytime during the migration lifcycle via:
|
65
|
+
@active_record and @legacy_record.
|
66
|
+
|
67
|
+
A callbacks example:
|
68
|
+
|
69
|
+
class PostMigration < ActiveMigration::Base
|
70
|
+
|
71
|
+
set_active_model 'Post'
|
72
|
+
|
73
|
+
set_legacy_model 'Legacy::Post'
|
74
|
+
|
75
|
+
map [['title_tx', 'title' ],
|
76
|
+
['writer_id', 'author_id']]
|
77
|
+
|
78
|
+
before_save :upcase_name
|
79
|
+
|
80
|
+
def upcase_name
|
81
|
+
@active_record.name.upcase
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
ActiveMigration::Dependencies provides a dependency tree for your migrations. If you have dependencies set, they'll
|
87
|
+
be ran first.
|
88
|
+
|
89
|
+
A dependencies example:
|
90
|
+
|
91
|
+
class PostMigration < ActiveMigration::Base
|
92
|
+
|
93
|
+
set_active_model 'Post'
|
94
|
+
|
95
|
+
set_legacy_model 'Legacy::Post'
|
96
|
+
|
97
|
+
map [['title_tx', 'title' ],
|
98
|
+
['writer_id', 'author_id']]
|
99
|
+
|
100
|
+
set_dependencies [:author_migration]
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
ActiveMigration::KeyMapper provides a system to persist legacy PK/FK relationships. It's possible to migrate your
|
105
|
+
PK's and FK's through ActiveMigration. However, sometimes that's not possible or desirable. The keymapper allows
|
106
|
+
you to serialize the PK of the legacy record mapped to the new PK of the active record. You can then recall that
|
107
|
+
mapping (usually with a legacy FK) later in other migrations to maintain relationships.
|
108
|
+
|
109
|
+
First you need to serialize the PK of a model. Let's say your Manufacturer model has_may Products.
|
110
|
+
|
111
|
+
class AuthorMigration < ActiveMigration::Base
|
112
|
+
|
113
|
+
set_active_model 'Author'
|
114
|
+
|
115
|
+
set_legacy_model 'Legacy::Writer'
|
116
|
+
|
117
|
+
map [['name_tx', 'name' ],
|
118
|
+
['handle_tx', 'nickname']]
|
119
|
+
|
120
|
+
write_key_map true
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
Later, in your PostMigration you may need to recall the legacy Author PK to maintain the relationship, that can be
|
125
|
+
done like so:
|
126
|
+
|
127
|
+
class PostMigration < ActiveMigration::Base
|
128
|
+
|
129
|
+
set_active_model 'Post'
|
130
|
+
|
131
|
+
set_legacy_model 'Legacy::Post'
|
132
|
+
|
133
|
+
map [['title_tx', 'title' ],
|
134
|
+
['writer_id', 'author_id', :author_migration]]
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
This will lookup the PK of the legacy author by the 'writer_id' and return the new PK assigned to the model when it
|
139
|
+
was saved.
|
140
|
+
|
141
|
+
== Requirements
|
142
|
+
|
143
|
+
- ActiveSupport
|
144
|
+
- ActiveRecord
|
145
|
+
|
146
|
+
== License
|
147
|
+
|
148
|
+
(The MIT License)
|
149
|
+
|
150
|
+
Copyright (c) 2008 Matt Lins
|
151
|
+
|
152
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
153
|
+
a copy of this software and associated documentation files (the
|
154
|
+
'Software'), to deal in the Software without restriction, including
|
155
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
156
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
157
|
+
permit persons to whom the Software is furnished to do so, subject to
|
158
|
+
the following conditions:
|
159
|
+
|
160
|
+
The above copyright notice and this permission notice shall be
|
161
|
+
included in all copies or substantial portions of the Software.
|
162
|
+
|
163
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
164
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
165
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
166
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
167
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
168
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
169
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
|
5
|
+
desc 'Run the specs'
|
6
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
7
|
+
t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
|
8
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
9
|
+
end
|
10
|
+
|
11
|
+
Rake::RDocTask.new do |t|
|
12
|
+
t.rdoc_dir = 'doc'
|
13
|
+
t.rdoc_files.include('README')
|
14
|
+
t.rdoc_files.include('lib/**/*.rb')
|
15
|
+
t.options << '--inline-source'
|
16
|
+
t.options << '--all'
|
17
|
+
t.options << '--line-numbers'
|
18
|
+
end
|
19
|
+
|
20
|
+
begin
|
21
|
+
require 'jeweler'
|
22
|
+
Jeweler::Tasks.new do |s|
|
23
|
+
s.name = "active_migration"
|
24
|
+
s.summary = "A library to assist with the migration of data from legacy databases."
|
25
|
+
s.email = "mattlins@gmail.com"
|
26
|
+
s.homepage = "http://github.com/mlins/active_migration"
|
27
|
+
s.description = "A library to assist with the migration of data from legacy databases."
|
28
|
+
s.authors = ["Matt Lins"]
|
29
|
+
s.files = FileList["[A-Z]*", "{lib,spec}/**/*"]
|
30
|
+
s.add_dependency 'activesupport', '>= 2.2.2'
|
31
|
+
s.add_dependency 'activerecord', '>= 2.2.2'
|
32
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
35
|
+
end
|
data/VERSION.yml
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
module ActiveMigration
|
2
|
+
|
3
|
+
# Generic ActiveMigration exception class.
|
4
|
+
class ActiveMigrationError < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
# ActiveMigration::Base is subclassed by your migration. It defines a DSL similar to ActiveRecord, in which it feel more
|
8
|
+
# like a configuration.
|
9
|
+
#
|
10
|
+
# == Typical Usage:
|
11
|
+
#
|
12
|
+
# class PostMigration < ActiveMigration::Base
|
13
|
+
#
|
14
|
+
# set_active_model 'Post'
|
15
|
+
#
|
16
|
+
# set_legacy_model 'Legacy::Post'
|
17
|
+
#
|
18
|
+
# map [['name_tx', 'name' ],
|
19
|
+
# ['description_tx', 'description'],
|
20
|
+
# ['date', 'created_at' ]]
|
21
|
+
#
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
class Base
|
25
|
+
|
26
|
+
cattr_accessor :logger
|
27
|
+
|
28
|
+
class << self
|
29
|
+
|
30
|
+
attr_accessor :legacy_model, :active_model, :mappings, :legacy_find_options, :active_record_mode
|
31
|
+
|
32
|
+
# Sets the legacy model to be migrated from. It's wise to namespace your legacy
|
33
|
+
# models to prevent class duplicates.
|
34
|
+
#
|
35
|
+
# Also, *args can be passed a Hash to hold finder options for legacy record lookup.
|
36
|
+
#
|
37
|
+
# *Note:* If you set :limit, it will stagger your selects with an offset. This is intended to break up large datasets
|
38
|
+
# to conserve memory. Keep in mind, for this functionality to work :offset(because it is needed internally)
|
39
|
+
# can never be specified, it will be deleted.
|
40
|
+
#
|
41
|
+
# set_legacy_model Legacy::Post
|
42
|
+
#
|
43
|
+
# set_legacy_model Legacy::Post,
|
44
|
+
# :conditions => 'some_field = value',
|
45
|
+
# :order => 'this_field ASC',
|
46
|
+
# :limit => 5
|
47
|
+
#
|
48
|
+
def set_legacy_model(legacy_model, *args)
|
49
|
+
@legacy_model = eval(legacy_model)
|
50
|
+
args[0].delete(:offset) if args[0]
|
51
|
+
@legacy_find_options = args[0] unless args.empty?
|
52
|
+
@legacy_find_options ||= {}
|
53
|
+
end
|
54
|
+
alias legacy_model= set_legacy_model
|
55
|
+
|
56
|
+
# Sets the active model to be migrated to.
|
57
|
+
#
|
58
|
+
# Also, an additional parameter for the method of instantiation. Valid
|
59
|
+
# parameters are: :create or :update. Defaults to :create. Use this if records already
|
60
|
+
# exist in the active database. Lookup with :update will be done via the PK of the legacy
|
61
|
+
# record.
|
62
|
+
#
|
63
|
+
# set_active_model 'Post'
|
64
|
+
#
|
65
|
+
# set_active_model 'Post',
|
66
|
+
# :update
|
67
|
+
#
|
68
|
+
def set_active_model(active_model, mode=:create)
|
69
|
+
@active_model = eval(active_model)
|
70
|
+
@active_record_mode = mode
|
71
|
+
end
|
72
|
+
alias active_model= set_active_model
|
73
|
+
|
74
|
+
# Sets the mappings for the migration. Mappings are specified in a multidimensional array. Each array
|
75
|
+
# elment contains another array in which the legacy field is the first element and the active field is
|
76
|
+
# the second elment.
|
77
|
+
#
|
78
|
+
# map [['some_old_field', 'new_spiffy_field']]
|
79
|
+
#
|
80
|
+
def map(mappings)
|
81
|
+
@mappings = mappings
|
82
|
+
end
|
83
|
+
alias mappings= map
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
# Runs the migration.
|
88
|
+
#
|
89
|
+
# MyMigration.new.run
|
90
|
+
#
|
91
|
+
def run
|
92
|
+
logger.info("#{self.class.to_s} is starting.")
|
93
|
+
count_options = self.class.legacy_find_options.dup
|
94
|
+
count_options.delete(:order)
|
95
|
+
count_options.delete(:group)
|
96
|
+
count_options.delete(:limit)
|
97
|
+
count_options.delete(:offset)
|
98
|
+
@num_of_records = self.class.legacy_model.count(count_options)
|
99
|
+
if self.class.legacy_find_options[:limit] && (@num_of_records > self.class.legacy_find_options[:limit])
|
100
|
+
run_in_batches @num_of_records
|
101
|
+
else
|
102
|
+
run_normal
|
103
|
+
end
|
104
|
+
logger.info("#{self.class.to_s} migrated all #{@num_of_records} records successfully.")
|
105
|
+
end
|
106
|
+
|
107
|
+
protected
|
108
|
+
|
109
|
+
# This is called everytime there is an error. You should override this method
|
110
|
+
# and handle it in the apporpriate way.
|
111
|
+
#
|
112
|
+
def handle_error()
|
113
|
+
end
|
114
|
+
|
115
|
+
# This is called everytime there is a successful record migration. You should override this
|
116
|
+
# method and handle it in the appropriate way.
|
117
|
+
#
|
118
|
+
def handle_success()
|
119
|
+
end
|
120
|
+
|
121
|
+
# This method can be called at any point in the in the migration lifecycle (usually within callbacks)
|
122
|
+
# to stop the current record migration and continue on to the next.
|
123
|
+
#
|
124
|
+
def skip
|
125
|
+
@skip = true
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def run_in_batches(num_of_records) #:nodoc:
|
131
|
+
num_of_last_record = 0
|
132
|
+
while num_of_records > 0 do
|
133
|
+
self.class.legacy_find_options[:offset] = num_of_last_record
|
134
|
+
num_of_last_record += self.class.legacy_find_options[:limit]
|
135
|
+
num_of_records -= self.class.legacy_find_options[:limit]
|
136
|
+
run_normal
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def run_normal #:nodoc:
|
141
|
+
legacy_records = self.class.legacy_model.find(:all, self.class.legacy_find_options)
|
142
|
+
legacy_records.each do |@legacy_record|
|
143
|
+
find_or_create_active_record
|
144
|
+
migrate_record
|
145
|
+
unless skip?
|
146
|
+
save
|
147
|
+
unless skip?
|
148
|
+
unless @active_record.is_a?(Array)
|
149
|
+
logger.debug("#{self.class.to_s} successfully migrated a record from #{self.class.legacy_model.table_name} to #{self.class.active_model.table_name}. The legacy record had an id of #{@legacy_record.id}. The active record has an id of #{@active_record.id}")
|
150
|
+
else
|
151
|
+
@active_record.each do |record|
|
152
|
+
logger.debug("#{self.class.to_s} successfully migrated a record from #{self.class.legacy_model.table_name} to #{self.class.active_model.table_name}. The legacy record had an id of #{@legacy_record.id}. The active record has an id of #{record.id}")
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
else
|
157
|
+
handle_success
|
158
|
+
end
|
159
|
+
@skip = false
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def find_or_create_active_record #:nodoc:
|
164
|
+
@active_record = (self.class.active_record_mode == :create) ? self.class.active_model.new : self.class.active_model.find(@legacy_record.id)
|
165
|
+
end
|
166
|
+
|
167
|
+
def migrate_record #:nodoc:
|
168
|
+
self.class.mappings.each do |@mapping|
|
169
|
+
migrate_field
|
170
|
+
end unless self.class.mappings.nil?
|
171
|
+
end
|
172
|
+
|
173
|
+
# FIXME - #migrate_field needs to be refactored.
|
174
|
+
def migrate_field #:nodoc:
|
175
|
+
begin
|
176
|
+
eval("@active_record.#{@mapping[1]} = @legacy_record.#{@mapping[0]}")
|
177
|
+
rescue
|
178
|
+
logger.error("#{self.class.to_s} had an error while trying to migrate #{self.class.legacy_model.table_name}.#{@mapping[0]} to #{self.class.active_model.table_name}.#{@mapping[1]}. The legacy record had an id of #{@legacy_record.id}.")
|
179
|
+
handle_error
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def save #:nodoc:
|
184
|
+
if self.class.active_record_mode == :create
|
185
|
+
create
|
186
|
+
else
|
187
|
+
update
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def create #:nodoc:
|
192
|
+
process_records
|
193
|
+
end
|
194
|
+
|
195
|
+
def update #:nodoc:
|
196
|
+
process_records
|
197
|
+
end
|
198
|
+
|
199
|
+
def process_records #:nodoc:
|
200
|
+
return if skip?
|
201
|
+
unless @active_record.is_a?(Array)
|
202
|
+
save_and_resolve(@active_record)
|
203
|
+
else
|
204
|
+
@active_record.each do |record|
|
205
|
+
save_and_resolve(record)
|
206
|
+
handle_success if skip?
|
207
|
+
@skip = false
|
208
|
+
end
|
209
|
+
end
|
210
|
+
@validate_record = true
|
211
|
+
end
|
212
|
+
|
213
|
+
def save_and_resolve(record) #:nodoc:
|
214
|
+
while record.changed? && !skip?
|
215
|
+
if record.save(validate_record?)
|
216
|
+
handle_success
|
217
|
+
else
|
218
|
+
while !record.valid? && !skip? do
|
219
|
+
errors = record.errors.collect {|field,msg| field + " " + msg}.join(", ")
|
220
|
+
logger.error("#{self.class.to_s} had an error while trying to save the active_record. The associated legacy_record had an id of #{@legacy_record.id}. The active record had the following errors: #{errors}")
|
221
|
+
handle_error
|
222
|
+
end
|
223
|
+
handle_success
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def validate_record? #:nodoc:
|
229
|
+
@validate_record.nil? ? true : @validate_record
|
230
|
+
end
|
231
|
+
|
232
|
+
def skip? #:nodoc:
|
233
|
+
@skip.nil? ? false : @skip
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module ActiveMigration
|
2
|
+
# Callbacks are hooks into the ActiveMigration migration lifecycle. This typical flow is
|
3
|
+
# below. Bold items are internal calls.
|
4
|
+
#
|
5
|
+
# - before_run
|
6
|
+
# - *run*
|
7
|
+
# - before_migrate_record
|
8
|
+
# - *migrate_record*
|
9
|
+
# - after_migrate_record
|
10
|
+
# - before_migrate_field
|
11
|
+
# - *migrate_field*
|
12
|
+
# - after_migrate_field
|
13
|
+
# - before_save (before_create, before_update)
|
14
|
+
# - *save*
|
15
|
+
# - after_save (after_create, after_update)
|
16
|
+
# - after_run
|
17
|
+
#
|
18
|
+
module Callbacks
|
19
|
+
|
20
|
+
CALLBACKS = %w(before_run after_run
|
21
|
+
before_migrate_record after_migrate_record
|
22
|
+
before_migrate_field after_migrate_field
|
23
|
+
before_save after_save before_create after_create
|
24
|
+
before_update after_update)
|
25
|
+
|
26
|
+
def self.included(base)#:nodoc:
|
27
|
+
[:run, :migrate_record, :migrate_field, :save, :update, :create].each do |method|
|
28
|
+
base.send :alias_method_chain, method, :callbacks
|
29
|
+
end
|
30
|
+
base.send :include, ActiveSupport::Callbacks
|
31
|
+
base.define_callbacks *CALLBACKS
|
32
|
+
end
|
33
|
+
|
34
|
+
# This is called before you anything actually starts.
|
35
|
+
#
|
36
|
+
def before_run() end
|
37
|
+
# This is called after everything else finishes.
|
38
|
+
#
|
39
|
+
def after_run() end
|
40
|
+
def run_with_callbacks #:nodoc:
|
41
|
+
callback(:before_run)
|
42
|
+
run_without_callbacks
|
43
|
+
callback(:after_run)
|
44
|
+
end
|
45
|
+
|
46
|
+
# This is called before the iteration of field migrations.
|
47
|
+
#
|
48
|
+
def before_migrate_record() end
|
49
|
+
# This is called after the iteration of field migrations.
|
50
|
+
#
|
51
|
+
def after_migrate_record() end
|
52
|
+
def migrate_record_with_callbacks #:nodoc:
|
53
|
+
callback(:before_migrate_record)
|
54
|
+
migrate_record_without_callbacks
|
55
|
+
callback(:after_migrate_record)
|
56
|
+
end
|
57
|
+
|
58
|
+
# This is called before each field migration.
|
59
|
+
#
|
60
|
+
def before_migrate_field() end
|
61
|
+
# This is called directly after each field migration.
|
62
|
+
#
|
63
|
+
def after_migrate_field() end
|
64
|
+
def migrate_field_with_callbacks#:nodoc:
|
65
|
+
callback(:before_migrate_field)
|
66
|
+
migrate_field_without_callbacks
|
67
|
+
callback(:after_migrate_field)
|
68
|
+
end
|
69
|
+
|
70
|
+
# This is called directly before the active record is saved.
|
71
|
+
#
|
72
|
+
def before_save() end
|
73
|
+
# This is called directly after the active record is saved.
|
74
|
+
#
|
75
|
+
def after_save() end
|
76
|
+
def save_with_callbacks
|
77
|
+
callback(:before_save)
|
78
|
+
save_without_callbacks
|
79
|
+
callback(:after_save)
|
80
|
+
end
|
81
|
+
|
82
|
+
# This is only called before update if active_record_mode is set to :update.
|
83
|
+
#
|
84
|
+
def before_update() end
|
85
|
+
# This is only called after update if active_record_mode is set to :update.
|
86
|
+
#
|
87
|
+
def after_update() end
|
88
|
+
def update_with_callbacks
|
89
|
+
callback(:before_update)
|
90
|
+
update_without_callbacks
|
91
|
+
callback(:after_update)
|
92
|
+
end
|
93
|
+
|
94
|
+
# This is only called before create if active_record_mode is set to :create(default).
|
95
|
+
#
|
96
|
+
def before_create() end
|
97
|
+
# This is only called after update if active_record_mode is set to :create(default).
|
98
|
+
#
|
99
|
+
def after_create() end
|
100
|
+
def create_with_callbacks
|
101
|
+
callback(:before_create)
|
102
|
+
create_without_callbacks
|
103
|
+
callback(:after_create)
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def callback(method) #:nodoc:
|
109
|
+
run_callbacks(method)
|
110
|
+
send(method)
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module ActiveMigration
|
2
|
+
# Dependencies are supported by ActiveMigration in this module. If you set some dependencies
|
3
|
+
# they'll be ran before Base#run is called. Specifying dependencies is easy:
|
4
|
+
#
|
5
|
+
# set_dependencies [:supplier_migration, :manufacturer_migration]
|
6
|
+
#
|
7
|
+
module Dependencies
|
8
|
+
|
9
|
+
def self.included(base)#:nodoc:
|
10
|
+
base.class_eval do
|
11
|
+
alias_method_chain :run, :dependencies
|
12
|
+
class << self
|
13
|
+
attr_accessor :dependencies, :completed
|
14
|
+
# Sets the dependencies for the migration
|
15
|
+
#
|
16
|
+
# set_dependencies [:supplier_migration, :manufacturer_migration]
|
17
|
+
def set_dependencies(dependencies)
|
18
|
+
@dependencies = dependencies
|
19
|
+
end
|
20
|
+
alias dependencies= set_dependencies
|
21
|
+
def completed? #:nodoc:
|
22
|
+
@completed
|
23
|
+
end
|
24
|
+
def is_completed #:nodoc:
|
25
|
+
@completed = true
|
26
|
+
end
|
27
|
+
alias completed completed?
|
28
|
+
def dependencies #:nodoc:
|
29
|
+
@dependencies || []
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def run_with_dependencies(skip_dependencies=false) #:nodoc:
|
36
|
+
if skip_dependencies
|
37
|
+
logger.info("#{self.class.to_s} is skipping dependencies.")
|
38
|
+
run_without_dependencies
|
39
|
+
else
|
40
|
+
self.class.dependencies.each do |dependency|
|
41
|
+
migration = dependency.to_s.camelize.constantize
|
42
|
+
unless migration.completed?
|
43
|
+
logger.info("#{self.class.to_s} is running #{migration.to_s} as a dependency.")
|
44
|
+
migration.new.run
|
45
|
+
migration.is_completed
|
46
|
+
end
|
47
|
+
end
|
48
|
+
run_without_dependencies unless self.class.completed?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|