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,409 @@
|
|
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
|
+
VALUE_TYPES = Int, Real, AutoCounter, String, Date, DateTime, Decimal
|
9
|
+
RAW_VALUES = [2, 3.0, 4, "5", Date.new(2008, 04, 20), DateTime.new(2008, 04, 20, 10, 28, 14)]
|
10
|
+
ALT_VALUES = [3, 4.0, 5, "6", Date.new(2009, 04, 20), DateTime.new(2009, 04, 20, 10, 28, 14)]
|
11
|
+
VALUE_SUB_FOR_VALUE = {}
|
12
|
+
VALUES_FOR_TYPE = VALUE_TYPES.zip(RAW_VALUES, ALT_VALUES).inject({}) do |h, (vt, v1, v2)|
|
13
|
+
next h unless v1 and v2
|
14
|
+
h[vt] = [v1, v2]
|
15
|
+
h
|
16
|
+
end
|
17
|
+
VALUE_TYPE_FOR_OBJECT_TYPE = {}
|
18
|
+
OBJECT_TYPES = []
|
19
|
+
|
20
|
+
module TestValueTypesModule
|
21
|
+
class ESCID < AutoCounter
|
22
|
+
value_type
|
23
|
+
end
|
24
|
+
BASE_VALUE_TYPE_ROLE_NAMES = VALUE_TYPES.map { |base_type| base_type.name.snakecase }
|
25
|
+
VALUE_TYPE_ROLE_NAMES = BASE_VALUE_TYPE_ROLE_NAMES.map { |n| [ :"#{n}_value", :"#{n}_sub_value" ] }.flatten
|
26
|
+
VALUE_TYPES.map do |value_type|
|
27
|
+
code = <<-END
|
28
|
+
class #{value_type.name}Value < #{value_type.name}
|
29
|
+
value_type
|
30
|
+
end
|
31
|
+
|
32
|
+
class #{value_type.name}ValueSub < #{value_type.name}Value
|
33
|
+
# Note no new "value_type" is required here, it comes through inheritance
|
34
|
+
end
|
35
|
+
|
36
|
+
class #{value_type.name}Entity
|
37
|
+
identified_by :#{identifying_role_name = "id_#{value_type.name.snakecase}_value"}
|
38
|
+
has_one :#{identifying_role_name}, :class => #{value_type.name}Value
|
39
|
+
end
|
40
|
+
|
41
|
+
class #{value_type.name}EntitySub < #{value_type.name}Entity
|
42
|
+
end
|
43
|
+
|
44
|
+
class #{value_type.name}EntitySubCtr < #{value_type.name}Entity
|
45
|
+
identified_by :counter
|
46
|
+
has_one :counter, :class => "ESCID"
|
47
|
+
end
|
48
|
+
|
49
|
+
VALUE_SUB_FOR_VALUE[#{value_type.name}Value] = #{value_type.name}ValueSub
|
50
|
+
classes = [
|
51
|
+
#{value_type.name}Value,
|
52
|
+
#{value_type.name}ValueSub,
|
53
|
+
#{value_type.name}Entity,
|
54
|
+
#{value_type.name}EntitySub,
|
55
|
+
#{value_type.name}EntitySubCtr,
|
56
|
+
]
|
57
|
+
OBJECT_TYPES.concat(classes)
|
58
|
+
classes.each { |klass| VALUE_TYPE_FOR_OBJECT_TYPE[klass] = value_type }
|
59
|
+
END
|
60
|
+
eval code
|
61
|
+
end
|
62
|
+
OBJECT_TYPE_NAMES = OBJECT_TYPES.map{|object_type| object_type.basename}
|
63
|
+
|
64
|
+
class Octopus
|
65
|
+
identified_by :zero
|
66
|
+
has_one :zero, :class => IntValue
|
67
|
+
maybe :has_a_unary
|
68
|
+
OBJECT_TYPE_NAMES.each do |object_type_name|
|
69
|
+
has_one object_type_name.snakecase.to_sym
|
70
|
+
one_to_one ("one_"+object_type_name.snakecase).to_sym, :class => object_type_name
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "Roles of an Object Type" do
|
76
|
+
|
77
|
+
it "should return a roles collection" do
|
78
|
+
roles = TestValueTypesModule::Octopus.roles
|
79
|
+
roles.should_not be_nil
|
80
|
+
roles.size.should == 2+VALUE_TYPES.size*5*2
|
81
|
+
|
82
|
+
# Quick check of role metadata:
|
83
|
+
roles.each do |role_name, role|
|
84
|
+
role.object_type.modspace.should == TestValueTypesModule
|
85
|
+
if !role.counterpart
|
86
|
+
role.should be_unary
|
87
|
+
else
|
88
|
+
role.counterpart.object_type.modspace.should == TestValueTypesModule
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "Object type role values" do
|
95
|
+
def object_identifying_parameters object_type_name, value
|
96
|
+
if object_type_name =~ /^(.*)EntitySubCtr$/
|
97
|
+
[{ :"id_#{$1.snakecase}_value" => value, :counter => :new}]
|
98
|
+
else
|
99
|
+
[value]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "Instantiating bare objects" do
|
104
|
+
OBJECT_TYPES.each do |object_type|
|
105
|
+
required_value_type = VALUE_TYPE_FOR_OBJECT_TYPE[object_type]
|
106
|
+
object_type_name = object_type.basename
|
107
|
+
values = VALUES_FOR_TYPE[required_value_type]
|
108
|
+
next unless values
|
109
|
+
|
110
|
+
it "should allow instantiation of a bare #{object_type_name}" do
|
111
|
+
object_identifying_parameters =
|
112
|
+
if object_type_name =~ /^(.*)EntitySubCtr$/
|
113
|
+
[{ :"id_#{$1.snakecase}_value" => values[0], :counter => :new}]
|
114
|
+
else
|
115
|
+
[values[0]]
|
116
|
+
end
|
117
|
+
object = object_type.new(*object_identifying_parameters)
|
118
|
+
object.class.should == object_type
|
119
|
+
object.constellation.should be_nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "A constellation" do
|
125
|
+
before :each do
|
126
|
+
@constellation = ActiveFacts::API::Constellation.new(TestValueTypesModule)
|
127
|
+
end
|
128
|
+
|
129
|
+
OBJECT_TYPES.each do |object_type|
|
130
|
+
required_value_type = VALUE_TYPE_FOR_OBJECT_TYPE[object_type]
|
131
|
+
object_type_name = object_type.basename
|
132
|
+
values = VALUES_FOR_TYPE[required_value_type]
|
133
|
+
|
134
|
+
it "should return an initially empty instance index collection for #{object_type_name}" do
|
135
|
+
@constellation.send(object_type_name).should be_empty
|
136
|
+
end
|
137
|
+
|
138
|
+
next unless values
|
139
|
+
|
140
|
+
it "should allow assertion of an #{object_type_name} instance using #{values[0].inspect}" do
|
141
|
+
# REVISIT: Assertion of a subtype having the same identifier as a supertype is... dodgey.
|
142
|
+
# What should it do? Migrate the previous object to its subtype?
|
143
|
+
object = @constellation.send(object_type_name, *object_identifying_parameters(object_type_name, values[0]))
|
144
|
+
|
145
|
+
# Make sure we got what we expected:
|
146
|
+
object.class.should == object_type
|
147
|
+
|
148
|
+
# Make sure the instance index contains this single object:
|
149
|
+
instances = @constellation.send(object_type_name)
|
150
|
+
instances.size.should == 1
|
151
|
+
instances.map{|k,o| o}.first.should == object
|
152
|
+
unless object.class.is_entity_type
|
153
|
+
# Look up value types using the value instance, not just the raw value:
|
154
|
+
instances[object].should == object
|
155
|
+
end
|
156
|
+
|
157
|
+
# Make sure all the identifying roles are populated correctly:
|
158
|
+
if object_type.respond_to?(:identifying_roles)
|
159
|
+
object.class.identifying_roles.each do |identifying_role|
|
160
|
+
identifying_value = object.send(identifying_role.name)
|
161
|
+
identifying_value.should_not be_nil
|
162
|
+
|
163
|
+
counterpart_object_type = identifying_role.counterpart.object_type
|
164
|
+
role_superclasses = [ counterpart_object_type.superclass, counterpart_object_type.superclass.superclass ]
|
165
|
+
# Autocounter values do not compare to Integers:
|
166
|
+
unless role_superclasses.include?(AutoCounter) or identifying_role.object_type.basename =~ /Entity/
|
167
|
+
identifying_value.should == identifying_role.object_type.new(*values[0])
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
if object_type.respond_to?(:identifying_roles)
|
174
|
+
# REVISIT: Here, there are many possible problems with re-assigning identifying role values. We need tests!
|
175
|
+
# The implementation will need to be reworked to detect problems and reverse any partial changes before chucking an exception
|
176
|
+
=begin
|
177
|
+
it "should not allow re-assigning a #{object_type_name} entity's identifying role value from #{values[0]} to #{values[1]}" do
|
178
|
+
object = @constellation.send(object_type_name, *object_identifying_parameters(object_type_name, values[0]))
|
179
|
+
object.class.identifying_roles.each do |identifying_role|
|
180
|
+
next if identifying_role.name == :counter
|
181
|
+
lambda {
|
182
|
+
object.send(:"#{identifying_role.name}=", values[1])
|
183
|
+
}.should raise_error
|
184
|
+
end
|
185
|
+
end
|
186
|
+
=end
|
187
|
+
|
188
|
+
it "should allow nullifying and reassigning a #{object_type_name} entity's identifying role value" do
|
189
|
+
object = @constellation.send(object_type_name, *object_identifying_parameters(object_type_name, values[0]))
|
190
|
+
object.class.identifying_roles.each do |identifying_role|
|
191
|
+
next if identifying_role.name == :counter
|
192
|
+
assigned = object.send(:"#{identifying_role.name}=", nil)
|
193
|
+
assigned.should be_nil
|
194
|
+
object.send(:"#{identifying_role.name}=", values[1])
|
195
|
+
end
|
196
|
+
end
|
197
|
+
else
|
198
|
+
it "should allow initialising value type #{object_type.name} with an instance of that value type" do
|
199
|
+
bare_value = object_type.new(*object_identifying_parameters(object_type_name, values[0]))
|
200
|
+
object = @constellation.send(object_type_name, bare_value)
|
201
|
+
|
202
|
+
# Now link the bare value to an Octopus:
|
203
|
+
octopus = @constellation.Octopus(0)
|
204
|
+
octopus_role_name = :"octopus_as_one_#{object_type_name.snakecase}"
|
205
|
+
bare_value.send(:"#{octopus_role_name}=", octopus)
|
206
|
+
counterpart_name = bare_value.class.roles[octopus_role_name].counterpart.name
|
207
|
+
|
208
|
+
# Create a reference by assigning the object from a RoleProxy:
|
209
|
+
proxy = octopus.send(counterpart_name)
|
210
|
+
#proxy.should be_respond_to(:__getobj__)
|
211
|
+
object2 = @constellation.send(object_type_name, proxy)
|
212
|
+
object2.should == object
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
describe "Role values" do
|
220
|
+
before :each do
|
221
|
+
@constellation = ActiveFacts::API::Constellation.new(TestValueTypesModule)
|
222
|
+
@object = @constellation.Octopus(0)
|
223
|
+
@roles = @object.class.roles
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should return its constellation and vocabulary" do
|
227
|
+
# Strictly, these are not role value tests
|
228
|
+
@object.constellation.should == @constellation
|
229
|
+
@object.constellation.vocabulary.should == TestValueTypesModule
|
230
|
+
@object.class.vocabulary.should == TestValueTypesModule
|
231
|
+
end
|
232
|
+
|
233
|
+
TestValueTypesModule::Octopus.roles.each do |role_name, role|
|
234
|
+
next if role_name == :zero
|
235
|
+
|
236
|
+
it "should respond to getting its #{role_name} role" do
|
237
|
+
@object.should be_respond_to role.name
|
238
|
+
end
|
239
|
+
|
240
|
+
it "should respond to setting its #{role_name} role" do
|
241
|
+
@object.should be_respond_to :"#{role.name}="
|
242
|
+
end
|
243
|
+
|
244
|
+
if role.unary?
|
245
|
+
it "should allow its #{role_name} unary role to be assigned and reassigned" do
|
246
|
+
@object.has_a_unary.should be_nil
|
247
|
+
@object.has_a_unary = true
|
248
|
+
@object.has_a_unary.should == true
|
249
|
+
@object.has_a_unary = 23
|
250
|
+
@object.has_a_unary.should == true
|
251
|
+
@object.has_a_unary = false
|
252
|
+
@object.has_a_unary.should be_false
|
253
|
+
@object.has_a_unary = nil
|
254
|
+
@object.has_a_unary.should be_nil
|
255
|
+
end
|
256
|
+
else
|
257
|
+
it "should allow its #{role_name} role to be assigned and reassigned a base value" do
|
258
|
+
object_type = role.counterpart.object_type
|
259
|
+
required_value_type = VALUE_TYPE_FOR_OBJECT_TYPE[object_type]
|
260
|
+
values = VALUES_FOR_TYPE[required_value_type]
|
261
|
+
next unless values
|
262
|
+
value = object_identifying_parameters(object_type.basename, values[0])
|
263
|
+
|
264
|
+
# Set the role to the first value:
|
265
|
+
assigned = @object.send(:"#{role_name}=", value)
|
266
|
+
assigned.class.should == object_type
|
267
|
+
fetched = @object.send(role_name)
|
268
|
+
fetched.should == assigned
|
269
|
+
|
270
|
+
if role.counterpart.unique # A one-to-one
|
271
|
+
# The counterpart should point back at us
|
272
|
+
assigned.send(role.counterpart.name).should == @object
|
273
|
+
else # A many-to-one
|
274
|
+
# The counterpart should include us in its RoleValues
|
275
|
+
reflection = assigned.send(role.counterpart.name)
|
276
|
+
reflection.should_not be_empty
|
277
|
+
reflection.size.should == 1
|
278
|
+
reflection.should be_include(@object)
|
279
|
+
end
|
280
|
+
|
281
|
+
# Update the role to the second value:
|
282
|
+
value = object_identifying_parameters(object_type.basename, values[1])
|
283
|
+
assigned2 = @object.send(:"#{role_name}=", value)
|
284
|
+
assigned2.class.should == object_type
|
285
|
+
fetched = @object.send(role_name)
|
286
|
+
fetched.should == assigned2
|
287
|
+
|
288
|
+
if role.counterpart.unique # A one-to-one
|
289
|
+
# REVISIT: The old counterpart role should be nullified
|
290
|
+
#assigned.send(role.counterpart.name).should be_nil
|
291
|
+
|
292
|
+
# The counterpart should point back at us
|
293
|
+
assigned2.send(role.counterpart.name).should == @object
|
294
|
+
else # A many-to-one
|
295
|
+
# REVISIT: The old counterpart RoleValues should be empty
|
296
|
+
reflection = assigned2.send(role.counterpart.name)
|
297
|
+
#reflection.size.should == 0
|
298
|
+
|
299
|
+
# The counterpart should include us in its RoleValues
|
300
|
+
reflection2 = assigned2.send(role.counterpart.name)
|
301
|
+
reflection2.size.should == 1
|
302
|
+
reflection2.should be_include(@object)
|
303
|
+
end
|
304
|
+
|
305
|
+
# Nullify the role
|
306
|
+
nullified = @object.send(:"#{role_name}=", nil)
|
307
|
+
nullified.should be_nil
|
308
|
+
if role.counterpart.unique # A one-to-one
|
309
|
+
assigned2.send(role.counterpart.name).should be_nil
|
310
|
+
else # A many-to-one
|
311
|
+
reflection3 = assigned2.send(role.counterpart.name)
|
312
|
+
reflection3.size.should == 0
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
it "should allow its #{role_name} role to be assigned and reassigned a base value" do
|
317
|
+
object_type = role.counterpart.object_type
|
318
|
+
required_value_type = VALUE_TYPE_FOR_OBJECT_TYPE[object_type]
|
319
|
+
values = VALUES_FOR_TYPE[required_value_type]
|
320
|
+
next unless values
|
321
|
+
value = object_identifying_parameters(object_type.basename, values[0])
|
322
|
+
|
323
|
+
# Set the role to the first value:
|
324
|
+
assigned = @object.send(:"#{role_name}=", value)
|
325
|
+
fetched = @object.send(role_name)
|
326
|
+
fetched.class.should == object_type
|
327
|
+
end
|
328
|
+
|
329
|
+
it "should allow its #{role_name} role to be assigned a value instance" do
|
330
|
+
object_type = role.counterpart.object_type
|
331
|
+
required_value_type = VALUE_TYPE_FOR_OBJECT_TYPE[object_type]
|
332
|
+
values = VALUES_FOR_TYPE[required_value_type]
|
333
|
+
next unless values
|
334
|
+
value = @constellation.send(object_type.basename, *object_identifying_parameters(object_type.basename, values[0]))
|
335
|
+
|
336
|
+
assigned = @object.send(:"#{role_name}=", value)
|
337
|
+
assigned.class.should == object_type
|
338
|
+
fetched = @object.send(role_name)
|
339
|
+
fetched.should == assigned
|
340
|
+
|
341
|
+
# Nullify the role
|
342
|
+
nullified = @object.send(:"#{role_name}=", nil)
|
343
|
+
nullified.should be_nil
|
344
|
+
end
|
345
|
+
|
346
|
+
it "should allow its #{role_name} role to be assigned a value subtype instance, retaining the subtype" do
|
347
|
+
object_type = role.counterpart.object_type
|
348
|
+
required_value_type = VALUE_TYPE_FOR_OBJECT_TYPE[object_type] # The raw value type
|
349
|
+
values = VALUES_FOR_TYPE[required_value_type]
|
350
|
+
object_type = VALUE_SUB_FOR_VALUE[object_type] # The value type subtype
|
351
|
+
next unless values and object_type
|
352
|
+
value = @constellation.send(object_type.basename, *object_identifying_parameters(object_type.basename, values[0]))
|
353
|
+
assigned = @object.send(:"#{role_name}=", value)
|
354
|
+
# This requires the declared type, not the subtype:
|
355
|
+
# assigned.class.should == role.counterpart.object_type
|
356
|
+
# This requires the subtype, as the test implies:
|
357
|
+
assigned.class.should == object_type
|
358
|
+
fetched = @object.send(role_name)
|
359
|
+
fetched.should == assigned
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
unless !role.counterpart or # A unary
|
364
|
+
role.counterpart.unique or # A one-to-one
|
365
|
+
VALUES_FOR_TYPE[VALUE_TYPE_FOR_OBJECT_TYPE[role.counterpart.object_type]] == nil
|
366
|
+
describe "Operations on #{role.counterpart.object_type.basename} RoleValues collections" do
|
367
|
+
before :each do
|
368
|
+
object_type = role.counterpart.object_type
|
369
|
+
required_value_type = VALUE_TYPE_FOR_OBJECT_TYPE[object_type]
|
370
|
+
values = VALUES_FOR_TYPE[required_value_type]
|
371
|
+
return unless values
|
372
|
+
value = object_identifying_parameters(object_type.basename, values[0])
|
373
|
+
assigned = @object.send(:"#{role_name}=", value)
|
374
|
+
@role_values = assigned.send(role.counterpart.name)
|
375
|
+
end
|
376
|
+
|
377
|
+
it "should support Array addition" do
|
378
|
+
added = @role_values + ["foo"]
|
379
|
+
added.class.should == Array
|
380
|
+
added.size.should == 2
|
381
|
+
end
|
382
|
+
|
383
|
+
it "should support Array subtraction" do
|
384
|
+
# We only added one value, so subtracting it leaves us empty
|
385
|
+
counterpart_value = @role_values.single
|
386
|
+
(@role_values - [counterpart_value]).should be_empty
|
387
|
+
end
|
388
|
+
|
389
|
+
it "should support each" do
|
390
|
+
count = 0
|
391
|
+
@role_values.each { |rv| count += 1 }
|
392
|
+
count.should == 1
|
393
|
+
end
|
394
|
+
|
395
|
+
it "should support detect" do
|
396
|
+
@role_values.detect { |rv| true }.should be_true
|
397
|
+
end
|
398
|
+
|
399
|
+
it "should verbalise" do
|
400
|
+
@role_values.verbalise.should =~ /Octopus.*Zero '0'/
|
401
|
+
end
|
402
|
+
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
end
|
407
|
+
|
408
|
+
end
|
409
|
+
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
# ActiveFacts tests: Roles of object_type 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 "Roles" do
|
@@ -9,7 +10,7 @@ describe "Roles" do
|
|
9
10
|
Object.send :remove_const, :Mod if Object.const_defined?("Mod")
|
10
11
|
module Mod
|
11
12
|
class Name < String
|
12
|
-
value_type
|
13
|
+
value_type :length => 40, :scale => 0, :restrict => /^[A-Z][a-z]*/
|
13
14
|
end
|
14
15
|
class LegalEntity
|
15
16
|
identified_by :name
|
@@ -23,9 +24,10 @@ describe "Roles" do
|
|
23
24
|
class Person < LegalEntity
|
24
25
|
# identified_by # No identifier needed, inherit from superclass
|
25
26
|
# New identifier:
|
26
|
-
identified_by :family, :
|
27
|
+
identified_by :family, :name
|
27
28
|
has_one :family, :class => Name
|
28
|
-
|
29
|
+
alias :given :name
|
30
|
+
alias :given= :name=
|
29
31
|
has_one :related_to, :class => LegalEntity
|
30
32
|
end
|
31
33
|
end
|
@@ -41,7 +43,13 @@ describe "Roles" do
|
|
41
43
|
end
|
42
44
|
role = Mod::Existing1.roles(:name)
|
43
45
|
role.should_not be_nil
|
44
|
-
role.
|
46
|
+
role.inspect.class.should == String
|
47
|
+
role.counterpart.object_type.should == Mod::Name
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should provide value type metadata" do
|
51
|
+
Mod::Name.length.should == 40
|
52
|
+
Mod::Name.scale.should == 0
|
45
53
|
end
|
46
54
|
|
47
55
|
it "should inject the respective role name into the matching object_type" do
|
@@ -51,6 +59,9 @@ describe "Roles" do
|
|
51
59
|
has_one :name
|
52
60
|
end
|
53
61
|
end
|
62
|
+
# REVISIT: need to make more tests for the class's role accessor methods:
|
63
|
+
Mod::Name.roles(:all_existing1).should == Mod::Name.all_existing1_role
|
64
|
+
|
54
65
|
Mod::Name.roles(:all_existing1).should_not be_nil
|
55
66
|
Mod::LegalEntity.roles(:all_contract_as_first).should_not be_nil
|
56
67
|
end
|
@@ -65,7 +76,7 @@ describe "Roles" do
|
|
65
76
|
# print "Mod::Existing2.roles = "; p Mod::Existing2.roles
|
66
77
|
r = Mod::Existing2.roles(:given_name)
|
67
78
|
r.should_not be_nil
|
68
|
-
|
79
|
+
r.counterpart.should be_nil
|
69
80
|
module Mod
|
70
81
|
class GivenName < String
|
71
82
|
value_type
|
@@ -74,7 +85,7 @@ describe "Roles" do
|
|
74
85
|
# puts "Should resolve now:"
|
75
86
|
r = Mod::Existing2.roles(:given_name)
|
76
87
|
r.should_not be_nil
|
77
|
-
r.
|
88
|
+
r.counterpart.object_type.should == Mod::GivenName
|
78
89
|
end
|
79
90
|
|
80
91
|
it "should handle subtyping a value type" do
|
@@ -86,8 +97,8 @@ describe "Roles" do
|
|
86
97
|
end
|
87
98
|
r = Mod::FamilyName.roles(:patriarch)
|
88
99
|
r.should_not be_nil
|
89
|
-
r.
|
90
|
-
r.
|
100
|
+
r.counterpart.object_type.should == Mod::Person
|
101
|
+
r.counterpart.object_type.roles(:family_name_as_patriarch).counterpart.object_type.should == Mod::FamilyName
|
91
102
|
end
|
92
103
|
|
93
104
|
it "should instantiate the matching object_type on assignment" do
|
@@ -114,11 +125,39 @@ describe "Roles" do
|
|
114
125
|
it "should instantiate subclasses sensibly" do
|
115
126
|
c = ActiveFacts::API::Constellation.new(Mod)
|
116
127
|
bloggs = c.LegalEntity("Bloggs & Co")
|
117
|
-
#pending
|
118
128
|
p = c.Person("Fred", "Bloggs")
|
119
129
|
p.related_to = "Bloggs & Co"
|
120
130
|
p.related_to.should be_is_a(Mod::LegalEntity)
|
121
|
-
|
131
|
+
p.related_to.should == bloggs
|
132
|
+
|
133
|
+
# REVISIT: The raw instance doesn't override == to compare itself to a RoleProxy unfortunately...
|
134
|
+
# So this test succeeds when we'd like it to fail
|
135
|
+
#bloggs.should_not == p.related_to
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should forward missing methods on the role proxies" do
|
139
|
+
c = ActiveFacts::API::Constellation.new(Mod)
|
140
|
+
p = c.Person("Fred", "Bloggs")
|
141
|
+
|
142
|
+
# Make sure that RoleProxy's method_missing delegates, then forwards the send
|
143
|
+
lambda {
|
144
|
+
p.family.foo
|
145
|
+
}.should raise_error(NoMethodError)
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should forward re-raise exceptions from missing methods on the role proxies" do
|
149
|
+
c = ActiveFacts::API::Constellation.new(Mod)
|
150
|
+
p = c.Person("Fred", "Bloggs")
|
151
|
+
|
152
|
+
# x = p.family.__getobj__
|
153
|
+
#def x.barf
|
154
|
+
# raise "Yawning..."
|
155
|
+
#end
|
156
|
+
lambda {
|
157
|
+
p.family.barf
|
158
|
+
#}.should raise_error(RuntimeError)
|
159
|
+
}.should raise_error(NoMethodError)
|
160
|
+
|
122
161
|
end
|
123
162
|
|
124
163
|
end
|