declare_schema 0.10.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -1
  3. data/Gemfile.lock +1 -1
  4. data/lib/declare_schema/model/foreign_key_definition.rb +4 -8
  5. data/lib/declare_schema/model/index_definition.rb +0 -19
  6. data/lib/declare_schema/schema_change/all.rb +22 -0
  7. data/lib/declare_schema/schema_change/base.rb +45 -0
  8. data/lib/declare_schema/schema_change/column_add.rb +27 -0
  9. data/lib/declare_schema/schema_change/column_change.rb +32 -0
  10. data/lib/declare_schema/schema_change/column_remove.rb +20 -0
  11. data/lib/declare_schema/schema_change/column_rename.rb +23 -0
  12. data/lib/declare_schema/schema_change/foreign_key_add.rb +25 -0
  13. data/lib/declare_schema/schema_change/foreign_key_remove.rb +20 -0
  14. data/lib/declare_schema/schema_change/index_add.rb +33 -0
  15. data/lib/declare_schema/schema_change/index_remove.rb +20 -0
  16. data/lib/declare_schema/schema_change/primary_key_change.rb +33 -0
  17. data/lib/declare_schema/schema_change/table_add.rb +37 -0
  18. data/lib/declare_schema/schema_change/table_change.rb +36 -0
  19. data/lib/declare_schema/schema_change/table_remove.rb +22 -0
  20. data/lib/declare_schema/schema_change/table_rename.rb +22 -0
  21. data/lib/declare_schema/version.rb +1 -1
  22. data/lib/generators/declare_schema/migration/migrator.rb +172 -174
  23. data/spec/lib/declare_schema/interactive_primary_key_spec.rb +52 -14
  24. data/spec/lib/declare_schema/migration_generator_spec.rb +175 -303
  25. data/spec/lib/declare_schema/model/foreign_key_definition_spec.rb +0 -12
  26. data/spec/lib/declare_schema/schema_change/base_spec.rb +75 -0
  27. data/spec/lib/declare_schema/schema_change/column_add_spec.rb +30 -0
  28. data/spec/lib/declare_schema/schema_change/column_change_spec.rb +33 -0
  29. data/spec/lib/declare_schema/schema_change/column_remove_spec.rb +30 -0
  30. data/spec/lib/declare_schema/schema_change/column_rename_spec.rb +28 -0
  31. data/spec/lib/declare_schema/schema_change/foreign_key_add_spec.rb +29 -0
  32. data/spec/lib/declare_schema/schema_change/foreign_key_remove_spec.rb +29 -0
  33. data/spec/lib/declare_schema/schema_change/index_add_spec.rb +56 -0
  34. data/spec/lib/declare_schema/schema_change/index_remove_spec.rb +29 -0
  35. data/spec/lib/declare_schema/schema_change/primary_key_change_spec.rb +69 -0
  36. data/spec/lib/declare_schema/schema_change/table_add_spec.rb +50 -0
  37. data/spec/lib/declare_schema/schema_change/table_change_spec.rb +30 -0
  38. data/spec/lib/declare_schema/schema_change/table_remove_spec.rb +27 -0
  39. data/spec/lib/declare_schema/schema_change/table_rename_spec.rb +27 -0
  40. data/spec/lib/generators/declare_schema/migration/migrator_spec.rb +59 -11
  41. data/spec/support/acceptance_spec_helpers.rb +2 -2
  42. metadata +34 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e993cfd0cf72aa7610f32743ce838d6c7e6ec07370cd3427649ab80cce7e9ac2
4
- data.tar.gz: 273de3135fa09049b57a598342a36c283dc956498016dbc98297910233b1b5bb
3
+ metadata.gz: 4a529991bad5ab16728f11b3f6c165b62853c3b68713e0ec69d6370d7d3c0e5b
4
+ data.tar.gz: 6a131f8af82b018b843f639e501371cf0db9a65b91a4acb99aea44d3eb1674d9
5
5
  SHA512:
6
- metadata.gz: 1f28b92b3614dc61323b386eeb67c681873c983acd66869b705e6c920c906c9429f26030135e916726fa0b068c6b68e5cce57902f64c230c0a5cf289fdec9645
7
- data.tar.gz: 2d23d69ffedf63881a3d33bbde3f2bcc62ed9e12938e981bb7388400a3f4dec28aa3ade8714a3c352c93b743496e70777f6a54e710d91d7e6adb2ca66fc23954
6
+ metadata.gz: 655ab291337bf8f11fb249de1b97608e7ff73b14ff543f9248e3f92d9bf1770553b8bc2f962c72e8763d27ae559c35bdff1f12f4b6b460a1d46abfab00e931c0
7
+ data.tar.gz: b67d3340bb42b91bddd0940e0a9e5a895bd57f2017ef8d4712e1e4331ba46e78b17d9f7e9b0e60de9f921232897c0edeffe37621060632ecb18267db1ce15dc0
data/CHANGELOG.md CHANGED
@@ -4,9 +4,15 @@ Inspired by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
4
4
 
5
5
  Note: this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.10.1] - 2021-03-18
8
+ ### Fixed
9
+ - Migration steps are now generated in a defined dependency order, so that--for example--indexes that depend
10
+ on columns are deleted first, before the columns themselves are deleted (since the latter implicitly does the former, which would break the migration when run).
11
+ - Related to the above, down migration steps are now always generated in exactly the reverse order of the up migration steps.
12
+
7
13
  ## [0.10.0] - 2021-03-17
8
14
  ### Deprecated
9
- - The `fields` dsl method is being deprecated.
15
+ - Deprecated the `fields` DSL method in favor of `declare_schema`.
10
16
 
11
17
  ### Added
12
18
  - Added the `declare_schema` method to replace `fields`. We now expect a column's type to come before the name
@@ -148,6 +154,7 @@ using the appropriate Rails configuration attributes.
148
154
  ### Added
149
155
  - Initial version from https://github.com/Invoca/hobo_fields v4.1.0.
150
156
 
157
+ [0.10.1]: https://github.com/Invoca/declare_schema/compare/v0.10.0...v0.10.1
151
158
  [0.10.0]: https://github.com/Invoca/declare_schema/compare/v0.9.0...v0.10.0
152
159
  [0.9.0]: https://github.com/Invoca/declare_schema/compare/v0.8.0...v0.9.0
153
160
  [0.8.0]: https://github.com/Invoca/declare_schema/compare/v0.7.1...v0.8.0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- declare_schema (0.10.0)
4
+ declare_schema (0.10.1)
5
5
  rails (>= 4.2)
6
6
 
7
7
  GEM
@@ -7,14 +7,15 @@ module DeclareSchema
7
7
  class ForeignKeyDefinition
8
8
  include Comparable
9
9
 
10
- attr_reader :constraint_name, :model, :foreign_key, :foreign_key_name, :options, :on_delete_cascade
10
+ attr_reader :constraint_name, :model, :foreign_key, :foreign_key_name, :parent_table_name, :child_table_name, :options, :on_delete_cascade
11
+
11
12
 
12
13
  def initialize(model, foreign_key, options = {})
13
14
  @model = model
14
15
  @foreign_key = foreign_key.to_s.presence
15
16
  @options = options
16
17
 
17
- @child_table = model.table_name # unless a table rename, which would happen when a class is renamed??
18
+ @child_table_name = model.table_name # unless a table rename, which would happen when a class is renamed??
18
19
  @parent_table_name = options[:parent_table]&.to_s
19
20
  @foreign_key_name = options[:foreign_key]&.to_s || @foreign_key
20
21
 
@@ -61,11 +62,6 @@ module DeclareSchema
61
62
  foreign_key.sub(/_id\z/, '').camelize.constantize.table_name
62
63
  end
63
64
 
64
- def to_add_statement
65
- "add_foreign_key(#{@child_table.inspect}, #{parent_table_name.inspect}, " +
66
- "column: #{@foreign_key_name.inspect}, name: #{@constraint_name.inspect})"
67
- end
68
-
69
65
  def <=>(rhs)
70
66
  key <=> rhs.send(:key)
71
67
  end
@@ -75,7 +71,7 @@ module DeclareSchema
75
71
  private
76
72
 
77
73
  def key
78
- @key ||= [@child_table, parent_table_name, @foreign_key_name, @on_delete_cascade].map(&:to_s)
74
+ @key ||= [@child_table_name, parent_table_name, @foreign_key_name, @on_delete_cascade].map(&:to_s)
79
75
  end
80
76
 
81
77
  def hash
@@ -93,25 +93,6 @@ module DeclareSchema
93
93
  name == PRIMARY_KEY_NAME
94
94
  end
95
95
 
96
- def to_add_statement(new_table_name, existing_primary_key = nil)
97
- if primary_key? && !ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/)
98
- to_add_primary_key_statement(new_table_name, existing_primary_key)
99
- else
100
- # Note: + below keeps that interpolated string from being frozen, so we can << into it.
101
- r = +"add_index #{new_table_name.to_sym.inspect}, #{fields.map(&:to_sym).inspect}"
102
- r << ", unique: true" if unique
103
- r << ", where: '#{where}'" if where.present?
104
- r << ", name: '#{name}'"
105
- r
106
- end
107
- end
108
-
109
- def to_add_primary_key_statement(new_table_name, existing_primary_key)
110
- drop = "DROP PRIMARY KEY, " if existing_primary_key
111
- statement = "ALTER TABLE #{new_table_name} #{drop}ADD PRIMARY KEY (#{fields.join(', ')})"
112
- "execute #{statement.inspect}"
113
- end
114
-
115
96
  def to_key
116
97
  @key ||= [table, fields, name, unique, where].map(&:to_s)
117
98
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeclareSchema
4
+ module SchemaChange
5
+ module All
6
+ end
7
+ autoload :Base, 'declare_schema/schema_change/base'
8
+ autoload :ColumnAdd, 'declare_schema/schema_change/column_add'
9
+ autoload :ColumnChange, 'declare_schema/schema_change/column_change'
10
+ autoload :ColumnRename, 'declare_schema/schema_change/column_rename'
11
+ autoload :ForeignKeyAdd, 'declare_schema/schema_change/foreign_key_add'
12
+ autoload :ForeignKeyRemove, 'declare_schema/schema_change/foreign_key_remove'
13
+ autoload :IndexAdd, 'declare_schema/schema_change/index_add'
14
+ autoload :IndexRemove, 'declare_schema/schema_change/index_remove'
15
+ autoload :PrimaryKeyChange, 'declare_schema/schema_change/primary_key_change'
16
+ autoload :TableAdd, 'declare_schema/schema_change/table_add'
17
+ autoload :TableChange, 'declare_schema/schema_change/table_change'
18
+ autoload :TableRemove, 'declare_schema/schema_change/table_remove'
19
+ autoload :TableRename, 'declare_schema/schema_change/table_rename'
20
+ end
21
+ end
22
+
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module DeclareSchema
6
+ module SchemaChange
7
+ class Base
8
+ class << self
9
+ def format_options(options)
10
+ options.map do |k, v|
11
+ value =
12
+ if v.is_a?(Hash)
13
+ "{ #{format_options(v).join(', ')} }"
14
+ else
15
+ v.inspect
16
+ end
17
+ if k.is_a?(Symbol)
18
+ "#{k}: #{value}"
19
+ else
20
+ "#{k.inspect} => #{value}"
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ def up
27
+ up_command + spacing(up_command)
28
+ end
29
+
30
+ def down
31
+ down_command + spacing(down_command)
32
+ end
33
+
34
+ private
35
+
36
+ def spacing(command)
37
+ if command["\n"]
38
+ "\n\n"
39
+ else
40
+ "\n"
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module DeclareSchema
6
+ module SchemaChange
7
+ class ColumnAdd < Base
8
+ def initialize(table_name, column_name, column_type, **column_options)
9
+ @table_name = table_name
10
+ @column_name = column_name
11
+ @column_type = column_type
12
+ @column_options = column_options
13
+ end
14
+
15
+ def up_command
16
+ "add_column #{[@table_name.to_sym.inspect,
17
+ @column_name.to_sym.inspect,
18
+ @column_type.to_sym.inspect,
19
+ *self.class.format_options(@column_options)].join(", ")}"
20
+ end
21
+
22
+ def down_command
23
+ "remove_column #{@table_name.to_sym.inspect}, #{@column_name.to_sym.inspect}"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module DeclareSchema
6
+ module SchemaChange
7
+ class ColumnChange < Base
8
+ def initialize(table_name, column_name, old_type:, old_options:, new_type:, new_options:)
9
+ @table_name = table_name
10
+ @column_name = column_name
11
+ @old_type = old_type
12
+ @old_options = old_options
13
+ @new_type = new_type
14
+ @new_options = new_options
15
+ end
16
+
17
+ def up_command
18
+ "change_column #{[@table_name.to_sym.inspect,
19
+ @column_name.to_sym.inspect,
20
+ @new_type.to_sym.inspect,
21
+ *self.class.format_options(@new_options)].join(", ")}"
22
+ end
23
+
24
+ def down_command
25
+ "change_column #{[@table_name.to_sym.inspect,
26
+ @column_name.to_sym.inspect,
27
+ @old_type.to_sym.inspect,
28
+ *self.class.format_options(@old_options)].join(", ")}"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'column_add'
4
+
5
+ module DeclareSchema
6
+ module SchemaChange
7
+ class ColumnRemove < ColumnAdd
8
+ alias column_add_up_command up_command
9
+ alias column_add_down_command down_command
10
+
11
+ def up_command
12
+ column_add_down_command
13
+ end
14
+
15
+ def down_command
16
+ column_add_up_command
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module DeclareSchema
6
+ module SchemaChange
7
+ class ColumnRename < Base
8
+ def initialize(table_name, old_name, new_name)
9
+ @table_name = table_name
10
+ @old_name = old_name
11
+ @new_name = new_name
12
+ end
13
+
14
+ def up_command
15
+ "rename_column #{@table_name.to_sym.inspect}, #{@old_name.to_sym.inspect}, #{@new_name.to_sym.inspect}"
16
+ end
17
+
18
+ def down_command
19
+ "rename_column #{@table_name.to_sym.inspect}, #{@new_name.to_sym.inspect}, #{@old_name.to_sym.inspect}"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module DeclareSchema
6
+ module SchemaChange
7
+ class ForeignKeyAdd < Base
8
+ def initialize(table_name, parent_table_name, column_name:, name:)
9
+ @table_name = table_name
10
+ @parent_table_name = parent_table_name
11
+ @column_name = column_name
12
+ @name = name
13
+ end
14
+
15
+ def up_command
16
+ "add_foreign_key #{@table_name.to_sym.inspect}, #{@parent_table_name.to_sym.inspect}, " +
17
+ "column: #{@column_name.to_sym.inspect}, name: #{@name.to_sym.inspect}"
18
+ end
19
+
20
+ def down_command
21
+ "remove_foreign_key #{@table_name.to_sym.inspect}, name: #{@name.to_sym.inspect}"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'foreign_key_add'
4
+
5
+ module DeclareSchema
6
+ module SchemaChange
7
+ class ForeignKeyRemove < ForeignKeyAdd
8
+ alias index_add_up_command up_command
9
+ alias index_add_down_command down_command
10
+
11
+ def up_command
12
+ index_add_down_command
13
+ end
14
+
15
+ def down_command
16
+ index_add_up_command
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module DeclareSchema
6
+ module SchemaChange
7
+ class IndexAdd < Base
8
+ def initialize(table_name, column_names, name:, unique:, where: nil)
9
+ @table_name = table_name
10
+ @column_names = column_names
11
+ @name = name
12
+ @unique = unique
13
+ @where = where.presence
14
+ end
15
+
16
+ def up_command
17
+ options = {
18
+ name: @name.to_sym,
19
+ }
20
+ options[:unique] = true if @unique
21
+ options[:where] = @where if @where
22
+
23
+ "add_index #{[@table_name.to_sym.inspect,
24
+ @column_names.map(&:to_sym).inspect,
25
+ *self.class.format_options(options)].join(', ')}"
26
+ end
27
+
28
+ def down_command
29
+ "remove_index #{@table_name.to_sym.inspect}, name: #{@name.to_sym.inspect}"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'index_add'
4
+
5
+ module DeclareSchema
6
+ module SchemaChange
7
+ class IndexRemove < IndexAdd
8
+ alias index_add_up_command up_command
9
+ alias index_add_down_command down_command
10
+
11
+ def up_command
12
+ index_add_down_command
13
+ end
14
+
15
+ def down_command
16
+ index_add_up_command
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module DeclareSchema
6
+ module SchemaChange
7
+ class PrimaryKeyChange < Base
8
+ def initialize(table_name, old_column_names, new_column_names)
9
+ @table_name = table_name
10
+ @old_column_names = old_column_names.presence
11
+ @new_column_names = new_column_names.presence
12
+ end
13
+
14
+ def up_command
15
+ alter_primary_key(@old_column_names, @new_column_names)
16
+ end
17
+
18
+ def down_command
19
+ alter_primary_key(@new_column_names, @old_column_names)
20
+ end
21
+
22
+ private
23
+
24
+ def alter_primary_key(old_col_names, new_col_names)
25
+ drop_command = "DROP PRIMARY KEY" if old_col_names
26
+ add_command = "ADD PRIMARY KEY (#{new_col_names.join(', ')})" if new_col_names
27
+ commands = [drop_command, add_command].compact.join(', ')
28
+ statement = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(@table_name)} #{commands}"
29
+ "execute #{statement.inspect}"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module DeclareSchema
6
+ module SchemaChange
7
+ class TableAdd < Base
8
+ def initialize(table_name, fields, create_table_options, sql_options: nil)
9
+ @table_name = table_name
10
+ fields.all? do |type, name, options|
11
+ type.is_a?(Symbol) && name.is_a?(Symbol) && options.is_a?(Hash)
12
+ end or raise ArgumentError, "fields must be Array(Array(Symbol, Symbol, Hash)); got #{fields.inspect}"
13
+ @fields = fields
14
+ @create_table_options = create_table_options
15
+ @create_table_options = @create_table_options.merge(options: sql_options) if sql_options.present?
16
+ end
17
+
18
+ def up_command
19
+ longest_field_type_length = @fields.map { |type, _name, _option| type.to_s.length }.max
20
+
21
+ <<~EOS.strip
22
+ create_table #{[@table_name.to_sym.inspect, *self.class.format_options(@create_table_options)].join(', ')} do |t|
23
+ #{@fields.map do |type, name, options|
24
+ padded_type = format("%-*s", longest_field_type_length, type)
25
+ args = [name.inspect, *self.class.format_options(options)].join(', ')
26
+ " t.#{padded_type} #{args}"
27
+ end.join("\n")}
28
+ end
29
+ EOS
30
+ end
31
+
32
+ def down_command
33
+ "drop_table #{@table_name.to_sym.inspect}"
34
+ end
35
+ end
36
+ end
37
+ end