activefacts 1.1.0 → 1.2.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 +4 -4
- data/bin/cql +137 -91
- data/css/style.css +3 -3
- data/examples/CQL/Insurance.cql +1 -1
- data/examples/CQL/SeparateSubtype.cql +2 -2
- data/lib/activefacts/cql/Language/English.treetop +9 -0
- data/lib/activefacts/cql/ObjectTypes.treetop +1 -1
- data/lib/activefacts/cql/Terms.treetop +3 -1
- data/lib/activefacts/cql/ValueTypes.treetop +10 -4
- data/lib/activefacts/cql/compiler.rb +1 -0
- data/lib/activefacts/cql/compiler/clause.rb +53 -23
- data/lib/activefacts/cql/compiler/entity_type.rb +0 -4
- data/lib/activefacts/cql/compiler/expression.rb +9 -13
- data/lib/activefacts/cql/compiler/fact.rb +49 -48
- data/lib/activefacts/cql/compiler/fact_type.rb +23 -20
- data/lib/activefacts/cql/compiler/query.rb +49 -121
- data/lib/activefacts/cql/compiler/shared.rb +5 -1
- data/lib/activefacts/cql/compiler/value_type.rb +4 -2
- data/lib/activefacts/generate/rails/schema.rb +138 -108
- data/lib/activefacts/generate/transform/surrogate.rb +1 -2
- data/lib/activefacts/mapping/rails.rb +52 -45
- data/lib/activefacts/persistence/columns.rb +5 -5
- data/lib/activefacts/persistence/tables.rb +6 -4
- data/lib/activefacts/support.rb +0 -2
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +64 -42
- data/lib/activefacts/vocabulary/metamodel.rb +14 -12
- data/lib/activefacts/vocabulary/verbaliser.rb +98 -92
- data/spec/cql/expressions_spec.rb +8 -3
- data/spec/cql/parser/entity_types_spec.rb +1 -1
- data/spec/cql/parser/expressions_spec.rb +66 -52
- data/spec/cql/parser/fact_types_spec.rb +1 -1
- data/spec/cql/parser/literals_spec.rb +10 -10
- data/spec/cql/parser/pragmas_spec.rb +3 -3
- data/spec/cql/parser/value_types_spec.rb +1 -1
- metadata +2 -2
@@ -66,8 +66,7 @@ module ActiveFacts
|
|
66
66
|
|
67
67
|
class ValueType
|
68
68
|
def needs_surrogate
|
69
|
-
|
70
|
-
!(supertype_names.include?('Auto Counter') or supertype_names.include?('Guid') or supertype_names.include?('ID'))
|
69
|
+
!is_auto_assigned
|
71
70
|
end
|
72
71
|
|
73
72
|
def inject_surrogate
|
@@ -4,52 +4,7 @@ require 'active_support'
|
|
4
4
|
require 'digest/sha1'
|
5
5
|
|
6
6
|
module ActiveFacts
|
7
|
-
|
8
7
|
module Persistence
|
9
|
-
# Return ActiveRecord type and (modified?) length for the passed base type
|
10
|
-
def self.rails_type(type, length)
|
11
|
-
rails_type = case type
|
12
|
-
when /^Auto ?Counter$/
|
13
|
-
'integer' # REVISIT: Need to detect surrogate ID fields and handle them correctly
|
14
|
-
|
15
|
-
when /^Unsigned ?Integer$/,
|
16
|
-
/^Integer$/,
|
17
|
-
/^Signed ?Integer$/,
|
18
|
-
/^Unsigned ?Small ?Integer$/,
|
19
|
-
/^Signed ?Small ?Integer$/,
|
20
|
-
/^Unsigned ?Tiny ?Integer$/
|
21
|
-
length = nil
|
22
|
-
'integer'
|
23
|
-
|
24
|
-
when /^Decimal$/
|
25
|
-
'decimal'
|
26
|
-
|
27
|
-
when /^Fixed ?Length ?Text$/, /^Char$/
|
28
|
-
'string'
|
29
|
-
when /^Variable ?Length ?Text$/, /^String$/
|
30
|
-
'string'
|
31
|
-
when /^Large ?Length ?Text$/, /^Text$/
|
32
|
-
'text'
|
33
|
-
|
34
|
-
when /^Date ?And ?Time$/, /^Date ?Time$/
|
35
|
-
'datetime'
|
36
|
-
when /^Date$/
|
37
|
-
'datetime'
|
38
|
-
when /^Time$/
|
39
|
-
'time'
|
40
|
-
when /^Auto ?Time ?Stamp$/
|
41
|
-
'timestamp'
|
42
|
-
|
43
|
-
when /^Money$/
|
44
|
-
'decimal'
|
45
|
-
when /^Picture ?Raw ?Data$/, /^Image$/, /^Variable ?Length ?Raw ?Data$/, /^Blob$/
|
46
|
-
'binary'
|
47
|
-
when /^BIT$/
|
48
|
-
'boolean'
|
49
|
-
else type # raise "ActiveRecord type unknown for standard type #{type}"
|
50
|
-
end
|
51
|
-
[rails_type, length]
|
52
|
-
end
|
53
8
|
|
54
9
|
def self.rails_plural_name name
|
55
10
|
# Crunch spaces and pluralise the first part, all in snake_case
|
@@ -74,6 +29,58 @@ module ActiveFacts
|
|
74
29
|
def rails_name
|
75
30
|
Persistence::rails_singular_name(name('_'))
|
76
31
|
end
|
32
|
+
|
33
|
+
def rails_type
|
34
|
+
type_name, params, constraints = *type()
|
35
|
+
rails_type = case type_name
|
36
|
+
when /^Auto ?Counter$/i
|
37
|
+
'serial' # REVISIT: Need to detect surrogate ID fields and handle them correctly
|
38
|
+
|
39
|
+
when /^[Ug]uid$/i
|
40
|
+
'uuid'
|
41
|
+
|
42
|
+
when /^Unsigned ?Integer$/i,
|
43
|
+
/^Integer$/i,
|
44
|
+
/^Signed ?Integer$/i,
|
45
|
+
/^Unsigned ?Small ?Integer$/i,
|
46
|
+
/^Signed ?Small ?Integer$/i,
|
47
|
+
/^Unsigned ?Tiny ?Integer$/i
|
48
|
+
length = nil
|
49
|
+
'integer'
|
50
|
+
|
51
|
+
when /^Decimal$/i
|
52
|
+
'decimal'
|
53
|
+
|
54
|
+
when /^Float$/i, /^Double$/i, /^Real$/i
|
55
|
+
'float'
|
56
|
+
|
57
|
+
when /^Fixed ?Length ?Text$/i, /^Char$/i
|
58
|
+
'string'
|
59
|
+
when /^Variable ?Length ?Text$/i, /^String$/i
|
60
|
+
'string'
|
61
|
+
when /^Large ?Length ?Text$/i, /^Text$/i
|
62
|
+
'text'
|
63
|
+
|
64
|
+
when /^Date ?And ?Time$/i, /^Date ?Time$/i
|
65
|
+
'datetime'
|
66
|
+
when /^Date$/i
|
67
|
+
'datetime'
|
68
|
+
when /^Time$/i
|
69
|
+
'time'
|
70
|
+
when /^Auto ?Time ?Stamp$/i
|
71
|
+
'timestamp'
|
72
|
+
|
73
|
+
when /^Money$/i
|
74
|
+
'decimal'
|
75
|
+
when /^Picture ?Raw ?Data$/i, /^Image$/i, /^Variable ?Length ?Raw ?Data$/i, /^Blob$/i
|
76
|
+
'binary'
|
77
|
+
when /^BIT$/i, /^Boolean$/i
|
78
|
+
'boolean'
|
79
|
+
else
|
80
|
+
type_name # raise "ActiveRecord type unknown for standard type #{type}"
|
81
|
+
end
|
82
|
+
[rails_type, params[:length]]
|
83
|
+
end
|
77
84
|
end
|
78
85
|
|
79
86
|
class Index
|
@@ -163,15 +163,15 @@ module ActiveFacts
|
|
163
163
|
end
|
164
164
|
|
165
165
|
vt = references[-1].is_self_value ? references[-1].from : references[-1].to
|
166
|
-
|
167
|
-
params[:scale] ||= vt.scale if vt.scale.to_i != 0
|
168
|
-
while vt.supertype
|
166
|
+
begin
|
169
167
|
params[:length] ||= vt.length if vt.length.to_i != 0
|
170
168
|
params[:scale] ||= vt.scale if vt.scale.to_i != 0
|
171
169
|
constraints << vt.value_constraint if vt.value_constraint
|
170
|
+
last_vt = vt
|
172
171
|
vt = vt.supertype
|
173
|
-
end
|
174
|
-
|
172
|
+
end while vt
|
173
|
+
params[:underlying_type] = last_vt
|
174
|
+
return [last_vt.name, params, constraints]
|
175
175
|
end
|
176
176
|
|
177
177
|
# The comment is the readings from the References expressed as a series of steps (not a full verbalisation)
|
@@ -46,13 +46,15 @@ module ActiveFacts
|
|
46
46
|
@is_table
|
47
47
|
end
|
48
48
|
|
49
|
-
# Is this ValueType auto-assigned on first save to the database?
|
49
|
+
# Is this ValueType auto-assigned either at assert or on first save to the database?
|
50
50
|
def is_auto_assigned
|
51
|
-
# REVISIT: Find a better way to determine AutoCounters (ValueType unary role?)
|
52
51
|
type = self
|
53
|
-
|
54
|
-
|
52
|
+
while type
|
53
|
+
return true if type.name =~ /^Auto/ || type.transaction_phase
|
54
|
+
type = type.supertype
|
55
|
+
end
|
55
56
|
end
|
57
|
+
false
|
56
58
|
end
|
57
59
|
|
58
60
|
class EntityType < DomainObjectType
|
data/lib/activefacts/support.rb
CHANGED
data/lib/activefacts/version.rb
CHANGED
@@ -265,6 +265,13 @@ module ActiveFacts
|
|
265
265
|
def subtypes_transitive
|
266
266
|
[self] + subtypes.map{|st| st.subtypes_transitive}.flatten
|
267
267
|
end
|
268
|
+
|
269
|
+
def common_supertype(other)
|
270
|
+
return nil unless other.is_?(ActiveFacts::Metamodel::ValueType)
|
271
|
+
return self if other.supertypes_transitive.include?(self)
|
272
|
+
return other if supertypes_transitive.include(other)
|
273
|
+
nil
|
274
|
+
end
|
268
275
|
end
|
269
276
|
|
270
277
|
class EntityType
|
@@ -478,6 +485,13 @@ module ActiveFacts
|
|
478
485
|
return nil
|
479
486
|
end
|
480
487
|
|
488
|
+
def common_supertype(other)
|
489
|
+
return nil unless other.is_?(ActiveFacts::Metamodel::EntityType)
|
490
|
+
candidates = supertypes_transitive & other.supertypes_transitive
|
491
|
+
return candidates[0] if candidates.size <= 1
|
492
|
+
candidates[0] # REVISIT: This might not be the closest supertype
|
493
|
+
end
|
494
|
+
|
481
495
|
# This entity type has just objectified a fact type. Create the necessary ImplicitFactTypes with phantom roles
|
482
496
|
def create_implicit_fact_types
|
483
497
|
fact_type.all_role.map do |role|
|
@@ -722,23 +736,26 @@ module ActiveFacts
|
|
722
736
|
"#{is_optional ? 'maybe ' : ''}" +
|
723
737
|
(is_unary_step ? '(unary) ' : "from #{input_play.describe} ") +
|
724
738
|
"#{is_disallowed ? 'not ' : ''}" +
|
725
|
-
"to #{
|
726
|
-
"
|
727
|
-
|
728
|
-
|
739
|
+
"to #{output_plays.map(&:describe)*', '}" +
|
740
|
+
(objectification_variable ? ", objectified as #{objectification_variable.describe}" : '') +
|
741
|
+
" '#{fact_type.default_reading}'"
|
742
|
+
end
|
743
|
+
|
744
|
+
def input_play
|
745
|
+
all_play.detect{|p| p.is_input}
|
746
|
+
end
|
747
|
+
|
748
|
+
def output_plays
|
749
|
+
all_play.reject{|p| p.is_input}
|
729
750
|
end
|
730
751
|
|
731
752
|
def is_unary_step
|
732
753
|
# Preserve this in case we have to use a real variable for the phantom
|
733
|
-
|
754
|
+
all_play.size == 1
|
734
755
|
end
|
735
756
|
|
736
757
|
def is_objectification_step
|
737
|
-
|
738
|
-
end
|
739
|
-
|
740
|
-
def all_play
|
741
|
-
[input_play, output_play].uniq + all_incidental_play.to_a
|
758
|
+
!!objectification_variable
|
742
759
|
end
|
743
760
|
|
744
761
|
def external_fact_type
|
@@ -750,38 +767,27 @@ module ActiveFacts
|
|
750
767
|
def describe
|
751
768
|
object_type.name +
|
752
769
|
(subscript ? "(#{subscript})" : '') +
|
753
|
-
"
|
770
|
+
" Var#{ordinal}" +
|
754
771
|
(value ? ' = '+value.to_s : '')
|
755
772
|
end
|
756
773
|
|
757
774
|
def all_step
|
758
|
-
all_play.map
|
759
|
-
jr.all_step_as_input_play.to_a +
|
760
|
-
jr.all_step_as_output_play.to_a
|
761
|
-
end.
|
762
|
-
flatten.
|
763
|
-
uniq
|
775
|
+
all_play.map(&:step).uniq
|
764
776
|
end
|
765
777
|
end
|
766
778
|
|
767
779
|
class Play
|
768
780
|
def describe
|
769
|
-
"#{role.object_type.name}
|
781
|
+
"#{role.object_type.name} Var#{variable.ordinal}" +
|
770
782
|
(role_ref ? " (projected)" : "")
|
771
783
|
end
|
772
|
-
|
773
|
-
def all_step
|
774
|
-
(all_step_as_input_play.to_a +
|
775
|
-
all_step_as_output_play.to_a +
|
776
|
-
[step]).flatten.compact.uniq
|
777
|
-
end
|
778
784
|
end
|
779
785
|
|
780
786
|
class Query
|
781
787
|
def show
|
782
788
|
steps_shown = {}
|
783
789
|
trace :query, "Displaying full contents of Query #{concept.guid}" do
|
784
|
-
all_variable.sort_by{|
|
790
|
+
all_variable.sort_by{|var| var.ordinal}.each do |variable|
|
785
791
|
trace :query, "#{variable.describe}" do
|
786
792
|
variable.all_step.
|
787
793
|
each do |step|
|
@@ -798,7 +804,7 @@ module ActiveFacts
|
|
798
804
|
end
|
799
805
|
|
800
806
|
def all_step
|
801
|
-
all_variable.map{|
|
807
|
+
all_variable.map{|var| var.all_step.to_a}.flatten.uniq
|
802
808
|
end
|
803
809
|
|
804
810
|
# Check all parts of this query for validity
|
@@ -808,7 +814,7 @@ module ActiveFacts
|
|
808
814
|
|
809
815
|
# Check the variables:
|
810
816
|
steps = []
|
811
|
-
variables = all_variable.sort_by{|
|
817
|
+
variables = all_variable.sort_by{|var| var.ordinal}
|
812
818
|
variables.each_with_index do |variable, i|
|
813
819
|
raise "Variable #{i} should have ordinal #{variable.ordinal}" unless variable.ordinal == i
|
814
820
|
raise "Variable #{i} has missing object_type" unless variable.object_type
|
@@ -1025,22 +1031,38 @@ module ActiveFacts
|
|
1025
1031
|
def verbalise(context = nil)
|
1026
1032
|
return "#{object_type.name} #{value}" if object_type.is_a?(ValueType)
|
1027
1033
|
|
1028
|
-
return "#{object_type.name}
|
1034
|
+
return "#{object_type.name} (in which #{fact.verbalise(context)})" if object_type.fact_type
|
1029
1035
|
|
1030
1036
|
# It's an entity that's not an objectified fact type
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1037
|
+
|
1038
|
+
# If it has a simple identifier, there's no need to fully verbalise the identifying facts.
|
1039
|
+
# This recursive block returns either the identifying value or nil
|
1040
|
+
simple_identifier = proc do |instance|
|
1041
|
+
if instance.object_type.is_a?(ActiveFacts::Metamodel::ValueType)
|
1042
|
+
instance
|
1043
|
+
else
|
1044
|
+
pi = instance.object_type.preferred_identifier
|
1045
|
+
identifying_role_refs = pi.role_sequence.all_role_ref_in_order
|
1046
|
+
if identifying_role_refs.size != 1
|
1047
|
+
nil
|
1048
|
+
else
|
1049
|
+
role = identifying_role_refs[0].role
|
1050
|
+
my_role = (role.fact_type.all_role.to_a-[role])[0]
|
1051
|
+
identifying_fact = my_role.all_role_value.detect{|rv| rv.instance == self}.fact
|
1052
|
+
irv = identifying_fact.all_role_value.detect{|rv| rv.role == role}
|
1053
|
+
identifying_instance = irv.instance
|
1054
|
+
simple_identifier.call(identifying_instance)
|
1055
|
+
end
|
1056
|
+
end
|
1057
|
+
end
|
1058
|
+
|
1059
|
+
if (id = simple_identifier.call(self))
|
1060
|
+
"#{object_type.name} #{id.value}"
|
1061
|
+
else
|
1062
|
+
pi = object_type.preferred_identifier
|
1063
|
+
identifying_role_refs = pi.role_sequence.all_role_ref_in_order
|
1064
|
+
"#{object_type.name}" +
|
1065
|
+
" is identified by " + # REVISIT: Where the single fact type is TypeInheritance, we can shrink this
|
1044
1066
|
identifying_role_refs.map do |rr|
|
1045
1067
|
rr = rr.preferred_reference
|
1046
1068
|
[ (l = rr.leading_adjective) ? l+"-" : nil,
|
@@ -1056,7 +1078,7 @@ module ActiveFacts
|
|
1056
1078
|
#identifying_instance = counterpart_role.all_role_value.detect{|rv| rv.fact == identifying_fact}.instance
|
1057
1079
|
identifying_fact.verbalise(context)
|
1058
1080
|
end*", "
|
1059
|
-
|
1081
|
+
end
|
1060
1082
|
|
1061
1083
|
end
|
1062
1084
|
end
|
@@ -66,6 +66,7 @@ module ActiveFacts
|
|
66
66
|
one_to_one :concept # See Concept.guid
|
67
67
|
one_to_one :role_sequence # See RoleSequence.guid
|
68
68
|
one_to_one :shape # See Shape.guid
|
69
|
+
one_to_one :step # See Step.guid
|
69
70
|
end
|
70
71
|
|
71
72
|
class ImplicationRuleName < String
|
@@ -292,6 +293,15 @@ module ActiveFacts
|
|
292
293
|
has_one :orm_diagram, :class => "ORMDiagram", :mandatory => true # See ORMDiagram.all_shape
|
293
294
|
end
|
294
295
|
|
296
|
+
class Step
|
297
|
+
identified_by :guid
|
298
|
+
has_one :alternative_set # See AlternativeSet.all_step
|
299
|
+
has_one :fact_type, :mandatory => true # See FactType.all_step
|
300
|
+
one_to_one :guid, :mandatory => true # See Guid.step
|
301
|
+
maybe :is_disallowed
|
302
|
+
maybe :is_optional
|
303
|
+
end
|
304
|
+
|
295
305
|
class SubsetConstraint < SetConstraint
|
296
306
|
has_one :subset_role_sequence, :class => RoleSequence, :mandatory => true # See RoleSequence.all_subset_constraint_as_subset_role_sequence
|
297
307
|
has_one :superset_role_sequence, :class => RoleSequence, :mandatory => true # See RoleSequence.all_subset_constraint_as_superset_role_sequence
|
@@ -329,6 +339,7 @@ module ActiveFacts
|
|
329
339
|
one_to_one :projection, :class => Role # See Role.variable_as_projection
|
330
340
|
has_one :query, :mandatory => true # See Query.all_variable
|
331
341
|
has_one :role_name, :class => Name # See Name.all_variable_as_role_name
|
342
|
+
one_to_one :step, :counterpart => :objectification_variable # See Step.objectification_variable
|
332
343
|
has_one :subscript # See Subscript.all_variable
|
333
344
|
has_one :value # See Value.all_variable
|
334
345
|
end
|
@@ -419,10 +430,11 @@ module ActiveFacts
|
|
419
430
|
end
|
420
431
|
|
421
432
|
class Play
|
422
|
-
identified_by :
|
433
|
+
identified_by :step, :role
|
423
434
|
has_one :role, :mandatory => true # See Role.all_play
|
435
|
+
has_one :step, :mandatory => true # See Step.all_play
|
424
436
|
has_one :variable, :mandatory => true # See Variable.all_play
|
425
|
-
|
437
|
+
maybe :is_input
|
426
438
|
end
|
427
439
|
|
428
440
|
class Population
|
@@ -479,16 +491,6 @@ module ActiveFacts
|
|
479
491
|
maybe :is_mandatory
|
480
492
|
end
|
481
493
|
|
482
|
-
class Step
|
483
|
-
identified_by :input_play, :output_play
|
484
|
-
has_one :alternative_set # See AlternativeSet.all_step
|
485
|
-
has_one :fact_type, :mandatory => true # See FactType.all_step
|
486
|
-
has_one :input_play, :class => Play, :mandatory => true # See Play.all_step_as_input_play
|
487
|
-
maybe :is_disallowed
|
488
|
-
maybe :is_optional
|
489
|
-
has_one :output_play, :class => Play # See Play.all_step_as_output_play
|
490
|
-
end
|
491
|
-
|
492
494
|
class ValueConstraintShape < ConstraintShape
|
493
495
|
has_one :object_type_shape # See ObjectTypeShape.all_value_constraint_shape
|
494
496
|
one_to_one :role_display # See RoleDisplay.value_constraint_shape
|
@@ -163,8 +163,8 @@ module ActiveFacts
|
|
163
163
|
# Add a RoleRef to an existing Player
|
164
164
|
def add_role_player player, role_ref
|
165
165
|
#trace :subscript, "Adding role_ref #{role_ref.object_id} to player #{player.object_id}"
|
166
|
-
if
|
167
|
-
add_play(player,
|
166
|
+
if play = role_ref.play
|
167
|
+
add_play(player, play)
|
168
168
|
elsif !player.role_refs.include?(role_ref)
|
169
169
|
trace :subscript, "Adding reference to player #{player.object_id} for #{role_ref.role.object_type.name} in #{role_ref.role_sequence.describe} with #{role_ref.role_sequence.all_reading.size} readings"
|
170
170
|
player.role_refs.push(role_ref)
|
@@ -216,12 +216,11 @@ module ActiveFacts
|
|
216
216
|
return if plays.empty?
|
217
217
|
|
218
218
|
# If any of these plays are for a known player, use that, else make a new player.
|
219
|
-
existing_players = plays.map{|
|
219
|
+
existing_players = plays.map{|play| @player_by_play[play] }.compact.uniq
|
220
220
|
if existing_players.size > 1
|
221
221
|
raise "At most one existing player can play these roles: #{existing_players.map{|p|p.object_type.name}*', '}!"
|
222
222
|
end
|
223
223
|
p = existing_players[0] || player(plays[0])
|
224
|
-
debugger if plays.detect{|jr| jr.role.object_type != p.object_type }
|
225
224
|
trace :subscript, "roles are playes of #{p.describe}" do
|
226
225
|
plays.each do |play|
|
227
226
|
trace :subscript, "#{play.describe}" do
|
@@ -281,22 +280,22 @@ module ActiveFacts
|
|
281
280
|
trace :subscript, "Applying subscripts to #{dups.size} occurrences of #{object_type.name}" do
|
282
281
|
s = 0
|
283
282
|
dups.
|
284
|
-
sort_by
|
283
|
+
sort_by do |p| # Guarantee stable numbering
|
285
284
|
p.role_adjuncts(:role_name) + ' ' +
|
286
285
|
# Tie-breaker:
|
287
286
|
p.role_refs.map{|rr| rr.role.fact_type.preferred_reading.text}.sort.to_s
|
288
|
-
|
287
|
+
end.
|
289
288
|
each do |player|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
289
|
+
jrname = player.plays.map{|play| play.role_ref && play.role_ref.role.role_name}.compact[0]
|
290
|
+
rname = (rr = player.role_refs[0]) && rr.role.role_name
|
291
|
+
if jrname and !rname
|
292
|
+
# puts "Oops: rolename #{rname.inspect} != #{jrname.inspect}" if jrname != rname
|
293
|
+
player.variables_by_query.values.each{|jn| jn.role_name = jrname }
|
294
|
+
else
|
295
|
+
player.subscript = s+1
|
296
|
+
s += 1
|
297
|
+
end
|
298
|
+
end
|
300
299
|
end
|
301
300
|
end
|
302
301
|
end
|
@@ -364,7 +363,7 @@ module ActiveFacts
|
|
364
363
|
trace :subscript, "Indexing roles of fact types in #{query.all_step.size} steps" do
|
365
364
|
steps = []
|
366
365
|
# Register all references to each variable as being for the same player:
|
367
|
-
query.all_variable.sort_by
|
366
|
+
query.all_variable.to_a.sort_by(&:ordinal).each do |variable|
|
368
367
|
trace :subscript, "Adding Roles of #{variable.describe}" do
|
369
368
|
plays_have_same_player(variable.all_play.to_a)
|
370
369
|
steps = steps | variable.all_step
|
@@ -445,13 +444,12 @@ module ActiveFacts
|
|
445
444
|
# Expand this reading (or partial reading, during contraction)
|
446
445
|
def expand_reading_text(step, text, role_sequence, player_by_role = {})
|
447
446
|
if !player_by_role.empty? and !player_by_role.is_a?(Hash) || player_by_role.keys.detect{|k| !k.is_a?(ActiveFacts::Metamodel::Role)}
|
448
|
-
debugger
|
449
447
|
raise "Need to change this call to expand_reading_text to pass a role->variable hash"
|
450
448
|
end
|
451
449
|
rrs = role_sequence.all_role_ref_in_order
|
452
450
|
variable_by_role = {}
|
453
451
|
if step
|
454
|
-
plays =
|
452
|
+
plays = step.all_play
|
455
453
|
variable_by_role = plays.inject({}) { |h, play| h[play.role] = play.variable; h }
|
456
454
|
end
|
457
455
|
trace :subscript, "expanding '#{text}' with #{role_sequence.describe}" do
|
@@ -462,8 +460,9 @@ module ActiveFacts
|
|
462
460
|
variable = variable_by_role[role_ref.role]
|
463
461
|
|
464
462
|
play_name = variable && variable.role_name
|
463
|
+
raise hell if player && player.is_a?(ActiveFacts::Metamodel::EntityType) && player.fact_type && !variable
|
465
464
|
subscripted_player(role_ref, player && player.subscript, play_name, variable && variable.value) +
|
466
|
-
objectification_verbalisation(
|
465
|
+
objectification_verbalisation(variable)
|
467
466
|
end
|
468
467
|
end
|
469
468
|
end
|
@@ -495,40 +494,35 @@ module ActiveFacts
|
|
495
494
|
@query = query
|
496
495
|
return unless query
|
497
496
|
|
498
|
-
@variables = query.all_variable.sort_by
|
497
|
+
@variables = query.all_variable.to_a.sort_by(&:ordinal)
|
499
498
|
|
500
|
-
@steps = @variables.map
|
499
|
+
@steps = @variables.map(&:all_step).flatten.uniq
|
501
500
|
@steps_by_variable = @variables.
|
502
|
-
inject({}) do |h,
|
503
|
-
|
501
|
+
inject({}) do |h, var|
|
502
|
+
var.all_step.each{|step| (h[var] ||= []) << step}
|
504
503
|
h
|
505
504
|
end
|
506
505
|
end
|
507
506
|
|
508
|
-
#
|
507
|
+
# De-index this step now that we've processed it:
|
509
508
|
def step_completed(step)
|
510
509
|
@steps.delete(step)
|
511
510
|
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
@steps_by_variable.delete(input_node) if steps.empty?
|
516
|
-
|
517
|
-
output_node = step.output_play.variable
|
518
|
-
if (input_node != output_node)
|
519
|
-
steps = @steps_by_variable[output_node]
|
511
|
+
step.all_play.each do |play|
|
512
|
+
var = play.variable
|
513
|
+
steps = @steps_by_variable[var]
|
520
514
|
steps.delete(step)
|
521
|
-
|
515
|
+
@steps_by_variable.delete(var) if steps.empty?
|
522
516
|
end
|
523
517
|
end
|
524
518
|
|
525
|
-
def choose_step(
|
526
|
-
next_steps = @steps_by_variable[
|
519
|
+
def choose_step(next_var)
|
520
|
+
next_steps = @steps_by_variable[next_var]
|
527
521
|
|
528
522
|
# We need to emit each objectification before mentioning an object that plays a role in one, if possible
|
529
523
|
# so that we don't wind up with an objectification as the only way to mention its name.
|
530
524
|
|
531
|
-
# If we don't have a
|
525
|
+
# If we don't have a next_var against which we can contract,
|
532
526
|
# so just use any step involving this node, or just any step.
|
533
527
|
if next_steps
|
534
528
|
if next_step = next_steps.detect { |ns| !ns.is_objectification_step }
|
@@ -558,26 +552,26 @@ module ActiveFacts
|
|
558
552
|
end
|
559
553
|
|
560
554
|
# The step we just emitted (using the reading given) is contractable iff
|
561
|
-
# the reading has the
|
562
|
-
def node_contractable_against_reading(
|
555
|
+
# the reading has the next_var's role player as the final text
|
556
|
+
def node_contractable_against_reading(next_var, reading)
|
563
557
|
reading &&
|
564
558
|
# Find whether last role has no following text, and its ordinal
|
565
559
|
(reading.text =~ /\{([0-9])\}$/) &&
|
566
560
|
# This reading's RoleRef for that role:
|
567
561
|
(role_ref = reading.role_sequence.all_role_ref_in_order[$1.to_i]) &&
|
568
562
|
# was that RoleRef for the upcoming node?
|
569
|
-
role_ref.role.object_type ==
|
563
|
+
role_ref.role.object_type == next_var.object_type
|
570
564
|
end
|
571
565
|
|
572
|
-
def reading_starts_with_node(reading,
|
566
|
+
def reading_starts_with_node(reading, next_var)
|
573
567
|
reading.text =~ /^\{([0-9])\}/ and
|
574
568
|
role_ref = reading.role_sequence.all_role_ref.detect{|rr| rr.ordinal == $1.to_i} and
|
575
|
-
role_ref.role.object_type ==
|
569
|
+
role_ref.role.object_type == next_var.object_type
|
576
570
|
end
|
577
571
|
|
578
|
-
# The last reading we emitted ended with the object type name for
|
572
|
+
# The last reading we emitted ended with the object type name for next_var.
|
579
573
|
# Choose a step and a reading that can be contracted against that name
|
580
|
-
def contractable_step(next_steps,
|
574
|
+
def contractable_step(next_steps, next_var)
|
581
575
|
next_reading = nil
|
582
576
|
next_step =
|
583
577
|
next_steps.detect do |js|
|
@@ -585,51 +579,47 @@ module ActiveFacts
|
|
585
579
|
# If we find a reading here, it can be contracted against the previous one
|
586
580
|
next_reading =
|
587
581
|
js.fact_type.all_reading_by_ordinal.detect do |reading|
|
588
|
-
# This step is contractable iff the FactType has a reading that starts with the role of
|
589
|
-
reading_starts_with_node(reading,
|
582
|
+
# This step is contractable iff the FactType has a reading that starts with the role of next_var (no preceding text)
|
583
|
+
reading_starts_with_node(reading, next_var)
|
590
584
|
end
|
591
585
|
next_reading
|
592
586
|
end
|
593
|
-
trace :query, "#{next_reading ? "'"+next_reading.expand+"'" : "No reading"} contracts against last node '#{
|
587
|
+
trace :query, "#{next_reading ? "'"+next_reading.expand+"'" : "No reading"} contracts against last node '#{next_var.object_type.name}'"
|
594
588
|
return [next_step, next_reading]
|
595
589
|
end
|
596
590
|
|
597
|
-
|
598
|
-
|
591
|
+
def objectification_verbalisation(variable)
|
592
|
+
return '' unless variable
|
593
|
+
raise "Not fully re-implemented, should pass the variable instead of #{variable.inspect}" unless variable.is_a?(ActiveFacts::Metamodel::Variable)
|
599
594
|
objectified_node = nil
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
detect do |js|
|
604
|
-
# The objectifying entity type should always be the input_variable here, but be safe:
|
605
|
-
js.is_objectification_step and
|
606
|
-
(objectified_node = js.input_play.variable).object_type == object_type ||
|
607
|
-
(objectified_node = js.output_play.variable).object_type == object_type
|
608
|
-
end
|
609
|
-
return ''
|
610
|
-
end
|
595
|
+
object_type = variable.object_type
|
596
|
+
return '' unless object_type.is_a?(Metamodel::EntityType) # Not a entity type
|
597
|
+
return '' unless object_type.fact_type # Not objectified
|
611
598
|
|
612
|
-
|
613
|
-
|
599
|
+
objectification_step = variable.step
|
600
|
+
return '' unless objectification_step
|
614
601
|
|
615
602
|
steps = [objectification_step]
|
616
603
|
step_completed(objectification_step)
|
604
|
+
|
605
|
+
=begin
|
617
606
|
while other_step =
|
618
607
|
@steps.
|
619
|
-
detect{|
|
620
|
-
|
621
|
-
|
608
|
+
detect{|step|
|
609
|
+
step.is_objectification_step and
|
610
|
+
step.input_play.variable.object_type == object_type || step.output_play.variable.object_type == object_type
|
622
611
|
}
|
623
612
|
steps << other_step
|
624
613
|
trace :query, "Emitting objectification step allows deleting #{other_step.describe}"
|
625
614
|
step_completed(other_step)
|
626
615
|
end
|
616
|
+
=end
|
627
617
|
|
628
618
|
# Find all references to roles in this objectified fact type which are relevant to the variables of these steps:
|
629
619
|
player_by_role = {}
|
630
620
|
steps.each do |step|
|
631
|
-
step.all_play.to_a.map do |
|
632
|
-
player_by_role[
|
621
|
+
step.all_play.to_a.map do |play|
|
622
|
+
player_by_role[play.role] = @player_by_play[play]
|
633
623
|
end
|
634
624
|
end
|
635
625
|
|
@@ -639,14 +629,14 @@ module ActiveFacts
|
|
639
629
|
" (in which #{expand_reading_text(objectification_step, reading.text, reading.role_sequence, player_by_role)})"
|
640
630
|
end
|
641
631
|
|
642
|
-
def elided_objectification(next_step, fact_type, last_is_contractable,
|
632
|
+
def elided_objectification(next_step, fact_type, last_is_contractable, next_var)
|
643
633
|
if last_is_contractable
|
644
634
|
# Choose a reading that's contractable against the previous step, if possible
|
645
635
|
reading = fact_type.all_reading_by_ordinal.
|
646
636
|
detect do |reading|
|
647
637
|
# Only contract a negative reading if we want one
|
648
638
|
(!next_step.is_disallowed || !reading.is_negative == !next_step.is_disallowed) and
|
649
|
-
reading_starts_with_node(reading,
|
639
|
+
reading_starts_with_node(reading, next_var)
|
650
640
|
end
|
651
641
|
end
|
652
642
|
last_is_contractable = false unless reading
|
@@ -655,7 +645,7 @@ module ActiveFacts
|
|
655
645
|
# Find which role occurs last in the reading, and which Variable is attached
|
656
646
|
reading.text =~ /\{(\d)\}[^{]*\Z/
|
657
647
|
last_role_ref = reading.role_sequence.all_role_ref_in_order[$1.to_i]
|
658
|
-
exit_node = @variables.detect{|jn| jn.all_play.detect{|
|
648
|
+
exit_node = @variables.detect{|jn| jn.all_play.detect{|play| play.role == last_role_ref.role}}
|
659
649
|
exit_step = nil
|
660
650
|
|
661
651
|
trace :query, "Stepping over an objectification to #{exit_node.object_type.name} requires eliding the other implied steps" do
|
@@ -683,35 +673,49 @@ module ActiveFacts
|
|
683
673
|
def verbalise_query query
|
684
674
|
prepare_query query
|
685
675
|
readings = ''
|
686
|
-
|
676
|
+
next_var = @role_refs[0].play.variable # Choose a place to start
|
687
677
|
last_is_contractable = false
|
688
|
-
|
678
|
+
|
679
|
+
trace :query, "Verbalising query" do
|
680
|
+
if trace(:query)
|
681
|
+
trace :query, "variables:" do
|
682
|
+
@variables.each do |var|
|
683
|
+
trace :query, var.describe
|
684
|
+
end
|
685
|
+
end
|
686
|
+
trace :query, "steps:" do
|
687
|
+
@steps.each do |step|
|
688
|
+
trace :query, step.describe
|
689
|
+
end
|
690
|
+
end
|
691
|
+
end
|
692
|
+
|
689
693
|
until @steps.empty?
|
690
694
|
next_reading = nil
|
691
|
-
# Choose
|
692
|
-
next_steps = @steps_by_variable[
|
693
|
-
trace :query, "Next Steps from #{
|
695
|
+
# Choose amongst all remaining steps we can take from the next node, if any
|
696
|
+
next_steps = @steps_by_variable[next_var]
|
697
|
+
trace :query, "Next Steps from #{next_var.describe} are #{(next_steps||[]).map{|js| js.describe }.inspect}"
|
694
698
|
|
695
699
|
# See if we can find a next step that contracts against the last (if any):
|
696
700
|
next_step = nil
|
697
701
|
if last_is_contractable && next_steps
|
698
|
-
next_step, next_reading = *contractable_step(next_steps,
|
702
|
+
next_step, next_reading = *contractable_step(next_steps, next_var)
|
699
703
|
end
|
700
704
|
|
701
705
|
if next_step
|
702
|
-
trace :query, "Chose #{next_step.describe} because it's contractable against last node #{
|
706
|
+
trace :query, "Chose #{next_step.describe} because it's contractable against last node #{next_var.object_type.name} using #{next_reading.expand}"
|
703
707
|
|
704
708
|
player_by_role =
|
705
|
-
next_step.all_play.inject({}) {|h,
|
709
|
+
next_step.all_play.inject({}) {|h, play| h[play.role] = @player_by_play[play]; h }
|
706
710
|
raise "REVISIT: Needed a negated reading here" if !next_reading.is_negative != !next_step.is_disallowed
|
707
711
|
raise "REVISIT: Need to emit 'maybe' here" if next_step.is_optional
|
708
712
|
readings += expand_contracted_text(next_step, next_reading, player_by_role)
|
709
713
|
step_completed(next_step)
|
710
714
|
else
|
711
|
-
next_step = choose_step(
|
715
|
+
next_step = choose_step(next_var) if !next_step
|
712
716
|
|
713
717
|
player_by_role =
|
714
|
-
next_step.all_play.inject({}) {|h,
|
718
|
+
next_step.all_play.inject({}) {|h, play| h[play.role] = @player_by_play[play]; h }
|
715
719
|
|
716
720
|
if next_step.is_unary_step
|
717
721
|
# Objectified unaries get emitted as unaries, not as objectifications:
|
@@ -731,20 +735,20 @@ module ActiveFacts
|
|
731
735
|
objectified_node = next_step.input_play.variable
|
732
736
|
raise "Assumption violated that the objectification is the input play" unless objectified_node.object_type.fact_type
|
733
737
|
objectified_node.all_step.map do |other_step|
|
734
|
-
|
735
|
-
player_by_role[
|
738
|
+
other_step.all_play.map do |play|
|
739
|
+
player_by_role[play.role] = @player_by_play[play]
|
736
740
|
end
|
737
741
|
end
|
738
742
|
|
739
|
-
if last_is_contractable and
|
743
|
+
if last_is_contractable and next_var.object_type.is_a?(EntityType) and next_var.object_type.fact_type == fact_type
|
740
744
|
# The last reading we emitted ended with the name of the objectification of this fact type, so we can contract the objectification
|
741
745
|
# REVISIT: Do we need to use player_by_role here (if this objectification is traversed twice and so is subscripted)
|
742
746
|
readings += objectification_verbalisation(fact_type.entity_type)
|
743
747
|
else
|
744
748
|
# This objectified fact type does not need to be made explicit.
|
745
749
|
negation = next_step.is_disallowed
|
746
|
-
next_reading,
|
747
|
-
*elided_objectification(next_step, fact_type, last_is_contractable,
|
750
|
+
next_reading, next_var, next_step, last_is_contractable =
|
751
|
+
*elided_objectification(next_step, fact_type, last_is_contractable, next_var)
|
748
752
|
if last_is_contractable
|
749
753
|
raise "REVISIT: Need to emit 'maybe' here" if next_step and next_step.is_optional
|
750
754
|
readings += expand_contracted_text(next_step, next_reading, player_by_role)
|
@@ -760,11 +764,11 @@ module ActiveFacts
|
|
760
764
|
end
|
761
765
|
else
|
762
766
|
fact_type = next_step.fact_type
|
763
|
-
# Prefer a reading that starts with the player of
|
767
|
+
# Prefer a reading that starts with the player of next_var
|
764
768
|
next_reading = fact_type.all_reading_by_ordinal.
|
765
769
|
detect do |reading|
|
766
770
|
(!next_step.is_disallowed || !reading.is_negative == !next_step.is_disallowed) and
|
767
|
-
reading_starts_with_node(reading,
|
771
|
+
reading_starts_with_node(reading, next_var)
|
768
772
|
end || fact_type.preferred_reading(next_step.is_disallowed)
|
769
773
|
# REVISIT: If this step and reading has role references with adjectives, we need to expand using those
|
770
774
|
readings += " and " unless readings.empty?
|
@@ -777,14 +781,16 @@ module ActiveFacts
|
|
777
781
|
|
778
782
|
if next_step
|
779
783
|
# Continue from this step with the node having the most steps remaining
|
780
|
-
input_steps = @steps_by_variable[
|
781
|
-
|
782
|
-
|
784
|
+
input_steps = @steps_by_variable[input_var = next_step.input_play.variable] || []
|
785
|
+
output_play = next_step.output_plays.last
|
786
|
+
output_steps = (output_play && (output_var = output_play.variable) && @steps_by_variable[output_var]) || []
|
787
|
+
|
788
|
+
next_var = input_steps.size > output_steps.size ? input_var : output_var
|
783
789
|
# Prepare for possible contraction following:
|
784
|
-
last_is_contractable = next_reading && node_contractable_against_reading(
|
790
|
+
last_is_contractable = next_reading && node_contractable_against_reading(next_var, next_reading)
|
785
791
|
else
|
786
792
|
# This shouldn't happen, but an elided objectification that had missing steps can cause it. Survive:
|
787
|
-
|
793
|
+
next_var = (steps[0].input_play || steps[0].output_plays.last).variable
|
788
794
|
last_is_contractable = false
|
789
795
|
end
|
790
796
|
|