declare_schema 0.8.0.pre.4 → 0.10.0.pre.dc.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/declare_schema_build.yml +1 -1
- data/CHANGELOG.md +21 -1
- data/Gemfile.lock +1 -1
- data/README.md +91 -13
- data/lib/declare_schema.rb +46 -0
- data/lib/declare_schema/dsl.rb +40 -0
- data/lib/declare_schema/extensions/active_record/fields_declaration.rb +23 -4
- data/lib/declare_schema/model.rb +51 -59
- data/lib/declare_schema/model/column.rb +2 -0
- data/lib/declare_schema/model/field_spec.rb +12 -9
- data/lib/declare_schema/model/habtm_model_shim.rb +1 -1
- data/lib/declare_schema/version.rb +1 -1
- data/lib/generators/declare_schema/migration/migrator.rb +22 -33
- data/lib/generators/declare_schema/support/model.rb +4 -4
- data/spec/lib/declare_schema/api_spec.rb +7 -7
- data/spec/lib/declare_schema/field_declaration_dsl_spec.rb +41 -15
- data/spec/lib/declare_schema/field_spec_spec.rb +73 -22
- data/spec/lib/declare_schema/generator_spec.rb +3 -3
- data/spec/lib/declare_schema/interactive_primary_key_spec.rb +78 -26
- data/spec/lib/declare_schema/migration_generator_spec.rb +1989 -815
- data/spec/lib/declare_schema/model/column_spec.rb +47 -17
- data/spec/lib/declare_schema/model/foreign_key_definition_spec.rb +146 -57
- data/spec/lib/declare_schema/model/habtm_model_shim_spec.rb +3 -3
- data/spec/lib/declare_schema/model/index_definition_spec.rb +188 -77
- data/spec/lib/declare_schema/model/table_options_definition_spec.rb +75 -11
- data/spec/lib/declare_schema_spec.rb +101 -0
- data/spec/lib/generators/declare_schema/migration/migrator_spec.rb +12 -2
- metadata +7 -6
- 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
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
7
|
-
|
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
|
-
|
10
|
-
|
11
|
-
|
12
|
+
class Network < ActiveRecord::Base
|
13
|
+
fields do
|
14
|
+
name :string, limit: 127, index: true
|
12
15
|
|
13
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
32
|
-
|
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
|
-
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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 '
|
66
|
-
|
67
|
-
|
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
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
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 '
|
83
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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.
|
91
|
+
expect(subject._defined_primary_key).to eq(false)
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
-
describe '#
|
95
|
+
describe '#_defined_primary_key' do
|
96
96
|
it 'returns false' do
|
97
|
-
expect(subject.
|
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
|
-
|
14
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
+
class IndexDefinitionTestModel < ActiveRecord::Base
|
20
|
+
fields do
|
21
|
+
name :string, limit: 127, index: true
|
19
22
|
|
20
|
-
|
23
|
+
timestamps
|
24
|
+
end
|
21
25
|
end
|
22
|
-
end
|
23
26
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
class IndexDefinitionCompoundIndexModel < ActiveRecord::Base
|
28
|
+
fields do
|
29
|
+
fk1_id :integer
|
30
|
+
fk2_id :integer
|
28
31
|
|
29
|
-
|
32
|
+
timestamps
|
33
|
+
end
|
30
34
|
end
|
31
35
|
end
|
32
|
-
end
|
33
36
|
|
34
|
-
|
37
|
+
describe 'instance methods' do
|
38
|
+
let(:model) { model_class.new }
|
39
|
+
subject { declared_class.new(model_class) }
|
35
40
|
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
[
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
-
|
68
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
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
|
-
|
80
|
-
|
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
|
-
|
83
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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
|
-
|
109
|
-
|
218
|
+
context 'with compound-column PK' do
|
219
|
+
let(:model_class) { IndexDefinitionCompoundIndexModel }
|
110
220
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
-
|
120
|
-
|
229
|
+
expect(model_class.connection).to receive(:dup).and_return(connection_stub)
|
230
|
+
end
|
121
231
|
|
122
|
-
|
123
|
-
|
124
|
-
|
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
|