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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b59ad47f996564cc02e0577133692329e9a3b7052e2b0e0268c713cbf44b563
4
- data.tar.gz: 98ab757d6e2fcdba24451c5f1816555e7ba6d65ba5c47d1dc0ab4cc94d562a7d
3
+ metadata.gz: d70afb71a32546dd38ae87d4e39956c97bd095c56cc957266a159edb4cbfc3cb
4
+ data.tar.gz: 2b95f318d045045dba0cb712bc0aa647d95717688e2cb258702e9bea9c398d6f
5
5
  SHA512:
6
- metadata.gz: 296572f26ef2ac623e3409c4048f18ada537588100ada9b42caf08d73d9bd251c5ff9111b1079de512ff95892c3263177393268b860a6624617849f4c15ca49a
7
- data.tar.gz: bb9a83dfb41ce4649ae45b58d4aaea1faeb9e47057f10b0161926a472164f15a2485faab8c4448058391f3daf5a1cd19c5c26cad8dd8a4377a7204f93bb4dd18
6
+ metadata.gz: f97b549b57586af15371dfadba856b9d506989d210a76ae21cc85e20d0eff097ca58fd26a1901760c8755d0b45be6583bfabc2f9f71e288cd0949d1ab98df150
7
+ data.tar.gz: 6f773f40eb0028229a75aa58055b3765580261f4c50d21bd01ab64ab5b5f8f001129626f442d022825d9038729a6bdf2519dcff425b31febeed681230257347c
@@ -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 test"
49
- run: bundle exec rake
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/monarch_migrate/tmp
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 "railties", "~> 5.2"
2
+ gem "rails", "~> 5.2"
3
3
  end
4
4
 
5
5
  appraise "rails_6.1" do
6
- gem "railties", "~> 6.1"
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 "railties", "~> 7.0"
13
+ gem "rails", "~> 7.0"
11
14
  end
data/Gemfile CHANGED
@@ -2,3 +2,11 @@ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in *.gemspec
4
4
  gemspec name: "monarch_migrate"
5
+
6
+ gem "appraisal"
7
+ gem "break"
8
+ gem "pry", require: false
9
+ gem "rake"
10
+ gem "rspec-rails"
11
+ gem "sqlite3"
12
+ gem "standard"
data/Gemfile.lock CHANGED
@@ -1,32 +1,73 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- monarch_migrate (0.4.0)
5
- activerecord (>= 5.2.0)
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
- actionpack (7.0.3)
12
- actionview (= 7.0.3)
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
- actionview (7.0.3)
19
- activesupport (= 7.0.3)
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
- activemodel (7.0.3)
25
- activesupport (= 7.0.3)
26
- activerecord (7.0.3)
27
- activemodel (= 7.0.3)
28
- activesupport (= 7.0.3)
29
- activesupport (7.0.3)
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
- i18n (1.10.0)
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
- minitest (5.15.0)
50
- nokogiri (1.13.6-x86_64-darwin)
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.3.1)
57
- rack-test (1.1.0)
58
- rack (>= 1.0, < 3)
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.2)
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.4.0)
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.18.0)
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.2)
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.1.0)
98
- zeitwerk (2.5.4)
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
- minitest
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
- # Sensible Data Migrations for Rails
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. Using it
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
- The first issue is that application deployment now depends on the data migration
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 usually stay in `db/migrate` for posterity.
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 so when there are [scripts][2] to
47
+ This is unnecessary for a pristine database. Especially with [scripts][seed-scripts] to
22
48
  seed the correct data.
23
49
 
24
- In addition, using ActiveRecord models in migrations has its [quirks](#using-activerecord-models-in-migrations).
50
+ The purpose of `monarch_migrate` is to solve the above issues and to:
25
51
 
26
- The purpose of `monarch_migrate` is to solve the above issues with separating data
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
- This library assumes that:
56
+ It assumes that:
30
57
 
31
- - You run data migrations *only* on production and rely on seed [scripts][2] i.e. `dev:prime` for local development.
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 MonarchMigrate, you need to run the generator:
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 above generates a schema migration which adds a table for keeping track
51
- of run data migrations.
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 in MonarchMigrate have a similar structure to regular migrations
57
- in Rails. Files are put into `db/data_migrate` and follow the same naming pattern.
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
- To create a new data migration, run:
98
+
99
+ ### Create a Data Migration
60
100
 
61
101
  ```shell
62
- rails generate monarch_migrate:data_migration downcase_usernames
102
+ rails generate data_migration backfill_users_name
63
103
  ```
64
104
 
65
- In contrast to regular migrations, there is no need to inherit any classes:
105
+ Edit the newly created file to define the data migration:
66
106
 
67
107
  ```ruby
68
- # db/data_migrate/20220605083010_downcase_usernames.rb
108
+ # db/data_migrate/20220605083010_backfill_users_name.rb
69
109
  ActiveRecord::Base.connection.execute(<<-SQL)
70
- UPDATE users SET username = lower(username);
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
- As seen above, it is plain ruby code where you can refer to any object you
77
- need. MonarchMigrate will run each migration in a separate transaction.
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
- Or a specific version:
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 MonarchMigrate.
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
- ### Using ActiveRecord Models in Migrations
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
- Suppose we have designed a system where users have first and last names. Time passes and
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][2], there is no need to run them anyway.
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 Postgres.
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 above, MonarchMigrate (similar to Rails) runs each migration in a separate
159
- transaction. A long-running task within a migration will keep the transaction open for
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
- To avoid this, we can run such tasks asynchronously.
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
- Back to the previous example:
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 still a mystery to scientists how the new generations know where to go,
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 a truly incredible data migration!
378
+ Genetically speaking, this is an incredible data migration!
193
379
 
194
380
 
195
- ## See Also
196
381
 
197
- Alternative gems
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
- [2]: https://thoughtbot.com/blog/priming-the-pump
216
- [3]: https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/
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(:test) do |t|
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
- task default: :test
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]
@@ -2,4 +2,4 @@ Description:
2
2
  Generate a schema migration to create the table used for data migrations
3
3
 
4
4
  Examples:
5
- rails generate monarch_migrate:install
5
+ rails generate monarch_migrate:install
@@ -28,7 +28,7 @@ module MonarchMigrate
28
28
  end
29
29
 
30
30
  def migration_table_name
31
- MonarchMigrate.data_migrations_table_name
31
+ MigrationRecord.table_name
32
32
  end
33
33
 
34
34
  private
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Generate a new data migration
3
+
4
+ Examples:
5
+ rails generate data_migration assign_name_to_users
@@ -1,6 +1,6 @@
1
1
  require "rails/generators/active_record"
2
2
 
3
- module MonarchMigrate
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!
@@ -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,5 @@
1
+ require "rails_helper"
2
+
3
+ RSpec.describe "<%= described_class %>", type: :data_migration do
4
+ pending "add some examples to (or delete) #{__FILE__}"
5
+ 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
@@ -0,0 +1,7 @@
1
+ require "test_helper"
2
+
3
+ class <%= class_name %>Test < MonarchMigrate::TestCase
4
+ # test "the truth" do
5
+ # assert true
6
+ # end
7
+ 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 run(io = nil)
26
- io ||= File.open(File::NULL, "w")
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
- io.puts "Running data migration #{version}: #{name}"
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
- io.puts "Migration complete"
37
+ puts "Migration complete"
35
38
  rescue => e
36
- io.puts "Migration failed due to #{e}"
37
- raise ActiveRecord::Rollback
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
- io.puts
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(io = nil)
24
- io ||= File.open(File::NULL, "w")
25
-
23
+ def run
26
24
  if pending_migrations.any?
27
- io.puts "Running #{pending_migrations.size} data migrations"
28
- pending_migrations.sort_by(&:version).each { |m| m.run(io) }
25
+ puts "Running #{pending_migrations.size} data migrations"
26
+ pending_migrations.sort_by(&:version).each(&:run)
29
27
  else
30
- io.puts "No data migrations pending"
28
+ puts "No data migrations pending"
31
29
  []
32
30
  end
33
31
  end
@@ -1,3 +1,5 @@
1
+ require "rails"
2
+
1
3
  module MonarchMigrate
2
4
  class Railtie < Rails::Railtie
3
5
  railtie_name :monarch_migrate
@@ -0,0 +1,5 @@
1
+ require "monarch_migrate/testing"
2
+
3
+ RSpec.configure do |config|
4
+ config.include MonarchMigrate::Testing::RSpec, type: :data_migration
5
+ 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($stdout)
6
+ MonarchMigrate.migrator.run
7
7
  end
8
8
 
9
9
  namespace :migrate do
@@ -0,0 +1,5 @@
1
+ require "monarch_migrate/testing"
2
+
3
+ class MonarchMigrate::TestCase < ActiveSupport::TestCase
4
+ include MonarchMigrate::Testing::TestUnit
5
+ end
@@ -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
@@ -1,3 +1,3 @@
1
1
  module MonarchMigrate
2
- VERSION = "0.4.0"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -15,6 +15,7 @@ module MonarchMigrate
15
15
  end
16
16
  end
17
17
 
18
+ require "rails"
18
19
  require "monarch_migrate/migration"
19
20
  require "monarch_migrate/migration_record"
20
21
  require "monarch_migrate/migrator"
@@ -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("activerecord", ">= 5.2.0")
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.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-06-06 00:00:00.000000000 Z
11
+ date: 2022-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: activerecord
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
@@ -1,5 +0,0 @@
1
- Description:
2
- Generate a new data migration
3
-
4
- Examples:
5
- rails generate monarch_migrate:data_migration assign_name_to_users