declare_schema 0.9.0 → 0.11.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 +32 -0
- data/Gemfile.lock +1 -1
- data/README.md +12 -2
- data/lib/declare_schema.rb +12 -1
- data/lib/declare_schema/dsl.rb +39 -0
- data/lib/declare_schema/extensions/active_record/fields_declaration.rb +22 -3
- data/lib/declare_schema/model.rb +4 -6
- data/lib/declare_schema/model/column.rb +1 -1
- data/lib/declare_schema/model/foreign_key_definition.rb +6 -11
- data/lib/declare_schema/model/index_definition.rb +1 -20
- data/lib/declare_schema/schema_change/all.rb +22 -0
- data/lib/declare_schema/schema_change/base.rb +45 -0
- data/lib/declare_schema/schema_change/column_add.rb +27 -0
- data/lib/declare_schema/schema_change/column_change.rb +32 -0
- data/lib/declare_schema/schema_change/column_remove.rb +20 -0
- data/lib/declare_schema/schema_change/column_rename.rb +23 -0
- data/lib/declare_schema/schema_change/foreign_key_add.rb +25 -0
- data/lib/declare_schema/schema_change/foreign_key_remove.rb +20 -0
- data/lib/declare_schema/schema_change/index_add.rb +33 -0
- data/lib/declare_schema/schema_change/index_remove.rb +20 -0
- data/lib/declare_schema/schema_change/primary_key_change.rb +33 -0
- data/lib/declare_schema/schema_change/table_add.rb +37 -0
- data/lib/declare_schema/schema_change/table_change.rb +36 -0
- data/lib/declare_schema/schema_change/table_remove.rb +22 -0
- data/lib/declare_schema/schema_change/table_rename.rb +22 -0
- data/lib/declare_schema/version.rb +1 -1
- data/lib/generators/declare_schema/migration/USAGE +14 -24
- data/lib/generators/declare_schema/migration/migration_generator.rb +40 -38
- data/lib/generators/declare_schema/migration/migrator.rb +175 -177
- data/lib/generators/declare_schema/migration/templates/migration.rb.erb +3 -3
- data/lib/generators/declare_schema/support/model.rb +4 -4
- data/spec/lib/declare_schema/api_spec.rb +8 -8
- data/spec/lib/declare_schema/field_declaration_dsl_spec.rb +41 -15
- data/spec/lib/declare_schema/field_spec_spec.rb +2 -2
- data/spec/lib/declare_schema/generator_spec.rb +5 -5
- data/spec/lib/declare_schema/interactive_primary_key_spec.rb +117 -28
- data/spec/lib/declare_schema/migration_generator_spec.rb +1990 -843
- data/spec/lib/declare_schema/model/column_spec.rb +49 -23
- data/spec/lib/declare_schema/model/foreign_key_definition_spec.rb +158 -57
- data/spec/lib/declare_schema/model/habtm_model_shim_spec.rb +0 -2
- data/spec/lib/declare_schema/model/index_definition_spec.rb +189 -78
- data/spec/lib/declare_schema/model/table_options_definition_spec.rb +75 -11
- data/spec/lib/declare_schema/schema_change/base_spec.rb +75 -0
- data/spec/lib/declare_schema/schema_change/column_add_spec.rb +30 -0
- data/spec/lib/declare_schema/schema_change/column_change_spec.rb +33 -0
- data/spec/lib/declare_schema/schema_change/column_remove_spec.rb +30 -0
- data/spec/lib/declare_schema/schema_change/column_rename_spec.rb +28 -0
- data/spec/lib/declare_schema/schema_change/foreign_key_add_spec.rb +29 -0
- data/spec/lib/declare_schema/schema_change/foreign_key_remove_spec.rb +29 -0
- data/spec/lib/declare_schema/schema_change/index_add_spec.rb +56 -0
- data/spec/lib/declare_schema/schema_change/index_remove_spec.rb +29 -0
- data/spec/lib/declare_schema/schema_change/primary_key_change_spec.rb +69 -0
- data/spec/lib/declare_schema/schema_change/table_add_spec.rb +50 -0
- data/spec/lib/declare_schema/schema_change/table_change_spec.rb +30 -0
- data/spec/lib/declare_schema/schema_change/table_remove_spec.rb +27 -0
- data/spec/lib/declare_schema/schema_change/table_rename_spec.rb +27 -0
- data/spec/lib/generators/declare_schema/migration/migrator_spec.rb +59 -11
- data/spec/spec_helper.rb +1 -1
- data/spec/support/acceptance_spec_helpers.rb +2 -2
- metadata +35 -6
- 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
|
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
|
-
|
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
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
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
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
28
|
-
|
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
|
-
|
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 } }
|
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, 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 '
|
66
|
-
|
67
|
-
|
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
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
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 '
|
83
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
@@ -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 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
|
-
|
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
|
-
name #{if defined?(
|
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 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
|
-
|
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
|