dynamic_migrations 3.1.0 → 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/dynamic_migrations/postgres/generator/column.rb +44 -19
- data/lib/dynamic_migrations/postgres/generator/foreign_key_constraint.rb +35 -14
- data/lib/dynamic_migrations/postgres/generator/fragment.rb +37 -2
- data/lib/dynamic_migrations/postgres/generator/function.rb +50 -25
- data/lib/dynamic_migrations/postgres/generator/index.rb +34 -14
- data/lib/dynamic_migrations/postgres/generator/migration.rb +175 -0
- data/lib/dynamic_migrations/postgres/generator/migration_dependency_sorter.rb +21 -0
- data/lib/dynamic_migrations/postgres/generator/primary_key.rb +16 -6
- data/lib/dynamic_migrations/postgres/generator/schema.rb +14 -6
- data/lib/dynamic_migrations/postgres/generator/schema_migration.rb +17 -0
- data/lib/dynamic_migrations/postgres/generator/table.rb +39 -19
- data/lib/dynamic_migrations/postgres/generator/table_migration.rb +51 -0
- data/lib/dynamic_migrations/postgres/generator/trigger.rb +34 -14
- data/lib/dynamic_migrations/postgres/generator/unique_constraint.rb +34 -14
- data/lib/dynamic_migrations/postgres/generator/validation.rb +38 -18
- data/lib/dynamic_migrations/postgres/generator.rb +163 -294
- data/lib/dynamic_migrations/version.rb +1 -1
- data/lib/dynamic_migrations.rb +4 -2
- metadata +6 -4
- data/lib/dynamic_migrations/postgres/generator/schema_migrations/section.rb +0 -37
- data/lib/dynamic_migrations/postgres/generator/schema_migrations.rb +0 -92
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 971a68fa07394767b18c8781df8be28eada7a1024256698f63bc4c0b0c06a560
|
4
|
+
data.tar.gz: 266f91ca87e1da40a0fbd13a9cde183e167c2950f1d0718169fdb8e4ff4eec52
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4f5ca51ab18b44f7a832d9442c7412e725c6b4d688fd481d45752168a6fb6665b15cf288934399d0eb7ef64e8a78a44730fa138cb0f1ff0e1435c135a8b627e
|
7
|
+
data.tar.gz: 223b0432f2198a98f33dfeba24f156938b47d286908a62571f93b7acfebeef6fc5594f0eac8c47805e01c1038c25af6af68321e0e829d4f673b5a5292587eede
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [3.1.1](https://github.com/craigulliott/dynamic_migrations/compare/v3.1.0...v3.1.1) (2023-08-16)
|
4
|
+
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
* refactoring the migration generator so that it handles dependencies between migrations ([8d0f8d8](https://github.com/craigulliott/dynamic_migrations/commit/8d0f8d8cd4a22b974b6449c62ad2a2d74aeffb2e))
|
9
|
+
|
3
10
|
## [3.1.0](https://github.com/craigulliott/dynamic_migrations/compare/v3.0.0...v3.1.0) (2023-08-14)
|
4
11
|
|
5
12
|
|
@@ -28,11 +28,16 @@ module DynamicMigrations
|
|
28
28
|
data_type = "\"#{data_type}\""
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
add_fragment schema: column.table.schema,
|
32
|
+
table: column.table,
|
33
|
+
migration_method: :add_column,
|
34
|
+
object: column,
|
35
|
+
code_comment: code_comment,
|
36
|
+
migration: <<~RUBY
|
37
|
+
add_column :#{column.table.name}, :#{column.name}, :#{data_type}, #{options_syntax}, comment: <<~COMMENT
|
38
|
+
#{indent column.description}
|
39
|
+
COMMENT
|
40
|
+
RUBY
|
36
41
|
end
|
37
42
|
|
38
43
|
def change_column column, code_comment = nil
|
@@ -54,15 +59,25 @@ module DynamicMigrations
|
|
54
59
|
data_type = ":\"#{data_type}\""
|
55
60
|
end
|
56
61
|
|
57
|
-
|
58
|
-
|
59
|
-
|
62
|
+
add_fragment schema: column.table.schema,
|
63
|
+
table: column.table,
|
64
|
+
migration_method: :change_column,
|
65
|
+
object: column,
|
66
|
+
code_comment: code_comment,
|
67
|
+
migration: <<~RUBY
|
68
|
+
change_column :#{column.table.name}, :#{column.name}, :#{data_type}, #{options_syntax}
|
69
|
+
RUBY
|
60
70
|
end
|
61
71
|
|
62
72
|
def remove_column column, code_comment = nil
|
63
|
-
|
64
|
-
|
65
|
-
|
73
|
+
add_fragment schema: column.table.schema,
|
74
|
+
table: column.table,
|
75
|
+
migration_method: :remove_column,
|
76
|
+
object: column,
|
77
|
+
code_comment: code_comment,
|
78
|
+
migration: <<~RUBY
|
79
|
+
remove_column :#{column.table.name}, :#{column.name}
|
80
|
+
RUBY
|
66
81
|
end
|
67
82
|
|
68
83
|
# add a comment to a column
|
@@ -73,18 +88,28 @@ module DynamicMigrations
|
|
73
88
|
raise MissingDescriptionError
|
74
89
|
end
|
75
90
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
91
|
+
add_fragment schema: column.table.schema,
|
92
|
+
table: column.table,
|
93
|
+
migration_method: :set_column_comment,
|
94
|
+
object: column,
|
95
|
+
code_comment: code_comment,
|
96
|
+
migration: <<~RUBY
|
97
|
+
set_column_comment :#{column.table.name}, :#{column.name}, <<~COMMENT
|
98
|
+
#{indent description}
|
99
|
+
COMMENT
|
100
|
+
RUBY
|
81
101
|
end
|
82
102
|
|
83
103
|
# remove the comment from a column
|
84
104
|
def remove_column_comment column, code_comment = nil
|
85
|
-
|
86
|
-
|
87
|
-
|
105
|
+
add_fragment schema: column.table.schema,
|
106
|
+
table: column.table,
|
107
|
+
migration_method: :remove_column_comment,
|
108
|
+
object: column,
|
109
|
+
code_comment: code_comment,
|
110
|
+
migration: <<~RUBY
|
111
|
+
remove_column_comment :#{column.table.name}, :#{column.name}
|
112
|
+
RUBY
|
88
113
|
end
|
89
114
|
end
|
90
115
|
end
|
@@ -29,15 +29,26 @@ module DynamicMigrations
|
|
29
29
|
|
30
30
|
options_syntax = options.map { |k, v| "#{k}: #{v}" }.join(", ")
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
add_fragment schema: foreign_key_constraint.table.schema,
|
33
|
+
table: foreign_key_constraint.table,
|
34
|
+
migration_method: :add_foreign_key,
|
35
|
+
object: foreign_key_constraint,
|
36
|
+
code_comment: code_comment,
|
37
|
+
dependent_table: foreign_key_constraint.foreign_table,
|
38
|
+
migration: <<~RUBY
|
39
|
+
add_foreign_key :#{foreign_key_constraint.table.name}, #{column_names}, :#{foreign_key_constraint.foreign_table.name}, #{foreign_column_names}, #{options_syntax}
|
40
|
+
RUBY
|
35
41
|
end
|
36
42
|
|
37
43
|
def remove_foreign_key_constraint foreign_key_constraint, code_comment = nil
|
38
|
-
|
39
|
-
|
40
|
-
|
44
|
+
add_fragment schema: foreign_key_constraint.table.schema,
|
45
|
+
table: foreign_key_constraint.table,
|
46
|
+
migration_method: :remove_foreign_key,
|
47
|
+
object: foreign_key_constraint,
|
48
|
+
code_comment: code_comment,
|
49
|
+
migration: <<~RUBY
|
50
|
+
remove_foreign_key :#{foreign_key_constraint.table.name}, :#{foreign_key_constraint.name}
|
51
|
+
RUBY
|
41
52
|
end
|
42
53
|
|
43
54
|
def recreate_foreign_key_constraint original_foreign_key_constraint, updated_foreign_key_constraint
|
@@ -65,18 +76,28 @@ module DynamicMigrations
|
|
65
76
|
raise MissingDescriptionError
|
66
77
|
end
|
67
78
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
79
|
+
add_fragment schema: foreign_key_constraint.table.schema,
|
80
|
+
table: foreign_key_constraint.table,
|
81
|
+
migration_method: :set_foreign_key_constraint_comment,
|
82
|
+
object: foreign_key_constraint,
|
83
|
+
code_comment: code_comment,
|
84
|
+
migration: <<~RUBY
|
85
|
+
set_foreign_key_comment :#{foreign_key_constraint.table.name}, :#{foreign_key_constraint.name}, <<~COMMENT
|
86
|
+
#{indent description}
|
87
|
+
COMMENT
|
88
|
+
RUBY
|
73
89
|
end
|
74
90
|
|
75
91
|
# remove the comment from a foreign_key_constraint
|
76
92
|
def remove_foreign_key_constraint_comment foreign_key_constraint, code_comment = nil
|
77
|
-
|
78
|
-
|
79
|
-
|
93
|
+
add_fragment schema: foreign_key_constraint.table.schema,
|
94
|
+
table: foreign_key_constraint.table,
|
95
|
+
migration_method: :remove_foreign_key_constraint_comment,
|
96
|
+
object: foreign_key_constraint,
|
97
|
+
code_comment: code_comment,
|
98
|
+
migration: <<~RUBY
|
99
|
+
remove_foreign_key_comment :#{foreign_key_constraint.table.name}, :#{foreign_key_constraint.name}
|
100
|
+
RUBY
|
80
101
|
end
|
81
102
|
end
|
82
103
|
end
|
@@ -2,15 +2,25 @@ module DynamicMigrations
|
|
2
2
|
module Postgres
|
3
3
|
class Generator
|
4
4
|
class Fragment
|
5
|
+
attr_reader :schema_name
|
6
|
+
attr_reader :table_name
|
7
|
+
attr_reader :migration_method
|
5
8
|
attr_reader :object_name
|
6
|
-
attr_reader :
|
9
|
+
attr_reader :dependency_schema_name
|
10
|
+
attr_reader :dependency_table_name
|
7
11
|
|
8
|
-
def initialize object_name, code_comment, content
|
12
|
+
def initialize schema_name, table_name, migration_method, object_name, code_comment, content
|
13
|
+
@schema_name = schema_name
|
14
|
+
@table_name = table_name
|
15
|
+
@migration_method = migration_method
|
9
16
|
@object_name = object_name
|
10
17
|
@code_comment = code_comment
|
11
18
|
@content = content
|
12
19
|
end
|
13
20
|
|
21
|
+
# Returns a string representation of the fragment for use in the final
|
22
|
+
# migration. This final string is a combination of the code_comment (if present)
|
23
|
+
# and the content of the fragment.
|
14
24
|
def to_s
|
15
25
|
strings = []
|
16
26
|
comment = @code_comment
|
@@ -21,9 +31,34 @@ module DynamicMigrations
|
|
21
31
|
strings.join("\n").strip
|
22
32
|
end
|
23
33
|
|
34
|
+
# Returns true if the fragment has a code comment, otherwise false.
|
24
35
|
def has_code_comment?
|
25
36
|
!@code_comment.nil?
|
26
37
|
end
|
38
|
+
|
39
|
+
# If a table dependency has been set, then returns a hash with the schema_name
|
40
|
+
# and table_name, otherwise returns nil.
|
41
|
+
def dependency
|
42
|
+
if dependency_schema_name && dependency_table_name
|
43
|
+
{
|
44
|
+
schema_name: dependency_schema_name,
|
45
|
+
table_name: dependency_table_name
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# returns true if the fragment has a table dependency, and the dependency matches
|
51
|
+
# the provided schema_name and table_name, otherwise returns false.
|
52
|
+
def is_dependent_on? schema_name, table_name
|
53
|
+
dependency_schema_name && schema_name == dependency_schema_name && table_name == dependency_table_name || false
|
54
|
+
end
|
55
|
+
|
56
|
+
# Set the table table dependency for this fragment. Takes a schema name and
|
57
|
+
# table name
|
58
|
+
def set_dependent_table schema_name, table_name
|
59
|
+
@dependency_schema_name = schema_name
|
60
|
+
@dependency_table_name = table_name
|
61
|
+
end
|
27
62
|
end
|
28
63
|
end
|
29
64
|
end
|
@@ -25,13 +25,18 @@ module DynamicMigrations
|
|
25
25
|
fn_sql << ";"
|
26
26
|
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
28
|
+
add_fragment schema: function.schema,
|
29
|
+
table: function.triggers.first&.table,
|
30
|
+
migration_method: :create_function,
|
31
|
+
object: function,
|
32
|
+
code_comment: code_comment,
|
33
|
+
migration: comment_sql + <<~RUBY
|
34
|
+
create_function :#{function.name}#{optional_options_syntax} do
|
35
|
+
<<~SQL
|
36
|
+
#{indent fn_sql}
|
37
|
+
SQL
|
38
|
+
end
|
39
|
+
RUBY
|
35
40
|
end
|
36
41
|
|
37
42
|
def update_function function, code_comment = nil
|
@@ -41,35 +46,55 @@ module DynamicMigrations
|
|
41
46
|
fn_sql << ";"
|
42
47
|
end
|
43
48
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
49
|
+
add_fragment schema: function.schema,
|
50
|
+
table: function.triggers.first&.table,
|
51
|
+
migration_method: :update_function,
|
52
|
+
object: function,
|
53
|
+
code_comment: code_comment,
|
54
|
+
migration: <<~RUBY
|
55
|
+
update_function :#{function.name} do
|
56
|
+
<<~SQL
|
57
|
+
#{indent fn_sql}
|
58
|
+
SQL
|
59
|
+
end
|
60
|
+
RUBY
|
51
61
|
end
|
52
62
|
|
53
63
|
def drop_function function, code_comment = nil
|
54
|
-
|
55
|
-
|
56
|
-
|
64
|
+
add_fragment schema: function.schema,
|
65
|
+
table: function.triggers.first&.table,
|
66
|
+
migration_method: :drop_function,
|
67
|
+
object: function,
|
68
|
+
code_comment: code_comment,
|
69
|
+
migration: <<~RUBY
|
70
|
+
drop_function :#{function.name}
|
71
|
+
RUBY
|
57
72
|
end
|
58
73
|
|
59
74
|
# add a comment to a function
|
60
75
|
def set_function_comment function, code_comment = nil
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
76
|
+
add_fragment schema: function.schema,
|
77
|
+
table: function.triggers.first&.table,
|
78
|
+
migration_method: :set_function_comment,
|
79
|
+
object: function,
|
80
|
+
code_comment: code_comment,
|
81
|
+
migration: <<~RUBY
|
82
|
+
set_function_comment :#{function.name}, <<~COMMENT
|
83
|
+
#{indent function.description || ""}
|
84
|
+
COMMENT
|
85
|
+
RUBY
|
66
86
|
end
|
67
87
|
|
68
88
|
# remove the comment from a function
|
69
89
|
def remove_function_comment function, code_comment = nil
|
70
|
-
|
71
|
-
|
72
|
-
|
90
|
+
add_fragment schema: function.schema,
|
91
|
+
table: function.triggers.first&.table,
|
92
|
+
migration_method: :remove_function_comment,
|
93
|
+
object: function,
|
94
|
+
code_comment: code_comment,
|
95
|
+
migration: <<~RUBY
|
96
|
+
remove_function_comment :#{function.name}
|
97
|
+
RUBY
|
73
98
|
end
|
74
99
|
end
|
75
100
|
end
|
@@ -46,15 +46,25 @@ module DynamicMigrations
|
|
46
46
|
|
47
47
|
options_syntax = options.map { |k, v| "#{k}: #{v}" }.join(", ")
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
add_fragment schema: index.table.schema,
|
50
|
+
table: index.table,
|
51
|
+
migration_method: :add_index,
|
52
|
+
object: index,
|
53
|
+
code_comment: code_comment,
|
54
|
+
migration: where_sql + <<~RUBY
|
55
|
+
add_index :#{index.table.name}, #{column_names}, #{options_syntax}
|
56
|
+
RUBY
|
52
57
|
end
|
53
58
|
|
54
59
|
def remove_index index, code_comment = nil
|
55
|
-
|
56
|
-
|
57
|
-
|
60
|
+
add_fragment schema: index.table.schema,
|
61
|
+
table: index.table,
|
62
|
+
migration_method: :remove_index,
|
63
|
+
object: index,
|
64
|
+
code_comment: code_comment,
|
65
|
+
migration: <<~RUBY
|
66
|
+
remove_index :#{index.table.name}, :#{index.name}
|
67
|
+
RUBY
|
58
68
|
end
|
59
69
|
|
60
70
|
def recreate_index original_index, updated_index
|
@@ -82,18 +92,28 @@ module DynamicMigrations
|
|
82
92
|
raise MissingDescriptionError
|
83
93
|
end
|
84
94
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
95
|
+
add_fragment schema: index.table.schema,
|
96
|
+
table: index.table,
|
97
|
+
migration_method: :set_index_comment,
|
98
|
+
object: index,
|
99
|
+
code_comment: code_comment,
|
100
|
+
migration: <<~RUBY
|
101
|
+
set_index_comment :#{index.table.name}, :#{index.name}, <<~COMMENT
|
102
|
+
#{indent description}
|
103
|
+
COMMENT
|
104
|
+
RUBY
|
90
105
|
end
|
91
106
|
|
92
107
|
# remove the comment from a index
|
93
108
|
def remove_index_comment index, code_comment = nil
|
94
|
-
|
95
|
-
|
96
|
-
|
109
|
+
add_fragment schema: index.table.schema,
|
110
|
+
table: index.table,
|
111
|
+
migration_method: :remove_index_comment,
|
112
|
+
object: index,
|
113
|
+
code_comment: code_comment,
|
114
|
+
migration: <<~RUBY
|
115
|
+
remove_index_comment :#{index.table.name}, :#{index.name}
|
116
|
+
RUBY
|
97
117
|
end
|
98
118
|
end
|
99
119
|
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
module DynamicMigrations
|
2
|
+
module Postgres
|
3
|
+
class Generator
|
4
|
+
class Migration
|
5
|
+
class UnexpectedSchemaError < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
class SectionNotFoundError < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
class UnexpectedMigrationMethodNameError < StandardError
|
12
|
+
end
|
13
|
+
|
14
|
+
class DuplicateStructureTemplateError < StandardError
|
15
|
+
end
|
16
|
+
|
17
|
+
class NoFragmentsError < StandardError
|
18
|
+
end
|
19
|
+
|
20
|
+
# Defines a new section in the migration file, this is used to group
|
21
|
+
# migration fragments of the provided method names together under the
|
22
|
+
# provided header
|
23
|
+
def self.add_structure_template method_names, header
|
24
|
+
@structure_templates ||= []
|
25
|
+
|
26
|
+
if (@structure_templates.map { |s| s[:methods] }.flatten & method_names).any?
|
27
|
+
raise DuplicateStructureTemplateError, "Duplicate structure template for methods `#{method_names}`"
|
28
|
+
end
|
29
|
+
|
30
|
+
@structure_templates << {
|
31
|
+
methods: method_names,
|
32
|
+
header_comment: <<~COMMENT.strip
|
33
|
+
#
|
34
|
+
# #{header.strip}
|
35
|
+
#
|
36
|
+
COMMENT
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
# return the list of structure templates for use in this migration
|
41
|
+
def self.structure_templates
|
42
|
+
@structure_templates || []
|
43
|
+
end
|
44
|
+
|
45
|
+
# return the list of structure templates for use in this migration
|
46
|
+
def self.clear_structure_templates
|
47
|
+
@structure_templates = []
|
48
|
+
end
|
49
|
+
|
50
|
+
attr_reader :schema_name
|
51
|
+
attr_reader :fragments
|
52
|
+
|
53
|
+
def initialize schema_name
|
54
|
+
@schema_name = schema_name
|
55
|
+
@fragments = []
|
56
|
+
end
|
57
|
+
|
58
|
+
# Add a migration fragment to this migration, if the migration is not
|
59
|
+
# configured (via a structure template) to handle the method_name of the
|
60
|
+
# fragment, then am error is raised. An error will also be raised if the
|
61
|
+
# migration belongs to a different schema than the provided fragment.
|
62
|
+
def add_fragment fragment
|
63
|
+
raise UnexpectedSchemaError unless @schema_name == fragment.schema_name
|
64
|
+
|
65
|
+
unless supported_migration_method? fragment.migration_method
|
66
|
+
raise UnexpectedMigrationMethodNameError, "Expected method to be a valid migrator method, got `#{fragment.migration_method}`"
|
67
|
+
end
|
68
|
+
|
69
|
+
@fragments << fragment
|
70
|
+
|
71
|
+
fragment
|
72
|
+
end
|
73
|
+
|
74
|
+
# Return an array of table dependencies for this migration, this array comes from
|
75
|
+
# combining any table dependencies from each fragment.
|
76
|
+
# Will raise an error if no fragments have been provided.
|
77
|
+
def dependencies
|
78
|
+
raise NoFragmentsError if fragments.empty?
|
79
|
+
@fragments.map(&:dependency).compact
|
80
|
+
end
|
81
|
+
|
82
|
+
# removes and returns any fragments which have a dependency on the table with the
|
83
|
+
# provided schema_name and table_name, this is used for extracting fragments which
|
84
|
+
# cause circular dependencies so they can be placed into their own migrations
|
85
|
+
def extract_fragments_with_dependency schema_name, table_name
|
86
|
+
results = @fragments.filter { |f| f.is_dependent_on? schema_name, table_name }
|
87
|
+
# remove any of these from the internal array of fragments
|
88
|
+
@fragments.filter! { |f| !f.is_dependent_on?(schema_name, table_name) }
|
89
|
+
# return the results
|
90
|
+
results
|
91
|
+
end
|
92
|
+
|
93
|
+
# Combine the fragments, and build a string representation of the migration
|
94
|
+
# using the structure templates defined in this class.
|
95
|
+
# Will raise an error if no fragments have been provided.
|
96
|
+
def content
|
97
|
+
raise NoFragmentsError if fragments.empty?
|
98
|
+
sections = []
|
99
|
+
self.class.structure_templates.each do |section|
|
100
|
+
# add the header comment if we have a migration which matches one of the
|
101
|
+
# methods in this section
|
102
|
+
if (section[:methods] & @fragments.map(&:migration_method)).any?
|
103
|
+
sections << section[:header_comment].strip
|
104
|
+
end
|
105
|
+
|
106
|
+
# iterate through this sections methods in order and look
|
107
|
+
# for any that match the migrations we have
|
108
|
+
section[:methods].each do |migration_method|
|
109
|
+
# if we have any migration fragments for this method then add them
|
110
|
+
@fragments.filter { |f| f.migration_method == migration_method }.each do |fragment|
|
111
|
+
sections << fragment.to_s
|
112
|
+
sections << ""
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
sections.join("\n").strip
|
117
|
+
end
|
118
|
+
|
119
|
+
# Using the migration fragments, generate a friendly/descriptive name for the migration.
|
120
|
+
# Will raise an error if no fragments have been provided.
|
121
|
+
def name
|
122
|
+
raise NoFragmentsError if fragments.empty?
|
123
|
+
|
124
|
+
if fragments_for_method? :create_schema
|
125
|
+
"create_#{first_fragment_using_migration_method(:create_schema).schema_name}_schema".to_sym
|
126
|
+
|
127
|
+
elsif fragments_for_method? :drop_schema
|
128
|
+
"drop_#{first_fragment_using_migration_method(:drop_schema).schema_name}_schema".to_sym
|
129
|
+
|
130
|
+
elsif fragments_for_method? :create_table
|
131
|
+
"create_#{first_fragment_using_migration_method(:create_table).table_name}".to_sym
|
132
|
+
|
133
|
+
elsif fragments_for_method? :drop_table
|
134
|
+
"drop_#{first_fragment_using_migration_method(:drop_table).table_name}".to_sym
|
135
|
+
|
136
|
+
elsif all_fragments_for_method? [:create_function]
|
137
|
+
"create_function_#{@fragments.find { |s| s.migration_method == :create_function }&.object_name}".to_sym
|
138
|
+
|
139
|
+
elsif all_fragments_for_method? [:create_function, :update_function, :drop_function, :set_function_comment, :remove_function_comment]
|
140
|
+
:schema_functions
|
141
|
+
|
142
|
+
elsif @fragments.first&.table_name
|
143
|
+
"changes_for_#{@fragments.first&.table_name}".to_sym
|
144
|
+
|
145
|
+
else
|
146
|
+
:changes
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def supported_migration_method? method_name
|
153
|
+
self.class.structure_templates.map { |s| s[:methods] }.flatten.include? method_name
|
154
|
+
end
|
155
|
+
|
156
|
+
def fragments_for_method? migration_method
|
157
|
+
@fragments.map(&:migration_method).include? migration_method
|
158
|
+
end
|
159
|
+
|
160
|
+
def first_fragment_using_migration_method migration_method
|
161
|
+
fragment = @fragments.find(&:migration_method)
|
162
|
+
if fragment.nil?
|
163
|
+
raise SectionNotFoundError, "No fragment of type #{migration_method} found"
|
164
|
+
end
|
165
|
+
fragment
|
166
|
+
end
|
167
|
+
|
168
|
+
# return true if the current migration only has the provided content types and comments
|
169
|
+
def all_fragments_for_method? migration_methods
|
170
|
+
(@fragments.map(&:migration_method) - migration_methods - [:comment]).empty?
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# TSort is a module included in the Ruby standard library for
|
2
|
+
# executing topological sorts. We use it here to sort the migration
|
3
|
+
# fragments so that they are executed in the correct order (i.e. tables
|
4
|
+
# which have foreign keys are created after the tables they point to).
|
5
|
+
require "tsort"
|
6
|
+
|
7
|
+
module DynamicMigrations
|
8
|
+
module Postgres
|
9
|
+
class Generator
|
10
|
+
class MigrationDependencySorter < Hash
|
11
|
+
include TSort
|
12
|
+
|
13
|
+
alias_method :tsort_each_node, :each_key
|
14
|
+
|
15
|
+
def tsort_each_child(node, &block)
|
16
|
+
fetch(node).each(&block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -22,15 +22,25 @@ module DynamicMigrations
|
|
22
22
|
|
23
23
|
options_syntax = options.map { |k, v| "#{k}: #{v}" }.join(", ")
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
add_fragment schema: primary_key.table.schema,
|
26
|
+
table: primary_key.table,
|
27
|
+
migration_method: :add_primary_key,
|
28
|
+
object: primary_key,
|
29
|
+
code_comment: code_comment,
|
30
|
+
migration: <<~RUBY
|
31
|
+
add_primary_key :#{primary_key.table.name}, #{column_names}, #{options_syntax}
|
32
|
+
RUBY
|
28
33
|
end
|
29
34
|
|
30
35
|
def remove_primary_key primary_key, code_comment = nil
|
31
|
-
|
32
|
-
|
33
|
-
|
36
|
+
add_fragment schema: primary_key.table.schema,
|
37
|
+
table: primary_key.table,
|
38
|
+
migration_method: :remove_primary_key,
|
39
|
+
object: primary_key,
|
40
|
+
code_comment: code_comment,
|
41
|
+
migration: <<~RUBY
|
42
|
+
remove_primary_key :#{primary_key.table.name}, :#{primary_key.name}
|
43
|
+
RUBY
|
34
44
|
end
|
35
45
|
|
36
46
|
def recreate_primary_key original_primary_key, updated_primary_key
|