declare_schema 0.8.0.pre.5 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/declare_schema_build.yml +1 -1
  3. data/CHANGELOG.md +21 -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/column.rb +2 -0
  11. data/lib/declare_schema/model/field_spec.rb +11 -8
  12. data/lib/declare_schema/model/habtm_model_shim.rb +1 -1
  13. data/lib/declare_schema/version.rb +1 -1
  14. data/lib/generators/declare_schema/migration/migrator.rb +22 -33
  15. data/lib/generators/declare_schema/support/model.rb +4 -4
  16. data/spec/lib/declare_schema/api_spec.rb +7 -7
  17. data/spec/lib/declare_schema/field_declaration_dsl_spec.rb +41 -15
  18. data/spec/lib/declare_schema/field_spec_spec.rb +73 -22
  19. data/spec/lib/declare_schema/generator_spec.rb +3 -3
  20. data/spec/lib/declare_schema/interactive_primary_key_spec.rb +78 -26
  21. data/spec/lib/declare_schema/migration_generator_spec.rb +1989 -815
  22. data/spec/lib/declare_schema/model/column_spec.rb +47 -17
  23. data/spec/lib/declare_schema/model/foreign_key_definition_spec.rb +146 -57
  24. data/spec/lib/declare_schema/model/habtm_model_shim_spec.rb +3 -3
  25. data/spec/lib/declare_schema/model/index_definition_spec.rb +188 -77
  26. data/spec/lib/declare_schema/model/table_options_definition_spec.rb +75 -11
  27. data/spec/lib/declare_schema_spec.rb +101 -0
  28. data/spec/lib/generators/declare_schema/migration/migrator_spec.rb +12 -2
  29. metadata +7 -6
  30. data/test_responses.txt +0 -2
@@ -85,14 +85,6 @@ RSpec.describe DeclareSchema::Model::Column do
85
85
  end
86
86
 
87
87
  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
88
  let(:model) { ColumnTestModel }
97
89
  let(:type) { :integer }
98
90
  let(:current_table_name) { model.table_name }
@@ -108,18 +100,56 @@ RSpec.describe DeclareSchema::Model::Column do
108
100
  sql_type_metadata: {}) }
109
101
  subject { described_class.new(model, current_table_name, column) }
110
102
 
111
- describe '#type' do
112
- it 'returns type' do
113
- expect(subject.type).to eq(type)
103
+ context 'Using fields' do
104
+ before do
105
+ class ColumnTestModel < ActiveRecord::Base
106
+ fields do
107
+ title :string, limit: 127, null: false
108
+ count :integer, null: false
109
+ end
110
+ end
111
+ end
112
+
113
+ describe '#type' do
114
+ it 'returns type' do
115
+ expect(subject.type).to eq(type)
116
+ end
117
+ end
118
+
119
+ describe '#schema_attributes' do
120
+ it 'returns a hash with relevant key/values' do
121
+ if defined?(Mysql2)
122
+ expect(subject.schema_attributes).to eq(type: :integer, null: false, limit: 4)
123
+ else
124
+ expect(subject.schema_attributes).to eq(type: :integer, null: false)
125
+ end
126
+ end
114
127
  end
115
128
  end
116
129
 
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)
130
+ context 'Using declare_schema' do
131
+ before do
132
+ class ColumnTestModel < ActiveRecord::Base
133
+ declare_schema do
134
+ string :title, limit: 127, null: false
135
+ integer :count, null: false
136
+ end
137
+ end
138
+ end
139
+
140
+ describe '#type' do
141
+ it 'returns type' do
142
+ expect(subject.type).to eq(type)
143
+ end
144
+ end
145
+
146
+ describe '#schema_attributes' do
147
+ it 'returns a hash with relevant key/values' do
148
+ if defined?(Mysql2)
149
+ expect(subject.schema_attributes).to eq(type: :integer, null: false, limit: 4)
150
+ else
151
+ expect(subject.schema_attributes).to eq(type: :integer, null: false)
152
+ end
123
153
  end
124
154
  end
125
155
  end
@@ -3,89 +3,178 @@
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
- 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)}
26
20
 
27
- before do
28
- allow(connection).to receive(:index_name).with('models', column: 'network_id') { 'on_network_id' }
29
- end
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)}
30
27
 
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(connection).to receive(:index_name).with('models', column: 'network_id') { '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, index_name: :index_on_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, index_name: :index_on_network_id,
54
+ constraint_name: :constraint_1, dependent: :delete } }
55
+
56
+ it 'normalizes symbols to strings' do
57
+ expect(subject.foreign_key).to be_nil
58
+ expect(subject.foreign_key_name).to eq('the_network_id')
59
+ expect(subject.parent_table_name).to eq('networks')
60
+ expect(subject.constraint_name).to eq('constraint_1')
61
+ expect(subject.on_delete_cascade).to be_truthy
62
+ end
63
+ end
64
+ end
65
+
66
+ describe '#to_add_statement' do
67
+ it 'returns add_foreign_key command' do
68
+ expect(subject.to_add_statement).to eq('add_foreign_key("models", "networks", column: "network_id", name: "on_network_id")')
61
69
  end
62
70
  end
63
71
  end
64
72
 
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")')
73
+ describe 'class << self' do
74
+ let(:connection) { instance_double(ActiveRecord::Base.connection.class) }
75
+ let(:model) { instance_double('Model', table_name: 'models', connection: connection) }
76
+ let(:old_table_name) { 'networks' }
77
+ before do
78
+ allow(connection).to receive(:quote_table_name).with('networks') { 'networks' }
79
+ allow(connection).to receive(:select_rows) { [['CONSTRAINT `constraint` FOREIGN KEY (`network_id`) REFERENCES `networks` (`id`)']] }
80
+ allow(connection).to receive(:index_name).with('models', column: 'network_id') { }
81
+ end
82
+
83
+ describe '.for_model' do
84
+ subject { described_class.for_model(model, old_table_name) }
85
+
86
+ it 'returns new object' do
87
+ expect(subject.size).to eq(1), subject.inspect
88
+ expect(subject.first).to be_kind_of(described_class)
89
+ expect(subject.first.foreign_key).to eq('network_id')
90
+ end
68
91
  end
69
92
  end
70
93
  end
71
94
 
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' }
95
+ context 'Using declare_schema' do
76
96
  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') { }
97
+ load File.expand_path('../prepare_testapp.rb', __dir__)
98
+
99
+ class Network < ActiveRecord::Base
100
+ declare_schema do
101
+ string :name, limit: 127, index: true
102
+
103
+ timestamps
104
+ end
105
+ end
80
106
  end
81
107
 
82
- describe '.for_model' do
83
- subject { described_class.for_model(model, old_table_name) }
108
+ describe 'instance methods' do
109
+ let(:connection) { instance_double(ActiveRecord::Base.connection.class) }
110
+ let(:model) { instance_double('Model', table_name: 'models', connection: connection) }
111
+ let(:foreign_key) { :network_id }
112
+ let(:options) { {} }
113
+ subject { described_class.new(model, foreign_key, options)}
114
+
115
+ before do
116
+ allow(connection).to receive(:index_name).with('models', column: 'network_id') { 'on_network_id' }
117
+ end
118
+
119
+ describe '#initialize' do
120
+ it 'normalizes symbols to strings' do
121
+ expect(subject.foreign_key).to eq('network_id')
122
+ expect(subject.parent_table_name).to eq('networks')
123
+ end
124
+
125
+ context 'when most options passed' do
126
+ let(:options) { { parent_table: :networks, foreign_key: :the_network_id, index_name: :index_on_network_id } }
127
+
128
+ it 'normalizes symbols to strings' do
129
+ expect(subject.foreign_key).to eq('network_id')
130
+ expect(subject.foreign_key_name).to eq('the_network_id')
131
+ expect(subject.parent_table_name).to eq('networks')
132
+ expect(subject.foreign_key).to eq('network_id')
133
+ expect(subject.constraint_name).to eq('index_on_network_id')
134
+ expect(subject.on_delete_cascade).to be_falsey
135
+ end
136
+ end
84
137
 
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')
138
+ context 'when all options passed' do
139
+ let(:foreign_key) { nil }
140
+ let(:options) { { parent_table: :networks, foreign_key: :the_network_id, index_name: :index_on_network_id,
141
+ constraint_name: :constraint_1, dependent: :delete } }
142
+
143
+ it 'normalizes symbols to strings' do
144
+ expect(subject.foreign_key).to be_nil
145
+ expect(subject.foreign_key_name).to eq('the_network_id')
146
+ expect(subject.parent_table_name).to eq('networks')
147
+ expect(subject.constraint_name).to eq('constraint_1')
148
+ expect(subject.on_delete_cascade).to be_truthy
149
+ end
150
+ end
151
+ end
152
+
153
+ describe '#to_add_statement' do
154
+ it 'returns add_foreign_key command' do
155
+ expect(subject.to_add_statement).to eq('add_foreign_key("models", "networks", column: "network_id", name: "on_network_id")')
156
+ end
157
+ end
158
+ end
159
+
160
+ describe 'class << self' do
161
+ let(:connection) { instance_double(ActiveRecord::Base.connection.class) }
162
+ let(:model) { instance_double('Model', table_name: 'models', connection: connection) }
163
+ let(:old_table_name) { 'networks' }
164
+ before do
165
+ allow(connection).to receive(:quote_table_name).with('networks') { 'networks' }
166
+ allow(connection).to receive(:select_rows) { [['CONSTRAINT `constraint` FOREIGN KEY (`network_id`) REFERENCES `networks` (`id`)']] }
167
+ allow(connection).to receive(:index_name).with('models', column: 'network_id') { }
168
+ end
169
+
170
+ describe '.for_model' do
171
+ subject { described_class.for_model(model, old_table_name) }
172
+
173
+ it 'returns new object' do
174
+ expect(subject.size).to eq(1), subject.inspect
175
+ expect(subject.first).to be_kind_of(described_class)
176
+ expect(subject.first.foreign_key).to eq('network_id')
177
+ end
89
178
  end
90
179
  end
91
180
  end
@@ -88,13 +88,13 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
88
88
 
89
89
  describe '#primary_key' do
90
90
  it 'returns false' do
91
- expect(subject.primary_key).to eq(false)
91
+ expect(subject._defined_primary_key).to eq(false)
92
92
  end
93
93
  end
94
94
 
95
- describe '#defined_primary_key' do
95
+ describe '#_defined_primary_key' do
96
96
  it 'returns false' do
97
- expect(subject.defined_primary_key).to eq(false)
97
+ expect(subject._defined_primary_key).to eq(false)
98
98
  end
99
99
  end
100
100
 
@@ -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 Rails::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
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 Rails::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