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
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../../lib/declare_schema/schema_change/primary_key_change'
4
+
5
+ RSpec.describe DeclareSchema::SchemaChange::PrimaryKeyChange do
6
+ before do
7
+ load File.expand_path('../prepare_testapp.rb', __dir__)
8
+ end
9
+
10
+ let(:table_name) { 'users' }
11
+ let(:old_column_names) { ['id'] }
12
+ let(:new_column_names) { [:last_name, 'first_name'] }
13
+ let(:name) { 'PRIMARY' }
14
+ subject { described_class.new(table_name, old_column_names, new_column_names) }
15
+
16
+ describe '#up/down' do
17
+ context 'when PRIMARY KEY set -> set' do
18
+ describe '#up' do
19
+ it 'responds with command' do
20
+ command = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(table_name)} DROP PRIMARY KEY, ADD PRIMARY KEY (#{new_column_names.join(', ')})"
21
+ expect(subject.up).to eq("execute #{command.inspect}\n")
22
+ end
23
+ end
24
+
25
+ describe '#down' do
26
+ it 'responds with command' do
27
+ command = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(table_name)} DROP PRIMARY KEY, ADD PRIMARY KEY (#{old_column_names.join(', ')})"
28
+ expect(subject.down).to eq("execute #{command.inspect}\n")
29
+ end
30
+ end
31
+ end
32
+
33
+ context 'when PRIMARY KEY unset -> set' do
34
+ let(:old_column_names) { nil }
35
+
36
+ describe '#up' do
37
+ it 'responds with command' do
38
+ command = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(table_name)} ADD PRIMARY KEY (#{new_column_names.join(', ')})"
39
+ expect(subject.up).to eq("execute #{command.inspect}\n")
40
+ end
41
+ end
42
+
43
+ describe '#down' do
44
+ it 'responds with command' do
45
+ command = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(table_name)} DROP PRIMARY KEY"
46
+ expect(subject.down).to eq("execute #{command.inspect}\n")
47
+ end
48
+ end
49
+ end
50
+
51
+ context 'when PRIMARY KEY set -> unset' do
52
+ let(:new_column_names) { nil }
53
+
54
+ describe '#up' do
55
+ it 'responds with command' do
56
+ command = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(table_name)} DROP PRIMARY KEY"
57
+ expect(subject.up).to eq("execute #{command.inspect}\n")
58
+ end
59
+ end
60
+
61
+ describe '#down' do
62
+ it 'responds with command' do
63
+ command = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(table_name)} ADD PRIMARY KEY (#{old_column_names.join(', ')})"
64
+ expect(subject.down).to eq("execute #{command.inspect}\n")
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../../lib/declare_schema/schema_change/table_add'
4
+
5
+ RSpec.describe DeclareSchema::SchemaChange::TableAdd do
6
+ before do
7
+ load File.expand_path('../prepare_testapp.rb', __dir__)
8
+ end
9
+
10
+ let(:table_name) { 'networks' }
11
+ let(:fields) { [[:string, :title, limit: 255, null: false ], [:boolean, :admin, null: false]] }
12
+ let(:create_table_options) { { id: :primary_key } }
13
+ let(:sql_options) { '' }
14
+
15
+ subject { described_class.new(table_name, fields, create_table_options, sql_options: sql_options) }
16
+
17
+ describe '#up/down' do
18
+ describe '#up' do
19
+ it 'responds with command' do
20
+ expect(subject.up).to eq(<<~EOS)
21
+ create_table :networks, id: :primary_key do |t|
22
+ t.string :title, limit: 255, null: false
23
+ t.boolean :admin, null: false
24
+ end
25
+
26
+ EOS
27
+ end
28
+
29
+ context 'with sql_options' do
30
+ let(:sql_options) { 'CHARACTER SET utf8mb4' }
31
+
32
+ it 'responds with command' do
33
+ expect(subject.up).to eq(<<~EOS)
34
+ create_table :networks, id: :primary_key, options: "CHARACTER SET utf8mb4" do |t|
35
+ t.string :title, limit: 255, null: false
36
+ t.boolean :admin, null: false
37
+ end
38
+
39
+ EOS
40
+ end
41
+ end
42
+ end
43
+
44
+ describe '#down' do
45
+ it 'responds with command' do
46
+ expect(subject.down).to eq("drop_table :#{table_name}\n")
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../../lib/declare_schema/schema_change/table_change'
4
+
5
+ RSpec.describe DeclareSchema::SchemaChange::TableChange do
6
+ before do
7
+ load File.expand_path('../prepare_testapp.rb', __dir__)
8
+ end
9
+
10
+ let(:table_name) { 'networks' }
11
+ let(:old_options) { { charset: 'utf8', collation: 'utf8_ci' } }
12
+ let(:new_options) { { charset: 'utf8mb4', collation: 'utf8mb4_bin' } }
13
+ subject { described_class.new(table_name, old_options, new_options) }
14
+
15
+ describe '#up/down' do
16
+ describe '#up' do
17
+ it 'responds with command' do
18
+ statement = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(table_name)} CHARACTER SET utf8mb4 COLLATE utf8mb4_bin"
19
+ expect(subject.up).to eq("execute #{statement.inspect}\n")
20
+ end
21
+ end
22
+
23
+ describe '#down' do
24
+ it 'responds with command' do
25
+ statement = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(table_name)} CHARACTER SET utf8 COLLATE utf8_ci"
26
+ expect(subject.down).to eq("execute #{statement.inspect}\n")
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../../lib/declare_schema/schema_change/table_remove'
4
+
5
+ RSpec.describe DeclareSchema::SchemaChange::TableRemove do
6
+ before do
7
+ load File.expand_path('../prepare_testapp.rb', __dir__)
8
+ end
9
+
10
+ let(:table_name) { 'networks' }
11
+ let(:add_table_back) { "create table networks(\n)" }
12
+ subject { described_class.new(table_name, add_table_back) }
13
+
14
+ describe '#up/down' do
15
+ describe '#up' do
16
+ it 'responds with command' do
17
+ expect(subject.up).to eq("drop_table :#{table_name}\n")
18
+ end
19
+ end
20
+
21
+ describe '#down' do
22
+ it 'responds with command' do
23
+ expect(subject.down).to eq("#{add_table_back}\n\n")
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../../lib/declare_schema/schema_change/table_rename'
4
+
5
+ RSpec.describe DeclareSchema::SchemaChange::TableRename do
6
+ before do
7
+ load File.expand_path('../prepare_testapp.rb', __dir__)
8
+ end
9
+
10
+ let(:old_name) { 'networks' }
11
+ let(:new_name) { 'customers' }
12
+ subject { described_class.new(old_name, new_name) }
13
+
14
+ describe '#up/down' do
15
+ describe '#up' do
16
+ it 'responds with command' do
17
+ expect(subject.up).to eq("rename_table :#{old_name}, :#{new_name}\n")
18
+ end
19
+ end
20
+
21
+ describe '#down' do
22
+ it 'responds with command' do
23
+ expect(subject.down).to eq("rename_table :#{new_name}, :#{old_name}\n")
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe DeclareSchema do
4
+ describe '#default_charset' do
5
+ subject { described_class.default_charset }
6
+
7
+ context 'when not explicitly set' do
8
+ it { should eq("utf8mb4") }
9
+ end
10
+
11
+ context 'when explicitly set' do
12
+ before { described_class.default_charset = "utf8" }
13
+ after { described_class.default_charset = "utf8mb4" }
14
+ it { should eq("utf8") }
15
+ end
16
+ end
17
+
18
+ describe '#default_collation' do
19
+ subject { described_class.default_collation }
20
+
21
+ context 'when not explicitly set' do
22
+ it { should eq("utf8mb4_bin") }
23
+ end
24
+
25
+ context 'when explicitly set' do
26
+ before { described_class.default_collation = "utf8mb4_general_ci" }
27
+ after { described_class.default_collation = "utf8mb4_bin" }
28
+ it { should eq("utf8mb4_general_ci") }
29
+ end
30
+ end
31
+
32
+ describe '#default_text_limit' do
33
+ subject { described_class.default_text_limit }
34
+
35
+ context 'when not explicitly set' do
36
+ it { should eq(0xffff_ffff) }
37
+ end
38
+
39
+ context 'when explicitly set' do
40
+ before { described_class.default_text_limit = 0xffff }
41
+ after { described_class.default_text_limit = 0xffff_ffff }
42
+ it { should eq(0xffff) }
43
+ end
44
+ end
45
+
46
+ describe '#default_string_limit' do
47
+ subject { described_class.default_string_limit }
48
+
49
+ context 'when not explicitly set' do
50
+ it { should eq(nil) }
51
+ end
52
+
53
+ context 'when explicitly set' do
54
+ before { described_class.default_string_limit = 225 }
55
+ after { described_class.default_string_limit = nil }
56
+ it { should eq(225) }
57
+ end
58
+ end
59
+
60
+ describe '#default_null' do
61
+ subject { described_class.default_null }
62
+
63
+ context 'when not explicitly set' do
64
+ it { should eq(false) }
65
+ end
66
+
67
+ context 'when explicitly set' do
68
+ before { described_class.default_null = true }
69
+ after { described_class.default_null = false }
70
+ it { should eq(true) }
71
+ end
72
+ end
73
+
74
+ describe '#default_generate_foreign_keys' do
75
+ subject { described_class.default_generate_foreign_keys }
76
+
77
+ context 'when not explicitly set' do
78
+ it { should eq(true) }
79
+ end
80
+
81
+ context 'when explicitly set' do
82
+ before { described_class.default_generate_foreign_keys = false }
83
+ after { described_class.default_generate_foreign_keys = true }
84
+ it { should eq(false) }
85
+ end
86
+ end
87
+
88
+ describe '#default_generate_indexing' do
89
+ subject { described_class.default_generate_indexing }
90
+
91
+ context 'when not explicitly set' do
92
+ it { should eq(true) }
93
+ end
94
+
95
+ context 'when explicitly set' do
96
+ before { described_class.default_generate_indexing = false }
97
+ after { described_class.default_generate_indexing = true }
98
+ it { should eq(false) }
99
+ end
100
+ end
101
+ end
@@ -11,18 +11,8 @@ module Generators
11
11
  module DeclareSchema
12
12
  module Migration
13
13
  RSpec.describe Migrator do
14
- before do
15
- ActiveRecord::Base.connection.tables
16
- end
17
-
18
14
  subject { described_class.new }
19
15
 
20
- describe 'format_options' do
21
- it 'returns an array of option .inspect strings, with symbols using the modern : hash notation' do
22
- expect(subject.format_options({ limit: 4, 'key' => 'value "quoted"' })).to eq(["limit: 4", '"key" => "value \"quoted\""'])
23
- end
24
- end
25
-
26
16
  describe '#before_generating_migration' do
27
17
  it 'requires a block be passed' do
28
18
  expect { described_class.before_generating_migration }.to raise_error(ArgumentError, 'A block is required when setting the before_generating_migration callback')
@@ -38,9 +28,14 @@ module Generators
38
28
 
39
29
  context 'when explicitly set' do
40
30
  before { described_class.default_charset = "utf8" }
41
- after { described_class.default_charset = described_class::DEFAULT_CHARSET }
31
+ after { described_class.default_charset = "utf8mb4" }
42
32
  it { should eq("utf8") }
43
33
  end
34
+
35
+ it 'should output deprecation warning' do
36
+ expect { described_class.default_charset = "utf8mb4" }.to output(/DEPRECATION WARNING: default_charset= is deprecated/).to_stderr
37
+ expect { subject }.to output(/DEPRECATION WARNING: default_charset is deprecated/).to_stderr
38
+ end
44
39
  end
45
40
 
46
41
  describe '#default_collation' do
@@ -52,12 +47,17 @@ module Generators
52
47
 
53
48
  context 'when explicitly set' do
54
49
  before { described_class.default_collation = "utf8mb4_general_ci" }
55
- after { described_class.default_collation = described_class::DEFAULT_COLLATION }
50
+ after { described_class.default_collation = "utf8mb4_bin" }
56
51
  it { should eq("utf8mb4_general_ci") }
57
52
  end
53
+
54
+ it 'should output deprecation warning' do
55
+ expect { described_class.default_collation = "utf8mb4_bin" }.to output(/DEPRECATION WARNING: default_collation= is deprecated/).to_stderr
56
+ expect { subject }.to output(/DEPRECATION WARNING: default_collation is deprecated/).to_stderr
57
+ end
58
58
  end
59
59
 
60
- describe 'load_rails_models' do
60
+ describe '#load_rails_models' do
61
61
  before do
62
62
  expect(Rails.application).to receive(:eager_load!)
63
63
  expect(Rails::Engine).to receive(:subclasses).and_return([])
@@ -80,6 +80,64 @@ module Generators
80
80
  it { should be_nil }
81
81
  end
82
82
  end
83
+
84
+ describe '#order_migrations' do
85
+ let(:class_name_order) do
86
+ %w[ TableRename
87
+ TableAdd
88
+ TableChange
89
+ ColumnAdd
90
+ ColumnRename
91
+ ColumnChange
92
+ PrimaryKeyChange
93
+ IndexAdd
94
+ ForeignKeyAdd
95
+ ForeignKeyRemove
96
+ IndexRemove
97
+ ColumnRemove
98
+ TableRemove ]
99
+ end
100
+ let(:one_of_each) do
101
+ class_name_order.map do |class_name|
102
+ klass = klass_from_class_name(class_name)
103
+ instance_double(klass).tap do |double|
104
+ allow(double).to receive(:class).and_return(klass)
105
+ end
106
+ end
107
+ end
108
+ let(:one_of_each_shuffled) { one_of_each.shuffle }
109
+
110
+ it 'orders properly' do
111
+ ordered = subject.order_migrations(one_of_each_shuffled)
112
+ expect(ordered.map { |c| c.class.name.sub(/.*::/, '') }).to eq(class_name_order)
113
+ end
114
+
115
+ context 'when there are dups' do
116
+ let(:one_of_each_with_dups) do
117
+ (class_name_order * 2).map do |class_name|
118
+ klass = klass_from_class_name(class_name)
119
+ instance_double(klass).tap do |double|
120
+ allow(double).to receive(:class).and_return(klass)
121
+ end
122
+ end
123
+ end
124
+ let(:one_of_each_with_dups_shuffled) { one_of_each_with_dups.shuffle }
125
+ let(:one_of_each_with_dups_shuffled_grouped) { one_of_each_with_dups_shuffled.group_by { |c| c.class.name } }
126
+
127
+ it 'sorts stably' do
128
+ ordered = subject.order_migrations(one_of_each_with_dups_shuffled)
129
+ ordered_grouped = ordered.group_by { |c| c.class.name }
130
+ ordered_grouped.each do |class_name, schema_changes|
131
+ shuffled_for_class = one_of_each_with_dups_shuffled_grouped[class_name]
132
+ expect(schema_changes.map(&:object_id)).to eq(shuffled_for_class.map(&:object_id))
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ def klass_from_class_name(class_name)
139
+ "::DeclareSchema::SchemaChange::#{class_name}".constantize
140
+ end
83
141
  end
84
142
  end
85
143
  end
@@ -45,13 +45,13 @@ module AcceptanceSpecHelpers
45
45
 
46
46
  class MigrationUpEquals < RSpec::Matchers::BuiltIn::Eq
47
47
  def matches?(subject)
48
- super(subject[0].gsub(/, +([a-z_]+:)/i, ', \1')) # normalize multiple spaces to one
48
+ super(subject[0].strip.gsub(/, +([a-z_]+:)/i, ', \1').gsub(/\n+/, "\n")) # normalize multiple spaces and newlines to one
49
49
  end
50
50
  end
51
51
 
52
52
  class MigrationDownEquals < RSpec::Matchers::BuiltIn::Eq
53
53
  def matches?(subject)
54
- super(subject[1].gsub(/, +([a-z_]+:)/i, ', \1')) # normalize multiple spaces to one
54
+ super(subject[1].strip.gsub(/, +([a-z_]+:)/i, ', \1').gsub(/\n+/, "\n")) # normalize multiple spaces and newlines to one
55
55
  end
56
56
  end
57
57
  end