rails_async_migrations 1.0.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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +138 -0
  5. data/.travis.yml +7 -0
  6. data/Gemfile +7 -0
  7. data/Gemfile.lock +171 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +162 -0
  10. data/Rakefile +6 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/lib/generators/rails_async_migrations/install_generator.rb +22 -0
  14. data/lib/generators/rails_async_migrations/templates/create_async_schema_migrations.rb +11 -0
  15. data/lib/rails_async_migrations/config.rb +13 -0
  16. data/lib/rails_async_migrations/connection/active_record.rb +59 -0
  17. data/lib/rails_async_migrations/migration/check_queue.rb +72 -0
  18. data/lib/rails_async_migrations/migration/fire_migration.rb +56 -0
  19. data/lib/rails_async_migrations/migration/lock.rb +84 -0
  20. data/lib/rails_async_migrations/migration/method_added.rb +24 -0
  21. data/lib/rails_async_migrations/migration/overwrite.rb +28 -0
  22. data/lib/rails_async_migrations/migration/run.rb +73 -0
  23. data/lib/rails_async_migrations/migration/unlock.rb +42 -0
  24. data/lib/rails_async_migrations/migration.rb +23 -0
  25. data/lib/rails_async_migrations/models/async_schema_migration.rb +18 -0
  26. data/lib/rails_async_migrations/mutators/base.rb +9 -0
  27. data/lib/rails_async_migrations/mutators/trigger_callback.rb +57 -0
  28. data/lib/rails_async_migrations/mutators/turn_async.rb +22 -0
  29. data/lib/rails_async_migrations/mutators.rb +24 -0
  30. data/lib/rails_async_migrations/railtie.rb +7 -0
  31. data/lib/rails_async_migrations/tracer.rb +23 -0
  32. data/lib/rails_async_migrations/version.rb +3 -0
  33. data/lib/rails_async_migrations/workers/sidekiq/check_queue_worker.rb +16 -0
  34. data/lib/rails_async_migrations/workers/sidekiq/fire_migration_worker.rb +18 -0
  35. data/lib/rails_async_migrations/workers.rb +58 -0
  36. data/lib/rails_async_migrations.rb +35 -0
  37. data/lib/tasks/rails_async_migrations.rake +6 -0
  38. data/rails_async_migrations.gemspec +30 -0
  39. metadata +193 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a9ffc3ef530a9f156013b067663ddeea6b06c65e155a668c56936b6803a50945
4
+ data.tar.gz: e72fe2f4fe6289eff981e77653a3a1d00b97a90bd4750138a816ca38ea2a594f
5
+ SHA512:
6
+ metadata.gz: 42e96d488de2c1b5d49b039e0174a193cfcbccae1fa619526e46f91b9edb840cf4f2c7041f325f43800cd705d3b5935d694b798402f7d9f3c6b34697fd721885
7
+ data.tar.gz: e8de2e332165ebdd35554bb1df53c74b77f5a3dd884cafe9624f522bcec133810b589e8c89263cad1097ead136f0d4e7f2ad0b2a660373c325c158d244b56f1b
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,138 @@
1
+ # require: rubocop-rspec
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 2.5
5
+ # RuboCop has a bunch of cops enabled by default. This setting tells RuboCop
6
+ # to ignore them, so only the ones explicitly set in this file are enabled.
7
+ DisabledByDefault: true
8
+ Exclude:
9
+ - '**/.DS_Store'
10
+ - 'db/**/*'
11
+ - 'tmp/**/*'
12
+ - 'vendor/**/*'
13
+ - 'bin/**/*'
14
+ - 'log/**/*'
15
+ - 'script/**/*'
16
+
17
+ Include:
18
+ - 'app/**/*'
19
+ - 'spec/**/*'
20
+ - 'lib/**/*'
21
+
22
+ # Prefer &&/|| over and/or.
23
+ Style/AndOr:
24
+ Enabled: true
25
+
26
+ # Do not use braces for hash literals when they are the last argument of a
27
+ # method call.
28
+ Style/BracesAroundHashParameters:
29
+ Enabled: true
30
+
31
+ # Align `when` with `case`.
32
+ Layout/CaseIndentation:
33
+ Enabled: true
34
+
35
+ # Align comments with method definitions.
36
+ Layout/CommentIndentation:
37
+ Enabled: true
38
+
39
+ # No extra empty lines.
40
+ Layout/EmptyLines:
41
+ Enabled: true
42
+
43
+ # In a regular class definition, no empty lines around the body.
44
+ Layout/EmptyLinesAroundClassBody:
45
+ Enabled: true
46
+
47
+ # In a regular method definition, no empty lines around the body.
48
+ Layout/EmptyLinesAroundMethodBody:
49
+ Enabled: true
50
+
51
+ # In a regular module definition, no empty lines around the body.
52
+ Layout/EmptyLinesAroundModuleBody:
53
+ Enabled: true
54
+
55
+ # Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }.
56
+ Style/HashSyntax:
57
+ Enabled: true
58
+
59
+ # Two spaces, no tabs (for indentation).
60
+ Layout/IndentationWidth:
61
+ Enabled: true
62
+
63
+ Layout/SpaceAfterColon:
64
+ Enabled: true
65
+
66
+ Layout/SpaceAfterComma:
67
+ Enabled: true
68
+
69
+ Layout/SpaceAroundEqualsInParameterDefault:
70
+ Enabled: true
71
+
72
+ Layout/SpaceAroundKeyword:
73
+ Enabled: true
74
+
75
+ Layout/SpaceAroundOperators:
76
+ Enabled: true
77
+
78
+ Layout/SpaceBeforeFirstArg:
79
+ Enabled: true
80
+
81
+ # Defining a method with parameters needs parentheses.
82
+ Style/MethodDefParentheses:
83
+ Enabled: true
84
+
85
+ # Use `foo {}` not `foo{}`.
86
+ Layout/SpaceBeforeBlockBraces:
87
+ Enabled: true
88
+
89
+ # Use `foo { bar }` not `foo {bar}`.
90
+ Layout/SpaceInsideBlockBraces:
91
+ Enabled: true
92
+
93
+ # Use `{ a: 1 }` not `{a:1}`.
94
+ Layout/SpaceInsideHashLiteralBraces:
95
+ Enabled: true
96
+
97
+ Layout/SpaceInsideParens:
98
+ Enabled: true
99
+
100
+ # Check quotes usage according to lint rule below.
101
+ Style/StringLiterals:
102
+ Enabled: true
103
+ EnforcedStyle: single_quotes
104
+
105
+ # Detect hard tabs, no hard tabs.
106
+ Layout/Tab:
107
+ Enabled: true
108
+
109
+ # Blank lines should not have any spaces.
110
+ Layout/TrailingBlankLines:
111
+ Enabled: true
112
+
113
+ # No trailing whitespace.
114
+ Layout/TrailingWhitespace:
115
+ Enabled: true
116
+
117
+ # Use quotes for string literals when they are enough.
118
+ Style/UnneededPercentQ:
119
+ Enabled: true
120
+
121
+ # Align `end` with the matching keyword or starting expression except for
122
+ # assignments, where it should be aligned with the LHS.
123
+ Layout/EndAlignment:
124
+ Enabled: true
125
+ EnforcedStyleAlignWith: variable
126
+
127
+ # Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
128
+ Lint/RequireParentheses:
129
+ Enabled: true
130
+
131
+ Lint/Debugger:
132
+ Enabled: true
133
+
134
+ Metrics/LineLength:
135
+ # This will disable the rule completely, regardless what other options you put
136
+ Enabled: true
137
+ # Change the default 80 chars limit value
138
+ Max: 150
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.5.1
7
+ before_install: gem install bundler -v 1.17.1
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'activerecord', '~> 5.2.0'
6
+ gem 'sidekiq', '~> 5.2.3'
7
+ gem 'delayed_job_active_record', '~> 4.1.3'
data/Gemfile.lock ADDED
@@ -0,0 +1,171 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rails_async_migrations (1.0.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ actioncable (5.2.2)
10
+ actionpack (= 5.2.2)
11
+ nio4r (~> 2.0)
12
+ websocket-driver (>= 0.6.1)
13
+ actionmailer (5.2.2)
14
+ actionpack (= 5.2.2)
15
+ actionview (= 5.2.2)
16
+ activejob (= 5.2.2)
17
+ mail (~> 2.5, >= 2.5.4)
18
+ rails-dom-testing (~> 2.0)
19
+ actionpack (5.2.2)
20
+ actionview (= 5.2.2)
21
+ activesupport (= 5.2.2)
22
+ rack (~> 2.0)
23
+ rack-test (>= 0.6.3)
24
+ rails-dom-testing (~> 2.0)
25
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
26
+ actionview (5.2.2)
27
+ activesupport (= 5.2.2)
28
+ builder (~> 3.1)
29
+ erubi (~> 1.4)
30
+ rails-dom-testing (~> 2.0)
31
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
32
+ activejob (5.2.2)
33
+ activesupport (= 5.2.2)
34
+ globalid (>= 0.3.6)
35
+ activemodel (5.2.2)
36
+ activesupport (= 5.2.2)
37
+ activerecord (5.2.2)
38
+ activemodel (= 5.2.2)
39
+ activesupport (= 5.2.2)
40
+ arel (>= 9.0)
41
+ activestorage (5.2.2)
42
+ actionpack (= 5.2.2)
43
+ activerecord (= 5.2.2)
44
+ marcel (~> 0.3.1)
45
+ activesupport (5.2.2)
46
+ concurrent-ruby (~> 1.0, >= 1.0.2)
47
+ i18n (>= 0.7, < 2)
48
+ minitest (~> 5.1)
49
+ tzinfo (~> 1.1)
50
+ arel (9.0.0)
51
+ builder (3.2.3)
52
+ coderay (1.1.2)
53
+ concurrent-ruby (1.1.3)
54
+ connection_pool (2.2.2)
55
+ crass (1.0.4)
56
+ database_cleaner (1.7.0)
57
+ delayed_job (4.1.5)
58
+ activesupport (>= 3.0, < 5.3)
59
+ delayed_job_active_record (4.1.3)
60
+ activerecord (>= 3.0, < 5.3)
61
+ delayed_job (>= 3.0, < 5)
62
+ diff-lcs (1.3)
63
+ erubi (1.7.1)
64
+ globalid (0.4.1)
65
+ activesupport (>= 4.2.0)
66
+ i18n (1.2.0)
67
+ concurrent-ruby (~> 1.0)
68
+ loofah (2.2.3)
69
+ crass (~> 1.0.2)
70
+ nokogiri (>= 1.5.9)
71
+ mail (2.7.1)
72
+ mini_mime (>= 0.1.1)
73
+ marcel (0.3.3)
74
+ mimemagic (~> 0.3.2)
75
+ method_source (0.9.2)
76
+ mimemagic (0.3.2)
77
+ mini_mime (1.0.1)
78
+ mini_portile2 (2.3.0)
79
+ minitest (5.11.3)
80
+ nio4r (2.3.1)
81
+ nokogiri (1.8.5)
82
+ mini_portile2 (~> 2.3.0)
83
+ pry (0.12.2)
84
+ coderay (~> 1.1.0)
85
+ method_source (~> 0.9.0)
86
+ rack (2.0.6)
87
+ rack-protection (2.0.5)
88
+ rack
89
+ rack-test (1.1.0)
90
+ rack (>= 1.0, < 3)
91
+ rails (5.2.2)
92
+ actioncable (= 5.2.2)
93
+ actionmailer (= 5.2.2)
94
+ actionpack (= 5.2.2)
95
+ actionview (= 5.2.2)
96
+ activejob (= 5.2.2)
97
+ activemodel (= 5.2.2)
98
+ activerecord (= 5.2.2)
99
+ activestorage (= 5.2.2)
100
+ activesupport (= 5.2.2)
101
+ bundler (>= 1.3.0)
102
+ railties (= 5.2.2)
103
+ sprockets-rails (>= 2.0.0)
104
+ rails-dom-testing (2.0.3)
105
+ activesupport (>= 4.2.0)
106
+ nokogiri (>= 1.6)
107
+ rails-html-sanitizer (1.0.4)
108
+ loofah (~> 2.2, >= 2.2.2)
109
+ railties (5.2.2)
110
+ actionpack (= 5.2.2)
111
+ activesupport (= 5.2.2)
112
+ method_source
113
+ rake (>= 0.8.7)
114
+ thor (>= 0.19.0, < 2.0)
115
+ rake (10.5.0)
116
+ redis (4.1.0)
117
+ rspec (3.8.0)
118
+ rspec-core (~> 3.8.0)
119
+ rspec-expectations (~> 3.8.0)
120
+ rspec-mocks (~> 3.8.0)
121
+ rspec-core (3.8.0)
122
+ rspec-support (~> 3.8.0)
123
+ rspec-expectations (3.8.2)
124
+ diff-lcs (>= 1.2.0, < 2.0)
125
+ rspec-support (~> 3.8.0)
126
+ rspec-mocks (3.8.0)
127
+ diff-lcs (>= 1.2.0, < 2.0)
128
+ rspec-support (~> 3.8.0)
129
+ rspec-sidekiq (3.0.3)
130
+ rspec-core (~> 3.0, >= 3.0.0)
131
+ sidekiq (>= 2.4.0)
132
+ rspec-support (3.8.0)
133
+ sidekiq (5.2.3)
134
+ connection_pool (~> 2.2, >= 2.2.2)
135
+ rack-protection (>= 1.5.0)
136
+ redis (>= 3.3.5, < 5)
137
+ sprockets (3.7.2)
138
+ concurrent-ruby (~> 1.0)
139
+ rack (> 1, < 3)
140
+ sprockets-rails (3.2.1)
141
+ actionpack (>= 4.0)
142
+ activesupport (>= 4.0)
143
+ sprockets (>= 3.0.0)
144
+ sqlite3 (1.3.13)
145
+ thor (0.20.3)
146
+ thread_safe (0.3.6)
147
+ tzinfo (1.2.5)
148
+ thread_safe (~> 0.1)
149
+ websocket-driver (0.7.0)
150
+ websocket-extensions (>= 0.1.0)
151
+ websocket-extensions (0.1.3)
152
+
153
+ PLATFORMS
154
+ ruby
155
+
156
+ DEPENDENCIES
157
+ activerecord (~> 5.2.0)
158
+ bundler (~> 1.17)
159
+ database_cleaner
160
+ delayed_job_active_record (~> 4.1.3)
161
+ pry (~> 0.12)
162
+ rails (~> 5.2)
163
+ rails_async_migrations!
164
+ rake (~> 10.0)
165
+ rspec (~> 3.0)
166
+ rspec-sidekiq
167
+ sidekiq (~> 5.2.3)
168
+ sqlite3
169
+
170
+ BUNDLED WITH
171
+ 1.17.1
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Laurent Schaffner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,162 @@
1
+ # RailsAsyncMigrations
2
+
3
+ `ActiveRecord::Migration` extension to turn your migrations asynschonous in a simple and straight forward way.
4
+
5
+ ## Motives
6
+
7
+ This library was made with the intent to help small companies which are struggling to scale at technical level. Small projects don't need asynchronous migrations queues, and big companies build their own parallel systems when facing scaling problems, but what about medium sized companies with limited resources ?
8
+
9
+ When a project grows, your database starts to be heavy and changing the data through the deployment process can be very painful. There are numerous reasons you want this process to be [at least] partially asynchronous.
10
+
11
+ Most people turn heavy data changes into `rake tasks` or split workers; there are two schools of thought about this.
12
+
13
+ 1. Migrations should only mutate database structures and not its data, and if it's the case, it should be split and processed via other means.
14
+ 2. Migrations are everything which alter data one time, typically during a deployment of new code and structure.
15
+
16
+ Turning data changes into a `rake task` can be a good idea, and it's ideal to test it out too, but sometimes you need this **fat ass loop** of **>1,000,000 records** which will be run **once, and only once** to be run fast and without locking down the deployment process itself; making a `rake task` for that is overkill. After all, it will only be used once and within a specific structure / data conxtext. This gem is here to answer this need.
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ ```ruby
23
+ gem 'rails_async_migrations'
24
+ ```
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ After the gem has been installed, use the generator to add the needed changes
31
+
32
+ $ rails generate rails_async_migrations:install
33
+
34
+ This will add a new migration for the table `async_schema_migrations` which will be used by the gem. You can also add the migration yourself like so:
35
+
36
+ ```
37
+ class CreateAsyncSchemaMigrations < ActiveRecord::Migration[5.2]
38
+ def change
39
+ create_table :async_schema_migrations do |t|
40
+ t.string :version
41
+ t.string :direction
42
+ t.string :state
43
+
44
+ t.timestamps
45
+ end
46
+ end
47
+ end
48
+ ```
49
+
50
+ ## Usage
51
+
52
+ To turn some of your migrations asynchronous, generate a migration as you would normally do and use the `turn_async` keyword.
53
+
54
+ ```
55
+ class Test < ActiveRecord::Migration[5.2]
56
+ turn_async
57
+
58
+ def change
59
+ create_table 'tests' do |t|
60
+ t.string :test
61
+ end
62
+ end
63
+ end
64
+ ```
65
+
66
+ From now on, when you run this migration it'll simply run the normal queue, but the content of `#change` will be taken away and later on executed into an asynchronous queue.
67
+
68
+ What is turned asynchronous is executed exactly the same way as a classical migration, which means you can use all keywords of the classic `ActiveRecord::Migration` such as `create_table`, `add_column`, etc.
69
+
70
+ ## Configuration
71
+
72
+ **No configuration is needed to start using this library**, but some options are given to adapt to your needs.
73
+
74
+ Add the following lines into your `config/initializer/` folder
75
+
76
+ ```
77
+ RailsAsyncMigrations.config do |config|
78
+ # :verbose can be used if you want a full log of the execution
79
+ config.mode = :quiet
80
+
81
+ # which kind of worker do you want to use for this library
82
+ # for now you have two options: :delayed_job or :sidekiq
83
+ config.workers = :sidekiq
84
+
85
+ # when the migration is turned asynchronous
86
+ # it watches over some specific `ActiveRecord` methods
87
+ # by adding them to this array, you'll lock and turn those methods asynchronous
88
+ # by removing them you'll let them use the classical migration process
89
+ # for example, if you set the `locked_methods` to %i[async] the migration will be processed normally
90
+ # but the content of the `#async` method will be taken away and executed within the asynchronous queue
91
+ config.locked_methods = %i[change up down]
92
+ end
93
+ ```
94
+
95
+ ## Queue
96
+
97
+ Each migration which is turned asynchronous follows each other, once one migration of the queue is ended without problem, it passes to the next one.
98
+
99
+ If it fails, the error will be raised within the worker so it retries until it eventually works, or until it's considered dead. None of the further asynchronous migrations will be run until you fix the failed one, which is a good protection for data consistency.
100
+
101
+ You can also manually launch the queue check and fire by using:
102
+
103
+ $ rake rails_async_migrations:check_queue
104
+
105
+ **For now, there is no rollback mechanism authorized, even if the source code is ready for it, it complexifies the build up logic and may not be needed in asynchronous cases.**
106
+
107
+ ## States
108
+
109
+ | State | Description |
110
+ | -------------- | ---------------------------------------------------------------------------------------------------------------------------- |
111
+ | **created** | the migration has just been added through the classical migration queue |
112
+ | **pending** | the migration has been spotted by the asynchronous queue and will be processed |
113
+ | **processing** | the migration is being processed right now |
114
+ | **done** | the migration was successful |
115
+ | **failed** | the migration failed while being processed, there may be other attempts to make it pass depending your workers configuration |
116
+
117
+ ## Failure handling
118
+
119
+ If your migration crashes, and blocks the rest of your asynchronous migrations but you want to execute them anyway, you can change the code of the migration file and push it again so it passes, or simply remove / update as `state = done` the matching `async_schema_migrations` row.
120
+
121
+ The `version` value is always the same as the classic migrations ones.
122
+
123
+ ## Compatibility
124
+
125
+ You can use this library through different adapters
126
+
127
+ | Type | Version | Documentation |
128
+ | ---------------- | ------- | --------------------------------------------- |
129
+ | **Sidekiq** | 5.2.3 | https://github.com/mperham/sidekiq |
130
+ | **Delayed::Job** | 4.1.3 | https://github.com/collectiveidea/delayed_job |
131
+
132
+ If you use other technologies to setup your workers, please hit me up and I'll create additional adapters for you.
133
+
134
+ The gem has been tested and is working with `ActiveRecord 5.2.2`, if you notice abnormal behavior with other versions or want it compatible with earlier versions, please hit me up.
135
+
136
+ ## Development
137
+
138
+ I created this library as my company was struggling in its deployment process. It lacks functionalities but this is a good starting point; everything is easily extendable so don't hesitate to add your own needed methods to it.
139
+
140
+ You're more than welcome to open an issue with feature requests so I can work on improving this library.
141
+
142
+ ## Contributing
143
+
144
+ 1. Fork it ( https://github.com/[my-github-username]/rails_async_migrations/fork )
145
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
146
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
147
+ 4. Push to the branch (`git push origin my-new-feature`)
148
+ 5. Create a new Pull Request
149
+
150
+ ## Author
151
+
152
+ [Laurent Schaffner](http://www.laurentschaffner.com)
153
+
154
+ ## Credits
155
+
156
+ This project and its idea was inspired by [Kir Shatrov article](https://kirshatrov.com/2018/04/01/async-migrations/) on the matter, it's worth a look!
157
+
158
+ ## License
159
+
160
+ MIT License.
161
+
162
+ ## Changelog
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "rails_async_migrations"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,22 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module RailsAsyncMigrations
5
+ module Generators
6
+ class InstallGenerator < ::Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+ source_root File.expand_path('../templates', __FILE__)
9
+ desc "Add the migrations for AsyncSchemaMigration"
10
+
11
+ def self.next_migration_number(path)
12
+ next_migration_number = current_migration_number(path) + 1
13
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
14
+ end
15
+
16
+ def copy_migrations
17
+ migration_template "create_async_schema_migrations.rb",
18
+ "db/migrate/create_async_schema_migrations.rb"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ class CreateAsyncSchemaMigrations < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :async_schema_migrations do |t|
4
+ t.string :version
5
+ t.string :direction
6
+ t.string :state
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ # configuration of the gem and
2
+ # default values set here
3
+ module RailsAsyncMigrations
4
+ class Config
5
+ attr_accessor :locked_methods, :mode, :workers
6
+
7
+ def initialize
8
+ @locked_methods = %i[change up down]
9
+ @mode = :quiet # verbose, quiet
10
+ @workers = :sidekiq # delayed_job, sidekiq
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,59 @@
1
+ module RailsAsyncMigrations
2
+ module Connection
3
+ class ActiveRecord
4
+ attr_reader :current_direction
5
+
6
+ def initialize(current_direction)
7
+ @current_direction = current_direction
8
+ end
9
+
10
+ # NOTE : down isn't available
11
+ # from the public API of the gem
12
+ def current_version
13
+ if current_direction == :down
14
+ migration_context.current_version
15
+ elsif current_direction == :up
16
+ pending_migrations.first
17
+ end
18
+ end
19
+
20
+ def current_migration
21
+ @current_migration ||= migration_from current_version
22
+ end
23
+
24
+ def migration_from(version)
25
+ migration_context.migrations.find do |migration|
26
+ migration.version.to_s == version.to_s
27
+ end
28
+ end
29
+
30
+ def allowed_direction?
31
+ current_direction == :up
32
+ end
33
+
34
+ private
35
+
36
+ def pending_migrations
37
+ achieved_migrations - all_migrations
38
+ end
39
+
40
+ def achieved_migrations
41
+ migration_context.migrations.collect(&:version)
42
+ end
43
+
44
+ def all_migrations
45
+ migration_context.get_all_versions
46
+ end
47
+
48
+ def migration_context
49
+ connection.migration_context
50
+ end
51
+
52
+ # NOTE: seems at it was ActiveRecord::Migrator
53
+ # in anterior versions
54
+ def connection
55
+ @connection || ::ActiveRecord::Base.connection
56
+ end
57
+ end
58
+ end
59
+ end