activefacts 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +5 -13
  2. data/Rakefile +2 -2
  3. data/bin/afgen +1 -1
  4. data/bin/cql +118 -27
  5. data/examples/CQL/Insurance.cql +2 -2
  6. data/examples/CQL/Metamodel.cql +3 -3
  7. data/examples/CQL/SchoolActivities.cql +1 -1
  8. data/examples/CQL/Warehousing.cql +5 -4
  9. data/lib/activefacts/cql.rb +1 -1
  10. data/lib/activefacts/cql/Language/English.treetop +2 -1
  11. data/lib/activefacts/cql/compiler.rb +6 -6
  12. data/lib/activefacts/cql/compiler/clause.rb +69 -46
  13. data/lib/activefacts/cql/compiler/constraint.rb +14 -14
  14. data/lib/activefacts/cql/compiler/entity_type.rb +24 -24
  15. data/lib/activefacts/cql/compiler/fact.rb +40 -27
  16. data/lib/activefacts/cql/compiler/fact_type.rb +16 -16
  17. data/lib/activefacts/cql/compiler/query.rb +12 -12
  18. data/lib/activefacts/cql/compiler/shared.rb +9 -0
  19. data/lib/activefacts/cql/compiler/value_type.rb +4 -4
  20. data/lib/activefacts/cql/parser.rb +9 -9
  21. data/lib/activefacts/generate/cql.rb +41 -20
  22. data/lib/activefacts/generate/helpers/oo.rb +33 -70
  23. data/lib/activefacts/generate/helpers/ordered.rb +61 -87
  24. data/lib/activefacts/generate/ruby.rb +12 -72
  25. data/lib/activefacts/generate/transform/surrogate.rb +13 -13
  26. data/lib/activefacts/input/orm.rb +72 -71
  27. data/lib/activefacts/persistence/columns.rb +66 -31
  28. data/lib/activefacts/persistence/foreignkey.rb +6 -6
  29. data/lib/activefacts/persistence/index.rb +12 -12
  30. data/lib/activefacts/persistence/object_type.rb +15 -12
  31. data/lib/activefacts/persistence/reference.rb +20 -18
  32. data/lib/activefacts/persistence/tables.rb +40 -36
  33. data/lib/activefacts/support.rb +69 -123
  34. data/lib/activefacts/version.rb +2 -2
  35. data/lib/activefacts/vocabulary/extensions.rb +42 -39
  36. data/lib/activefacts/vocabulary/metamodel.rb +11 -1
  37. data/lib/activefacts/vocabulary/verbaliser.rb +28 -28
  38. data/spec/cql/contractions_spec.rb +1 -1
  39. data/spec/cql/entity_type_spec.rb +1 -1
  40. data/spec/cql/fact_type_matching_spec.rb +3 -3
  41. data/spec/cql/role_matching_spec.rb +4 -4
  42. data/spec/cql/samples_spec.rb +2 -2
  43. data/spec/cql_cql_spec.rb +1 -1
  44. data/spec/helpers/array_matcher.rb +1 -1
  45. data/spec/norma_ruby_sql_spec.rb +2 -2
  46. data/spec/norma_tables_spec.rb +3 -2
  47. metadata +47 -68
@@ -13,7 +13,7 @@ module ActiveFacts
13
13
  end
14
14
 
15
15
  def prepare_roles clauses = nil
16
- debug :binding, "preparing roles" do
16
+ trace :binding, "preparing roles" do
17
17
  @context ||= CompilationContext.new(@vocabulary)
18
18
  @context.bind clauses||[], @conditions, @returning
19
19
  end
@@ -28,7 +28,7 @@ module ActiveFacts
28
28
 
29
29
  # Build the query:
30
30
  unless @conditions.empty? and !@returning
31
- debug :query, "building query for derived fact type" do
31
+ trace :query, "building query for derived fact type" do
32
32
  @query = build_variables(@conditions.flatten)
33
33
  @roles_by_binding = build_all_steps(@conditions)
34
34
  @query.validate
@@ -43,7 +43,7 @@ module ActiveFacts
43
43
  @conditions.each do |condition|
44
44
  next if condition.is_naked_object_type
45
45
  # REVISIT: Many conditions will imply a number of different steps, which need to be handled (similar to nested_clauses).
46
- debug :projection, "matching condition fact_type #{condition.inspect}" do
46
+ trace :projection, "matching condition fact_type #{condition.inspect}" do
47
47
  fact_type = condition.match_existing_fact_type @context
48
48
  raise "Unrecognised fact type #{condition.inspect} in #{self.class}" unless fact_type
49
49
  end
@@ -131,7 +131,7 @@ module ActiveFacts
131
131
  @existing_clauses = [first_clause]
132
132
  elsif (n = @clauses.size - @existing_clauses.size) > 0
133
133
  raise "Cannot extend a negated fact type" if @existing_clauses.detect {|clause| clause.certainty == false }
134
- debug :binding, "Extending existing fact type with #{n} new readings"
134
+ trace :binding, "Extending existing fact type with #{n} new readings"
135
135
  end
136
136
 
137
137
  # Now make any new readings:
@@ -236,7 +236,7 @@ module ActiveFacts
236
236
  return nil if fact_types.empty? # There are no matched fact types
237
237
 
238
238
  if @clauses.size == 1 && @existing_clauses[0].side_effects.cost != 0
239
- debug :matching, "There's only a single clause, but it's not an exact match"
239
+ trace :matching, "There's only a single clause, but it's not an exact match"
240
240
  return nil
241
241
  end
242
242
 
@@ -283,9 +283,9 @@ module ActiveFacts
283
283
 
284
284
  # We need to check uniqueness constraints after processing the whole vocabulary
285
285
  # raise "Fact type must be named as it has no identifying uniqueness constraint" unless @name || @fact_type.all_role.size == 1
286
- debug :constraint, "Need to check #{@fact_type.default_reading.inspect} for a uniqueness constraint"
286
+ trace :constraint, "Need to check #{@fact_type.default_reading.inspect} for a uniqueness constraint"
287
287
  fact_type.check_and_add_spanning_uniqueness_constraint = proc do
288
- debug :constraint, "Checking #{@fact_type.default_reading.inspect} for a uniqueness constraint"
288
+ trace :constraint, "Checking #{@fact_type.default_reading.inspect} for a uniqueness constraint"
289
289
  existing_pc = nil
290
290
  found = @fact_type.all_role.
291
291
  detect do |role|
@@ -312,9 +312,9 @@ module ActiveFacts
312
312
  :max_frequency => 1,
313
313
  :is_preferred_identifier => true # (prefer || !!@fact_type.entity_type)
314
314
  )
315
- debug :constraint, "Made new fact type implicit PC GUID=#{pc.concept.guid} #{pc.name} min=nil max=1 over #{rs.describe}"
315
+ trace :constraint, "Made new fact type implicit PC GUID=#{pc.concept.guid} #{pc.name} min=nil max=1 over #{rs.describe}"
316
316
  elsif pc
317
- debug :constraint, "Will rely on existing UC GUID=#{pc.concept.guid} #{pc.name} to be used as PI over #{rs.describe}"
317
+ trace :constraint, "Will rely on existing UC GUID=#{pc.concept.guid} #{pc.name} to be used as PI over #{rs.describe}"
318
318
  end
319
319
  end
320
320
  end
@@ -349,7 +349,7 @@ module ActiveFacts
349
349
  clauses_m = clauses_by_refs[variants[m]]
350
350
  l_keys = variants[l]-common
351
351
  m_keys = variants[m]-common
352
- debug :binding, "Try to collapse variant #{m} onto #{l}; diffs are #{l_keys.inspect} -> #{m_keys.inspect}"
352
+ trace :binding, "Try to collapse variant #{m} onto #{l}; diffs are #{l_keys.inspect} -> #{m_keys.inspect}"
353
353
  rebindings = 0
354
354
  l_keys.each_with_index do |l_key, i|
355
355
  # Find possible rebinding candidates; there must be exactly one.
@@ -358,19 +358,19 @@ module ActiveFacts
358
358
  m_key = m_keys[j]
359
359
  l_ref = refs_by_clause_and_key[[clauses_l[0], l_key]]
360
360
  m_ref = refs_by_clause_and_key[[clauses_m[0], m_key]]
361
- debug :binding, "Can we match #{l_ref.inspect} (#{i}) with #{m_ref.inspect} (#{j})?"
361
+ trace :binding, "Can we match #{l_ref.inspect} (#{i}) with #{m_ref.inspect} (#{j})?"
362
362
  next if m_ref.player != l_ref.player
363
363
  if has_more_adjectives(m_ref, l_ref)
364
- debug :binding, "can rebind #{m_ref.inspect} to #{l_ref.inspect}"
364
+ trace :binding, "can rebind #{m_ref.inspect} to #{l_ref.inspect}"
365
365
  candidates << [m_ref, l_ref]
366
366
  elsif has_more_adjectives(l_ref, m_ref)
367
- debug :binding, "can rebind #{l_ref.inspect} to #{m_ref.inspect}"
367
+ trace :binding, "can rebind #{l_ref.inspect} to #{m_ref.inspect}"
368
368
  candidates << [l_ref, m_ref]
369
369
  end
370
370
  end
371
371
 
372
- # debug :binding, "found #{candidates.size} rebinding candidates for this role"
373
- debug :binding, "rebinding is ambiguous so not attempted" if candidates.size > 1
372
+ # trace :binding, "found #{candidates.size} rebinding candidates for this role"
373
+ trace :binding, "rebinding is ambiguous so not attempted" if candidates.size > 1
374
374
  if (candidates.size == 1)
375
375
  candidates[0][0].rebind_to(@context, candidates[0][1])
376
376
  rebindings += 1
@@ -379,7 +379,7 @@ module ActiveFacts
379
379
  end
380
380
  if (rebindings == l_keys.size)
381
381
  # Successfully rebound this fact type
382
- debug :binding, "Successfully rebound clauses #{clauses_l.map{|r|r.inspect}*'; '} on to #{clauses_m.map{|r|r.inspect}*'; '}"
382
+ trace :binding, "Successfully rebound clauses #{clauses_l.map{|r|r.inspect}*'; '} on to #{clauses_m.map{|r|r.inspect}*'; '}"
383
383
  break
384
384
  else
385
385
  # No point continuing, we failed on this one.
@@ -4,11 +4,11 @@ module ActiveFacts
4
4
  class Definition
5
5
  # Make a Variable for every binding present in these clauses
6
6
  def build_variables(clauses_list)
7
- debug :query, "Building variables" do
7
+ trace :query, "Building variables" do
8
8
  query = @constellation.Query(:new)
9
9
  all_bindings_in_clauses(clauses_list).
10
10
  each do |binding|
11
- debug :query, "Creating variable #{query.all_variable.size} for #{binding.inspect}"
11
+ trace :query, "Creating variable #{query.all_variable.size} for #{binding.inspect}"
12
12
  binding.variable = @constellation.Variable(query, query.all_variable.size, :object_type => binding.player)
13
13
  if literal = binding.refs.detect{|r| r.literal}
14
14
  if literal.kind_of?(ActiveFacts::CQL::Compiler::Reference)
@@ -25,7 +25,7 @@ module ActiveFacts
25
25
 
26
26
  def build_all_steps(clauses_list)
27
27
  roles_by_binding = {}
28
- debug :query, "Building steps" do
28
+ trace :query, "Building steps" do
29
29
  clauses_list.each do |clause|
30
30
  next if clause.is_naked_object_type
31
31
  build_steps(clause, roles_by_binding)
@@ -37,7 +37,7 @@ module ActiveFacts
37
37
  def build_steps clause, roles_by_binding = {}, objectification_node = nil
38
38
  plays = []
39
39
  incidental_roles = []
40
- debug :query, "Creating Role Sequence for #{clause.inspect} with #{clause.refs.size} role refs" do
40
+ trace :query, "Creating Role Sequence for #{clause.inspect} with #{clause.refs.size} role refs" do
41
41
  objectification_step = nil
42
42
  clause.refs.each do |ref|
43
43
  # These refs are the Compiler::References, which have associated Metamodel::RoleRefs,
@@ -62,7 +62,7 @@ module ActiveFacts
62
62
  end
63
63
  refs_count += objectification_ref_count
64
64
 
65
- debug :query, "Creating Variable #{ref.inspect} (counts #{refs_count}/#{objectification_ref_count}) and objectification Step for #{ref.inspect}" do
65
+ trace :query, "Creating Variable #{ref.inspect} (counts #{refs_count}/#{objectification_ref_count}) and objectification Step for #{ref.inspect}" do
66
66
 
67
67
  raise "Internal error: Trying to add role of #{role.object_type.name} to variable for #{binding.variable.object_type.name}" unless binding.variable.object_type == role.object_type
68
68
  play = @constellation.Play(binding.variable, role)
@@ -81,7 +81,7 @@ module ActiveFacts
81
81
  # This is an implicit objectification, just the FT clause, not ET(where ...clause...)
82
82
  # We need to create a Variable for this object, even though it has no References
83
83
  query = binding.variable.query
84
- debug :query, "Creating JN#{query.all_variable.size} for #{clause.fact_type.entity_type.name} in objectification"
84
+ trace :query, "Creating JN#{query.all_variable.size} for #{clause.fact_type.entity_type.name} in objectification"
85
85
  objectification_node = @constellation.Variable(query, query.all_variable.size, :object_type => clause.fact_type.entity_type)
86
86
  end
87
87
  raise "Internal error: Trying to add role of #{role.link_fact_type.all_role.single.object_type.name} to variable for #{objectification_node.object_type.name}" unless objectification_node.object_type == role.link_fact_type.all_role.single.object_type
@@ -99,14 +99,14 @@ module ActiveFacts
99
99
  if clause.certainty == nil
100
100
  objectification_step.is_optional = true
101
101
  end
102
- debug :query, "New #{objectification_step.describe}"
103
- debug :query, "Associating #{incidental_roles.map(&:describe)*', '} incidental roles with #{objectification_step.describe}" if incidental_roles.size > 0
102
+ trace :query, "New #{objectification_step.describe}"
103
+ trace :query, "Associating #{incidental_roles.map(&:describe)*', '} incidental roles with #{objectification_step.describe}" if incidental_roles.size > 0
104
104
  incidental_roles.each { |jr| jr.step = objectification_step }
105
105
  incidental_roles = []
106
106
  plays = []
107
107
  end
108
108
  else
109
- debug :query, "Creating Reference for #{ref.inspect}" do
109
+ trace :query, "Creating Reference for #{ref.inspect}" do
110
110
  # REVISIT: If there's an implicit subtyping step here, create it; then always raise the error here.
111
111
  # I don't want to do this for now because the verbaliser will always verbalise all steps.
112
112
  if binding.variable.object_type != role.object_type and
@@ -129,7 +129,7 @@ module ActiveFacts
129
129
  # Each of these ImplicitFactTypes has a single phantom role played by the objectifying entity type
130
130
  # One of these phantom roles is likely to be the subject of an objectification step.
131
131
  ref.nested_clauses.each do |r|
132
- debug :query, "Building objectification step for #{ref.nested_clauses.inspect}" do
132
+ trace :query, "Building objectification step for #{ref.nested_clauses.inspect}" do
133
133
  build_steps r, roles_by_binding, binding.variable
134
134
  end
135
135
  end
@@ -154,8 +154,8 @@ module ActiveFacts
154
154
  :is_disallowed => clause.certainty == false,
155
155
  :is_optional => clause.certainty == nil
156
156
  )
157
- debug :query, "New #{js.describe}"
158
- debug :query, "Associating #{incidental_roles.map(&:describe)*', '} incidental roles with #{js.describe}" if incidental_roles.size > 0
157
+ trace :query, "New #{js.describe}"
158
+ trace :query, "Associating #{incidental_roles.map(&:describe)*', '} incidental roles with #{js.describe}" if incidental_roles.size > 0
159
159
  incidental_roles.each { |jr| jr.step = js }
160
160
  end
161
161
  roles_by_binding
@@ -35,6 +35,15 @@ module ActiveFacts
35
35
  def <=>(other)
36
36
  key <=> other.key
37
37
  end
38
+
39
+ def add_ref ref
40
+ @refs << ref
41
+ ref
42
+ end
43
+
44
+ def delete_ref ref
45
+ @refs.delete ref
46
+ end
38
47
  end
39
48
 
40
49
  class CompilationContext
@@ -29,9 +29,9 @@ module ActiveFacts
29
29
  end
30
30
  @offset = nil if @offset.to_f == 0
31
31
 
32
- debug :units, "Defining new unit #{@singular}#{@plural ? "/"+@plural : ""}" do
33
- debug :units, "Coefficient is #{coefficient.numerator}#{coefficient.denominator != 1 ? "/#{coefficient.denominator}" : ""} #{coefficient.is_precise ? "exactly" : "approximately"}" if coefficient
34
- debug :units, "Offset is #{@offset}" if @offset
32
+ trace :units, "Defining new unit #{@singular}#{@plural ? "/"+@plural : ""}" do
33
+ trace :units, "Coefficient is #{coefficient.numerator}#{coefficient.denominator != 1 ? "/#{coefficient.denominator}" : ""} #{coefficient.is_precise ? "exactly" : "approximately"}" if coefficient
34
+ trace :units, "Offset is #{@offset}" if @offset
35
35
  raise "Redefinition of unit #{@singular}" if @constellation.Unit.values.detect{|u| u.name == @singular}
36
36
  raise "Redefinition of unit #{@plural}" if @constellation.Unit.values.detect{|u| u.name == @plural}
37
37
  unit = @constellation.Unit(:new,
@@ -45,7 +45,7 @@ module ActiveFacts
45
45
  )
46
46
  @base_units.each do |base_unit, exponent|
47
47
  base = @constellation.Unit.values.detect{|u| u.name == base_unit || u.plural_name == base_unit }
48
- debug :units, "Base unit #{base_unit}^#{exponent} #{base ? "" : "(implicitly fundamental)"}"
48
+ trace :units, "Base unit #{base_unit}^#{exponent} #{base ? "" : "(implicitly fundamental)"}"
49
49
  base ||= @constellation.Unit(:new, :name => base_unit, :is_fundamental => true, :vocabulary => @vocabulary)
50
50
  @constellation.Derivation(:derived_unit => unit, :base_unit => base, :exponent => exponent)
51
51
  end
@@ -58,12 +58,12 @@ module ActiveFacts
58
58
  end
59
59
 
60
60
  def object_type(name, kind)
61
- index_name(@terms, name) && debug(:context, "new #{kind} '#{name}'")
61
+ index_name(@terms, name) && trace(:context, "new #{kind} '#{name}'")
62
62
  true
63
63
  end
64
64
 
65
65
  def reset_role_names
66
- debug :context, "\tresetting role names #{@role_names.keys.sort*", "}" if @role_names && @role_names.size > 0
66
+ trace :context, "\tresetting role names #{@role_names.keys.sort*", "}" if @role_names && @role_names.size > 0
67
67
  @role_names = {}
68
68
  end
69
69
 
@@ -72,17 +72,17 @@ module ActiveFacts
72
72
  end
73
73
 
74
74
  def new_leading_adjective_term(adj, term)
75
- index_name(@role_names, "#{adj} #{term}", term) && debug(:context, "new compound term '#{adj}- #{term}'")
75
+ index_name(@role_names, "#{adj} #{term}", term) && trace(:context, "new compound term '#{adj}- #{term}'")
76
76
  true
77
77
  end
78
78
 
79
79
  def new_trailing_adjective_term(adj, term)
80
- index_name(@role_names, "#{term} #{adj}", term) && debug(:context, "new compound term '#{term} -#{adj}'")
80
+ index_name(@role_names, "#{term} #{adj}", term) && trace(:context, "new compound term '#{term} -#{adj}'")
81
81
  true
82
82
  end
83
83
 
84
84
  def role_name(name)
85
- index_name(@role_names, name) && debug(:context, "new role '#{name}'")
85
+ index_name(@role_names, name) && trace(:context, "new role '#{name}'")
86
86
  true
87
87
  end
88
88
 
@@ -98,11 +98,11 @@ module ActiveFacts
98
98
  @global_term = @term = @term_part
99
99
  @context_saver.context = {:term => @term, :global_term => @global_term }
100
100
  end
101
- debug :context, "Term #{t[s] ? "is" : "starts"} '#{@term_part}'"
101
+ trace :context, "Term #{t[s] ? "is" : "starts"} '#{@term_part}'"
102
102
  elsif @allowed_forward_terms.include?(@term_part)
103
103
  @term = @term_part
104
104
  @context_saver.context = {:term => @term, :global_term => @term }
105
- debug :context, "Term #{s} is an allowed forward"
105
+ trace :context, "Term #{s} is an allowed forward"
106
106
  return true
107
107
  end
108
108
  t
@@ -119,13 +119,13 @@ module ActiveFacts
119
119
  w = "role_name"
120
120
  end
121
121
  if t
122
- debug :context, "Multi-word #{w} #{t[@term_part] ? 'ends at' : 'continues to'} #{@term_part.inspect}"
122
+ trace :context, "Multi-word #{w} #{t[@term_part] ? 'ends at' : 'continues to'} #{@term_part.inspect}"
123
123
 
124
124
  # Record the name of the full term and the underlying global term:
125
125
  if t[@term_part]
126
126
  @term = @term_part if t[@term_part]
127
127
  @global_term = (t = t[@term_part]) == true ? @term_part : t
128
- debug :context, "saving context #{@term}/#{@global_term}"
128
+ trace :context, "saving context #{@term}/#{@global_term}"
129
129
  @context_saver.context = {:term => @term, :global_term => @global_term }
130
130
  end
131
131
  end
@@ -15,8 +15,9 @@ module ActiveFacts
15
15
  # afgen --cql <file>.cql
16
16
  class CQL < Helpers::OrderedDumper
17
17
  private
18
- def vocabulary_start(vocabulary)
19
- puts "vocabulary #{vocabulary.name};\n\n"
18
+ def vocabulary_start
19
+ puts "vocabulary #{@vocabulary.name};\n\n"
20
+ build_indices
20
21
  end
21
22
 
22
23
  def vocabulary_end
@@ -56,7 +57,7 @@ module ActiveFacts
56
57
  end
57
58
 
58
59
  def entity_type_dump(o)
59
- @object_types_dumped[o] = true
60
+ o.ordered_dumped!
60
61
  pi = o.preferred_identifier
61
62
 
62
63
  supers = o.supertypes
@@ -67,7 +68,7 @@ module ActiveFacts
67
68
  else
68
69
  non_subtype_dump(o, pi)
69
70
  end
70
- @constraints_used[pi] = true
71
+ pi.ordered_dumped! if pi
71
72
  end
72
73
 
73
74
  def append_ring_to_reading(reading, ring)
@@ -124,8 +125,8 @@ module ActiveFacts
124
125
  end
125
126
  end
126
127
  end
127
- debug :mode, "Didn't find standard forward reading" unless forward_reading
128
- debug :mode, "Didn't find standard reverse reading" unless reverse_reading
128
+ trace :mode, "Didn't find standard forward reading" unless forward_reading
129
+ trace :mode, "Didn't find standard reverse reading" unless reverse_reading
129
130
  [forward_reading, reverse_reading]
130
131
  end
131
132
 
@@ -149,7 +150,7 @@ module ActiveFacts
149
150
  return nil if entity_type.fact_type and
150
151
  entity_type.fact_type.all_role.detect{|role| role.object_type == value_role.object_type }
151
152
 
152
- @fact_types_dumped[fact_type] = true # We've covered this fact type
153
+ fact_type.ordered_dumped! # We've covered this fact type
153
154
 
154
155
  # Elide the constraints that would have been emitted on the standard readings.
155
156
  # If there is a UC that's not in the standard form for a reference mode,
@@ -158,13 +159,13 @@ module ActiveFacts
158
159
  fact_constraints.each do |pc|
159
160
  if (pc.role_sequence.all_role_ref.size == 1 and pc.max_frequency == 1)
160
161
  # It's a uniqueness constraint, and will be regenerated
161
- @constraints_used[pc] = true
162
+ pc.ordered_dumped!
162
163
  end
163
164
  end
164
165
 
165
166
  # Figure out which non-standard readings exist, if any:
166
167
  nonstandard_readings = fact_type.all_reading - [forward_reading, reverse_reading]
167
- debug :mode, "--- nonstandard_readings.size now = #{nonstandard_readings.size}" if nonstandard_readings.size > 0
168
+ trace :mode, "--- nonstandard_readings.size now = #{nonstandard_readings.size}" if nonstandard_readings.size > 0
168
169
 
169
170
  verbaliser = ActiveFacts::Metamodel::Verbaliser.new
170
171
 
@@ -214,7 +215,7 @@ module ActiveFacts
214
215
  identifying_facts.each do |fact_type|
215
216
  # The RoleRefs for corresponding roles across all readings are for the same player.
216
217
  verbaliser.alternate_readings fact_type.all_reading
217
- @fact_types_dumped[fact_type] = true unless fact_type.entity_type # Must dump objectification still!
218
+ fact_type.ordered_dumped! unless fact_type.entity_type # Must dump objectification still!
218
219
  end
219
220
  verbaliser.create_subscripts(:rolenames)
220
221
 
@@ -304,7 +305,7 @@ module ActiveFacts
304
305
  if reading_texts.size > 1
305
306
  ambiguity =
306
307
  fact_type.all_role.to_a[0].object_type.all_role.map{|r| r.fact_type}.
307
- select{|f| f != fact_type && @fact_types_dumped.include?(f) }.
308
+ select{|f| f != fact_type && f.ordered_dumped }.
308
309
  detect do |dft|
309
310
  ambiguous_readings =
310
311
  reading_texts & dft.all_reading.map{|r| naiive_expand(r)}
@@ -397,16 +398,16 @@ module ActiveFacts
397
398
 
398
399
  # Tell the verbaliser all we know, so it can figure out which players to subscript:
399
400
  players = []
400
- debug :subscript, "Preparing query across projected roles in set comparison constraint" do
401
+ trace :subscript, "Preparing query across projected roles in set comparison constraint" do
401
402
  transposed_role_refs.each do |role_refs|
402
403
  verbaliser.role_refs_have_subtype_steps role_refs
403
404
  join_over, = ActiveFacts::Metamodel.plays_over(role_refs.map{|rr| rr.role})
404
405
  players << join_over
405
406
  end
406
407
  end
407
- debug :subscript, "Preparing query between roles in set comparison constraint" do
408
+ trace :subscript, "Preparing query between roles in set comparison constraint" do
408
409
  role_sequences.each do |role_sequence|
409
- debug :subscript, "role sequence is #{role_sequence.describe}" do
410
+ trace :subscript, "role sequence is #{role_sequence.describe}" do
410
411
  verbaliser.prepare_role_sequence role_sequence
411
412
  end
412
413
  end
@@ -559,8 +560,8 @@ module ActiveFacts
559
560
  value_constraints = []
560
561
  roles.each do |role|
561
562
  value_constraints <<
562
- if vc = role.role_value_constraint and !@constraints_used[vc]
563
- @constraints_used[vc] = true
563
+ if vc = role.role_value_constraint and !vc.ordered_dumped
564
+ vc.ordered_dumped!
564
565
  vc.describe
565
566
  else
566
567
  nil
@@ -571,10 +572,10 @@ module ActiveFacts
571
572
  constraint = fact_constraints.
572
573
  detect do |c| # Find a UC that spans all other Roles
573
574
  c.is_a?(ActiveFacts::Metamodel::PresenceConstraint) &&
574
- !@constraints_used[c] && # Already verbalised
575
+ !c.ordered_dumped && # Already verbalised
575
576
  roles-c.role_sequence.all_role_ref.map(&:role) == [role]
576
577
  end
577
- @constraints_used[constraint] = true if constraint
578
+ constraint.ordered_dumped! if constraint
578
579
  constraint && constraint.frequency
579
580
  else
580
581
  nil
@@ -585,8 +586,8 @@ module ActiveFacts
585
586
  expanded = "it is not the case that "+expanded if (reading.is_negative)
586
587
 
587
588
  if (ft_rings = @ring_constraints_by_fact[reading.fact_type]) &&
588
- (ring = ft_rings.detect{|rc| !@constraints_used[rc]})
589
- @constraints_used[ring] = true
589
+ (ring = ft_rings.detect{|rc| !rc.ordered_dumped})
590
+ ring.ordered_dumped!
590
591
  append_ring_to_reading(expanded, ring)
591
592
  end
592
593
  expanded
@@ -609,6 +610,26 @@ module ActiveFacts
609
610
  expanded
610
611
  end
611
612
 
613
+ def build_indices
614
+ @presence_constraints_by_fact = Hash.new{ |h, k| h[k] = [] }
615
+ @ring_constraints_by_fact = Hash.new{ |h, k| h[k] = [] }
616
+
617
+ @vocabulary.all_constraint.each { |c|
618
+ case c
619
+ when ActiveFacts::Metamodel::PresenceConstraint
620
+ fact_types = c.role_sequence.all_role_ref.map{|rr| rr.role.fact_type}.uniq # All fact types spanned by this constraint
621
+ if fact_types.size == 1 # There's only one, save it:
622
+ # trace "Single-fact constraint on #{fact_types[0].concept.guid}: #{c.name}"
623
+ (@presence_constraints_by_fact[fact_types[0]] ||= []) << c
624
+ end
625
+ when ActiveFacts::Metamodel::RingConstraint
626
+ (@ring_constraints_by_fact[c.role.fact_type] ||= []) << c
627
+ else
628
+ # trace "Found unhandled constraint #{c.class} #{c.name}"
629
+ end
630
+ }
631
+ end
632
+
612
633
  end
613
634
  end
614
635