activefacts-api 0.8.9 → 0.8.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,401 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'activefacts/api'
|
3
|
+
|
4
|
+
describe "An Entity Type" do
|
5
|
+
before :all do
|
6
|
+
module Mod
|
7
|
+
class Name < String
|
8
|
+
value_type
|
9
|
+
end
|
10
|
+
|
11
|
+
class Number < Int
|
12
|
+
value_type
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
before :each do
|
17
|
+
@c = ActiveFacts::API::Constellation.new(Mod)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "whose instances are identified by a single value role" do
|
21
|
+
before :all do
|
22
|
+
module Mod
|
23
|
+
class Business
|
24
|
+
identified_by :name
|
25
|
+
one_to_one :name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should fail if the role isn't one-to-one" do
|
31
|
+
pending "Lacks a check for one-to-one identifying role" do
|
32
|
+
proc do
|
33
|
+
module Mod
|
34
|
+
class Cat
|
35
|
+
identified_by :name
|
36
|
+
has_one :name
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end.should raise_error
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "when asserted" do
|
44
|
+
before :each do
|
45
|
+
@bus = @c.Business('Acme')
|
46
|
+
@acme = @c.Name['Acme']
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should return a new instance if not previously present" do
|
50
|
+
@bus.should be_a(Mod::Business)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should assert the identifying value" do
|
54
|
+
@acme.should be_a(Mod::Name)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should be found in the constellation using the value" do
|
58
|
+
@c.Business[['Acme']].should == @bus
|
59
|
+
@c.Business[[@acme]].should == @bus
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should belong to the constellation" do
|
63
|
+
@bus.constellation.should == @c
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should be assigned to the value's counterpart role" do
|
67
|
+
@acme.business.should == @bus
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should return a previously-existing instance" do
|
71
|
+
@c.Business[['Acme']].should == @bus
|
72
|
+
@c.Business.size.should == 1
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "when the value is changed" do
|
77
|
+
before :each do
|
78
|
+
@fly = @c.Business('Fly')
|
79
|
+
@bus = @c.Business('Acme')
|
80
|
+
@acme = @c.Name['Acme']
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should fail if the new value already exists" do
|
84
|
+
pending "Doesn't check validity of rename" do
|
85
|
+
proc do
|
86
|
+
@bus.name = 'Acme'
|
87
|
+
end.should raise_error
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "to a previously-nonexistent value" do
|
92
|
+
before :each do
|
93
|
+
@bus.name = 'Bloggs'
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should assert the new identifier" do
|
97
|
+
@c.Name['Bloggs'].should be_a(Mod::Name)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should allow the change" do
|
101
|
+
@bus.name.should == 'Bloggs'
|
102
|
+
@c.Business.size.should == 2
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should be found under the new identifier" do
|
106
|
+
pending "entities are not re-indexed on identifier assignment" do
|
107
|
+
@c.Business['Bloggs'].should == @bus
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should be in the constellation's index under the new identifier" do
|
112
|
+
pending "entities are not re-indexed on identifier assignment" do
|
113
|
+
@c.Business.keys[0].should == ['Bloggs']
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should be the counterpart of the new identifier" do
|
118
|
+
@c.Name['Bloggs'].business.should == @bus
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should not be found in the constellation using the old value" do
|
122
|
+
pending "entities are not de-indexed on identifier assignment" do
|
123
|
+
@c.Business[['Acme']].should be_nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
it "the old value's back-reference is set to nil" do
|
128
|
+
@acme.business.should be_nil
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "and the old identifying value plays no other roles" do
|
132
|
+
describe "and the player of the identifying role is not independent" do
|
133
|
+
it "should retract the previous value" do
|
134
|
+
pending "All value types default to independent" do
|
135
|
+
@c.Name['Acme'].should be_nil
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "when retracted" do
|
144
|
+
before :each do
|
145
|
+
@bus = @c.Business('Acme')
|
146
|
+
@acme = @c.Name['Acme']
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should disappear from the constellation" do
|
150
|
+
@bus.retract
|
151
|
+
@c.Business[['Acme']].should be_nil
|
152
|
+
end
|
153
|
+
|
154
|
+
describe "and the identifying value plays no other roles" do
|
155
|
+
describe "and the player of the identifying role is not independent" do
|
156
|
+
before :each do
|
157
|
+
@bus.retract
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should retract the identifying value also" do
|
161
|
+
pending "All value types default to independent" do
|
162
|
+
@c.Name['Acme'].should be_nil
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should not appear as the value's counterpart role" do
|
167
|
+
@acme.business.should be_nil
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "and the identifying value plays other roles" do
|
172
|
+
before :all do
|
173
|
+
module Mod
|
174
|
+
class Dog
|
175
|
+
identified_by :name
|
176
|
+
one_to_one :name
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
before :each do
|
181
|
+
@c.Dog("Acme")
|
182
|
+
@bus.retract
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should retain the identifying value, but with a nil counterpart role" do
|
186
|
+
@c.Name['Acme'].should == @acme
|
187
|
+
@acme.business.should be_nil
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe "identified by two values" do
|
195
|
+
before :all do
|
196
|
+
module Mod
|
197
|
+
class Building
|
198
|
+
identified_by :name
|
199
|
+
one_to_one :name
|
200
|
+
end
|
201
|
+
|
202
|
+
class Room
|
203
|
+
identified_by :building, :number
|
204
|
+
has_one :building
|
205
|
+
has_one :number
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
before :each do
|
210
|
+
@c = ActiveFacts::API::Constellation.new(Mod)
|
211
|
+
end
|
212
|
+
|
213
|
+
it "should fail if any role is one-to-one" do
|
214
|
+
pending "Lacks a check for one-to-one identifying role" do
|
215
|
+
proc do
|
216
|
+
module Mod
|
217
|
+
class Floor
|
218
|
+
identified_by :building, :number
|
219
|
+
has_one :building
|
220
|
+
one_to_one :number # Error, invalid identifier
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end.should raise_error
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
describe "when asserted" do
|
228
|
+
before :each do
|
229
|
+
@b = @c.Building('Mackay')
|
230
|
+
@mackay = @c.Name['Mackay']
|
231
|
+
@r = @c.Room(@b, 101)
|
232
|
+
@rn = @r.number
|
233
|
+
end
|
234
|
+
|
235
|
+
it "should return a new instance if not previously present" do
|
236
|
+
@r.should be_a(Mod::Room)
|
237
|
+
end
|
238
|
+
|
239
|
+
it "should assert the identifying values" do
|
240
|
+
@rn.should be_a(Mod::Number)
|
241
|
+
@c.Number[@rn].should == @rn # Yes
|
242
|
+
@c.Number[101].should == @rn # No
|
243
|
+
@c.Number[101].should be_eql 101 # No
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should be found in the constellation using the value" do
|
247
|
+
@c.Room[[@b.identifying_role_values, @rn]].should == @r
|
248
|
+
@c.Room[[@b.identifying_role_values, 101]].should == @r
|
249
|
+
@c.Room[[['Mackay'], 101]].should == @r
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should belong to the constellation" do
|
253
|
+
@r.constellation.should == @c
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should be added to the values' counterpart roles" do
|
257
|
+
@rn.all_room.to_a.should == [@r]
|
258
|
+
@b.all_room.to_a.should == [@r]
|
259
|
+
end
|
260
|
+
|
261
|
+
it "should return a previously-existing instance" do
|
262
|
+
@c.Room(@b, 101).should == @r
|
263
|
+
@c.Room(['Mackay'], 101).should == @r
|
264
|
+
@c.Room.size.should == 1
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
describe "when the value is changed" do
|
269
|
+
before :each do
|
270
|
+
@b = @c.Building('Mackay')
|
271
|
+
@mackay = @c.Name['Mackay']
|
272
|
+
@r = @c.Room(@b, 101)
|
273
|
+
@rn = @r.number
|
274
|
+
end
|
275
|
+
|
276
|
+
it "should fail if the new value already exists" do
|
277
|
+
@c.Room(@b, 102)
|
278
|
+
pending "Doesn't check validity of rename" do
|
279
|
+
proc {
|
280
|
+
@r.number = 102
|
281
|
+
}.should raise_error
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
describe "to a previously-nonexistent value" do
|
286
|
+
before :each do
|
287
|
+
@r.number = 103
|
288
|
+
@new_number = @r.number
|
289
|
+
end
|
290
|
+
|
291
|
+
it "should assert the new identifier" do
|
292
|
+
@new_number.should_not be_nil
|
293
|
+
end
|
294
|
+
|
295
|
+
it "should allow the change" do
|
296
|
+
@r.number.should == @new_number
|
297
|
+
@r.number.should be_eql(103)
|
298
|
+
end
|
299
|
+
|
300
|
+
it "should be found under the new identifier" do
|
301
|
+
pending "entities are not re-indexed on identifier assignment" do
|
302
|
+
@c.Room[[@b.identifying_role_values, 103]].should == @r
|
303
|
+
@c.Room[[['Mackay'], 101]].should be_nil
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
it "should be in the constellation's index under the new identifier" do
|
308
|
+
pending "entities are not de-indexed on identifier assignment" do
|
309
|
+
@c.Room.keys[0].should == [['Mackay'], @r.number]
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
it "should be included in the counterparts of the new identifier roles" do
|
314
|
+
@b.all_room.to_a.should == [@r]
|
315
|
+
@new_number.all_room.to_a.should == [@r]
|
316
|
+
end
|
317
|
+
|
318
|
+
it "should not be found in the constellation using the old value" do
|
319
|
+
pending "entities are not de-indexed on identifier assignment" do
|
320
|
+
@c.Room.keys[0].should_not == [['Mackay'],101]
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
it "the old value's back-reference is set to nil" do
|
325
|
+
@rn.all_room.to_a.should_not be_include(@r)
|
326
|
+
end
|
327
|
+
|
328
|
+
describe "and the old identifying value plays no other roles" do
|
329
|
+
describe "and the player of the identifying role is not independent" do
|
330
|
+
it "should retract the previous value" do
|
331
|
+
pending "All value types default to independent" do
|
332
|
+
@c.Number[101].should be_nil
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
=begin
|
341
|
+
describe "when retracted" do
|
342
|
+
it "should disappear from the constellation"
|
343
|
+
describe "and the identifying value plays no other roles" do
|
344
|
+
describe "and the player of the identifying role is not independent" do
|
345
|
+
it "should retract the identifying value also"
|
346
|
+
it "should not appear as the value's counterpart role"
|
347
|
+
end
|
348
|
+
describe "and the identifying value plays other roles" do
|
349
|
+
it "should retain the identifying value, but with a nil counterpart role"
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
=end
|
354
|
+
|
355
|
+
end
|
356
|
+
|
357
|
+
=begin
|
358
|
+
describe "which inherits its identification from a supertype" do
|
359
|
+
describe "which also has a secondary supertype" do
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
describe "which has a supertype that has separate identification" do
|
364
|
+
before :each do
|
365
|
+
module Mod
|
366
|
+
class Animal
|
367
|
+
identified_by :number
|
368
|
+
one_to_one :neumber
|
369
|
+
end
|
370
|
+
class Dog < Animal
|
371
|
+
identified_by :name
|
372
|
+
one_to_one :name
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
describe "when asserted" do
|
378
|
+
describe "and both identifiers are new" do
|
379
|
+
it "should be found using the respective identifiers"
|
380
|
+
end
|
381
|
+
describe "and only the supertype identifier is new" do
|
382
|
+
it "should be rejected because of the duplicate subtype identifier"
|
383
|
+
end
|
384
|
+
describe "and only the subtype identifier is new" do
|
385
|
+
it "should be rejected because of the duplicate supertype identifier"
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
describe "when the subtype identifier is changed" do
|
390
|
+
it "should fail if the new subtype value already exists"
|
391
|
+
it "should allow the change if the new subtype value doesn't already exist"
|
392
|
+
end
|
393
|
+
|
394
|
+
describe "when the supertype identifier is changed" do
|
395
|
+
it "should fail if the new supertype value already exists"
|
396
|
+
it "should allow the change if the new supertype value doesn't already exist"
|
397
|
+
end
|
398
|
+
|
399
|
+
end
|
400
|
+
=end
|
401
|
+
end
|
@@ -0,0 +1,384 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts tests: Value instances in the Runtime API
|
3
|
+
# Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
|
4
|
+
#
|
5
|
+
require 'rspec'
|
6
|
+
require 'activefacts/api'
|
7
|
+
|
8
|
+
describe "An instance of every type of ObjectType" do
|
9
|
+
before :each do
|
10
|
+
Object.send :remove_const, :Mod if Object.const_defined?("Mod")
|
11
|
+
module Mod
|
12
|
+
# These are the base value types we're going to test:
|
13
|
+
@base_types = [
|
14
|
+
Int, Real, AutoCounter, String, Date, DateTime, Decimal
|
15
|
+
]
|
16
|
+
|
17
|
+
# Construct the names of the roles they play:
|
18
|
+
@base_type_roles = @base_types.map do |t|
|
19
|
+
t.name.snakecase
|
20
|
+
end
|
21
|
+
@role_names = @base_type_roles.inject([]) {|a, t|
|
22
|
+
a << :"#{t}_value"
|
23
|
+
} +
|
24
|
+
@base_type_roles.inject([]) {|a, t|
|
25
|
+
a << :"#{t}_sub_value"
|
26
|
+
}
|
27
|
+
|
28
|
+
# Create a value type and a subtype of that value type for each base type:
|
29
|
+
@base_types.each do |base_type|
|
30
|
+
eval <<-END
|
31
|
+
class #{base_type.name}Value < #{base_type.name}
|
32
|
+
value_type
|
33
|
+
end
|
34
|
+
|
35
|
+
class #{base_type.name}SubValue < #{base_type.name}Value
|
36
|
+
# Note no new "value_type" is required here, it comes through inheritance
|
37
|
+
end
|
38
|
+
END
|
39
|
+
end
|
40
|
+
|
41
|
+
# Create a TestByX, TestByXSub, and TestSubByX class for all base types X
|
42
|
+
# Each class has a has_one and a one_to_one for all roles.
|
43
|
+
# and is identified by the has_one :x role
|
44
|
+
@base_types.each do |base_type|
|
45
|
+
code = <<-END
|
46
|
+
class TestBy#{base_type.name}
|
47
|
+
identified_by :#{base_type.name.snakecase}_value#{
|
48
|
+
@role_names.map do |role_name|
|
49
|
+
%Q{
|
50
|
+
has_one :#{role_name}#{
|
51
|
+
mandatory = (role_name == (base_type.name.snakecase+'_value').to_sym ? ', :mandatory => true' : '')
|
52
|
+
}
|
53
|
+
one_to_one :one_#{role_name}, :class => #{role_name.to_s.camelcase}}
|
54
|
+
end*""
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
class TestBy#{base_type.name}Sub
|
59
|
+
identified_by :#{base_type.name.snakecase}_sub_value#{
|
60
|
+
@role_names.map do |role_name|
|
61
|
+
%Q{
|
62
|
+
has_one :#{role_name}
|
63
|
+
one_to_one :one_#{role_name}, :class => #{role_name.to_s.camelcase}}
|
64
|
+
end*""
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
class TestSubBy#{base_type.name} < TestBy#{base_type.name}
|
69
|
+
# Entity subtypes, inherit identification and all roles
|
70
|
+
end
|
71
|
+
|
72
|
+
class TestBy#{base_type.name}Entity
|
73
|
+
identified_by :test_by_#{base_type.name.snakecase}
|
74
|
+
one_to_one :test_by_#{base_type.name.snakecase}
|
75
|
+
end
|
76
|
+
END
|
77
|
+
eval code
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Simple Values
|
82
|
+
@int = 0
|
83
|
+
@real = 0.0
|
84
|
+
@auto_counter = 0
|
85
|
+
@new_auto_counter = :new
|
86
|
+
@string = "zero"
|
87
|
+
@date = [2008, 04, 19]
|
88
|
+
@date_time = [2008, 04, 19, 10, 28, 14]
|
89
|
+
@decimal = BigDecimal.new('98765432109876543210')
|
90
|
+
|
91
|
+
# Value Type instances
|
92
|
+
@int_value = Mod::IntValue.new(1)
|
93
|
+
@real_value = Mod::RealValue.new(1.0)
|
94
|
+
@auto_counter_value = Mod::AutoCounterValue.new(1)
|
95
|
+
@new_auto_counter_value = Mod::AutoCounterValue.new(:new)
|
96
|
+
@string_value = Mod::StringValue.new("one")
|
97
|
+
@date_value = Mod::DateValue.new(2008, 04, 20)
|
98
|
+
# Parse the date:
|
99
|
+
@date_value = Mod::DateValue.new '2nd Nov 2001'
|
100
|
+
d = ::Date.civil(2008, 04, 20)
|
101
|
+
@date_time_value = Mod::DateTimeValue.new d # 2008, 04, 20, 10, 28, 14
|
102
|
+
# This next isn't in the same pattern; it makes a Decimal from a BigDecimal rather than a String (coverage reasons)
|
103
|
+
@decimal_value = Mod::DecimalValue.new(BigDecimal.new('98765432109876543210'))
|
104
|
+
|
105
|
+
# Value SubType instances
|
106
|
+
@int_sub_value = Mod::IntSubValue.new(4)
|
107
|
+
@real_sub_value = Mod::RealSubValue.new(4.0)
|
108
|
+
@auto_counter_sub_value = Mod::AutoCounterSubValue.new(4)
|
109
|
+
@auto_counter_sub_value_new = Mod::AutoCounterSubValue.new(:new)
|
110
|
+
@string_sub_value = Mod::StringSubValue.new("five")
|
111
|
+
@date_sub_value = Mod::DateSubValue.new(2008, 04, 25)
|
112
|
+
@date_time_sub_value = Mod::DateTimeSubValue.new(::DateTime.civil(2008, 04, 26, 10, 28, 14))
|
113
|
+
# This next isn't in the same pattern; it makes a Decimal from a BigNum rather than a String (coverage reasons)
|
114
|
+
@decimal_sub_value = Mod::DecimalSubValue.new(98765432109876543210)
|
115
|
+
|
116
|
+
# Entities identified by Value Type, SubType and Entity-by-value-type instances
|
117
|
+
@test_by_int = Mod::TestByInt.new(2)
|
118
|
+
@test_by_real = Mod::TestByReal.new(2.0)
|
119
|
+
@test_by_auto_counter = Mod::TestByAutoCounter.new(2)
|
120
|
+
@test_by_auto_counter_new = Mod::TestByAutoCounter.new(:new)
|
121
|
+
@test_by_string = Mod::TestByString.new("two")
|
122
|
+
@test_by_date = Mod::TestByDate.new(Date.new(2008,04,28))
|
123
|
+
#@test_by_date = Mod::TestByDate.new(2008,04,28)
|
124
|
+
# Pass an array of values directly to DateTime.civil:
|
125
|
+
@test_by_date_time = Mod::TestByDateTime.new([[2008,04,28,10,28,15]])
|
126
|
+
#@test_by_date_time = Mod::TestByDateTime.new(DateTime.new(2008,04,28,10,28,15))
|
127
|
+
@test_by_decimal = Mod::TestByDecimal.new('98765432109876543210')
|
128
|
+
|
129
|
+
@test_by_int_sub = Mod::TestByIntSub.new(2)
|
130
|
+
@test_by_real_sub = Mod::TestByRealSub.new(5.0)
|
131
|
+
@test_by_auto_counter_sub = Mod::TestByAutoCounterSub.new(6)
|
132
|
+
@test_by_auto_counter_new_sub = Mod::TestByAutoCounterSub.new(:new)
|
133
|
+
@test_by_string_sub = Mod::TestByStringSub.new("six")
|
134
|
+
@test_by_date_sub = Mod::TestByDateSub.new(Date.new(2008,04,27))
|
135
|
+
@test_by_date_time_sub = Mod::TestByDateTimeSub.new(2008,04,29,10,28,15)
|
136
|
+
@test_by_decimal_sub = Mod::TestByDecimalSub.new('98765432109876543210')
|
137
|
+
|
138
|
+
@test_by_int_entity = Mod::TestByIntEntity.new(@test_by_int)
|
139
|
+
@test_by_real_entity = Mod::TestByRealEntity.new(@test_by_real)
|
140
|
+
@test_by_auto_counter_entity = Mod::TestByAutoCounterEntity.new(@test_by_auto_counter)
|
141
|
+
@test_by_auto_counter_new_entity = Mod::TestByAutoCounterEntity.new(@test_by_auto_counter_new)
|
142
|
+
@test_by_string_entity = Mod::TestByStringEntity.new(@test_by_string)
|
143
|
+
@test_by_date_entity = Mod::TestByDateEntity.new(@test_by_date)
|
144
|
+
@test_by_date_time_entity = Mod::TestByDateTimeEntity.new(@test_by_date_time)
|
145
|
+
@test_by_decimal_entity = Mod::TestByDecimalEntity.new(@test_by_decimal)
|
146
|
+
|
147
|
+
# Entity subtypes
|
148
|
+
@test_sub_by_int = Mod::TestSubByInt.new(2)
|
149
|
+
@test_sub_by_real = Mod::TestSubByReal.new(2.0)
|
150
|
+
@test_sub_by_auto_counter = Mod::TestSubByAutoCounter.new(2)
|
151
|
+
@test_sub_by_auto_counter_new = Mod::TestSubByAutoCounter.new(:new)
|
152
|
+
@test_sub_by_string = Mod::TestSubByString.new("two")
|
153
|
+
@test_sub_by_date = Mod::TestSubByDate.new(Date.new(2008,04,28))
|
154
|
+
@test_sub_by_date_time = Mod::TestSubByDateTime.new(2008,04,28,10,28,15)
|
155
|
+
@test_sub_by_decimal = Mod::TestSubByDecimal.new('98765432109876543210')
|
156
|
+
|
157
|
+
# These arrays get zipped together in various ways. Keep them aligned.
|
158
|
+
@values = [
|
159
|
+
@int, @real, @auto_counter, @new_auto_counter,
|
160
|
+
@string, @date, @date_time, @decimal
|
161
|
+
]
|
162
|
+
@classes = [
|
163
|
+
Int, Real, AutoCounter, AutoCounter,
|
164
|
+
String, Date, DateTime, Decimal
|
165
|
+
]
|
166
|
+
@value_types = [
|
167
|
+
Mod::IntValue, Mod::RealValue, Mod::AutoCounterValue, Mod::AutoCounterValue,
|
168
|
+
Mod::StringValue, Mod::DateValue, Mod::DateTimeValue, Mod::DecimalValue,
|
169
|
+
Mod::IntSubValue, Mod::RealSubValue, Mod::AutoCounterSubValue, Mod::AutoCounterSubValue,
|
170
|
+
Mod::StringSubValue, Mod::DateSubValue, Mod::DateTimeSubValue, Mod::DecimalSubValue,
|
171
|
+
]
|
172
|
+
@value_instances = [
|
173
|
+
@int_value, @real_value, @auto_counter_value, @new_auto_counter_value,
|
174
|
+
@string_value, @date_value, @date_time_value, @decimal_value,
|
175
|
+
@int_sub_value, @real_sub_value, @auto_counter_sub_value, @auto_counter_sub_value_new,
|
176
|
+
@string_sub_value, @date_sub_value, @date_time_sub_value, @decimal_sub_value,
|
177
|
+
@int_value, @real_value, @auto_counter_value, @new_auto_counter_value,
|
178
|
+
@string_value, @date_value, @date_time_value, @decimal_value,
|
179
|
+
]
|
180
|
+
@entity_types = [
|
181
|
+
Mod::TestByInt, Mod::TestByReal, Mod::TestByAutoCounter, Mod::TestByAutoCounter,
|
182
|
+
Mod::TestByString, Mod::TestByDate, Mod::TestByDateTime, Mod::TestByDecimal,
|
183
|
+
Mod::TestByIntSub, Mod::TestByRealSub, Mod::TestByAutoCounterSub, Mod::TestByAutoCounterSub,
|
184
|
+
Mod::TestByStringSub, Mod::TestByDateSub, Mod::TestByDateTimeSub, Mod::TestByDecimalSub,
|
185
|
+
Mod::TestSubByInt, Mod::TestSubByReal, Mod::TestSubByAutoCounter, Mod::TestSubByAutoCounter,
|
186
|
+
Mod::TestSubByString, Mod::TestSubByDate, Mod::TestSubByDateTime, Mod::TestByDecimalEntity,
|
187
|
+
]
|
188
|
+
@entities = [
|
189
|
+
@test_by_int, @test_by_real, @test_by_auto_counter, @test_by_auto_counter_new,
|
190
|
+
@test_by_string, @test_by_date, @test_by_date_time, @test_by_decimal,
|
191
|
+
@test_by_int_sub, @test_by_real_sub, @test_by_auto_counter_sub, @test_by_auto_counter_new_sub,
|
192
|
+
@test_by_string_sub, @test_by_date_sub, @test_by_date_time_sub, @test_by_decimal_sub,
|
193
|
+
@test_sub_by_int, @test_sub_by_real, @test_sub_by_auto_counter, @test_sub_by_auto_counter_new,
|
194
|
+
@test_sub_by_string, @test_sub_by_date, @test_sub_by_date_time, @test_sub_by_decimal,
|
195
|
+
]
|
196
|
+
@entities_by_entity = [
|
197
|
+
@test_by_int_entity,
|
198
|
+
@test_by_real_entity,
|
199
|
+
@test_by_auto_counter_entity,
|
200
|
+
@test_by_auto_counter_new_entity,
|
201
|
+
@test_by_string_entity,
|
202
|
+
@test_by_date_entity,
|
203
|
+
@test_by_date_time_entity,
|
204
|
+
@test_by_decimal_entity,
|
205
|
+
]
|
206
|
+
@entities_by_entity_types = [
|
207
|
+
Mod::TestByIntEntity, Mod::TestByRealEntity, Mod::TestByAutoCounterEntity, Mod::TestByAutoCounterEntity,
|
208
|
+
Mod::TestByStringEntity, Mod::TestByDateEntity, Mod::TestByDateTimeEntity, Mod::TestByDecimalEntity,
|
209
|
+
]
|
210
|
+
@test_role_names = [
|
211
|
+
:int_value, :real_value, :auto_counter_value, :auto_counter_value,
|
212
|
+
:string_value, :date_value, :date_time_value, :decimal_value,
|
213
|
+
:int_sub_value, :real_sub_value, :auto_counter_sub_value, :auto_counter_sub_value,
|
214
|
+
:string_sub_value, :date_sub_value, :date_time_sub_value, :decimal_sub_value,
|
215
|
+
:int_value, :real_value, :auto_counter_value, :auto_counter_value,
|
216
|
+
:string_value, :date_value, :date_time_value, :decimal_value,
|
217
|
+
]
|
218
|
+
@role_values = [
|
219
|
+
3, 4.0, 5, 6,
|
220
|
+
"three", Date.new(2008,4,21), DateTime.new(2008,4,22,10,28,16),
|
221
|
+
'98765432109876543210'
|
222
|
+
]
|
223
|
+
@role_alternate_values = [
|
224
|
+
4, 5.0, 6, 7,
|
225
|
+
"four", Date.new(2009,4,21), DateTime.new(2009,4,22,10,28,16),
|
226
|
+
'98765432109876543211'
|
227
|
+
]
|
228
|
+
@subtype_role_instances = [
|
229
|
+
Mod::IntSubValue.new(6), Mod::RealSubValue.new(6.0),
|
230
|
+
Mod::AutoCounterSubValue.new(:new), Mod::AutoCounterSubValue.new(8),
|
231
|
+
Mod::StringSubValue.new("seven"),
|
232
|
+
Mod::DateSubValue.new(2008,4,29), Mod::DateTimeSubValue.new(2008,4,30,10,28,16),
|
233
|
+
Mod::DecimalSubValue.new('98765432109876543210'),
|
234
|
+
]
|
235
|
+
end
|
236
|
+
|
237
|
+
describe "verbalisation" do
|
238
|
+
it "if a value type, should verbalise" do
|
239
|
+
@value_types.each do |value_type|
|
240
|
+
#puts "#{value_type} verbalises as #{value_type.verbalise}"
|
241
|
+
value_type.respond_to?(:verbalise).should be_true
|
242
|
+
verbalisation = value_type.verbalise
|
243
|
+
verbalisation.should =~ %r{\b#{value_type.basename}\b}
|
244
|
+
verbalisation.should =~ %r{\b#{value_type.superclass.basename}\b}
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
it "if an entity type, should verbalise" do
|
249
|
+
@entity_types.each do |entity_type|
|
250
|
+
#puts entity_type.verbalise
|
251
|
+
entity_type.respond_to?(:verbalise).should be_true
|
252
|
+
verbalisation = entity_type.verbalise
|
253
|
+
verbalisation.should =~ %r{\b#{entity_type.basename}\b}
|
254
|
+
|
255
|
+
# All identifying roles should be in the verbalisation.
|
256
|
+
# Strictly this should be the role name, but we don't set names here.
|
257
|
+
entity_type.identifying_role_names.each do |ir|
|
258
|
+
role = entity_type.roles(ir)
|
259
|
+
role.should_not be_nil
|
260
|
+
counterpart_object_type = role.counterpart.object_type
|
261
|
+
verbalisation.should =~ %r{\b#{counterpart_object_type.basename}\b}
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
it "should inspect" do
|
267
|
+
(@value_instances+@entities+@entities_by_entity).each do |object|
|
268
|
+
object.inspect
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
it "if a value, should verbalise" do
|
273
|
+
@value_instances.each do |value|
|
274
|
+
#puts value.verbalise
|
275
|
+
value.respond_to?(:verbalise).should be_true
|
276
|
+
verbalisation = value.verbalise
|
277
|
+
verbalisation.should =~ %r{\b#{value.class.basename}\b}
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
it "if an entity, should respond to verbalise" do
|
282
|
+
(@entities+@entities_by_entity).each do |entity|
|
283
|
+
#puts entity.verbalise
|
284
|
+
entity.respond_to?(:verbalise).should be_true
|
285
|
+
verbalisation = entity.verbalise
|
286
|
+
verbalisation.should =~ %r{\b#{entity.class.basename}\b}
|
287
|
+
entity.class.identifying_role_names.each do |ir|
|
288
|
+
role = entity.class.roles(ir)
|
289
|
+
role.should_not be_nil
|
290
|
+
counterpart_object_type = role.counterpart.object_type
|
291
|
+
verbalisation.should =~ %r{\b#{counterpart_object_type.basename}\b}
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
it "should respond to constellation" do
|
298
|
+
(@value_instances+@entities+@entities_by_entity).each do |instance|
|
299
|
+
instance.respond_to?(:constellation).should be_true
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
it "should return the module in response to .vocabulary()" do
|
304
|
+
(@value_types+@entity_types).zip((@value_instances+@entities+@entities_by_entity)).each do |object_type, instance|
|
305
|
+
instance.class.vocabulary.should == Mod
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
it "should disallow treating an unresolved AutoCounter as an integer" do
|
310
|
+
c = ActiveFacts::API::Constellation.new(Mod)
|
311
|
+
a = c.AutoCounterValue(:new)
|
312
|
+
lambda {
|
313
|
+
b = 2 + a
|
314
|
+
}.should raise_error
|
315
|
+
a.assign(3)
|
316
|
+
lambda {
|
317
|
+
b = 2 + a
|
318
|
+
a.to_i
|
319
|
+
}.should_not raise_error
|
320
|
+
end
|
321
|
+
|
322
|
+
it "should complain when not enough identifying values are provided for an entity" do
|
323
|
+
c = ActiveFacts::API::Constellation.new(Mod)
|
324
|
+
lambda {
|
325
|
+
c.TestByInt(:int_value => nil)
|
326
|
+
}.should raise_error
|
327
|
+
end
|
328
|
+
|
329
|
+
it "should complain when too many identifying values are provided for an entity" do
|
330
|
+
c = ActiveFacts::API::Constellation.new(Mod)
|
331
|
+
lambda {
|
332
|
+
c.TestByInt(2, 3)
|
333
|
+
}.should raise_error
|
334
|
+
end
|
335
|
+
|
336
|
+
it "should handle a non-mandatory missing identifying role" do
|
337
|
+
module Mod2
|
338
|
+
class Word
|
339
|
+
identified_by :singular, :plural
|
340
|
+
has_one :singular, :class => "Spelling", :mandatory => true
|
341
|
+
has_one :plural, :class => "Spelling"
|
342
|
+
end
|
343
|
+
class Spelling < String
|
344
|
+
value_type
|
345
|
+
end
|
346
|
+
end
|
347
|
+
c = ActiveFacts::API::Constellation.new(Mod2)
|
348
|
+
s = c.Word('sheep')
|
349
|
+
f = c.Word('fish', :plural => nil)
|
350
|
+
a = c.Word('aircraft', nil)
|
351
|
+
s.plural.should be_nil
|
352
|
+
f.plural.should be_nil
|
353
|
+
a.plural.should be_nil
|
354
|
+
end
|
355
|
+
|
356
|
+
it "should handle a unary as an identifying role" do
|
357
|
+
module Mod2
|
358
|
+
class Status
|
359
|
+
identified_by :is_ok
|
360
|
+
maybe :is_ok
|
361
|
+
end
|
362
|
+
end
|
363
|
+
c = ActiveFacts::API::Constellation.new(Mod2)
|
364
|
+
|
365
|
+
n = c.Status(:is_ok => nil)
|
366
|
+
t = c.Status(:is_ok => true)
|
367
|
+
f = c.Status(:is_ok => false)
|
368
|
+
s = c.Status(:is_ok => 'foo')
|
369
|
+
n.is_ok.should == nil
|
370
|
+
t.is_ok.should == true
|
371
|
+
f.is_ok.should == false
|
372
|
+
s.is_ok.should == true
|
373
|
+
|
374
|
+
n.is_ok = nil
|
375
|
+
t.is_ok = true
|
376
|
+
f.is_ok = false
|
377
|
+
s.is_ok = true
|
378
|
+
n.is_ok.should == nil
|
379
|
+
t.is_ok.should == true
|
380
|
+
f.is_ok.should == false
|
381
|
+
s.is_ok.should == true
|
382
|
+
end
|
383
|
+
|
384
|
+
end
|