nandi 0.8.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e9c08faeee8155ae5b0e5357cabca63c2f31bfd65fb464e16991b47c4b9cab75
4
- data.tar.gz: 0e408aa01adaf0ce11560acef08d899c208d2c4e3afdd31b3fb9777a0d0b2408
3
+ metadata.gz: 6e1e64259650cb225c127f8a89c899464864a9d5ca76fb4abd58d48c6930e624
4
+ data.tar.gz: 41ef391e7da05c3997a13adf87ff5aa032fd6a46762d4cc7263c691d179ed237
5
5
  SHA512:
6
- metadata.gz: fe9b90ee4085d5f7d208e481f9ae4fca325f87ab37ff7b3c1a74063adbe65a7777ab66e8afbb8b4d18408362ef1c71f88884eeab6643c19ffd3e8de3f98b007f
7
- data.tar.gz: fdbca6280d8c5299661b78fa1163593f903605c056337c3e55b3ca512370717fa7c8af97bfe055393205a6c15fe224aa32a97616dfec8fb118daf39a10b42ca0
6
+ metadata.gz: 1672112e9464d5f8cefd75477cd7cdbae2d05822b0544d9f9240140b55121171e8f7e4183b09a5fcb2ab0d240e701aa891faebb5e99bafd5e4e764e2079e4f9d
7
+ data.tar.gz: 490135828100e636745efc67dfece39f04506ce18b135ab7487041b0929302b59973ae2848b7eef899dbd347050767c0f20fb3e1bdca73d0228d4e19c84e1279
data/README.md CHANGED
@@ -4,7 +4,7 @@ Friendly Postgres migrations for people who don't want to take down their databa
4
4
 
5
5
  ## Supported
6
6
 
7
- - Ruby 2.4 or above
7
+ - Ruby 2.5 or above
8
8
  - Rails 5.2 or above
9
9
  - Postgres 11 or above
10
10
 
@@ -161,11 +161,11 @@ We now have three new migration files:
161
161
 
162
162
  class AddReferenceOnFoosToBars < Nandi::Migration
163
163
  def up
164
- add_reference :foos, :bar
164
+ add_column :foos, :bar_id, :bigint
165
165
  end
166
166
 
167
167
  def down
168
- remove_reference :foos, :bar
168
+ remove_column :foos, :bar_id
169
169
  end
170
170
  end
171
171
 
@@ -202,10 +202,10 @@ class AddReferenceOnFoosToBars < ActiveRecord::Migration[5.2]
202
202
  set_statement_timeout(1_500)
203
203
 
204
204
  def up
205
- add_reference(:foos, :bar)
205
+ add_column(:foos, :bar_id, :bigint)
206
206
  end
207
207
  def down
208
- remove_reference(:foos, :bar)
208
+ remove_column(:foos, :bar_id)
209
209
  end
210
210
  end
211
211
 
@@ -268,10 +268,9 @@ Add a foreign key constraint. The generated SQL will include the NOT VALID param
268
268
  ### `#add_index(table, fields, **kwargs)`
269
269
  Adds a new index to the database.
270
270
 
271
- Nandi will:
272
-
271
+ Nandi will
273
272
  - add the `CONCURRENTLY` option, which means the change takes a less restrictive lock at the cost of not running in a DDL transaction
274
- - use the `BTREE` index type which is the safest to create.
273
+ - default to the `BTREE` index type, as it is commonly a good fit.
275
274
 
276
275
  Because index creation is particularly failure-prone, and because we cannot run in a transaction and therefore risk partially applied migrations that (in a Rails environment) require manual intervention, Nandi Validates that, if there is a add_index statement in the migration, it must be the only statement.
277
276
 
@@ -287,7 +286,7 @@ end
287
286
  ```
288
287
 
289
288
  ### `#add_reference(table, ref_name, **extra_args)`
290
- Adds a new reference column. Nandi will validate that the foreign key flag is not set to true; use `add_foreign_key` and `validate_foreign_key` instead!
289
+ Adds a new reference column. Nandi will validate that the foreign key flag is not set to true; use `add_foreign_key` and `validate_foreign_key` instead! Nandi will also set the `index: false` flag, as index creation is unsafe unless done concurrently in a separate migration.
291
290
 
292
291
  ### `#remove_reference(table, ref_name, **extra_args)`
293
292
  Removes a reference column.
data/exe/nandi-enforce CHANGED
@@ -31,6 +31,6 @@ OptionParser.new do |o|
31
31
  end
32
32
  end.parse!
33
33
 
34
- enforcer = Nandi::SafeMigrationEnforcer.new(opts)
34
+ enforcer = Nandi::SafeMigrationEnforcer.new(**opts)
35
35
 
36
36
  enforcer.run
@@ -11,7 +11,7 @@ module Nandi
11
11
  argument :target, type: :string
12
12
  class_option :name, type: :string
13
13
  class_option :column, type: :string
14
- class_option :type, type: :string
14
+ class_option :type, type: :string, default: "bigint"
15
15
  class_option :no_create_column, type: :boolean
16
16
  class_option :validation_timeout, type: :numeric, default: 15 * 60 * 1000
17
17
 
@@ -61,11 +61,11 @@ module Nandi
61
61
  private
62
62
 
63
63
  def type
64
- options["type"]&.to_sym
64
+ options["type"].to_sym
65
65
  end
66
66
 
67
67
  def reference_name
68
- target.singularize.to_sym
68
+ "#{target.singularize}_id".to_sym
69
69
  end
70
70
 
71
71
  def base_path
@@ -2,10 +2,10 @@
2
2
 
3
3
  class <%= add_reference_name.camelize %> < Nandi::Migration
4
4
  def up
5
- add_reference <%= format_value(table) %>, <%= format_value(reference_name) %><% if type %>, type: <%= format_value(type) %><% end %>
5
+ add_column <%= format_value(table) %>, <%= format_value(reference_name) %>, <%= format_value(type) %>
6
6
  end
7
7
 
8
8
  def down
9
- remove_reference <%= format_value(table) %>, <%= format_value(reference_name) %>
9
+ remove_column <%= format_value(table) %>, <%= format_value(reference_name) %>
10
10
  end
11
11
  end
@@ -64,7 +64,7 @@ module Nandi
64
64
  key = if k.is_a?(Symbol)
65
65
  symbol_key(k)
66
66
  else
67
- format_value(k) + " =>"
67
+ "#{format_value(k)} =>"
68
68
  end
69
69
  "#{key} #{format_value(v)}"
70
70
  end
@@ -21,11 +21,10 @@ module Nandi
21
21
  name: name,
22
22
 
23
23
  # Overrides and extra options
24
- **@extra_args,
24
+ **extra_args_with_default_index_type,
25
25
 
26
26
  # Mandatory values
27
27
  algorithm: :concurrently,
28
- using: :btree,
29
28
  }
30
29
  end
31
30
 
@@ -38,13 +37,19 @@ module Nandi
38
37
  private
39
38
 
40
39
  def name
41
- :"idx_#{table.to_s}_on_#{field_names}"
40
+ :"idx_#{table}_on_#{field_names}"
42
41
  end
43
42
 
44
43
  def field_names
45
44
  field_names = fields.respond_to?(:map) ? fields.map(&:to_s).join("_") : fields
46
45
  field_names.to_s.scan(/\w+/).join("_")
47
46
  end
47
+
48
+ def extra_args_with_default_index_type
49
+ {
50
+ using: :btree,
51
+ }.merge(@extra_args)
52
+ end
48
53
  end
49
54
  end
50
55
  end
@@ -3,12 +3,13 @@
3
3
  module Nandi
4
4
  module Instructions
5
5
  class AddReference
6
+ DEFAULT_EXTRA_ARGS = { index: false }.freeze
6
7
  attr_reader :table, :ref_name, :extra_args
7
8
 
8
9
  def initialize(table:, ref_name:, **kwargs)
9
10
  @table = table
10
11
  @ref_name = ref_name
11
- @extra_args = kwargs
12
+ @extra_args = DEFAULT_EXTRA_ARGS.merge(kwargs)
12
13
  end
13
14
 
14
15
  def procedure
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/hash/indifferent_access"
4
+ require "digest"
4
5
 
5
6
  module Nandi
6
7
  class Lockfile
@@ -42,7 +43,26 @@ module Nandi
42
43
  end
43
44
 
44
45
  def persist!
45
- File.write(path, lockfile.to_h.deep_stringify_keys.to_yaml)
46
+ # This is a somewhat ridiculous trick to avoid merge conflicts in git.
47
+ #
48
+ # Normally, new migrations are added to the bottom of the Nandi lockfile.
49
+ # This is relatively unfriendly to git's merge algorithm, and means that
50
+ # if someone merges a pull request with a completely unrelated migration,
51
+ # you'll have to rebase to get yours merged as the last line of the file
52
+ # will be seen as a conflict (both branches added content there).
53
+ #
54
+ # This is in contrast to something like Gemfile.lock, where changes tend
55
+ # to be distributed throughout the file. The idea behind sorting by
56
+ # SHA-256 hash is to distribute new Nandi lockfile entries evenly, but
57
+ # also stably through the file. It needs to be stable or we'd have even
58
+ # worse merge conflict problems (e.g. if we randomised the order on
59
+ # writing the file, the whole thing would conflict pretty much every time
60
+ # it was regenerated).
61
+ content = Hash[lockfile.to_h.deep_stringify_keys.sort_by do |k, _|
62
+ Digest::SHA256.hexdigest(k)
63
+ end].to_yaml
64
+
65
+ File.write(path, content)
46
66
  end
47
67
 
48
68
  def path
@@ -43,6 +43,7 @@ module Nandi
43
43
 
44
44
  class << self
45
45
  attr_reader :lock_timeout, :statement_timeout
46
+
46
47
  # For sake both of correspondence with Postgres syntax and familiarity
47
48
  # with activerecord-safe_migrations's identically named macros, we
48
49
  # disable this cop.
@@ -111,7 +112,7 @@ module Nandi
111
112
  # Nandi will:
112
113
  # * add the `CONCURRENTLY` option, which means the change takes a less
113
114
  # restrictive lock at the cost of not running in a DDL transaction
114
- # * use the `BTREE` index type which is the safest to create.
115
+ # * default to the `BTREE` index type, as it is commonly a good fit.
115
116
  #
116
117
  # Because index creation is particularly failure-prone, and because
117
118
  # we cannot run in a transaction and therefore risk partially applied
@@ -16,16 +16,28 @@ module Nandi
16
16
  end
17
17
 
18
18
  def call
19
- foreign_key = instruction.extra_args.fetch(:foreign_key) { false }
20
-
21
- assert(
22
- !foreign_key,
23
- foreign_key_message,
19
+ foreign_key = instruction.extra_args.fetch(:foreign_key, false)
20
+ index = instruction.extra_args.fetch(:index, false)
21
+
22
+ collect_errors(
23
+ assert(
24
+ !foreign_key,
25
+ foreign_key_message,
26
+ ),
27
+ assert(
28
+ !index,
29
+ index_message,
30
+ ),
24
31
  )
25
32
  end
26
33
 
27
34
  private
28
35
 
36
+ def index_message
37
+ "Indexing a reference column on creation can make the table unavailable." \
38
+ "Use the `add_index` method in a separate migration to index the column."
39
+ end
40
+
29
41
  def foreign_key_message
30
42
  "Adding a foreign key constraint must be done in two separate migrations. " \
31
43
  "Use the `add_foreign_key` and `validate_foreign_key` methods, or the " \
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nandi
4
+ VERSION = "0.11.1"
5
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nandi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.11.1
5
5
  platform: ruby
6
6
  authors:
7
- - James Turley
8
- autorequire:
7
+ - GoCardless Engineering
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-04 00:00:00.000000000 Z
11
+ date: 2021-08-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -100,28 +100,28 @@ dependencies:
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: 2.3.14
103
+ version: 2.15.0
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: 2.3.14
110
+ version: 2.15.0
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: pry-byebug
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 3.7.0
117
+ version: 3.9.0
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: 3.7.0
124
+ version: 3.9.0
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: rails
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -140,16 +140,22 @@ dependencies:
140
140
  name: rake
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: 12.3.3
143
146
  - - "~>"
144
147
  - !ruby/object:Gem::Version
145
- version: '10.0'
148
+ version: '13.0'
146
149
  type: :development
147
150
  prerelease: false
148
151
  version_requirements: !ruby/object:Gem::Requirement
149
152
  requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: 12.3.3
150
156
  - - "~>"
151
157
  - !ruby/object:Gem::Version
152
- version: '10.0'
158
+ version: '13.0'
153
159
  - !ruby/object:Gem::Dependency
154
160
  name: rspec
155
161
  requirement: !ruby/object:Gem::Requirement
@@ -184,14 +190,14 @@ dependencies:
184
190
  requirements:
185
191
  - - "~>"
186
192
  - !ruby/object:Gem::Version
187
- version: '0.61'
193
+ version: '1.0'
188
194
  type: :development
189
195
  prerelease: false
190
196
  version_requirements: !ruby/object:Gem::Requirement
191
197
  requirements:
192
198
  - - "~>"
193
199
  - !ruby/object:Gem::Version
194
- version: '0.61'
200
+ version: '1.0'
195
201
  - !ruby/object:Gem::Dependency
196
202
  name: yard
197
203
  requirement: !ruby/object:Gem::Requirement
@@ -206,9 +212,9 @@ dependencies:
206
212
  - - "~>"
207
213
  - !ruby/object:Gem::Version
208
214
  version: '0.9'
209
- description:
215
+ description:
210
216
  email:
211
- - jamesturley@gocardless.com
217
+ - engineering@gocardless.com
212
218
  executables:
213
219
  - nandi-enforce
214
220
  extensions: []
@@ -276,6 +282,7 @@ files:
276
282
  - lib/nandi/validation/result.rb
277
283
  - lib/nandi/validation/timeout_validator.rb
278
284
  - lib/nandi/validator.rb
285
+ - lib/nandi/version.rb
279
286
  - lib/templates/nandi/renderers/active_record/generate/show.rb.erb
280
287
  - lib/templates/nandi/renderers/active_record/instructions/add_check_constraint/show.rb.erb
281
288
  - lib/templates/nandi/renderers/active_record/instructions/add_column/show.rb.erb
@@ -292,10 +299,11 @@ files:
292
299
  - lib/templates/nandi/renderers/active_record/instructions/remove_not_null_constraint/show.rb.erb
293
300
  - lib/templates/nandi/renderers/active_record/instructions/remove_reference/show.rb.erb
294
301
  - lib/templates/nandi/renderers/active_record/instructions/validate_constraint/show.rb.erb
295
- homepage:
296
- licenses: []
302
+ homepage: https://github.com/gocardless/nandi
303
+ licenses:
304
+ - MIT
297
305
  metadata: {}
298
- post_install_message:
306
+ post_install_message:
299
307
  rdoc_options: []
300
308
  require_paths:
301
309
  - lib
@@ -303,15 +311,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
303
311
  requirements:
304
312
  - - ">="
305
313
  - !ruby/object:Gem::Version
306
- version: '0'
314
+ version: 2.4.0
307
315
  required_rubygems_version: !ruby/object:Gem::Requirement
308
316
  requirements:
309
317
  - - ">="
310
318
  - !ruby/object:Gem::Version
311
319
  version: '0'
312
320
  requirements: []
313
- rubygems_version: 3.0.3
314
- signing_key:
321
+ rubygems_version: 3.2.3
322
+ signing_key:
315
323
  specification_version: 4
316
324
  summary: Fear-free migrations for PostgreSQL
317
325
  test_files: []