declare_schema 0.9.0 → 0.11.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/declare_schema_build.yml +1 -1
  3. data/CHANGELOG.md +32 -0
  4. data/Gemfile.lock +1 -1
  5. data/README.md +12 -2
  6. data/lib/declare_schema.rb +12 -1
  7. data/lib/declare_schema/dsl.rb +39 -0
  8. data/lib/declare_schema/extensions/active_record/fields_declaration.rb +22 -3
  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/foreign_key_definition.rb +6 -11
  12. data/lib/declare_schema/model/index_definition.rb +1 -20
  13. data/lib/declare_schema/schema_change/all.rb +22 -0
  14. data/lib/declare_schema/schema_change/base.rb +45 -0
  15. data/lib/declare_schema/schema_change/column_add.rb +27 -0
  16. data/lib/declare_schema/schema_change/column_change.rb +32 -0
  17. data/lib/declare_schema/schema_change/column_remove.rb +20 -0
  18. data/lib/declare_schema/schema_change/column_rename.rb +23 -0
  19. data/lib/declare_schema/schema_change/foreign_key_add.rb +25 -0
  20. data/lib/declare_schema/schema_change/foreign_key_remove.rb +20 -0
  21. data/lib/declare_schema/schema_change/index_add.rb +33 -0
  22. data/lib/declare_schema/schema_change/index_remove.rb +20 -0
  23. data/lib/declare_schema/schema_change/primary_key_change.rb +33 -0
  24. data/lib/declare_schema/schema_change/table_add.rb +37 -0
  25. data/lib/declare_schema/schema_change/table_change.rb +36 -0
  26. data/lib/declare_schema/schema_change/table_remove.rb +22 -0
  27. data/lib/declare_schema/schema_change/table_rename.rb +22 -0
  28. data/lib/declare_schema/version.rb +1 -1
  29. data/lib/generators/declare_schema/migration/USAGE +14 -24
  30. data/lib/generators/declare_schema/migration/migration_generator.rb +40 -38
  31. data/lib/generators/declare_schema/migration/migrator.rb +175 -177
  32. data/lib/generators/declare_schema/migration/templates/migration.rb.erb +3 -3
  33. data/lib/generators/declare_schema/support/model.rb +4 -4
  34. data/spec/lib/declare_schema/api_spec.rb +8 -8
  35. data/spec/lib/declare_schema/field_declaration_dsl_spec.rb +41 -15
  36. data/spec/lib/declare_schema/field_spec_spec.rb +2 -2
  37. data/spec/lib/declare_schema/generator_spec.rb +5 -5
  38. data/spec/lib/declare_schema/interactive_primary_key_spec.rb +117 -28
  39. data/spec/lib/declare_schema/migration_generator_spec.rb +1990 -843
  40. data/spec/lib/declare_schema/model/column_spec.rb +49 -23
  41. data/spec/lib/declare_schema/model/foreign_key_definition_spec.rb +158 -57
  42. data/spec/lib/declare_schema/model/habtm_model_shim_spec.rb +0 -2
  43. data/spec/lib/declare_schema/model/index_definition_spec.rb +189 -78
  44. data/spec/lib/declare_schema/model/table_options_definition_spec.rb +75 -11
  45. data/spec/lib/declare_schema/schema_change/base_spec.rb +75 -0
  46. data/spec/lib/declare_schema/schema_change/column_add_spec.rb +30 -0
  47. data/spec/lib/declare_schema/schema_change/column_change_spec.rb +33 -0
  48. data/spec/lib/declare_schema/schema_change/column_remove_spec.rb +30 -0
  49. data/spec/lib/declare_schema/schema_change/column_rename_spec.rb +28 -0
  50. data/spec/lib/declare_schema/schema_change/foreign_key_add_spec.rb +29 -0
  51. data/spec/lib/declare_schema/schema_change/foreign_key_remove_spec.rb +29 -0
  52. data/spec/lib/declare_schema/schema_change/index_add_spec.rb +56 -0
  53. data/spec/lib/declare_schema/schema_change/index_remove_spec.rb +29 -0
  54. data/spec/lib/declare_schema/schema_change/primary_key_change_spec.rb +69 -0
  55. data/spec/lib/declare_schema/schema_change/table_add_spec.rb +50 -0
  56. data/spec/lib/declare_schema/schema_change/table_change_spec.rb +30 -0
  57. data/spec/lib/declare_schema/schema_change/table_remove_spec.rb +27 -0
  58. data/spec/lib/declare_schema/schema_change/table_rename_spec.rb +27 -0
  59. data/spec/lib/generators/declare_schema/migration/migrator_spec.rb +59 -11
  60. data/spec/spec_helper.rb +1 -1
  61. data/spec/support/acceptance_spec_helpers.rb +2 -2
  62. metadata +35 -6
  63. data/test_responses.txt +0 -2
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rails'
4
-
5
3
  begin
6
4
  require 'mysql2'
7
5
  rescue LoadError
@@ -16,7 +14,7 @@ RSpec.describe DeclareSchema::Model::Column do
16
14
 
17
15
  describe 'class methods' do
18
16
  describe '.native_type?' do
19
- if Rails::VERSION::MAJOR >= 5
17
+ if ActiveSupport::VERSION::MAJOR >= 5
20
18
  let(:native_types) { [:string, :text, :integer, :float, :decimal, :datetime, :time, :date, :binary, :boolean, :json] }
21
19
  else
22
20
  let(:native_types) { [:string, :text, :integer, :float, :decimal, :datetime, :time, :date, :binary, :boolean] }
@@ -65,9 +63,7 @@ RSpec.describe DeclareSchema::Model::Column do
65
63
  end
66
64
 
67
65
  describe '.deserialize_default_value' do
68
- require 'rails'
69
-
70
- if ::Rails::VERSION::MAJOR >= 5
66
+ if ::ActiveSupport::VERSION::MAJOR >= 5
71
67
  it 'deserializes :boolean' do
72
68
  expect(described_class.deserialize_default_value(nil, :boolean, 'true')).to eq(true)
73
69
  expect(described_class.deserialize_default_value(nil, :boolean, 'false')).to eq(false)
@@ -85,14 +81,6 @@ RSpec.describe DeclareSchema::Model::Column do
85
81
  end
86
82
 
87
83
  describe 'instance methods' do
88
- before do
89
- class ColumnTestModel < ActiveRecord::Base
90
- fields do
91
- title :string, limit: 127, null: false
92
- count :integer, null: false
93
- end
94
- end
95
- end
96
84
  let(:model) { ColumnTestModel }
97
85
  let(:type) { :integer }
98
86
  let(:current_table_name) { model.table_name }
@@ -108,18 +96,56 @@ RSpec.describe DeclareSchema::Model::Column do
108
96
  sql_type_metadata: {}) }
109
97
  subject { described_class.new(model, current_table_name, column) }
110
98
 
111
- describe '#type' do
112
- it 'returns type' do
113
- expect(subject.type).to eq(type)
99
+ context 'Using fields' do
100
+ before do
101
+ class ColumnTestModel < ActiveRecord::Base
102
+ fields do
103
+ title :string, limit: 127, null: false
104
+ count :integer, null: false
105
+ end
106
+ end
107
+ end
108
+
109
+ describe '#type' do
110
+ it 'returns type' do
111
+ expect(subject.type).to eq(type)
112
+ end
113
+ end
114
+
115
+ describe '#schema_attributes' do
116
+ it 'returns a hash with relevant key/values' do
117
+ if defined?(Mysql2)
118
+ expect(subject.schema_attributes).to eq(type: :integer, null: false, limit: 4)
119
+ else
120
+ expect(subject.schema_attributes).to eq(type: :integer, null: false)
121
+ end
122
+ end
114
123
  end
115
124
  end
116
125
 
117
- describe '#schema_attributes' do
118
- it 'returns a hash with relevant key/values' do
119
- if defined?(Mysql2)
120
- expect(subject.schema_attributes).to eq(type: :integer, null: false, limit: 4)
121
- else
122
- expect(subject.schema_attributes).to eq(type: :integer, null: false)
126
+ context 'Using declare_schema' do
127
+ before do
128
+ class ColumnTestModel < ActiveRecord::Base
129
+ declare_schema do
130
+ string :title, limit: 127, null: false
131
+ integer :count, null: false
132
+ end
133
+ end
134
+ end
135
+
136
+ describe '#type' do
137
+ it 'returns type' do
138
+ expect(subject.type).to eq(type)
139
+ end
140
+ end
141
+
142
+ describe '#schema_attributes' do
143
+ it 'returns a hash with relevant key/values' do
144
+ if defined?(Mysql2)
145
+ expect(subject.schema_attributes).to eq(type: :integer, null: false, limit: 4)
146
+ else
147
+ expect(subject.schema_attributes).to eq(type: :integer, null: false)
148
+ end
123
149
  end
124
150
  end
125
151
  end
@@ -3,89 +3,190 @@
3
3
  require_relative '../../../../lib/declare_schema/model/foreign_key_definition'
4
4
 
5
5
  RSpec.describe DeclareSchema::Model::ForeignKeyDefinition do
6
- before do
7
- load File.expand_path('../prepare_testapp.rb', __dir__)
6
+ let(:model_class) { Network }
7
+
8
+ context 'Using fields' do
9
+ before do
10
+ load File.expand_path('../prepare_testapp.rb', __dir__)
8
11
 
9
- class Network < ActiveRecord::Base
10
- fields do
11
- name :string, limit: 127, index: true
12
+ class Network < ActiveRecord::Base
13
+ fields do
14
+ name :string, limit: 127, index: true
12
15
 
13
- timestamps
16
+ timestamps
17
+ end
14
18
  end
15
19
  end
16
- end
17
-
18
- let(:model_class) { Network }
19
20
 
20
- describe 'instance methods' do
21
- let(:connection) { instance_double(ActiveRecord::Base.connection.class) }
22
- let(:model) { instance_double('Model', table_name: 'models', connection: connection) }
23
- let(:foreign_key) { :network_id }
24
- let(:options) { {} }
25
- subject { described_class.new(model, foreign_key, options)}
21
+ describe 'instance methods' do
22
+ let(:connection) { instance_double(ActiveRecord::Base.connection.class) }
23
+ let(:model) { instance_double('Model', table_name: 'models', connection: connection) }
24
+ let(:foreign_key) { :network_id }
25
+ let(:options) { {} }
26
+ subject { described_class.new(model, foreign_key, options)}
26
27
 
27
- before do
28
- allow(connection).to receive(:index_name).with('models', column: 'network_id') { 'on_network_id' }
29
- end
30
-
31
- describe '#initialize' do
32
- it 'normalizes symbols to strings' do
33
- expect(subject.foreign_key).to eq('network_id')
34
- expect(subject.parent_table_name).to eq('networks')
28
+ before do
29
+ allow(model.connection).to receive(:index_name).with(any_args) { 'index_on_network_id' }
35
30
  end
36
31
 
37
- context 'when most options passed' do
38
- let(:options) { { parent_table: :networks, foreign_key: :the_network_id, index_name: :index_on_network_id } }
39
-
32
+ describe '#initialize' do
40
33
  it 'normalizes symbols to strings' do
41
34
  expect(subject.foreign_key).to eq('network_id')
42
- expect(subject.foreign_key_name).to eq('the_network_id')
43
35
  expect(subject.parent_table_name).to eq('networks')
44
- expect(subject.foreign_key).to eq('network_id')
45
- expect(subject.constraint_name).to eq('index_on_network_id')
46
- expect(subject.on_delete_cascade).to be_falsey
47
36
  end
48
- end
49
37
 
50
- context 'when all options passed' do
51
- let(:foreign_key) { nil }
52
- let(:options) { { parent_table: :networks, foreign_key: :the_network_id, index_name: :index_on_network_id,
53
- constraint_name: :constraint_1, dependent: :delete } }
38
+ context 'when most options passed' do
39
+ let(:options) { { parent_table: :networks, foreign_key: :the_network_id } }
40
+
41
+ it 'normalizes symbols to strings' do
42
+ expect(subject.foreign_key).to eq('network_id')
43
+ expect(subject.foreign_key_name).to eq('the_network_id')
44
+ expect(subject.parent_table_name).to eq('networks')
45
+ expect(subject.foreign_key).to eq('network_id')
46
+ expect(subject.constraint_name).to eq('index_on_network_id')
47
+ expect(subject.on_delete_cascade).to be_falsey
48
+ end
49
+ end
54
50
 
55
- it 'normalizes symbols to strings' do
56
- expect(subject.foreign_key).to be_nil
57
- expect(subject.foreign_key_name).to eq('the_network_id')
58
- expect(subject.parent_table_name).to eq('networks')
59
- expect(subject.constraint_name).to eq('constraint_1')
60
- expect(subject.on_delete_cascade).to be_truthy
51
+ context 'when all options passed' do
52
+ let(:foreign_key) { nil }
53
+ let(:options) { { parent_table: :networks, foreign_key: :the_network_id, constraint_name: :constraint_1, dependent: :delete } }
54
+
55
+ it 'normalizes symbols to strings' do
56
+ expect(subject.foreign_key).to be_nil
57
+ expect(subject.foreign_key_name).to eq('the_network_id')
58
+ expect(subject.parent_table_name).to eq('networks')
59
+ expect(subject.constraint_name).to eq('constraint_1')
60
+ expect(subject.on_delete_cascade).to be_truthy
61
+ end
62
+ end
63
+
64
+ context 'when constraint name passed as empty string' do
65
+ let(:options) { { constraint_name: "" } }
66
+ it 'defaults to rails constraint name' do
67
+ expect(subject.constraint_name).to eq("index_on_network_id")
68
+ end
69
+ end
70
+
71
+ context 'when no constraint name passed' do
72
+ it 'defaults to rails constraint name' do
73
+ expect(subject.constraint_name).to eq("index_on_network_id")
74
+ end
61
75
  end
62
76
  end
63
77
  end
64
78
 
65
- describe '#to_add_statement' do
66
- it 'returns add_foreign_key command' do
67
- expect(subject.to_add_statement).to eq('add_foreign_key("models", "networks", column: "network_id", name: "on_network_id")')
79
+ describe 'class << self' do
80
+ let(:connection) { instance_double(ActiveRecord::Base.connection.class) }
81
+ let(:model) { instance_double('Model', table_name: 'models', connection: connection) }
82
+ let(:old_table_name) { 'networks' }
83
+ before do
84
+ allow(connection).to receive(:quote_table_name).with('networks') { 'networks' }
85
+ allow(connection).to receive(:select_rows) { [['CONSTRAINT `constraint` FOREIGN KEY (`network_id`) REFERENCES `networks` (`id`)']] }
86
+ allow(connection).to receive(:index_name).with('models', column: 'network_id') { }
87
+ end
88
+
89
+ describe '.for_model' do
90
+ subject { described_class.for_model(model, old_table_name) }
91
+
92
+ it 'returns new object' do
93
+ expect(subject.size).to eq(1), subject.inspect
94
+ expect(subject.first).to be_kind_of(described_class)
95
+ expect(subject.first.foreign_key).to eq('network_id')
96
+ end
68
97
  end
69
98
  end
70
99
  end
71
100
 
72
- describe 'class << self' do
73
- let(:connection) { instance_double(ActiveRecord::Base.connection.class) }
74
- let(:model) { instance_double('Model', table_name: 'models', connection: connection) }
75
- let(:old_table_name) { 'networks' }
101
+ context 'Using declare_schema' do
76
102
  before do
77
- allow(connection).to receive(:quote_table_name).with('networks') { 'networks' }
78
- allow(connection).to receive(:select_rows) { [['CONSTRAINT `constraint` FOREIGN KEY (`network_id`) REFERENCES `networks` (`id`)']] }
79
- allow(connection).to receive(:index_name).with('models', column: 'network_id') { }
103
+ load File.expand_path('../prepare_testapp.rb', __dir__)
104
+
105
+ class Network < ActiveRecord::Base
106
+ declare_schema do
107
+ string :name, limit: 127, index: true
108
+
109
+ timestamps
110
+ end
111
+ end
112
+ end
113
+
114
+ describe 'instance methods' do
115
+ let(:connection) { instance_double(ActiveRecord::Base.connection.class) }
116
+ let(:model) { instance_double('Model', table_name: 'models', connection: connection) }
117
+ let(:foreign_key) { :network_id }
118
+ let(:options) { {} }
119
+ subject { described_class.new(model, foreign_key, options)}
120
+
121
+ before do
122
+ allow(model.connection).to receive(:index_name).with(any_args) { 'index_on_network_id' }
123
+ end
124
+
125
+ describe '#initialize' do
126
+ it 'normalizes symbols to strings' do
127
+ expect(subject.foreign_key).to eq('network_id')
128
+ expect(subject.parent_table_name).to eq('networks')
129
+ end
130
+
131
+ context 'when most options passed' do
132
+ let(:options) { { parent_table: :networks, foreign_key: :the_network_id } }
133
+
134
+ it 'normalizes symbols to strings' do
135
+ expect(subject.foreign_key).to eq('network_id')
136
+ expect(subject.foreign_key_name).to eq('the_network_id')
137
+ expect(subject.parent_table_name).to eq('networks')
138
+ expect(subject.foreign_key).to eq('network_id')
139
+ expect(subject.constraint_name).to eq('index_on_network_id')
140
+ expect(subject.on_delete_cascade).to be_falsey
141
+ end
142
+ end
143
+
144
+ context 'when all options passed' do
145
+ let(:foreign_key) { nil }
146
+ let(:options) { { parent_table: :networks, foreign_key: :the_network_id, constraint_name: :constraint_1, dependent: :delete } }
147
+
148
+ it 'normalizes symbols to strings' do
149
+ expect(subject.foreign_key).to be_nil
150
+ expect(subject.foreign_key_name).to eq('the_network_id')
151
+ expect(subject.parent_table_name).to eq('networks')
152
+ expect(subject.constraint_name).to eq('constraint_1')
153
+ expect(subject.on_delete_cascade).to be_truthy
154
+ end
155
+ end
156
+
157
+ context 'when constraint name passed as empty string' do
158
+ let(:options) { { constraint_name: "" } }
159
+ it 'defaults to rails constraint name' do
160
+ expect(subject.constraint_name).to eq("index_on_network_id")
161
+ end
162
+ end
163
+
164
+ context 'when no constraint name passed' do
165
+ it 'defaults to rails constraint name' do
166
+ expect(subject.constraint_name).to eq("index_on_network_id")
167
+ end
168
+ end
169
+ end
80
170
  end
81
171
 
82
- describe '.for_model' do
83
- subject { described_class.for_model(model, old_table_name) }
172
+ describe 'class << self' do
173
+ let(:connection) { instance_double(ActiveRecord::Base.connection.class) }
174
+ let(:model) { instance_double('Model', table_name: 'models', connection: connection) }
175
+ let(:old_table_name) { 'networks' }
176
+ before do
177
+ allow(connection).to receive(:quote_table_name).with('networks') { 'networks' }
178
+ allow(connection).to receive(:select_rows) { [['CONSTRAINT `constraint` FOREIGN KEY (`network_id`) REFERENCES `networks` (`id`)']] }
179
+ allow(connection).to receive(:index_name).with('models', column: 'network_id') { }
180
+ end
181
+
182
+ describe '.for_model' do
183
+ subject { described_class.for_model(model, old_table_name) }
84
184
 
85
- it 'returns new object' do
86
- expect(subject.size).to eq(1), subject.inspect
87
- expect(subject.first).to be_kind_of(described_class)
88
- expect(subject.first.foreign_key).to eq('network_id')
185
+ it 'returns new object' do
186
+ expect(subject.size).to eq(1), subject.inspect
187
+ expect(subject.first).to be_kind_of(described_class)
188
+ expect(subject.first.foreign_key).to eq('network_id')
189
+ end
89
190
  end
90
191
  end
91
192
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rails'
4
-
5
3
  begin
6
4
  require 'mysql2'
7
5
  rescue LoadError
@@ -10,119 +10,230 @@ require_relative '../../../../lib/declare_schema/model/index_definition'
10
10
  # unless you manually create any Sqlite tables with UNIQUE constraints.
11
11
 
12
12
  RSpec.describe DeclareSchema::Model::IndexDefinition do
13
- before do
14
- load File.expand_path('../prepare_testapp.rb', __dir__)
13
+ let(:model_class) { IndexDefinitionTestModel }
14
+
15
+ context 'Using fields' do
16
+ before do
17
+ load File.expand_path('../prepare_testapp.rb', __dir__)
15
18
 
16
- class IndexDefinitionTestModel < ActiveRecord::Base
17
- fields do
18
- name :string, limit: 127, index: true
19
+ class IndexDefinitionTestModel < ActiveRecord::Base
20
+ fields do
21
+ name :string, limit: 127, index: true
19
22
 
20
- timestamps
23
+ timestamps
24
+ end
21
25
  end
22
- end
23
26
 
24
- class IndexDefinitionCompoundIndexModel < ActiveRecord::Base
25
- fields do
26
- fk1_id :integer
27
- fk2_id :integer
27
+ class IndexDefinitionCompoundIndexModel < ActiveRecord::Base
28
+ fields do
29
+ fk1_id :integer
30
+ fk2_id :integer
28
31
 
29
- timestamps
32
+ timestamps
33
+ end
30
34
  end
31
35
  end
32
- end
33
36
 
34
- let(:model_class) { IndexDefinitionTestModel }
37
+ describe 'instance methods' do
38
+ let(:model) { model_class.new }
39
+ subject { declared_class.new(model_class) }
35
40
 
36
- describe 'instance methods' do
37
- let(:model) { model_class.new }
38
- subject { declared_class.new(model_class) }
41
+ it 'has index_definitions' do
42
+ expect(model_class.index_definitions).to be_kind_of(Array)
43
+ expect(model_class.index_definitions.map(&:name)).to eq(['on_name'])
44
+ expect([:name, :fields, :unique].map { |attr| model_class.index_definitions[0].send(attr)}).to eq(
45
+ ['on_name', ['name'], false]
46
+ )
47
+ end
39
48
 
40
- it 'has index_definitions' do
41
- expect(model_class.index_definitions).to be_kind_of(Array)
42
- expect(model_class.index_definitions.map(&:name)).to eq(['on_name'])
43
- expect([:name, :fields, :unique].map { |attr| model_class.index_definitions[0].send(attr)}).to eq(
44
- ['on_name', ['name'], false]
45
- )
49
+ it 'has index_definitions_with_primary_key' do
50
+ expect(model_class.index_definitions_with_primary_key).to be_kind_of(Array)
51
+ result = model_class.index_definitions_with_primary_key.sort_by(&:name)
52
+ expect(result.map(&:name)).to eq(['PRIMARY', 'on_name'])
53
+ expect([:name, :fields, :unique].map { |attr| result[0].send(attr)}).to eq(
54
+ ['PRIMARY', ['id'], true]
55
+ )
56
+ expect([:name, :fields, :unique].map { |attr| result[1].send(attr)}).to eq(
57
+ ['on_name', ['name'], false]
58
+ )
59
+ end
46
60
  end
47
61
 
48
- it 'has index_definitions_with_primary_key' do
49
- expect(model_class.index_definitions_with_primary_key).to be_kind_of(Array)
50
- result = model_class.index_definitions_with_primary_key.sort_by(&:name)
51
- expect(result.map(&:name)).to eq(['PRIMARY', 'on_name'])
52
- expect([:name, :fields, :unique].map { |attr| result[0].send(attr)}).to eq(
53
- ['PRIMARY', ['id'], true]
54
- )
55
- expect([:name, :fields, :unique].map { |attr| result[1].send(attr)}).to eq(
56
- ['on_name', ['name'], false]
57
- )
62
+ describe 'class methods' do
63
+ describe 'index_name' do
64
+ it 'works with a single column' do
65
+ expect(described_class.index_name('parent_id')).to eq('on_parent_id')
66
+ end
67
+
68
+ it 'works with many columns' do
69
+ expect(described_class.index_name(['a', 'b', 'c'])).to eq('on_a_and_b_and_c')
70
+ end
71
+ end
72
+
73
+ context 'with a migrated database' do
74
+ before do
75
+ ActiveRecord::Base.connection.execute <<~EOS
76
+ CREATE TABLE index_definition_test_models (
77
+ id INTEGER NOT NULL PRIMARY KEY,
78
+ name #{if defined?(SQLite3) then 'TEXT' else 'VARCHAR(255)' end} NOT NULL
79
+ )
80
+ EOS
81
+ ActiveRecord::Base.connection.execute <<~EOS
82
+ CREATE UNIQUE INDEX index_definition_test_models_on_name ON index_definition_test_models(name)
83
+ EOS
84
+ ActiveRecord::Base.connection.execute <<~EOS
85
+ CREATE TABLE index_definition_compound_index_models (
86
+ fk1_id INTEGER NOT NULL,
87
+ fk2_id INTEGER NOT NULL,
88
+ PRIMARY KEY (fk1_id, fk2_id)
89
+ )
90
+ EOS
91
+ ActiveRecord::Base.connection.schema_cache.clear!
92
+ end
93
+
94
+ describe 'for_model' do
95
+ subject { described_class.for_model(model_class) }
96
+
97
+ context 'with single-column PK' do
98
+ it 'returns the indexes for the model' do
99
+ expect(subject.size).to eq(2), subject.inspect
100
+ expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
101
+ ['index_definition_test_models_on_name', ['name'], true]
102
+ )
103
+ expect([:name, :columns, :unique].map { |attr| subject[1].send(attr) }).to eq(
104
+ ['PRIMARY', ['id'], true]
105
+ )
106
+ end
107
+ end
108
+
109
+ context 'with compound-column PK' do
110
+ let(:model_class) { IndexDefinitionCompoundIndexModel }
111
+
112
+ it 'returns the indexes for the model' do
113
+ if ActiveSupport::VERSION::MAJOR < 5
114
+ expect(model_class.connection).to receive(:primary_key).with('index_definition_compound_index_models').and_return(nil)
115
+ connection_stub = instance_double(ActiveRecord::Base.connection.class, "connection")
116
+ expect(connection_stub).to receive(:indexes).
117
+ with('index_definition_compound_index_models').
118
+ and_return([DeclareSchema::Model::IndexDefinition.new(model_class, ['fk1_id', 'fk2_id'], name: 'PRIMARY')])
119
+
120
+ expect(model_class.connection).to receive(:dup).and_return(connection_stub)
121
+ end
122
+
123
+ expect(subject.size).to eq(1), subject.inspect
124
+ expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
125
+ ['PRIMARY', ['fk1_id', 'fk2_id'], true]
126
+ )
127
+ end
128
+ end
129
+ end
130
+ end
58
131
  end
59
132
  end
60
133
 
61
- describe 'class methods' do
62
- describe 'index_name' do
63
- it 'works with a single column' do
64
- expect(described_class.index_name('parent_id')).to eq('on_parent_id')
134
+ context 'Using declare_schema' do
135
+ before do
136
+ load File.expand_path('../prepare_testapp.rb', __dir__)
137
+
138
+ class IndexDefinitionTestModel < ActiveRecord::Base
139
+ declare_schema do
140
+ string :name, limit: 127, index: true
141
+
142
+ timestamps
143
+ end
65
144
  end
66
145
 
67
- it 'works with many columns' do
68
- expect(described_class.index_name(['a', 'b', 'c'])).to eq('on_a_and_b_and_c')
146
+ class IndexDefinitionCompoundIndexModel < ActiveRecord::Base
147
+ declare_schema do
148
+ integer :fk1_id
149
+ integer :fk2_id
150
+
151
+ timestamps
152
+ end
69
153
  end
70
154
  end
71
155
 
72
- context 'with a migrated database' do
73
- before do
74
- ActiveRecord::Base.connection.execute <<~EOS
156
+ describe 'instance methods' do
157
+ let(:model) { model_class.new }
158
+ subject { declared_class.new(model_class) }
159
+
160
+ it 'has index_definitions' do
161
+ expect(model_class.index_definitions).to be_kind_of(Array)
162
+ expect(model_class.index_definitions.map(&:name)).to eq(['on_name'])
163
+ expect([:name, :fields, :unique].map { |attr| model_class.index_definitions[0].send(attr)}).to eq(
164
+ ['on_name', ['name'], false]
165
+ )
166
+ end
167
+
168
+ it 'has index_definitions_with_primary_key' do
169
+ expect(model_class.index_definitions_with_primary_key).to be_kind_of(Array)
170
+ result = model_class.index_definitions_with_primary_key.sort_by(&:name)
171
+ expect(result.map(&:name)).to eq(['PRIMARY', 'on_name'])
172
+ expect([:name, :fields, :unique].map { |attr| result[0].send(attr)}).to eq(
173
+ ['PRIMARY', ['id'], true]
174
+ )
175
+ expect([:name, :fields, :unique].map { |attr| result[1].send(attr)}).to eq(
176
+ ['on_name', ['name'], false]
177
+ )
178
+ end
179
+ end
180
+
181
+ describe 'class << self' do
182
+ context 'with a migrated database' do
183
+ before do
184
+ ActiveRecord::Base.connection.execute <<~EOS
75
185
  CREATE TABLE index_definition_test_models (
76
186
  id INTEGER NOT NULL PRIMARY KEY,
77
- name #{if defined?(Sqlite3) then 'TEXT' else 'VARCHAR(255)' end} NOT NULL
187
+ name #{if defined?(SQLite3) then 'TEXT' else 'VARCHAR(255)' end} NOT NULL
78
188
  )
79
- EOS
80
- ActiveRecord::Base.connection.execute <<~EOS
189
+ EOS
190
+ ActiveRecord::Base.connection.execute <<~EOS
81
191
  CREATE UNIQUE INDEX index_definition_test_models_on_name ON index_definition_test_models(name)
82
- EOS
83
- ActiveRecord::Base.connection.execute <<~EOS
192
+ EOS
193
+ ActiveRecord::Base.connection.execute <<~EOS
84
194
  CREATE TABLE index_definition_compound_index_models (
85
195
  fk1_id INTEGER NOT NULL,
86
196
  fk2_id INTEGER NOT NULL,
87
197
  PRIMARY KEY (fk1_id, fk2_id)
88
198
  )
89
- EOS
90
- ActiveRecord::Base.connection.schema_cache.clear!
91
- end
92
-
93
- describe 'for_model' do
94
- subject { described_class.for_model(model_class) }
199
+ EOS
200
+ ActiveRecord::Base.connection.schema_cache.clear!
201
+ end
95
202
 
96
- context 'with single-column PK' do
97
- it 'returns the indexes for the model' do
98
- expect(subject.size).to eq(2), subject.inspect
99
- expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
100
- ['index_definition_test_models_on_name', ['name'], true]
101
- )
102
- expect([:name, :columns, :unique].map { |attr| subject[1].send(attr) }).to eq(
103
- ['PRIMARY', ['id'], true]
104
- )
203
+ describe 'for_model' do
204
+ subject { described_class.for_model(model_class) }
205
+
206
+ context 'with single-column PK' do
207
+ it 'returns the indexes for the model' do
208
+ expect(subject.size).to eq(2), subject.inspect
209
+ expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
210
+ ['index_definition_test_models_on_name', ['name'], true]
211
+ )
212
+ expect([:name, :columns, :unique].map { |attr| subject[1].send(attr) }).to eq(
213
+ ['PRIMARY', ['id'], true]
214
+ )
215
+ end
105
216
  end
106
- end
107
217
 
108
- context 'with compound-column PK' do
109
- let(:model_class) { IndexDefinitionCompoundIndexModel }
218
+ context 'with compound-column PK' do
219
+ let(:model_class) { IndexDefinitionCompoundIndexModel }
110
220
 
111
- it 'returns the indexes for the model' do
112
- if Rails::VERSION::MAJOR < 5
113
- expect(model_class.connection).to receive(:primary_key).with('index_definition_compound_index_models').and_return(nil)
114
- connection_stub = instance_double(ActiveRecord::Base.connection.class, "connection")
115
- expect(connection_stub).to receive(:indexes).
116
- with('index_definition_compound_index_models').
117
- and_return([DeclareSchema::Model::IndexDefinition.new(model_class, ['fk1_id', 'fk2_id'], name: 'PRIMARY')])
221
+ it 'returns the indexes for the model' do
222
+ if ActiveSupport::VERSION::MAJOR < 5
223
+ expect(model_class.connection).to receive(:primary_key).with('index_definition_compound_index_models').and_return(nil)
224
+ connection_stub = instance_double(ActiveRecord::Base.connection.class, "connection")
225
+ expect(connection_stub).to receive(:indexes).
226
+ with('index_definition_compound_index_models').
227
+ and_return([DeclareSchema::Model::IndexDefinition.new(model_class, ['fk1_id', 'fk2_id'], name: 'PRIMARY')])
118
228
 
119
- expect(model_class.connection).to receive(:dup).and_return(connection_stub)
120
- end
229
+ expect(model_class.connection).to receive(:dup).and_return(connection_stub)
230
+ end
121
231
 
122
- expect(subject.size).to eq(1), subject.inspect
123
- expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
124
- ['PRIMARY', ['fk1_id', 'fk2_id'], true]
125
- )
232
+ expect(subject.size).to eq(1), subject.inspect
233
+ expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
234
+ ['PRIMARY', ['fk1_id', 'fk2_id'], true]
235
+ )
236
+ end
126
237
  end
127
238
  end
128
239
  end