activefacts-api 1.8.1 → 1.8.3

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,577 +0,0 @@
1
- #
2
- # ActiveFacts tests: Constellation instances in the Runtime API
3
- # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
- #
5
-
6
- describe "A Constellation instance" do
7
- before :each do
8
- Object.send :remove_const, :Mod if Object.const_defined?("Mod")
9
- module Mod
10
- @base_types = [
11
- Int, Real, AutoCounter, String, Date, DateTime, Decimal, Guid
12
- ]
13
-
14
- # Create a value type and a subtype of that value type for each base type:
15
- @base_types.each do |base_type|
16
- Mod.module_eval <<-END
17
- class #{base_type.basename}Val < #{base_type.name}
18
- value_type
19
- end
20
-
21
- class #{base_type.basename}SubVal < #{base_type.name}Val
22
- # Note no new "value_type" is required here, it comes through inheritance
23
- end
24
- END
25
- end
26
-
27
- class Name < StringVal
28
- value_type
29
- #has_one :attr, Name
30
- has_one :undefined_role # This will be unsatisfied with the non-existence of the UndefinedRole class
31
- end
32
-
33
- class LegalEntity
34
- identified_by :name
35
- one_to_one :name, :mandatory => true
36
- end
37
-
38
- class Surrogate
39
- identified_by :auto_counter_val
40
- one_to_one :auto_counter_val
41
- end
42
-
43
- class Company < LegalEntity
44
- supertypes Surrogate
45
- end
46
-
47
- class Person < LegalEntity
48
- identified_by :name, :family_name # REVISIT: want a way to role_alias :name, :given_name
49
- supertypes :surrogate # Use a Symbol binding this time
50
-
51
- has_one :family_name, :class => Name
52
- has_one :employer, :class => Company
53
- one_to_one :birth_name, :class => Name
54
- end
55
- end
56
- @constellation = ActiveFacts::API::Constellation.new(Mod)
57
- end
58
-
59
- describe "Vocabulary" do
60
- it "should create the constellation" do
61
- Mod.constellation.should be_an_instance_of ActiveFacts::API::Constellation
62
- end
63
-
64
- it "should create the constellation by direct populate" do
65
- Mod.populate do
66
- Name "foo"
67
- end.should be_an_instance_of ActiveFacts::API::Constellation
68
- end
69
-
70
- it "should verbalise" do
71
- s = Mod.verbalise
72
- s.should be_an_instance_of String
73
- end
74
- end
75
-
76
- it "should allow creating a constellation" do
77
- @constellation = ActiveFacts::API::Constellation.new(Mod)
78
- end
79
-
80
- it "should complain when accessing a non-class as a method" do
81
- Mod::Foo = 23
82
- lambda { @constellation.Foo }.should raise_error(NoMethodError)
83
- end
84
-
85
- it "should complain when accessing a class that isn't an object type" do
86
- class Mod::Bar; end
87
- proc { @constellation.Bar }.should raise_error(NoMethodError)
88
- proc { @constellation.instances[Mod::Bar] }.should raise_error(ActiveFacts::API::InvalidObjectType)
89
- end
90
-
91
- it "should deny handling an object type defined outside the current module" do
92
- class ::Bar; end
93
- lambda { @constellation.Bar }.should raise_error(NoMethodError)
94
- lambda { @constellation.instances[Bar] }.should raise_error(ActiveFacts::API::InvalidObjectType)
95
- end
96
-
97
- it "should allow inspection" do
98
- lambda { @constellation.inspect }.should_not raise_error
99
- end
100
-
101
- it "should support fetching its vocabulary" do
102
- @constellation.vocabulary.should == Mod
103
- end
104
-
105
- # it "should support fetching its query" do
106
- # pending
107
- # @constellation.query.should == Mod
108
- # end
109
-
110
- it "should create methods to assert instances" do
111
- # Check that methods have not yet been created:
112
- @constellation.should_not respond_to(:Name)
113
- @constellation.should_not respond_to(:LegalEntity)
114
- @constellation.should_not respond_to(:Company)
115
- @constellation.should_not respond_to(:Person)
116
-
117
- # Assert instances
118
- name = foo = acme = fred_fly = nil
119
- lambda {
120
- name = @constellation.Name("foo")
121
- foo = @constellation.LegalEntity("foo")
122
- acme = @constellation.Company("Acme, Inc", :auto_counter_val => :new)
123
- fred_fly = @constellation.Person("fred", "fly", :auto_counter_val => :new)
124
- }.should_not raise_error
125
-
126
- # Check that methods have not yet been created:
127
- @constellation.should respond_to(:Name)
128
- @constellation.should respond_to(:LegalEntity)
129
- @constellation.should respond_to(:Company)
130
- @constellation.should respond_to(:Person)
131
-
132
- # Check the instances
133
- name.class.should == Mod::Name
134
- name.constellation.should == @constellation
135
-
136
- foo.class.should == Mod::LegalEntity
137
- foo.constellation.should == @constellation
138
- foo.inspect.should =~ / in Conste/
139
- foo.verbalise.should =~ /LegalEntity\(/
140
-
141
- acme.class.should == Mod::Company
142
- acme.constellation.should == @constellation
143
- acme.inspect.should =~ / in Conste/
144
- acme.verbalise.should =~ /Company\(/
145
-
146
- fred_fly.class.should == Mod::Person
147
- fred_fly.constellation.should == @constellation
148
- fred_fly.inspect.should =~ / in Conste/
149
- fred_fly.verbalise.should =~ /Person\(/
150
- end
151
-
152
- it "should re-use instances constructed the same way" do
153
- name1 = @constellation.Name("foo")
154
- foo1 = @constellation.LegalEntity("foo")
155
- acme1 = @constellation.Company("Acme, Inc", :auto_counter_val => :new)
156
- fred_fly1 = @constellation.Person("fred", "fly", :auto_counter_val => :new)
157
-
158
- name2 = @constellation.Name("foo")
159
- foo2 = @constellation.LegalEntity("foo")
160
- acme2 = @constellation.Company("Acme, Inc") # , :auto_counter_val => :new)
161
- fred_fly2 = @constellation.Person("fred", "fly") # , :auto_counter_val => :new)
162
-
163
- name1.object_id.should == name2.object_id
164
- foo1.object_id.should == foo2.object_id
165
- acme1.object_id.should == acme2.object_id
166
- fred_fly1.object_id.should == fred_fly2.object_id
167
- end
168
-
169
- describe "re-assertion with any one of multiple identifiers" do
170
- before :each do
171
- # Create some instances:
172
- @name1 = @constellation.Name("foo") # Value type
173
- @foo1 = @constellation.LegalEntity("foo") # Entity Type with simple identifier
174
- @acme1 = @constellation.Company("Acme, Inc", :auto_counter_val => :new)
175
- @acme1_id = @acme1.auto_counter_val
176
- end
177
-
178
- it "should be allowed with a normal value type id" do
179
- # Reassert the instances:
180
- @name2 = @constellation.Name("foo")
181
- @foo2 = @constellation.LegalEntity("foo")
182
- @acme2 = nil
183
- lambda {
184
- # Without the auto_counter_val
185
- @acme2 = @constellation.Company("Acme, Inc")
186
- }.should_not raise_error
187
-
188
- # This creates a new auto_counter_val, changing the acme instance (and hence, both references to it)
189
- @acme2.auto_counter_val = :new
190
- @acme2.should == @acme1
191
- @acme2.auto_counter_val.should_not be_defined
192
- @acme2.auto_counter_val.to_s.should_not == @acme1_id.to_s
193
- end
194
-
195
- it "should be allowed with an autocounter id" do
196
- acme3 = @constellation.Surrogate(@acme1_id)
197
- acme3.should == @acme1
198
- end
199
- end
200
-
201
- it "Should raise an exception with assigning a role whose referent (object type) has not yet been defined" do
202
- n = @constellation.Name("Fred")
203
- # This does not raise the "settable_roles_exception". I'm no longer sure how I did this, so I can't get coverage on this code :(
204
- proc { n.undefined_role = 'foo' }.should raise_error(NoMethodError)
205
- end
206
-
207
- # Maybe not complete yet
208
- describe "assigning additional arguments on asserting a value type" do
209
- before :each do
210
- # This should work, but...
211
- # birth_name = @constellation.Name("Smith", :person_as_birth_name => {:name => "Janet", :family_name => "Jones", :auto_counter_val => :new})
212
- # for now, use the following form
213
- @birth_name = @constellation.Name("Smith", :person_as_birth_name => ["Janet", "Jones", {:auto_counter_val => :new}])
214
- @person = @birth_name.person_as_birth_name
215
- end
216
-
217
- it "should create required instances" do
218
- @person.should_not be_nil
219
- @person.family_name.should == "Jones"
220
- @person.name.should == "Janet"
221
- @person.birth_name.should == "Smith"
222
- end
223
-
224
- it "should initialise secondary supertypes" do
225
- @acv = @person.auto_counter_val
226
- @acv.should_not be_nil
227
- @acv.surrogate.should == @person
228
- end
229
- end
230
-
231
- it "should support population blocks" do
232
- @constellation.populate do
233
- Name("bar")
234
- LegalEntity("foo")
235
- Person("Fred", "Nerk", :auto_counter_val => :new)
236
- Company("Acme, Inc", :auto_counter_val => :new)
237
- end
238
- @constellation.Name.size.should == 5
239
- @constellation.Surrogate.size.should == 2
240
- end
241
-
242
- it "should verbalise itself" do
243
- @constellation.populate do
244
- Name("bar")
245
- LegalEntity("foo")
246
- c = Company("Acme, Inc", :auto_counter_val => :new)
247
- c.is_a?(Mod::Surrogate).should == true
248
- c.auto_counter_val.should_not == nil
249
- p = Person("Fred", "Nerk", :auto_counter_val => :new)
250
- p.employer = c
251
- p.birth_name = "Nerk"
252
- end
253
- s = @constellation.verbalise
254
- names = s.split(/\n/).grep(/\tEvery /).map{|l| l.sub(/.*Every (.*):$/, '\1')}
255
- expected = ["Company", "LegalEntity", "Name", "Person", "StringVal", "Surrogate"]
256
- names.sort.should == expected
257
- end
258
-
259
- it "should support string capitalisation functions" do
260
- names = ["Company", "LegalEntity", "Name", "Person", "StringVal", "Surrogate"]
261
- camelwords = names.map{|n| n.camelwords }
262
- camelwords.should == [["Company"], ["Legal", "Entity"], ["Name"], ["Person"], ["String", "Val"], ["Surrogate"]]
263
-
264
- snakes = names.map{|n| n.snakecase }
265
- snakes.should == ["company", "legal_entity", "name", "person", "string_val", "surrogate"]
266
-
267
- camelupper = snakes.map{|n| n.camelcase }
268
- camelupper.should == ["Company", "LegalEntity", "Name", "Person", "StringVal", "Surrogate"]
269
-
270
- camellower = snakes.map{|n| n.camelcase(:lower) }
271
- camellower.should == ["company", "legalEntity", "name", "person", "stringVal", "surrogate"]
272
- end
273
-
274
- it "should allow inspection of instance indices" do
275
- baz = @constellation.Name("baz")
276
- @constellation.Name.inspect.class.should == String
277
- end
278
-
279
- it "should index value instances, including by its superclasses" do
280
- baz = @constellation.Name("baz")
281
- @constellation.Name.keys.sort.should == ["baz"]
282
-
283
- @constellation.StringVal.keys.sort.should == ["baz"]
284
- @constellation.StringVal[baz].should == baz
285
- @constellation.StringVal["baz"].should == baz
286
- end
287
-
288
- describe "instance indices" do
289
- it "should support each" do
290
- baz = @constellation.Name("baz")
291
- count = 0
292
- @constellation.Name.each { |k, v| count += 1 }
293
- count.should == 1
294
- end
295
-
296
- it "should support detect" do
297
- baz = @constellation.Name("baz")
298
- @constellation.Name.detect { |rv| true }.should be_truthy
299
- end
300
- end
301
-
302
- it "should index entity instances, including by its superclass and secondary supertypes" do
303
- name = "Acme, Inc"
304
- fred = "Fred"
305
- fly = "Fly"
306
- acme = @constellation.Company name, :auto_counter_val => :new
307
- fred_fly = @constellation.Person fred, fly, :auto_counter_val => :new
308
-
309
- # REVISIT: This should be illegal:
310
- #fred_fly.auto_counter_val = :new
311
-
312
- @constellation.Person.keys.sort.should == [[fred, fly]]
313
- @constellation.Company.keys.sort.should == [[name]]
314
-
315
- @constellation.LegalEntity.keys.sort.should include [name]
316
- @constellation.LegalEntity.keys.sort.should include [fred]
317
-
318
- @constellation.Surrogate.values.should include acme
319
- @constellation.Surrogate.values.should include fred_fly
320
- end
321
-
322
- it "should handle one-to-ones correctly" do
323
- person = @constellation.Person "Fred", "Smith", :auto_counter_val => :new, :birth_name => "Nerk"
324
-
325
- nerk = @constellation.Name["Nerk"]
326
- nerk.should_not be_nil
327
- nerk.person_as_birth_name.should == person
328
- person.birth_name = nil
329
- nerk.person_as_birth_name.should be_nil
330
- @constellation.Name["Nerk"].should_not be_nil
331
- end
332
-
333
- it "should allow retraction of instances" do
334
- @constellation.Person
335
- person = @constellation.Person "Fred", "Smith", :auto_counter_val => :new, :birth_name => "Nerk"
336
- smith = @constellation.Name("Smith")
337
-
338
- # Check things are indexed properly:
339
- @constellation.Surrogate.size.should == 1
340
- @constellation.LegalEntity.size.should == 1
341
- @constellation.Person.size.should == 1
342
- person.family_name.should == smith
343
- smith.all_person_as_family_name.size.should == 1
344
-
345
- @constellation.retract(smith)
346
-
347
- @constellation.Name["Fred"].should_not be_nil # FamilyName is not mandatory, so Fred still exists
348
- @constellation.Name["Smith"].should be_nil
349
-
350
- @constellation.Surrogate.size.should == 1
351
- @constellation.LegalEntity.size.should == 1
352
- @constellation.Person.size.should == 1
353
-
354
- person.family_name.should be_nil
355
-
356
- smith.all_person_as_family_name.size.should == 0
357
- end
358
-
359
- it "should retract linked instances (cascading)" do
360
- fred = @constellation.Person "Fred", "Smith", :auto_counter_val => :new, :birth_name => "Nerk"
361
- george = @constellation.Person "George", "Smith", :auto_counter_val => :new, :birth_name => "Patrick"
362
- smith = @constellation.Name("Smith")
363
-
364
- @constellation.Person.size.should == 2
365
- fred.family_name.should == smith
366
- george.family_name.should == smith
367
- smith.all_person_as_family_name.size.should == 2
368
-
369
- @constellation.retract(fred)
370
-
371
- @constellation.Person.size.should == 1 # Fred is gone, George still exists
372
- @constellation.Person.values[0].name.should == 'George'
373
- fred.family_name.should be_nil
374
- smith.all_person_as_family_name.size.should == 1
375
- end
376
-
377
- it "should fail to recognise references to unresolved forward referenced classes" do
378
- module Mod2
379
- class Foo
380
- identified_by :name
381
- one_to_one :name
382
- has_one :not_yet
383
- has_one :baz, :class => "BAZ"
384
- end
385
-
386
- class Name < String
387
- value_type
388
- end
389
- end
390
-
391
- @c = ActiveFacts::API::Constellation.new(Mod2)
392
- le = @c.Foo("Foo")
393
- lambda {
394
- le.not_yet
395
- }.should raise_error(NoMethodError)
396
- lambda {
397
- le.baz
398
- }.should raise_error(NoMethodError)
399
-
400
- # Now define the classes and try again:
401
- module Mod2
402
- class NotYet < String
403
- value_type
404
- end
405
- class BAZ < String
406
- value_type
407
- end
408
- end
409
- lambda {
410
- le.not_yet
411
- le.not_yet = 'not_yet'
412
- }.should_not raise_error
413
- lambda {
414
- le.baz
415
- le.baz = 'baz'
416
- }.should_not raise_error
417
- end
418
-
419
- it "should not allow references to classes outside the vocabulary" do
420
- module Outside
421
- class Other < String
422
- value_type
423
- end
424
- end
425
-
426
- lambda {
427
- module Mod
428
- class IntVal
429
- has_one :thingummy, :class => Outside::Other
430
- end
431
- end
432
- }.should raise_error(ActiveFacts::API::CrossVocabularyRoleException)
433
- end
434
-
435
- it "should disallow unrecognised supertypes" do
436
- lambda {
437
- module Mod
438
- class LegalEntity
439
- supertypes :foo
440
- end
441
- end
442
- }.should raise_error(NameError)
443
-
444
- lambda {
445
- module Mod
446
- class LegalEntity
447
- supertypes Bignum
448
- end
449
- end
450
- }.should raise_error(ActiveFacts::API::InvalidSupertypeException)
451
-
452
- lambda {
453
- module Mod
454
- class LegalEntity
455
- supertypes 3
456
- end
457
- end
458
- }.should raise_error(ActiveFacts::API::InvalidSupertypeException)
459
- end
460
-
461
- it "should allow supertypes with supertypes" do
462
- lambda {
463
- module Mod
464
- class ListedCompany < Company
465
- end
466
- end
467
- c = @constellation.ListedCompany("foo", :auto_counter_val => 23)
468
- }.should_not raise_error
469
- end
470
-
471
- it "should be able to attach a new supertype on an entity type to make it a (sub-)subtype" do
472
- module Mod
473
- class Dad
474
- identified_by :name
475
- end
476
- class Son < Dad
477
- identified_by :name
478
- end
479
- # the grand son will be linked on the fly
480
- class GrandSon
481
- identified_by :name
482
- end
483
- end
484
- Mod::GrandSon.supertypes(Mod::Son)
485
- Mod::GrandSon.supertypes.should include Mod::Son
486
- Mod::Son.supertypes.should include Mod::Dad
487
- end
488
-
489
- it "should keep information on where the identification came from" do
490
- module Mod
491
- class Dad
492
- identified_by :name
493
- end
494
- class Son < Dad
495
- identified_by :name
496
- end
497
- # Note the inheritance.
498
- class GrandSon < Son
499
- identified_by :name
500
- end
501
- end
502
-
503
- Mod::Son.identification_inherited_from.should == Mod::Dad
504
- Mod::Son.identification_inherited_from.should == Mod::Dad
505
- Mod::GrandSon.identification_inherited_from.should == Mod::Son
506
- Mod::GrandSon.overrides_identification_of.should == Mod::Dad
507
- end
508
-
509
- it "should disallow using a value type as a supertypes for an entity type" do
510
- lambda {
511
- module Mod
512
- class CompanyName
513
- identified_by :name
514
- supertypes :name
515
- end
516
- end
517
- }.should raise_error(ActiveFacts::API::InvalidSupertypeException)
518
- end
519
-
520
- it "should error on invalid :class values" do
521
- lambda {
522
- module Mod
523
- class Surrogate
524
- has_one :Name, :class => 3
525
- end
526
- end
527
- }.should raise_error(ArgumentError)
528
- end
529
-
530
- it "should error on misleading :class values" do
531
- lambda {
532
- module Mod
533
- class Surrogate
534
- has_one :Name, :class => Extra
535
- end
536
- end
537
- }.should raise_error(NameError)
538
- end
539
-
540
- it "should allow assert using an object of the same type" do
541
- c = @constellation.Company("foo", :auto_counter_val => 23)
542
- c2 = ActiveFacts::API::Constellation.new(Mod)
543
- lambda {
544
- c2.Company(c, :auto_counter_val => :new)
545
- }.should_not raise_error
546
- c2.Company.keys.should == [["foo"]]
547
- end
548
-
549
- it "should allow cross-constellation construction" do
550
- c = @constellation.Company("foo", :auto_counter_val => 23)
551
- lambda {
552
- c2 = ActiveFacts::API::Constellation.new(Mod)
553
- c2.Company(c.name, :auto_counter_val => :new)
554
- }.should_not raise_error
555
- end
556
-
557
- it "should copy values during cross-constellation assignment" do
558
- c = @constellation.Company("foo", :auto_counter_val => 23)
559
-
560
- # Now make a new constellation and use the above values to initialise new instances
561
- p = nil
562
- lambda {
563
- c2 = ActiveFacts::API::Constellation.new(Mod)
564
- p = c2.Person('Fred', 'Smith', :auto_counter_val => :new)
565
- p.employer = [ c.name, {:auto_counter_val => c.auto_counter_val}]
566
- }.should_not raise_error
567
- c.auto_counter_val.should_not === p.employer.auto_counter_val
568
- c.auto_counter_val.should_not == p.employer.auto_counter_val
569
- c.auto_counter_val.to_s.should == p.employer.auto_counter_val.to_s
570
- p.employer.should_not === c
571
-
572
- lambda {
573
- # Disallowed because it re-assigns the auto_counter_val identification value
574
- p.employer = [ "foo", {:auto_counter_val => :new}]
575
- }.should raise_error(ActiveFacts::API::TypeConflictException)
576
- end
577
- end