nandi 0.8.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 +7 -0
- data/README.md +483 -0
- data/Rakefile +8 -0
- data/exe/nandi-enforce +36 -0
- data/lib/generators/nandi/check_constraint/USAGE +19 -0
- data/lib/generators/nandi/check_constraint/check_constraint_generator.rb +52 -0
- data/lib/generators/nandi/check_constraint/templates/add_check_constraint.rb +15 -0
- data/lib/generators/nandi/check_constraint/templates/validate_check_constraint.rb +9 -0
- data/lib/generators/nandi/compile/USAGE +19 -0
- data/lib/generators/nandi/compile/compile_generator.rb +62 -0
- data/lib/generators/nandi/foreign_key/USAGE +47 -0
- data/lib/generators/nandi/foreign_key/foreign_key_generator.rb +91 -0
- data/lib/generators/nandi/foreign_key/templates/add_foreign_key.rb +13 -0
- data/lib/generators/nandi/foreign_key/templates/add_reference.rb +11 -0
- data/lib/generators/nandi/foreign_key/templates/validate_foreign_key.rb +9 -0
- data/lib/generators/nandi/migration/USAGE +9 -0
- data/lib/generators/nandi/migration/migration_generator.rb +24 -0
- data/lib/generators/nandi/migration/templates/migration.rb +13 -0
- data/lib/generators/nandi/not_null_check/USAGE +19 -0
- data/lib/generators/nandi/not_null_check/not_null_check_generator.rb +56 -0
- data/lib/generators/nandi/not_null_check/templates/add_not_null_check.rb +11 -0
- data/lib/generators/nandi/not_null_check/templates/validate_not_null_check.rb +9 -0
- data/lib/nandi.rb +35 -0
- data/lib/nandi/compiled_migration.rb +86 -0
- data/lib/nandi/config.rb +126 -0
- data/lib/nandi/file_diff.rb +32 -0
- data/lib/nandi/file_matcher.rb +72 -0
- data/lib/nandi/formatting.rb +79 -0
- data/lib/nandi/instructions.rb +21 -0
- data/lib/nandi/instructions/add_check_constraint.rb +23 -0
- data/lib/nandi/instructions/add_column.rb +24 -0
- data/lib/nandi/instructions/add_foreign_key.rb +40 -0
- data/lib/nandi/instructions/add_index.rb +50 -0
- data/lib/nandi/instructions/add_reference.rb +23 -0
- data/lib/nandi/instructions/change_column_default.rb +23 -0
- data/lib/nandi/instructions/create_table.rb +83 -0
- data/lib/nandi/instructions/drop_constraint.rb +22 -0
- data/lib/nandi/instructions/drop_table.rb +21 -0
- data/lib/nandi/instructions/irreversible_migration.rb +15 -0
- data/lib/nandi/instructions/remove_column.rb +23 -0
- data/lib/nandi/instructions/remove_index.rb +41 -0
- data/lib/nandi/instructions/remove_not_null_constraint.rb +22 -0
- data/lib/nandi/instructions/remove_reference.rb +23 -0
- data/lib/nandi/instructions/validate_constraint.rb +22 -0
- data/lib/nandi/lockfile.rb +58 -0
- data/lib/nandi/migration.rb +388 -0
- data/lib/nandi/renderers.rb +7 -0
- data/lib/nandi/renderers/active_record.rb +13 -0
- data/lib/nandi/renderers/active_record/generate.rb +59 -0
- data/lib/nandi/renderers/active_record/instructions.rb +146 -0
- data/lib/nandi/safe_migration_enforcer.rb +143 -0
- data/lib/nandi/timeout_policies.rb +38 -0
- data/lib/nandi/timeout_policies/access_exclusive.rb +54 -0
- data/lib/nandi/timeout_policies/concurrent.rb +64 -0
- data/lib/nandi/validation.rb +11 -0
- data/lib/nandi/validation/add_column_validator.rb +43 -0
- data/lib/nandi/validation/add_reference_validator.rb +38 -0
- data/lib/nandi/validation/each_validator.rb +34 -0
- data/lib/nandi/validation/failure_helpers.rb +35 -0
- data/lib/nandi/validation/remove_index_validator.rb +30 -0
- data/lib/nandi/validation/result.rb +30 -0
- data/lib/nandi/validation/timeout_validator.rb +37 -0
- data/lib/nandi/validator.rb +102 -0
- data/lib/templates/nandi/renderers/active_record/generate/show.rb.erb +27 -0
- data/lib/templates/nandi/renderers/active_record/instructions/add_check_constraint/show.rb.erb +7 -0
- data/lib/templates/nandi/renderers/active_record/instructions/add_column/show.rb.erb +6 -0
- data/lib/templates/nandi/renderers/active_record/instructions/add_foreign_key/show.rb.erb +5 -0
- data/lib/templates/nandi/renderers/active_record/instructions/add_index/show.rb.erb +5 -0
- data/lib/templates/nandi/renderers/active_record/instructions/add_reference/show.rb.erb +5 -0
- data/lib/templates/nandi/renderers/active_record/instructions/change_column_default/show.rb.erb +1 -0
- data/lib/templates/nandi/renderers/active_record/instructions/create_table/show.rb.erb +8 -0
- data/lib/templates/nandi/renderers/active_record/instructions/drop_constraint/show.rb.erb +4 -0
- data/lib/templates/nandi/renderers/active_record/instructions/drop_table/show.rb.erb +1 -0
- data/lib/templates/nandi/renderers/active_record/instructions/irreversible_migration/show.rb.erb +1 -0
- data/lib/templates/nandi/renderers/active_record/instructions/remove_column/show.rb.erb +5 -0
- data/lib/templates/nandi/renderers/active_record/instructions/remove_index/show.rb.erb +4 -0
- data/lib/templates/nandi/renderers/active_record/instructions/remove_not_null_constraint/show.rb.erb +1 -0
- data/lib/templates/nandi/renderers/active_record/instructions/remove_reference/show.rb.erb +5 -0
- data/lib/templates/nandi/renderers/active_record/instructions/validate_constraint/show.rb.erb +3 -0
- metadata +317 -0
data/Rakefile
ADDED
data/exe/nandi-enforce
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require "bundler"
|
6
|
+
Bundler.setup(:default)
|
7
|
+
|
8
|
+
require "optparse"
|
9
|
+
|
10
|
+
require_relative "../lib/nandi"
|
11
|
+
require_relative "../lib/nandi/safe_migration_enforcer"
|
12
|
+
|
13
|
+
opts = {}
|
14
|
+
OptionParser.new do |o|
|
15
|
+
o.on("--safe-migration-dir DIR", "directory containing Nandi migrations") do |v|
|
16
|
+
opts[:safe_migration_dir] = v
|
17
|
+
end
|
18
|
+
o.on("--ar-migration-dir DIR", "directory containing ActiveRecord migrations") do |v|
|
19
|
+
opts[:ar_migration_dir] = v
|
20
|
+
end
|
21
|
+
o.on("--require PATH", "file to require before execution") do |v|
|
22
|
+
opts[:require_path] = v
|
23
|
+
end
|
24
|
+
o.on("--files FILE_SPEC",
|
25
|
+
"Files to compile for check - eg, all, git-diff, 201901010101") do |v|
|
26
|
+
opts[:files] = v
|
27
|
+
end
|
28
|
+
o.on("-h", "--help") do
|
29
|
+
puts o
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
end.parse!
|
33
|
+
|
34
|
+
enforcer = Nandi::SafeMigrationEnforcer.new(opts)
|
35
|
+
|
36
|
+
enforcer.run
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Description:
|
2
|
+
Generates two new database migrations which will safely add an arbitrary
|
3
|
+
check constraint and validate it separately.
|
4
|
+
|
5
|
+
Example:
|
6
|
+
rails generate nandi:check_constraint foos baz_or_quux_not_null
|
7
|
+
|
8
|
+
This will create:
|
9
|
+
db/safe_migrations/20190424123727_add_check_constraint_baz_or_quux_not_null_on_foos.rb
|
10
|
+
db/safe_migrations/20190424123728_validate_check_constraint_baz_or_quux_not_null_on_foos.rb
|
11
|
+
|
12
|
+
Example:
|
13
|
+
rails generate nandi:check_constraint foos baz_or_quux_not_null --validation-timeout 20000
|
14
|
+
|
15
|
+
This will create:
|
16
|
+
db/safe_migrations/20190424123727_add_check_constraint_baz_or_quux_not_null_on_foos.rb
|
17
|
+
db/safe_migrations/20190424123728_validate_check_constraint_baz_or_quux_not_null_on_foos.rb
|
18
|
+
|
19
|
+
The statement timeout in the second migration will be set to 20,000ms.
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators"
|
4
|
+
require "nandi/formatting"
|
5
|
+
|
6
|
+
module Nandi
|
7
|
+
class CheckConstraintGenerator < Rails::Generators::Base
|
8
|
+
include Nandi::Formatting
|
9
|
+
|
10
|
+
argument :table, type: :string
|
11
|
+
argument :name, type: :string
|
12
|
+
class_option :validation_timeout, type: :numeric, default: 15 * 60 * 1000
|
13
|
+
|
14
|
+
source_root File.expand_path("templates", __dir__)
|
15
|
+
|
16
|
+
attr_reader :add_check_constraint_name, :validate_check_constraint_name
|
17
|
+
|
18
|
+
def add_check_constraint
|
19
|
+
self.table = table.to_sym
|
20
|
+
self.name = name.to_sym
|
21
|
+
|
22
|
+
@add_check_constraint_name = "add_check_constraint_#{name}_on_#{table}"
|
23
|
+
|
24
|
+
template(
|
25
|
+
"add_check_constraint.rb",
|
26
|
+
"#{base_path}/#{timestamp}_#{add_check_constraint_name}.rb",
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate_check_constraint
|
31
|
+
self.table = table.to_sym
|
32
|
+
self.name = name.to_sym
|
33
|
+
|
34
|
+
@validate_check_constraint_name = "validate_check_constraint_#{name}_on_#{table}"
|
35
|
+
|
36
|
+
template(
|
37
|
+
"validate_check_constraint.rb",
|
38
|
+
"#{base_path}/#{timestamp(1)}_#{validate_check_constraint_name}.rb",
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def base_path
|
45
|
+
Nandi.config.migration_directory || "db/safe_migrations"
|
46
|
+
end
|
47
|
+
|
48
|
+
def timestamp(offset = 0)
|
49
|
+
(Time.now.utc + offset).strftime("%Y%m%d%H%M%S")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class <%= add_check_constraint_name.camelize %> < Nandi::Migration
|
4
|
+
def up
|
5
|
+
add_check_constraint <%= format_value(table) %>,
|
6
|
+
<%= format_value(name) %>,
|
7
|
+
<<~SQL
|
8
|
+
-- foo IS NOT NULL OR bar IS NOT NULL
|
9
|
+
SQL
|
10
|
+
end
|
11
|
+
|
12
|
+
def down
|
13
|
+
drop_constraint <%= format_value(table) %>, <%= format_value(name) %>
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Description:
|
2
|
+
Takes all files from the db/nandi directory and turns
|
3
|
+
them into ActiveRecordmigration files.
|
4
|
+
|
5
|
+
Examples:
|
6
|
+
rails generate nandi:compile
|
7
|
+
compiles all migrations that have changed since last git commit
|
8
|
+
|
9
|
+
rails generate nandi:compile --files all
|
10
|
+
compiles all migrations ever
|
11
|
+
|
12
|
+
rails generate nandi:compile --files 20190101010101
|
13
|
+
compiles specific migration
|
14
|
+
|
15
|
+
rails generate nandi:compile --files 20190101
|
16
|
+
compiles all migrations from January 1st 2019
|
17
|
+
|
18
|
+
rails generate nandi:compile --files >=20190101
|
19
|
+
compiles all migrations from January 1st 2019 and after
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators"
|
4
|
+
require "nandi"
|
5
|
+
require "nandi/migration"
|
6
|
+
require "nandi/file_matcher"
|
7
|
+
require "nandi/lockfile"
|
8
|
+
|
9
|
+
module Nandi
|
10
|
+
class CompileGenerator < Rails::Generators::Base
|
11
|
+
source_root File.expand_path("templates", __dir__)
|
12
|
+
|
13
|
+
class_option :files,
|
14
|
+
type: :string,
|
15
|
+
default: Nandi.config.compile_files,
|
16
|
+
desc: <<-DESC
|
17
|
+
Files to compile. May be one of the following:
|
18
|
+
-- 'all' compiles all files
|
19
|
+
-- 'git-diff' only changed
|
20
|
+
-- a full or partial version timestamp, eg '20190101010101', '20190101'
|
21
|
+
-- a timestamp range , eg '>=20190101010101'
|
22
|
+
DESC
|
23
|
+
|
24
|
+
def compile_migration_files
|
25
|
+
Nandi.compile(files: files) do |results|
|
26
|
+
results.each do |result|
|
27
|
+
Nandi::Lockfile.add(
|
28
|
+
file_name: result.file_name,
|
29
|
+
source_digest: result.source_digest,
|
30
|
+
compiled_digest: result.compiled_digest,
|
31
|
+
)
|
32
|
+
|
33
|
+
unless result.migration_unchanged?
|
34
|
+
create_file result.output_path, result.body, force: true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
Nandi::Lockfile.persist!
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def safe_migrations_dir
|
45
|
+
if Nandi.config.migration_directory.nil?
|
46
|
+
Rails.root.join("db", "safe_migrations").to_s
|
47
|
+
else
|
48
|
+
File.expand_path(Nandi.config.migration_directory)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def output_path
|
53
|
+
Nandi.config.output_directory || "db/migrate"
|
54
|
+
end
|
55
|
+
|
56
|
+
def files
|
57
|
+
safe_migration_files = Dir.chdir(safe_migrations_dir) { Dir["*.rb"] }
|
58
|
+
FileMatcher.call(files: safe_migration_files, spec: options["files"]).
|
59
|
+
map { |file| File.join(safe_migrations_dir, file) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
Description:
|
2
|
+
Generates two new database migrations which will safely add a foreign key
|
3
|
+
constraint and validate it separately.
|
4
|
+
|
5
|
+
Example:
|
6
|
+
rails generate nandi:foreign_key foos bars
|
7
|
+
|
8
|
+
This will create:
|
9
|
+
db/safe_migrations/20190424123727_add_reference_on_bars_to_foos.rb
|
10
|
+
db/safe_migrations/20190424123728_add_foreign_key_on_bars_to_foos.rb
|
11
|
+
db/safe_migrations/20190424123729_validate_foreign_key_on_bars_to_foos.rb
|
12
|
+
|
13
|
+
These migrations will create a new column on the table foos called bar_id, a
|
14
|
+
new foreign key constraint on that column against the id column of bars, and
|
15
|
+
validate that constraint separately.
|
16
|
+
|
17
|
+
Example:
|
18
|
+
rails generate nandi:foreign_key foos bars --type text
|
19
|
+
|
20
|
+
This will create:
|
21
|
+
db/safe_migrations/20190424123727_add_reference_on_bars_to_foos.rb
|
22
|
+
db/safe_migrations/20190424123728_add_foreign_key_on_bars_to_foos.rb
|
23
|
+
db/safe_migrations/20190424123729_validate_foreign_key_on_bars_to_foos.rb
|
24
|
+
|
25
|
+
These migrations will create a new text column on the table foos called bar_id,
|
26
|
+
a new foreign key constraint on that column against the id column of bars, and
|
27
|
+
validate that constraint separately.
|
28
|
+
|
29
|
+
Example:
|
30
|
+
rails generate nandi:foreign_key foos bars --no-create-column
|
31
|
+
|
32
|
+
This will create:
|
33
|
+
db/safe_migrations/20190424123727_add_foreign_key_on_bars_to_foos.rb
|
34
|
+
db/safe_migrations/20190424123728_validate_foreign_key_on_bars_to_foos.rb
|
35
|
+
|
36
|
+
Assumes that there is a column on table foos called bar_id that points to bars.
|
37
|
+
Will create an FK constraint called foos_bars_fk.
|
38
|
+
|
39
|
+
Example:
|
40
|
+
rails generate nandi:foreign_key foos bars --column special_bar_id --name my_fk
|
41
|
+
|
42
|
+
This will create:
|
43
|
+
db/safe_migrations/20190424123727_add_foreign_key_on_bars_to_foos.rb
|
44
|
+
db/safe_migrations/20190424123728_validate_foreign_key_on_bars_to_foos.rb
|
45
|
+
|
46
|
+
Assumes that there is a column on table foos called special_bar_id that points to
|
47
|
+
bars. Will create an FK constraint called my_fk
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators"
|
4
|
+
require "nandi/formatting"
|
5
|
+
|
6
|
+
module Nandi
|
7
|
+
class ForeignKeyGenerator < Rails::Generators::Base
|
8
|
+
include Nandi::Formatting
|
9
|
+
|
10
|
+
argument :table, type: :string
|
11
|
+
argument :target, type: :string
|
12
|
+
class_option :name, type: :string
|
13
|
+
class_option :column, type: :string
|
14
|
+
class_option :type, type: :string
|
15
|
+
class_option :no_create_column, type: :boolean
|
16
|
+
class_option :validation_timeout, type: :numeric, default: 15 * 60 * 1000
|
17
|
+
|
18
|
+
source_root File.expand_path("templates", __dir__)
|
19
|
+
|
20
|
+
attr_reader :add_reference_name,
|
21
|
+
:add_foreign_key_name,
|
22
|
+
:validate_foreign_key_name
|
23
|
+
|
24
|
+
def add_reference
|
25
|
+
return if options["no_create_column"]
|
26
|
+
|
27
|
+
self.table = table.to_sym
|
28
|
+
|
29
|
+
@add_reference_name = "add_reference_on_#{table}_to_#{target}"
|
30
|
+
|
31
|
+
template(
|
32
|
+
"add_reference.rb",
|
33
|
+
"#{base_path}/#{timestamp}_#{add_reference_name}.rb",
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_foreign_key
|
38
|
+
self.table = table.to_sym
|
39
|
+
self.target = target.to_sym
|
40
|
+
|
41
|
+
@add_foreign_key_name = "add_foreign_key_on_#{table}_to_#{target}"
|
42
|
+
|
43
|
+
template(
|
44
|
+
"add_foreign_key.rb",
|
45
|
+
"#{base_path}/#{timestamp(1)}_#{add_foreign_key_name}.rb",
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def validate_foreign_key
|
50
|
+
self.table = table.to_sym
|
51
|
+
self.target = target.to_sym
|
52
|
+
|
53
|
+
@validate_foreign_key_name = "validate_foreign_key_on_#{table}_to_#{target}"
|
54
|
+
|
55
|
+
template(
|
56
|
+
"validate_foreign_key.rb",
|
57
|
+
"#{base_path}/#{timestamp(2)}_#{validate_foreign_key_name}.rb",
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def type
|
64
|
+
options["type"]&.to_sym
|
65
|
+
end
|
66
|
+
|
67
|
+
def reference_name
|
68
|
+
target.singularize.to_sym
|
69
|
+
end
|
70
|
+
|
71
|
+
def base_path
|
72
|
+
Nandi.config.migration_directory || "db/safe_migrations"
|
73
|
+
end
|
74
|
+
|
75
|
+
def timestamp(offset = 0)
|
76
|
+
(Time.now.utc + offset).strftime("%Y%m%d%H%M%S")
|
77
|
+
end
|
78
|
+
|
79
|
+
def name
|
80
|
+
options["name"]&.to_sym || :"#{@table}_#{@target}_fk"
|
81
|
+
end
|
82
|
+
|
83
|
+
def column
|
84
|
+
options["column"]&.to_sym
|
85
|
+
end
|
86
|
+
|
87
|
+
def any_options?
|
88
|
+
options["name"] || options["column"]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class <%= add_foreign_key_name.camelize %> < Nandi::Migration
|
4
|
+
def up
|
5
|
+
add_foreign_key <%= format_value(table) %>, <%= format_value(target) %><% if any_options? %>,
|
6
|
+
<% if column %>column: <%= format_value(column) %><% end %><% if column && name %>,<% end %>
|
7
|
+
<% if name %>name: <%= format_value(name) %><% end %> <% end %>
|
8
|
+
end
|
9
|
+
|
10
|
+
def down
|
11
|
+
drop_constraint <%= format_value(table) %>, <%= format_value(name) %>
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class <%= add_reference_name.camelize %> < Nandi::Migration
|
4
|
+
def up
|
5
|
+
add_reference <%= format_value(table) %>, <%= format_value(reference_name) %><% if type %>, type: <%= format_value(type) %><% end %>
|
6
|
+
end
|
7
|
+
|
8
|
+
def down
|
9
|
+
remove_reference <%= format_value(table) %>, <%= format_value(reference_name) %>
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators"
|
4
|
+
|
5
|
+
module Nandi
|
6
|
+
class MigrationGenerator < Rails::Generators::NamedBase
|
7
|
+
source_root File.expand_path("templates", __dir__)
|
8
|
+
|
9
|
+
def create_migration_file
|
10
|
+
timestamp = Time.now.utc.strftime("%Y%m%d%H%M%S")
|
11
|
+
|
12
|
+
template(
|
13
|
+
"migration.rb",
|
14
|
+
"#{base_path}/#{timestamp}_#{file_name.underscore}.rb",
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def base_path
|
21
|
+
Nandi.config.migration_directory || "db/safe_migrations"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|