monarch_migrate 0.4.0 → 0.6.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +13 -2
- data/.gitignore +3 -1
- data/Appraisals +6 -3
- data/Gemfile +8 -0
- data/Gemfile.lock +139 -30
- data/README.md +257 -62
- data/Rakefile +11 -2
- data/lib/generators/monarch_migrate/install/USAGE +1 -1
- data/lib/generators/monarch_migrate/install/install_generator.rb +1 -1
- data/lib/generators/rails/data_migration/USAGE +5 -0
- data/lib/generators/{monarch_migrate → rails}/data_migration/data_migration_generator.rb +3 -1
- data/lib/generators/{monarch_migrate → rails}/data_migration/templates/data_migration.rb.erb +0 -0
- data/lib/generators/rspec/USAGE +12 -0
- data/lib/generators/rspec/data_migration_generator.rb +37 -0
- data/lib/generators/rspec/templates/data_migration_spec.rb.erb +5 -0
- data/lib/generators/test_unit/USAGE +12 -0
- data/lib/generators/test_unit/data_migration_generator.rb +34 -0
- data/lib/generators/test_unit/templates/unit_test.rb.erb +7 -0
- data/lib/monarch_migrate/migration.rb +15 -7
- data/lib/monarch_migrate/migrator.rb +4 -6
- data/lib/monarch_migrate/railtie.rb +2 -0
- data/lib/monarch_migrate/rspec.rb +5 -0
- data/lib/monarch_migrate/tasks.rake +1 -1
- data/lib/monarch_migrate/test_unit.rb +5 -0
- data/lib/monarch_migrate/testing.rb +88 -0
- data/lib/monarch_migrate/version.rb +1 -1
- data/lib/monarch_migrate.rb +1 -0
- data/monarch_migrate.gemspec +1 -8
- metadata +15 -90
- data/lib/generators/monarch_migrate/data_migration/USAGE +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d70afb71a32546dd38ae87d4e39956c97bd095c56cc957266a159edb4cbfc3cb
|
4
|
+
data.tar.gz: 2b95f318d045045dba0cb712bc0aa647d95717688e2cb258702e9bea9c398d6f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f97b549b57586af15371dfadba856b9d506989d210a76ae21cc85e20d0eff097ca58fd26a1901760c8755d0b45be6583bfabc2f9f71e288cd0949d1ab98df150
|
7
|
+
data.tar.gz: 6f773f40eb0028229a75aa58055b3765580261f4c50d21bd01ab64ab5b5f8f001129626f442d022825d9038729a6bdf2519dcff425b31febeed681230257347c
|
data/.github/workflows/ci.yml
CHANGED
@@ -45,5 +45,16 @@ jobs:
|
|
45
45
|
- name: "Reset app database"
|
46
46
|
run: bundle exec rake fake:db:reset
|
47
47
|
|
48
|
-
- name: "Run
|
49
|
-
run:
|
48
|
+
- name: "Run tests without acceptance"
|
49
|
+
run: |
|
50
|
+
bundle exec rake test
|
51
|
+
|
52
|
+
- name: "Run acceptance tests for RSpec"
|
53
|
+
run: |
|
54
|
+
bundle add rspec-rails
|
55
|
+
bundle exec rake TEST=test/acceptance/testing_test.rb TESTOPTS="--name='test_rspec_tests'"
|
56
|
+
|
57
|
+
- name: "Run acceptance tests for TestUnit"
|
58
|
+
run: |
|
59
|
+
bundle remove rspec-rails
|
60
|
+
bundle exec rake TEST=test/acceptance/testing_test.rb TESTOPTS="--name='test_test_unit_tests'"
|
data/.gitignore
CHANGED
@@ -72,11 +72,13 @@ fabric.properties
|
|
72
72
|
.idea/caches/build_file_checksums.ser
|
73
73
|
|
74
74
|
# Migration generated during tests
|
75
|
-
/test/generators
|
75
|
+
/test/generators/**/tmp
|
76
76
|
|
77
77
|
# Ignore gem compile directory
|
78
78
|
/pkg
|
79
79
|
|
80
|
+
/gemfiles/*.lock
|
81
|
+
|
80
82
|
# Ignore bundler config.
|
81
83
|
/.bundle
|
82
84
|
|
data/Appraisals
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
appraise "rails_5.2" do
|
2
|
-
gem "
|
2
|
+
gem "rails", "~> 5.2"
|
3
3
|
end
|
4
4
|
|
5
5
|
appraise "rails_6.1" do
|
6
|
-
gem "
|
6
|
+
gem "rails", "~> 6.1"
|
7
|
+
gem "net-smtp", require: false
|
8
|
+
gem "net-imap", require: false
|
9
|
+
gem "net-pop", require: false
|
7
10
|
end
|
8
11
|
|
9
12
|
appraise "rails_7.0" do
|
10
|
-
gem "
|
13
|
+
gem "rails", "~> 7.0"
|
11
14
|
end
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,32 +1,73 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
monarch_migrate (0.
|
5
|
-
|
6
|
-
railties (>= 5.2.0)
|
4
|
+
monarch_migrate (0.6.0)
|
5
|
+
rails (>= 5.2.0)
|
7
6
|
|
8
7
|
GEM
|
9
8
|
remote: https://rubygems.org/
|
10
9
|
specs:
|
11
|
-
|
12
|
-
|
13
|
-
activesupport (= 7.0.3)
|
10
|
+
actioncable (7.0.3.1)
|
11
|
+
actionpack (= 7.0.3.1)
|
12
|
+
activesupport (= 7.0.3.1)
|
13
|
+
nio4r (~> 2.0)
|
14
|
+
websocket-driver (>= 0.6.1)
|
15
|
+
actionmailbox (7.0.3.1)
|
16
|
+
actionpack (= 7.0.3.1)
|
17
|
+
activejob (= 7.0.3.1)
|
18
|
+
activerecord (= 7.0.3.1)
|
19
|
+
activestorage (= 7.0.3.1)
|
20
|
+
activesupport (= 7.0.3.1)
|
21
|
+
mail (>= 2.7.1)
|
22
|
+
net-imap
|
23
|
+
net-pop
|
24
|
+
net-smtp
|
25
|
+
actionmailer (7.0.3.1)
|
26
|
+
actionpack (= 7.0.3.1)
|
27
|
+
actionview (= 7.0.3.1)
|
28
|
+
activejob (= 7.0.3.1)
|
29
|
+
activesupport (= 7.0.3.1)
|
30
|
+
mail (~> 2.5, >= 2.5.4)
|
31
|
+
net-imap
|
32
|
+
net-pop
|
33
|
+
net-smtp
|
34
|
+
rails-dom-testing (~> 2.0)
|
35
|
+
actionpack (7.0.3.1)
|
36
|
+
actionview (= 7.0.3.1)
|
37
|
+
activesupport (= 7.0.3.1)
|
14
38
|
rack (~> 2.0, >= 2.2.0)
|
15
39
|
rack-test (>= 0.6.3)
|
16
40
|
rails-dom-testing (~> 2.0)
|
17
41
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
18
|
-
|
19
|
-
|
42
|
+
actiontext (7.0.3.1)
|
43
|
+
actionpack (= 7.0.3.1)
|
44
|
+
activerecord (= 7.0.3.1)
|
45
|
+
activestorage (= 7.0.3.1)
|
46
|
+
activesupport (= 7.0.3.1)
|
47
|
+
globalid (>= 0.6.0)
|
48
|
+
nokogiri (>= 1.8.5)
|
49
|
+
actionview (7.0.3.1)
|
50
|
+
activesupport (= 7.0.3.1)
|
20
51
|
builder (~> 3.1)
|
21
52
|
erubi (~> 1.4)
|
22
53
|
rails-dom-testing (~> 2.0)
|
23
54
|
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
24
|
-
|
25
|
-
activesupport (= 7.0.3)
|
26
|
-
|
27
|
-
|
28
|
-
activesupport (= 7.0.3)
|
29
|
-
|
55
|
+
activejob (7.0.3.1)
|
56
|
+
activesupport (= 7.0.3.1)
|
57
|
+
globalid (>= 0.3.6)
|
58
|
+
activemodel (7.0.3.1)
|
59
|
+
activesupport (= 7.0.3.1)
|
60
|
+
activerecord (7.0.3.1)
|
61
|
+
activemodel (= 7.0.3.1)
|
62
|
+
activesupport (= 7.0.3.1)
|
63
|
+
activestorage (7.0.3.1)
|
64
|
+
actionpack (= 7.0.3.1)
|
65
|
+
activejob (= 7.0.3.1)
|
66
|
+
activerecord (= 7.0.3.1)
|
67
|
+
activesupport (= 7.0.3.1)
|
68
|
+
marcel (~> 1.0)
|
69
|
+
mini_mime (>= 1.1.0)
|
70
|
+
activesupport (7.0.3.1)
|
30
71
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
31
72
|
i18n (>= 1.6, < 2)
|
32
73
|
minitest (>= 5.1)
|
@@ -36,42 +77,103 @@ GEM
|
|
36
77
|
rake
|
37
78
|
thor (>= 0.14.0)
|
38
79
|
ast (2.4.2)
|
80
|
+
break (0.40.0)
|
39
81
|
builder (3.2.4)
|
82
|
+
coderay (1.1.3)
|
40
83
|
concurrent-ruby (1.1.10)
|
41
84
|
crass (1.0.6)
|
85
|
+
diff-lcs (1.5.0)
|
86
|
+
digest (3.1.0)
|
42
87
|
erubi (1.10.0)
|
43
|
-
|
88
|
+
globalid (1.0.0)
|
89
|
+
activesupport (>= 5.0)
|
90
|
+
i18n (1.12.0)
|
44
91
|
concurrent-ruby (~> 1.0)
|
45
92
|
loofah (2.18.0)
|
46
93
|
crass (~> 1.0.2)
|
47
94
|
nokogiri (>= 1.5.9)
|
95
|
+
mail (2.7.1)
|
96
|
+
mini_mime (>= 0.1.1)
|
97
|
+
marcel (1.0.2)
|
48
98
|
method_source (1.0.0)
|
49
|
-
|
50
|
-
|
99
|
+
mini_mime (1.1.2)
|
100
|
+
mini_portile2 (2.8.0)
|
101
|
+
minitest (5.16.2)
|
102
|
+
net-imap (0.2.3)
|
103
|
+
digest
|
104
|
+
net-protocol
|
105
|
+
strscan
|
106
|
+
net-pop (0.1.1)
|
107
|
+
digest
|
108
|
+
net-protocol
|
109
|
+
timeout
|
110
|
+
net-protocol (0.1.3)
|
111
|
+
timeout
|
112
|
+
net-smtp (0.3.1)
|
113
|
+
digest
|
114
|
+
net-protocol
|
115
|
+
timeout
|
116
|
+
nio4r (2.5.8)
|
117
|
+
nokogiri (1.13.7)
|
118
|
+
mini_portile2 (~> 2.8.0)
|
51
119
|
racc (~> 1.4)
|
52
120
|
parallel (1.22.1)
|
53
121
|
parser (3.1.2.0)
|
54
122
|
ast (~> 2.4.1)
|
123
|
+
pry (0.14.1)
|
124
|
+
coderay (~> 1.1)
|
125
|
+
method_source (~> 1.0)
|
55
126
|
racc (1.6.0)
|
56
|
-
rack (2.2.
|
57
|
-
rack-test (
|
58
|
-
rack (>= 1.
|
127
|
+
rack (2.2.4)
|
128
|
+
rack-test (2.0.2)
|
129
|
+
rack (>= 1.3)
|
130
|
+
rails (7.0.3.1)
|
131
|
+
actioncable (= 7.0.3.1)
|
132
|
+
actionmailbox (= 7.0.3.1)
|
133
|
+
actionmailer (= 7.0.3.1)
|
134
|
+
actionpack (= 7.0.3.1)
|
135
|
+
actiontext (= 7.0.3.1)
|
136
|
+
actionview (= 7.0.3.1)
|
137
|
+
activejob (= 7.0.3.1)
|
138
|
+
activemodel (= 7.0.3.1)
|
139
|
+
activerecord (= 7.0.3.1)
|
140
|
+
activestorage (= 7.0.3.1)
|
141
|
+
activesupport (= 7.0.3.1)
|
142
|
+
bundler (>= 1.15.0)
|
143
|
+
railties (= 7.0.3.1)
|
59
144
|
rails-dom-testing (2.0.3)
|
60
145
|
activesupport (>= 4.2.0)
|
61
146
|
nokogiri (>= 1.6)
|
62
|
-
rails-html-sanitizer (1.4.
|
147
|
+
rails-html-sanitizer (1.4.3)
|
63
148
|
loofah (~> 2.3)
|
64
|
-
railties (7.0.3)
|
65
|
-
actionpack (= 7.0.3)
|
66
|
-
activesupport (= 7.0.3)
|
149
|
+
railties (7.0.3.1)
|
150
|
+
actionpack (= 7.0.3.1)
|
151
|
+
activesupport (= 7.0.3.1)
|
67
152
|
method_source
|
68
153
|
rake (>= 12.2)
|
69
154
|
thor (~> 1.0)
|
70
155
|
zeitwerk (~> 2.5)
|
71
156
|
rainbow (3.1.1)
|
72
157
|
rake (13.0.6)
|
73
|
-
regexp_parser (2.
|
158
|
+
regexp_parser (2.5.0)
|
74
159
|
rexml (3.2.5)
|
160
|
+
rspec-core (3.11.0)
|
161
|
+
rspec-support (~> 3.11.0)
|
162
|
+
rspec-expectations (3.11.0)
|
163
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
164
|
+
rspec-support (~> 3.11.0)
|
165
|
+
rspec-mocks (3.11.1)
|
166
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
167
|
+
rspec-support (~> 3.11.0)
|
168
|
+
rspec-rails (5.1.2)
|
169
|
+
actionpack (>= 5.2)
|
170
|
+
activesupport (>= 5.2)
|
171
|
+
railties (>= 5.2)
|
172
|
+
rspec-core (~> 3.10)
|
173
|
+
rspec-expectations (~> 3.10)
|
174
|
+
rspec-mocks (~> 3.10)
|
175
|
+
rspec-support (~> 3.10)
|
176
|
+
rspec-support (3.11.0)
|
75
177
|
rubocop (1.29.1)
|
76
178
|
parallel (~> 1.10)
|
77
179
|
parser (>= 3.1.0.0)
|
@@ -81,30 +183,37 @@ GEM
|
|
81
183
|
rubocop-ast (>= 1.17.0, < 2.0)
|
82
184
|
ruby-progressbar (~> 1.7)
|
83
185
|
unicode-display_width (>= 1.4.0, < 3.0)
|
84
|
-
rubocop-ast (1.
|
186
|
+
rubocop-ast (1.19.1)
|
85
187
|
parser (>= 3.1.1.0)
|
86
188
|
rubocop-performance (1.13.3)
|
87
189
|
rubocop (>= 1.7.0, < 2.0)
|
88
190
|
rubocop-ast (>= 0.4.0)
|
89
191
|
ruby-progressbar (1.11.0)
|
90
|
-
sqlite3 (1.4.
|
192
|
+
sqlite3 (1.4.4)
|
91
193
|
standard (1.12.1)
|
92
194
|
rubocop (= 1.29.1)
|
93
195
|
rubocop-performance (= 1.13.3)
|
196
|
+
strscan (3.0.3)
|
94
197
|
thor (1.2.1)
|
198
|
+
timeout (0.3.0)
|
95
199
|
tzinfo (2.0.4)
|
96
200
|
concurrent-ruby (~> 1.0)
|
97
|
-
unicode-display_width (2.
|
98
|
-
|
201
|
+
unicode-display_width (2.2.0)
|
202
|
+
websocket-driver (0.7.5)
|
203
|
+
websocket-extensions (>= 0.1.0)
|
204
|
+
websocket-extensions (0.1.5)
|
205
|
+
zeitwerk (2.6.0)
|
99
206
|
|
100
207
|
PLATFORMS
|
101
208
|
ruby
|
102
209
|
|
103
210
|
DEPENDENCIES
|
104
211
|
appraisal
|
105
|
-
|
212
|
+
break
|
106
213
|
monarch_migrate!
|
214
|
+
pry
|
107
215
|
rake
|
216
|
+
rspec-rails
|
108
217
|
sqlite3
|
109
218
|
standard
|
110
219
|
|
data/README.md
CHANGED
@@ -1,4 +1,31 @@
|
|
1
|
-
#
|
1
|
+
# monarch_migrate [![Build Status][ci-image]][ci] [![Gem Version][version-image]][version]
|
2
|
+
|
3
|
+
A library for Rails developers who are not willing to leave data migrations to chance.
|
4
|
+
|
5
|
+
<br />
|
6
|
+
|
7
|
+
## Table of Contents
|
8
|
+
|
9
|
+
- [Rationale](#rationale)
|
10
|
+
- [Install](#install)
|
11
|
+
- [Usage](#usage)
|
12
|
+
- [Create a Data Migration](#create-a-data-migration)
|
13
|
+
- [Running Data Migrations](#running-data-migrations)
|
14
|
+
- [Display Status of Data Migrations](#display-status-of-data-migrations)
|
15
|
+
- [Reverting Data Migrations](#reverting-data-migrations)
|
16
|
+
- [Background Tasks in Data Migrations](#background-tasks-in-data-migrations)
|
17
|
+
- [Testing](#testing)
|
18
|
+
- [RSpec](#rspec)
|
19
|
+
- [TestUnit](#testunit)
|
20
|
+
- [Suggested Workflow](#suggested-workflow)
|
21
|
+
- [Known Issues and Limitations](#known-issues-and-limitations)
|
22
|
+
- [Trivia](#trivia)
|
23
|
+
- [Acknowledgments](#acknowledgments)
|
24
|
+
- [License](#license)
|
25
|
+
|
26
|
+
|
27
|
+
## Rationale
|
28
|
+
<sup>[(Back to top)](#table-of-contents)</sup>
|
2
29
|
|
3
30
|
<blockquote>
|
4
31
|
<p>The main purpose of Rails' migration feature is to issue commands that modify the schema using a consistent process. Migrations can also be used to add or modify data. This is useful in an existing database that can't be destroyed and recreated, such as a production database.</p>
|
@@ -7,31 +34,35 @@
|
|
7
34
|
</a>
|
8
35
|
</blockquote>
|
9
36
|
|
10
|
-
The motivation behind Rails' migration mechanism is schema modification.
|
11
|
-
for data changes in the database comes second.
|
12
|
-
|
13
|
-
Yet, adding of modifying data via regular migrations can be problematic.
|
37
|
+
The motivation behind Rails' migration mechanism is schema modification.
|
38
|
+
Using it for data changes in the database comes second. Yet, adding or
|
39
|
+
modifying data via regular migrations can be problematic.
|
14
40
|
|
15
|
-
|
41
|
+
One issue is that application deployment now depends on the data migration
|
16
42
|
to be completed. This may not be a problem with small databases but large
|
17
43
|
databases with millions of records will respond with hanging or failed migrations.
|
18
44
|
|
19
|
-
Another issue is that data migration files
|
45
|
+
Another issue is that data migration files tend to stay in `db/migrate` for posterity.
|
20
46
|
As a result, they will run whenever a developer sets up their local development environment.
|
21
|
-
This is unnecessary for a pristine database. Especially
|
47
|
+
This is unnecessary for a pristine database. Especially with [scripts][seed-scripts] to
|
22
48
|
seed the correct data.
|
23
49
|
|
24
|
-
|
50
|
+
The purpose of `monarch_migrate` is to solve the above issues and to:
|
25
51
|
|
26
|
-
|
27
|
-
from schema migrations.
|
52
|
+
- Provide a [uniform process](#suggested-workflow) for modifying data in the database.
|
53
|
+
- Separate data migrations from schema migrations.
|
54
|
+
- Allow automated testing of data migrations.
|
28
55
|
|
29
|
-
|
56
|
+
It assumes that:
|
30
57
|
|
31
|
-
- You run data migrations
|
58
|
+
- You run data migrations only on production and rely on seed [scripts][seed-scripts] for local development.
|
32
59
|
- You run data migrations manually.
|
60
|
+
- You want to test data migrations in a thorough and automated manner.
|
61
|
+
|
62
|
+
|
33
63
|
|
34
64
|
## Install
|
65
|
+
<sup>[(Back to top)](#table-of-contents)</sup>
|
35
66
|
|
36
67
|
Add the gem to your Gemfile:
|
37
68
|
|
@@ -41,40 +72,51 @@ gem "monarch_migrate"
|
|
41
72
|
|
42
73
|
Run the bundle command to install it.
|
43
74
|
|
44
|
-
After you install
|
75
|
+
After you install the gem, you need to run the generator:
|
45
76
|
|
46
77
|
```shell
|
47
78
|
rails generate monarch_migrate:install
|
48
79
|
```
|
49
80
|
|
50
|
-
The
|
51
|
-
of
|
81
|
+
The install generator creates a schema migration file that adds a `data_migration_records`
|
82
|
+
table. It is where the gem keeps track of data migrations we have ran.
|
83
|
+
|
84
|
+
Run the schema migration to create the table:
|
85
|
+
|
86
|
+
```shell
|
87
|
+
rails db:migrate
|
88
|
+
```
|
89
|
+
|
52
90
|
|
53
91
|
|
54
92
|
## Usage
|
93
|
+
<sup>[(Back to top)](#table-of-contents)</sup>
|
55
94
|
|
56
|
-
Data migrations
|
57
|
-
|
95
|
+
Data migrations have a similar structure to regular migrations in Rails.
|
96
|
+
Files follow the same naming pattern but reside in `db/data_migrate`.
|
58
97
|
|
59
|
-
|
98
|
+
|
99
|
+
### Create a Data Migration
|
60
100
|
|
61
101
|
```shell
|
62
|
-
rails generate
|
102
|
+
rails generate data_migration backfill_users_name
|
63
103
|
```
|
64
104
|
|
65
|
-
|
105
|
+
Edit the newly created file to define the data migration:
|
66
106
|
|
67
107
|
```ruby
|
68
|
-
# db/data_migrate/
|
108
|
+
# db/data_migrate/20220605083010_backfill_users_name.rb
|
69
109
|
ActiveRecord::Base.connection.execute(<<-SQL)
|
70
|
-
UPDATE users SET
|
110
|
+
UPDATE users SET name = concat(first_name, ' ', last_name) WHERE name IS NULL;
|
71
111
|
SQL
|
72
|
-
|
73
|
-
SearchIndex.rebuild
|
74
112
|
```
|
75
113
|
|
76
|
-
|
77
|
-
|
114
|
+
In contrast to regular migrations, there is no need to inherit any classes. It is plain
|
115
|
+
ruby code where you can refer to any object you need. If the database adapter supports
|
116
|
+
transactions, each data migration will automatically be wrapped in a separate transaction.
|
117
|
+
|
118
|
+
|
119
|
+
### Running Data Migrations
|
78
120
|
|
79
121
|
To run pending data migrations:
|
80
122
|
|
@@ -82,18 +124,175 @@ To run pending data migrations:
|
|
82
124
|
rails data:migrate
|
83
125
|
```
|
84
126
|
|
85
|
-
|
127
|
+
To run a specific version:
|
86
128
|
|
87
129
|
```shell
|
88
130
|
rails data:migrate VERSION=20220605083010
|
89
131
|
```
|
90
132
|
|
133
|
+
|
134
|
+
### Display Status of Data Migrations
|
135
|
+
|
136
|
+
You can see the status of data migrations with:
|
137
|
+
|
138
|
+
```shell
|
139
|
+
rails data:migrate:status
|
140
|
+
```
|
141
|
+
|
142
|
+
### Reverting Data Migrations
|
143
|
+
|
144
|
+
Rollback functionality is not provided by design. Create another data migration instead.
|
145
|
+
|
146
|
+
|
147
|
+
### Background Tasks in Data Migrations
|
148
|
+
|
149
|
+
After the data manipulation is complete, you may want to trigger a long running task.
|
150
|
+
Yet, there are [some pitfalls](#long-running-tasks-in-migrations) to be aware of.
|
151
|
+
|
152
|
+
To avoid them, use an after-commit callback:
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
# db/data_migrate/20220605083010_backfill_users_name.rb
|
156
|
+
ActiveRecord::Base.connection.execute(<<-SQL)
|
157
|
+
UPDATE users SET name = concat(first_name, ' ', last_name) WHERE name IS NULL;
|
158
|
+
SQL
|
159
|
+
|
160
|
+
after_commit do
|
161
|
+
SearchIndex::RebuildJob.perform_later
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
|
166
|
+
## Testing
|
167
|
+
<sup>[(Back to top)](#table-of-contents)</sup>
|
168
|
+
|
169
|
+
Testing data migrations can be the difference between rerunning
|
170
|
+
the migration and having to recover from a data loss. This is
|
171
|
+
why `monarch_migrate` includes test helpers for both RSpec and TestUnit.
|
172
|
+
|
173
|
+
|
174
|
+
### RSpec
|
175
|
+
|
176
|
+
For `rspec`, add the following line to your `spec/rails_helper.rb`:
|
177
|
+
|
178
|
+
```ruby
|
179
|
+
require "monarch_migrate/rspec"
|
180
|
+
```
|
181
|
+
|
182
|
+
Then:
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
# spec/data_migrations/20220605083010_backfill_users_name_spec.rb
|
186
|
+
describe "20220605083010_backfill_users_name", type: :data_migration do
|
187
|
+
subject { run_data_migration }
|
188
|
+
|
189
|
+
it "assigns user's name" do
|
190
|
+
user = users(:without_name_migrated)
|
191
|
+
# or if you're using FactoryBot:
|
192
|
+
# user = create(:user, first_name: "Guybrush", last_name: "Threepwood", name: nil)
|
193
|
+
|
194
|
+
expect { subject }.to change { user.reload.name }.to("Guybrush Threepwood")
|
195
|
+
end
|
196
|
+
|
197
|
+
it "does not assign name to already migrated users" do
|
198
|
+
user = users(:with_name_migrated)
|
199
|
+
# or if you're using FactoryBot:
|
200
|
+
# user = create(:user, first_name: "", last_name: "", name: "Guybrush Threepwood")
|
201
|
+
|
202
|
+
expect { subject }.not_to change { user.reload.name }
|
203
|
+
end
|
204
|
+
|
205
|
+
context "when the user has no last name" do
|
206
|
+
it "does not leave a trailing space" do
|
207
|
+
user = users(:without_name_migrated)
|
208
|
+
# or if you're using FactoryBot:
|
209
|
+
# user = create(:user, first_name: "Guybrush", last_name: nil, name: nil)
|
210
|
+
|
211
|
+
expect { subject }.to change { user.reload.name }.to("Guybrush")
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# And so on ...
|
216
|
+
end
|
217
|
+
```
|
218
|
+
|
219
|
+
### TestUnit
|
220
|
+
|
221
|
+
For `test_unit`, add this line to your `test/test_helper.rb`:
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
require "monarch_migrate/test_unit"
|
225
|
+
```
|
226
|
+
|
227
|
+
Then:
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
# test/data_migrations/20220605083010_backfill_users_name_test.rb
|
231
|
+
class BackfillUsersNameTest < MonarchMigrate::TestCase
|
232
|
+
def test_assigns_users_name
|
233
|
+
user = users(:without_name_migrated)
|
234
|
+
# or if you're using FactoryBot:
|
235
|
+
# user = create(:user, first_name: "Guybrush", last_name: "Threepwood", name: nil)
|
236
|
+
|
237
|
+
run_data_migration
|
238
|
+
|
239
|
+
assert_equal "Guybrush Threepwood", user.reload.name
|
240
|
+
end
|
241
|
+
|
242
|
+
def test_does_not_assign_name_to_alredy_migrated_users
|
243
|
+
user = users(:with_name_migrated)
|
244
|
+
# or if you're using FactoryBot:
|
245
|
+
# user = create(:user, first_name: "", last_name: "", name: "Guybrush Threepwood")
|
246
|
+
|
247
|
+
run_data_migration
|
248
|
+
|
249
|
+
assert_equal "Guybrush Threepwood", user.reload.name
|
250
|
+
end
|
251
|
+
|
252
|
+
def test_does_not_leave_trailing_space_when_user_has_no_last_name
|
253
|
+
user = users(:without_name_migrated)
|
254
|
+
# or if you're using FactoryBot:
|
255
|
+
# user = create(:user, first_name: "Guybrush", last_name: nil, name: nil)
|
256
|
+
|
257
|
+
run_data_migration
|
258
|
+
|
259
|
+
assert_equal "Guybrush", user.reload.name
|
260
|
+
end
|
261
|
+
|
262
|
+
# And so on ...
|
263
|
+
end
|
264
|
+
```
|
265
|
+
|
266
|
+
In certain cases, it makes sense to also test with manually running the data migration
|
267
|
+
against a production clone of the database.
|
268
|
+
|
269
|
+
|
270
|
+
|
271
|
+
## Suggested Workflow
|
272
|
+
<sup>[(Back to top)](#table-of-contents)</sup>
|
273
|
+
|
274
|
+
Data migrations become obsolete, once the data manipulation successfully completes. The same applies for the corresponding tests.
|
275
|
+
|
276
|
+
The suggested development workflow is:
|
277
|
+
|
278
|
+
1. Implement the data migration. Use TDD, if appropriate.
|
279
|
+
1. Commit, push and wait for CI to pass.
|
280
|
+
1. Request review from peers.
|
281
|
+
1. Once approved, remove the test files in a consecutive commit and push again.
|
282
|
+
1. Merge into trunk.
|
283
|
+
|
284
|
+
This will keep the test files in repo's history for posterity.
|
285
|
+
|
286
|
+
|
287
|
+
|
91
288
|
## Known Issues and Limitations
|
289
|
+
<sup>[(Back to top)](#table-of-contents)</sup>
|
92
290
|
|
93
|
-
The following issues and limitations are not necessary inherent to
|
291
|
+
The following issues and limitations are not necessary inherent to `monarch_migrate`.
|
94
292
|
Some are innate to migrations in general.
|
95
293
|
|
96
|
-
|
294
|
+
|
295
|
+
### Using Models in Migrations
|
97
296
|
|
98
297
|
<blockquote>
|
99
298
|
<p>The Active Record way claims that intelligence belongs in your models, not in the database.</p>
|
@@ -106,15 +305,7 @@ Typically, data migrations relate closely to business models. In an ideal Rails
|
|
106
305
|
data manipulations would depend on model logic to enforce validation, conform to
|
107
306
|
business rules, etc. Hence, it is very tempting to use ActiveRecord models in migrations.
|
108
307
|
|
109
|
-
|
110
|
-
it becomes clear this is [wrong][3]. Now, we want to put things right and come
|
111
|
-
up with the following plan:
|
112
|
-
|
113
|
-
1. Add a `name` column to `users` table to hold the entire name of a person.
|
114
|
-
2. Change the `User` model and use a data migration to update existing records.
|
115
|
-
3. Drop `first_name` and `last_name` columns.
|
116
|
-
|
117
|
-
A regular Rails migration for updating existing records may look something like this:
|
308
|
+
Here a regular Rails migration:
|
118
309
|
|
119
310
|
```ruby
|
120
311
|
# db/migrate/20220605083010_backfill_users_name.rb
|
@@ -147,33 +338,28 @@ end
|
|
147
338
|
Unfortunately, with regular Rails migrations we will still face issue 4.
|
148
339
|
|
149
340
|
To avoid it, we need to separate data from schema migrations and not run data
|
150
|
-
migrations locally. With seed [scripts][
|
341
|
+
migrations locally. With seed [scripts][seed-scripts], there is no need to run them anyway.
|
151
342
|
|
152
343
|
Keep the above in mind when referencing ActiveRecord models in data migrations. Ideally,
|
153
|
-
limit their use and do as much processing as possible in
|
344
|
+
limit their use and do as much processing as possible in the database.
|
154
345
|
|
155
346
|
|
156
347
|
### Long-running Tasks in Migrations
|
157
348
|
|
158
|
-
As mentioned
|
159
|
-
|
349
|
+
As mentioned, each data migration runs in a separate transaction.
|
350
|
+
A long-running task within a migration keeps the transaction open for
|
160
351
|
the duration of the task. As a result, the migration may hang or fail.
|
161
352
|
|
162
|
-
|
353
|
+
You could run such tasks asynchronously (i.e. in a background job) but the task may start
|
354
|
+
before the transaction commits. This is a [known issue][sync-issue]. In addition,
|
355
|
+
a database under load can suffer from longer commit times.
|
163
356
|
|
164
|
-
|
357
|
+
See [Background Tasks in Data Migrations](#background-tasks-in-data-migrations).
|
165
358
|
|
166
|
-
```ruby
|
167
|
-
# db/data_migrate/20220605083010_downcase_usernames.rb
|
168
|
-
ActiveRecord::Base.connection.execute(<<-SQL)
|
169
|
-
UPDATE users SET username = lower(username);
|
170
|
-
SQL
|
171
|
-
|
172
|
-
SearchIndex::RebuildJob.perform_later
|
173
|
-
```
|
174
359
|
|
175
360
|
|
176
361
|
## Trivia
|
362
|
+
<sup>[(Back to top)](#table-of-contents)</sup>
|
177
363
|
|
178
364
|
One of the most impressive migrations on Earth is the multi-generational
|
179
365
|
round trip of the monarch butterfly.
|
@@ -185,19 +371,16 @@ The population cycles through three to five generations to reach their
|
|
185
371
|
destination. In the end, a new generation of butterflies complete the
|
186
372
|
journey their great-great-great-grandparents started.
|
187
373
|
|
188
|
-
It is
|
374
|
+
It is a mystery to scientists how the new generations know where to go,
|
189
375
|
but they appear to navigate using a combination of the Earth's magnetic field
|
190
376
|
and the position of the sun.
|
191
377
|
|
192
|
-
Genetically speaking, this is
|
378
|
+
Genetically speaking, this is an incredible data migration!
|
193
379
|
|
194
380
|
|
195
|
-
## See Also
|
196
381
|
|
197
|
-
|
198
|
-
|
199
|
-
- [nonschema_migrations](https://github.com/jasonfb/nonschema_migrations) - Exactly like schema migrations but for data.
|
200
|
-
- [data-migrate](https://github.com/ilyakatz/data-migrate) - A gem to run data migrations alongside schema migrations.
|
382
|
+
## Acknowledgments
|
383
|
+
<sup>[(Back to top)](#table-of-contents)</sup>
|
201
384
|
|
202
385
|
Articles
|
203
386
|
|
@@ -207,10 +390,22 @@ Articles
|
|
207
390
|
- [Ruby on Rails Model Patterns and Anti-patterns](https://blog.appsignal.com/2020/11/18/rails-model-patterns-and-anti-patterns.html)
|
208
391
|
- [Rails Migrations with Zero Downtime](https://www.cloudbees.com/blog/rails-migrations-zero-downtime)
|
209
392
|
|
393
|
+
Alternative gems
|
394
|
+
|
395
|
+
- https://github.com/OffgridElectric/rails-data-migrations
|
396
|
+
- https://github.com/ilyakatz/data-migrate
|
397
|
+
- https://github.com/jasonfb/nonschema_migrations
|
398
|
+
|
399
|
+
|
210
400
|
|
211
401
|
## License
|
402
|
+
<sup>[(Back to top)](#table-of-contents)</sup>
|
212
403
|
|
213
404
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
214
405
|
|
215
|
-
[
|
216
|
-
[
|
406
|
+
[ci-image]: https://github.com/lunohodov/monarch/actions/workflows/ci.yml/badge.svg
|
407
|
+
[ci]: https://github.com/lunohodov/monarch/actions/workflows/ci.yml
|
408
|
+
[seed-scripts]: https://thoughtbot.com/blog/priming-the-pump
|
409
|
+
[sync-issue]: https://github.com/mperham/sidekiq/wiki/FAQ#why-am-i-seeing-a-lot-of-cant-find-modelname-with-id12345-errors-with-sidekiq
|
410
|
+
[version-image]: https://badge.fury.io/rb/monarch_migrate.svg
|
411
|
+
[version]: https://badge.fury.io/rb/monarch_migrate
|
data/Rakefile
CHANGED
@@ -1,15 +1,24 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require "rake/testtask"
|
3
|
+
require "standard/rake"
|
3
4
|
|
4
5
|
namespace :fake do
|
5
6
|
require_relative "test/fake/application"
|
6
7
|
Fake::Application.load_tasks
|
7
8
|
end
|
8
9
|
|
9
|
-
Rake::TestTask.new(
|
10
|
+
Rake::TestTask.new("test") do |t|
|
10
11
|
t.libs << "test"
|
11
12
|
t.libs << "lib"
|
12
13
|
t.test_files = FileList["test/**/*_test.rb"]
|
14
|
+
.exclude("test/acceptance/**/*_test.rb")
|
15
|
+
.exclude("test/fixtures/**/*_test.rb")
|
13
16
|
end
|
14
17
|
|
15
|
-
|
18
|
+
Rake::TestTask.new("test:acceptance") do |t|
|
19
|
+
t.libs << "test"
|
20
|
+
t.libs << "lib"
|
21
|
+
t.test_files = FileList["test/acceptance/*_test.rb"]
|
22
|
+
end
|
23
|
+
|
24
|
+
task default: %w[test test:acceptance]
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require "rails/generators/active_record"
|
2
2
|
|
3
|
-
module
|
3
|
+
module Rails
|
4
4
|
module Generators
|
5
5
|
class DataMigrationGenerator < Rails::Generators::NamedBase
|
6
6
|
include ActiveRecord::Generators::Migration
|
@@ -16,6 +16,8 @@ module MonarchMigrate
|
|
16
16
|
)
|
17
17
|
end
|
18
18
|
|
19
|
+
hook_for :test_framework, as: :data_migration
|
20
|
+
|
19
21
|
private
|
20
22
|
|
21
23
|
def validate_file_name!
|
data/lib/generators/{monarch_migrate → rails}/data_migration/templates/data_migration.rb.erb
RENAMED
File without changes
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Description:
|
2
|
+
Generate a spec for existing data migration.
|
3
|
+
|
4
|
+
Examples:
|
5
|
+
Given a data migration `db/data_migrate/200010101011_downcase_users_name.rb`:
|
6
|
+
rails generate rspec:data_migration 200010101011_downcase_users_name
|
7
|
+
|
8
|
+
Alternatively, you can skip the version:
|
9
|
+
rails generate rspec:data_migration downcase_users_name
|
10
|
+
|
11
|
+
This will create:
|
12
|
+
spec/data_migrations/200010101011_downcase_users_name_spec.rb
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Rspec
|
2
|
+
module Generators
|
3
|
+
class DataMigrationGenerator < ::Rails::Generators::NamedBase
|
4
|
+
source_root File.expand_path("templates", __dir__)
|
5
|
+
|
6
|
+
def create_data_migration_test
|
7
|
+
unless data_migration
|
8
|
+
say "Expecting a data migration matching *#{data_migration_pattern} but none found. Aborting..."
|
9
|
+
return
|
10
|
+
end
|
11
|
+
|
12
|
+
template(
|
13
|
+
"data_migration_spec.rb.erb",
|
14
|
+
File.join("spec/data_migrations", "#{described_class}_spec.rb")
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def described_class
|
21
|
+
File.basename(data_migration.filename, ".rb")
|
22
|
+
end
|
23
|
+
|
24
|
+
def data_migration_pattern
|
25
|
+
"#{file_name}.rb"
|
26
|
+
end
|
27
|
+
|
28
|
+
def data_migration
|
29
|
+
@data_migration ||=
|
30
|
+
MonarchMigrate.migrator
|
31
|
+
.migrations
|
32
|
+
.reverse
|
33
|
+
.find { |m| m.filename.ends_with?(data_migration_pattern) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Description:
|
2
|
+
Generate a spec for existing data migration.
|
3
|
+
|
4
|
+
Examples:
|
5
|
+
Given a data migration `db/data_migrate/200010101011_downcase_users_name.rb`:
|
6
|
+
rails generate test_unit:data_migration 200010101011_downcase_users_name
|
7
|
+
|
8
|
+
Alternatively, you can skip the version:
|
9
|
+
rails generate test_unit:data_migration downcase_users_name
|
10
|
+
|
11
|
+
This will create:
|
12
|
+
test/data_migrations/200010101011_downcase_users_name_test.rb
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module TestUnit
|
2
|
+
module Generators
|
3
|
+
class DataMigrationGenerator < ::Rails::Generators::NamedBase
|
4
|
+
source_root File.expand_path("templates", __dir__)
|
5
|
+
|
6
|
+
check_class_collision suffix: "Test"
|
7
|
+
|
8
|
+
def create_data_migration_test
|
9
|
+
unless data_migration
|
10
|
+
say "Expecting a data migration matching *#{data_migration_pattern} but none found. Aborting..."
|
11
|
+
return
|
12
|
+
end
|
13
|
+
|
14
|
+
prefix = File.basename(data_migration.filename, ".rb")
|
15
|
+
|
16
|
+
template "unit_test.rb.erb", File.join("test/data_migrations", "#{prefix}_test.rb")
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def data_migration_pattern
|
22
|
+
"#{file_name}.rb"
|
23
|
+
end
|
24
|
+
|
25
|
+
def data_migration
|
26
|
+
@data_migration ||=
|
27
|
+
MonarchMigrate.migrator
|
28
|
+
.migrations
|
29
|
+
.reverse
|
30
|
+
.find { |m| m.filename.ends_with?(data_migration_pattern) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -4,6 +4,7 @@ module MonarchMigrate
|
|
4
4
|
class Migration
|
5
5
|
def initialize(path)
|
6
6
|
@path = path.to_s
|
7
|
+
@after_commit_callback = nil
|
7
8
|
end
|
8
9
|
|
9
10
|
def filename
|
@@ -22,27 +23,34 @@ module MonarchMigrate
|
|
22
23
|
!MigrationRecord.exists?(version: version)
|
23
24
|
end
|
24
25
|
|
25
|
-
def
|
26
|
-
|
26
|
+
def after_commit(&block)
|
27
|
+
@after_commit_callback = block
|
28
|
+
end
|
27
29
|
|
30
|
+
def run
|
28
31
|
ActiveRecord::Base.connection.transaction do
|
29
|
-
|
32
|
+
puts "Running data migration #{version}: #{name}"
|
30
33
|
|
31
34
|
begin
|
32
35
|
instance_eval File.read(path), path
|
33
36
|
MigrationRecord.create!(version: version)
|
34
|
-
|
37
|
+
puts "Migration complete"
|
35
38
|
rescue => e
|
36
|
-
|
37
|
-
|
39
|
+
puts "Migration failed due to #{e}"
|
40
|
+
# Deliberately raising ActiveRecord::Rollback does not
|
41
|
+
# pass on the exception and the callback will be triggered
|
42
|
+
raise
|
38
43
|
end
|
39
44
|
|
40
|
-
|
45
|
+
puts
|
41
46
|
end
|
47
|
+
|
48
|
+
after_commit_callback&.call
|
42
49
|
end
|
43
50
|
|
44
51
|
private
|
45
52
|
|
46
53
|
attr_reader :path
|
54
|
+
attr_reader :after_commit_callback
|
47
55
|
end
|
48
56
|
end
|
@@ -20,14 +20,12 @@ module MonarchMigrate
|
|
20
20
|
migrations.select(&:pending?)
|
21
21
|
end
|
22
22
|
|
23
|
-
def run
|
24
|
-
io ||= File.open(File::NULL, "w")
|
25
|
-
|
23
|
+
def run
|
26
24
|
if pending_migrations.any?
|
27
|
-
|
28
|
-
pending_migrations.sort_by(&:version).each
|
25
|
+
puts "Running #{pending_migrations.size} data migrations"
|
26
|
+
pending_migrations.sort_by(&:version).each(&:run)
|
29
27
|
else
|
30
|
-
|
28
|
+
puts "No data migrations pending"
|
31
29
|
[]
|
32
30
|
end
|
33
31
|
end
|
@@ -3,7 +3,7 @@ require "monarch_migrate"
|
|
3
3
|
namespace :data do
|
4
4
|
desc "Run pending data migrations, or a single version specified by environment variable VERSION"
|
5
5
|
task migrate: :environment do
|
6
|
-
MonarchMigrate.migrator.run
|
6
|
+
MonarchMigrate.migrator.run
|
7
7
|
end
|
8
8
|
|
9
9
|
namespace :migrate do
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require "active_support/core_ext/string"
|
2
|
+
require "active_support/testing/stream"
|
3
|
+
|
4
|
+
module MonarchMigrate
|
5
|
+
module Testing
|
6
|
+
MigrationRunError = Class.new(RuntimeError)
|
7
|
+
|
8
|
+
module Helpers
|
9
|
+
include ::ActiveSupport::Testing::Stream
|
10
|
+
|
11
|
+
def data_migration
|
12
|
+
@data_migration ||=
|
13
|
+
begin
|
14
|
+
filename = data_migration_basename
|
15
|
+
migrator
|
16
|
+
.migrations
|
17
|
+
.find(method(:not_found)) { |m| m.filename.ends_with?(filename) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def run_data_migration
|
22
|
+
output = capture(:stdout) { data_migration.run }
|
23
|
+
ensure_no_error(output)
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def data_migration_basename
|
29
|
+
raise NotImplementedError
|
30
|
+
end
|
31
|
+
|
32
|
+
def data_migration_failed(message)
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
def migrator
|
37
|
+
::MonarchMigrate.migrator
|
38
|
+
end
|
39
|
+
|
40
|
+
def not_found
|
41
|
+
path = File.join(migrator.path, "*_#{data_migration_basename}")
|
42
|
+
raise <<~MSG
|
43
|
+
\n\r
|
44
|
+
Can not find data migration at path: #{path}
|
45
|
+
\n\r
|
46
|
+
MSG
|
47
|
+
end
|
48
|
+
|
49
|
+
def ensure_no_error(str)
|
50
|
+
if %r{Migration failed}.match?(str)
|
51
|
+
data_migration_failed(<<-CMD)
|
52
|
+
Failed running data migration: #{data_migration.filename}
|
53
|
+
\n\r
|
54
|
+
Output:
|
55
|
+
\n\r
|
56
|
+
\n\r
|
57
|
+
#{str}
|
58
|
+
CMD
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
module RSpec
|
64
|
+
include Helpers
|
65
|
+
|
66
|
+
def data_migration_basename
|
67
|
+
spec_path = ::RSpec.current_example.metadata[:file_path]
|
68
|
+
File.basename(spec_path).sub(/_spec\.rb$/, ".rb")
|
69
|
+
end
|
70
|
+
|
71
|
+
def data_migration_failed(message)
|
72
|
+
fail message
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
module TestUnit
|
77
|
+
include Helpers
|
78
|
+
|
79
|
+
def data_migration_basename
|
80
|
+
File.basename(self.class.name.underscore).sub(/_test$/, ".rb")
|
81
|
+
end
|
82
|
+
|
83
|
+
def data_migration_failed(message)
|
84
|
+
flunk message
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/lib/monarch_migrate.rb
CHANGED
data/monarch_migrate.gemspec
CHANGED
@@ -20,12 +20,5 @@ Gem::Specification.new do |spec|
|
|
20
20
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|gemfiles|tmp)/}) }
|
21
21
|
end
|
22
22
|
|
23
|
-
spec.add_dependency("
|
24
|
-
spec.add_dependency("railties", ">= 5.2.0")
|
25
|
-
|
26
|
-
spec.add_development_dependency("appraisal")
|
27
|
-
spec.add_development_dependency("minitest")
|
28
|
-
spec.add_development_dependency("rake")
|
29
|
-
spec.add_development_dependency("sqlite3")
|
30
|
-
spec.add_development_dependency("standard")
|
23
|
+
spec.add_dependency("rails", ">= 5.2.0")
|
31
24
|
end
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: monarch_migrate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yanko Ivanov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: rails
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -24,90 +24,6 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 5.2.0
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: railties
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 5.2.0
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: 5.2.0
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: appraisal
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: minitest
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: rake
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: sqlite3
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: standard
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
27
|
description:
|
112
28
|
email:
|
113
29
|
- yanko.ivanov@gmail.com
|
@@ -126,18 +42,27 @@ files:
|
|
126
42
|
- Rakefile
|
127
43
|
- bin/setup
|
128
44
|
- db/schema.rb
|
129
|
-
- lib/generators/monarch_migrate/data_migration/USAGE
|
130
|
-
- lib/generators/monarch_migrate/data_migration/data_migration_generator.rb
|
131
|
-
- lib/generators/monarch_migrate/data_migration/templates/data_migration.rb.erb
|
132
45
|
- lib/generators/monarch_migrate/install/USAGE
|
133
46
|
- lib/generators/monarch_migrate/install/install_generator.rb
|
134
47
|
- lib/generators/monarch_migrate/install/templates/create_data_migration_records.rb.erb
|
48
|
+
- lib/generators/rails/data_migration/USAGE
|
49
|
+
- lib/generators/rails/data_migration/data_migration_generator.rb
|
50
|
+
- lib/generators/rails/data_migration/templates/data_migration.rb.erb
|
51
|
+
- lib/generators/rspec/USAGE
|
52
|
+
- lib/generators/rspec/data_migration_generator.rb
|
53
|
+
- lib/generators/rspec/templates/data_migration_spec.rb.erb
|
54
|
+
- lib/generators/test_unit/USAGE
|
55
|
+
- lib/generators/test_unit/data_migration_generator.rb
|
56
|
+
- lib/generators/test_unit/templates/unit_test.rb.erb
|
135
57
|
- lib/monarch_migrate.rb
|
136
58
|
- lib/monarch_migrate/migration.rb
|
137
59
|
- lib/monarch_migrate/migration_record.rb
|
138
60
|
- lib/monarch_migrate/migrator.rb
|
139
61
|
- lib/monarch_migrate/railtie.rb
|
62
|
+
- lib/monarch_migrate/rspec.rb
|
140
63
|
- lib/monarch_migrate/tasks.rake
|
64
|
+
- lib/monarch_migrate/test_unit.rb
|
65
|
+
- lib/monarch_migrate/testing.rb
|
141
66
|
- lib/monarch_migrate/version.rb
|
142
67
|
- monarch_migrate.gemspec
|
143
68
|
homepage: https://github.com/lunohodov/monarch
|