sbf-dm-constraints 1.3.0.beta

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.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +39 -0
  3. data/.rspec +5 -0
  4. data/.rubocop.yml +468 -0
  5. data/Gemfile +70 -0
  6. data/LICENSE +20 -0
  7. data/README.rdoc +58 -0
  8. data/Rakefile +4 -0
  9. data/dm-constraints.gemspec +21 -0
  10. data/lib/data_mapper/constraints/adapters/abstract_adapter.rb +33 -0
  11. data/lib/data_mapper/constraints/adapters/do_adapter.rb +192 -0
  12. data/lib/data_mapper/constraints/adapters/extension.rb +45 -0
  13. data/lib/data_mapper/constraints/adapters/mysql_adapter.rb +26 -0
  14. data/lib/data_mapper/constraints/adapters/oracle_adapter.rb +53 -0
  15. data/lib/data_mapper/constraints/adapters/postgres_adapter.rb +13 -0
  16. data/lib/data_mapper/constraints/adapters/sqlite_adapter.rb +22 -0
  17. data/lib/data_mapper/constraints/adapters/sqlserver_adapter.rb +11 -0
  18. data/lib/data_mapper/constraints/migrations/model.rb +34 -0
  19. data/lib/data_mapper/constraints/migrations/relationship.rb +41 -0
  20. data/lib/data_mapper/constraints/migrations/singleton_methods.rb +44 -0
  21. data/lib/data_mapper/constraints/relationship/many_to_many.rb +48 -0
  22. data/lib/data_mapper/constraints/relationship/one_to_many.rb +79 -0
  23. data/lib/data_mapper/constraints/resource.rb +30 -0
  24. data/lib/data_mapper/constraints/version.rb +5 -0
  25. data/lib/dm-constraints.rb +19 -0
  26. data/spec/integration/constraints_spec.rb +630 -0
  27. data/spec/isolated/require_after_setup_spec.rb +36 -0
  28. data/spec/isolated/require_before_setup_spec.rb +35 -0
  29. data/spec/isolated/require_spec.rb +15 -0
  30. data/spec/rcov.opts +6 -0
  31. data/spec/spec_helper.rb +26 -0
  32. data/tasks/spec.rake +21 -0
  33. data/tasks/yard.rake +9 -0
  34. data/tasks/yardstick.rake +19 -0
  35. metadata +94 -0
@@ -0,0 +1,19 @@
1
+ require 'dm-core'
2
+
3
+ require 'data_mapper/constraints/resource'
4
+
5
+ require 'data_mapper/constraints/migrations/model'
6
+ require 'data_mapper/constraints/migrations/relationship'
7
+ require 'data_mapper/constraints/migrations/singleton_methods'
8
+
9
+ require 'data_mapper/constraints/relationship/one_to_many'
10
+ require 'data_mapper/constraints/relationship/many_to_many'
11
+
12
+ require 'data_mapper/constraints/adapters/extension'
13
+ require 'data_mapper/constraints/adapters/abstract_adapter'
14
+
15
+ module DataMapper
16
+ module Constraints
17
+ VALID_CONSTRAINT_VALUES = %i(protect destroy destroy! set_nil skip).to_set.freeze
18
+ end
19
+ end
@@ -0,0 +1,630 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe 'DataMapper::Constraints', "(with #{DataMapper::Spec.adapter_name})" do
4
+ supported_by :all do
5
+ before :all do
6
+ @in_memory = defined?(DataMapper::Adapters::InMemoryAdapter) && @adapter.kind_of?(DataMapper::Adapters::InMemoryAdapter)
7
+ @yaml = defined?(DataMapper::Adapters::YamlAdapter) && @adapter.kind_of?(DataMapper::Adapters::YamlAdapter)
8
+
9
+ @skip = @in_memory || @yaml
10
+ end
11
+
12
+ before :all do
13
+ class ::Article
14
+ include DataMapper::Resource
15
+
16
+ property :id, Serial
17
+ property :title, String, :required => true
18
+ property :content, Text
19
+
20
+ has 1, :revision
21
+ has n, :comments
22
+ has n, :authors, :through => Resource
23
+ end
24
+
25
+ class ::Author
26
+ include DataMapper::Resource
27
+
28
+ property :first_name, String, :key => true
29
+ property :last_name, String, :key => true
30
+
31
+ has n, :comments
32
+ has n, :articles, :through => Resource
33
+ end
34
+
35
+ class ::Comment
36
+ include DataMapper::Resource
37
+
38
+ property :id, Serial
39
+ property :body, Text
40
+
41
+ belongs_to :article
42
+ belongs_to :author
43
+ end
44
+
45
+ # Used to test a belongs_to association with no has() association
46
+ # on the other end
47
+ class ::Revision
48
+ include DataMapper::Resource
49
+
50
+ property :id, Serial
51
+ property :text, String
52
+
53
+ belongs_to :article
54
+ end
55
+ end
56
+
57
+ describe 'create related objects' do
58
+ before :all do
59
+ class ::Comment
60
+ belongs_to :article, :required => false
61
+ belongs_to :author, :required => false
62
+ end
63
+
64
+ class ::Revision
65
+ belongs_to :article, :required => false
66
+ end
67
+ end
68
+
69
+ it 'is able to create related objects with a foreign key constraint' do
70
+ @article = Article.create(:title => 'Man on the Moon')
71
+ @comment = @article.comments.create(:body => 'So true!')
72
+ end
73
+
74
+ it 'is able to create related objects with a composite foreign key constraint' do
75
+ @author = Author.create(:first_name => 'John', :last_name => 'Doe')
76
+ @comment = @author.comments.create(:body => 'So true!')
77
+ end
78
+
79
+ supported_by :postgres, :mysql do
80
+ it 'is not be able to create related objects with a failing foreign key constraint' do
81
+ jruby = defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
82
+ pending 'JRuby throws a DataObjects::SQLError for integrity errors, which is wrong' if jruby
83
+
84
+ article = Article.create(:title => 'Man on the Moon')
85
+ expect {
86
+ Comment.create(:body => 'So true!', :article_id => article.id + 1)
87
+ }.to raise_error(DataObjects::IntegrityError)
88
+ end
89
+ end
90
+ end
91
+
92
+ describe 'belongs_to without matching has association' do
93
+ before do
94
+ @article = Article.create(:title => 'Man on the Moon')
95
+ @other_article = Article.create(:title => 'Dolly cloned')
96
+ @revision = Revision.create(:text => 'Riveting!', :article => @other_article)
97
+ end
98
+
99
+ it 'destroys the parent if there are no children in the association' do
100
+ expect(@article.destroy).to be(true)
101
+ expect(@article.model.get(*@article.key)).to be_nil
102
+ end
103
+
104
+ it 'the child is destroyable' do
105
+ expect(@revision.destroy).to be(true)
106
+ expect(@revision.model.get(*@revision.key)).to be_nil
107
+ end
108
+ end
109
+
110
+ describe 'constraint options' do
111
+ describe 'when no constraint options are given' do
112
+ before do
113
+ @article = Article.create(:title => 'Man on the Moon')
114
+ @author = Author.create(:first_name => 'John', :last_name => 'Doe')
115
+ @other_author = Author.create(:first_name => 'Joe', :last_name => 'Smith')
116
+ @comment = @other_author.comments.create(:body => 'So true!', :article => @article)
117
+ end
118
+
119
+ it 'destroys the parent if there are no children in the association' do
120
+ expect(@author.destroy).to be(true)
121
+ expect(@author.model.get(*@author.key)).to be_nil
122
+ end
123
+
124
+ it 'does not destroy the parent if there are children in the association' do
125
+ expect(@other_author.destroy).to be(false)
126
+ expect(@other_author.model.get(*@other_author.key)).not_to be_nil
127
+ end
128
+ end
129
+
130
+ describe 'when :constraint => :protect is given' do
131
+ before :all do
132
+ class ::Article
133
+ has 1, :revision, :constraint => :protect
134
+ has n, :comments, :constraint => :protect
135
+ has n, :authors, :constraint => :protect, :through => Resource
136
+ end
137
+
138
+ class ::Author
139
+ has n, :comments, :constraint => :protect
140
+ has n, :articles, :constraint => :protect, :through => Resource
141
+ end
142
+
143
+ class ::Comment
144
+ belongs_to :article
145
+ belongs_to :author
146
+ end
147
+
148
+ class ::Revision
149
+ belongs_to :article
150
+ end
151
+ end
152
+
153
+ describe 'one-to-one associations' do
154
+ before do
155
+ @article = Article.create(:title => 'Man on the Moon')
156
+ @revision = Revision.create(:text => 'Riveting!', :article => @article)
157
+ end
158
+
159
+ it 'does not destroy the parent if there are children in the association' do
160
+ expect(@article.destroy).to be(false)
161
+ expect(@article.model.get(*@article.key)).not_to be_nil
162
+ end
163
+
164
+ it 'the child is destroyable' do
165
+ expect(@revision.destroy).to be(true)
166
+ expect(@revision.model.get(*@revision.key)).to be_nil
167
+ end
168
+ end
169
+
170
+ describe 'one-to-many associations' do
171
+ before do
172
+ @article = Article.create(:title => 'Man on the Moon')
173
+ @author = Author.create(:first_name => 'John', :last_name => 'Doe')
174
+ @another_author = Author.create(:first_name => 'Joe', :last_name => 'Smith')
175
+ @comment = @another_author.comments.create(:body => 'So true!', :article => @article)
176
+ end
177
+
178
+ it 'destroys the parent if there are no children in the association' do
179
+ expect(@author.destroy).to be(true)
180
+ expect(@author.model.get(*@author.key)).to be_nil
181
+ end
182
+
183
+ it 'does not destroy the parent if there are children in the association' do
184
+ expect(@another_author.destroy).to be(false)
185
+ end
186
+
187
+ it 'the child is destroyable' do
188
+ expect(@comment.destroy).to be(true)
189
+ expect(@comment.model.get(*@comment.key)).to be_nil
190
+ end
191
+ end
192
+
193
+ describe 'many-to-many associations' do
194
+ before do
195
+ pending 'The adapter does not support m:m associations yet' if @skip
196
+ end
197
+
198
+ before do
199
+ @author = Author.create(:first_name => 'John', :last_name => 'Doe')
200
+ @another_author = Author.create(:first_name => 'Joe', :last_name => 'Smith')
201
+ @article = Article.create(:title => 'Man on the Moon', :authors => [ @author ])
202
+ end
203
+
204
+ it 'destroys the parent if there are no children in the association' do
205
+ expect(@another_author.destroy).to be(true)
206
+ expect(@another_author.model.get(*@another_author.key)).to be_nil
207
+ end
208
+
209
+ it 'does not destroy the parent if there are children in the association' do
210
+ expect(@author.articles).not_to eq []
211
+ expect(@author.destroy).to be(false)
212
+ end
213
+
214
+ it 'the child is be destroyable' do
215
+ @article.authors.clear
216
+ expect(@article.save).to be(true)
217
+ expect(@article.authors).to be_empty
218
+ end
219
+ end
220
+ end
221
+
222
+ describe 'when :constraint => :destroy! is given' do
223
+ before :all do
224
+ class ::Article
225
+ has 1, :revision, :constraint => :destroy!
226
+ has n, :comments, :constraint => :destroy!
227
+ has n, :authors, :constraint => :destroy!, :through => Resource
228
+ end
229
+
230
+ class ::Author
231
+ has n, :comments, :constraint => :destroy!
232
+ has n, :articles, :constraint => :destroy!, :through => Resource
233
+ end
234
+
235
+ class ::Comment
236
+ belongs_to :article
237
+ belongs_to :author
238
+ end
239
+
240
+ class ::Revision
241
+ belongs_to :article
242
+ end
243
+ end
244
+
245
+ describe 'one-to-one associations' do
246
+ before do
247
+ @article = Article.create(:title => 'Man on the Moon')
248
+ @revision = Revision.create(:text => 'Riveting!', :article => @article)
249
+ end
250
+
251
+ it 'lets the parent to be destroyed' do
252
+ expect(@article.destroy).to be(true)
253
+ expect(@article.model.get(*@article.key)).to be_nil
254
+ end
255
+
256
+ it 'destroys the children' do
257
+ revision = @article.revision
258
+ expect(@article.destroy).to be(true)
259
+ expect(revision.model.get(*revision.key)).to be_nil
260
+ end
261
+
262
+ it 'the child is destroyable' do
263
+ expect(@revision.destroy).to be(true)
264
+ expect(@revision.model.get(*@revision.key)).to be_nil
265
+ end
266
+ end
267
+
268
+ describe 'one-to-many associations' do
269
+ before do
270
+ @article = Article.create(:title => 'Man on the Moon')
271
+ @author = Author.create(:first_name => 'John', :last_name => 'Doe')
272
+ @comment = @author.comments.create(:body => 'So true!', :article => @article)
273
+ @another_comment = @author.comments.create(:body => 'Nice comment', :article => @article)
274
+ end
275
+
276
+ it 'lets the parent to be destroyed' do
277
+ expect(@author.destroy).to be(true)
278
+ expect(@author.model.get(*@author.key)).to be_nil
279
+ end
280
+
281
+ it 'destroys the children' do
282
+ expect(@author.destroy).to be(true)
283
+ @author.comments.all? { |comment| expect(comment).to be_new }
284
+ end
285
+
286
+ it 'the child is destroyable' do
287
+ expect(@comment.destroy).to be(true)
288
+ expect(@comment.model.get(*@comment.key)).to be_nil
289
+ end
290
+ end
291
+
292
+ describe 'many-to-many associations' do
293
+ before do
294
+ pending 'The adapter does not support m:m associations yet' if @skip
295
+ end
296
+
297
+ before do
298
+ @article = Article.create(:title => 'Man on the Moon')
299
+ @other_article = Article.create(:title => 'Dolly cloned')
300
+ @author = Author.create(:first_name => 'John', :last_name => 'Doe', :articles => [ @article, @other_article ])
301
+ end
302
+
303
+ it 'lets the parent to be destroyed' do
304
+ expect(@author.destroy).to be(true)
305
+ expect(@author.model.get(*@author.key)).to be_nil
306
+ end
307
+
308
+ it 'destroys the children' do
309
+ expect(@author.destroy).to be(true)
310
+ expect(@article.model.get(*@article.key)).to be_nil
311
+ expect(@other_article.model.get(*@other_article.key)).to be_nil
312
+ end
313
+
314
+ it 'the child is destroyable' do
315
+ expect(@article.destroy).to be(true)
316
+ expect(@article.model.get(*@article.key)).to be_nil
317
+ end
318
+ end
319
+ end
320
+
321
+ describe 'when :constraint => :destroy is given' do
322
+ before :all do
323
+ class ::Article
324
+ has 1, :revision, :constraint => :destroy
325
+ has n, :comments, :constraint => :destroy
326
+ has n, :authors, :constraint => :destroy, :through => Resource
327
+ end
328
+
329
+ class ::Author
330
+ has n, :comments, :constraint => :destroy
331
+ has n, :articles, :constraint => :destroy, :through => Resource
332
+ end
333
+
334
+ class ::Comment
335
+ belongs_to :article
336
+ belongs_to :author
337
+ end
338
+
339
+ class ::Revision
340
+ belongs_to :article
341
+ end
342
+ end
343
+
344
+ describe 'one-to-one associations' do
345
+ before do
346
+ @article = Article.create(:title => 'Man on the Moon')
347
+ @revision = Revision.create(:text => 'Riveting!', :article => @article)
348
+ end
349
+
350
+ it 'lets the parent to be destroyed' do
351
+ expect(@article.destroy).to be(true)
352
+ expect(@article.model.get(*@article.key)).to be_nil
353
+ end
354
+
355
+ it 'destroys the children' do
356
+ revision = @article.revision
357
+ expect(@article.destroy).to be(true)
358
+ expect(revision.model.get(*revision.key)).to be_nil
359
+ end
360
+
361
+ it 'the child is destroyable' do
362
+ expect(@revision.destroy).to be(true)
363
+ expect(@revision.model.get(*@revision.key)).to be_nil
364
+ end
365
+ end
366
+
367
+ describe 'one-to-many associations' do
368
+ before do
369
+ @article = Article.create(:title => 'Man on the Moon')
370
+ @author = Author.create(:first_name => 'John', :last_name => 'Doe')
371
+ @comment = @author.comments.create(:body => 'So true!', :article => @article)
372
+ @other_comment = @author.comments.create(:body => "That's nonsense", :article => @article)
373
+ end
374
+
375
+ it 'lets the parent to be destroyed' do
376
+ expect(@author.destroy).to be(true)
377
+ expect(@author.model.get(*@author.key)).to be_nil
378
+ end
379
+
380
+ it 'destroys the children' do
381
+ expect(@author.destroy).to be(true)
382
+ @author.comments.all? { |comment| expect(comment).to be_new }
383
+ end
384
+
385
+ it 'the child is destroyable' do
386
+ expect(@comment.destroy).to be(true)
387
+ expect(@comment.model.get(*@comment.key)).to be_nil
388
+ end
389
+ end
390
+
391
+ describe 'many-to-many associations' do
392
+ before do
393
+ pending 'The adapter does not support m:m associations yet' if @skip
394
+ end
395
+
396
+ before do
397
+ @article = Article.create(:title => 'Man on the Moon')
398
+ @other_article = Article.create(:title => 'Dolly cloned')
399
+ @author = Author.create(:first_name => 'John', :last_name => 'Doe', :articles => [ @article, @other_article ])
400
+ end
401
+
402
+ it 'destroys the parent and the children, too' do
403
+ expect(@author.destroy).to be(true)
404
+ expect(@author.model.get(*@author.key)).to be_nil
405
+
406
+ expect(@article.model.get(*@article.key)).to be_nil
407
+ expect(@other_article.model.get(*@other_article.key)).to be_nil
408
+ end
409
+
410
+ it 'the child is destroyable' do
411
+ expect(@article.destroy).to be(true)
412
+ expect(@article.model.get(*@article.key)).to be_nil
413
+ end
414
+ end
415
+ end
416
+
417
+ describe 'when :constraint => :set_nil is given' do
418
+ before :all do
419
+ # NOTE: M:M Relationships are not supported by :set_nil,
420
+ # see 'when checking constraint types' tests at bottom
421
+
422
+ class ::Article
423
+ has 1, :revision, :constraint => :set_nil
424
+ has n, :comments, :constraint => :set_nil
425
+ end
426
+
427
+ class ::Author
428
+ has n, :comments, :constraint => :set_nil
429
+ end
430
+
431
+ class ::Comment
432
+ belongs_to :article, :required => false
433
+ belongs_to :author, :required => false
434
+ end
435
+
436
+ class ::Revision
437
+ belongs_to :article, :required => false
438
+ end
439
+ end
440
+
441
+ describe 'one-to-one associations' do
442
+ before do
443
+ @article = Article.create(:title => 'Man on the Moon')
444
+ @revision = Revision.create(:text => 'Riveting!', :article => @article)
445
+ end
446
+
447
+ it 'lets the parent to be destroyed' do
448
+ expect(@article.destroy).to be(true)
449
+ expect(@article.model.get(*@article.key)).to be_nil
450
+ end
451
+
452
+ it "sets the child's foreign_key id to nil" do
453
+ revision = @article.revision
454
+ expect(@article.destroy).to be(true)
455
+ expect(revision.article).to be_nil
456
+ expect(revision.model.get(*revision.key).article).to be_nil
457
+ end
458
+
459
+ it 'the child is destroyable' do
460
+ expect(@revision.destroy).to be(true)
461
+ expect(@revision.model.get(*@revision.key)).to be_nil
462
+ end
463
+ end
464
+
465
+ describe 'one-to-many associations' do
466
+ before do
467
+ @author = Author.create(:first_name => 'John', :last_name => 'Doe')
468
+ @comment = @author.comments.create(:body => 'So true!')
469
+ @other_comment = @author.comments.create(:body => "That's nonsense")
470
+ end
471
+
472
+ it 'lets the parent be destroyed' do
473
+ expect(@author.destroy).to be(true)
474
+ expect(@author.model.get(*@author.key)).to be_nil
475
+ end
476
+
477
+ it 'sets the foreign_key ids of children to nil' do
478
+ expect(@author.destroy).to be(true)
479
+ @author.comments.all? { |comment| expect(comment.author).to be_nil }
480
+ end
481
+
482
+ it 'the children are destroyable' do
483
+ expect(@comment.destroy).to be(true)
484
+ expect(@comment.model.get(*@comment.key)).to be_nil
485
+
486
+ expect(@other_comment.destroy).to be(true)
487
+ expect(@other_comment.model.get(*@other_comment.key)).to be_nil
488
+ end
489
+ end
490
+ end
491
+
492
+ describe 'when :constraint => :skip is given' do
493
+ before :all do
494
+ class ::Article
495
+ has 1, :revision, :constraint => :skip
496
+ has n, :comments, :constraint => :skip
497
+ has n, :authors, :constraint => :skip, :through => Resource
498
+ end
499
+
500
+ class ::Author
501
+ has n, :comments, :constraint => :skip
502
+ has n, :articles, :constraint => :skip, :through => Resource
503
+ end
504
+
505
+ class ::Comment
506
+ belongs_to :article
507
+ belongs_to :author
508
+ end
509
+
510
+ class ::Revision
511
+ belongs_to :article
512
+ end
513
+ end
514
+
515
+ describe 'one-to-one associations' do
516
+ before do
517
+ @article = Article.create(:title => 'Man on the Moon')
518
+ @revision = Revision.create(:text => 'Riveting!', :article => @article)
519
+ end
520
+
521
+ it 'lets the parent be destroyed' do
522
+ expect(@article.destroy).to be(true)
523
+ expect(@article.model.get(*@article.key)).to be_nil
524
+ end
525
+
526
+ it 'lets the children become orphan records' do
527
+ expect(@article.destroy).to be(true)
528
+ expect(@revision.model.get(*@revision.key).article).to be_nil
529
+ end
530
+
531
+ it 'the child is destroyable' do
532
+ expect(@revision.destroy).to be(true)
533
+ expect(@revision.model.get(*@revision.key)).to be_nil
534
+ end
535
+ end
536
+
537
+ describe 'one-to-many associations' do
538
+ before do
539
+ @article = Article.create(:title => 'Man on the Moon')
540
+ @author = Author.create(:first_name => 'John', :last_name => 'Doe')
541
+ @comment = @author.comments.create(:body => 'So true!', :article => @article)
542
+ @other_comment = @author.comments.create(:body => "That's nonsense", :article => @article)
543
+ end
544
+
545
+ it 'lets the parent be destroyed' do
546
+ expect(@author.destroy).to be(true)
547
+ expect(@author.model.get(*@author.key)).to be_nil
548
+ end
549
+
550
+ it 'lets the children become orphan records' do
551
+ expect(@author.destroy).to be(true)
552
+ expect(@comment.model.get(*@comment.key).author).to be_nil
553
+ expect(@other_comment.model.get(*@other_comment.key).author).to be_nil
554
+ end
555
+
556
+ it 'the children are destroyable' do
557
+ expect(@comment.destroy).to be(true)
558
+ expect(@other_comment.destroy).to be(true)
559
+ expect(@other_comment.model.get(*@other_comment.key)).to be_nil
560
+ end
561
+ end
562
+
563
+ describe 'many-to-many associations' do
564
+ before do
565
+ pending 'The adapter does not support m:m associations yet' if @skip
566
+ end
567
+
568
+ before do
569
+ @article = Article.create(:title => 'Man on the Moon')
570
+ @other_article = Article.create(:title => 'Dolly cloned')
571
+ @author = Author.create(:first_name => 'John', :last_name => 'Doe', :articles => [ @article, @other_article ])
572
+ end
573
+
574
+ it 'the children are be destroyable' do
575
+ expect(@article.destroy).to be(true)
576
+ expect(@article.model.get(*@article.key)).to be_nil
577
+ end
578
+ end
579
+ end
580
+
581
+ describe 'when checking constraint types' do
582
+ # M:M relationships results in a join table composed of composite (composed of two parts)
583
+ # primary key.
584
+ # Setting a portion of this primary key is not possible for two reasons:
585
+ # 1. the columns are defined as :required => true
586
+ # 2. there could be duplicate rows if more than one of either of the types
587
+ # was deleted while being associated to the same type on the other side of the relationship
588
+ # Given
589
+ # Author(name: John Doe, ID: 1) =>
590
+ # Articles[Article(title: Man on the Moon, ID: 1), Article(title: Dolly cloned, ID: 2)]
591
+ # Author(Name: James Duncan, ID: 2) =>
592
+ # Articles[Article(title: Man on the Moon, ID: 1), Article(title: The end is nigh, ID: 3)]
593
+ #
594
+ # Table authors_articles would look like (author_id, article_id)
595
+ # (1, 1)
596
+ # (1, 2)
597
+ # (2, 1)
598
+ # (2, 3)
599
+ #
600
+ # If both articles were deleted and the primary key was set to null
601
+ # (null, 1)
602
+ # (null, 2)
603
+ # (null, 1) # duplicate error!
604
+ # (null, 3)
605
+ #
606
+ # I would suggest setting :constraint to :skip in this scenario which will leave
607
+ # you with orphaned rows.
608
+ it 'raises an error if :set_nil is given for a M:M relationship' do
609
+ expect {
610
+ class ::Article
611
+ has n, :authors, :through => Resource, :constraint => :set_nil
612
+ end
613
+
614
+ class ::Author
615
+ has n, :articles, :through => Resource, :constraint => :set_nil
616
+ end
617
+ }.to raise_error(ArgumentError)
618
+ end
619
+
620
+ it 'raises an error if an unknown type is given' do
621
+ expect do
622
+ class ::Author
623
+ has n, :articles, :constraint => :chocolate
624
+ end
625
+ end.to raise_error(ArgumentError)
626
+ end
627
+ end
628
+ end
629
+ end
630
+ end
@@ -0,0 +1,36 @@
1
+ require 'rspec'
2
+
3
+ require_relative 'require_spec'
4
+ require 'dm-core/spec/setup'
5
+ require 'dm-core/spec/lib/adapter_helpers'
6
+
7
+ # To really test this behavior, this spec needs to be run in isolation and not
8
+ # as part of the typical rake spec run, which requires dm-transactions upfront
9
+
10
+ RSpec.configure do |config|
11
+ config.extend(DataMapper::Spec::Adapters::Helpers)
12
+ end
13
+
14
+ describe "require 'dm-constraints' after calling DataMapper.setup" do
15
+
16
+ before(:all) do
17
+
18
+ @adapter = DataMapper::Spec.adapter
19
+ require 'dm-constraints'
20
+
21
+ class ::Person
22
+ include DataMapper::Resource
23
+ property :id, Serial
24
+ has n, :tasks
25
+ end
26
+
27
+ class ::Task
28
+ include DataMapper::Resource
29
+ property :id, Serial
30
+ belongs_to :person
31
+ end
32
+
33
+ end
34
+
35
+ it_behaves_like "require 'dm-constraints'"
36
+ end