declare_schema 0.8.0.pre.6 → 0.10.1

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/declare_schema_build.yml +1 -1
  3. data/CHANGELOG.md +28 -1
  4. data/Gemfile.lock +1 -1
  5. data/README.md +91 -13
  6. data/lib/declare_schema.rb +46 -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 +51 -59
  10. data/lib/declare_schema/model/field_spec.rb +11 -8
  11. data/lib/declare_schema/model/foreign_key_definition.rb +4 -8
  12. data/lib/declare_schema/model/habtm_model_shim.rb +1 -1
  13. data/lib/declare_schema/model/index_definition.rb +0 -19
  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/migrator.rb +189 -202
  31. data/lib/generators/declare_schema/support/model.rb +4 -4
  32. data/spec/lib/declare_schema/api_spec.rb +7 -7
  33. data/spec/lib/declare_schema/field_declaration_dsl_spec.rb +41 -15
  34. data/spec/lib/declare_schema/field_spec_spec.rb +50 -6
  35. data/spec/lib/declare_schema/generator_spec.rb +3 -3
  36. data/spec/lib/declare_schema/interactive_primary_key_spec.rb +116 -26
  37. data/spec/lib/declare_schema/migration_generator_spec.rb +1891 -845
  38. data/spec/lib/declare_schema/model/column_spec.rb +47 -17
  39. data/spec/lib/declare_schema/model/foreign_key_definition_spec.rb +134 -57
  40. data/spec/lib/declare_schema/model/habtm_model_shim_spec.rb +3 -3
  41. data/spec/lib/declare_schema/model/index_definition_spec.rb +188 -77
  42. data/spec/lib/declare_schema/model/table_options_definition_spec.rb +75 -11
  43. data/spec/lib/declare_schema/schema_change/base_spec.rb +75 -0
  44. data/spec/lib/declare_schema/schema_change/column_add_spec.rb +30 -0
  45. data/spec/lib/declare_schema/schema_change/column_change_spec.rb +33 -0
  46. data/spec/lib/declare_schema/schema_change/column_remove_spec.rb +30 -0
  47. data/spec/lib/declare_schema/schema_change/column_rename_spec.rb +28 -0
  48. data/spec/lib/declare_schema/schema_change/foreign_key_add_spec.rb +29 -0
  49. data/spec/lib/declare_schema/schema_change/foreign_key_remove_spec.rb +29 -0
  50. data/spec/lib/declare_schema/schema_change/index_add_spec.rb +56 -0
  51. data/spec/lib/declare_schema/schema_change/index_remove_spec.rb +29 -0
  52. data/spec/lib/declare_schema/schema_change/primary_key_change_spec.rb +69 -0
  53. data/spec/lib/declare_schema/schema_change/table_add_spec.rb +50 -0
  54. data/spec/lib/declare_schema/schema_change/table_change_spec.rb +30 -0
  55. data/spec/lib/declare_schema/schema_change/table_remove_spec.rb +27 -0
  56. data/spec/lib/declare_schema/schema_change/table_rename_spec.rb +27 -0
  57. data/spec/lib/declare_schema_spec.rb +101 -0
  58. data/spec/lib/generators/declare_schema/migration/migrator_spec.rb +71 -13
  59. data/spec/support/acceptance_spec_helpers.rb +2 -2
  60. metadata +33 -3
  61. data/test_responses.txt +0 -2
@@ -47,9 +47,9 @@ module DeclareSchema
47
47
  end
48
48
 
49
49
  def initialize(model, name, type, position: 0, **options)
50
- defined_primary_key = model.defined_primary_key
50
+ _defined_primary_key = model._defined_primary_key
51
51
 
52
- name.to_s == defined_primary_key and raise ArgumentError, "you may not provide a field spec for the primary key #{name.inspect}"
52
+ name.to_s == _defined_primary_key and raise ArgumentError, "you may not provide a field spec for the primary key #{name.inspect}"
53
53
 
54
54
  @model = model
55
55
  @name = name.to_sym
@@ -58,18 +58,21 @@ module DeclareSchema
58
58
  @position = position
59
59
  @options = options.dup
60
60
 
61
- @options.has_key?(:null) or @options[:null] = false
61
+ @options.has_key?(:null) or @options[:null] = ::DeclareSchema.default_null
62
+ @options[:null].nil? and raise "null: must be provided for field #{model}##{@name}: #{@options.inspect} since ::DeclareSchema#default_null is set to 'nil'; do you want `null: false`?"
62
63
 
63
64
  case @type
64
65
  when :text
65
66
  if self.class.mysql_text_limits?
66
67
  @options[:default].nil? or raise MysqlTextMayNotHaveDefault, "when using MySQL, non-nil default may not be given for :text field #{model}##{@name}"
67
- @options[:limit] = self.class.round_up_mysql_text_limit(@options[:limit] || MYSQL_LONGTEXT_LIMIT)
68
+ @options[:limit] ||= ::DeclareSchema.default_text_limit or
69
+ raise("limit: must be provided for :text field #{model}##{@name}: #{@options.inspect} since ::DeclareSchema#default_text_limit is set to 'nil'; do you want `limit: 0xffff_ffff`?")
70
+ @options[:limit] = self.class.round_up_mysql_text_limit(@options[:limit])
68
71
  else
69
72
  @options.delete(:limit)
70
73
  end
71
74
  when :string
72
- @options[:limit] or raise "limit: must be given for :string field #{model}##{@name}: #{@options.inspect}; do you want `limit: 255`?"
75
+ @options[:limit] ||= ::DeclareSchema.default_string_limit or raise "limit: must be provided for :string field #{model}##{@name}: #{@options.inspect} since ::DeclareSchema#default_string_limit is set to 'nil'; do you want `limit: 255`?"
73
76
  when :bigint
74
77
  @type = :integer
75
78
  @options[:limit] = 8
@@ -96,15 +99,15 @@ module DeclareSchema
96
99
 
97
100
  if @type.in?([:text, :string])
98
101
  if ActiveRecord::Base.connection.class.name.match?(/mysql/i)
99
- @options[:charset] ||= model.table_options[:charset] || Generators::DeclareSchema::Migration::Migrator.default_charset
100
- @options[:collation] ||= model.table_options[:collation] || Generators::DeclareSchema::Migration::Migrator.default_collation
102
+ @options[:charset] ||= model.table_options[:charset] || ::DeclareSchema.default_charset
103
+ @options[:collation] ||= model.table_options[:collation] || ::DeclareSchema.default_collation
101
104
  else
102
105
  @options.delete(:charset)
103
106
  @options.delete(:collation)
104
107
  end
105
108
  else
106
109
  @options[:charset] and warn("charset may only given for :string and :text fields for SQL type #{@type} in field #{model}##{@name}")
107
- @options[:collation] and warne("collation may only given for :string and :text fields for SQL type #{@type} in field #{model}##{@name}")
110
+ @options[:collation] and warn("collation may only given for :string and :text fields for SQL type #{@type} in field #{model}##{@name}")
108
111
  end
109
112
 
110
113
  @options = Hash[@options.sort_by { |k, _v| OPTION_INDEXES[k] || 9999 }]
@@ -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
@@ -47,7 +47,7 @@ module DeclareSchema
47
47
  false # no single-column primary key in database
48
48
  end
49
49
 
50
- def defined_primary_key
50
+ def _defined_primary_key
51
51
  false # no single-column primary key declared
52
52
  end
53
53
 
@@ -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