activefacts-api 0.8.9 → 0.8.10
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.
- data/Rakefile +4 -2
- data/TODO +29 -0
- data/VERSION +1 -1
- data/lib/activefacts/api.rb +2 -2
- data/lib/activefacts/api/constellation.rb +51 -19
- data/lib/activefacts/api/entity.rb +151 -93
- data/lib/activefacts/api/instance.rb +17 -9
- data/lib/activefacts/api/instance_index.rb +36 -35
- data/lib/activefacts/api/numeric.rb +30 -18
- data/lib/activefacts/api/object_type.rb +109 -101
- data/lib/activefacts/api/role.rb +62 -25
- data/lib/activefacts/api/role_values.rb +0 -58
- data/lib/activefacts/api/standard_types.rb +14 -5
- data/lib/activefacts/api/value.rb +22 -19
- data/lib/activefacts/api/vocabulary.rb +12 -9
- data/lib/activefacts/tracer.rb +109 -0
- data/spec/{api/autocounter_spec.rb → autocounter_spec.rb} +9 -4
- data/spec/constellation_spec.rb +434 -0
- data/spec/{api/entity_type_spec.rb → entity_type_spec.rb} +1 -0
- data/spec/identification_spec.rb +401 -0
- data/spec/instance_spec.rb +384 -0
- data/spec/role_values_spec.rb +409 -0
- data/spec/{api/roles_spec.rb → roles_spec.rb} +49 -10
- data/spec/{api/value_type_spec.rb → value_type_spec.rb} +1 -0
- metadata +36 -24
- data/lib/activefacts/api/role_proxy.rb +0 -71
- data/spec/api/constellation_spec.rb +0 -129
- data/spec/api/instance_spec.rb +0 -462
@@ -2,6 +2,7 @@
|
|
2
2
|
# ActiveFacts tests: Value instances in the Runtime API
|
3
3
|
# Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
|
4
4
|
#
|
5
|
+
require 'rspec'
|
5
6
|
require 'activefacts/api'
|
6
7
|
|
7
8
|
describe "AutoCounter Value Type instances" do
|
@@ -51,9 +52,13 @@ describe "AutoCounter Value Type instances" do
|
|
51
52
|
}.should raise_error
|
52
53
|
end
|
53
54
|
|
54
|
-
it "should not allow its
|
55
|
+
it "should not allow its value to be re-assigned" do
|
55
56
|
lambda {
|
56
|
-
@thing.thing_id
|
57
|
+
@thing.thing_id.assign(3)
|
58
|
+
}.should_not raise_error
|
59
|
+
lambda {
|
60
|
+
@thing.thing_id.assign(4)
|
61
|
+
#@thing.thing_id.assign(@thing_id)
|
57
62
|
}.should raise_error
|
58
63
|
end
|
59
64
|
|
@@ -77,8 +82,8 @@ describe "AutoCounter Value Type instances" do
|
|
77
82
|
facets = []
|
78
83
|
facets << @constellation.ThingFacet(thing, 0)
|
79
84
|
facets << @constellation.ThingFacet(thing, 1)
|
80
|
-
facets[0].thing.
|
81
|
-
facets[0].thing.thing_id.
|
85
|
+
facets[0].thing.should be_eql(facets[1].thing)
|
86
|
+
facets[0].thing.thing_id.should be_eql(facets[1].thing.thing_id)
|
82
87
|
end
|
83
88
|
|
84
89
|
end
|
@@ -0,0 +1,434 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts tests: Constellation instances in the Runtime API
|
3
|
+
# Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'rspec'
|
7
|
+
require 'activefacts/api'
|
8
|
+
|
9
|
+
describe "A Constellation instance" do
|
10
|
+
before :each do
|
11
|
+
Object.send :remove_const, :Mod if Object.const_defined?("Mod")
|
12
|
+
module Mod
|
13
|
+
@base_types = [
|
14
|
+
Int, Real, AutoCounter, String, Date, DateTime
|
15
|
+
]
|
16
|
+
|
17
|
+
# Create a value type and a subtype of that value type for each base type:
|
18
|
+
@base_types.each do |base_type|
|
19
|
+
eval <<-END
|
20
|
+
class #{base_type.basename}Value < #{base_type.name}
|
21
|
+
value_type
|
22
|
+
end
|
23
|
+
|
24
|
+
class #{base_type.basename}SubValue < #{base_type.name}Value
|
25
|
+
# Note no new "value_type" is required here, it comes through inheritance
|
26
|
+
end
|
27
|
+
END
|
28
|
+
end
|
29
|
+
|
30
|
+
class Name < StringValue
|
31
|
+
value_type
|
32
|
+
#has_one :attr, Name
|
33
|
+
end
|
34
|
+
|
35
|
+
class LegalEntity
|
36
|
+
identified_by :name
|
37
|
+
has_one :name, :mandatory => true
|
38
|
+
end
|
39
|
+
|
40
|
+
class SurrogateId
|
41
|
+
identified_by :auto_counter_value
|
42
|
+
has_one :auto_counter_value
|
43
|
+
end
|
44
|
+
|
45
|
+
class Company < LegalEntity
|
46
|
+
supertypes SurrogateId
|
47
|
+
end
|
48
|
+
|
49
|
+
class Person < LegalEntity
|
50
|
+
identified_by :name, :family_name # REVISIT: want a way to role_alias :name, :given_name
|
51
|
+
supertypes :surrogate_id # Use a Symbol binding this time
|
52
|
+
|
53
|
+
has_one :family_name, :class => Name
|
54
|
+
has_one :employer, :class => Company
|
55
|
+
one_to_one :birth_name, :class => Name
|
56
|
+
end
|
57
|
+
end
|
58
|
+
@constellation = ActiveFacts::API::Constellation.new(Mod)
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "Vocabulary" do
|
62
|
+
it "should create the constellation" do
|
63
|
+
Mod.constellation.should be_is_a ActiveFacts::API::Constellation
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should create the constellation by direct populate" do
|
67
|
+
Mod.populate do
|
68
|
+
Name "foo"
|
69
|
+
end.should be_is_a ActiveFacts::API::Constellation
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should verbalise" do
|
73
|
+
s = Mod.verbalise
|
74
|
+
s.should be_is_a String
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should allow creating a constellation" do
|
79
|
+
@constellation = ActiveFacts::API::Constellation.new(Mod)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should complain when accessing a non-class as a method" do
|
83
|
+
Mod::Foo = 23
|
84
|
+
lambda { @constellation.Foo }.should raise_error
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should complain when accessing a class that isn't an object type" do
|
88
|
+
class Mod::Bar; end
|
89
|
+
lambda { @constellation.Bar }.should raise_error
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should allow inspection" do
|
93
|
+
lambda { @constellation.inspect }.should_not raise_error
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should support fetching its vocabulary" do
|
97
|
+
@constellation.vocabulary.should == Mod
|
98
|
+
end
|
99
|
+
|
100
|
+
# it "should support fetching its query" do
|
101
|
+
# pending
|
102
|
+
# @constellation.query.should == Mod
|
103
|
+
# end
|
104
|
+
|
105
|
+
it "should support methods to assert instances via the instance index for that type" do
|
106
|
+
name = foo = acme = fred_fly = nil
|
107
|
+
lambda {
|
108
|
+
name = @constellation.Name("foo")
|
109
|
+
foo = @constellation.LegalEntity("foo")
|
110
|
+
acme = @constellation.Company("Acme, Inc", :auto_counter_value => :new)
|
111
|
+
fred_fly = @constellation.Person("fred", "fly", :auto_counter_value => :new)
|
112
|
+
}.should_not raise_error
|
113
|
+
name.class.should == Mod::Name
|
114
|
+
name.constellation.should == @constellation
|
115
|
+
|
116
|
+
foo.class.should == Mod::LegalEntity
|
117
|
+
foo.constellation.should == @constellation
|
118
|
+
foo.inspect.should =~ / in Conste/
|
119
|
+
foo.verbalise.should =~ /LegalEntity\(/
|
120
|
+
|
121
|
+
acme.class.should == Mod::Company
|
122
|
+
acme.constellation.should == @constellation
|
123
|
+
acme.inspect.should =~ / in Conste/
|
124
|
+
acme.verbalise.should =~ /Company\(/
|
125
|
+
|
126
|
+
fred_fly.class.should == Mod::Person
|
127
|
+
fred_fly.constellation.should == @constellation
|
128
|
+
fred_fly.inspect.should =~ / in Conste/
|
129
|
+
fred_fly.verbalise.should =~ /Person\(/
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should re-use instances constructed the same way" do
|
133
|
+
name1 = @constellation.Name("foo")
|
134
|
+
foo1 = @constellation.LegalEntity("foo")
|
135
|
+
acme1 = @constellation.Company("Acme, Inc", :auto_counter_value => :new)
|
136
|
+
fred_fly1 = @constellation.Person("fred", "fly", :auto_counter_value => :new)
|
137
|
+
|
138
|
+
name2 = @constellation.Name("foo")
|
139
|
+
foo2 = @constellation.LegalEntity("foo")
|
140
|
+
acme2 = @constellation.Company("Acme, Inc", :auto_counter_value => :new)
|
141
|
+
fred_fly2 = @constellation.Person("fred", "fly", :auto_counter_value => :new)
|
142
|
+
|
143
|
+
name1.object_id.should == name2.object_id
|
144
|
+
foo1.object_id.should == foo2.object_id
|
145
|
+
acme1.object_id.should == acme2.object_id
|
146
|
+
fred_fly1.object_id.should == fred_fly2.object_id
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should support methods to assert instances via the class for that type" do
|
150
|
+
name = foo = acme = fred_fly = nil
|
151
|
+
lambda {
|
152
|
+
name = @constellation.Name.assert("foo")
|
153
|
+
foo = @constellation.LegalEntity.assert("foo")
|
154
|
+
acme = @constellation.Company.assert("Acme, Inc", :auto_counter_value => :new)
|
155
|
+
fred_fly = @constellation.Person.assert("fred", "fly", :auto_counter_value => :new)
|
156
|
+
}.should_not raise_error
|
157
|
+
name.class.should == Mod::Name
|
158
|
+
name.constellation.should == @constellation
|
159
|
+
|
160
|
+
foo.class.should == Mod::LegalEntity
|
161
|
+
foo.constellation.should == @constellation
|
162
|
+
foo.inspect.should =~ / in Conste/
|
163
|
+
foo.verbalise.should =~ /LegalEntity\(/
|
164
|
+
|
165
|
+
acme.class.should == Mod::Company
|
166
|
+
acme.constellation.should == @constellation
|
167
|
+
acme.inspect.should =~ / in Conste/
|
168
|
+
acme.verbalise.should =~ /Company\(/
|
169
|
+
|
170
|
+
fred_fly.class.should == Mod::Person
|
171
|
+
fred_fly.constellation.should == @constellation
|
172
|
+
fred_fly.inspect.should =~ / in Conste/
|
173
|
+
fred_fly.verbalise.should =~ /Person\(/
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should support population blocks" do
|
177
|
+
@constellation.populate do
|
178
|
+
Name("bar")
|
179
|
+
LegalEntity("foo")
|
180
|
+
Person("Fred", "Nerk", :auto_counter_value => :new)
|
181
|
+
Company("Acme, Inc", :auto_counter_value => :new)
|
182
|
+
end
|
183
|
+
@constellation.Name.size.should == 5
|
184
|
+
@constellation.SurrogateId.size.should == 2
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should verbalise itself" do
|
188
|
+
@constellation.populate do
|
189
|
+
Name("bar")
|
190
|
+
LegalEntity("foo")
|
191
|
+
c = Company("Acme, Inc", :auto_counter_value => :new)
|
192
|
+
p = Person("Fred", "Nerk", :auto_counter_value => :new, :employer => c)
|
193
|
+
p.birth_name = "Nerk"
|
194
|
+
end
|
195
|
+
s = @constellation.verbalise
|
196
|
+
names = s.split(/\n/).grep(/\tEvery /).map{|l| l.sub(/.*Every (.*):$/, '\1')}
|
197
|
+
expected = ["AutoCounterValue", "Company", "LegalEntity", "Name", "Person", "StringValue", "SurrogateId"]
|
198
|
+
names.sort.should == expected
|
199
|
+
end
|
200
|
+
|
201
|
+
it "should support string capitalisation functions" do
|
202
|
+
names = ["Company", "LegalEntity", "Name", "Person", "StringValue", "SurrogateId"]
|
203
|
+
camelwords = names.map{|n| n.camelwords }
|
204
|
+
camelwords.should == [["Company"], ["Legal", "Entity"], ["Name"], ["Person"], ["String", "Value"], ["Surrogate", "Id"]]
|
205
|
+
|
206
|
+
snakes = names.map{|n| n.snakecase }
|
207
|
+
snakes.should == ["company", "legal_entity", "name", "person", "string_value", "surrogate_id"]
|
208
|
+
|
209
|
+
camelupper = snakes.map{|n| n.camelcase }
|
210
|
+
camelupper.should == ["Company", "LegalEntity", "Name", "Person", "StringValue", "SurrogateId"]
|
211
|
+
|
212
|
+
camellower = snakes.map{|n| n.camelcase(:lower) }
|
213
|
+
camellower.should == ["company", "legalEntity", "name", "person", "stringValue", "surrogateId"]
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should allow inspection of instance indices" do
|
217
|
+
baz = @constellation.Name("baz")
|
218
|
+
@constellation.Name.inspect.class.should == String
|
219
|
+
end
|
220
|
+
|
221
|
+
it "should index value instances, including by its superclasses" do
|
222
|
+
baz = @constellation.Name("baz")
|
223
|
+
@constellation.Name.keys.sort.should == ["baz"]
|
224
|
+
|
225
|
+
@constellation.StringValue.keys.sort.should == ["baz"]
|
226
|
+
@constellation.StringValue.include?(baz).should == baz
|
227
|
+
@constellation.StringValue.include?("baz").should == baz
|
228
|
+
end
|
229
|
+
|
230
|
+
describe "instance indices" do
|
231
|
+
it "should support each" do
|
232
|
+
baz = @constellation.Name("baz")
|
233
|
+
count = 0
|
234
|
+
@constellation.Name.each { |rv| count += 1 }
|
235
|
+
count.should == 1
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should support detect" do
|
239
|
+
baz = @constellation.Name("baz")
|
240
|
+
@constellation.Name.detect { |rv| true }.should be_true
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
it "should index entity instances, including by its superclass and secondary supertypes" do
|
245
|
+
name = "Acme, Inc"
|
246
|
+
fred = "Fred"
|
247
|
+
fly = "Fly"
|
248
|
+
acme = @constellation.Company name, :auto_counter_value => :new
|
249
|
+
fred_fly = @constellation.Person fred, fly, :auto_counter_value => :new
|
250
|
+
|
251
|
+
# REVISIT: This should be illegal:
|
252
|
+
#fred_fly.auto_counter_value = :new
|
253
|
+
|
254
|
+
@constellation.Person.keys.sort.should == [[fred, fly]]
|
255
|
+
@constellation.Company.keys.sort.should == [[name]]
|
256
|
+
|
257
|
+
@constellation.LegalEntity.keys.sort.should be_include([name])
|
258
|
+
@constellation.LegalEntity.keys.sort.should be_include([fred])
|
259
|
+
|
260
|
+
@constellation.SurrogateId.values.should be_include(acme)
|
261
|
+
@constellation.SurrogateId.values.should be_include(fred_fly)
|
262
|
+
end
|
263
|
+
|
264
|
+
it "should handle one-to-ones correctly" do
|
265
|
+
person = @constellation.Person "Fred", "Smith", :auto_counter_value => :new, :birth_name => "Nerk"
|
266
|
+
|
267
|
+
pending "Extra parameters on an assert get processed in Role#adapt before @constellation gets set" do
|
268
|
+
#person.birth_name = "Nerk"
|
269
|
+
|
270
|
+
nerk = @constellation.Name["Nerk"]
|
271
|
+
nerk.should_not be_nil
|
272
|
+
nerk.person_as_birth_name.should == person
|
273
|
+
person.birth_name = nil
|
274
|
+
nerk.person_as_birth_name.should be_nil
|
275
|
+
@constellation.Name["Nerk"].should_not be_nil
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
it "should allow retraction of instances" do
|
280
|
+
person = @constellation.Person "Fred", "Smith", :auto_counter_value => :new, :birth_name => "Nerk"
|
281
|
+
|
282
|
+
@constellation.retract(@constellation.Name("Smith"))
|
283
|
+
@constellation.Name["Smith"].should be_nil
|
284
|
+
@constellation.Name["Fred"].should_not be_nil
|
285
|
+
|
286
|
+
person.family_name.should be_nil
|
287
|
+
@constellation.retract(@constellation.Name("Fred"))
|
288
|
+
@constellation.Name["Fred"].should be_nil
|
289
|
+
pending "Retraction of identifiers doesn't de/re-index" do
|
290
|
+
@constellation.Person.size.should == 0
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
it "should fail to recognise references to unresolved forward referenced classes" do
|
295
|
+
module Mod2
|
296
|
+
class Foo
|
297
|
+
identified_by :name
|
298
|
+
one_to_one :name
|
299
|
+
has_one :bar
|
300
|
+
has_one :baz, :class => "BAZ"
|
301
|
+
end
|
302
|
+
|
303
|
+
class Name < String
|
304
|
+
value_type
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
@c = ActiveFacts::API::Constellation.new(Mod2)
|
309
|
+
le = @c.Foo("Foo")
|
310
|
+
lambda {
|
311
|
+
le.bar
|
312
|
+
}.should raise_error(NoMethodError)
|
313
|
+
lambda {
|
314
|
+
le.baz
|
315
|
+
}.should raise_error(NoMethodError)
|
316
|
+
|
317
|
+
# Now define the classes and try again:
|
318
|
+
module Mod2
|
319
|
+
class Bar < String
|
320
|
+
value_type
|
321
|
+
end
|
322
|
+
class BAZ < String
|
323
|
+
value_type
|
324
|
+
end
|
325
|
+
end
|
326
|
+
lambda {
|
327
|
+
le.bar
|
328
|
+
le.bar = 'bar'
|
329
|
+
}.should_not raise_error
|
330
|
+
lambda {
|
331
|
+
le.baz
|
332
|
+
le.baz = 'baz'
|
333
|
+
}.should_not raise_error
|
334
|
+
end
|
335
|
+
|
336
|
+
it "should not allow references to classes outside the vocabulary" do
|
337
|
+
module Outside
|
338
|
+
class Other < String
|
339
|
+
value_type
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
lambda {
|
344
|
+
module Mod
|
345
|
+
class IntValue
|
346
|
+
has_one :thingummy, :class => Outside::Other
|
347
|
+
end
|
348
|
+
end
|
349
|
+
}.should raise_error
|
350
|
+
end
|
351
|
+
|
352
|
+
it "should disallow unrecognised supertypes" do
|
353
|
+
lambda {
|
354
|
+
module Mod
|
355
|
+
class LegalEntity
|
356
|
+
supertypes :foo
|
357
|
+
end
|
358
|
+
end
|
359
|
+
}.should raise_error(NameError)
|
360
|
+
|
361
|
+
lambda {
|
362
|
+
module Mod
|
363
|
+
class LegalEntity
|
364
|
+
supertypes Bignum
|
365
|
+
end
|
366
|
+
end
|
367
|
+
}.should raise_error(RuntimeError)
|
368
|
+
|
369
|
+
lambda {
|
370
|
+
module Mod
|
371
|
+
class LegalEntity
|
372
|
+
supertypes 3
|
373
|
+
end
|
374
|
+
end
|
375
|
+
}.should raise_error(RuntimeError)
|
376
|
+
end
|
377
|
+
|
378
|
+
it "should allow supertypes with supertypes" do
|
379
|
+
lambda {
|
380
|
+
module Mod
|
381
|
+
class ListedCompany < Company
|
382
|
+
end
|
383
|
+
end
|
384
|
+
c = @constellation.ListedCompany("foo", :auto_counter_value => 23)
|
385
|
+
}.should_not raise_error(NameError)
|
386
|
+
end
|
387
|
+
|
388
|
+
it "should error on invalid :class values" do
|
389
|
+
lambda {
|
390
|
+
module Mod
|
391
|
+
class SurrogateId
|
392
|
+
has_one :Name, :class => 3
|
393
|
+
end
|
394
|
+
end
|
395
|
+
}.should raise_error
|
396
|
+
end
|
397
|
+
|
398
|
+
it "should error on misleading :class values" do
|
399
|
+
lambda {
|
400
|
+
module Mod
|
401
|
+
class SurrogateId
|
402
|
+
has_one :Name, :class => Extra
|
403
|
+
end
|
404
|
+
end
|
405
|
+
}.should raise_error
|
406
|
+
end
|
407
|
+
|
408
|
+
it "should allow assert using an object of the same type" do
|
409
|
+
c = @constellation.Company("foo", :auto_counter_value => 23)
|
410
|
+
c2 = ActiveFacts::API::Constellation.new(Mod)
|
411
|
+
lambda {
|
412
|
+
c2.Company(c, :auto_counter_value => :new)
|
413
|
+
}.should_not raise_error
|
414
|
+
c2.Company.keys.should == [["foo"]]
|
415
|
+
end
|
416
|
+
|
417
|
+
it "should allow cross-constellation construction" do
|
418
|
+
c = @constellation.Company("foo", :auto_counter_value => 23)
|
419
|
+
lambda {
|
420
|
+
c2 = ActiveFacts::API::Constellation.new(Mod)
|
421
|
+
c2.Company(c.name, :auto_counter_value => :new)
|
422
|
+
}.should_not raise_error
|
423
|
+
end
|
424
|
+
|
425
|
+
it "should allow cross-constellation assignment" do
|
426
|
+
c = @constellation.Company("foo", :auto_counter_value => 23)
|
427
|
+
lambda {
|
428
|
+
c2 = ActiveFacts::API::Constellation.new(Mod)
|
429
|
+
p = c2.Person('Fred', 'Smith', :auto_counter_value => :new)
|
430
|
+
p.employer = [c, {:auto_counter_value => :new}]
|
431
|
+
}.should_not raise_error
|
432
|
+
end
|
433
|
+
|
434
|
+
end
|