declare_schema 0.8.0 → 0.11.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/declare_schema_build.yml +1 -1
  3. data/CHANGELOG.md +37 -0
  4. data/Gemfile.lock +1 -1
  5. data/README.md +102 -14
  6. data/lib/declare_schema.rb +57 -0
  7. data/lib/declare_schema/dsl.rb +39 -0
  8. data/lib/declare_schema/extensions/active_record/fields_declaration.rb +23 -4
  9. data/lib/declare_schema/model.rb +4 -6
  10. data/lib/declare_schema/model/column.rb +1 -1
  11. data/lib/declare_schema/model/field_spec.rb +9 -6
  12. data/lib/declare_schema/model/foreign_key_definition.rb +6 -11
  13. data/lib/declare_schema/model/index_definition.rb +1 -20
  14. data/lib/declare_schema/schema_change/all.rb +22 -0
  15. data/lib/declare_schema/schema_change/base.rb +45 -0
  16. data/lib/declare_schema/schema_change/column_add.rb +27 -0
  17. data/lib/declare_schema/schema_change/column_change.rb +32 -0
  18. data/lib/declare_schema/schema_change/column_remove.rb +20 -0
  19. data/lib/declare_schema/schema_change/column_rename.rb +23 -0
  20. data/lib/declare_schema/schema_change/foreign_key_add.rb +25 -0
  21. data/lib/declare_schema/schema_change/foreign_key_remove.rb +20 -0
  22. data/lib/declare_schema/schema_change/index_add.rb +33 -0
  23. data/lib/declare_schema/schema_change/index_remove.rb +20 -0
  24. data/lib/declare_schema/schema_change/primary_key_change.rb +33 -0
  25. data/lib/declare_schema/schema_change/table_add.rb +37 -0
  26. data/lib/declare_schema/schema_change/table_change.rb +36 -0
  27. data/lib/declare_schema/schema_change/table_remove.rb +22 -0
  28. data/lib/declare_schema/schema_change/table_rename.rb +22 -0
  29. data/lib/declare_schema/version.rb +1 -1
  30. data/lib/generators/declare_schema/migration/USAGE +14 -24
  31. data/lib/generators/declare_schema/migration/migration_generator.rb +40 -38
  32. data/lib/generators/declare_schema/migration/migrator.rb +184 -198
  33. data/lib/generators/declare_schema/migration/templates/migration.rb.erb +1 -1
  34. data/lib/generators/declare_schema/support/model.rb +4 -4
  35. data/spec/lib/declare_schema/api_spec.rb +8 -8
  36. data/spec/lib/declare_schema/field_declaration_dsl_spec.rb +41 -15
  37. data/spec/lib/declare_schema/field_spec_spec.rb +44 -2
  38. data/spec/lib/declare_schema/generator_spec.rb +5 -5
  39. data/spec/lib/declare_schema/interactive_primary_key_spec.rb +117 -28
  40. data/spec/lib/declare_schema/migration_generator_spec.rb +1990 -843
  41. data/spec/lib/declare_schema/model/column_spec.rb +49 -23
  42. data/spec/lib/declare_schema/model/foreign_key_definition_spec.rb +158 -57
  43. data/spec/lib/declare_schema/model/habtm_model_shim_spec.rb +0 -2
  44. data/spec/lib/declare_schema/model/index_definition_spec.rb +189 -78
  45. data/spec/lib/declare_schema/model/table_options_definition_spec.rb +75 -11
  46. data/spec/lib/declare_schema/schema_change/base_spec.rb +75 -0
  47. data/spec/lib/declare_schema/schema_change/column_add_spec.rb +30 -0
  48. data/spec/lib/declare_schema/schema_change/column_change_spec.rb +33 -0
  49. data/spec/lib/declare_schema/schema_change/column_remove_spec.rb +30 -0
  50. data/spec/lib/declare_schema/schema_change/column_rename_spec.rb +28 -0
  51. data/spec/lib/declare_schema/schema_change/foreign_key_add_spec.rb +29 -0
  52. data/spec/lib/declare_schema/schema_change/foreign_key_remove_spec.rb +29 -0
  53. data/spec/lib/declare_schema/schema_change/index_add_spec.rb +56 -0
  54. data/spec/lib/declare_schema/schema_change/index_remove_spec.rb +29 -0
  55. data/spec/lib/declare_schema/schema_change/primary_key_change_spec.rb +69 -0
  56. data/spec/lib/declare_schema/schema_change/table_add_spec.rb +50 -0
  57. data/spec/lib/declare_schema/schema_change/table_change_spec.rb +30 -0
  58. data/spec/lib/declare_schema/schema_change/table_remove_spec.rb +27 -0
  59. data/spec/lib/declare_schema/schema_change/table_rename_spec.rb +27 -0
  60. data/spec/lib/declare_schema_spec.rb +101 -0
  61. data/spec/lib/generators/declare_schema/migration/migrator_spec.rb +71 -13
  62. data/spec/spec_helper.rb +1 -1
  63. data/spec/support/acceptance_spec_helpers.rb +2 -2
  64. metadata +36 -6
  65. data/test_responses.txt +0 -2
@@ -7,20 +7,20 @@ 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
 
21
- @constraint_name = options[:constraint_name]&.to_s ||
22
- options[:index_name]&.to_s ||
23
- IndexDefinition.index_name(@foreign_key_name)
22
+ @constraint_name = options[:constraint_name]&.to_s.presence ||
23
+ model.connection.index_name(model.table_name, column: @foreign_key_name)
24
24
  @on_delete_cascade = options[:dependent] == :delete
25
25
  end
26
26
 
@@ -61,11 +61,6 @@ module DeclareSchema
61
61
  foreign_key.sub(/_id\z/, '').camelize.constantize.table_name
62
62
  end
63
63
 
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
64
  def <=>(rhs)
70
65
  key <=> rhs.send(:key)
71
66
  end
@@ -75,7 +70,7 @@ module DeclareSchema
75
70
  private
76
71
 
77
72
  def key
78
- @key ||= [@child_table, parent_table_name, @foreign_key_name, @on_delete_cascade].map(&:to_s)
73
+ @key ||= [@child_table_name, parent_table_name, @foreign_key_name, @on_delete_cascade].map(&:to_s)
79
74
  end
80
75
 
81
76
  def hash
@@ -68,7 +68,7 @@ module DeclareSchema
68
68
 
69
69
  # This is the old approach which is still needed for MySQL in Rails 4 and SQLite
70
70
  def sqlite_compound_primary_key(model, table)
71
- ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/) || Rails::VERSION::MAJOR < 5 or return nil
71
+ ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/) || ActiveSupport::VERSION::MAJOR < 5 or return nil
72
72
 
73
73
  connection = model.connection.dup
74
74
 
@@ -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
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module DeclareSchema
6
+ module SchemaChange
7
+ class TableChange < Base
8
+ def initialize(table_name, old_options, new_options)
9
+ @table_name = table_name
10
+ @old_options = old_options
11
+ @new_options = new_options
12
+ end
13
+
14
+ def up_command
15
+ alter_table(@table_name, @new_options)
16
+ end
17
+
18
+ def down_command
19
+ alter_table(@table_name, @old_options)
20
+ end
21
+
22
+ private
23
+
24
+ TABLE_OPTIONS_TO_SQL_MAPPINGS = {
25
+ charset: 'CHARACTER SET',
26
+ collation: 'COLLATE'
27
+ }.freeze
28
+
29
+ def alter_table(table_name, options)
30
+ sql_options = options.map { |key, value| [TABLE_OPTIONS_TO_SQL_MAPPINGS[key], value] }
31
+ statement = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(table_name)} #{sql_options.join(' ')}"
32
+ "execute #{statement.inspect}"
33
+ end
34
+ end
35
+ end
36
+ end