polymorpheus 3.1.0 → 3.1.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/lib/polymorpheus.rb +1 -0
- data/lib/polymorpheus/mysql_adapter.rb +1 -1
- data/lib/polymorpheus/schema_dumper.rb +3 -2
- data/lib/polymorpheus/trigger.rb +21 -20
- data/lib/polymorpheus/version.rb +1 -1
- data/polymorpheus.gemspec +2 -3
- data/spec/interface/belongs_to_polymorphic_spec.rb +135 -0
- data/spec/interface/has_many_as_polymorph_spec.rb +69 -0
- data/spec/interface/validates_polymorph_spec.rb +35 -0
- data/spec/interface_spec.rb +18 -206
- data/spec/mysql2_adapter_spec.rb +270 -120
- data/spec/schema_dumper_spec.rb +14 -21
- data/spec/spec_helper.rb +34 -2
- data/spec/support/active_record/connection_adapters/abstract_mysql_adapter.rb +9 -0
- data/spec/support/class_defs.rb +12 -0
- data/spec/support/connection_helpers.rb +21 -0
- data/spec/support/schema_helpers.rb +17 -0
- data/spec/{sql_logger.rb → support/sql_logger.rb} +1 -1
- data/spec/support/sql_test_helpers.rb +41 -0
- data/spec/trigger_spec.rb +5 -6
- metadata +14 -23
- data/spec/shared_examples.rb +0 -125
- data/spec/support/db_setup.rb +0 -38
data/spec/mysql2_adapter_spec.rb
CHANGED
@@ -1,167 +1,317 @@
|
|
1
|
-
require 'active_record'
|
2
1
|
require 'spec_helper'
|
3
|
-
require 'sql_logger'
|
4
|
-
require 'polymorpheus'
|
5
|
-
require 'polymorpheus/trigger'
|
6
|
-
require 'shared_examples'
|
7
|
-
|
8
|
-
Polymorpheus::Adapter.load!
|
9
2
|
|
10
3
|
describe Polymorpheus::ConnectionAdapters::MysqlAdapter do
|
4
|
+
# The foreign key name is not truncated, so the maximum column name
|
5
|
+
# length ends up being: 64 - "pets_" - "_fk" == 56
|
6
|
+
#
|
7
|
+
# Using `t.references` also adds an index on this column, with a slightly
|
8
|
+
# longer name: 64 - "index_pets_on_" - "_id" == 46
|
9
|
+
#
|
10
|
+
# Go with the shorter of the two here, since it's still long enough to test
|
11
|
+
# the generation of Polymorpheus' trigger names.
|
12
|
+
let(:long_column1) { ('x' * 46).to_sym }
|
13
|
+
let(:long_column2) { ('y' * 46).to_sym }
|
11
14
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
include Polymorpheus::SqlLogger
|
19
|
-
alias_method :original_execute, :execute
|
20
|
-
alias_method :execute, :log_sql_statements
|
15
|
+
before do
|
16
|
+
create_table(:pets) do |t|
|
17
|
+
t.references :cat
|
18
|
+
t.references :dog
|
19
|
+
t.string :name
|
20
|
+
t.string :color
|
21
21
|
end
|
22
|
-
end
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
alias_method :execute, :original_execute
|
27
|
-
end
|
28
|
-
end
|
23
|
+
create_table(:cats)
|
24
|
+
create_table(:dogs)
|
29
25
|
|
30
|
-
|
31
|
-
let(:sql) { connection.sql_statements }
|
32
|
-
|
33
|
-
def clean_sql(sql_string)
|
34
|
-
sql_string.gsub(/^\n\s*/,'').gsub(/\s*\n\s*$/,'')
|
35
|
-
.gsub(/\n\s*/,"\n").gsub(/\s*$/,"")
|
36
|
-
.gsub('`', '')
|
37
|
-
.gsub(/\ FOREIGN KEY/, "\nFOREIGN KEY")
|
38
|
-
.gsub(/\ REFERENCES/, "\nREFERENCES")
|
39
|
-
.gsub(/\ ON DELETE/, "\nON DELETE")
|
40
|
-
.gsub(/\ ON UPDATE/, "\nON UPDATE")
|
41
|
-
.gsub(/([[:alpha:]])\(/, '\1 (')
|
26
|
+
clear_sql_history
|
42
27
|
end
|
43
28
|
|
44
|
-
|
45
|
-
|
46
|
-
|
29
|
+
after do
|
30
|
+
drop_table :pets
|
31
|
+
drop_table :cats
|
32
|
+
drop_table :dogs
|
47
33
|
end
|
48
34
|
|
49
35
|
#######################################################
|
50
36
|
# Specs
|
51
37
|
#######################################################
|
52
38
|
|
53
|
-
describe
|
54
|
-
|
55
|
-
|
56
|
-
|
39
|
+
describe '#add_polymorphic_constraints' do
|
40
|
+
it 'adds foreign keys with no uniqueness constraints' do
|
41
|
+
add_polymorphic_constraints(
|
42
|
+
'pets',
|
43
|
+
{ cat_id: 'cats.id', dog_id: 'dogs.id' }
|
44
|
+
)
|
45
|
+
|
46
|
+
should_execute_sql <<-EOS
|
47
|
+
DROP TRIGGER IF EXISTS pfki_pets_catid_dogid
|
48
|
+
DROP TRIGGER IF EXISTS pfku_pets_catid_dogid
|
49
|
+
CREATE TRIGGER pfki_pets_catid_dogid BEFORE INSERT ON pets
|
50
|
+
FOR EACH ROW
|
51
|
+
BEGIN
|
52
|
+
IF(IF(NEW.cat_id IS NULL, 0, 1) + IF(NEW.dog_id IS NULL, 0, 1)) <> 1 THEN
|
53
|
+
SET NEW = 'Error';
|
54
|
+
END IF;
|
55
|
+
END
|
56
|
+
CREATE TRIGGER pfku_pets_catid_dogid BEFORE UPDATE ON pets
|
57
|
+
FOR EACH ROW
|
58
|
+
BEGIN
|
59
|
+
IF(IF(NEW.cat_id IS NULL, 0, 1) + IF(NEW.dog_id IS NULL, 0, 1)) <> 1 THEN
|
60
|
+
SET NEW = 'Error';
|
61
|
+
END IF;
|
62
|
+
END
|
63
|
+
|
64
|
+
ALTER TABLE `pets` ADD CONSTRAINT `pets_cat_id_fk`
|
65
|
+
FOREIGN KEY (`cat_id`)
|
66
|
+
REFERENCES `cats`(id)
|
57
67
|
|
58
|
-
|
68
|
+
ALTER TABLE `pets` ADD CONSTRAINT `pets_dog_id_fk`
|
69
|
+
FOREIGN KEY (`dog_id`)
|
70
|
+
REFERENCES `dogs`(id)
|
71
|
+
EOS
|
59
72
|
end
|
60
73
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
68
|
-
let(:remove_indices_sql) do
|
69
|
-
%{ DROP INDEX pfk_pets_kittyid ON pets
|
70
|
-
DROP INDEX pfk_pets_dogid ON pets }
|
71
|
-
end
|
74
|
+
it 'adds uniqueness specified with true' do
|
75
|
+
add_polymorphic_constraints(
|
76
|
+
'pets',
|
77
|
+
{ cat_id: 'cats.id', dog_id: 'dogs.id' },
|
78
|
+
unique: true
|
79
|
+
)
|
72
80
|
|
73
|
-
|
81
|
+
should_execute_sql <<-EOS
|
82
|
+
CREATE UNIQUE INDEX pfk_pets_catid ON pets (cat_id)
|
83
|
+
CREATE UNIQUE INDEX pfk_pets_dogid ON pets (dog_id)
|
84
|
+
EOS
|
74
85
|
end
|
75
86
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
end
|
83
|
-
let(:remove_indices_sql) do
|
84
|
-
%{ DROP INDEX pfk_pets_kittyid_field1 ON pets
|
85
|
-
DROP INDEX pfk_pets_dogid_field1 ON pets }
|
86
|
-
end
|
87
|
+
it 'adds uniqueness specified with a string' do
|
88
|
+
add_polymorphic_constraints('pets', { cat_id: 'cats.id' }, unique: 'name')
|
89
|
+
should_execute_sql <<-EOS
|
90
|
+
CREATE UNIQUE INDEX pfk_pets_catid_name ON pets (cat_id, name)
|
91
|
+
EOS
|
92
|
+
end
|
87
93
|
|
88
|
-
|
94
|
+
it 'adds uniqueness specified as an array' do
|
95
|
+
add_polymorphic_constraints(
|
96
|
+
'pets',
|
97
|
+
{ cat_id: 'cats.id' },
|
98
|
+
unique: [:name, :color]
|
99
|
+
)
|
100
|
+
should_execute_sql <<-EOS
|
101
|
+
CREATE UNIQUE INDEX pfk_pets_catid_name_color ON pets (cat_id, name, color)
|
102
|
+
EOS
|
89
103
|
end
|
90
104
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
105
|
+
it 'adds an on update constraint' do
|
106
|
+
add_polymorphic_constraints(
|
107
|
+
'pets',
|
108
|
+
{ cat_id: 'cats.id' },
|
109
|
+
on_update: :cascade
|
110
|
+
)
|
111
|
+
should_execute_sql <<-EOS
|
112
|
+
ALTER TABLE `pets` ADD CONSTRAINT `pets_cat_id_fk`
|
113
|
+
FOREIGN KEY (`cat_id`)
|
114
|
+
REFERENCES `cats`(id)
|
115
|
+
ON UPDATE CASCADE
|
116
|
+
EOS
|
117
|
+
end
|
102
118
|
|
103
|
-
|
119
|
+
it 'raises an error when on_update has invalid arguments' do
|
120
|
+
expect do
|
121
|
+
add_polymorphic_constraints(
|
122
|
+
'pets',
|
123
|
+
{ cat_id: 'cats.id' },
|
124
|
+
on_update: :invalid
|
125
|
+
)
|
126
|
+
end.to raise_error ArgumentError
|
104
127
|
end
|
105
128
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
129
|
+
it 'adds an on delete constraint' do
|
130
|
+
add_polymorphic_constraints(
|
131
|
+
'pets',
|
132
|
+
{ cat_id: 'cats.id' },
|
133
|
+
on_delete: :cascade
|
134
|
+
)
|
135
|
+
should_execute_sql <<-EOS
|
136
|
+
ALTER TABLE `pets` ADD CONSTRAINT `pets_cat_id_fk`
|
137
|
+
FOREIGN KEY (`cat_id`)
|
138
|
+
REFERENCES `cats`(id)
|
139
|
+
ON DELETE CASCADE
|
140
|
+
EOS
|
141
|
+
end
|
113
142
|
|
114
|
-
|
143
|
+
it 'raises an error when on_delete has invalid arguments' do
|
144
|
+
expect do
|
145
|
+
add_polymorphic_constraints(
|
146
|
+
'pets',
|
147
|
+
{ cat_id: 'cats.id' },
|
148
|
+
on_update: :invalid
|
149
|
+
)
|
150
|
+
end.to raise_error ArgumentError
|
115
151
|
end
|
116
152
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
%{ ALTER TABLE `pets` ADD CONSTRAINT `pets_dog_id_fk` FOREIGN KEY (`dog_id`) REFERENCES `dogs`(id) ON DELETE RESTRICT ON UPDATE CASCADE
|
122
|
-
ALTER TABLE `pets` ADD CONSTRAINT `pets_kitty_id_fk` FOREIGN KEY (`kitty_id`) REFERENCES `cats`(name) ON DELETE RESTRICT ON UPDATE CASCADE }
|
153
|
+
it 'truncates long trigger names to 64 characters' do
|
154
|
+
create_table(:pets) do |t|
|
155
|
+
t.references long_column1
|
156
|
+
t.references long_column2
|
123
157
|
end
|
124
158
|
|
125
|
-
|
159
|
+
add_polymorphic_constraints(
|
160
|
+
'pets',
|
161
|
+
{ "#{long_column1}_id" => 'cats.id', "#{long_column2}_id" => 'dogs.id' }
|
162
|
+
)
|
163
|
+
|
164
|
+
should_execute_sql <<-EOS
|
165
|
+
DROP TRIGGER IF EXISTS pfki_pets_xxxxxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyyyy
|
166
|
+
DROP TRIGGER IF EXISTS pfku_pets_xxxxxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyyyy
|
167
|
+
CREATE TRIGGER pfki_pets_xxxxxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyyyy BEFORE INSERT ON pets
|
168
|
+
FOR EACH ROW
|
169
|
+
BEGIN
|
170
|
+
IF(IF(NEW.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_id IS NULL, 0, 1) + IF(NEW.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy_id IS NULL, 0, 1)) <> 1 THEN
|
171
|
+
SET NEW = 'Error';
|
172
|
+
END IF;
|
173
|
+
END
|
174
|
+
CREATE TRIGGER pfku_pets_xxxxxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyyyy BEFORE UPDATE ON pets
|
175
|
+
FOR EACH ROW
|
176
|
+
BEGIN
|
177
|
+
IF(IF(NEW.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_id IS NULL, 0, 1) + IF(NEW.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy_id IS NULL, 0, 1)) <> 1 THEN
|
178
|
+
SET NEW = 'Error';
|
179
|
+
END IF;
|
180
|
+
END
|
181
|
+
EOS
|
126
182
|
end
|
183
|
+
end
|
127
184
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
185
|
+
describe '#remove_polymorphic_constraints' do
|
186
|
+
it 'removes triggers and foreign keys with no uniqueness constraints' do
|
187
|
+
add_polymorphic_constraints(
|
188
|
+
'pets',
|
189
|
+
{ cat_id: 'cats.id', dog_id: 'dogs.id' },
|
190
|
+
unique: true
|
191
|
+
)
|
192
|
+
clear_sql_history
|
135
193
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
194
|
+
remove_polymorphic_constraints(
|
195
|
+
'pets',
|
196
|
+
{ cat_id: 'cats.id', dog_id: 'dogs.id' }
|
197
|
+
)
|
198
|
+
should_execute_sql <<-EOS
|
199
|
+
DROP TRIGGER IF EXISTS pfki_pets_catid_dogid
|
200
|
+
DROP TRIGGER IF EXISTS pfku_pets_catid_dogid
|
201
|
+
EOS
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'removes uniqueness index specified with true' do
|
205
|
+
add_polymorphic_constraints(
|
206
|
+
'pets',
|
207
|
+
{ cat_id: 'cats.id', dog_id: 'dogs.id' },
|
208
|
+
unique: true
|
209
|
+
)
|
210
|
+
clear_sql_history
|
141
211
|
|
142
|
-
|
143
|
-
|
212
|
+
remove_polymorphic_constraints(
|
213
|
+
'pets',
|
214
|
+
{ cat_id: 'cats.id', dog_id: 'dogs.id' },
|
215
|
+
unique: true
|
216
|
+
)
|
217
|
+
should_execute_sql <<-EOS
|
218
|
+
DROP INDEX pfk_pets_catid ON pets
|
219
|
+
DROP INDEX pfk_pets_dogid ON pets
|
220
|
+
EOS
|
144
221
|
end
|
145
222
|
|
146
|
-
|
147
|
-
|
223
|
+
it 'removes uniqueness index specified with a string' do
|
224
|
+
add_polymorphic_constraints(
|
225
|
+
'pets',
|
226
|
+
{ cat_id: 'cats.id', dog_id: 'dogs.id' },
|
227
|
+
unique: 'name'
|
228
|
+
)
|
229
|
+
clear_sql_history
|
230
|
+
|
231
|
+
remove_polymorphic_constraints(
|
232
|
+
'pets',
|
233
|
+
{ cat_id: 'cats.id', dog_id: 'dogs.id' },
|
234
|
+
unique: 'name'
|
235
|
+
)
|
148
236
|
|
149
|
-
it_behaves_like "mysql2 migration statements"
|
150
237
|
end
|
151
|
-
end
|
152
238
|
|
153
|
-
|
154
|
-
|
155
|
-
|
239
|
+
it 'removes uniqueness index specified with an array' do
|
240
|
+
add_polymorphic_constraints(
|
241
|
+
'pets',
|
242
|
+
{ cat_id: 'cats.id', dog_id: 'dogs.id' },
|
243
|
+
unique: [:name, :color]
|
244
|
+
)
|
245
|
+
clear_sql_history
|
246
|
+
|
247
|
+
remove_polymorphic_constraints(
|
248
|
+
'pets',
|
249
|
+
{ cat_id: 'cats.id', dog_id: 'dogs.id' },
|
250
|
+
unique: [:name, :color]
|
251
|
+
)
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'removes an on update constraint' do
|
256
|
+
add_polymorphic_constraints(
|
257
|
+
'pets',
|
258
|
+
{ cat_id: 'cats.id', dog_id: 'dogs.id' },
|
259
|
+
on_update: :cascade
|
260
|
+
)
|
261
|
+
clear_sql_history
|
262
|
+
|
263
|
+
remove_polymorphic_constraints(
|
264
|
+
'pets',
|
265
|
+
{ cat_id: 'cats.id', dog_id: 'dogs.id' },
|
266
|
+
on_update: :cascade
|
267
|
+
)
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
it 'removes an on delete constraint' do
|
272
|
+
add_polymorphic_constraints(
|
273
|
+
'pets',
|
274
|
+
{ cat_id: 'cats.id', dog_id: 'dogs.id' },
|
275
|
+
on_delete: :cascade
|
276
|
+
)
|
277
|
+
clear_sql_history
|
278
|
+
|
279
|
+
remove_polymorphic_constraints(
|
280
|
+
'pets',
|
281
|
+
{ cat_id: 'cats.id', dog_id: 'dogs.id' },
|
282
|
+
on_update: :cascade
|
283
|
+
)
|
156
284
|
|
157
|
-
before do
|
158
|
-
connection.stub_sql('show triggers', [:trigger1, :trigger2])
|
159
|
-
Trigger.stub(:new).with(:trigger1).and_return(trigger1)
|
160
|
-
Trigger.stub(:new).with(:trigger2).and_return(trigger2)
|
161
285
|
end
|
162
286
|
|
163
|
-
|
164
|
-
|
287
|
+
it 'truncates long trigger names to 64 characters' do
|
288
|
+
create_table(:pets) do |t|
|
289
|
+
t.references long_column1
|
290
|
+
t.references long_column2
|
291
|
+
end
|
292
|
+
args = [
|
293
|
+
'pets',
|
294
|
+
{ "#{long_column1}_id" => 'cats.id', "#{long_column2}_id" => 'dogs.id' }
|
295
|
+
]
|
296
|
+
add_polymorphic_constraints(*args)
|
297
|
+
clear_sql_history
|
298
|
+
remove_polymorphic_constraints(*args)
|
299
|
+
should_execute_sql <<-EOS
|
300
|
+
DROP TRIGGER IF EXISTS pfki_pets_xxxxxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyyyy
|
301
|
+
DROP TRIGGER IF EXISTS pfku_pets_xxxxxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyyyy
|
302
|
+
EOS
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
describe "#triggers" do
|
307
|
+
it 'returns the triggers for the current schema' do
|
308
|
+
add_polymorphic_constraints(
|
309
|
+
'pets',
|
310
|
+
{ cat_id: 'cats.id', dog_id: 'dogs.id' }
|
311
|
+
)
|
312
|
+
expect(triggers.map(&:name)).to eq(
|
313
|
+
['pfki_pets_catid_dogid', 'pfku_pets_catid_dogid']
|
314
|
+
)
|
165
315
|
end
|
166
316
|
end
|
167
317
|
end
|
data/spec/schema_dumper_spec.rb
CHANGED
@@ -1,38 +1,32 @@
|
|
1
|
-
require 'active_record'
|
2
1
|
require 'spec_helper'
|
3
|
-
require 'sql_logger'
|
4
|
-
require 'polymorpheus'
|
5
|
-
require 'polymorpheus/trigger'
|
6
|
-
require 'stringio'
|
7
|
-
|
8
|
-
# this is normally done via a Railtie in non-testing situations
|
9
|
-
ActiveRecord::SchemaDumper.class_eval { include Polymorpheus::SchemaDumper }
|
10
2
|
|
11
3
|
describe Polymorpheus::SchemaDumper do
|
12
|
-
|
13
4
|
let(:connection) { ActiveRecord::Base.connection }
|
14
5
|
let(:stream) { StringIO.new }
|
15
6
|
|
16
7
|
before do
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
"utf8_unicode_ci"])]
|
8
|
+
create_table :story_arcs do |t|
|
9
|
+
t.references :hero
|
10
|
+
t.references :villain
|
11
|
+
end
|
12
|
+
create_table :heros
|
13
|
+
create_table :villains
|
14
|
+
ActiveRecord::Base.connection.add_polymorphic_constraints(
|
15
|
+
'story_arcs',
|
16
|
+
{ hero_id: 'heros.id', villain_id: 'villains.id' }
|
27
17
|
)
|
28
18
|
|
29
19
|
ActiveRecord::SchemaDumper.dump(connection, stream)
|
30
20
|
end
|
31
21
|
|
22
|
+
after do
|
23
|
+
drop_table :story_arcs # drop first, due to the foreign key
|
24
|
+
end
|
25
|
+
|
32
26
|
subject { stream.string }
|
33
27
|
|
34
28
|
let(:schema_statement) do
|
35
|
-
%{ add_polymorphic_triggers(:
|
29
|
+
%{ add_polymorphic_triggers(:story_arcs, ["hero_id", "villain_id"])}
|
36
30
|
end
|
37
31
|
|
38
32
|
specify "the schema statement is part of the dump" do
|
@@ -42,5 +36,4 @@ describe Polymorpheus::SchemaDumper do
|
|
42
36
|
specify "there is exactly one instance of the schema statement" do
|
43
37
|
subject.index(schema_statement).should == subject.rindex(schema_statement)
|
44
38
|
end
|
45
|
-
|
46
39
|
end
|