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
@@ -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
|