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.
@@ -2,6 +2,7 @@
2
2
  # ActiveFacts tests: Entity classes 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 "Entity Type class definitions" do
@@ -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