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.
- checksums.yaml +5 -13
- data/Rakefile +2 -2
- data/bin/afgen +1 -1
- data/bin/cql +118 -27
- data/examples/CQL/Insurance.cql +2 -2
- data/examples/CQL/Metamodel.cql +3 -3
- data/examples/CQL/SchoolActivities.cql +1 -1
- data/examples/CQL/Warehousing.cql +5 -4
- data/lib/activefacts/cql.rb +1 -1
- data/lib/activefacts/cql/Language/English.treetop +2 -1
- data/lib/activefacts/cql/compiler.rb +6 -6
- data/lib/activefacts/cql/compiler/clause.rb +69 -46
- data/lib/activefacts/cql/compiler/constraint.rb +14 -14
- data/lib/activefacts/cql/compiler/entity_type.rb +24 -24
- data/lib/activefacts/cql/compiler/fact.rb +40 -27
- data/lib/activefacts/cql/compiler/fact_type.rb +16 -16
- data/lib/activefacts/cql/compiler/query.rb +12 -12
- data/lib/activefacts/cql/compiler/shared.rb +9 -0
- data/lib/activefacts/cql/compiler/value_type.rb +4 -4
- data/lib/activefacts/cql/parser.rb +9 -9
- data/lib/activefacts/generate/cql.rb +41 -20
- data/lib/activefacts/generate/helpers/oo.rb +33 -70
- data/lib/activefacts/generate/helpers/ordered.rb +61 -87
- data/lib/activefacts/generate/ruby.rb +12 -72
- data/lib/activefacts/generate/transform/surrogate.rb +13 -13
- data/lib/activefacts/input/orm.rb +72 -71
- data/lib/activefacts/persistence/columns.rb +66 -31
- data/lib/activefacts/persistence/foreignkey.rb +6 -6
- data/lib/activefacts/persistence/index.rb +12 -12
- data/lib/activefacts/persistence/object_type.rb +15 -12
- data/lib/activefacts/persistence/reference.rb +20 -18
- data/lib/activefacts/persistence/tables.rb +40 -36
- data/lib/activefacts/support.rb +69 -123
- data/lib/activefacts/version.rb +2 -2
- data/lib/activefacts/vocabulary/extensions.rb +42 -39
- data/lib/activefacts/vocabulary/metamodel.rb +11 -1
- data/lib/activefacts/vocabulary/verbaliser.rb +28 -28
- data/spec/cql/contractions_spec.rb +1 -1
- data/spec/cql/entity_type_spec.rb +1 -1
- data/spec/cql/fact_type_matching_spec.rb +3 -3
- data/spec/cql/role_matching_spec.rb +4 -4
- data/spec/cql/samples_spec.rb +2 -2
- data/spec/cql_cql_spec.rb +1 -1
- data/spec/helpers/array_matcher.rb +1 -1
- data/spec/norma_ruby_sql_spec.rb +2 -2
- data/spec/norma_tables_spec.rb +3 -2
- metadata +47 -68
@@ -13,7 +13,7 @@ module ActiveFacts
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def prepare_roles clauses = nil
|
16
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
#
|
373
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
103
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
158
|
-
|
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
|
@@ -29,9 +29,9 @@ module ActiveFacts
|
|
29
29
|
end
|
30
30
|
@offset = nil if @offset.to_f == 0
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
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) &&
|
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
|
-
|
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) &&
|
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) &&
|
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) &&
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
128
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 &&
|
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
|
-
|
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
|
-
|
408
|
+
trace :subscript, "Preparing query between roles in set comparison constraint" do
|
408
409
|
role_sequences.each do |role_sequence|
|
409
|
-
|
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
|
563
|
-
|
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
|
-
|
575
|
+
!c.ordered_dumped && # Already verbalised
|
575
576
|
roles-c.role_sequence.all_role_ref.map(&:role) == [role]
|
576
577
|
end
|
577
|
-
|
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|
|
589
|
-
|
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
|
|