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.
- checksums.yaml +7 -0
- data/.gitignore +39 -0
- data/.rspec +5 -0
- data/.rubocop.yml +468 -0
- data/Gemfile +70 -0
- data/LICENSE +20 -0
- data/README.rdoc +58 -0
- data/Rakefile +4 -0
- data/dm-constraints.gemspec +21 -0
- data/lib/data_mapper/constraints/adapters/abstract_adapter.rb +33 -0
- data/lib/data_mapper/constraints/adapters/do_adapter.rb +192 -0
- data/lib/data_mapper/constraints/adapters/extension.rb +45 -0
- data/lib/data_mapper/constraints/adapters/mysql_adapter.rb +26 -0
- data/lib/data_mapper/constraints/adapters/oracle_adapter.rb +53 -0
- data/lib/data_mapper/constraints/adapters/postgres_adapter.rb +13 -0
- data/lib/data_mapper/constraints/adapters/sqlite_adapter.rb +22 -0
- data/lib/data_mapper/constraints/adapters/sqlserver_adapter.rb +11 -0
- data/lib/data_mapper/constraints/migrations/model.rb +34 -0
- data/lib/data_mapper/constraints/migrations/relationship.rb +41 -0
- data/lib/data_mapper/constraints/migrations/singleton_methods.rb +44 -0
- data/lib/data_mapper/constraints/relationship/many_to_many.rb +48 -0
- data/lib/data_mapper/constraints/relationship/one_to_many.rb +79 -0
- data/lib/data_mapper/constraints/resource.rb +30 -0
- data/lib/data_mapper/constraints/version.rb +5 -0
- data/lib/dm-constraints.rb +19 -0
- data/spec/integration/constraints_spec.rb +630 -0
- data/spec/isolated/require_after_setup_spec.rb +36 -0
- data/spec/isolated/require_before_setup_spec.rb +35 -0
- data/spec/isolated/require_spec.rb +15 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec_helper.rb +26 -0
- data/tasks/spec.rake +21 -0
- data/tasks/yard.rake +9 -0
- data/tasks/yardstick.rake +19 -0
- 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
|