activefacts 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.
Files changed (120) hide show
  1. data/.gemtest +0 -0
  2. data/Manifest.txt +28 -33
  3. data/Rakefile +11 -12
  4. data/bin/cql +90 -46
  5. data/examples/CQL/Blog.cql +2 -1
  6. data/examples/CQL/CompanyDirectorEmployee.cql +2 -2
  7. data/examples/CQL/Death.cql +1 -1
  8. data/examples/CQL/Diplomacy.cql +9 -9
  9. data/examples/CQL/Genealogy.cql +3 -2
  10. data/examples/CQL/Insurance.cql +10 -7
  11. data/examples/CQL/JoinEquality.cql +2 -2
  12. data/examples/CQL/Marriage.cql +1 -1
  13. data/examples/CQL/Metamodel.cql +73 -53
  14. data/examples/CQL/MetamodelNext.cql +89 -67
  15. data/examples/CQL/OneToOnes.cql +2 -2
  16. data/examples/CQL/ServiceDirector.cql +10 -5
  17. data/examples/CQL/Supervision.cql +3 -3
  18. data/examples/CQL/Tests.Test5.Load.cql +1 -1
  19. data/examples/CQL/Warehousing.cql +4 -2
  20. data/lib/activefacts/cql/CQLParser.treetop +26 -60
  21. data/lib/activefacts/cql/Context.treetop +12 -2
  22. data/lib/activefacts/cql/Expressions.treetop +14 -30
  23. data/lib/activefacts/cql/FactTypes.treetop +165 -110
  24. data/lib/activefacts/cql/Language/English.treetop +167 -54
  25. data/lib/activefacts/cql/LexicalRules.treetop +16 -2
  26. data/lib/activefacts/cql/{Concepts.treetop → ObjectTypes.treetop} +36 -37
  27. data/lib/activefacts/cql/Terms.treetop +57 -27
  28. data/lib/activefacts/cql/ValueTypes.treetop +39 -13
  29. data/lib/activefacts/cql/compiler.rb +5 -3
  30. data/lib/activefacts/cql/compiler/{reading.rb → clause.rb} +407 -285
  31. data/lib/activefacts/cql/compiler/constraint.rb +178 -275
  32. data/lib/activefacts/cql/compiler/entity_type.rb +73 -64
  33. data/lib/activefacts/cql/compiler/expression.rb +418 -0
  34. data/lib/activefacts/cql/compiler/fact.rb +146 -145
  35. data/lib/activefacts/cql/compiler/fact_type.rb +197 -80
  36. data/lib/activefacts/cql/compiler/join.rb +159 -0
  37. data/lib/activefacts/cql/compiler/shared.rb +51 -23
  38. data/lib/activefacts/cql/compiler/value_type.rb +56 -2
  39. data/lib/activefacts/cql/parser.rb +15 -4
  40. data/lib/activefacts/generate/absorption.rb +7 -7
  41. data/lib/activefacts/generate/cql.rb +100 -37
  42. data/lib/activefacts/generate/oo.rb +28 -51
  43. data/lib/activefacts/generate/ordered.rb +60 -36
  44. data/lib/activefacts/generate/ruby.rb +6 -6
  45. data/lib/activefacts/generate/sql/server.rb +4 -4
  46. data/lib/activefacts/input/orm.rb +71 -53
  47. data/lib/activefacts/persistence.rb +1 -1
  48. data/lib/activefacts/persistence/columns.rb +27 -23
  49. data/lib/activefacts/persistence/foreignkey.rb +6 -6
  50. data/lib/activefacts/persistence/index.rb +17 -17
  51. data/lib/activefacts/persistence/{concept.rb → object_type.rb} +9 -9
  52. data/lib/activefacts/persistence/reference.rb +61 -36
  53. data/lib/activefacts/persistence/tables.rb +61 -59
  54. data/lib/activefacts/support.rb +54 -29
  55. data/lib/activefacts/version.rb +1 -1
  56. data/lib/activefacts/vocabulary/extensions.rb +99 -54
  57. data/lib/activefacts/vocabulary/metamodel.rb +43 -37
  58. data/lib/activefacts/vocabulary/verbaliser.rb +134 -109
  59. data/spec/absorption_spec.rb +8 -8
  60. data/spec/cql/comparison_spec.rb +91 -0
  61. data/spec/cql/contractions_spec.rb +251 -0
  62. data/spec/cql/entity_type_spec.rb +319 -0
  63. data/spec/cql/expressions_spec.rb +63 -0
  64. data/spec/cql/fact_type_matching_spec.rb +283 -0
  65. data/spec/cql/french_spec.rb +21 -0
  66. data/spec/cql/parser/bad_literals_spec.rb +86 -0
  67. data/spec/cql/parser/constraints_spec.rb +19 -0
  68. data/spec/cql/parser/entity_types_spec.rb +106 -0
  69. data/spec/cql/parser/expressions_spec.rb +179 -0
  70. data/spec/cql/parser/fact_types_spec.rb +41 -0
  71. data/spec/cql/parser/literals_spec.rb +312 -0
  72. data/spec/cql/parser/pragmas_spec.rb +89 -0
  73. data/spec/cql/parser/value_types_spec.rb +42 -0
  74. data/spec/cql/role_matching_spec.rb +147 -0
  75. data/spec/cql/samples_spec.rb +9 -9
  76. data/spec/cql_cql_spec.rb +1 -1
  77. data/spec/cql_dm_spec.rb +116 -0
  78. data/spec/cql_mysql_spec.rb +1 -1
  79. data/spec/cql_ruby_spec.rb +1 -1
  80. data/spec/cql_sql_spec.rb +3 -3
  81. data/spec/cql_symbol_tables_spec.rb +30 -30
  82. data/spec/cqldump_spec.rb +4 -4
  83. data/spec/helpers/array_matcher.rb +32 -27
  84. data/spec/helpers/diff_matcher.rb +6 -26
  85. data/spec/helpers/file_matcher.rb +41 -32
  86. data/spec/helpers/parse_to_ast_matcher.rb +76 -0
  87. data/spec/helpers/string_matcher.rb +32 -31
  88. data/spec/norma_cql_spec.rb +1 -1
  89. data/spec/norma_ruby_spec.rb +1 -1
  90. data/spec/norma_ruby_sql_spec.rb +1 -1
  91. data/spec/norma_sql_spec.rb +3 -1
  92. data/spec/norma_tables_spec.rb +1 -1
  93. data/spec/ruby_api_spec.rb +23 -0
  94. data/spec/spec_helper.rb +5 -4
  95. metadata +66 -66
  96. data/examples/CQL/OrienteeringER.cql +0 -58
  97. data/lib/activefacts/api.rb +0 -44
  98. data/lib/activefacts/api/concept.rb +0 -410
  99. data/lib/activefacts/api/constellation.rb +0 -128
  100. data/lib/activefacts/api/entity.rb +0 -256
  101. data/lib/activefacts/api/instance.rb +0 -60
  102. data/lib/activefacts/api/instance_index.rb +0 -80
  103. data/lib/activefacts/api/numeric.rb +0 -167
  104. data/lib/activefacts/api/role.rb +0 -80
  105. data/lib/activefacts/api/role_proxy.rb +0 -70
  106. data/lib/activefacts/api/role_values.rb +0 -117
  107. data/lib/activefacts/api/standard_types.rb +0 -87
  108. data/lib/activefacts/api/support.rb +0 -65
  109. data/lib/activefacts/api/value.rb +0 -135
  110. data/lib/activefacts/api/vocabulary.rb +0 -82
  111. data/spec/api/autocounter.rb +0 -82
  112. data/spec/api/constellation.rb +0 -130
  113. data/spec/api/entity_type.rb +0 -103
  114. data/spec/api/instance.rb +0 -461
  115. data/spec/api/roles.rb +0 -124
  116. data/spec/api/value_type.rb +0 -112
  117. data/spec/api_spec.rb +0 -13
  118. data/spec/cql/matching_spec.rb +0 -517
  119. data/spec/cql/unit_spec.rb +0 -394
  120. data/spec/spec.opts +0 -1
@@ -136,6 +136,11 @@ module ActiveFacts
136
136
  value_type :length => 256
137
137
  end
138
138
 
139
+ class TransactionTiming < String
140
+ value_type
141
+ restrict 'assert', 'commit'
142
+ end
143
+
139
144
  class UnitId < AutoCounter
140
145
  value_type
141
146
  end
@@ -169,12 +174,12 @@ module ActiveFacts
169
174
 
170
175
  class ContextNote
171
176
  identified_by :context_note_id
172
- has_one :concept # See Concept.all_context_note
173
177
  has_one :constraint # See Constraint.all_context_note
174
178
  one_to_one :context_note_id, :mandatory => true # See ContextNoteId.context_note
175
179
  has_one :context_note_kind, :mandatory => true # See ContextNoteKind.all_context_note
176
180
  has_one :discussion, :mandatory => true # See Discussion.all_context_note
177
181
  has_one :fact_type # See FactType.all_context_note
182
+ has_one :object_type # See ObjectType.all_context_note
178
183
  end
179
184
 
180
185
  class Enforcement
@@ -201,9 +206,9 @@ module ActiveFacts
201
206
 
202
207
  class Instance
203
208
  identified_by :instance_id
204
- has_one :concept, :mandatory => true # See Concept.all_instance
205
209
  one_to_one :fact # See Fact.instance
206
210
  one_to_one :instance_id, :mandatory => true # See InstanceId.instance
211
+ has_one :object_type, :mandatory => true # See ObjectType.all_instance
207
212
  has_one :population, :mandatory => true # See Population.all_instance
208
213
  has_one :value # See Value.all_instance
209
214
  end
@@ -215,9 +220,10 @@ module ActiveFacts
215
220
 
216
221
  class JoinNode
217
222
  identified_by :join, :ordinal
218
- has_one :concept, :mandatory => true # See Concept.all_join_node
219
223
  has_one :join, :mandatory => true # See Join.all_join_node
224
+ has_one :object_type, :mandatory => true # See ObjectType.all_join_node
220
225
  has_one :ordinal, :mandatory => true # See Ordinal.all_join_node
226
+ has_one :role_name, :class => Name # See Name.all_join_node_as_role_name
221
227
  has_one :subscript # See Subscript.all_join_node
222
228
  has_one :value # See Value.all_join_node
223
229
  end
@@ -254,8 +260,8 @@ module ActiveFacts
254
260
  identified_by :fact_type, :ordinal
255
261
  has_one :fact_type, :mandatory => true # See FactType.all_role
256
262
  has_one :ordinal, :mandatory => true # See Ordinal.all_role
257
- has_one :concept, :mandatory => true # See Concept.all_role
258
- one_to_one :implicit_fact_type # See ImplicitFactType.role
263
+ one_to_one :implicit_fact_type, :counterpart => :implying_role # See ImplicitFactType.implying_role
264
+ has_one :object_type, :mandatory => true # See ObjectType.all_role
259
265
  has_one :role_name, :class => Name # See Name.all_role_as_role_name
260
266
  end
261
267
 
@@ -329,14 +335,6 @@ module ActiveFacts
329
335
  has_one :value, :mandatory => true # See Value.all_bound
330
336
  end
331
337
 
332
- class Concept
333
- identified_by :vocabulary, :name
334
- maybe :is_independent
335
- has_one :name, :mandatory => true # See Name.all_concept
336
- has_one :pronoun # See Pronoun.all_concept
337
- has_one :vocabulary, :mandatory => true # See Vocabulary.all_concept
338
- end
339
-
340
338
  class ConstraintShape < Shape
341
339
  has_one :constraint, :mandatory => true # See Constraint.all_constraint_shape
342
340
  end
@@ -367,10 +365,6 @@ module ActiveFacts
367
365
  has_one :vocabulary, :mandatory => true # See Vocabulary.all_diagram
368
366
  end
369
367
 
370
- class EntityType < Concept
371
- one_to_one :fact_type # See FactType.entity_type
372
- end
373
-
374
368
  class FactTypeShape < Shape
375
369
  has_one :display_role_names_setting # See DisplayRoleNamesSetting.all_fact_type_shape
376
370
  has_one :fact_type, :mandatory => true # See FactType.all_fact_type_shape
@@ -397,9 +391,17 @@ module ActiveFacts
397
391
  has_one :context_note, :mandatory => true # See ContextNote.all_model_note_shape
398
392
  end
399
393
 
394
+ class ObjectType
395
+ identified_by :vocabulary, :name
396
+ maybe :is_independent
397
+ has_one :name, :mandatory => true # See Name.all_object_type
398
+ has_one :pronoun # See Pronoun.all_object_type
399
+ has_one :vocabulary, :mandatory => true # See Vocabulary.all_object_type
400
+ end
401
+
400
402
  class ObjectTypeShape < Shape
401
- has_one :concept, :mandatory => true # See Concept.all_object_type_shape
402
403
  maybe :has_expanded_reference_mode
404
+ has_one :object_type, :mandatory => true # See ObjectType.all_object_type_shape
403
405
  end
404
406
 
405
407
  class ObjectifiedFactTypeNameShape < Shape
@@ -461,14 +463,6 @@ module ActiveFacts
461
463
  maybe :is_mandatory
462
464
  end
463
465
 
464
- class TypeInheritance < FactType
465
- identified_by :subtype, :supertype
466
- has_one :subtype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_subtype
467
- has_one :supertype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_supertype
468
- has_one :assimilation # See Assimilation.all_type_inheritance
469
- maybe :provides_identification
470
- end
471
-
472
466
  class ValueConstraintShape < ConstraintShape
473
467
  has_one :object_type_shape # See ObjectTypeShape.all_value_constraint_shape
474
468
  one_to_one :role_display # See RoleDisplay.value_constraint_shape
@@ -480,8 +474,8 @@ module ActiveFacts
480
474
  has_one :minimum_bound, :class => Bound # See Bound.all_value_range_as_minimum_bound
481
475
  end
482
476
 
483
- class ValueType < Concept
484
- maybe :is_auto_assigned
477
+ class ValueType < ObjectType
478
+ has_one :auto_assigned_transaction_timing, :class => TransactionTiming # See TransactionTiming.all_value_type_as_auto_assigned_transaction_timing
485
479
  has_one :length # See Length.all_value_type
486
480
  has_one :scale # See Scale.all_value_type
487
481
  has_one :supertype, :class => ValueType # See ValueType.all_value_type_as_supertype
@@ -495,20 +489,32 @@ module ActiveFacts
495
489
  has_one :value_range, :mandatory => true # See ValueRange.all_allowed_range
496
490
  end
497
491
 
492
+ class EntityType < ObjectType
493
+ one_to_one :fact_type # See FactType.entity_type
494
+ end
495
+
498
496
  class ImplicitBooleanValueType < ValueType
499
497
  end
500
498
 
501
- class Parameter
502
- identified_by :name, :value_type
503
- has_one :name, :mandatory => true # See Name.all_parameter
504
- has_one :value_type, :mandatory => true # See ValueType.all_parameter
499
+ class Facet
500
+ identified_by :value_type, :name
501
+ has_one :name, :mandatory => true # See Name.all_facet
502
+ has_one :value_type, :mandatory => true # See ValueType.all_facet
503
+ end
504
+
505
+ class TypeInheritance < FactType
506
+ identified_by :subtype, :supertype
507
+ has_one :subtype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_subtype
508
+ has_one :supertype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_supertype
509
+ has_one :assimilation # See Assimilation.all_type_inheritance
510
+ maybe :provides_identification
505
511
  end
506
512
 
507
- class ParamValue
508
- identified_by :value, :parameter
509
- has_one :parameter, :mandatory => true # See Parameter.all_param_value
510
- has_one :value, :mandatory => true # See Value.all_param_value
511
- has_one :value_type, :mandatory => true # See ValueType.all_param_value
513
+ class FacetValue
514
+ identified_by :value_type, :facet
515
+ has_one :facet, :mandatory => true # See Facet.all_facet_value
516
+ has_one :value, :mandatory => true # See Value.all_facet_value
517
+ has_one :value_type, :mandatory => true # See ValueType.all_facet_value
512
518
  end
513
519
 
514
520
  end
@@ -11,15 +11,15 @@ module ActiveFacts
11
11
  # * Maintains verbalisation context to expand readings using subscripting where needed
12
12
  # * Verbalises Joins by iteratively choosing a Join Step and expanding readings
13
13
  #
14
- # The verbalisation context consists of a set of Players, each for one Concept.
15
- # There may be more than one Player for the same Concept. If adjectives or role
14
+ # The verbalisation context consists of a set of Players, each for one ObjectType.
15
+ # There may be more than one Player for the same ObjectType. If adjectives or role
16
16
  # names don't make such duplicates unambiguous, subscripts will be generated.
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
20
  # When a Player occurs in a Join, it corresponds to one Join Node of that Join.
21
21
  # Each such Player has one or more JoinRoles, which refer to roles played by
22
- # that Concept. Where a join traverses two roles of a ternary fact type, there
22
+ # that ObjectType. Where a join traverses two roles of a ternary fact type, there
23
23
  # will be a residual node that has only a single JoinRole with no other meaning.
24
24
  # A JoinRole must be for exactly one Player, so is used to identify a Player.
25
25
  #
@@ -37,11 +37,11 @@ module ActiveFacts
37
37
  #
38
38
  # A constrained RoleSequence that has no explicit Join may have an implicit join,
39
39
  # as per ORM2, when the roles aren't in the same fact type. These implicit joins
40
- # are over only one Concept, by traversing a single FactType (and possibly,
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
- # the Concept is an objectified Fact Type, the FactType traversed might be a
42
+ # the ObjectType is an objectified Fact Type, the FactType traversed might be a
43
43
  # phantom of the objectification. In the case of implicit joins, each Player is
44
- # identified by the projected RoleRef, except for the joined-over Concept whose
44
+ # identified by the projected RoleRef, except for the joined-over ObjectType whose
45
45
  # Player is... well, read the next paragraph!
46
46
  #
47
47
  # REVISIT: I believe that the foregoing paragraph is out of date, except with
@@ -54,7 +54,7 @@ module ActiveFacts
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 Concept (no implicit subtyping Join is allowed). Instead, the input modules
57
+ # same ObjectType (no implicit subtyping Join is allowed). Instead, the input modules
58
58
  # find the closest common supertype and create explicit JoinSteps so its roles
59
59
  # can be projected.
60
60
  #
@@ -99,9 +99,9 @@ module ActiveFacts
99
99
  end
100
100
 
101
101
  class Player
102
- attr_accessor :concept, :join_nodes_by_join, :subscript, :join_roles, :role_refs
103
- def initialize concept
104
- @concept = concept
102
+ attr_accessor :object_type, :join_nodes_by_join, :subscript, :join_roles, :role_refs
103
+ def initialize object_type
104
+ @object_type = object_type
105
105
  @join_nodes_by_join = {}
106
106
  @subscript = nil
107
107
  @join_roles = []
@@ -111,30 +111,41 @@ module ActiveFacts
111
111
  # What words are used (across all roles) for disambiguating the references to this player?
112
112
  # If more than one set of adjectives was used, this player must have been subject to loose binding.
113
113
  # This method is used to decide when subscripts aren't needed.
114
- def role_adjuncts
115
- adjuncts = @role_refs.map{|rr|
116
- [
117
- rr.leading_adjective,
118
- # rr.role.role_name,
119
- rr.trailing_adjective
120
- ].compact}.uniq.sort
114
+ def role_adjuncts matching
115
+ if matching == :loose
116
+ adjuncts = []
117
+ else
118
+ adjuncts = @role_refs.map{|rr|
119
+ [
120
+ rr.leading_adjective,
121
+ matching == :rolenames ? rr.role.role_name : nil,
122
+ rr.trailing_adjective
123
+ ].compact}.uniq.sort
124
+ end
125
+ adjuncts += [@join_nodes_by_join.values.map{|jn| jn.role_name}.compact[0]].compact
121
126
  adjuncts.flatten*"_"
122
127
  end
123
128
 
124
129
  def describe
125
- @concept.name + (@join_nodes_by_join.size > 0 ? " (in #{@join_nodes_by_join.size} joins)" : "")
130
+ @object_type.name + (@join_nodes_by_join.size > 0 ? " (in #{@join_nodes_by_join.size} joins)" : "")
126
131
  end
127
132
  end
128
133
 
129
134
  # Find or create a Player to which we can add this role_ref
130
135
  def player(ref)
131
- if ref.is_a?(ActiveFacts::Metamodel::JoinRole)
132
- @player_by_join_role[ref] or
133
- @players.push(p = Player.new(ref.role.concept)) && p
136
+ existing_player = if ref.is_a?(ActiveFacts::Metamodel::JoinRole)
137
+ @player_by_join_role[ref]
138
+ else
139
+ @player_by_role_ref[ref] or ref.join_role && @player_by_join_role[ref.join_role]
140
+ end
141
+ if existing_player
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}'"
143
+ return existing_player
134
144
  else
135
- @player_by_role_ref[ref] or
136
- ref.join_role && @player_by_join_role[ref.join_role] or
137
- @players.push(p = Player.new(ref.role.concept)) && p
145
+ debug :player, "Adding new 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}'"
146
+ p = Player.new(ref.role.object_type)
147
+ @players.push(p)
148
+ p
138
149
  end
139
150
  end
140
151
 
@@ -142,7 +153,7 @@ module ActiveFacts
142
153
  return if player.join_roles.include?(join_role)
143
154
  jn = join_role.join_node
144
155
  if jn1 = player.join_nodes_by_join[jn.join] and jn1 != jn
145
- raise "Player for #{player.concept.name} may only have one join node per join, not #{jn1.concept.name} and #{jn.concept.name}"
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}"
146
157
  end
147
158
  player.join_nodes_by_join[jn.join] = jn
148
159
  @player_by_join_role[join_role] = player
@@ -155,7 +166,7 @@ module ActiveFacts
155
166
  if jr = role_ref.join_role
156
167
  add_join_role(player, jr)
157
168
  elsif !player.role_refs.include?(role_ref)
158
- debug :subscript, "Adding reference to player #{player.object_id} for #{role_ref.role.concept.name} in #{role_ref.role_sequence.describe} with #{role_ref.role_sequence.all_reading.size} readings"
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"
159
170
  player.role_refs.push(role_ref)
160
171
  @player_by_role_ref[role_ref] = player
161
172
  end
@@ -182,11 +193,7 @@ module ActiveFacts
182
193
  elsif role_name = role_ref.role.role_name and role_name != ''
183
194
  role_name
184
195
  else
185
- role_words = []
186
- role_words << preferred_role_ref.leading_adjective if preferred_role_ref.leading_adjective != ""
187
- role_words << preferred_role_ref.role.concept.name
188
- role_words << preferred_role_ref.trailing_adjective if preferred_role_ref.trailing_adjective != ""
189
- role_name = role_words.compact*"-"
196
+ role_name = preferred_role_ref.cql_name
190
197
  if p = player(preferred_role_ref) and p.subscript
191
198
  role_name += "(#{p.subscript})"
192
199
  end
@@ -211,10 +218,10 @@ module ActiveFacts
211
218
  # If any of these join_roles are for a known player, use that, else make a new player.
212
219
  existing_players = join_roles.map{|jr| @player_by_join_role[jr] }.compact.uniq
213
220
  if existing_players.size > 1
214
- raise "Can't join these roles to more than one existing player: #{existing_players.map{|p|p.concept.name}*', '}!"
221
+ raise "Can't join these roles to more than one existing player: #{existing_players.map{|p|p.object_type.name}*', '}!"
215
222
  end
216
223
  p = existing_players[0] || player(join_roles[0])
217
- debugger if join_roles.detect{|jr| jr.role.concept != p.concept }
224
+ debugger if join_roles.detect{|jr| jr.role.object_type != p.object_type }
218
225
  debug :subscript, "Joining roles to #{p.describe}" do
219
226
  join_roles.each do |jr|
220
227
  debug :subscript, "#{jr.describe}" do
@@ -233,17 +240,17 @@ module ActiveFacts
233
240
  existing_players =
234
241
  role_refs.map{|rr| @player_by_role_ref[rr] || @player_by_join_role[rr.join_role] }.compact.uniq
235
242
  if existing_players.size > 1
236
- raise "Can't join these role_refs to more than one existing player: #{existing_players.map{|p|p.concept.name}*', '}!"
243
+ raise "Can't join these role_refs to more than one existing player: #{existing_players.map{|p|p.object_type.name}*', '}!"
237
244
  end
238
245
  p = existing_players[0] || player(role_refs[0])
239
246
 
240
- debug :subscript, "#{existing_players[0] ? 'Adding to existing' : 'Creating new'} player for #{role_refs.map{|rr| rr.role.concept.name}.uniq*', '}" do
247
+ debug :subscript, "#{existing_players[0] ? 'Adding to existing' : 'Creating new'} player for #{role_refs.map{|rr| rr.role.object_type.name}.uniq*', '}" do
241
248
  role_refs.each do |rr|
242
- unless p.concept == rr.role.concept
249
+ unless p.object_type == rr.role.object_type
243
250
  # This happens in SubtypePI because uniqueness constraint is built without its implicit subtyping join.
244
251
  # For now, explode only if there's no common supertype:
245
- if 0 == (p.concept.supertypes_transitive & rr.role.concept.supertypes_transitive).size
246
- raise "REVISIT: Internal error, trying to add role of #{rr.role.concept.name} to player #{p.concept.name}"
252
+ if 0 == (p.object_type.supertypes_transitive & rr.role.object_type.supertypes_transitive).size
253
+ raise "REVISIT: Internal error, trying to add role of #{rr.role.object_type.name} to player #{p.object_type.name}"
247
254
  end
248
255
  end
249
256
  add_role_player(p, rr)
@@ -251,21 +258,44 @@ module ActiveFacts
251
258
  end
252
259
  end
253
260
 
254
- def create_subscripts
261
+ # REVISIT: include_rolenames is a bit of a hack. Role names generally serve to disambiguate players,
262
+ # so subscripts wouldn't be needed, but where a constraint refers to a fact type which is defined with
263
+ # role names, those are considered. We should instead consider only the role names that are defined
264
+ # within the constraint, not in the underlying fact types. For now, this parameter is passed as true
265
+ # from all the object type verbalisations, and not from constraints.
266
+ def create_subscripts(matching = :normal)
255
267
  # Create subscripts, where necessary
256
268
  @players.each { |p| p.subscript = nil } # Wipe subscripts
257
269
  @players.
258
- map{|p| [p, p.concept] }.
259
- each do |player, concept|
270
+ map{|p| [p, p.object_type] }.
271
+ each do |player, object_type|
260
272
  next if player.subscript # Done previously
261
- dups = @players.select{|p| p.concept == concept && p.role_adjuncts == player.role_adjuncts }
273
+ dups = @players.select do |p|
274
+ p.object_type == object_type &&
275
+ p.role_adjuncts(matching) == player.role_adjuncts(matching)
276
+ end
262
277
  if dups.size == 1
263
- debug :subscript, "No subscript needed for #{concept.name}"
278
+ debug :subscript, "No subscript needed for #{object_type.name}"
264
279
  next
265
280
  end
266
- debug :subscript, "Applying subscripts to #{dups.size} occurrences of #{concept.name}" do
267
- dups.each_with_index do |player, index|
268
- player.subscript = index+1
281
+ debug :subscript, "Applying subscripts to #{dups.size} occurrences of #{object_type.name}" do
282
+ s = 0
283
+ dups.
284
+ sort_by{|p| # Guarantee stable numbering
285
+ p.role_adjuncts(:role_name) + ' ' +
286
+ # Tie-breaker:
287
+ p.role_refs.map{|rr| rr.role.fact_type.preferred_reading.text}.sort.to_s
288
+ }.
289
+ each do |player|
290
+ jrname = player.join_roles.map{|jr| jr.role_ref && jr.role_ref.role.role_name}.compact[0]
291
+ rname = (rr = player.role_refs[0]) && rr.role.role_name
292
+ if jrname and !rname
293
+ # puts "Oops: rolename #{rname.inspect} != #{jrname.inspect}" if jrname != rname
294
+ player.join_nodes_by_join.values.each{|jn| jn.role_name = jrname }
295
+ else
296
+ player.subscript = s+1
297
+ s += 1
298
+ end
269
299
  end
270
300
  end
271
301
  end
@@ -276,7 +306,7 @@ module ActiveFacts
276
306
  # and also define adjectives by using the hyphenated form (on at least the first occurrence).
277
307
  def expand_reading(reading, frequency_constraints = [], define_role_names = nil, value_constraints = [], &subscript_block)
278
308
  reading.expand(frequency_constraints, define_role_names, value_constraints) do |role_ref|
279
- (!(role_ref.role.role_name and define_role_names) and p = player(role_ref) and p.subscript) ? "(#{p.subscript})" : ""
309
+ (!(role_ref.role.role_name and define_role_names != nil) and p = player(role_ref) and p.subscript) ? "(#{p.subscript})" : ""
280
310
  end
281
311
  end
282
312
 
@@ -284,8 +314,8 @@ module ActiveFacts
284
314
  # REVISIT: This probably doesn't produce the required result. Need to fix the NORMA importer to create the join.
285
315
  def role_refs_are_subtype_joined roles
286
316
  role_refs = roles.is_a?(Array) ? roles : roles.all_role_ref.to_a
287
- role_refs_by_concept = role_refs.inject({}) { |h, r| (h[r.role.concept] ||= []) << r; h }
288
- role_refs_by_concept.values.each { |rrs| role_refs_have_same_player(rrs) }
317
+ role_refs_by_object_type = role_refs.inject({}) { |h, r| (h[r.role.object_type] ||= []) << r; h }
318
+ role_refs_by_object_type.values.each { |rrs| role_refs_have_same_player(rrs) }
289
319
  end
290
320
 
291
321
  # These roles are the players in an implicit counterpart join in a Presence Constraint.
@@ -293,9 +323,11 @@ module ActiveFacts
293
323
  # Fix the CQL compiler to create proper joins for these presence constraints instead.
294
324
  def roles_have_same_player roles
295
325
  role_refs = roles.map do |role|
296
- pr = role.fact_type.preferred_reading
297
- pr.role_sequence.all_role_ref.detect{|rr| rr.role == role}
298
- end
326
+ role.fact_type.all_reading.map{|reading|
327
+ reading.role_sequence.all_role_ref.detect{|rr| rr.role == role}
328
+ } +
329
+ role.all_role_ref.select{|rr| rr.role_sequence.all_reading.size == 0 }
330
+ end.flatten.uniq
299
331
  role_refs_have_same_player(role_refs)
300
332
  end
301
333
 
@@ -316,10 +348,10 @@ module ActiveFacts
316
348
  prrs = fact_type.preferred_reading.role_sequence.all_role_ref
317
349
  residual_roles = fact_type.all_role.select{|r| !@role_refs.detect{|rr| rr.role == r} }
318
350
  residual_roles.each do |role|
319
- debug :subscript, "Adding residual role for #{role.concept.name} (in #{fact_type.default_reading}) not covered in role sequence"
351
+ debug :subscript, "Adding residual role for #{role.object_type.name} (in #{fact_type.default_reading}) not covered in role sequence"
320
352
  preferred_role_ref = prrs.detect{|rr| rr.role == role}
321
353
  if p = @player_by_role_ref[preferred_role_ref] and !p.role_refs.include?(preferred_role_ref)
322
- raise "Adding DUPLICATE residual role for #{role.concept.name}"
354
+ raise "Adding DUPLICATE residual role for #{role.object_type.name}"
323
355
  end
324
356
  role_refs_have_same_player([prrs.detect{|rr| rr.role == role}])
325
357
  end
@@ -340,13 +372,13 @@ module ActiveFacts
340
372
  =begin
341
373
  # For each fact type traversed, register a player for each role *not* linked to this join
342
374
  # REVISIT: Using the preferred_reading role_ref is wrong here; the same preferred_reading might occur twice,
343
- # so the respective concept will need more than one Player and will be subscripted to keep them from being joined.
375
+ # so the respective object_type will need more than one Player and will be subscripted to keep them from being joined.
344
376
  # Accordingly, there must be a join step for each such role, and to enforce that, I raise an exception here on duplication.
345
377
  # This isn't needed now all JoinNodes have at least one JoinRole
346
378
 
347
379
  join_steps.map do |js|
348
380
  if js.fact_type.is_a?(ActiveFacts::Metamodel::ImplicitFactType)
349
- js.fact_type.role.fact_type
381
+ js.fact_type.implying_role.fact_type
350
382
  else
351
383
  js.fact_type
352
384
  end
@@ -358,10 +390,10 @@ module ActiveFacts
358
390
  prrs = fact_type.preferred_reading.role_sequence.all_role_ref
359
391
  residual_roles = fact_type.all_role.select{|r| !r.all_role_ref.detect{|rr| rr.join_node && rr.join_node.join == join} }
360
392
  residual_roles.each do |r|
361
- debug :subscript, "Adding residual role for #{r.concept.name} (in #{fact_type.default_reading}) not covered in join"
393
+ debug :subscript, "Adding residual role for #{r.object_type.name} (in #{fact_type.default_reading}) not covered in join"
362
394
  preferred_role_ref = prrs.detect{|rr| rr.role == r}
363
395
  if p = @player_by_role_ref[preferred_role_ref] and !p.role_refs.include?(preferred_role_ref)
364
- raise "Adding DUPLICATE residual role for #{r.concept.name} not covered in join"
396
+ raise "Adding DUPLICATE residual role for #{r.object_type.name} not covered in join"
365
397
  end
366
398
  role_refs_have_same_player([preferred_role_ref])
367
399
  end
@@ -388,7 +420,7 @@ module ActiveFacts
388
420
  # Choose a reading that start with the (first) role which caused us to emit this fact type:
389
421
  reading = fact_type.reading_preferably_starting_with_role(role_by_fact_type[fact_type])
390
422
  if join_over and # Find a reading preferably starting with the joined_over role:
391
- joined_role = fact_type.all_role.select{|r| join_over.subtypes_transitive.include?(r.concept)}[0]
423
+ joined_role = fact_type.all_role.select{|r| join_over.subtypes_transitive.include?(r.object_type)}[0]
392
424
  reading = fact_type.reading_preferably_starting_with_role joined_role
393
425
 
394
426
  # Use the name of the joined_over object, not the role player, in case of a subtype join:
@@ -399,13 +431,11 @@ module ActiveFacts
399
431
  reading.role_sequence.all_role_ref.each do |rr|
400
432
  next unless player = @player_by_role_ref[rr]
401
433
  next unless subscript = player.subscript
402
- debug :subscript, "Need to apply subscript #{subscript} to #{rr.role.concept.name}"
434
+ debug :subscript, "Need to apply subscript #{subscript} to #{rr.role.object_type.name}"
403
435
  end
404
436
  player_by_role = {}
405
437
  @player_by_role_ref.keys.each{|rr| player_by_role[rr.role] = @player_by_role_ref[rr] if rr.role.fact_type == fact_type }
406
- #role_refs = @player_by_role_ref.keys.select{|rr| rr.role.fact_type == fact_type}
407
438
  expand_reading_text(nil, reading.text, reading.role_sequence, player_by_role)
408
- #reading.expand(name_substitutions)
409
439
  end
410
440
  joiner ? readings*joiner : readings
411
441
  end
@@ -421,37 +451,29 @@ module ActiveFacts
421
451
  text.gsub(/\{(\d)\}/) do
422
452
  role_ref = rrs[$1.to_i]
423
453
  # REVISIT: We may need to use the step's role_refs to expand the role players here, not the reading's one (extra adjectives?)
424
- # REVISIT: There's no way to get literals to be emitted here (value join step?)
454
+ # REVISIT: There's no way to get literals to be emitted here (value join step or query result?)
425
455
 
426
456
  player = player_by_role[role_ref.role]
427
457
 
428
- =begin
429
- rr = role_refs.detect{|rr| rr.role == role_ref.role} || role_ref
430
-
431
- player = @player_by_role_ref[rr] and subscript = player.subscript
432
- if !subscript and
433
- pp = @players.select{|p|p.concept == rr.role.concept} and
434
- pp.detect{|p|p.subscript}
435
- # raise "Internal error: Subscripted players (of the same concept #{pp[0].concept.name}) when this player isn't subscripted"
436
- end
437
- =end
438
-
439
- subscripted_player(role_ref, player && player.subscript) +
440
- objectification_verbalisation(role_ref.role.concept)
458
+ join_role_name = player && player.join_nodes_by_join.values.map{|jn| jn.role_name}.compact[0]
459
+ subscripted_player(role_ref, player && player.subscript, join_role_name) +
460
+ objectification_verbalisation(role_ref.role.object_type)
441
461
  end
442
462
  end
443
463
  end
444
464
 
445
- def subscripted_player role_ref, subscript = nil
446
- if subscript
447
- debug :subscript, "Need to apply subscript #{subscript} to #{role_ref.role.concept.name}"
448
- end
449
- concept = role_ref.role.concept
450
- [
451
- role_ref.leading_adjective,
452
- concept.name,
453
- role_ref.trailing_adjective
454
- ].compact*' ' +
465
+ def subscripted_player role_ref, subscript = nil, join_role_name = nil
466
+ prr = @player_by_role_ref[role_ref]
467
+ subscript ||= prr.subscript if prr
468
+ debug :subscript, "Need to apply subscript #{subscript} to #{role_ref.role.object_type.name}" if subscript
469
+ object_type = role_ref.role.object_type
470
+ (join_role_name ||
471
+ [
472
+ role_ref.leading_adjective,
473
+ object_type.name,
474
+ role_ref.trailing_adjective
475
+ ].compact*' '
476
+ ) +
455
477
  (subscript ? "(#{subscript})" : '')
456
478
  end
457
479
 
@@ -515,9 +537,9 @@ module ActiveFacts
515
537
  debug :join, "Chose new random step from #{join_steps.size}: #{next_step.describe}"
516
538
  if next_step.is_objectification_step
517
539
  # if this objectification plays any roles (other than its FT roles) in remaining steps, use one of those first:
518
- fact_type = next_step.fact_type.role.fact_type
519
- jn = [next_step.input_join_role.join_node, next_step.output_join_role.join_node].detect{|jn| jn.concept == fact_type.entity_type}
520
- sr = @join_steps_by_join_node[jn].reject{|t| t.fact_type.role and t.fact_type.role.fact_type == fact_type}
540
+ fact_type = next_step.fact_type.implying_role.fact_type
541
+ jn = [next_step.input_join_role.join_node, next_step.output_join_role.join_node].detect{|jn| jn.object_type == fact_type.entity_type}
542
+ sr = @join_steps_by_join_node[jn].reject{|t| r = t.fact_type.implying_role and r.fact_type == fact_type}
521
543
  next_step = sr[0] if sr.size > 0
522
544
  end
523
545
  return next_step
@@ -534,13 +556,13 @@ module ActiveFacts
534
556
  # This reading's RoleRef for that role:
535
557
  (role_ref = reading.role_sequence.all_role_ref_in_order[$1.to_i]) &&
536
558
  # was that RoleRef for the upcoming node?
537
- role_ref.role.concept == next_node.concept
559
+ role_ref.role.object_type == next_node.object_type
538
560
  end
539
561
 
540
562
  def reading_starts_with_node(reading, next_node)
541
563
  reading.text =~ /^\{([0-9])\}/ and
542
564
  role_ref = reading.role_sequence.all_role_ref.detect{|rr| rr.ordinal == $1.to_i} and
543
- role_ref.role.concept == next_node.concept
565
+ role_ref.role.object_type == next_node.object_type
544
566
  end
545
567
 
546
568
  # The last reading we emitted ended with the object type name for next_node.
@@ -558,21 +580,21 @@ module ActiveFacts
558
580
  end
559
581
  next_reading
560
582
  end
561
- debug :join, "#{next_reading ? "'"+next_reading.expand+"'" : "No reading"} contracts against last node '#{next_node.concept.name}'"
583
+ debug :join, "#{next_reading ? "'"+next_reading.expand+"'" : "No reading"} contracts against last node '#{next_node.object_type.name}'"
562
584
  return [next_step, next_reading]
563
585
  end
564
586
 
565
- # REVISIT: There might be more than one objectification_verbalisation for a given concept. Need to get the Join Node here and emit an objectification step involving that node.
566
- def objectification_verbalisation(concept)
587
+ # 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.
588
+ def objectification_verbalisation(object_type)
567
589
  objectified_node = nil
568
- unless concept.is_a?(Metamodel::EntityType) and
569
- concept.fact_type and # Not objectified
590
+ unless object_type.is_a?(Metamodel::EntityType) and
591
+ object_type.fact_type and # Not objectified
570
592
  objectification_step = @join_steps.
571
593
  detect do |js|
572
594
  # The objectifying entity type should always be the input_join_node here, but be safe:
573
595
  js.is_objectification_step and
574
- (objectified_node = js.input_join_role.join_node).concept == concept ||
575
- (objectified_node = js.output_join_role.join_node).concept == concept
596
+ (objectified_node = js.input_join_role.join_node).object_type == object_type ||
597
+ (objectified_node = js.output_join_role.join_node).object_type == object_type
576
598
  end
577
599
  return ''
578
600
  end
@@ -586,7 +608,7 @@ module ActiveFacts
586
608
  @join_steps.
587
609
  detect{|js|
588
610
  js.is_objectification_step and
589
- js.input_join_role.join_node.concept == concept || js.output_join_role.join_node.concept == concept
611
+ js.input_join_role.join_node.object_type == object_type || js.output_join_role.join_node.object_type == object_type
590
612
  }
591
613
  steps << other_step
592
614
  debug :join, "Emitting objectification step allows deleting #{other_step.describe}"
@@ -601,9 +623,9 @@ module ActiveFacts
601
623
  end
602
624
  end
603
625
 
604
- # 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 == concept.fact_type}}}.flatten.compact.uniq
626
+ # 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
605
627
 
606
- reading = concept.fact_type.preferred_reading
628
+ reading = object_type.fact_type.preferred_reading
607
629
  " (where #{expand_reading_text(objectification_step, reading.text, reading.role_sequence, player_by_role)})"
608
630
  end
609
631
 
@@ -624,16 +646,19 @@ module ActiveFacts
624
646
  exit_node = @join_nodes.detect{|jn| jn.all_join_role.detect{|jr| jr.role == last_role_ref.role}}
625
647
  exit_step = nil
626
648
 
649
+ count = 0
627
650
  while other_step =
628
651
  @join_steps.
629
652
  detect{|js|
630
653
  next unless js.is_objectification_step
631
- next unless js.input_join_role.join_node.concept == fact_type.entity_type || js.output_join_role.join_node.concept == fact_type.entity_type
654
+ # REVISIT: This test is too weak: We need to ensure that the same join nodes are involved, not just the same object types:
655
+ 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
632
656
  exit_step = js if js.output_join_role.join_node == exit_node
633
657
  true
634
658
  }
635
659
  debug :join, "Emitting objectified FT allows deleting #{other_step.describe}"
636
660
  step_completed(other_step)
661
+ # 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
637
662
  end
638
663
  [ reading, exit_step ? exit_step.input_join_role.join_node : exit_node, exit_step, last_is_contractable]
639
664
  end
@@ -657,7 +682,7 @@ module ActiveFacts
657
682
  end
658
683
 
659
684
  if next_step
660
- debug :join, "Chose #{next_step.describe} because it's contractable against last node #{next_node.concept.name} using #{next_reading.expand}"
685
+ debug :join, "Chose #{next_step.describe} because it's contractable against last node #{next_node.object_type.name} using #{next_reading.expand}"
661
686
 
662
687
  player_by_role =
663
688
  next_step.all_join_role.inject({}) {|h, jr| h[jr.role] = @player_by_join_role[jr]; h }
@@ -671,26 +696,26 @@ module ActiveFacts
671
696
 
672
697
  if next_step.is_unary_step
673
698
  # Objectified unaries get emitted as unaries, not as objectifications:
674
- # REVISIT: There must be a simpler way of finding the preferred reading here:
675
- rr = next_step.input_join_node.all_role_ref.detect{|rr| rr.role.fact_type.is_a?(ImplicitFactType) }
676
- next_reading = rr.role.fact_type.role.fact_type.preferred_reading
699
+ role = next_step.input_join_role.role
700
+ role = role.fact_type.implying_role if role.fact_type.is_a?(ImplicitFactType)
701
+ next_reading = role.fact_type.preferred_reading
677
702
  readings += " and " unless readings.empty?
678
703
  readings += expand_reading_text(next_step, next_reading.text, next_reading.role_sequence, player_by_role)
679
704
  step_completed(next_step)
680
705
  elsif next_step.is_objectification_step
681
- fact_type = next_step.fact_type.role.fact_type
706
+ fact_type = next_step.fact_type.implying_role.fact_type
682
707
 
683
708
  # This objectification step is over an implicit fact type, so player_by_role won't have all the players
684
709
  # Add the players of other roles associated with steps from this objectified player.
685
710
  objectified_node = next_step.input_join_role.join_node
686
- raise "Assumption violated that the objectification is the input join role" unless objectified_node.concept.fact_type
711
+ raise "Assumption violated that the objectification is the input join role" unless objectified_node.object_type.fact_type
687
712
  objectified_node.all_join_step.map do |other_step|
688
713
  (other_step.all_incidental_join_role.to_a + [other_step.output_join_role]).map do |jr|
689
714
  player_by_role[jr.role] = @player_by_join_role[jr]
690
715
  end
691
716
  end
692
717
 
693
- if last_is_contractable and next_node.concept.is_a?(EntityType) and next_node.concept.fact_type == fact_type
718
+ if last_is_contractable and next_node.object_type.is_a?(EntityType) and next_node.object_type.fact_type == fact_type
694
719
  # The last reading we emitted ended with the name of the objectification of this fact type, so we can contract the objectification
695
720
  # REVISIT: Do we need to use player_by_role here (if this objectification is traversed twice and so is subscripted)
696
721
  readings += objectification_verbalisation(fact_type.entity_type)