dynamic_migrations 2.2.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -4
  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 +81 -6
  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 +49 -8
  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 +4 -0
  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 +6 -6
  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 -3
  65. metadata +44 -2
@@ -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