activefacts 0.8.16 → 0.8.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +15 -0
  2. data/Manifest.txt +10 -4
  3. data/bin/afgen +26 -20
  4. data/bin/cql +1 -1
  5. data/css/orm2.css +89 -9
  6. data/examples/CQL/CompanyDirectorEmployee.cql +4 -4
  7. data/examples/CQL/Genealogy.cql +5 -5
  8. data/examples/CQL/Metamodel.cql +121 -91
  9. data/examples/CQL/MonthInSeason.cql +2 -6
  10. data/examples/CQL/SeparateSubtype.cql +11 -9
  11. data/examples/CQL/ServiceDirector.cql +21 -33
  12. data/examples/CQL/Supervision.cql +0 -3
  13. data/examples/CQL/WindowInRoomInBldg.cql +10 -4
  14. data/examples/CQL/unit.cql +1 -1
  15. data/lib/activefacts.rb +1 -0
  16. data/lib/activefacts/cql/CQLParser.treetop +5 -1
  17. data/lib/activefacts/cql/Context.treetop +2 -7
  18. data/lib/activefacts/cql/Expressions.treetop +2 -2
  19. data/lib/activefacts/cql/FactTypes.treetop +37 -31
  20. data/lib/activefacts/cql/Language/English.treetop +21 -4
  21. data/lib/activefacts/cql/LexicalRules.treetop +59 -1
  22. data/lib/activefacts/cql/ObjectTypes.treetop +22 -12
  23. data/lib/activefacts/cql/Terms.treetop +13 -9
  24. data/lib/activefacts/cql/ValueTypes.treetop +30 -11
  25. data/lib/activefacts/cql/compiler.rb +34 -5
  26. data/lib/activefacts/cql/compiler/clause.rb +207 -116
  27. data/lib/activefacts/cql/compiler/constraint.rb +129 -105
  28. data/lib/activefacts/cql/compiler/entity_type.rb +49 -27
  29. data/lib/activefacts/cql/compiler/expression.rb +71 -42
  30. data/lib/activefacts/cql/compiler/fact.rb +70 -64
  31. data/lib/activefacts/cql/compiler/fact_type.rb +108 -57
  32. data/lib/activefacts/cql/compiler/query.rb +178 -0
  33. data/lib/activefacts/cql/compiler/shared.rb +13 -12
  34. data/lib/activefacts/cql/compiler/value_type.rb +10 -4
  35. data/lib/activefacts/cql/nodes.rb +1 -1
  36. data/lib/activefacts/cql/parser.rb +6 -2
  37. data/lib/activefacts/generate/absorption.rb +6 -3
  38. data/lib/activefacts/generate/cql.rb +140 -84
  39. data/lib/activefacts/generate/dm.rb +12 -6
  40. data/lib/activefacts/generate/help.rb +25 -6
  41. data/lib/activefacts/generate/helpers/oo.rb +195 -0
  42. data/lib/activefacts/generate/helpers/ordered.rb +589 -0
  43. data/lib/activefacts/generate/helpers/rails.rb +57 -0
  44. data/lib/activefacts/generate/html/glossary.rb +274 -54
  45. data/lib/activefacts/generate/json.rb +25 -22
  46. data/lib/activefacts/generate/null.rb +1 -0
  47. data/lib/activefacts/generate/rails/models.rb +244 -0
  48. data/lib/activefacts/generate/rails/schema.rb +185 -0
  49. data/lib/activefacts/generate/records.rb +1 -0
  50. data/lib/activefacts/generate/ruby.rb +51 -30
  51. data/lib/activefacts/generate/sql/mysql.rb +5 -3
  52. data/lib/activefacts/generate/sql/server.rb +8 -4
  53. data/lib/activefacts/generate/text.rb +1 -0
  54. data/lib/activefacts/generate/transform/surrogate.rb +209 -0
  55. data/lib/activefacts/generate/version.rb +1 -0
  56. data/lib/activefacts/input/orm.rb +234 -181
  57. data/lib/activefacts/mapping/rails.rb +122 -0
  58. data/lib/activefacts/persistence/columns.rb +34 -18
  59. data/lib/activefacts/persistence/foreignkey.rb +129 -71
  60. data/lib/activefacts/persistence/index.rb +42 -12
  61. data/lib/activefacts/persistence/reference.rb +37 -23
  62. data/lib/activefacts/persistence/tables.rb +53 -19
  63. data/lib/activefacts/registry.rb +11 -0
  64. data/lib/activefacts/support.rb +28 -10
  65. data/lib/activefacts/version.rb +1 -1
  66. data/lib/activefacts/vocabulary/extensions.rb +246 -117
  67. data/lib/activefacts/vocabulary/metamodel.rb +105 -65
  68. data/lib/activefacts/vocabulary/verbaliser.rb +226 -194
  69. data/spec/absorption_spec.rb +1 -0
  70. data/spec/cql/comparison_spec.rb +8 -8
  71. data/spec/cql/contractions_spec.rb +16 -43
  72. data/spec/cql/entity_type_spec.rb +2 -1
  73. data/spec/cql/expressions_spec.rb +2 -2
  74. data/spec/cql/fact_type_matching_spec.rb +4 -1
  75. data/spec/cql/parser/bad_literals_spec.rb +30 -30
  76. data/spec/cql/parser/entity_types_spec.rb +6 -6
  77. data/spec/cql/parser/expressions_spec.rb +25 -19
  78. data/spec/cql/samples_spec.rb +5 -4
  79. data/spec/cql_cql_spec.rb +2 -1
  80. data/spec/cql_dm_spec.rb +4 -0
  81. data/spec/cql_mysql_spec.rb +4 -0
  82. data/spec/cql_parse_spec.rb +2 -0
  83. data/spec/cql_ruby_spec.rb +4 -0
  84. data/spec/cql_sql_spec.rb +4 -0
  85. data/spec/cqldump_spec.rb +7 -4
  86. data/spec/helpers/parse_to_ast_matcher.rb +7 -3
  87. data/spec/helpers/test_parser.rb +2 -0
  88. data/spec/norma_cql_spec.rb +5 -2
  89. data/spec/norma_ruby_spec.rb +4 -1
  90. data/spec/norma_ruby_sql_spec.rb +4 -1
  91. data/spec/norma_sql_spec.rb +4 -1
  92. data/spec/norma_tables_spec.rb +2 -2
  93. data/spec/ruby_api_spec.rb +1 -1
  94. data/spec/spec_helper.rb +2 -0
  95. data/spec/transform_surrogate_spec.rb +59 -0
  96. metadata +70 -60
  97. data/TODO +0 -308
  98. data/lib/activefacts/cql/compiler/join.rb +0 -162
  99. data/lib/activefacts/generate/oo.rb +0 -176
  100. data/lib/activefacts/generate/ordered.rb +0 -602
@@ -11,9 +11,13 @@ module ActiveFacts
11
11
  value_type
12
12
  end
13
13
 
14
+ class AggregateCode < String
15
+ value_type :length => 32
16
+ end
17
+
14
18
  class Assimilation < String
15
19
  value_type
16
- restrict 'partitioned', 'separate'
20
+ restrict 'absorbed', 'partitioned', 'separate'
17
21
  end
18
22
 
19
23
  class ContextNoteKind < String
@@ -58,6 +62,10 @@ module ActiveFacts
58
62
  value_type
59
63
  end
60
64
 
65
+ class ImplicationRuleName < String
66
+ value_type
67
+ end
68
+
61
69
  class Length < UnsignedInteger
62
70
  value_type :length => 32
63
71
  end
@@ -87,6 +95,10 @@ module ActiveFacts
87
95
  restrict 'feminine', 'masculine', 'neuter', 'personal'
88
96
  end
89
97
 
98
+ class RegularExpression < String
99
+ value_type
100
+ end
101
+
90
102
  class RingType < String
91
103
  value_type
92
104
  end
@@ -126,9 +138,15 @@ module ActiveFacts
126
138
  one_to_one :agent_name, :mandatory => true # See AgentName.agent
127
139
  end
128
140
 
141
+ class Aggregate
142
+ identified_by :aggregate_code
143
+ one_to_one :aggregate_code, :mandatory => true # See AggregateCode.aggregate
144
+ end
145
+
129
146
  class AlternativeSet
130
147
  identified_by :guid
131
148
  one_to_one :guid, :mandatory => true # See Guid.alternative_set
149
+ maybe :members_are_exclusive
132
150
  end
133
151
 
134
152
  class Coefficient
@@ -141,6 +159,7 @@ module ActiveFacts
141
159
  class Concept
142
160
  identified_by :guid
143
161
  one_to_one :guid, :mandatory => true # See Guid.concept
162
+ has_one :implication_rule # See ImplicationRule.all_concept
144
163
  end
145
164
 
146
165
  class Constraint < Concept
@@ -149,9 +168,9 @@ module ActiveFacts
149
168
  end
150
169
 
151
170
  class ContextNote < Concept
152
- has_one :concept # See Concept.all_context_note
153
171
  has_one :context_note_kind, :mandatory => true # See ContextNoteKind.all_context_note
154
172
  has_one :discussion, :mandatory => true # See Discussion.all_context_note
173
+ has_one :relevant_concept, :class => Concept # See Concept.all_context_note_as_relevant_concept
155
174
  end
156
175
 
157
176
  class Enforcement
@@ -169,7 +188,9 @@ module ActiveFacts
169
188
  class FactType < Concept
170
189
  end
171
190
 
172
- class ImplicitFactType < FactType
191
+ class ImplicationRule
192
+ identified_by :implication_rule_name
193
+ one_to_one :implication_rule_name, :mandatory => true # See ImplicationRuleName.implication_rule
173
194
  end
174
195
 
175
196
  class Instance < Concept
@@ -179,23 +200,13 @@ module ActiveFacts
179
200
  has_one :value # See Value.all_instance
180
201
  end
181
202
 
182
- class Join < Concept
183
- end
184
-
185
- class JoinNode
186
- identified_by :join, :ordinal
187
- has_one :join, :mandatory => true # See Join.all_join_node
188
- has_one :object_type, :mandatory => true # See ObjectType.all_join_node
189
- has_one :ordinal, :mandatory => true # See Ordinal.all_join_node
190
- has_one :role_name, :class => Name # See Name.all_join_node_as_role_name
191
- has_one :subscript # See Subscript.all_join_node
192
- has_one :value # See Value.all_join_node
203
+ class LinkFactType < FactType
193
204
  end
194
205
 
195
- class Position
206
+ class Location
196
207
  identified_by :x, :y
197
- has_one :x, :mandatory => true # See X.all_position
198
- has_one :y, :mandatory => true # See Y.all_position
208
+ has_one :x, :mandatory => true # See X.all_location
209
+ has_one :y, :mandatory => true # See Y.all_location
199
210
  end
200
211
 
201
212
  class PresenceConstraint < Constraint
@@ -206,9 +217,13 @@ module ActiveFacts
206
217
  has_one :role_sequence, :mandatory => true # See RoleSequence.all_presence_constraint
207
218
  end
208
219
 
220
+ class Query < Concept
221
+ end
222
+
209
223
  class Reading
210
224
  identified_by :fact_type, :ordinal
211
225
  has_one :fact_type, :mandatory => true # See FactType.all_reading
226
+ maybe :is_negative
212
227
  has_one :ordinal, :mandatory => true # See Ordinal.all_reading
213
228
  has_one :role_sequence, :mandatory => true # See RoleSequence.all_reading
214
229
  has_one :text, :mandatory => true # See Text.all_reading
@@ -223,7 +238,7 @@ module ActiveFacts
223
238
  class Role < Concept
224
239
  identified_by :fact_type, :ordinal
225
240
  has_one :fact_type, :mandatory => true # See FactType.all_role
226
- one_to_one :implicit_fact_type, :counterpart => :implying_role # See ImplicitFactType.implying_role
241
+ one_to_one :link_fact_type, :counterpart => :implying_role # See LinkFactType.implying_role
227
242
  has_one :object_type, :mandatory => true # See ObjectType.all_role
228
243
  has_one :ordinal, :mandatory => true # See Ordinal.all_role
229
244
  has_one :role_name, :class => Name # See Name.all_role_as_role_name
@@ -236,7 +251,7 @@ module ActiveFacts
236
251
  end
237
252
 
238
253
  class RoleValue
239
- identified_by :instance, :fact
254
+ identified_by :fact, :role
240
255
  has_one :fact, :mandatory => true # See Fact.all_role_value
241
256
  has_one :instance, :mandatory => true # See Instance.all_role_value
242
257
  has_one :population, :mandatory => true # See Population.all_role_value
@@ -248,10 +263,10 @@ module ActiveFacts
248
263
 
249
264
  class Shape
250
265
  identified_by :guid
251
- has_one :diagram, :mandatory => true # See Diagram.all_shape
252
266
  one_to_one :guid, :mandatory => true # See Guid.shape
253
267
  maybe :is_expanded
254
- has_one :position # See Position.all_shape
268
+ has_one :location # See Location.all_shape
269
+ has_one :orm_diagram, :class => "ORMDiagram", :mandatory => true # See ORMDiagram.all_shape
255
270
  end
256
271
 
257
272
  class SubsetConstraint < SetConstraint
@@ -270,21 +285,41 @@ module ActiveFacts
270
285
  end
271
286
 
272
287
  class Value
273
- identified_by :literal, :is_a_string, :unit
274
- maybe :is_a_string
288
+ identified_by :literal, :is_literal_string, :unit
289
+ maybe :is_literal_string
275
290
  has_one :literal, :mandatory => true # See Literal.all_value
276
291
  has_one :unit # See Unit.all_value
292
+ has_one :value_type, :mandatory => true # See ValueType.all_value
277
293
  end
278
294
 
279
295
  class ValueConstraint < Constraint
296
+ has_one :regular_expression # See RegularExpression.all_value_constraint
280
297
  one_to_one :role, :counterpart => :role_value_constraint # See Role.role_value_constraint
281
298
  end
282
299
 
300
+ class Variable
301
+ identified_by :query, :ordinal
302
+ has_one :object_type, :mandatory => true # See ObjectType.all_variable
303
+ has_one :ordinal, :mandatory => true # See Ordinal.all_variable
304
+ one_to_one :projection, :class => Role # See Role.variable_as_projection
305
+ has_one :query, :mandatory => true # See Query.all_variable
306
+ has_one :role_name, :class => Name # See Name.all_variable_as_role_name
307
+ has_one :subscript # See Subscript.all_variable
308
+ has_one :value # See Value.all_variable
309
+ end
310
+
283
311
  class Vocabulary
284
312
  identified_by :name
285
313
  one_to_one :name, :mandatory => true # See Name.vocabulary
286
314
  end
287
315
 
316
+ class Aggregation
317
+ identified_by :aggregate, :aggregated_variable
318
+ has_one :aggregate, :mandatory => true # See Aggregate.all_aggregation
319
+ has_one :aggregated_variable, :class => Variable, :mandatory => true # See Variable.all_aggregation_as_aggregated_variable
320
+ has_one :variable, :mandatory => true # See Variable.all_aggregation
321
+ end
322
+
288
323
  class Agreement
289
324
  identified_by :context_note
290
325
  one_to_one :context_note, :mandatory => true # See ContextNote.agreement
@@ -333,27 +368,13 @@ module ActiveFacts
333
368
  has_one :rotation_setting # See RotationSetting.all_fact_type_shape
334
369
  end
335
370
 
336
- class JoinRole
337
- identified_by :join_node, :role
338
- has_one :join_node, :mandatory => true # See JoinNode.all_join_role
339
- has_one :role, :mandatory => true # See Role.all_join_role
340
- has_one :join_step, :counterpart => :incidental_join_role # See JoinStep.all_incidental_join_role
341
- end
342
-
343
- class JoinStep
344
- identified_by :input_join_role, :output_join_role
345
- has_one :alternative_set # See AlternativeSet.all_join_step
346
- has_one :fact_type, :mandatory => true # See FactType.all_join_step
347
- has_one :input_join_role, :class => JoinRole, :mandatory => true # See JoinRole.all_join_step_as_input_join_role
348
- maybe :is_anti
349
- maybe :is_outer
350
- has_one :output_join_role, :class => JoinRole, :mandatory => true # See JoinRole.all_join_step_as_output_join_role
351
- end
352
-
353
371
  class ModelNoteShape < Shape
354
372
  has_one :context_note, :mandatory => true # See ContextNote.all_model_note_shape
355
373
  end
356
374
 
375
+ class ORMDiagram < Diagram
376
+ end
377
+
357
378
  class ObjectType < Concept
358
379
  identified_by :vocabulary, :name
359
380
  maybe :is_independent
@@ -368,10 +389,16 @@ module ActiveFacts
368
389
  end
369
390
 
370
391
  class ObjectifiedFactTypeNameShape < Shape
371
- identified_by :fact_type_shape
372
392
  one_to_one :fact_type_shape, :mandatory => true # See FactTypeShape.objectified_fact_type_name_shape
373
393
  end
374
394
 
395
+ class Play
396
+ identified_by :variable, :role
397
+ has_one :role, :mandatory => true # See Role.all_play
398
+ has_one :variable, :mandatory => true # See Variable.all_play
399
+ has_one :step, :counterpart => :incidental_play # See Step.all_incidental_play
400
+ end
401
+
375
402
  class Population < Concept
376
403
  identified_by :vocabulary, :name
377
404
  has_one :name, :mandatory => true # See Name.all_population
@@ -379,7 +406,6 @@ module ActiveFacts
379
406
  end
380
407
 
381
408
  class ReadingShape < Shape
382
- identified_by :fact_type_shape
383
409
  one_to_one :fact_type_shape, :mandatory => true # See FactTypeShape.reading_shape
384
410
  has_one :reading, :mandatory => true # See Reading.all_reading_shape
385
411
  end
@@ -404,8 +430,8 @@ module ActiveFacts
404
430
  has_one :ordinal, :mandatory => true # See Ordinal.all_role_ref
405
431
  has_one :role, :mandatory => true # See Role.all_role_ref
406
432
  has_one :role_sequence, :mandatory => true # See RoleSequence.all_role_ref
407
- one_to_one :join_role # See JoinRole.role_ref
408
433
  has_one :leading_adjective, :class => Adjective # See Adjective.all_role_ref_as_leading_adjective
434
+ one_to_one :play # See Play.role_ref
409
435
  has_one :trailing_adjective, :class => Adjective # See Adjective.all_role_ref_as_trailing_adjective
410
436
  end
411
437
 
@@ -426,6 +452,16 @@ module ActiveFacts
426
452
  maybe :is_mandatory
427
453
  end
428
454
 
455
+ class Step
456
+ identified_by :input_play, :output_play
457
+ has_one :alternative_set # See AlternativeSet.all_step
458
+ has_one :fact_type, :mandatory => true # See FactType.all_step
459
+ has_one :input_play, :class => Play, :mandatory => true # See Play.all_step_as_input_play
460
+ maybe :is_disallowed
461
+ maybe :is_optional
462
+ has_one :output_play, :class => Play # See Play.all_step_as_output_play
463
+ end
464
+
429
465
  class ValueConstraintShape < ConstraintShape
430
466
  has_one :object_type_shape # See ObjectTypeShape.all_value_constraint_shape
431
467
  one_to_one :role_display # See RoleDisplay.value_constraint_shape
@@ -437,49 +473,53 @@ module ActiveFacts
437
473
  has_one :minimum_bound, :class => Bound # See Bound.all_value_range_as_minimum_bound
438
474
  end
439
475
 
440
- class ValueType < ObjectType
441
- has_one :auto_assigned_transaction_timing, :class => TransactionTiming # See TransactionTiming.all_value_type_as_auto_assigned_transaction_timing
442
- has_one :length # See Length.all_value_type
443
- has_one :scale # See Scale.all_value_type
444
- has_one :supertype, :class => ValueType # See ValueType.all_value_type_as_supertype
445
- has_one :unit # See Unit.all_value_type
446
- one_to_one :value_constraint # See ValueConstraint.value_type
447
- end
448
-
449
476
  class AllowedRange
450
477
  identified_by :value_constraint, :value_range
451
478
  has_one :value_constraint, :mandatory => true # See ValueConstraint.all_allowed_range
452
479
  has_one :value_range, :mandatory => true # See ValueRange.all_allowed_range
453
480
  end
454
481
 
455
- class EntityType < ObjectType
482
+ class DomainObjectType < ObjectType
483
+ end
484
+
485
+ class EntityType < DomainObjectType
456
486
  one_to_one :fact_type # See FactType.entity_type
457
487
  maybe :is_implied_by_objectification
458
488
  end
459
489
 
490
+ class TypeInheritance < FactType
491
+ identified_by :subtype, :supertype
492
+ has_one :subtype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_subtype
493
+ has_one :supertype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_supertype
494
+ has_one :assimilation # See Assimilation.all_type_inheritance
495
+ maybe :provides_identification
496
+ end
497
+
498
+ class ValueType < DomainObjectType
499
+ has_one :length # See Length.all_value_type
500
+ has_one :scale # See Scale.all_value_type
501
+ has_one :supertype, :class => ValueType # See ValueType.all_value_type_as_supertype
502
+ has_one :transaction_timing # See TransactionTiming.all_value_type
503
+ has_one :unit # See Unit.all_value_type
504
+ one_to_one :value_constraint # See ValueConstraint.value_type
505
+ end
506
+
460
507
  class Facet
461
508
  identified_by :value_type, :name
462
509
  has_one :name, :mandatory => true # See Name.all_facet
463
510
  has_one :value_type, :mandatory => true # See ValueType.all_facet
511
+ has_one :facet_value_type, :class => ValueType, :mandatory => true # See ValueType.all_facet_as_facet_value_type
464
512
  end
465
513
 
466
- class FacetValue
514
+ class FacetRestriction
467
515
  identified_by :value_type, :facet
468
- has_one :facet, :mandatory => true # See Facet.all_facet_value
469
- has_one :value, :mandatory => true # See Value.all_facet_value
470
- has_one :value_type, :mandatory => true # See ValueType.all_facet_value
516
+ has_one :facet, :mandatory => true # See Facet.all_facet_restriction
517
+ has_one :value_type, :mandatory => true # See ValueType.all_facet_restriction
518
+ has_one :value, :mandatory => true # See Value.all_facet_restriction
471
519
  end
472
520
 
473
521
  class ImplicitBooleanValueType < ValueType
474
522
  end
475
523
 
476
- class TypeInheritance < FactType
477
- identified_by :subtype, :supertype
478
- has_one :subtype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_subtype
479
- has_one :supertype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_supertype
480
- has_one :assimilation # See Assimilation.all_type_inheritance
481
- maybe :provides_identification
482
- end
483
-
484
524
  end
485
525
  end
@@ -9,7 +9,7 @@ module ActiveFacts
9
9
  #
10
10
  # The Verbaliser fulfils two roles:
11
11
  # * Maintains verbalisation context to expand readings using subscripting where needed
12
- # * Verbalises Joins by iteratively choosing a Join Step and expanding readings
12
+ # * Verbalises Queries by iteratively choosing a Step and expanding readings
13
13
  #
14
14
  # The verbalisation context consists of a set of Players, each for one ObjectType.
15
15
  # There may be more than one Player for the same ObjectType. If adjectives or role
@@ -17,30 +17,30 @@ module ActiveFacts
17
17
  # Thus, the verbalisation context must be completely populated before subscript
18
18
  # generation, which must be before any Player name gets verbalised.
19
19
  #
20
- # When a Player occurs in a Join, it corresponds to one Join Node of that Join.
21
- # Each such Player has one or more JoinRoles, which refer to roles played by
22
- # that ObjectType. Where a join traverses two roles of a ternary fact type, there
23
- # will be a residual node that has only a single JoinRole with no other meaning.
24
- # A JoinRole must be for exactly one Player, so is used to identify a Player.
20
+ # When a Player occurs in a Query, it corresponds to one Variable of that Query.
21
+ # Each such Player has one or more Plays, which refer to roles played by
22
+ # that ObjectType. Where a query traverses two roles of a ternary fact type, there
23
+ # will be a residual node that has only a single Play with no other meaning.
24
+ # A Play must be for exactly one Player, so is used to identify a Player.
25
25
  #
26
- # When a Player occurs outside a Join, it's identified by a projected RoleRef.
26
+ # When a Player occurs outside a Query, it's identified by a projected RoleRef.
27
27
  # REVISIT: This is untrue when a uniqueness constraint is imported from NORMA.
28
- # In this case no join will be constructed to project the roles of the constrained
28
+ # In this case no query will be constructed to project the roles of the constrained
29
29
  # object type (only the constrained roles will be projected) - this will be fixed.
30
30
  #
31
31
  # Each constraint (except Ring Constraints) has one or more RoleSequence containing
32
- # the projected RoleRefs. Each constrained RoleSequence may have an associated Join.
33
- # If it has a Join, each RoleRef is projected from a JoinRole, otherwise none are.
32
+ # the projected RoleRefs. Each constrained RoleSequence may have an associated Query.
33
+ # If it has a Query, each RoleRef is projected from a Play, otherwise none are.
34
34
  #
35
- # The only type of join possible in a Ring Constraint is a subtyping join, which
35
+ # The only type of query possible in a Ring Constraint is a subtyping query, which
36
36
  # is always implicit and unambiguous, so is never instantiated.
37
37
  #
38
- # A constrained RoleSequence that has no explicit Join may have an implicit join,
39
- # as per ORM2, when the roles aren't in the same fact type. These implicit joins
38
+ # A constrained RoleSequence that has no explicit Query may have an implicit query,
39
+ # as per ORM2, when the roles aren't in the same fact type. These implicit queries
40
40
  # are over only one ObjectType, by traversing a single FactType (and possibly,
41
41
  # multiple TypeInheritance FactTypes) for each RoleRef. Note however that when
42
42
  # the ObjectType is an objectified Fact Type, the FactType traversed might be a
43
- # phantom of the objectification. In the case of implicit joins, each Player is
43
+ # phantom of the objectification. In the case of implicit queries, each Player is
44
44
  # identified by the projected RoleRef, except for the joined-over ObjectType whose
45
45
  # Player is... well, read the next paragraph!
46
46
  #
@@ -48,14 +48,14 @@ module ActiveFacts
48
48
  # respect to PresenceConstraints imported from NORMA (both external mandatory
49
49
  # and external uniqueness constraints). The joined-over Player in a UC is
50
50
  # identified by its RoleRefs in the RoleSequence of the Fact Type's preferred
51
- # reading. Subtyping joins in a mandatory constraint will probably malfunction.
52
- # However, all other such joins are expliciti, and these should be also.
51
+ # reading. Subtyping steps in a mandatory constraint will probably malfunction.
52
+ # However, all other such queries are explicit, and these should be also.
53
53
  #
54
54
  # For a SetComparisonConstraint, there are two or more constrained RoleSequences.
55
55
  # The matching RoleRefs (by Ordinal position) are for joined players, that is,
56
56
  # one individual instance plays both roles. The RoleRefs must (now) be for the
57
- # same ObjectType (no implicit subtyping Join is allowed). Instead, the input modules
58
- # find the closest common supertype and create explicit JoinSteps so its roles
57
+ # same ObjectType (no implicit subtyping step is allowed). Instead, the input modules
58
+ # find the closest common supertype and create explicit Steps so its roles
59
59
  # can be projected.
60
60
  #
61
61
  # When expanding Reading text however, the RoleRefs in the reading's RoleSequence
@@ -67,44 +67,44 @@ module ActiveFacts
67
67
  class Verbaliser
68
68
  # Verbalisation context:
69
69
  attr_reader :players
70
- attr_reader :player_by_join_role # Used for each join
71
- attr_reader :player_joined_over # Used when there's an implicit join
72
- attr_reader :player_by_role_ref # Used when a constrained role sequence has no join
70
+ attr_reader :player_by_play # Used for each query
71
+ attr_reader :player_joined_over # Used when there's an implicit query
72
+ attr_reader :player_by_role_ref # Used when a constrained role sequence has no query
73
73
 
74
74
  # The projected role references over which we're verbalising
75
75
  attr_reader :role_refs
76
76
 
77
- # Join Verbaliser context:
78
- attr_reader :join
79
- attr_reader :join_nodes # All Join Nodes
80
- attr_reader :join_steps # All remaining unemitted Join Steps
81
- attr_reader :join_steps_by_join_node # A Hash by Join Node containing an array of remaining steps
77
+ # Query Verbaliser context:
78
+ attr_reader :query
79
+ attr_reader :variables # All Variables
80
+ attr_reader :steps # All remaining unemitted Steps
81
+ attr_reader :steps_by_variable # A Hash by Variable containing an array of remaining steps
82
82
 
83
83
  def initialize role_refs = nil
84
84
  @role_refs = role_refs
85
85
 
86
86
  # Verbalisation context:
87
87
  @players = []
88
- @player_by_join_role = {}
88
+ @player_by_play = {}
89
89
  @player_by_role_ref = {}
90
90
  @player_joined_over = nil
91
91
 
92
- # Join Verbaliser context:
93
- @join = nil
94
- @join_nodes = []
95
- @join_steps = []
96
- @join_steps_by_join_node = {}
92
+ # Query Verbaliser context:
93
+ @query = nil
94
+ @variables = []
95
+ @steps = []
96
+ @steps_by_variable = {}
97
97
 
98
98
  add_role_refs role_refs if role_refs
99
99
  end
100
100
 
101
101
  class Player
102
- attr_accessor :object_type, :join_nodes_by_join, :subscript, :join_roles, :role_refs
102
+ attr_accessor :object_type, :variables_by_query, :subscript, :plays, :role_refs
103
103
  def initialize object_type
104
104
  @object_type = object_type
105
- @join_nodes_by_join = {}
105
+ @variables_by_query = {}
106
106
  @subscript = nil
107
- @join_roles = []
107
+ @plays = []
108
108
  @role_refs = []
109
109
  end
110
110
 
@@ -122,21 +122,21 @@ module ActiveFacts
122
122
  rr.trailing_adjective
123
123
  ].compact}.uniq.sort
124
124
  end
125
- adjuncts += [@join_nodes_by_join.values.map{|jn| jn.role_name}.compact[0]].compact
125
+ adjuncts += [@variables_by_query.values.map{|jn| jn.role_name}.compact[0]].compact
126
126
  adjuncts.flatten*"_"
127
127
  end
128
128
 
129
129
  def describe
130
- @object_type.name + (@join_nodes_by_join.size > 0 ? " (in #{@join_nodes_by_join.size} joins)" : "")
130
+ @object_type.name + (@variables_by_query.size > 0 ? " (in #{@variables_by_query.size} variables)" : "")
131
131
  end
132
132
  end
133
133
 
134
134
  # Find or create a Player to which we can add this role_ref
135
135
  def player(ref)
136
- existing_player = if ref.is_a?(ActiveFacts::Metamodel::JoinRole)
137
- @player_by_join_role[ref]
136
+ existing_player = if ref.is_a?(ActiveFacts::Metamodel::Play)
137
+ @player_by_play[ref]
138
138
  else
139
- @player_by_role_ref[ref] or ref.join_role && @player_by_join_role[ref.join_role]
139
+ @player_by_role_ref[ref] or ref.play && @player_by_play[ref.play]
140
140
  end
141
141
  if existing_player
142
142
  debug :player, "Using existing player for #{ref.role.object_type.name} #{ref.respond_to?(:role_sequence) && ref.role_sequence.all_reading.size > 0 ? ' in reading' : ''}in '#{ref.role.fact_type.default_reading}'"
@@ -149,22 +149,22 @@ module ActiveFacts
149
149
  end
150
150
  end
151
151
 
152
- def add_join_role player, join_role
153
- return if player.join_roles.include?(join_role)
154
- jn = join_role.join_node
155
- if jn1 = player.join_nodes_by_join[jn.join] and jn1 != jn
156
- raise "Player for #{player.object_type.name} may only have one join node per join, not #{jn1.object_type.name} and #{jn.object_type.name}"
152
+ def add_play player, play
153
+ return if player.plays.include?(play)
154
+ jn = play.variable
155
+ if jn1 = player.variables_by_query[jn.query] and jn1 != jn
156
+ raise "Player for #{player.object_type.name} may only have one variable per query, not #{jn1.object_type.name} and #{jn.object_type.name}"
157
157
  end
158
- player.join_nodes_by_join[jn.join] = jn
159
- @player_by_join_role[join_role] = player
160
- player.join_roles << join_role
158
+ player.variables_by_query[jn.query] = jn
159
+ @player_by_play[play] = player
160
+ player.plays << play
161
161
  end
162
162
 
163
163
  # Add a RoleRef to an existing Player
164
164
  def add_role_player player, role_ref
165
165
  #debug :subscript, "Adding role_ref #{role_ref.object_id} to player #{player.object_id}"
166
- if jr = role_ref.join_role
167
- add_join_role(player, jr)
166
+ if jr = role_ref.play
167
+ add_play(player, jr)
168
168
  elsif !player.role_refs.include?(role_ref)
169
169
  debug :subscript, "Adding reference to player #{player.object_id} for #{role_ref.role.object_type.name} in #{role_ref.role_sequence.describe} with #{role_ref.role_sequence.all_reading.size} readings"
170
170
  player.role_refs.push(role_ref)
@@ -212,20 +212,20 @@ module ActiveFacts
212
212
  end
213
213
  end
214
214
 
215
- def join_roles_have_same_player join_roles
216
- return if join_roles.empty?
215
+ def plays_have_same_player plays
216
+ return if plays.empty?
217
217
 
218
- # If any of these join_roles are for a known player, use that, else make a new player.
219
- existing_players = join_roles.map{|jr| @player_by_join_role[jr] }.compact.uniq
218
+ # If any of these plays are for a known player, use that, else make a new player.
219
+ existing_players = plays.map{|jr| @player_by_play[jr] }.compact.uniq
220
220
  if existing_players.size > 1
221
- raise "Can't join these roles to more than one existing player: #{existing_players.map{|p|p.object_type.name}*', '}!"
221
+ raise "At most one existing player can play these roles: #{existing_players.map{|p|p.object_type.name}*', '}!"
222
222
  end
223
- p = existing_players[0] || player(join_roles[0])
224
- debugger if join_roles.detect{|jr| jr.role.object_type != p.object_type }
225
- debug :subscript, "Joining roles to #{p.describe}" do
226
- join_roles.each do |jr|
227
- debug :subscript, "#{jr.describe}" do
228
- add_join_role p, jr
223
+ p = existing_players[0] || player(plays[0])
224
+ debugger if plays.detect{|jr| jr.role.object_type != p.object_type }
225
+ debug :subscript, "roles are playes of #{p.describe}" do
226
+ plays.each do |play|
227
+ debug :subscript, "#{play.describe}" do
228
+ add_play p, play
229
229
  end
230
230
  end
231
231
  end
@@ -238,16 +238,16 @@ module ActiveFacts
238
238
 
239
239
  # If any of these role_refs are for a known player, use that, else make a new player.
240
240
  existing_players =
241
- role_refs.map{|rr| @player_by_role_ref[rr] || @player_by_join_role[rr.join_role] }.compact.uniq
241
+ role_refs.map{|rr| @player_by_role_ref[rr] || @player_by_play[rr.play] }.compact.uniq
242
242
  if existing_players.size > 1
243
- raise "Can't join these role_refs to more than one existing player: #{existing_players.map{|p|p.object_type.name}*', '}!"
243
+ raise "At most one existing player can play these roles: #{existing_players.map{|p|p.object_type.name}*', '}!"
244
244
  end
245
245
  p = existing_players[0] || player(role_refs[0])
246
246
 
247
247
  debug :subscript, "#{existing_players[0] ? 'Adding to existing' : 'Creating new'} player for #{role_refs.map{|rr| rr.role.object_type.name}.uniq*', '}" do
248
248
  role_refs.each do |rr|
249
249
  unless p.object_type == rr.role.object_type
250
- # This happens in SubtypePI because uniqueness constraint is built without its implicit subtyping join.
250
+ # This happens in SubtypePI because uniqueness constraint is built without its implicit subtyping step.
251
251
  # For now, explode only if there's no common supertype:
252
252
  if 0 == (p.object_type.supertypes_transitive & rr.role.object_type.supertypes_transitive).size
253
253
  raise "REVISIT: Internal error, trying to add role of #{rr.role.object_type.name} to player #{p.object_type.name}"
@@ -287,11 +287,11 @@ module ActiveFacts
287
287
  p.role_refs.map{|rr| rr.role.fact_type.preferred_reading.text}.sort.to_s
288
288
  }.
289
289
  each do |player|
290
- jrname = player.join_roles.map{|jr| jr.role_ref && jr.role_ref.role.role_name}.compact[0]
290
+ jrname = player.plays.map{|jr| jr.role_ref && jr.role_ref.role.role_name}.compact[0]
291
291
  rname = (rr = player.role_refs[0]) && rr.role.role_name
292
292
  if jrname and !rname
293
293
  # puts "Oops: rolename #{rname.inspect} != #{jrname.inspect}" if jrname != rname
294
- player.join_nodes_by_join.values.each{|jn| jn.role_name = jrname }
294
+ player.variables_by_query.values.each{|jn| jn.role_name = jrname }
295
295
  else
296
296
  player.subscript = s+1
297
297
  s += 1
@@ -312,9 +312,9 @@ module ActiveFacts
312
312
  end
313
313
  end
314
314
 
315
- # Where no explicit Join has been created, a join is still sometimes present (e.g. in a constraint from NORMA)
316
- # REVISIT: This probably doesn't produce the required result. Need to fix the NORMA importer to create the join.
317
- def role_refs_are_subtype_joined roles
315
+ # Where no explicit Query has been created, a query is still sometimes present (e.g. in a constraint from NORMA)
316
+ # REVISIT: This probably doesn't produce the required result. Need to fix the NORMA importer to create the query.
317
+ def role_refs_have_subtype_steps roles
318
318
  role_refs = roles.is_a?(Array) ? roles : roles.all_role_ref.to_a
319
319
  role_refs_by_object_type = role_refs.inject({}) { |h, r| (h[r.role.object_type] ||= []) << r; h }
320
320
  role_refs_by_object_type.values.each { |rrs| role_refs_have_same_player(rrs) }
@@ -322,7 +322,7 @@ module ActiveFacts
322
322
 
323
323
  # These roles are the players in an implicit counterpart join in a Presence Constraint.
324
324
  # REVISIT: It's not clear that we can safely use the preferred_reading's RoleRefs here.
325
- # Fix the CQL compiler to create proper joins for these presence constraints instead.
325
+ # Fix the CQL compiler to create proper queries for these presence constraints instead.
326
326
  def roles_have_same_player roles
327
327
  role_refs = roles.map do |role|
328
328
  role.fact_type.all_reading.map{|reading|
@@ -336,8 +336,8 @@ module ActiveFacts
336
336
  def prepare_role_sequence role_sequence, join_over = nil
337
337
  @role_refs = role_sequence.is_a?(Array) ? role_sequence : role_sequence.all_role_ref.to_a
338
338
 
339
- if jrr = @role_refs.detect{|rr| rr.join_role && rr.join_role.join_node}
340
- return prepare_join_players(jrr.join_role.join_node.join)
339
+ if jrr = @role_refs.detect{|rr| rr.play && rr.play.variable}
340
+ return prepare_query_players(jrr.play.variable.query)
341
341
  end
342
342
 
343
343
  # Ensure that all the joined-over role_refs are indexed for subscript generation.
@@ -360,42 +360,42 @@ module ActiveFacts
360
360
  end
361
361
  end
362
362
 
363
- def prepare_join_players join
364
- debug :subscript, "Indexing roles of fact types in #{join.all_join_step.size} join steps" do
365
- join_steps = []
366
- # Register all references to each join node as being for the same player:
367
- join.all_join_node.sort_by{|jn| jn.ordinal}.each do |join_node|
368
- debug :subscript, "Adding Roles of #{join_node.describe}" do
369
- join_roles_have_same_player(join_node.all_join_role.to_a)
370
- join_steps = join_steps | join_node.all_join_step
363
+ def prepare_query_players query
364
+ debug :subscript, "Indexing roles of fact types in #{query.all_step.size} steps" do
365
+ steps = []
366
+ # Register all references to each variable as being for the same player:
367
+ query.all_variable.sort_by{|jn| jn.ordinal}.each do |variable|
368
+ debug :subscript, "Adding Roles of #{variable.describe}" do
369
+ plays_have_same_player(variable.all_play.to_a)
370
+ steps = steps | variable.all_step
371
371
  end
372
372
  end
373
373
 
374
374
  =begin
375
- # For each fact type traversed, register a player for each role *not* linked to this join
375
+ # For each fact type traversed, register a player for each role *not* linked to this query
376
376
  # REVISIT: Using the preferred_reading role_ref is wrong here; the same preferred_reading might occur twice,
377
377
  # so the respective object_type will need more than one Player and will be subscripted to keep them from being joined.
378
- # Accordingly, there must be a join step for each such role, and to enforce that, I raise an exception here on duplication.
379
- # This isn't needed now all JoinNodes have at least one JoinRole
378
+ # Accordingly, there must be a step for each such role, and to enforce that, I raise an exception here on duplication.
379
+ # This isn't needed now all Variables have at least one Play
380
380
 
381
- join_steps.map do |js|
382
- if js.fact_type.is_a?(ActiveFacts::Metamodel::ImplicitFactType)
381
+ steps.map do |js|
382
+ if js.fact_type.is_a?(ActiveFacts::Metamodel::LinkFactType)
383
383
  js.fact_type.implying_role.fact_type
384
384
  else
385
385
  js.fact_type
386
386
  end
387
387
  end.uniq.each do |fact_type|
388
- #join_steps.map{|js|js.fact_type}.uniq.each do |fact_type|
389
- next if fact_type.is_a?(ActiveFacts::Metamodel::ImplicitFactType)
388
+ #steps.map{|js|js.fact_type}.uniq.each do |fact_type|
389
+ next if fact_type.is_a?(ActiveFacts::Metamodel::LinkFactType)
390
390
 
391
391
  debug :subscript, "Residual roles in '#{fact_type.default_reading}' are" do
392
392
  prrs = fact_type.preferred_reading.role_sequence.all_role_ref
393
- residual_roles = fact_type.all_role.select{|r| !r.all_role_ref.detect{|rr| rr.join_node && rr.join_node.join == join} }
393
+ residual_roles = fact_type.all_role.select{|r| !r.all_role_ref.detect{|rr| rr.variable && rr.variable.query == query} }
394
394
  residual_roles.each do |r|
395
- debug :subscript, "Adding residual role for #{r.object_type.name} (in #{fact_type.default_reading}) not covered in join"
395
+ debug :subscript, "Adding residual role for #{r.object_type.name} (in #{fact_type.default_reading}) not covered in query"
396
396
  preferred_role_ref = prrs.detect{|rr| rr.role == r}
397
397
  if p = @player_by_role_ref[preferred_role_ref] and !p.role_refs.include?(preferred_role_ref)
398
- raise "Adding DUPLICATE residual role for #{r.object_type.name} not covered in join"
398
+ raise "Adding DUPLICATE residual role for #{r.object_type.name} not covered in query"
399
399
  end
400
400
  role_refs_have_same_player([preferred_role_ref])
401
401
  end
@@ -405,15 +405,15 @@ module ActiveFacts
405
405
  end
406
406
  end
407
407
 
408
- def verbalise_over_role_sequence role_sequence, joiner = ' and ', role_proximity = :both
408
+ def verbalise_over_role_sequence role_sequence, conjunction = ' and ', role_proximity = :both
409
409
  @role_refs = role_sequence.is_a?(Array) ? role_sequence : role_sequence.all_role_ref.to_a
410
410
 
411
- if jrr = role_refs.detect{|rr| rr.join_role}
412
- return verbalise_join(jrr.join_role.join_node.join)
411
+ if jrr = role_refs.detect{|rr| rr.play}
412
+ return verbalise_query(jrr.play.variable.query)
413
413
  end
414
414
 
415
- # First, figure out whether there's a join:
416
- join_over, joined_roles = *Metamodel.join_roles_over(role_sequence.all_role_ref.map{|rr|rr.role}, role_proximity)
415
+ # First, figure out whether there's a query:
416
+ join_over, joined_roles = *Metamodel.plays_over(role_sequence.all_role_ref.map{|rr|rr.role}, role_proximity)
417
417
 
418
418
  role_by_fact_type = {}
419
419
  fact_types = @role_refs.map{|rr| ft = rr.role.fact_type; role_by_fact_type[ft] ||= rr.role; ft}.uniq
@@ -425,7 +425,7 @@ module ActiveFacts
425
425
  joined_role = fact_type.all_role.select{|r| join_over.subtypes_transitive.include?(r.object_type)}[0]
426
426
  reading = fact_type.reading_preferably_starting_with_role joined_role
427
427
 
428
- # Use the name of the joined_over object, not the role player, in case of a subtype join:
428
+ # Use the name of the joined_over object, not the role player, in case of a subtype step:
429
429
  rrrs = reading.role_sequence.all_role_ref_in_order
430
430
  role_index = (0..rrrs.size).detect{|i| rrrs[i].role == joined_role }
431
431
  name_substitutions[role_index] = [nil, join_over.name]
@@ -439,43 +439,48 @@ module ActiveFacts
439
439
  @player_by_role_ref.keys.each{|rr| player_by_role[rr.role] = @player_by_role_ref[rr] if rr.role.fact_type == fact_type }
440
440
  expand_reading_text(nil, reading.text, reading.role_sequence, player_by_role)
441
441
  end
442
- joiner ? readings*joiner : readings
442
+ conjunction ? readings*conjunction : readings
443
443
  end
444
444
 
445
445
  # Expand this reading (or partial reading, during contraction)
446
446
  def expand_reading_text(step, text, role_sequence, player_by_role = {})
447
447
  if !player_by_role.empty? and !player_by_role.is_a?(Hash) || player_by_role.keys.detect{|k| !k.is_a?(ActiveFacts::Metamodel::Role)}
448
448
  debugger
449
- raise "Need to change this call to expand_reading_text to pass a role->join_node hash"
449
+ raise "Need to change this call to expand_reading_text to pass a role->variable hash"
450
450
  end
451
451
  rrs = role_sequence.all_role_ref_in_order
452
+ variable_by_role = {}
453
+ if step
454
+ plays = ([step.input_play, step.output_play]+step.all_incidental_play.to_a).compact
455
+ variable_by_role = plays.inject({}) { |h, play| h[play.role] = play.variable; h }
456
+ end
452
457
  debug :subscript, "expanding '#{text}' with #{role_sequence.describe}" do
453
458
  text.gsub(/\{(\d)\}/) do
454
459
  role_ref = rrs[$1.to_i]
455
460
  # REVISIT: We may need to use the step's role_refs to expand the role players here, not the reading's one (extra adjectives?)
456
- # REVISIT: There's no way to get literals to be emitted here (value join step or query result?)
457
-
458
461
  player = player_by_role[role_ref.role]
462
+ variable = variable_by_role[role_ref.role]
459
463
 
460
- join_role_name = player && player.join_nodes_by_join.values.map{|jn| jn.role_name}.compact[0]
461
- subscripted_player(role_ref, player && player.subscript, join_role_name) +
464
+ play_name = variable && variable.role_name
465
+ subscripted_player(role_ref, player && player.subscript, play_name, variable && variable.value) +
462
466
  objectification_verbalisation(role_ref.role.object_type)
463
467
  end
464
468
  end
465
469
  end
466
470
 
467
- def subscripted_player role_ref, subscript = nil, join_role_name = nil
471
+ def subscripted_player role_ref, subscript = nil, play_name = nil, value = nil
468
472
  prr = @player_by_role_ref[role_ref]
469
473
  subscript ||= prr.subscript if prr
470
474
  debug :subscript, "Need to apply subscript #{subscript} to #{role_ref.role.object_type.name}" if subscript
471
475
  object_type = role_ref.role.object_type
472
- (join_role_name ||
476
+ (play_name ||
473
477
  [
474
478
  role_ref.leading_adjective,
475
479
  object_type.name,
476
480
  role_ref.trailing_adjective
477
481
  ].compact*' '
478
482
  ) +
483
+ (value ? ' '+value.inspect : '') +
479
484
  (subscript ? "(#{subscript})" : '')
480
485
  end
481
486
 
@@ -484,72 +489,75 @@ module ActiveFacts
484
489
  expand_reading_text(step, reading.text.sub(/\A\{\d\} /,''), reading.role_sequence, role_refs)
485
490
  end
486
491
 
487
- # Each join we wish to verbalise must first have had its players prepared.
488
- # Then, this prepares the join for verbalising:
489
- def prepare_join join
490
- @join = join
491
- return unless join
492
+ # Each query we wish to verbalise must first have had its players prepared.
493
+ # Then, this prepares the query for verbalising:
494
+ def prepare_query query
495
+ @query = query
496
+ return unless query
492
497
 
493
- @join_nodes = join.all_join_node.sort_by{|jn| jn.ordinal}
498
+ @variables = query.all_variable.sort_by{|jn| jn.ordinal}
494
499
 
495
- @join_steps = @join_nodes.map{|jn| jn.all_join_step }.flatten.uniq
496
- @join_steps_by_join_node = @join_nodes.
500
+ @steps = @variables.map{|jn| jn.all_step }.flatten.uniq
501
+ @steps_by_variable = @variables.
497
502
  inject({}) do |h, jn|
498
- jn.all_join_step.each{|js| (h[jn] ||= []) << js}
503
+ jn.all_step.each{|js| (h[jn] ||= []) << js}
499
504
  h
500
505
  end
501
506
  end
502
507
 
503
508
  # Remove this step now that we've processed it:
504
509
  def step_completed(step)
505
- @join_steps.delete(step)
510
+ @steps.delete(step)
506
511
 
507
- input_node = step.input_join_role.join_node
508
- steps = @join_steps_by_join_node[input_node]
512
+ input_node = step.input_play.variable
513
+ steps = @steps_by_variable[input_node]
509
514
  steps.delete(step)
510
- @join_steps_by_join_node.delete(input_node) if steps.empty?
515
+ @steps_by_variable.delete(input_node) if steps.empty?
511
516
 
512
- output_node = step.output_join_role.join_node
517
+ output_node = step.output_play.variable
513
518
  if (input_node != output_node)
514
- steps = @join_steps_by_join_node[output_node]
519
+ steps = @steps_by_variable[output_node]
515
520
  steps.delete(step)
516
- @join_steps_by_join_node.delete(output_node) if steps.empty?
521
+ @steps_by_variable.delete(output_node) if steps.empty?
517
522
  end
518
523
  end
519
524
 
520
525
  def choose_step(next_node)
521
- next_steps = @join_steps_by_join_node[next_node]
526
+ next_steps = @steps_by_variable[next_node]
527
+
528
+ # We need to emit each objectification before mentioning an object that plays a role in one, if possible
529
+ # so that we don't wind up with an objectification as the only way to mention its name.
522
530
 
523
531
  # If we don't have a next_node against which we can contract,
524
- # so just use any join step involving this node, or just any step.
532
+ # so just use any step involving this node, or just any step.
525
533
  if next_steps
526
534
  if next_step = next_steps.detect { |ns| !ns.is_objectification_step }
527
- debug :join, "Chose new non-objectification step: #{next_step.describe}"
535
+ debug :query, "Chose new non-objectification step: #{next_step.describe}"
528
536
  return next_step
529
537
  end
530
538
  end
531
539
 
532
- if next_step = @join_steps.detect { |ns| !ns.is_objectification_step }
533
- debug :join, "Chose random non-objectification step: #{next_step.describe}"
540
+ if next_step = @steps.detect { |ns| !ns.is_objectification_step }
541
+ debug :query, "Chose random non-objectification step: #{next_step.describe}"
534
542
  return next_step
535
543
  end
536
544
 
537
- next_step = @join_steps[0]
545
+ next_step = @steps[0]
538
546
  if next_step
539
- debug :join, "Chose new random step from #{join_steps.size}: #{next_step.describe}"
547
+ debug :query, "Chose new random step from #{steps.size}: #{next_step.describe}"
540
548
  if next_step.is_objectification_step
541
549
  # if this objectification plays any roles (other than its FT roles) in remaining steps, use one of those first:
542
550
  fact_type = next_step.fact_type.implying_role.fact_type
543
- jn = [next_step.input_join_role.join_node, next_step.output_join_role.join_node].detect{|jn| jn.object_type == fact_type.entity_type}
544
- sr = @join_steps_by_join_node[jn].reject{|t| r = t.fact_type.implying_role and r.fact_type == fact_type}
551
+ jn = [next_step.input_play.variable, next_step.output_play.variable].detect{|jn| jn.object_type == fact_type.entity_type}
552
+ sr = @steps_by_variable[jn].reject{|t| r = t.fact_type.implying_role and r.fact_type == fact_type}
545
553
  next_step = sr[0] if sr.size > 0
546
554
  end
547
555
  return next_step
548
556
  end
549
- raise "Internal error: There are more join steps here, but we failed to choose one"
557
+ raise "Internal error: There are more steps here, but we failed to choose one"
550
558
  end
551
559
 
552
- # The join step we just emitted (using the reading given) is contractable iff
560
+ # The step we just emitted (using the reading given) is contractable iff
553
561
  # the reading has the next_node's role player as the final text
554
562
  def node_contractable_against_reading(next_node, reading)
555
563
  reading &&
@@ -573,7 +581,7 @@ module ActiveFacts
573
581
  next_reading = nil
574
582
  next_step =
575
583
  next_steps.detect do |js|
576
- next false if js.is_objectification_step
584
+ next false if js.is_objectification_step or js.is_disallowed
577
585
  # If we find a reading here, it can be contracted against the previous one
578
586
  next_reading =
579
587
  js.fact_type.all_reading_by_ordinal.detect do |reading|
@@ -582,50 +590,50 @@ module ActiveFacts
582
590
  end
583
591
  next_reading
584
592
  end
585
- debug :join, "#{next_reading ? "'"+next_reading.expand+"'" : "No reading"} contracts against last node '#{next_node.object_type.name}'"
593
+ debug :query, "#{next_reading ? "'"+next_reading.expand+"'" : "No reading"} contracts against last node '#{next_node.object_type.name}'"
586
594
  return [next_step, next_reading]
587
595
  end
588
596
 
589
- # REVISIT: There might be more than one objectification_verbalisation for a given object_type. Need to get the Join Node here and emit an objectification step involving that node.
597
+ # REVISIT: There might be more than one objectification_verbalisation for a given object_type. Need to get the Variable here and emit an objectification step involving that node.
590
598
  def objectification_verbalisation(object_type)
591
599
  objectified_node = nil
592
600
  unless object_type.is_a?(Metamodel::EntityType) and
593
601
  object_type.fact_type and # Not objectified
594
- objectification_step = @join_steps.
602
+ objectification_step = @steps.
595
603
  detect do |js|
596
- # The objectifying entity type should always be the input_join_node here, but be safe:
604
+ # The objectifying entity type should always be the input_variable here, but be safe:
597
605
  js.is_objectification_step and
598
- (objectified_node = js.input_join_role.join_node).object_type == object_type ||
599
- (objectified_node = js.output_join_role.join_node).object_type == object_type
606
+ (objectified_node = js.input_play.variable).object_type == object_type ||
607
+ (objectified_node = js.output_play.variable).object_type == object_type
600
608
  end
601
609
  return ''
602
610
  end
603
611
 
604
612
  # REVISIT: We need to be working from the role_ref here - pass it in
605
- # if objectification_step.join_node != role_ref.join_node
613
+ # if objectification_step.variable != role_ref.variable
606
614
 
607
615
  steps = [objectification_step]
608
616
  step_completed(objectification_step)
609
617
  while other_step =
610
- @join_steps.
618
+ @steps.
611
619
  detect{|js|
612
620
  js.is_objectification_step and
613
- js.input_join_role.join_node.object_type == object_type || js.output_join_role.join_node.object_type == object_type
621
+ js.input_play.variable.object_type == object_type || js.output_play.variable.object_type == object_type
614
622
  }
615
623
  steps << other_step
616
- debug :join, "Emitting objectification step allows deleting #{other_step.describe}"
624
+ debug :query, "Emitting objectification step allows deleting #{other_step.describe}"
617
625
  step_completed(other_step)
618
626
  end
619
627
 
620
- # Find all references to roles in this objectified fact type which are relevant to the join nodes of these steps:
628
+ # Find all references to roles in this objectified fact type which are relevant to the variables of these steps:
621
629
  player_by_role = {}
622
- steps.each do |join_step|
623
- join_step.all_join_role.to_a.map do |jr|
624
- player_by_role[jr.role] = @player_by_join_role[jr]
630
+ steps.each do |step|
631
+ step.all_play.to_a.map do |jr|
632
+ player_by_role[jr.role] = @player_by_play[jr]
625
633
  end
626
634
  end
627
635
 
628
- # role_refs = steps.map{|step| [step.input_join_role.join_node, step.output_join_role.join_node].map{|jn| jn.all_role_ref.detect{|rr| rr.role.fact_type == object_type.fact_type}}}.flatten.compact.uniq
636
+ # role_refs = steps.map{|step| [step.input_play.variable, step.output_play.variable].map{|jn| jn.all_role_ref.detect{|rr| rr.role.fact_type == object_type.fact_type}}}.flatten.compact.uniq
629
637
 
630
638
  reading = object_type.fact_type.preferred_reading
631
639
  " (in which #{expand_reading_text(objectification_step, reading.text, reading.role_sequence, player_by_role)})"
@@ -636,46 +644,53 @@ module ActiveFacts
636
644
  # Choose a reading that's contractable against the previous step, if possible
637
645
  reading = fact_type.all_reading_by_ordinal.
638
646
  detect do |reading|
639
- reading_starts_with_node(reading, next_node)
647
+ # Only contract a negative reading if we want one
648
+ (!next_step.is_disallowed || !reading.is_negative == !next_step.is_disallowed) and
649
+ reading_starts_with_node(reading, next_node)
640
650
  end
641
651
  end
642
652
  last_is_contractable = false unless reading
643
- reading ||= fact_type.preferred_reading
653
+ reading ||= fact_type.preferred_reading(next_step.is_disallowed) || fact_type.preferred_reading
644
654
 
645
- # Find which role occurs last in the reading, and which Join Node is attached
655
+ # Find which role occurs last in the reading, and which Variable is attached
646
656
  reading.text =~ /\{(\d)\}[^{]*\Z/
647
657
  last_role_ref = reading.role_sequence.all_role_ref_in_order[$1.to_i]
648
- exit_node = @join_nodes.detect{|jn| jn.all_join_role.detect{|jr| jr.role == last_role_ref.role}}
658
+ exit_node = @variables.detect{|jn| jn.all_play.detect{|jr| jr.role == last_role_ref.role}}
649
659
  exit_step = nil
650
660
 
651
- count = 0
652
- while other_step =
653
- @join_steps.
654
- detect{|js|
655
- next unless js.is_objectification_step
656
- # REVISIT: This test is too weak: We need to ensure that the same join nodes are involved, not just the same object types:
657
- next unless js.input_join_role.join_node.object_type == fact_type.entity_type || js.output_join_role.join_node.object_type == fact_type.entity_type
658
- exit_step = js if js.output_join_role.join_node == exit_node
659
- true
660
- }
661
- debug :join, "Emitting objectified FT allows deleting #{other_step.describe}"
662
- step_completed(other_step)
663
- # raise "The objectification of '#{fact_type.default_reading}' should not cause the deletion of more than #{fact_type.all_role.size} other join steps" if (count += 1) > fact_type.all_role.size
664
- end
665
- [ reading, exit_step ? exit_step.input_join_role.join_node : exit_node, exit_step, last_is_contractable]
661
+ debug :query, "Stepping over an objectification to #{exit_node.object_type.name} requires eliding the other implied steps" do
662
+ count = 0
663
+ while other_step =
664
+ @steps.
665
+ detect{|js|
666
+ debug :query, "Considering step '#{js.fact_type.default_reading}'"
667
+ next unless js.is_objectification_step
668
+
669
+ # REVISIT: This test is too weak: We need to ensure that the same variables are involved, not just the same object types:
670
+ next unless js.input_play.variable.object_type == fact_type.entity_type || js.output_play.variable.object_type == fact_type.entity_type
671
+ exit_step = js if js.output_play.variable == exit_node
672
+ true
673
+ }
674
+ debug :query, "Emitting objectified FT allows deleting #{other_step.describe}"
675
+ step_completed(other_step)
676
+ # raise "The objectification of '#{fact_type.default_reading}' should not cause the deletion of more than #{fact_type.all_role.size} other steps" if (count += 1) > fact_type.all_role.size
677
+ end
678
+ end
679
+
680
+ [ reading, exit_step ? exit_step.input_play.variable : exit_node, exit_step, last_is_contractable]
666
681
  end
667
682
 
668
- def verbalise_join join
669
- prepare_join join
683
+ def verbalise_query query
684
+ prepare_query query
670
685
  readings = ''
671
- next_node = @role_refs[0].join_role.join_node # Choose a place to start
686
+ next_node = @role_refs[0].play.variable # Choose a place to start
672
687
  last_is_contractable = false
673
- debug :join, "Join Nodes are #{@join_nodes.map{|jn| jn.describe }.inspect}, Join Steps are #{@join_steps.map{|js| js.describe }.inspect}" do
674
- until @join_steps.empty?
688
+ debug :query, "Variables are #{@variables.map{|jn| jn.describe }.inspect}, Steps are #{@steps.map{|js| js.describe }.inspect}" do
689
+ until @steps.empty?
675
690
  next_reading = nil
676
691
  # Choose amonst all remaining steps we can take from the next node, if any
677
- next_steps = @join_steps_by_join_node[next_node]
678
- debug :join, "Next Steps from #{next_node.describe} are #{(next_steps||[]).map{|js| js.describe }.inspect}"
692
+ next_steps = @steps_by_variable[next_node]
693
+ debug :query, "Next Steps from #{next_node.describe} are #{(next_steps||[]).map{|js| js.describe }.inspect}"
679
694
 
680
695
  # See if we can find a next step that contracts against the last (if any):
681
696
  next_step = nil
@@ -684,24 +699,28 @@ module ActiveFacts
684
699
  end
685
700
 
686
701
  if next_step
687
- debug :join, "Chose #{next_step.describe} because it's contractable against last node #{next_node.object_type.name} using #{next_reading.expand}"
702
+ debug :query, "Chose #{next_step.describe} because it's contractable against last node #{next_node.object_type.name} using #{next_reading.expand}"
688
703
 
689
704
  player_by_role =
690
- next_step.all_join_role.inject({}) {|h, jr| h[jr.role] = @player_by_join_role[jr]; h }
705
+ next_step.all_play.inject({}) {|h, jr| h[jr.role] = @player_by_play[jr]; h }
706
+ raise "REVISIT: Needed a negated reading here" if !next_reading.is_negative != !next_step.is_disallowed
707
+ raise "REVISIT: Need to emit 'maybe' here" if next_step.is_optional
691
708
  readings += expand_contracted_text(next_step, next_reading, player_by_role)
692
709
  step_completed(next_step)
693
710
  else
694
711
  next_step = choose_step(next_node) if !next_step
695
712
 
696
713
  player_by_role =
697
- next_step.all_join_role.inject({}) {|h, jr| h[jr.role] = @player_by_join_role[jr]; h }
714
+ next_step.all_play.inject({}) {|h, jr| h[jr.role] = @player_by_play[jr]; h }
698
715
 
699
716
  if next_step.is_unary_step
700
717
  # Objectified unaries get emitted as unaries, not as objectifications:
701
- role = next_step.input_join_role.role
702
- role = role.fact_type.implying_role if role.fact_type.is_a?(ImplicitFactType)
703
- next_reading = role.fact_type.preferred_reading
718
+ role = next_step.input_play.role
719
+ role = role.fact_type.implying_role if role.fact_type.is_a?(LinkFactType)
720
+ next_reading = role.fact_type.preferred_reading(next_step.is_disallowed) || role.fact_type.preferred_reading
704
721
  readings += " and " unless readings.empty?
722
+ readings += "it is not the case that " if !next_step.is_disallowed != !next_reading.is_negative
723
+ raise "REVISIT: Need to emit 'maybe' here" if next_step.is_optional
705
724
  readings += expand_reading_text(next_step, next_reading.text, next_reading.role_sequence, player_by_role)
706
725
  step_completed(next_step)
707
726
  elsif next_step.is_objectification_step
@@ -709,11 +728,11 @@ module ActiveFacts
709
728
 
710
729
  # This objectification step is over an implicit fact type, so player_by_role won't have all the players
711
730
  # Add the players of other roles associated with steps from this objectified player.
712
- objectified_node = next_step.input_join_role.join_node
713
- raise "Assumption violated that the objectification is the input join role" unless objectified_node.object_type.fact_type
714
- objectified_node.all_join_step.map do |other_step|
715
- (other_step.all_incidental_join_role.to_a + [other_step.output_join_role]).map do |jr|
716
- player_by_role[jr.role] = @player_by_join_role[jr]
731
+ objectified_node = next_step.input_play.variable
732
+ raise "Assumption violated that the objectification is the input play" unless objectified_node.object_type.fact_type
733
+ objectified_node.all_step.map do |other_step|
734
+ (other_step.all_incidental_play.to_a + [other_step.output_play]).map do |jr|
735
+ player_by_role[jr.role] = @player_by_play[jr]
717
736
  end
718
737
  end
719
738
 
@@ -723,16 +742,20 @@ module ActiveFacts
723
742
  readings += objectification_verbalisation(fact_type.entity_type)
724
743
  else
725
744
  # This objectified fact type does not need to be made explicit.
745
+ negation = next_step.is_disallowed
726
746
  next_reading, next_node, next_step, last_is_contractable =
727
747
  *elided_objectification(next_step, fact_type, last_is_contractable, next_node)
728
748
  if last_is_contractable
749
+ raise "REVISIT: Need to emit 'maybe' here" if next_step and next_step.is_optional
729
750
  readings += expand_contracted_text(next_step, next_reading, player_by_role)
730
751
  else
731
752
  readings += " and " unless readings.empty?
753
+ readings += "it is not the case that " if !!negation != !!next_reading.is_negative
754
+ raise "REVISIT: Need to emit 'maybe' here" if next_step and next_step.is_optional
732
755
  readings += expand_reading_text(next_step, next_reading.text, next_reading.role_sequence, player_by_role)
733
756
  end
734
757
  # No need to continue if we just deleted the last step
735
- break if @join_steps.empty?
758
+ break if @steps.empty?
736
759
 
737
760
  end
738
761
  else
@@ -740,21 +763,30 @@ module ActiveFacts
740
763
  # Prefer a reading that starts with the player of next_node
741
764
  next_reading = fact_type.all_reading_by_ordinal.
742
765
  detect do |reading|
743
- reading_starts_with_node(reading, next_node)
744
- end || fact_type.preferred_reading
745
- # REVISIT: If this join step and reading has role references with adjectives, we need to expand using those
766
+ (!next_step.is_disallowed || !reading.is_negative == !next_step.is_disallowed) and
767
+ reading_starts_with_node(reading, next_node)
768
+ end || fact_type.preferred_reading(next_step.is_disallowed)
769
+ # REVISIT: If this step and reading has role references with adjectives, we need to expand using those
746
770
  readings += " and " unless readings.empty?
771
+ readings += "it is not the case that " if !next_step.is_disallowed != !next_reading.is_negative
772
+ raise "REVISIT: Need to emit 'maybe' here" if next_step and next_step.is_optional
747
773
  readings += expand_reading_text(next_step, next_reading.text, next_reading.role_sequence, player_by_role)
748
774
  step_completed(next_step)
749
775
  end
750
776
  end
751
777
 
752
- # Continue from this step with the node having the most steps remaining
753
- input_steps = @join_steps_by_join_node[input_node = next_step.input_join_role.join_node] || []
754
- output_steps = @join_steps_by_join_node[output_node = next_step.output_join_role.join_node] || []
755
- next_node = input_steps.size > output_steps.size ? input_node : output_node
756
- # Prepare for possible contraction following:
757
- last_is_contractable = next_reading && node_contractable_against_reading(next_node, next_reading)
778
+ if next_step
779
+ # Continue from this step with the node having the most steps remaining
780
+ input_steps = @steps_by_variable[input_node = next_step.input_play.variable] || []
781
+ output_steps = @steps_by_variable[output_node = next_step.output_play.variable] || []
782
+ next_node = input_steps.size > output_steps.size ? input_node : output_node
783
+ # Prepare for possible contraction following:
784
+ last_is_contractable = next_reading && node_contractable_against_reading(next_node, next_reading)
785
+ else
786
+ # This shouldn't happen, but an elided objectification that had missing steps can cause it. Survive:
787
+ next_node = (steps[0].input_play || steps[0].output_play).variable
788
+ last_is_contractable = false
789
+ end
758
790
 
759
791
  end
760
792
  end