nandi 0.9.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 (76) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +477 -0
  3. data/Rakefile +8 -0
  4. data/exe/nandi-enforce +36 -0
  5. data/lib/generators/nandi/check_constraint/USAGE +19 -0
  6. data/lib/generators/nandi/check_constraint/check_constraint_generator.rb +52 -0
  7. data/lib/generators/nandi/check_constraint/templates/add_check_constraint.rb +15 -0
  8. data/lib/generators/nandi/check_constraint/templates/validate_check_constraint.rb +9 -0
  9. data/lib/generators/nandi/compile/USAGE +19 -0
  10. data/lib/generators/nandi/compile/compile_generator.rb +62 -0
  11. data/lib/generators/nandi/foreign_key/USAGE +47 -0
  12. data/lib/generators/nandi/foreign_key/foreign_key_generator.rb +91 -0
  13. data/lib/generators/nandi/foreign_key/templates/add_foreign_key.rb +13 -0
  14. data/lib/generators/nandi/foreign_key/templates/add_reference.rb +11 -0
  15. data/lib/generators/nandi/foreign_key/templates/validate_foreign_key.rb +9 -0
  16. data/lib/generators/nandi/migration/USAGE +9 -0
  17. data/lib/generators/nandi/migration/migration_generator.rb +24 -0
  18. data/lib/generators/nandi/migration/templates/migration.rb +13 -0
  19. data/lib/generators/nandi/not_null_check/USAGE +19 -0
  20. data/lib/generators/nandi/not_null_check/not_null_check_generator.rb +56 -0
  21. data/lib/generators/nandi/not_null_check/templates/add_not_null_check.rb +11 -0
  22. data/lib/generators/nandi/not_null_check/templates/validate_not_null_check.rb +9 -0
  23. data/lib/nandi.rb +35 -0
  24. data/lib/nandi/compiled_migration.rb +86 -0
  25. data/lib/nandi/config.rb +126 -0
  26. data/lib/nandi/file_diff.rb +32 -0
  27. data/lib/nandi/file_matcher.rb +72 -0
  28. data/lib/nandi/formatting.rb +79 -0
  29. data/lib/nandi/instructions.rb +19 -0
  30. data/lib/nandi/instructions/add_check_constraint.rb +23 -0
  31. data/lib/nandi/instructions/add_column.rb +24 -0
  32. data/lib/nandi/instructions/add_foreign_key.rb +40 -0
  33. data/lib/nandi/instructions/add_index.rb +50 -0
  34. data/lib/nandi/instructions/change_column_default.rb +23 -0
  35. data/lib/nandi/instructions/create_table.rb +83 -0
  36. data/lib/nandi/instructions/drop_constraint.rb +22 -0
  37. data/lib/nandi/instructions/drop_table.rb +21 -0
  38. data/lib/nandi/instructions/irreversible_migration.rb +15 -0
  39. data/lib/nandi/instructions/remove_column.rb +23 -0
  40. data/lib/nandi/instructions/remove_index.rb +41 -0
  41. data/lib/nandi/instructions/remove_not_null_constraint.rb +22 -0
  42. data/lib/nandi/instructions/validate_constraint.rb +22 -0
  43. data/lib/nandi/lockfile.rb +58 -0
  44. data/lib/nandi/migration.rb +363 -0
  45. data/lib/nandi/renderers.rb +7 -0
  46. data/lib/nandi/renderers/active_record.rb +13 -0
  47. data/lib/nandi/renderers/active_record/generate.rb +59 -0
  48. data/lib/nandi/renderers/active_record/instructions.rb +134 -0
  49. data/lib/nandi/safe_migration_enforcer.rb +143 -0
  50. data/lib/nandi/timeout_policies.rb +38 -0
  51. data/lib/nandi/timeout_policies/access_exclusive.rb +54 -0
  52. data/lib/nandi/timeout_policies/concurrent.rb +64 -0
  53. data/lib/nandi/validation.rb +10 -0
  54. data/lib/nandi/validation/add_column_validator.rb +43 -0
  55. data/lib/nandi/validation/each_validator.rb +32 -0
  56. data/lib/nandi/validation/failure_helpers.rb +35 -0
  57. data/lib/nandi/validation/remove_index_validator.rb +30 -0
  58. data/lib/nandi/validation/result.rb +30 -0
  59. data/lib/nandi/validation/timeout_validator.rb +37 -0
  60. data/lib/nandi/validator.rb +102 -0
  61. data/lib/nandi/version.rb +5 -0
  62. data/lib/templates/nandi/renderers/active_record/generate/show.rb.erb +27 -0
  63. data/lib/templates/nandi/renderers/active_record/instructions/add_check_constraint/show.rb.erb +7 -0
  64. data/lib/templates/nandi/renderers/active_record/instructions/add_column/show.rb.erb +6 -0
  65. data/lib/templates/nandi/renderers/active_record/instructions/add_foreign_key/show.rb.erb +5 -0
  66. data/lib/templates/nandi/renderers/active_record/instructions/add_index/show.rb.erb +5 -0
  67. data/lib/templates/nandi/renderers/active_record/instructions/change_column_default/show.rb.erb +1 -0
  68. data/lib/templates/nandi/renderers/active_record/instructions/create_table/show.rb.erb +8 -0
  69. data/lib/templates/nandi/renderers/active_record/instructions/drop_constraint/show.rb.erb +4 -0
  70. data/lib/templates/nandi/renderers/active_record/instructions/drop_table/show.rb.erb +1 -0
  71. data/lib/templates/nandi/renderers/active_record/instructions/irreversible_migration/show.rb.erb +1 -0
  72. data/lib/templates/nandi/renderers/active_record/instructions/remove_column/show.rb.erb +5 -0
  73. data/lib/templates/nandi/renderers/active_record/instructions/remove_index/show.rb.erb +4 -0
  74. data/lib/templates/nandi/renderers/active_record/instructions/remove_not_null_constraint/show.rb.erb +1 -0
  75. data/lib/templates/nandi/renderers/active_record/instructions/validate_constraint/show.rb.erb +3 -0
  76. metadata +320 -0
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nandi
4
+ module Formatting
5
+ class UnsupportedValueError < StandardError; end
6
+
7
+ module ClassMethods
8
+ # Define an accessor method that will retrieve a value
9
+ # from a cell's model and format it with the format_value
10
+ # method below.
11
+ # @param name [String] the attribute on model to retrieve
12
+ # @example formatting the foo property
13
+ # class MyCell < Cells::ViewModel
14
+ # include Nandi::Formatting
15
+ #
16
+ # formatted_property :foo
17
+ # end
18
+ def formatted_property(name)
19
+ define_method(name) do
20
+ format_value(model.send(name))
21
+ end
22
+ end
23
+ end
24
+
25
+ # Create a string representation of the value that is a valid
26
+ # and correct Ruby literal for that value. Please note that the
27
+ # exact representation is not guaranteed to be the same between
28
+ # different platforms and Ruby versions. The guarantee is merely
29
+ # that calling this method with any supported type will produce
30
+ # a string that produces an equal value when it is passed to
31
+ # Kernel::eval
32
+ # @param value [Hash, Array, String, Symbol, Integer, Float, NilClass]
33
+ # value to format
34
+ def format_value(value, opts = {})
35
+ case value
36
+ when Hash
37
+ format_hash(value, opts)
38
+ when Array
39
+ "[#{value.map { |v| format_value(v, opts) }.join(', ')}]"
40
+ when String, Symbol, Integer, Float, NilClass, TrueClass, FalseClass
41
+ value.inspect
42
+ else
43
+ raise UnsupportedValueError,
44
+ "Cannot format value of type #{value.class.name}"
45
+ end
46
+ end
47
+
48
+ def self.included(base)
49
+ base.extend(ClassMethods)
50
+ end
51
+
52
+ private
53
+
54
+ def format_hash(value, opts)
55
+ if opts[:as_argument]
56
+ hash_pairs(value).join(", ")
57
+ else
58
+ "{\n #{hash_pairs(value).join(",\n ")}\n}"
59
+ end
60
+ end
61
+
62
+ def hash_pairs(value)
63
+ value.map do |k, v|
64
+ key = if k.is_a?(Symbol)
65
+ symbol_key(k)
66
+ else
67
+ format_value(k) + " =>"
68
+ end
69
+ "#{key} #{format_value(v)}"
70
+ end
71
+ end
72
+
73
+ def symbol_key(key)
74
+ canonical = key.inspect
75
+
76
+ "#{canonical[1..-1]}:"
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nandi/instructions/add_index"
4
+ require "nandi/instructions/remove_index"
5
+ require "nandi/instructions/create_table"
6
+ require "nandi/instructions/drop_table"
7
+ require "nandi/instructions/add_column"
8
+ require "nandi/instructions/remove_column"
9
+ require "nandi/instructions/add_foreign_key"
10
+ require "nandi/instructions/drop_constraint"
11
+ require "nandi/instructions/remove_not_null_constraint"
12
+ require "nandi/instructions/change_column_default"
13
+ require "nandi/instructions/validate_constraint"
14
+ require "nandi/instructions/add_check_constraint"
15
+ require "nandi/instructions/irreversible_migration"
16
+
17
+ module Nandi
18
+ module Instructions; end
19
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nandi
4
+ module Instructions
5
+ class AddCheckConstraint
6
+ attr_reader :table, :name, :check
7
+
8
+ def initialize(table:, name:, check:)
9
+ @table = table
10
+ @name = name
11
+ @check = check
12
+ end
13
+
14
+ def procedure
15
+ :add_check_constraint
16
+ end
17
+
18
+ def lock
19
+ Nandi::Migration::LockWeights::ACCESS_EXCLUSIVE
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nandi
4
+ module Instructions
5
+ class AddColumn
6
+ attr_reader :table, :name, :type, :extra_args
7
+
8
+ def initialize(table:, name:, type:, **kwargs)
9
+ @table = table
10
+ @name = name
11
+ @type = type
12
+ @extra_args = kwargs
13
+ end
14
+
15
+ def procedure
16
+ :add_column
17
+ end
18
+
19
+ def lock
20
+ Nandi::Migration::LockWeights::ACCESS_EXCLUSIVE
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/inflector"
4
+
5
+ module Nandi
6
+ module Instructions
7
+ class AddForeignKey
8
+ attr_reader :table, :target
9
+
10
+ def initialize(table:, target:, name: nil, **extra_args)
11
+ @table = table
12
+ @target = target
13
+ @extra_args = extra_args
14
+ @name = name
15
+ end
16
+
17
+ def procedure
18
+ :add_foreign_key
19
+ end
20
+
21
+ def extra_args
22
+ {
23
+ **@extra_args,
24
+ name: name,
25
+ validate: false,
26
+ }.compact
27
+ end
28
+
29
+ def lock
30
+ Nandi::Migration::LockWeights::ACCESS_EXCLUSIVE
31
+ end
32
+
33
+ private
34
+
35
+ def name
36
+ @name || :"#{table}_#{target}_fk"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nandi
4
+ module Instructions
5
+ class AddIndex
6
+ def initialize(fields:, table:, **kwargs)
7
+ @fields = Array(fields)
8
+ @fields = @fields.first if @fields.one?
9
+
10
+ @table = table
11
+ @extra_args = kwargs
12
+ end
13
+
14
+ def procedure
15
+ :add_index
16
+ end
17
+
18
+ def extra_args
19
+ {
20
+ # Overridable defaults
21
+ name: name,
22
+
23
+ # Overrides and extra options
24
+ **@extra_args,
25
+
26
+ # Mandatory values
27
+ algorithm: :concurrently,
28
+ using: :btree,
29
+ }
30
+ end
31
+
32
+ def lock
33
+ Nandi::Migration::LockWeights::SHARE
34
+ end
35
+
36
+ attr_reader :table, :fields
37
+
38
+ private
39
+
40
+ def name
41
+ :"idx_#{table.to_s}_on_#{field_names}"
42
+ end
43
+
44
+ def field_names
45
+ field_names = fields.respond_to?(:map) ? fields.map(&:to_s).join("_") : fields
46
+ field_names.to_s.scan(/\w+/).join("_")
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nandi
4
+ module Instructions
5
+ class ChangeColumnDefault
6
+ attr_reader :table, :column, :value
7
+
8
+ def initialize(table:, column:, value:)
9
+ @table = table
10
+ @column = column
11
+ @value = value
12
+ end
13
+
14
+ def procedure
15
+ :change_column_default
16
+ end
17
+
18
+ def lock
19
+ Nandi::Migration::LockWeights::ACCESS_EXCLUSIVE
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ostruct"
4
+
5
+ module Nandi
6
+ module Instructions
7
+ class CreateTable
8
+ attr_reader :table, :columns, :timestamps_args, :extra_args
9
+
10
+ def initialize(table:, columns_block:, **kwargs)
11
+ @table = table
12
+ columns_reader = ColumnsReader.new
13
+ columns_block.call(columns_reader)
14
+ @columns = columns_reader.columns
15
+ @extra_args = kwargs unless kwargs.empty?
16
+ @timestamps_args = columns_reader.timestamps_args
17
+ end
18
+
19
+ def procedure
20
+ :create_table
21
+ end
22
+
23
+ def lock
24
+ Nandi::Migration::LockWeights::ACCESS_EXCLUSIVE
25
+ end
26
+
27
+ class ColumnsReader
28
+ attr_reader :columns, :timestamps_args
29
+
30
+ TYPES = %i[
31
+ bigint
32
+ binary
33
+ boolean
34
+ date
35
+ datetime
36
+ decimal
37
+ float
38
+ integer
39
+ json
40
+ string
41
+ text
42
+ time
43
+ timestamp
44
+ virtual
45
+ bigserial bit bit_varying box
46
+ cidr circle citext
47
+ daterange
48
+ hstore
49
+ inet int4range int8range interval
50
+ jsonb
51
+ line lseg ltree
52
+ macaddr money
53
+ numrange
54
+ oid
55
+ path point polygon primary_key
56
+ serial
57
+ tsrange tstzrange tsvector
58
+ uuid
59
+ xml
60
+ ].freeze
61
+
62
+ def initialize
63
+ @columns = []
64
+ @timestamps_args = nil
65
+ end
66
+
67
+ def column(name, type, **args)
68
+ @columns << OpenStruct.new(name: name, type: type, args: args)
69
+ end
70
+
71
+ def timestamps(**args)
72
+ @timestamps_args = args
73
+ end
74
+
75
+ TYPES.each do |type|
76
+ define_method type do |name, **args|
77
+ column(name, type, **args)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nandi
4
+ module Instructions
5
+ class DropConstraint
6
+ attr_reader :table, :name
7
+
8
+ def initialize(table:, name:)
9
+ @table = table
10
+ @name = name
11
+ end
12
+
13
+ def procedure
14
+ :drop_constraint
15
+ end
16
+
17
+ def lock
18
+ Nandi::Migration::LockWeights::ACCESS_EXCLUSIVE
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nandi
4
+ module Instructions
5
+ class DropTable
6
+ attr_reader :table
7
+
8
+ def initialize(table:)
9
+ @table = table
10
+ end
11
+
12
+ def procedure
13
+ :drop_table
14
+ end
15
+
16
+ def lock
17
+ Nandi::Migration::LockWeights::ACCESS_EXCLUSIVE
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nandi
4
+ module Instructions
5
+ class IrreversibleMigration
6
+ def lock
7
+ Nandi::Migration::LockWeights::SHARE
8
+ end
9
+
10
+ def procedure
11
+ :irreversible_migration
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nandi
4
+ module Instructions
5
+ class RemoveColumn
6
+ attr_reader :table, :name, :extra_args
7
+
8
+ def initialize(table:, name:, **extra_args)
9
+ @table = table
10
+ @name = name
11
+ @extra_args = extra_args if extra_args.any?
12
+ end
13
+
14
+ def procedure
15
+ :remove_column
16
+ end
17
+
18
+ def lock
19
+ Nandi::Migration::LockWeights::ACCESS_EXCLUSIVE
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nandi
4
+ module Instructions
5
+ class RemoveIndex
6
+ def initialize(table:, field:)
7
+ @table = table
8
+ @field = field
9
+ end
10
+
11
+ def procedure
12
+ :remove_index
13
+ end
14
+
15
+ def extra_args
16
+ if field.is_a?(Hash)
17
+ field.merge(algorithm: :concurrently)
18
+ else
19
+ { column: columns, algorithm: :concurrently }
20
+ end
21
+ end
22
+
23
+ def lock
24
+ Nandi::Migration::LockWeights::SHARE
25
+ end
26
+
27
+ attr_reader :table
28
+
29
+ private
30
+
31
+ attr_reader :field
32
+
33
+ def columns
34
+ columns = Array(field)
35
+ columns = columns.first if columns.one?
36
+
37
+ columns
38
+ end
39
+ end
40
+ end
41
+ end