activefacts 0.6.0

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.
Files changed (84) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +83 -0
  3. data/README.rdoc +81 -0
  4. data/Rakefile +41 -0
  5. data/bin/afgen +46 -0
  6. data/bin/cql +52 -0
  7. data/examples/CQL/Address.cql +46 -0
  8. data/examples/CQL/Blog.cql +54 -0
  9. data/examples/CQL/CompanyDirectorEmployee.cql +51 -0
  10. data/examples/CQL/Death.cql +16 -0
  11. data/examples/CQL/Genealogy.cql +95 -0
  12. data/examples/CQL/Marriage.cql +18 -0
  13. data/examples/CQL/Metamodel.cql +238 -0
  14. data/examples/CQL/MultiInheritance.cql +19 -0
  15. data/examples/CQL/OilSupply.cql +47 -0
  16. data/examples/CQL/Orienteering.cql +108 -0
  17. data/examples/CQL/PersonPlaysGame.cql +17 -0
  18. data/examples/CQL/SchoolActivities.cql +31 -0
  19. data/examples/CQL/SimplestUnary.cql +12 -0
  20. data/examples/CQL/SubtypePI.cql +32 -0
  21. data/examples/CQL/Warehousing.cql +99 -0
  22. data/examples/CQL/WindowInRoomInBldg.cql +22 -0
  23. data/lib/activefacts.rb +10 -0
  24. data/lib/activefacts/api.rb +25 -0
  25. data/lib/activefacts/api/concept.rb +384 -0
  26. data/lib/activefacts/api/constellation.rb +106 -0
  27. data/lib/activefacts/api/entity.rb +239 -0
  28. data/lib/activefacts/api/instance.rb +54 -0
  29. data/lib/activefacts/api/numeric.rb +158 -0
  30. data/lib/activefacts/api/role.rb +94 -0
  31. data/lib/activefacts/api/standard_types.rb +67 -0
  32. data/lib/activefacts/api/support.rb +59 -0
  33. data/lib/activefacts/api/value.rb +122 -0
  34. data/lib/activefacts/api/vocabulary.rb +120 -0
  35. data/lib/activefacts/cql.rb +31 -0
  36. data/lib/activefacts/cql/CQLParser.treetop +104 -0
  37. data/lib/activefacts/cql/Concepts.treetop +112 -0
  38. data/lib/activefacts/cql/DataTypes.treetop +66 -0
  39. data/lib/activefacts/cql/Expressions.treetop +113 -0
  40. data/lib/activefacts/cql/FactTypes.treetop +185 -0
  41. data/lib/activefacts/cql/Language/English.treetop +92 -0
  42. data/lib/activefacts/cql/LexicalRules.treetop +169 -0
  43. data/lib/activefacts/cql/Rakefile +6 -0
  44. data/lib/activefacts/cql/parser.rb +88 -0
  45. data/lib/activefacts/generate/absorption.rb +87 -0
  46. data/lib/activefacts/generate/cql.rb +441 -0
  47. data/lib/activefacts/generate/cql/html.rb +397 -0
  48. data/lib/activefacts/generate/null.rb +19 -0
  49. data/lib/activefacts/generate/ordered.rb +557 -0
  50. data/lib/activefacts/generate/ruby.rb +326 -0
  51. data/lib/activefacts/generate/sql/server.rb +164 -0
  52. data/lib/activefacts/generate/text.rb +21 -0
  53. data/lib/activefacts/input/cql.rb +1268 -0
  54. data/lib/activefacts/input/orm.rb +926 -0
  55. data/lib/activefacts/persistence.rb +1 -0
  56. data/lib/activefacts/persistence/composition.rb +653 -0
  57. data/lib/activefacts/support.rb +51 -0
  58. data/lib/activefacts/version.rb +3 -0
  59. data/lib/activefacts/vocabulary.rb +6 -0
  60. data/lib/activefacts/vocabulary/extensions.rb +343 -0
  61. data/lib/activefacts/vocabulary/metamodel.rb +303 -0
  62. data/script/txt2html +71 -0
  63. data/spec/absorption_spec.rb +95 -0
  64. data/spec/api/autocounter.rb +82 -0
  65. data/spec/api/constellation.rb +130 -0
  66. data/spec/api/entity_type.rb +101 -0
  67. data/spec/api/instance.rb +428 -0
  68. data/spec/api/roles.rb +122 -0
  69. data/spec/api/value_type.rb +112 -0
  70. data/spec/api_spec.rb +14 -0
  71. data/spec/cql_cql_spec.rb +58 -0
  72. data/spec/cql_parse_spec.rb +31 -0
  73. data/spec/cql_ruby_spec.rb +60 -0
  74. data/spec/cql_sql_spec.rb +54 -0
  75. data/spec/cql_symbol_tables_spec.rb +259 -0
  76. data/spec/cql_unit_spec.rb +336 -0
  77. data/spec/cqldump_spec.rb +169 -0
  78. data/spec/norma_cql_spec.rb +48 -0
  79. data/spec/norma_ruby_spec.rb +50 -0
  80. data/spec/norma_sql_spec.rb +45 -0
  81. data/spec/norma_tables_spec.rb +94 -0
  82. data/spec/spec.opts +1 -0
  83. data/spec/spec_helper.rb +10 -0
  84. metadata +173 -0
@@ -0,0 +1,428 @@
1
+ #
2
+ # ActiveFacts tests: Value instances in the Runtime API
3
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
+ #
5
+ describe "An instance of every type of Concept" do
6
+ setup do
7
+ Object.send :remove_const, :Mod if Object.const_defined?("Mod")
8
+ module Mod
9
+ # These are the base value types we're going to test:
10
+ @base_types = [
11
+ Int, Real, AutoCounter, String, Date, DateTime
12
+ ]
13
+
14
+ # Construct the names of the roles they play:
15
+ @base_type_roles = @base_types.map do |t|
16
+ t.name.snakecase
17
+ end
18
+ @role_names = @base_type_roles.inject([]) {|a, t|
19
+ a << :"#{t}_value"
20
+ } +
21
+ @base_type_roles.inject([]) {|a, t|
22
+ a << :"#{t}_sub_value"
23
+ }
24
+
25
+ # Create a value type and a subtype of that value type for each base type:
26
+ @base_types.each do |base_type|
27
+ eval %Q{
28
+ class #{base_type.name}Value < #{base_type.name}
29
+ value_type
30
+ end
31
+
32
+ class #{base_type.name}SubValue < #{base_type.name}Value
33
+ # Note no new "value_type" is required here, it comes through inheritance
34
+ end
35
+ }
36
+ end
37
+
38
+ # Create a TestByX, TestByXSub, and TestSubByX class for all base types X
39
+ # Each class has a has_one and a one_to_one for all roles.
40
+ # and is identified by the has_one :x role
41
+ @base_types.each do |base_type|
42
+ eval %Q{
43
+ class TestBy#{base_type.name}
44
+ identified_by :#{base_type.name.snakecase}_value#{
45
+ @role_names.map do |role_name|
46
+ %Q{
47
+ has_one :#{role_name}
48
+ one_to_one :one_#{role_name}, #{role_name.to_s.camelcase(true)}}
49
+ end*""
50
+ }
51
+ end
52
+
53
+ class TestBy#{base_type.name}Sub
54
+ identified_by :#{base_type.name.snakecase}_sub_value#{
55
+ @role_names.map do |role_name|
56
+ %Q{
57
+ has_one :#{role_name}
58
+ one_to_one :one_#{role_name}, #{role_name.to_s.camelcase(true)}}
59
+ end*""
60
+ }
61
+ end
62
+
63
+ class TestSubBy#{base_type.name} < TestBy#{base_type.name}
64
+ # Entity subtypes, inherit identification and all roles
65
+ end
66
+
67
+ class TestBy#{base_type.name}Entity
68
+ identified_by :test_by_#{base_type.name.snakecase}
69
+ one_to_one :test_by_#{base_type.name.snakecase}
70
+ end}
71
+ end
72
+ end
73
+
74
+ # Simple Values
75
+ @int = 0
76
+ @real = 0.0
77
+ @auto_counter = 0
78
+ @new_auto_counter = :new
79
+ @string = "zero"
80
+ @date = [2008, 04, 19]
81
+ @date_time = [2008, 04, 19, 10, 28, 14]
82
+
83
+ # Value Type instances
84
+ @int_value = Mod::IntValue.new(1)
85
+ @real_value = Mod::RealValue.new(1.0)
86
+ @auto_counter_value = Mod::AutoCounterValue.new(1)
87
+ @new_auto_counter_value = Mod::AutoCounterValue.new(:new)
88
+ @string_value = Mod::StringValue.new("one")
89
+ @date_value = Mod::DateValue.new(2008, 04, 20)
90
+ @date_time_value = Mod::DateTimeValue.new(2008, 04, 20, 10, 28, 14)
91
+
92
+ # Value SubType instances
93
+ @int_sub_value = Mod::IntSubValue.new(4)
94
+ @real_sub_value = Mod::RealSubValue.new(4.0)
95
+ @auto_counter_sub_value = Mod::AutoCounterSubValue.new(4)
96
+ @auto_counter_sub_value_new = Mod::AutoCounterSubValue.new(:new)
97
+ @string_sub_value = Mod::StringSubValue.new("five")
98
+ @date_sub_value = Mod::DateSubValue.new(2008, 04, 25)
99
+ @date_time_sub_value = Mod::DateTimeSubValue.new(2008, 04, 26, 10, 28, 14)
100
+
101
+ # Entities identified by Value Type, SubType and Entity-by-value-type instances
102
+ @test_by_int = Mod::TestByInt.new(2)
103
+ @test_by_real = Mod::TestByReal.new(2.0)
104
+ @test_by_auto_counter = Mod::TestByAutoCounter.new(2)
105
+ @test_by_auto_counter_new = Mod::TestByAutoCounter.new(:new)
106
+ @test_by_string = Mod::TestByString.new("two")
107
+ @test_by_date = Mod::TestByDate.new(Date.new(2008,04,28))
108
+ #@test_by_date = Mod::TestByDate.new(2008,04,28)
109
+ @test_by_date_time = Mod::TestByDateTime.new(2008,04,28,10,28,15)
110
+ #@test_by_date_time = Mod::TestByDateTime.new(DateTime.new(2008,04,28,10,28,15))
111
+
112
+ @test_by_int_sub = Mod::TestByIntSub.new(2)
113
+ @test_by_real_sub = Mod::TestByRealSub.new(5.0)
114
+ @test_by_auto_counter_sub = Mod::TestByAutoCounterSub.new(6)
115
+ @test_by_auto_counter_new_sub = Mod::TestByAutoCounterSub.new(:new)
116
+ @test_by_string_sub = Mod::TestByStringSub.new("six")
117
+ @test_by_date_sub = Mod::TestByDateSub.new(Date.new(2008,04,27))
118
+ @test_by_date_time_sub = Mod::TestByDateTimeSub.new(2008,04,29,10,28,15)
119
+
120
+ @test_by_int_entity = Mod::TestByIntEntity.new(@test_by_int)
121
+ @test_by_real_entity = Mod::TestByRealEntity.new(@test_by_real)
122
+ @test_by_auto_counter_entity = Mod::TestByAutoCounterEntity.new(@test_by_auto_counter)
123
+ @test_by_auto_counter_new_entity = Mod::TestByAutoCounterEntity.new(@test_by_auto_counter_new)
124
+ @test_by_string_entity = Mod::TestByStringEntity.new(@test_by_string)
125
+ @test_by_date_entity = Mod::TestByDateEntity.new(@test_by_date)
126
+ @test_by_date_time_entity = Mod::TestByDateTimeEntity.new(@test_by_date_time)
127
+
128
+ # Entity subtypes
129
+ @test_sub_by_int = Mod::TestSubByInt.new(2)
130
+ @test_sub_by_real = Mod::TestSubByReal.new(2.0)
131
+ @test_sub_by_auto_counter = Mod::TestSubByAutoCounter.new(2)
132
+ @test_sub_by_auto_counter_new = Mod::TestSubByAutoCounter.new(:new)
133
+ @test_sub_by_string = Mod::TestSubByString.new("two")
134
+ @test_sub_by_date = Mod::TestSubByDate.new(Date.new(2008,04,28))
135
+ @test_sub_by_date_time = Mod::TestSubByDateTime.new(2008,04,28,10,28,15)
136
+
137
+ # These arrays get zipped together in various ways. Keep them aligned.
138
+ @values = [
139
+ @int, @real, @auto_counter, @new_auto_counter,
140
+ @string, @date, @date_time,
141
+ ]
142
+ @classes = [
143
+ Int, Real, AutoCounter, AutoCounter,
144
+ String, Date, DateTime,
145
+ ]
146
+ @value_types = [
147
+ Mod::IntValue, Mod::RealValue, Mod::AutoCounterValue, Mod::AutoCounterValue,
148
+ Mod::StringValue, Mod::DateValue, Mod::DateTimeValue,
149
+ Mod::IntSubValue, Mod::RealSubValue, Mod::AutoCounterSubValue, Mod::AutoCounterSubValue,
150
+ Mod::StringSubValue, Mod::DateSubValue, Mod::DateTimeSubValue,
151
+ ]
152
+ @value_instances = [
153
+ @int_value, @real_value, @auto_counter_value, @new_auto_counter_value,
154
+ @string_value, @date_value, @date_time_value,
155
+ @int_sub_value, @real_sub_value, @auto_counter_sub_value, @auto_counter_sub_value_new,
156
+ @string_sub_value, @date_sub_value, @date_time_sub_value,
157
+ @int_value, @real_value, @auto_counter_value, @new_auto_counter_value,
158
+ @string_value, @date_value, @date_time_value,
159
+ ]
160
+ @entity_types = [
161
+ Mod::TestByInt, Mod::TestByReal, Mod::TestByAutoCounter, Mod::TestByAutoCounter,
162
+ Mod::TestByString, Mod::TestByDate, Mod::TestByDateTime,
163
+ Mod::TestByIntSub, Mod::TestByRealSub, Mod::TestByAutoCounterSub, Mod::TestByAutoCounterSub,
164
+ Mod::TestByStringSub, Mod::TestByDateSub, Mod::TestByDateTimeSub,
165
+ Mod::TestSubByInt, Mod::TestSubByReal, Mod::TestSubByAutoCounter, Mod::TestSubByAutoCounter,
166
+ Mod::TestSubByString, Mod::TestSubByDate, Mod::TestSubByDateTime,
167
+ ]
168
+ @entities = [
169
+ @test_by_int, @test_by_real, @test_by_auto_counter, @test_by_auto_counter_new,
170
+ @test_by_string, @test_by_date, @test_by_date_time,
171
+ @test_by_int_sub, @test_by_real_sub, @test_by_auto_counter_sub, @test_by_auto_counter_new_sub,
172
+ @test_by_string_sub, @test_by_date_sub, @test_by_date_time_sub,
173
+ @test_sub_by_int, @test_sub_by_real, @test_sub_by_auto_counter, @test_sub_by_auto_counter_new,
174
+ @test_sub_by_string, @test_sub_by_date, @test_sub_by_date_time,
175
+ ]
176
+ @entities_by_entity = [
177
+ @test_by_int_entity,
178
+ @test_by_real_entity,
179
+ @test_by_auto_counter_entity,
180
+ @test_by_auto_counter_new_entity,
181
+ @test_by_string_entity,
182
+ @test_by_date_entity,
183
+ @test_by_date_time_entity,
184
+ ]
185
+ @entities_by_entity_types = [
186
+ Mod::TestByIntEntity, Mod::TestByRealEntity, Mod::TestByAutoCounterEntity, Mod::TestByAutoCounterEntity,
187
+ Mod::TestByStringEntity, Mod::TestByDateEntity, Mod::TestByDateTimeEntity,
188
+ ]
189
+ @test_role_names = [
190
+ :int_value, :real_value, :auto_counter_value, :auto_counter_value,
191
+ :string_value, :date_value, :date_time_value,
192
+ :int_sub_value, :real_sub_value, :auto_counter_sub_value, :auto_counter_sub_value,
193
+ :string_sub_value, :date_sub_value, :date_time_sub_value
194
+ ]
195
+ @role_values = [
196
+ 3, 3.0, 6, 7,
197
+ "three", Date.new(2008,4,21), DateTime.new(2008,4,22,10,28,16),
198
+ ]
199
+ @subtype_role_instances = [
200
+ Mod::IntSubValue.new(6), Mod::RealSubValue.new(6.0),
201
+ Mod::AutoCounterSubValue.new(:new), Mod::AutoCounterSubValue.new(8),
202
+ Mod::StringSubValue.new("seven"),
203
+ Mod::DateSubValue.new(2008,4,29), Mod::DateTimeSubValue.new(2008,4,30,10,28,16)
204
+ ]
205
+ end
206
+
207
+ it "if a value type, should verbalise" do
208
+ @value_types.each do |value_type|
209
+ #puts "#{value_type} verbalises as #{value_type.verbalise}"
210
+ value_type.respond_to?(:verbalise).should be_true
211
+ verbalisation = value_type.verbalise
212
+ verbalisation.should =~ %r{\b#{value_type.basename}\b}
213
+ verbalisation.should =~ %r{\b#{value_type.superclass.basename}\b}
214
+ end
215
+ end
216
+
217
+ it "if an entity type, should verbalise" do
218
+ @entity_types.each do |entity_type|
219
+ #puts entity_type.verbalise
220
+ entity_type.respond_to?(:verbalise).should be_true
221
+ verbalisation = entity_type.verbalise
222
+ verbalisation.should =~ %r{\b#{entity_type.basename}\b}
223
+
224
+ # All identifying roles should be in the verbalisation.
225
+ # Strictly this should be the role name, but we don't set names here.
226
+ entity_type.identifying_roles.each do |ir|
227
+ role = entity_type.roles(ir)
228
+ role.should_not be_nil
229
+ player = role.player
230
+ verbalisation.should =~ %r{\b#{player.basename}\b}
231
+ end
232
+ end
233
+ end
234
+
235
+ it "if a value, should verbalise" do
236
+ @value_instances.each do |value|
237
+ #puts value.verbalise
238
+ value.respond_to?(:verbalise).should be_true
239
+ verbalisation = value.verbalise
240
+ verbalisation.should =~ %r{\b#{value.class.basename}\b}
241
+ end
242
+ end
243
+
244
+ it "if an entity, should respond to verbalise" do
245
+ (@entities+@entities_by_entity).each do |entity|
246
+ #puts entity.verbalise
247
+ entity.respond_to?(:verbalise).should be_true
248
+ verbalisation = entity.verbalise
249
+ verbalisation.should =~ %r{\b#{entity.class.basename}\b}
250
+ entity.class.identifying_roles.each do |ir|
251
+ role = entity.class.roles(ir)
252
+ role.should_not be_nil
253
+ player = role.player
254
+ verbalisation.should =~ %r{\b#{player.basename}\b}
255
+ end
256
+ end
257
+ end
258
+
259
+ it "should respond to constellation" do
260
+ (@value_instances+@entities+@entities_by_entity).each do |instance|
261
+ instance.respond_to?(:constellation).should be_true
262
+ end
263
+ end
264
+
265
+ it "should respond to all its roles" do
266
+ @entities.each do |entity|
267
+ @test_role_names.each do |role_name|
268
+ entity.respond_to?(role_name).should be_true
269
+ entity.respond_to?(:"#{role_name}=").should be_true
270
+ entity.respond_to?(:"one_#{role_name}").should be_true
271
+ entity.respond_to?(:"one_#{role_name}=").should be_true
272
+ end
273
+ end
274
+ @entities_by_entity.each do |entity|
275
+ role = entity.class.roles(entity.class.identifying_roles[0])
276
+ role_name = role.name
277
+ entity.respond_to?(role_name).should be_true
278
+ entity.respond_to?(:"#{role_name}=").should be_true
279
+ end
280
+ end
281
+
282
+ it "should return the Concept in response to .class()" do
283
+ @value_types.zip(@value_instances).each do |concept, instance|
284
+ instance.class.should == concept
285
+ end
286
+ @entity_types.zip(@entities).each do |concept, instance|
287
+ instance.class.should == concept
288
+ end
289
+ @entities_by_entity_types.zip(@entities_by_entity).each do |concept, instance|
290
+ instance.class.should == concept
291
+ end
292
+ end
293
+
294
+ it "should return the module in response to .vocabulary()" do
295
+ (@value_types+@entity_types).zip((@value_instances+@entities+@entities_by_entity)).each do |concept, instance|
296
+ instance.class.vocabulary.should == Mod
297
+ end
298
+ end
299
+
300
+ it "each entity type should be able to be constructed using simple values" do
301
+ @entity_types.zip(@values+@values+@values, @classes+@classes+@classes).each do |entity_type, value, klass|
302
+ # An identifier parameter can be an array containing a simple value too
303
+ [ value,
304
+ Array === value ? nil : [value],
305
+ # entity_type.new(value) # REVISIT: It's not yet the case that an instance of the correct type can be used as a constructor parameter
306
+ ].compact.each do |value|
307
+ e = nil
308
+ lambda {
309
+ #puts "Constructing #{entity_type} using #{value.class} #{value.inspect}:"
310
+ e = entity_type.new(value)
311
+ }.should_not raise_error
312
+ # Verify that the identifying role has a equivalent value (except AutoCounter):
313
+ role_name = entity_type.identifying_roles[0]
314
+ role = entity_type.roles(role_name)
315
+ player = role.player
316
+ player_superclasses = [ player.superclass, player.superclass.superclass ]
317
+ e.send(role_name).should == klass.new(*value) unless player_superclasses.include?(AutoCounter)
318
+ end
319
+ end
320
+ end
321
+
322
+ it "that are entities should not allow its identifying roles to be assigned" do
323
+ pending
324
+ @entities.zip(@test_role_names, @role_values).each do |entity, role_name, value|
325
+ lambda {
326
+ entity.send(:"#{role_name}=", value)
327
+ }.should raise_error
328
+ end
329
+ @entities_by_entity.each do |entity|
330
+ role = entity.class.roles(entity.class.identifying_roles[0])
331
+ role_name = role.name
332
+ lambda {
333
+ entity.send(:"#{role_name}=", nil)
334
+ }.should raise_error
335
+ end
336
+ end
337
+
338
+ it "should allow its non-identifying roles to be assigned values" do
339
+ @entities.zip(@test_role_names).each do |entity, identifying_role|
340
+ @test_role_names.zip(@role_values).each do |role_name, value|
341
+ next if role_name == identifying_role
342
+ lambda {
343
+ entity.send(:"#{role_name}=", value)
344
+ }.should_not raise_error
345
+ lambda {
346
+ entity.send(:"one_#{role_name}=", value)
347
+ }.should_not raise_error
348
+ end
349
+ end
350
+ end
351
+
352
+ it "should allow its non-identifying roles to be assigned instances" do
353
+ @entities.zip(@test_role_names).each do |entity, identifying_role|
354
+ @test_role_names.zip(@value_types, @role_values).each do |role_name, klass, value|
355
+ next unless value
356
+ next if role_name == identifying_role
357
+ instance = klass.new(value)
358
+ lambda {
359
+ entity.send(:"#{role_name}=", instance)
360
+ }.should_not raise_error
361
+ entity.send(role_name).class.should == klass
362
+ lambda {
363
+ entity.send(:"one_#{role_name}=", instance)
364
+ }.should_not raise_error
365
+ entity.send(:"one_#{role_name}").class.should == klass
366
+ end
367
+ end
368
+ end
369
+
370
+ it "should allow its non-identifying roles to be assigned instances of value subtypes, retaining the subtype" do
371
+ @entities.zip(@test_role_names).each do |entity, identifying_role|
372
+ @test_role_names.zip(@subtype_role_instances).each do |role_name, instance|
373
+ next unless instance
374
+ next if role_name == identifying_role
375
+ lambda {
376
+ entity.send(:"#{role_name}=", instance)
377
+ }.should_not raise_error
378
+ entity.send(role_name).class.should == instance.class
379
+ lambda {
380
+ entity.send(:"one_#{role_name}=", instance)
381
+ }.should_not raise_error
382
+ entity.send(:"one_#{role_name}").class.should == instance.class
383
+ end
384
+ end
385
+ end
386
+
387
+ it "should add to has_one's conterpart role when non-identifying roles are assigned values" do
388
+ @entities.zip(@test_role_names).each do |entity, identifying_role|
389
+ @test_role_names.zip(@role_values).each do |role_name, value|
390
+ next if role_name == identifying_role or !value
391
+
392
+ # Test the has_one role:
393
+ role = entity.class.roles(role_name)
394
+ old_counterpart = entity.send(:"#{role_name}")
395
+ entity.send(:"#{role_name}=", value)
396
+ counterpart = entity.send(:"#{role_name}")
397
+ old_counterpart.should_not == counterpart
398
+ counterpart.send(role.counterpart.name).should be_include(entity)
399
+ old_counterpart.send(role.counterpart.name).should_not be_include(entity) if old_counterpart
400
+
401
+ # Test the one_to_one role:
402
+ role = entity.class.roles(:"one_#{role_name}")
403
+ old_counterpart = entity.send(:"one_#{role_name}")
404
+ entity.send(:"one_#{role_name}=", value)
405
+ counterpart = entity.send(:"one_#{role_name}")
406
+ old_counterpart.should_not == counterpart # Make sure we changed it!
407
+ counterpart.send(role.counterpart.name).should == entity
408
+ old_counterpart.send(role.counterpart.name).should be_nil if old_counterpart
409
+ end
410
+ end
411
+ end
412
+
413
+ it "should allow its non-identifying roles to be assigned nil" do
414
+ @entities.zip(@test_role_names).each do |entity, identifying_role|
415
+ @test_role_names.zip(@role_values).each do |role_name, value|
416
+ next if role_name == identifying_role
417
+ entity.send(:"#{role_name}=", value)
418
+ lambda {
419
+ entity.send(:"#{role_name}=", nil)
420
+ }.should_not raise_error
421
+ lambda {
422
+ entity.send(:"one_#{role_name}=", nil)
423
+ }.should_not raise_error
424
+ end
425
+ end
426
+ end
427
+
428
+ end
data/spec/api/roles.rb ADDED
@@ -0,0 +1,122 @@
1
+ #
2
+ # ActiveFacts tests: Roles of concept classes in the Runtime API
3
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
+ #
5
+ describe "Roles" do
6
+ setup do
7
+ Object.send :remove_const, :Mod if Object.const_defined?("Mod")
8
+ module Mod
9
+ class Name < String
10
+ value_type
11
+ end
12
+ class LegalEntity
13
+ identified_by :name
14
+ has_one :name
15
+ end
16
+ class Contract
17
+ identified_by :first, :second
18
+ has_one :first, LegalEntity
19
+ has_one :second, LegalEntity
20
+ end
21
+ class Person < LegalEntity
22
+ # identified_by # No identifier needed, inherit from superclass
23
+ # New identifier:
24
+ identified_by :family, :given
25
+ has_one :family, Name
26
+ has_one :given, Name
27
+ has_one :related_to, LegalEntity
28
+ end
29
+ end
30
+ # print "concept: "; p Mod.concept
31
+ end
32
+
33
+ it "should associate a role name with a matching existing concept" do
34
+ module Mod
35
+ class Existing1 < String
36
+ value_type
37
+ has_one :name
38
+ end
39
+ end
40
+ role = Mod::Existing1.roles(:name)
41
+ role.should_not be_nil
42
+ role.player.should == Mod::Name
43
+ end
44
+
45
+ it "should inject the respective role name into the matching concept" do
46
+ module Mod
47
+ class Existing1 < String
48
+ value_type
49
+ has_one :name
50
+ end
51
+ end
52
+ Mod::Name.roles(:all_existing1).should_not be_nil
53
+ Mod::LegalEntity.roles(:all_contract_by_first).should_not be_nil
54
+ end
55
+
56
+ it "should associate a role name with a matching concept after it's created" do
57
+ module Mod
58
+ class Existing2 < String
59
+ value_type
60
+ has_one :given_name
61
+ end
62
+ end
63
+ # print "Mod::Existing2.roles = "; p Mod::Existing2.roles
64
+ r = Mod::Existing2.roles(:given_name)
65
+ r.should_not be_nil
66
+ Symbol.should === r.player
67
+ module Mod
68
+ class GivenName < String
69
+ value_type
70
+ end
71
+ end
72
+ # puts "Should resolve now:"
73
+ r = Mod::Existing2.roles(:given_name)
74
+ r.should_not be_nil
75
+ r.player.should == Mod::GivenName
76
+ end
77
+
78
+ it "should handle subtyping a value type" do
79
+ module Mod
80
+ class FamilyName < Name
81
+ value_type
82
+ one_to_one :patriarch, Person
83
+ end
84
+ end
85
+ r = Mod::FamilyName.roles(:patriarch)
86
+ r.should_not be_nil
87
+ r.player.should == Mod::Person
88
+ r.player.roles(:family_name).player.should == Mod::FamilyName
89
+ end
90
+
91
+ it "should instantiate the matching concept on assignment" do
92
+ c = ActiveFacts::API::Constellation.new(Mod)
93
+ bloggs = c.LegalEntity("Bloggs")
94
+ acme = c.LegalEntity("Acme, Inc")
95
+ contract = c.Contract("Bloggs", acme)
96
+ #contract = c.Contract("Bloggs", "Acme, Inc")
97
+ contract.first.should == bloggs
98
+ contract.second.should == acme
99
+ end
100
+
101
+ it "should append the player into the respective role array in the matching concept" do
102
+ foo = Mod::Name.new("Foo")
103
+ le = Mod::LegalEntity.new(foo)
104
+ le.respond_to?(:name).should be_true
105
+ name = le.name
106
+ name.respond_to?(:all_legal_entity).should be_true
107
+
108
+ #pending
109
+ name.all_legal_entity.should === [le]
110
+ end
111
+
112
+ it "should instantiate subclasses sensibly" do
113
+ c = ActiveFacts::API::Constellation.new(Mod)
114
+ bloggs = c.LegalEntity("Bloggs & Co")
115
+ #pending
116
+ p = c.Person("Fred", "Bloggs")
117
+ p.related_to = "Bloggs & Co"
118
+ Mod::LegalEntity.should === p.related_to
119
+ bloggs.object_id.should == p.related_to.object_id
120
+ end
121
+
122
+ end