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.
@@ -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
- # Setup
14
- #######################################################
15
-
16
- before(:all) do
17
- class << ActiveRecord::Base.connection
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
- after(:all) do
25
- class << ActiveRecord::Base.connection
26
- alias_method :execute, :original_execute
27
- end
28
- end
23
+ create_table(:cats)
24
+ create_table(:dogs)
29
25
 
30
- let(:connection) { ActiveRecord::Base.connection }
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
- before do
45
- connection.clear_sql_history
46
- subject
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 "migration statements" do
54
- context "basic case with no uniqueness constraints" do
55
- include_context "columns with short names"
56
- let(:options) { {} }
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
- it_behaves_like "mysql2 migration statements"
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
- context "when uniqueness constraint is specified as true" do
62
- include_context "columns with short names"
63
- let(:options) { { :unique => true } }
64
- let(:unique_key_sql) do
65
- %{ CREATE UNIQUE INDEX pfk_pets_dogid ON pets (dog_id)
66
- CREATE UNIQUE INDEX pfk_pets_kittyid ON pets (kitty_id) }
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
- it_behaves_like "mysql2 migration statements"
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
- context "specifying uniqueness constraint as a string" do
77
- include_context "columns with short names"
78
- let(:options) { { :unique => 'field1' } }
79
- let(:unique_key_sql) do
80
- %{ CREATE UNIQUE INDEX pfk_pets_dogid_field1 ON pets (dog_id, field1)
81
- CREATE UNIQUE INDEX pfk_pets_kittyid_field1 ON pets (kitty_id, field1) }
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
- it_behaves_like "mysql2 migration statements"
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
- context "specifying uniqueness constraint as an array" do
92
- include_context "columns with short names"
93
- let(:options) { { :unique => [:foo, :bar] } }
94
- let(:unique_key_sql) do
95
- %{ CREATE UNIQUE INDEX pfk_pets_dogid_foo_bar ON pets (dog_id, foo, bar)
96
- CREATE UNIQUE INDEX pfk_pets_kittyid_foo_bar ON pets (kitty_id, foo, bar) }
97
- end
98
- let(:remove_indices_sql) do
99
- %{ DROP INDEX pfk_pets_kittyid_foo_bar ON pets
100
- DROP INDEX pfk_pets_dogid_foo_bar ON pets }
101
- end
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
- it_behaves_like "mysql2 migration statements"
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
- context "specifying an on update constraint" do
107
- include_context "columns with short names"
108
- let(:options) { { :on_update => :cascade } }
109
- let(:fkey_sql) do
110
- %{ ALTER TABLE `pets` ADD CONSTRAINT `pets_dog_id_fk` FOREIGN KEY (`dog_id`) REFERENCES `dogs`(id) ON UPDATE CASCADE
111
- ALTER TABLE `pets` ADD CONSTRAINT `pets_kitty_id_fk` FOREIGN KEY (`kitty_id`) REFERENCES `cats`(name) ON UPDATE CASCADE }
112
- end
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
- it_behaves_like "mysql2 migration statements"
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
- context "specifying on delete and on update constraints" do
118
- include_context "columns with short names"
119
- let(:options) { { :on_update => :cascade, :on_delete => :restrict } }
120
- let(:fkey_sql) do
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
- it_behaves_like "mysql2 migration statements"
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
- context "when on_delete and on_update have invalid arguments" do
129
- include_context "columns with short names"
130
- let(:options) { { :on_update => :invalid, :on_delete => nil } }
131
- let(:fkey_sql) do
132
- %{ ALTER TABLE `pets` ADD CONSTRAINT `pets_dog_id_fk` FOREIGN KEY (`dog_id`) REFERENCES `dogs`(id)
133
- ALTER TABLE `pets` ADD CONSTRAINT `pets_kitty_id_fk` FOREIGN KEY (`kitty_id`) REFERENCES `cats`(name) }
134
- end
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
- it "#add_polymorphic_constraints raises an argument error" do
137
- expect do
138
- connection.add_polymorphic_constraints(table, columns, options)
139
- end.to raise_error ArgumentError
140
- end
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
- it_behaves_like 'mysql2 add sql for polymorphic triggers'
143
- it_behaves_like 'mysql2 remove sql for polymorphic constraints'
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
- context "when table and column names combined are very long" do
147
- include_context "columns with long names"
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
- describe "#triggers" do
154
- let(:trigger1) { double(Trigger, :name => '1') }
155
- let(:trigger2) { double(Trigger, :name => '2') }
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
- specify do
164
- connection.triggers.should == [trigger1, trigger2]
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
@@ -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
- # pretend like we have a trigger defined
18
- connection.stub(:triggers).and_return(
19
- [Trigger.new(["trigger_name", "INSERT", "pets",
20
- %{BEGIN
21
- IF(IF(NEW.dog_id IS NULL, 0, 1) + IF(NEW.kitty_id IS NULL, 0, 1)) <> 1 THEN
22
- SET NEW = 'Error';
23
- END IF;
24
- END},
25
- "BEFORE", nil, "", "production@%", "utf8", "utf8_general_ci",
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(:pets, ["dog_id", "kitty_id"])}
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