nandi 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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