monarch_migrate 0.4.0 → 0.5.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 +113 -4
- data/README.md +161 -49
- 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/railtie.rb +2 -0
- data/lib/monarch_migrate/rspec.rb +5 -0
- data/lib/monarch_migrate/test_unit.rb +5 -0
- data/lib/monarch_migrate/testing.rb +87 -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: 814685cc086ec84d8f1e877c80134db4c702b9d06602f4169f99dc82c1cd1ca8
         | 
| 4 | 
            +
              data.tar.gz: 726adc5a2087bec90a6fa1fc1ec965d633c4de35f69298da2b42e7e043026af8
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 665c315826067e28509312d5cf9ef8729803dfb105a123aad44f81df9c0c2f46cd185f9d5381939def677d68553a150c37d2e1d5f040b2b281dc6085b47e31d9
         | 
| 7 | 
            +
              data.tar.gz: ff6cb7e01f9071363eb561f392c80e8b8e97b371f853f79f35f1f4a594b8165f4e063d9225ec1ff3af992874a57ef081d16455a049fe547bfe8c6a354578a8d3
         | 
    
        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
    
    | @@ -2,12 +2,36 @@ PATH | |
| 2 2 | 
             
              remote: .
         | 
| 3 3 | 
             
              specs:
         | 
| 4 4 | 
             
                monarch_migrate (0.4.0)
         | 
| 5 | 
            -
                   | 
| 6 | 
            -
                  railties (>= 5.2.0)
         | 
| 5 | 
            +
                  rails (>= 5.2.0)
         | 
| 7 6 |  | 
| 8 7 | 
             
            GEM
         | 
| 9 8 | 
             
              remote: https://rubygems.org/
         | 
| 10 9 | 
             
              specs:
         | 
| 10 | 
            +
                actioncable (7.0.3)
         | 
| 11 | 
            +
                  actionpack (= 7.0.3)
         | 
| 12 | 
            +
                  activesupport (= 7.0.3)
         | 
| 13 | 
            +
                  nio4r (~> 2.0)
         | 
| 14 | 
            +
                  websocket-driver (>= 0.6.1)
         | 
| 15 | 
            +
                actionmailbox (7.0.3)
         | 
| 16 | 
            +
                  actionpack (= 7.0.3)
         | 
| 17 | 
            +
                  activejob (= 7.0.3)
         | 
| 18 | 
            +
                  activerecord (= 7.0.3)
         | 
| 19 | 
            +
                  activestorage (= 7.0.3)
         | 
| 20 | 
            +
                  activesupport (= 7.0.3)
         | 
| 21 | 
            +
                  mail (>= 2.7.1)
         | 
| 22 | 
            +
                  net-imap
         | 
| 23 | 
            +
                  net-pop
         | 
| 24 | 
            +
                  net-smtp
         | 
| 25 | 
            +
                actionmailer (7.0.3)
         | 
| 26 | 
            +
                  actionpack (= 7.0.3)
         | 
| 27 | 
            +
                  actionview (= 7.0.3)
         | 
| 28 | 
            +
                  activejob (= 7.0.3)
         | 
| 29 | 
            +
                  activesupport (= 7.0.3)
         | 
| 30 | 
            +
                  mail (~> 2.5, >= 2.5.4)
         | 
| 31 | 
            +
                  net-imap
         | 
| 32 | 
            +
                  net-pop
         | 
| 33 | 
            +
                  net-smtp
         | 
| 34 | 
            +
                  rails-dom-testing (~> 2.0)
         | 
| 11 35 | 
             
                actionpack (7.0.3)
         | 
| 12 36 | 
             
                  actionview (= 7.0.3)
         | 
| 13 37 | 
             
                  activesupport (= 7.0.3)
         | 
| @@ -15,17 +39,34 @@ GEM | |
| 15 39 | 
             
                  rack-test (>= 0.6.3)
         | 
| 16 40 | 
             
                  rails-dom-testing (~> 2.0)
         | 
| 17 41 | 
             
                  rails-html-sanitizer (~> 1.0, >= 1.2.0)
         | 
| 42 | 
            +
                actiontext (7.0.3)
         | 
| 43 | 
            +
                  actionpack (= 7.0.3)
         | 
| 44 | 
            +
                  activerecord (= 7.0.3)
         | 
| 45 | 
            +
                  activestorage (= 7.0.3)
         | 
| 46 | 
            +
                  activesupport (= 7.0.3)
         | 
| 47 | 
            +
                  globalid (>= 0.6.0)
         | 
| 48 | 
            +
                  nokogiri (>= 1.8.5)
         | 
| 18 49 | 
             
                actionview (7.0.3)
         | 
| 19 50 | 
             
                  activesupport (= 7.0.3)
         | 
| 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)
         | 
| 55 | 
            +
                activejob (7.0.3)
         | 
| 56 | 
            +
                  activesupport (= 7.0.3)
         | 
| 57 | 
            +
                  globalid (>= 0.3.6)
         | 
| 24 58 | 
             
                activemodel (7.0.3)
         | 
| 25 59 | 
             
                  activesupport (= 7.0.3)
         | 
| 26 60 | 
             
                activerecord (7.0.3)
         | 
| 27 61 | 
             
                  activemodel (= 7.0.3)
         | 
| 28 62 | 
             
                  activesupport (= 7.0.3)
         | 
| 63 | 
            +
                activestorage (7.0.3)
         | 
| 64 | 
            +
                  actionpack (= 7.0.3)
         | 
| 65 | 
            +
                  activejob (= 7.0.3)
         | 
| 66 | 
            +
                  activerecord (= 7.0.3)
         | 
| 67 | 
            +
                  activesupport (= 7.0.3)
         | 
| 68 | 
            +
                  marcel (~> 1.0)
         | 
| 69 | 
            +
                  mini_mime (>= 1.1.0)
         | 
| 29 70 | 
             
                activesupport (7.0.3)
         | 
| 30 71 | 
             
                  concurrent-ruby (~> 1.0, >= 1.0.2)
         | 
| 31 72 | 
             
                  i18n (>= 1.6, < 2)
         | 
| @@ -36,26 +77,70 @@ 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.4.4)
         | 
| 86 | 
            +
                digest (3.1.0)
         | 
| 42 87 | 
             
                erubi (1.10.0)
         | 
| 88 | 
            +
                globalid (1.0.0)
         | 
| 89 | 
            +
                  activesupport (>= 5.0)
         | 
| 43 90 | 
             
                i18n (1.10.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)
         | 
| 99 | 
            +
                mini_mime (1.1.2)
         | 
| 100 | 
            +
                mini_portile2 (2.8.0)
         | 
| 49 101 | 
             
                minitest (5.15.0)
         | 
| 50 | 
            -
                 | 
| 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.6)
         | 
| 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 127 | 
             
                rack (2.2.3.1)
         | 
| 57 128 | 
             
                rack-test (1.1.0)
         | 
| 58 129 | 
             
                  rack (>= 1.0, < 3)
         | 
| 130 | 
            +
                rails (7.0.3)
         | 
| 131 | 
            +
                  actioncable (= 7.0.3)
         | 
| 132 | 
            +
                  actionmailbox (= 7.0.3)
         | 
| 133 | 
            +
                  actionmailer (= 7.0.3)
         | 
| 134 | 
            +
                  actionpack (= 7.0.3)
         | 
| 135 | 
            +
                  actiontext (= 7.0.3)
         | 
| 136 | 
            +
                  actionview (= 7.0.3)
         | 
| 137 | 
            +
                  activejob (= 7.0.3)
         | 
| 138 | 
            +
                  activemodel (= 7.0.3)
         | 
| 139 | 
            +
                  activerecord (= 7.0.3)
         | 
| 140 | 
            +
                  activestorage (= 7.0.3)
         | 
| 141 | 
            +
                  activesupport (= 7.0.3)
         | 
| 142 | 
            +
                  bundler (>= 1.15.0)
         | 
| 143 | 
            +
                  railties (= 7.0.3)
         | 
| 59 144 | 
             
                rails-dom-testing (2.0.3)
         | 
| 60 145 | 
             
                  activesupport (>= 4.2.0)
         | 
| 61 146 | 
             
                  nokogiri (>= 1.6)
         | 
| @@ -72,6 +157,23 @@ GEM | |
| 72 157 | 
             
                rake (13.0.6)
         | 
| 73 158 | 
             
                regexp_parser (2.4.0)
         | 
| 74 159 | 
             
                rexml (3.2.5)
         | 
| 160 | 
            +
                rspec-core (3.10.1)
         | 
| 161 | 
            +
                  rspec-support (~> 3.10.0)
         | 
| 162 | 
            +
                rspec-expectations (3.10.1)
         | 
| 163 | 
            +
                  diff-lcs (>= 1.2.0, < 2.0)
         | 
| 164 | 
            +
                  rspec-support (~> 3.10.0)
         | 
| 165 | 
            +
                rspec-mocks (3.10.2)
         | 
| 166 | 
            +
                  diff-lcs (>= 1.2.0, < 2.0)
         | 
| 167 | 
            +
                  rspec-support (~> 3.10.0)
         | 
| 168 | 
            +
                rspec-rails (5.0.1)
         | 
| 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.10.2)
         | 
| 75 177 | 
             
                rubocop (1.29.1)
         | 
| 76 178 | 
             
                  parallel (~> 1.10)
         | 
| 77 179 | 
             
                  parser (>= 3.1.0.0)
         | 
| @@ -91,10 +193,15 @@ GEM | |
| 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 201 | 
             
                unicode-display_width (2.1.0)
         | 
| 202 | 
            +
                websocket-driver (0.7.5)
         | 
| 203 | 
            +
                  websocket-extensions (>= 0.1.0)
         | 
| 204 | 
            +
                websocket-extensions (0.1.5)
         | 
| 98 205 | 
             
                zeitwerk (2.5.4)
         | 
| 99 206 |  | 
| 100 207 | 
             
            PLATFORMS
         | 
| @@ -102,9 +209,11 @@ PLATFORMS | |
| 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,5 +1,7 @@ | |
| 1 1 | 
             
            # Sensible Data Migrations for Rails
         | 
| 2 2 |  | 
| 3 | 
            +
            ## Why
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            <blockquote>
         | 
| 4 6 | 
             
              <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>
         | 
| 5 7 | 
             
              <a href="https://guides.rubyonrails.org/active_record_migrations.html#migrations-and-seed-data">
         | 
| @@ -8,28 +10,27 @@ | |
| 8 10 | 
             
            </blockquote>
         | 
| 9 11 |  | 
| 10 12 | 
             
            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.
         | 
| 13 | 
            +
            for data changes in the database comes second. Yet, adding or modifying data via
         | 
| 14 | 
            +
            regular migrations can be problematic.
         | 
| 14 15 |  | 
| 15 16 | 
             
            The first issue is that application deployment now depends on the data migration
         | 
| 16 17 | 
             
            to be completed. This may not be a problem with small databases but large
         | 
| 17 18 | 
             
            databases with millions of records will respond with hanging or failed migrations.
         | 
| 18 19 |  | 
| 19 | 
            -
            Another issue is that data migration files  | 
| 20 | 
            +
            Another issue is that data migration files tend to stay in `db/migrate` for posterity.
         | 
| 20 21 | 
             
            As a result, they will run whenever a developer sets up their local development environment.
         | 
| 21 | 
            -
            This is unnecessary for a pristine database. Especially  | 
| 22 | 
            +
            This is unnecessary for a pristine database. Especially when there are [scripts][2] to
         | 
| 22 23 | 
             
            seed the correct data.
         | 
| 23 24 |  | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
            The purpose of `monarch_migrate` is to solve the above issues with separating data
         | 
| 27 | 
            -
            from schema migrations.
         | 
| 25 | 
            +
            The purpose of `monarch_migrate` is to solve the above issues by separating data from schema migrations.
         | 
| 28 26 |  | 
| 29 | 
            -
             | 
| 27 | 
            +
            It is assumed that:
         | 
| 30 28 |  | 
| 31 29 | 
             
            - You run data migrations *only* on production and rely on seed [scripts][2] i.e. `dev:prime` for local development.
         | 
| 32 30 | 
             
            - You run data migrations manually.
         | 
| 31 | 
            +
            - You want to write tests your data migrations.
         | 
| 32 | 
            +
             | 
| 33 | 
            +
             | 
| 33 34 |  | 
| 34 35 | 
             
            ## Install
         | 
| 35 36 |  | 
| @@ -41,40 +42,51 @@ gem "monarch_migrate" | |
| 41 42 |  | 
| 42 43 | 
             
            Run the bundle command to install it.
         | 
| 43 44 |  | 
| 44 | 
            -
            After you install  | 
| 45 | 
            +
            After you install the gem, you need to run the generator:
         | 
| 45 46 |  | 
| 46 47 | 
             
            ```shell
         | 
| 47 48 | 
             
            rails generate monarch_migrate:install
         | 
| 48 49 | 
             
            ```
         | 
| 49 50 |  | 
| 50 | 
            -
            The  | 
| 51 | 
            -
            of  | 
| 51 | 
            +
            The install generator creates a migration file that adds a `data_migration_records`
         | 
| 52 | 
            +
            table. It is where the gem keeps track of data migrations we have already ran.
         | 
| 53 | 
            +
             | 
| 52 54 |  | 
| 53 55 |  | 
| 54 56 | 
             
            ## Usage
         | 
| 55 57 |  | 
| 56 | 
            -
            Data migrations  | 
| 57 | 
            -
             | 
| 58 | 
            +
            Data migrations have a similar structure to regular migrations in Rails. Files are
         | 
| 59 | 
            +
            put into `db/data_migrate` and follow the same naming pattern.
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            Let's start with an example.
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            Suppose we have designed a system where users have first and last names. Time passes and
         | 
| 64 | 
            +
            it becomes clear this is [wrong][3]. Now, we want to put things right and come
         | 
| 65 | 
            +
            up with the following plan:
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            1. Add a `name` column to `users` table to hold person's full name.
         | 
| 68 | 
            +
            2. Adapt the `User` model and use a data migration to update existing records.
         | 
| 69 | 
            +
            3. Drop `first_name` and `last_name` columns.
         | 
| 58 70 |  | 
| 59 | 
            -
            To create  | 
| 71 | 
            +
            To create the data migration to update existing user records, run:
         | 
| 60 72 |  | 
| 61 73 | 
             
            ```shell
         | 
| 62 | 
            -
            rails generate  | 
| 74 | 
            +
            rails generate data_migration backfill_users_name
         | 
| 63 75 | 
             
            ```
         | 
| 64 76 |  | 
| 65 77 | 
             
            In contrast to regular migrations, there is no need to inherit any classes:
         | 
| 66 78 |  | 
| 67 79 | 
             
            ```ruby
         | 
| 68 | 
            -
            # db/data_migrate/ | 
| 80 | 
            +
            # db/data_migrate/20220605083010_backfill_users_name.rb
         | 
| 69 81 | 
             
            ActiveRecord::Base.connection.execute(<<-SQL)
         | 
| 70 | 
            -
              UPDATE users SET  | 
| 82 | 
            +
              UPDATE users SET name = concat(first_name, ' ', last_name) WHERE name IS NULL;
         | 
| 71 83 | 
             
            SQL
         | 
| 72 84 |  | 
| 73 | 
            -
            SearchIndex. | 
| 85 | 
            +
            SearchIndex::RebuildJob.perform_later
         | 
| 74 86 | 
             
            ```
         | 
| 75 87 |  | 
| 76 88 | 
             
            As seen above, it is plain ruby code where you can refer to any object you
         | 
| 77 | 
            -
            need.  | 
| 89 | 
            +
            need. Each data migration runs in a separate transaction.
         | 
| 78 90 |  | 
| 79 91 | 
             
            To run pending data migrations:
         | 
| 80 92 |  | 
| @@ -88,12 +100,127 @@ Or a specific version: | |
| 88 100 | 
             
            rails data:migrate VERSION=20220605083010
         | 
| 89 101 | 
             
            ```
         | 
| 90 102 |  | 
| 103 | 
            +
             | 
| 104 | 
            +
            ## Testing
         | 
| 105 | 
            +
             | 
| 106 | 
            +
            Testing data migrations can be the difference between simply rerunning
         | 
| 107 | 
            +
            the migration and having to recover from a data loss.
         | 
| 108 | 
            +
             | 
| 109 | 
            +
            This is why `monarch_migrate` includes test helpers for both RSpec and TestUnit.
         | 
| 110 | 
            +
             | 
| 111 | 
            +
            ### RSpec
         | 
| 112 | 
            +
             | 
| 113 | 
            +
            For `rspec`, add the following line to your `spec/rails_helper.rb`:
         | 
| 114 | 
            +
             | 
| 115 | 
            +
            ```ruby
         | 
| 116 | 
            +
            require "monarch_migrate/rspec"
         | 
| 117 | 
            +
            ```
         | 
| 118 | 
            +
             | 
| 119 | 
            +
            Then:
         | 
| 120 | 
            +
             | 
| 121 | 
            +
            ```ruby
         | 
| 122 | 
            +
            # spec/data_migrations/20220605083010_backfill_users_name_spec.rb
         | 
| 123 | 
            +
            describe "20220605083010_backfill_users_name", type: :data_migration do
         | 
| 124 | 
            +
              subject { run_data_migration }
         | 
| 125 | 
            +
             | 
| 126 | 
            +
              it "assigns user's name" do
         | 
| 127 | 
            +
                user = users(:without_name_migrated)
         | 
| 128 | 
            +
                # or if you're using FactoryBot:
         | 
| 129 | 
            +
                # user = create(:user, first_name: "Guybrush", last_name: "Threepwood", name: nil)
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                expect(subject).to change { user.reload.name }.to("Guybrush Threepwood")
         | 
| 132 | 
            +
              end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
              it "does not assign name to already migrated users" do
         | 
| 135 | 
            +
                user = users(:with_name_migrated)
         | 
| 136 | 
            +
                # or if you're using FactoryBot:
         | 
| 137 | 
            +
                # user = create(:user, first_name: "", last_name: "", name: "Guybrush Threepwood")
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                expect(subject).not_to change { user.reload.name }
         | 
| 140 | 
            +
              end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
              context "when the user has no last name" do
         | 
| 143 | 
            +
                it "does not leave a trailing space" do
         | 
| 144 | 
            +
                  user = users(:without_name_migrated)
         | 
| 145 | 
            +
                  # or if you're using FactoryBot:
         | 
| 146 | 
            +
                  # user = create(:user, first_name: "Guybrush", last_name: nil, name: nil)
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                  expect(subject).to change { user.reload.name }.to("Guybrush")
         | 
| 149 | 
            +
                end
         | 
| 150 | 
            +
              end
         | 
| 151 | 
            +
             | 
| 152 | 
            +
              # And so on ...
         | 
| 153 | 
            +
            end
         | 
| 154 | 
            +
            ```
         | 
| 155 | 
            +
             | 
| 156 | 
            +
            ### TestUnit
         | 
| 157 | 
            +
             | 
| 158 | 
            +
            For `test_unit`, add this line to your `test/test_helper.rb`:
         | 
| 159 | 
            +
             | 
| 160 | 
            +
            ```ruby
         | 
| 161 | 
            +
            require "monarch_migrate/test_unit"
         | 
| 162 | 
            +
            ```
         | 
| 163 | 
            +
             | 
| 164 | 
            +
            Then:
         | 
| 165 | 
            +
             | 
| 166 | 
            +
            ```ruby
         | 
| 167 | 
            +
            # test/data_migrations/20220605083010_backfill_users_name_test.rb
         | 
| 168 | 
            +
            class BackfillUsersNameTest < MonarchMigrate::TestCase
         | 
| 169 | 
            +
              def test_assigns_users_name
         | 
| 170 | 
            +
                user = users(:without_name_migrated)
         | 
| 171 | 
            +
                # or if you're using FactoryBot:
         | 
| 172 | 
            +
                # user = create(:user, first_name: "Guybrush", last_name: "Threepwood", name: nil)
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                run_data_migration
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                assert_equal "Guybrush Threepwood", user.reload.name
         | 
| 177 | 
            +
              end
         | 
| 178 | 
            +
             | 
| 179 | 
            +
              def test_does_not_assign_name_to_alredy_migrated_users
         | 
| 180 | 
            +
                user = users(:with_name_migrated)
         | 
| 181 | 
            +
                # or if you're using FactoryBot:
         | 
| 182 | 
            +
                # user = create(:user, first_name: "", last_name: "", name: "Guybrush Threepwood")
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                run_data_migration
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                assert_equal "Guybrush Threepwood", user.reload.name
         | 
| 187 | 
            +
              end
         | 
| 188 | 
            +
             | 
| 189 | 
            +
              def test_does_not_leave_trailing_space_when_user_has_no_last_name
         | 
| 190 | 
            +
                user = users(:without_name_migrated)
         | 
| 191 | 
            +
                # or if you're using FactoryBot:
         | 
| 192 | 
            +
                # user = create(:user, first_name: "Guybrush", last_name: nil, name: nil)
         | 
| 193 | 
            +
             | 
| 194 | 
            +
                run_data_migration
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                assert_equal "Guybrush", user.reload.name
         | 
| 197 | 
            +
              end
         | 
| 198 | 
            +
             | 
| 199 | 
            +
              # And so on ...
         | 
| 200 | 
            +
            end
         | 
| 201 | 
            +
            ```
         | 
| 202 | 
            +
             | 
| 203 | 
            +
            Data migrations become obsolete, once the data manipulation successfully completes.
         | 
| 204 | 
            +
            So are the corresponding tests. These will fail after database columns are dropped
         | 
| 205 | 
            +
            e.g. `first_name` and `last_name`.
         | 
| 206 | 
            +
             | 
| 207 | 
            +
            One solution is to use the following development workflow:
         | 
| 208 | 
            +
             | 
| 209 | 
            +
            1. Implement the data migration using TDD.
         | 
| 210 | 
            +
            1. Commit, push and wait for CI to pass.
         | 
| 211 | 
            +
            1. Request review from peers.
         | 
| 212 | 
            +
            1. Once approved, remove the test files in a consecutive commit and push again.
         | 
| 213 | 
            +
            1. Merge into trunk.
         | 
| 214 | 
            +
             | 
| 215 | 
            +
            This will also keep the test files in repo's history for posterity.
         | 
| 216 | 
            +
             | 
| 91 217 | 
             
            ## Known Issues and Limitations
         | 
| 92 218 |  | 
| 93 | 
            -
            The following issues and limitations are not necessary inherent to  | 
| 219 | 
            +
            The following issues and limitations are not necessary inherent to `monarch_migrate`.
         | 
| 94 220 | 
             
            Some are innate to migrations in general.
         | 
| 95 221 |  | 
| 96 | 
            -
             | 
| 222 | 
            +
             | 
| 223 | 
            +
            ### Using Models in Migrations
         | 
| 97 224 |  | 
| 98 225 | 
             
            <blockquote>
         | 
| 99 226 | 
             
              <p>The Active Record way claims that intelligence belongs in your models, not in the database.</p>
         | 
| @@ -106,15 +233,7 @@ Typically, data migrations relate closely to business models. In an ideal Rails | |
| 106 233 | 
             
            data manipulations would depend on model logic to enforce validation, conform to
         | 
| 107 234 | 
             
            business rules, etc. Hence, it is very tempting to use ActiveRecord models in migrations.
         | 
| 108 235 |  | 
| 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:
         | 
| 236 | 
            +
            Here is a regular Rails migration for our example:
         | 
| 118 237 |  | 
| 119 238 | 
             
            ```ruby
         | 
| 120 239 | 
             
            # db/migrate/20220605083010_backfill_users_name.rb
         | 
| @@ -155,22 +274,12 @@ limit their use and do as much processing as possible in Postgres. | |
| 155 274 |  | 
| 156 275 | 
             
            ### Long-running Tasks in Migrations
         | 
| 157 276 |  | 
| 158 | 
            -
            As mentioned | 
| 159 | 
            -
             | 
| 277 | 
            +
            As mentioned, each data migration runs in a separate transaction.
         | 
| 278 | 
            +
            A long-running task within a migration keeps the transaction open for
         | 
| 160 279 | 
             
            the duration of the task. As a result, the migration may hang or fail.
         | 
| 161 280 |  | 
| 162 | 
            -
            To avoid this,  | 
| 163 | 
            -
             | 
| 164 | 
            -
            Back to the previous example:
         | 
| 165 | 
            -
             | 
| 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
         | 
| 281 | 
            +
            To avoid this, run such tasks asynchronously.
         | 
| 171 282 |  | 
| 172 | 
            -
            SearchIndex::RebuildJob.perform_later
         | 
| 173 | 
            -
            ```
         | 
| 174 283 |  | 
| 175 284 |  | 
| 176 285 | 
             
            ## Trivia
         | 
| @@ -185,19 +294,21 @@ The population cycles through three to five generations to reach their | |
| 185 294 | 
             
            destination. In the end, a new generation of butterflies complete the
         | 
| 186 295 | 
             
            journey their great-great-great-grandparents started.
         | 
| 187 296 |  | 
| 188 | 
            -
            It is  | 
| 297 | 
            +
            It is a mystery to scientists how the new generations know where to go,
         | 
| 189 298 | 
             
            but they appear to navigate using a combination of the Earth's magnetic field
         | 
| 190 299 | 
             
            and the position of the sun.
         | 
| 191 300 |  | 
| 192 | 
            -
            Genetically speaking, this is  | 
| 301 | 
            +
            Genetically speaking, this is an incredible data migration!
         | 
| 302 | 
            +
             | 
| 193 303 |  | 
| 194 304 |  | 
| 195 305 | 
             
            ## See Also
         | 
| 196 306 |  | 
| 197 307 | 
             
            Alternative gems
         | 
| 198 308 |  | 
| 199 | 
            -
            -  | 
| 200 | 
            -
            -  | 
| 309 | 
            +
            - https://github.com/OffgridElectric/rails-data-migrations
         | 
| 310 | 
            +
            - https://github.com/ilyakatz/data-migrate
         | 
| 311 | 
            +
            - https://github.com/jasonfb/nonschema_migrations
         | 
| 201 312 |  | 
| 202 313 | 
             
            Articles
         | 
| 203 314 |  | 
| @@ -208,6 +319,7 @@ Articles | |
| 208 319 | 
             
            - [Rails Migrations with Zero Downtime](https://www.cloudbees.com/blog/rails-migrations-zero-downtime)
         | 
| 209 320 |  | 
| 210 321 |  | 
| 322 | 
            +
             | 
| 211 323 | 
             
            ## License
         | 
| 212 324 |  | 
| 213 325 | 
             
            The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
         | 
    
        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
         | 
| @@ -0,0 +1,87 @@ | |
| 1 | 
            +
            require "active_support/core_ext/string"
         | 
| 2 | 
            +
            require "stringio"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module MonarchMigrate
         | 
| 5 | 
            +
              module Testing
         | 
| 6 | 
            +
                MigrationRunError = Class.new(RuntimeError)
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                module Helpers
         | 
| 9 | 
            +
                  def data_migration
         | 
| 10 | 
            +
                    @data_migration ||=
         | 
| 11 | 
            +
                      begin
         | 
| 12 | 
            +
                        filename = data_migration_basename
         | 
| 13 | 
            +
                        migrator
         | 
| 14 | 
            +
                          .migrations
         | 
| 15 | 
            +
                          .find(method(:not_found)) { |m| m.filename.ends_with?(filename) }
         | 
| 16 | 
            +
                      end
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def run_data_migration
         | 
| 20 | 
            +
                    output = StringIO.new
         | 
| 21 | 
            +
                    data_migration.run(output)
         | 
| 22 | 
            +
                    output.string.tap { ensure_no_error(_1) }
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  protected
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  def data_migration_basename
         | 
| 28 | 
            +
                    raise NotImplementedError
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def data_migration_failed(message)
         | 
| 32 | 
            +
                    raise NotImplementedError
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  def migrator
         | 
| 36 | 
            +
                    ::MonarchMigrate.migrator
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  def not_found
         | 
| 40 | 
            +
                    path = File.join(migrator.path, "*_#{data_migration_basename}")
         | 
| 41 | 
            +
                    raise <<~MSG
         | 
| 42 | 
            +
                      \n\r
         | 
| 43 | 
            +
                      Can not find data migration at path: #{path}
         | 
| 44 | 
            +
                      \n\r
         | 
| 45 | 
            +
                    MSG
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def ensure_no_error(str)
         | 
| 49 | 
            +
                    if %r{Migration failed}.match?(str)
         | 
| 50 | 
            +
                      data_migration_failed(<<-CMD)
         | 
| 51 | 
            +
                        Failed running data migration: #{data_migration.filename}
         | 
| 52 | 
            +
                        \n\r
         | 
| 53 | 
            +
                        Output:
         | 
| 54 | 
            +
                        \n\r
         | 
| 55 | 
            +
                        \n\r
         | 
| 56 | 
            +
                        #{str}
         | 
| 57 | 
            +
                      CMD
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                module RSpec
         | 
| 63 | 
            +
                  include Helpers
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  def data_migration_basename
         | 
| 66 | 
            +
                    spec_path = ::RSpec.current_example.metadata[:file_path]
         | 
| 67 | 
            +
                    File.basename(spec_path).sub(/_spec\.rb$/, ".rb")
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  def data_migration_failed(message)
         | 
| 71 | 
            +
                    fail message
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                module TestUnit
         | 
| 76 | 
            +
                  include Helpers
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  def data_migration_basename
         | 
| 79 | 
            +
                    File.basename(self.class.name.underscore).sub(/_test$/, ".rb")
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  def data_migration_failed(message)
         | 
| 83 | 
            +
                    flunk message
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
              end
         | 
| 87 | 
            +
            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.5.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- | 
| 11 | 
            +
            date: 2022-06-18 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
         |