dynamic_migrations 2.1.0 → 3.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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/lib/dynamic_migrations/active_record/migrators/column.rb +21 -0
  4. data/lib/dynamic_migrations/active_record/migrators/foreign_key_constraint.rb +112 -0
  5. data/lib/dynamic_migrations/active_record/migrators/function.rb +108 -0
  6. data/lib/dynamic_migrations/active_record/migrators/index.rb +27 -0
  7. data/lib/dynamic_migrations/active_record/migrators/schema.rb +21 -0
  8. data/lib/dynamic_migrations/active_record/migrators/table.rb +21 -0
  9. data/lib/dynamic_migrations/active_record/migrators/trigger.rb +109 -0
  10. data/lib/dynamic_migrations/active_record/migrators/unique_constraint.rb +63 -0
  11. data/lib/dynamic_migrations/active_record/migrators/validation.rb +67 -0
  12. data/lib/dynamic_migrations/active_record/migrators.rb +64 -0
  13. data/lib/dynamic_migrations/name_helper.rb +13 -0
  14. data/lib/dynamic_migrations/postgres/generator/column.rb +92 -0
  15. data/lib/dynamic_migrations/postgres/generator/foreign_key_constraint.rb +84 -0
  16. data/lib/dynamic_migrations/postgres/generator/fragment.rb +30 -0
  17. data/lib/dynamic_migrations/postgres/generator/function.rb +77 -0
  18. data/lib/dynamic_migrations/postgres/generator/index.rb +101 -0
  19. data/lib/dynamic_migrations/postgres/generator/primary_key.rb +55 -0
  20. data/lib/dynamic_migrations/postgres/generator/schema.rb +19 -0
  21. data/lib/dynamic_migrations/postgres/generator/schema_migrations/section.rb +37 -0
  22. data/lib/dynamic_migrations/postgres/generator/schema_migrations.rb +92 -0
  23. data/lib/dynamic_migrations/postgres/generator/table.rb +122 -0
  24. data/lib/dynamic_migrations/postgres/generator/trigger.rb +101 -0
  25. data/lib/dynamic_migrations/postgres/generator/unique_constraint.rb +79 -0
  26. data/lib/dynamic_migrations/postgres/generator/validation.rb +87 -0
  27. data/lib/dynamic_migrations/postgres/generator.rb +359 -0
  28. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/functions.rb +68 -0
  29. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/columns.rb +72 -0
  30. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/foreign_key_constraints.rb +73 -0
  31. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/indexes.rb +73 -0
  32. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/primary_key.rb +49 -0
  33. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/triggers.rb +73 -0
  34. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/unique_constraints.rb +73 -0
  35. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/validations.rb +73 -0
  36. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables.rb +80 -0
  37. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas.rb +48 -0
  38. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations.rb +59 -0
  39. data/lib/dynamic_migrations/postgres/server/database/differences.rb +76 -16
  40. data/lib/dynamic_migrations/postgres/server/database/keys_and_unique_constraints_loader.rb +35 -9
  41. data/lib/dynamic_migrations/postgres/server/database/loaded_schemas_builder.rb +50 -26
  42. data/lib/dynamic_migrations/postgres/server/database/schema/function.rb +69 -0
  43. data/lib/dynamic_migrations/postgres/server/database/schema/functions.rb +63 -0
  44. data/lib/dynamic_migrations/postgres/server/database/schema/table/column.rb +6 -44
  45. data/lib/dynamic_migrations/postgres/server/database/schema/table/columns.rb +1 -1
  46. data/lib/dynamic_migrations/postgres/server/database/schema/table/foreign_key_constraint.rb +40 -5
  47. data/lib/dynamic_migrations/postgres/server/database/schema/table/index.rb +23 -9
  48. data/lib/dynamic_migrations/postgres/server/database/schema/table/primary_key.rb +21 -6
  49. data/lib/dynamic_migrations/postgres/server/database/schema/table/trigger.rb +151 -0
  50. data/lib/dynamic_migrations/postgres/server/database/schema/table/triggers.rb +66 -0
  51. data/lib/dynamic_migrations/postgres/server/database/schema/table/unique_constraint.rb +19 -9
  52. data/lib/dynamic_migrations/postgres/server/database/schema/table/validation.rb +20 -1
  53. data/lib/dynamic_migrations/postgres/server/database/schema/table.rb +15 -5
  54. data/lib/dynamic_migrations/postgres/server/database/schema/tables.rb +63 -0
  55. data/lib/dynamic_migrations/postgres/server/database/schema.rb +3 -49
  56. data/lib/dynamic_migrations/postgres/server/database/source.rb +21 -0
  57. data/lib/dynamic_migrations/postgres/server/database/structure_loader.rb +22 -112
  58. data/lib/dynamic_migrations/postgres/server/database/triggers_and_functions_loader.rb +131 -0
  59. data/lib/dynamic_migrations/postgres/server/database/validations_loader.rb +10 -4
  60. data/lib/dynamic_migrations/postgres/server/database.rb +2 -1
  61. data/lib/dynamic_migrations/postgres/server.rb +6 -0
  62. data/lib/dynamic_migrations/postgres.rb +1 -1
  63. data/lib/dynamic_migrations/version.rb +1 -1
  64. data/lib/dynamic_migrations.rb +47 -4
  65. metadata +44 -3
  66. data/lib/dynamic_migrations/postgres/data_types.rb +0 -320
@@ -0,0 +1,122 @@
1
+ module DynamicMigrations
2
+ module Postgres
3
+ class Generator
4
+ module Table
5
+ class NoTableCommentError < StandardError
6
+ end
7
+
8
+ class NoTableColumnCommentError < StandardError
9
+ end
10
+
11
+ def create_table table, code_comment = nil
12
+ if table.description.nil?
13
+ raise NoTableCommentError, "Refusing to generate create_table migration, no description was provided for `#{table.schema.name}`.`#{table.name}`"
14
+ end
15
+
16
+ add_migration table.schema.name, table.name, :create_table, table.name, code_comment, <<~RUBY
17
+ table_comment = <<~COMMENT
18
+ #{indent table.description}
19
+ COMMENT
20
+ create_table :#{table.name}, #{table_options table} do |t|
21
+ #{indent table_columns(table.columns)}
22
+ end
23
+ RUBY
24
+ end
25
+
26
+ def drop_table table, code_comment = nil
27
+ add_migration table.schema.name, table.name, :drop_table, table.name, code_comment, <<~RUBY
28
+ drop_table :#{table.name}, force: true
29
+ RUBY
30
+ end
31
+
32
+ # add a comment to a table
33
+ def set_table_comment table, code_comment = nil
34
+ description = table.description
35
+
36
+ if description.nil?
37
+ raise MissingDescriptionError
38
+ end
39
+
40
+ add_migration table.schema.name, table.name, :set_table_comment, table.name, code_comment, <<~RUBY
41
+ set_table_comment :#{table.name}, <<~COMMENT
42
+ #{indent description}
43
+ COMMENT
44
+ RUBY
45
+ end
46
+
47
+ # remove the comment from a table
48
+ def remove_table_comment table, code_comment = nil
49
+ add_migration table.schema.name, table.name, :remove_table_comment, table.name, code_comment, <<~RUBY
50
+ remove_table_comment :#{table.name}
51
+ RUBY
52
+ end
53
+
54
+ private
55
+
56
+ def table_columns columns
57
+ lines = []
58
+ timestamps = []
59
+ columns.each do |column|
60
+ # skip the :id, as it is handled by the table_options method
61
+ next if column.name == :id
62
+ # skip the :created_at and :updated_at as they are handled below
63
+ if column.name == :created_at || column.name == :updated_at
64
+ timestamps << column.name
65
+ next
66
+ end
67
+
68
+ options = {}
69
+ options[:null] = column.null
70
+
71
+ unless column.default.nil?
72
+ options[:default] = "\"#{column.default}\""
73
+ end
74
+
75
+ if column.description.nil?
76
+ raise NoTableColumnCommentError, "Refusing to generate create_table migration, no description was provided for `#{column.table.schema.name}`.`#{column.table.name}` column `#{column.name}`"
77
+ end
78
+ options[:comment] = <<~RUBY
79
+ <<~COMMENT
80
+ #{indent column.description}
81
+ COMMENT
82
+ RUBY
83
+
84
+ options_syntax = options.map { |k, v| "#{k}: #{v}" }.join(", ")
85
+
86
+ lines << "t.#{column.data_type} :#{column.name}, #{options_syntax}"
87
+ end
88
+
89
+ if timestamps.any?
90
+ lines << "t.timestamps :#{timestamps.join(", :")}"
91
+ end
92
+
93
+ lines.join("\n")
94
+ end
95
+
96
+ def table_options table
97
+ options = []
98
+ options << if table.has_column?(:id)
99
+ "id: :#{table.column(:id).data_type}"
100
+ else
101
+ "id: false"
102
+ end
103
+
104
+ if table.has_primary_key?
105
+ pk_column_names = table.primary_key.columns.map(&:name)
106
+ # if there is only one primary key column and it is not called id, then
107
+ # we define it here. If it is called :id then we don't need to define it
108
+ if pk_column_names.count == 1 && pk_column_names.first != :id
109
+ options << "primary_key: :#{pk_column_names.first}"
110
+ elsif pk_column_names.count > 1
111
+ options << "primary_key: [:#{pk_column_names.join(", :")}]"
112
+ end
113
+ end
114
+
115
+ options << "comment: table_comment"
116
+
117
+ options.join(", ")
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,101 @@
1
+ module DynamicMigrations
2
+ module Postgres
3
+ class Generator
4
+ module Trigger
5
+ def add_trigger trigger, code_comment = nil
6
+ options = {
7
+ name: ":#{trigger.name}"
8
+ }
9
+
10
+ # if the trigger is a row trigger and the action timing is before or after
11
+ # then we can use some syntactic sugar to make the migration look nicer
12
+ # we use method names like before_insert, after_update, etc. and drop the
13
+ # unnessessary options
14
+ if trigger.action_orientation == :row && [:before, :after].include?(trigger.action_timing)
15
+ method_name = "#{trigger.action_timing}_#{trigger.event_manipulation}"
16
+ else
17
+ method_name = "add_trigger"
18
+ options[:action_timing] = ":#{trigger.action_timing}"
19
+ options[:event_manipulation] = ":#{trigger.event_manipulation}"
20
+ options[:action_orientation] = ":#{trigger.action_orientation}"
21
+ end
22
+
23
+ # added here because we want the timing and manipulation to visually appear first
24
+ options[:function_schema_name] = ":#{trigger.function.schema.name}"
25
+ options[:function_name] = ":#{trigger.function.name}"
26
+
27
+ unless trigger.action_condition.nil?
28
+ options[:action_condition] = ":#{trigger.action_condition}"
29
+ end
30
+
31
+ unless trigger.action_reference_old_table.nil?
32
+ options[:action_reference_old_table] = ":#{trigger.action_reference_old_table}"
33
+ end
34
+
35
+ unless trigger.action_reference_new_table.nil?
36
+ options[:action_reference_new_table] = ":#{trigger.action_reference_new_table}"
37
+ end
38
+
39
+ unless trigger.description.nil?
40
+ options[:comment] = <<~RUBY
41
+ <<~COMMENT
42
+ #{indent trigger.description}
43
+ COMMENT
44
+ RUBY
45
+ end
46
+
47
+ options_syntax = options.map { |k, v| "#{k}: #{v}" }.join(", ")
48
+
49
+ add_migration trigger.table.schema.name, trigger.table.name, :add_trigger, trigger.name, code_comment, <<~RUBY
50
+ #{method_name} :#{trigger.table.name}, #{options_syntax}
51
+ RUBY
52
+ end
53
+
54
+ def remove_trigger trigger, code_comment = nil
55
+ add_migration trigger.table.schema.name, trigger.table.name, :remove_trigger, trigger.name, code_comment, <<~RUBY
56
+ remove_trigger :#{trigger.table.name}, :#{trigger.name}
57
+ RUBY
58
+ end
59
+
60
+ def recreate_trigger original_trigger, updated_trigger
61
+ # remove the original trigger
62
+ removal_fragment = remove_trigger original_trigger, <<~CODE_COMMENT
63
+ Removing original trigger because it has changed (it is recreated below)
64
+ Changes:
65
+ #{indent original_trigger.differences_descriptions(updated_trigger).join("\n")}
66
+ CODE_COMMENT
67
+
68
+ # recrete the trigger with the new options
69
+ recreation_fragment = add_trigger updated_trigger, <<~CODE_COMMENT
70
+ Recreating this trigger
71
+ CODE_COMMENT
72
+
73
+ # return the new fragments (the main reason to return them here is for the specs)
74
+ [removal_fragment, recreation_fragment]
75
+ end
76
+
77
+ # add a comment to a trigger
78
+ def set_trigger_comment trigger, code_comment = nil
79
+ description = trigger.description
80
+
81
+ if description.nil?
82
+ raise MissingDescriptionError
83
+ end
84
+
85
+ add_migration trigger.table.schema.name, trigger.table.name, :set_trigger_comment, trigger.name, code_comment, <<~RUBY
86
+ set_trigger_comment :#{trigger.table.name}, :#{trigger.name}, <<~COMMENT
87
+ #{indent description}
88
+ COMMENT
89
+ RUBY
90
+ end
91
+
92
+ # remove the comment from a trigger
93
+ def remove_trigger_comment trigger, code_comment = nil
94
+ add_migration trigger.table.schema.name, trigger.table.name, :remove_trigger_comment, trigger.name, code_comment, <<~RUBY
95
+ remove_trigger_comment :#{trigger.table.name}, :#{trigger.name}
96
+ RUBY
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,79 @@
1
+ module DynamicMigrations
2
+ module Postgres
3
+ class Generator
4
+ module UniqueConstraint
5
+ def add_unique_constraint unique_constraint, code_comment = nil
6
+ # the migration accepts either a single column name or an array of column names
7
+ # we use the appropriate syntax just to make the migration prettier and easier
8
+ # to understand
9
+ column_names = (unique_constraint.column_names.count == 1) ? ":#{unique_constraint.column_names.first}" : "[:#{unique_constraint.column_names.join(", :")}]"
10
+
11
+ options = {
12
+ name: ":#{unique_constraint.name}",
13
+ deferrable: unique_constraint.deferrable,
14
+ initially_deferred: unique_constraint.initially_deferred
15
+ }
16
+
17
+ unless unique_constraint.description.nil?
18
+ options[:comment] = <<~RUBY
19
+ <<~COMMENT
20
+ #{indent unique_constraint.description}
21
+ COMMENT
22
+ RUBY
23
+ end
24
+
25
+ options_syntax = options.map { |k, v| "#{k}: #{v}" }.join(", ")
26
+
27
+ add_migration unique_constraint.table.schema.name, unique_constraint.table.name, :add_unique_constraint, unique_constraint.name, code_comment, <<~RUBY
28
+ add_unique_constraint :#{unique_constraint.table.name}, #{column_names}, #{options_syntax}
29
+ RUBY
30
+ end
31
+
32
+ def remove_unique_constraint unique_constraint, code_comment = nil
33
+ add_migration unique_constraint.table.schema.name, unique_constraint.table.name, :remove_unique_constraint, unique_constraint.name, code_comment, <<~RUBY
34
+ remove_unique_constraint :#{unique_constraint.table.name}, :#{unique_constraint.name}
35
+ RUBY
36
+ end
37
+
38
+ def recreate_unique_constraint original_unique_constraint, updated_unique_constraint
39
+ # remove the original unique_constraint
40
+ removal_fragment = remove_unique_constraint original_unique_constraint, <<~CODE_COMMENT
41
+ Removing original unique constraint because it has changed (it is recreated below)
42
+ Changes:
43
+ #{indent original_unique_constraint.differences_descriptions(updated_unique_constraint).join("\n")}
44
+ CODE_COMMENT
45
+
46
+ # recrete the unique_constraint with the new options
47
+ recreation_fragment = add_unique_constraint updated_unique_constraint, <<~CODE_COMMENT
48
+ Recreating this unique constraint
49
+ CODE_COMMENT
50
+
51
+ # return the new fragments (the main reason to return them here is for the specs)
52
+ [removal_fragment, recreation_fragment]
53
+ end
54
+
55
+ # add a comment to a unique_constraint
56
+ def set_unique_constraint_comment unique_constraint, code_comment = nil
57
+ description = unique_constraint.description
58
+
59
+ if description.nil?
60
+ raise MissingDescriptionError
61
+ end
62
+
63
+ add_migration unique_constraint.table.schema.name, unique_constraint.table.name, :set_unique_constraint_comment, unique_constraint.name, code_comment, <<~RUBY
64
+ set_unique_constraint_comment :#{unique_constraint.table.name}, :#{unique_constraint.name}, <<~COMMENT
65
+ #{indent description}
66
+ COMMENT
67
+ RUBY
68
+ end
69
+
70
+ # remove the comment from a unique_constraint
71
+ def remove_unique_constraint_comment unique_constraint, code_comment = nil
72
+ add_migration unique_constraint.table.schema.name, unique_constraint.table.name, :remove_unique_constraint_comment, unique_constraint.name, code_comment, <<~RUBY
73
+ remove_unique_constraint_comment :#{unique_constraint.table.name}, :#{unique_constraint.name}
74
+ RUBY
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,87 @@
1
+ module DynamicMigrations
2
+ module Postgres
3
+ class Generator
4
+ module Validation
5
+ def add_validation validation, code_comment = nil
6
+ options = {
7
+ name: ":#{validation.name}",
8
+ deferrable: validation.deferrable,
9
+ initially_deferred: validation.initially_deferred
10
+ }
11
+
12
+ if validation.description.nil?
13
+ comment_sql = ""
14
+ else
15
+ comment_sql = <<~RUBY
16
+ #{validation.name}_comment = <<~COMMENT
17
+ #{indent validation.description || ""}
18
+ COMMENT
19
+ RUBY
20
+ options[:comment] = "#{validation.name}_comment"
21
+ end
22
+
23
+ options_syntax = options.map { |k, v| "#{k}: #{v}" }.join(", ")
24
+
25
+ validation_sql = validation.check_clause.strip
26
+ # ensure that the validation ends with a semicolon
27
+ unless validation_sql.end_with? ";"
28
+ validation_sql << ";"
29
+ end
30
+
31
+ add_migration validation.table.schema.name, validation.table.name, :add_validation, validation.name, code_comment, (comment_sql + <<~RUBY)
32
+ add_validation :#{validation.table.name}, #{options_syntax} do
33
+ <<~SQL
34
+ #{indent validation_sql}
35
+ SQL
36
+ end
37
+ RUBY
38
+ end
39
+
40
+ def remove_validation validation, code_comment = nil
41
+ add_migration validation.table.schema.name, validation.table.name, :remove_validation, validation.name, code_comment, <<~RUBY
42
+ remove_validation :#{validation.table.name}, :#{validation.name}
43
+ RUBY
44
+ end
45
+
46
+ def recreate_validation original_validation, updated_validation
47
+ # remove the original validation
48
+ removal_fragment = remove_validation original_validation, <<~CODE_COMMENT
49
+ Removing original validation because it has changed (it is recreated below)
50
+ Changes:
51
+ #{indent original_validation.differences_descriptions(updated_validation).join("\n")}
52
+ CODE_COMMENT
53
+
54
+ # recrete the validation with the new options
55
+ recreation_fragment = add_validation updated_validation, <<~CODE_COMMENT
56
+ Recreating this validation
57
+ CODE_COMMENT
58
+
59
+ # return the new fragments (the main reason to return them here is for the specs)
60
+ [removal_fragment, recreation_fragment]
61
+ end
62
+
63
+ # add a comment to a validation
64
+ def set_validation_comment validation, code_comment = nil
65
+ description = validation.description
66
+
67
+ if description.nil?
68
+ raise MissingDescriptionError
69
+ end
70
+
71
+ add_migration validation.table.schema.name, validation.table.name, :set_validation_comment, validation.name, code_comment, <<~RUBY
72
+ set_validation_comment :#{validation.table.name}, :#{validation.name}, <<~COMMENT
73
+ #{indent description}
74
+ COMMENT
75
+ RUBY
76
+ end
77
+
78
+ # remove the comment from a validation
79
+ def remove_validation_comment validation, code_comment = nil
80
+ add_migration validation.table.schema.name, validation.table.name, :remove_validation_comment, validation.name, code_comment, <<~RUBY
81
+ remove_validation_comment :#{validation.table.name}, :#{validation.name}
82
+ RUBY
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end