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.
- 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
|