activefacts 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|