activefacts 0.8.9 → 0.8.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gemtest +0 -0
- data/Manifest.txt +28 -33
- data/Rakefile +11 -12
- data/bin/cql +90 -46
- data/examples/CQL/Blog.cql +2 -1
- data/examples/CQL/CompanyDirectorEmployee.cql +2 -2
- data/examples/CQL/Death.cql +1 -1
- data/examples/CQL/Diplomacy.cql +9 -9
- data/examples/CQL/Genealogy.cql +3 -2
- data/examples/CQL/Insurance.cql +10 -7
- data/examples/CQL/JoinEquality.cql +2 -2
- data/examples/CQL/Marriage.cql +1 -1
- data/examples/CQL/Metamodel.cql +73 -53
- data/examples/CQL/MetamodelNext.cql +89 -67
- data/examples/CQL/OneToOnes.cql +2 -2
- data/examples/CQL/ServiceDirector.cql +10 -5
- data/examples/CQL/Supervision.cql +3 -3
- data/examples/CQL/Tests.Test5.Load.cql +1 -1
- data/examples/CQL/Warehousing.cql +4 -2
- data/lib/activefacts/cql/CQLParser.treetop +26 -60
- data/lib/activefacts/cql/Context.treetop +12 -2
- data/lib/activefacts/cql/Expressions.treetop +14 -30
- data/lib/activefacts/cql/FactTypes.treetop +165 -110
- data/lib/activefacts/cql/Language/English.treetop +167 -54
- data/lib/activefacts/cql/LexicalRules.treetop +16 -2
- data/lib/activefacts/cql/{Concepts.treetop → ObjectTypes.treetop} +36 -37
- data/lib/activefacts/cql/Terms.treetop +57 -27
- data/lib/activefacts/cql/ValueTypes.treetop +39 -13
- data/lib/activefacts/cql/compiler.rb +5 -3
- data/lib/activefacts/cql/compiler/{reading.rb → clause.rb} +407 -285
- data/lib/activefacts/cql/compiler/constraint.rb +178 -275
- data/lib/activefacts/cql/compiler/entity_type.rb +73 -64
- data/lib/activefacts/cql/compiler/expression.rb +418 -0
- data/lib/activefacts/cql/compiler/fact.rb +146 -145
- data/lib/activefacts/cql/compiler/fact_type.rb +197 -80
- data/lib/activefacts/cql/compiler/join.rb +159 -0
- data/lib/activefacts/cql/compiler/shared.rb +51 -23
- data/lib/activefacts/cql/compiler/value_type.rb +56 -2
- data/lib/activefacts/cql/parser.rb +15 -4
- data/lib/activefacts/generate/absorption.rb +7 -7
- data/lib/activefacts/generate/cql.rb +100 -37
- data/lib/activefacts/generate/oo.rb +28 -51
- data/lib/activefacts/generate/ordered.rb +60 -36
- data/lib/activefacts/generate/ruby.rb +6 -6
- data/lib/activefacts/generate/sql/server.rb +4 -4
- data/lib/activefacts/input/orm.rb +71 -53
- data/lib/activefacts/persistence.rb +1 -1
- data/lib/activefacts/persistence/columns.rb +27 -23
- data/lib/activefacts/persistence/foreignkey.rb +6 -6
- data/lib/activefacts/persistence/index.rb +17 -17
- data/lib/activefacts/persistence/{concept.rb → object_type.rb} +9 -9
- data/lib/activefacts/persistence/reference.rb +61 -36
- data/lib/activefacts/persistence/tables.rb +61 -59
- data/lib/activefacts/support.rb +54 -29
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +99 -54
- data/lib/activefacts/vocabulary/metamodel.rb +43 -37
- data/lib/activefacts/vocabulary/verbaliser.rb +134 -109
- data/spec/absorption_spec.rb +8 -8
- data/spec/cql/comparison_spec.rb +91 -0
- data/spec/cql/contractions_spec.rb +251 -0
- data/spec/cql/entity_type_spec.rb +319 -0
- data/spec/cql/expressions_spec.rb +63 -0
- data/spec/cql/fact_type_matching_spec.rb +283 -0
- data/spec/cql/french_spec.rb +21 -0
- data/spec/cql/parser/bad_literals_spec.rb +86 -0
- data/spec/cql/parser/constraints_spec.rb +19 -0
- data/spec/cql/parser/entity_types_spec.rb +106 -0
- data/spec/cql/parser/expressions_spec.rb +179 -0
- data/spec/cql/parser/fact_types_spec.rb +41 -0
- data/spec/cql/parser/literals_spec.rb +312 -0
- data/spec/cql/parser/pragmas_spec.rb +89 -0
- data/spec/cql/parser/value_types_spec.rb +42 -0
- data/spec/cql/role_matching_spec.rb +147 -0
- data/spec/cql/samples_spec.rb +9 -9
- data/spec/cql_cql_spec.rb +1 -1
- data/spec/cql_dm_spec.rb +116 -0
- data/spec/cql_mysql_spec.rb +1 -1
- data/spec/cql_ruby_spec.rb +1 -1
- data/spec/cql_sql_spec.rb +3 -3
- data/spec/cql_symbol_tables_spec.rb +30 -30
- data/spec/cqldump_spec.rb +4 -4
- data/spec/helpers/array_matcher.rb +32 -27
- data/spec/helpers/diff_matcher.rb +6 -26
- data/spec/helpers/file_matcher.rb +41 -32
- data/spec/helpers/parse_to_ast_matcher.rb +76 -0
- data/spec/helpers/string_matcher.rb +32 -31
- data/spec/norma_cql_spec.rb +1 -1
- data/spec/norma_ruby_spec.rb +1 -1
- data/spec/norma_ruby_sql_spec.rb +1 -1
- data/spec/norma_sql_spec.rb +3 -1
- data/spec/norma_tables_spec.rb +1 -1
- data/spec/ruby_api_spec.rb +23 -0
- data/spec/spec_helper.rb +5 -4
- metadata +66 -66
- data/examples/CQL/OrienteeringER.cql +0 -58
- data/lib/activefacts/api.rb +0 -44
- data/lib/activefacts/api/concept.rb +0 -410
- data/lib/activefacts/api/constellation.rb +0 -128
- data/lib/activefacts/api/entity.rb +0 -256
- data/lib/activefacts/api/instance.rb +0 -60
- data/lib/activefacts/api/instance_index.rb +0 -80
- data/lib/activefacts/api/numeric.rb +0 -167
- data/lib/activefacts/api/role.rb +0 -80
- data/lib/activefacts/api/role_proxy.rb +0 -70
- data/lib/activefacts/api/role_values.rb +0 -117
- data/lib/activefacts/api/standard_types.rb +0 -87
- data/lib/activefacts/api/support.rb +0 -65
- data/lib/activefacts/api/value.rb +0 -135
- data/lib/activefacts/api/vocabulary.rb +0 -82
- data/spec/api/autocounter.rb +0 -82
- data/spec/api/constellation.rb +0 -130
- data/spec/api/entity_type.rb +0 -103
- data/spec/api/instance.rb +0 -461
- data/spec/api/roles.rb +0 -124
- data/spec/api/value_type.rb +0 -112
- data/spec/api_spec.rb +0 -13
- data/spec/cql/matching_spec.rb +0 -517
- data/spec/cql/unit_spec.rb +0 -394
- data/spec/spec.opts +0 -1
data/lib/activefacts/support.rb
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
#
|
|
8
8
|
#module ActiveFacts
|
|
9
9
|
$debug_indent = nil
|
|
10
|
-
$debug_nested = false
|
|
10
|
+
$debug_nested = false # Set when a block enables all enclosed debugging
|
|
11
11
|
$debug_keys = nil
|
|
12
12
|
$debug_available = {}
|
|
13
13
|
|
|
@@ -46,40 +46,58 @@
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def debug_selected(args)
|
|
49
|
-
# Figure out whether this trace is enabled and
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
# Figure out whether this trace is enabled (itself or by :all), if it nests, and if we should print the key:
|
|
50
|
+
key =
|
|
51
|
+
if Symbol === args[0]
|
|
52
|
+
control = args.shift
|
|
53
|
+
if (s = control.to_s) =~ /_\Z/
|
|
54
|
+
nested = true
|
|
55
|
+
s.sub(/_\Z/, '').to_sym # Avoid creating new strings willy-nilly
|
|
56
|
+
else
|
|
57
|
+
control
|
|
58
|
+
end
|
|
59
|
+
else
|
|
60
|
+
:all
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
$debug_available[key] ||= key # Remember that this debug was requested, for help
|
|
64
|
+
enabled = $debug_nested || # This debug is enabled because it's in a nested block
|
|
65
|
+
$debug_keys[key] || # This debug is enabled in its own right
|
|
66
|
+
$debug_keys[:all] # This debug is enabled because all are
|
|
67
|
+
$debug_nested = nested
|
|
68
|
+
[
|
|
69
|
+
(enabled ? 1 : 0),
|
|
70
|
+
$debug_keys[:all] ? " %-15s"%control : nil
|
|
71
|
+
]
|
|
58
72
|
end
|
|
59
73
|
|
|
60
|
-
def
|
|
74
|
+
def debug_show(*args)
|
|
61
75
|
debug_initialize unless $debug_indent
|
|
62
76
|
|
|
63
|
-
enabled,
|
|
77
|
+
enabled, key_to_show = debug_selected(args)
|
|
64
78
|
|
|
65
79
|
# Emit the message if enabled or a parent is:
|
|
66
80
|
if args.size > 0 && enabled == 1
|
|
67
|
-
puts "\##{
|
|
81
|
+
puts "\##{key_to_show} " +
|
|
82
|
+
' '*$debug_indent +
|
|
83
|
+
args.
|
|
84
|
+
# A laudable aim, certainly, but in practise the Procs leak and slow things down:
|
|
85
|
+
# map{|a| a.respond_to?(:call) ? a.call : a}.
|
|
86
|
+
join(' ')
|
|
68
87
|
end
|
|
88
|
+
$debug_indent += enabled
|
|
89
|
+
enabled
|
|
90
|
+
end
|
|
69
91
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
$debug_nesting = old_nested
|
|
77
|
-
end
|
|
78
|
-
else
|
|
79
|
-
r = enabled == 1 # If no block, return whether enabled
|
|
92
|
+
def debug(*args, &block)
|
|
93
|
+
begin
|
|
94
|
+
old_indent, old_nested, enabled = $debug_indent, $debug_nested, debug_show(*args)
|
|
95
|
+
return (block || proc { enabled == 1 }).call
|
|
96
|
+
ensure
|
|
97
|
+
$debug_indent, $debug_nested = old_indent, old_nested
|
|
80
98
|
end
|
|
81
|
-
r
|
|
82
99
|
end
|
|
100
|
+
|
|
83
101
|
#end
|
|
84
102
|
|
|
85
103
|
# Return all duplicate objects in the array (using hash-equality)
|
|
@@ -94,10 +112,17 @@ class Array
|
|
|
94
112
|
end.keys
|
|
95
113
|
end
|
|
96
114
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
115
|
+
if RUBY_VERSION =~ /^1\.8/
|
|
116
|
+
# Fake up Ruby 1.9's Array#index method, mostly
|
|
117
|
+
alias_method :__orig_index, :index
|
|
118
|
+
def index *a, &b
|
|
119
|
+
if a.size == 0
|
|
120
|
+
raise "Not faking Enumerator for #{RUBY_VERSION}" if !b
|
|
121
|
+
(0...size).detect{|i| return i if b.call(self[i]) }
|
|
122
|
+
else
|
|
123
|
+
__orig_index(*a, &b)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
101
126
|
end
|
|
102
127
|
|
|
103
128
|
# If any element, or sequence of elements, repeats immediately, delete the repetition.
|
|
@@ -109,7 +134,7 @@ class Array
|
|
|
109
134
|
while i < size # Need to re-evaluate size on each loop - the array shrinks.
|
|
110
135
|
j = i
|
|
111
136
|
#puts "Looking for repetitions of #{self[i]}@[#{i}]"
|
|
112
|
-
while tail = self[j+1..-1] and k = tail.index(self[i]
|
|
137
|
+
while tail = self[j+1..-1] and k = tail.index {|e| compare_block.call(e, self[i]) }
|
|
113
138
|
length = j+1+k-i
|
|
114
139
|
#puts "Found at #{j+1+k} (subsequence of length #{j+1+k-i}), will need to repeat to #{j+k+length}"
|
|
115
140
|
if j+k+1+length <= size && compare_block[self[i, length], self[j+k+1, length]]
|
data/lib/activefacts/version.rb
CHANGED
|
@@ -13,9 +13,9 @@ module ActiveFacts
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def preferred_reading
|
|
16
|
-
|
|
17
|
-
raise "No reading for (#{all_role.map{|r| r.
|
|
18
|
-
|
|
16
|
+
pr = all_reading_by_ordinal[0]
|
|
17
|
+
raise "No reading for (#{all_role.map{|r| r.object_type.name}*", "})" unless pr
|
|
18
|
+
pr
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def describe(highlight = nil)
|
|
@@ -40,12 +40,12 @@ module ActiveFacts
|
|
|
40
40
|
# This entity type has just objectified a fact type. Create the necessary ImplicitFactTypes with phantom roles
|
|
41
41
|
def create_implicit_fact_type_for_unary
|
|
42
42
|
role = all_role.single
|
|
43
|
-
|
|
43
|
+
return if role.implicit_fact_type # Already exists
|
|
44
44
|
# NORMA doesn't create an implicit fact type here, rather the fact type has an implicit extra role, so looks like a binary
|
|
45
45
|
# We only do it when the unary fact type is not objectified
|
|
46
|
-
implicit_fact_type = @constellation.ImplicitFactType(:new, :
|
|
47
|
-
entity_type = @entity_type || @constellation.ImplicitBooleanValueType(role.
|
|
48
|
-
phantom_role = @constellation.Role(implicit_fact_type, 0, :
|
|
46
|
+
implicit_fact_type = @constellation.ImplicitFactType(:new, :implying_role => role)
|
|
47
|
+
entity_type = @entity_type || @constellation.ImplicitBooleanValueType(role.object_type.vocabulary, "_ImplicitBooleanValueType")
|
|
48
|
+
phantom_role = @constellation.Role(implicit_fact_type, 0, :object_type => entity_type)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def reading_preferably_starting_with_role role
|
|
@@ -57,7 +57,7 @@ module ActiveFacts
|
|
|
57
57
|
|
|
58
58
|
class Role
|
|
59
59
|
def describe(highlight = nil)
|
|
60
|
-
|
|
60
|
+
object_type.name + (self == highlight ? "*" : "")
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
# Is there are internal uniqueness constraint on this role only?
|
|
@@ -85,6 +85,33 @@ module ActiveFacts
|
|
|
85
85
|
def preferred_reference
|
|
86
86
|
fact_type.preferred_reading.role_sequence.all_role_ref.detect{|rr| rr.role == self }
|
|
87
87
|
end
|
|
88
|
+
|
|
89
|
+
# Return true if this role is functional (has only one instance wrt its player)
|
|
90
|
+
# A role in an objectified fact type is deemed to refer to the implicit role of the objectification.
|
|
91
|
+
def is_functional
|
|
92
|
+
fact_type.entity_type or
|
|
93
|
+
fact_type.all_role.size != 2 or
|
|
94
|
+
all_role_ref.detect do |rr|
|
|
95
|
+
rr.role_sequence.all_role_ref.size == 1 and
|
|
96
|
+
rr.role_sequence.all_presence_constraint.detect do |pc|
|
|
97
|
+
pc.max_frequency == 1 and !pc.enforcement # Alethic uniqueness constraint
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def role_method
|
|
103
|
+
if fact_type.all_role.size == 1
|
|
104
|
+
# The object of an objectified unary has no boolean; the existence of the object is the fact
|
|
105
|
+
return nil if fact_type.entity_type
|
|
106
|
+
return preferred_role_name(role)
|
|
107
|
+
end
|
|
108
|
+
if fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
|
|
109
|
+
# No method is available to indicate that the object is the specified subtype
|
|
110
|
+
# In future, this might be implemented using the type discriminator
|
|
111
|
+
return nil
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
88
115
|
end
|
|
89
116
|
|
|
90
117
|
class RoleRef
|
|
@@ -100,15 +127,28 @@ module ActiveFacts
|
|
|
100
127
|
name_array =
|
|
101
128
|
if role.fact_type.all_role.size == 1
|
|
102
129
|
if role.fact_type.is_a?(ImplicitFactType)
|
|
103
|
-
"#{role.
|
|
130
|
+
"#{role.object_type.name} phantom for #{role.fact_type.role.object_type.name}"
|
|
104
131
|
else
|
|
105
132
|
role.fact_type.preferred_reading.text.gsub(/\{[0-9]\}/,'').strip.split(/\s/)
|
|
106
133
|
end
|
|
107
134
|
else
|
|
108
|
-
role.role_name || [leading_adjective, role.
|
|
135
|
+
role.role_name || [leading_adjective, role.object_type.name, trailing_adjective].compact.map{|w| w.split(/\s/)}.flatten
|
|
109
136
|
end
|
|
110
137
|
return joiner ? Array(name_array)*joiner : Array(name_array)
|
|
111
138
|
end
|
|
139
|
+
|
|
140
|
+
def cql_name
|
|
141
|
+
if role.fact_type.all_role.size == 1
|
|
142
|
+
role_name
|
|
143
|
+
elsif role.role_name
|
|
144
|
+
role.role_name
|
|
145
|
+
else
|
|
146
|
+
# Where an adjective has multiple words, the hyphen is inserted outside the outermost space, leaving the space
|
|
147
|
+
(leading_adjective ? leading_adjective.strip.sub(/ |$/, '-\0').sub(/[^-]$/,'\0 ') : '') +
|
|
148
|
+
role.object_type.name+
|
|
149
|
+
(trailing_adjective ? ' '+trailing_adjective.strip.sub(/.* |^/, '\0-').sub(/^[^-]/,' \0') : '')
|
|
150
|
+
end
|
|
151
|
+
end
|
|
112
152
|
end
|
|
113
153
|
|
|
114
154
|
class RoleSequence
|
|
@@ -139,6 +179,7 @@ module ActiveFacts
|
|
|
139
179
|
|
|
140
180
|
class EntityType
|
|
141
181
|
def preferred_identifier
|
|
182
|
+
#return @preferred_identifier if @preferred_identifier
|
|
142
183
|
if fact_type
|
|
143
184
|
|
|
144
185
|
# For a nested fact type, the PI is a unique constraint over N or N-1 roles
|
|
@@ -157,10 +198,10 @@ module ActiveFacts
|
|
|
157
198
|
if (of = rsr.role.fact_type) != fact_type
|
|
158
199
|
case of.all_role.size
|
|
159
200
|
when 1 # A unary FT must be played by the objectification of this fact type
|
|
160
|
-
next rsr.role.
|
|
201
|
+
next rsr.role.object_type != fact_type.entity_type
|
|
161
202
|
when 2 # A binary FT must have the objectification of this FT as the other player
|
|
162
203
|
other_role = (of.all_role-[rsr.role])[0]
|
|
163
|
-
next other_role.
|
|
204
|
+
next other_role.object_type != fact_type.entity_type
|
|
164
205
|
else
|
|
165
206
|
next true # A role in a ternary (or higher) cannot be usd in our identifier
|
|
166
207
|
end
|
|
@@ -180,7 +221,7 @@ module ActiveFacts
|
|
|
180
221
|
debug :pi, "Got PI #{pi.name||pi.object_id} for nested #{name}" if pi
|
|
181
222
|
debug :pi, "Looking for PI on entity that nests this fact" unless pi
|
|
182
223
|
raise "Oops, pi for nested fact is #{pi.class}" unless !pi || pi.is_a?(ActiveFacts::Metamodel::PresenceConstraint)
|
|
183
|
-
return pi if pi
|
|
224
|
+
return @preferred_identifier = pi if pi
|
|
184
225
|
end
|
|
185
226
|
end
|
|
186
227
|
|
|
@@ -202,8 +243,8 @@ module ActiveFacts
|
|
|
202
243
|
# Note this works with unary fact types:
|
|
203
244
|
pi_role = ftroles[ftroles[0] != role ? 0 : -1]
|
|
204
245
|
|
|
205
|
-
next if ftroles.size == 2 && pi_role.
|
|
206
|
-
debug :pi, " Considering #{pi_role.
|
|
246
|
+
next if ftroles.size == 2 && pi_role.object_type == self
|
|
247
|
+
debug :pi, " Considering #{pi_role.object_type.name} as a PI role"
|
|
207
248
|
|
|
208
249
|
# If this is an identifying role, the PI is a PC whose role_sequence spans the role.
|
|
209
250
|
# Walk through all role_sequences that span this role, and test each:
|
|
@@ -221,13 +262,13 @@ module ActiveFacts
|
|
|
221
262
|
debug :pi, " Role Sequence touches #{fact_type.describe(pi_role)}"
|
|
222
263
|
|
|
223
264
|
fact_type_roles = fact_type.all_role
|
|
224
|
-
debug :pi, " residual is #{fact_type_roles.map{|r| r.
|
|
265
|
+
debug :pi, " residual is #{fact_type_roles.map{|r| r.object_type.name}.inspect} minus #{rsr.role.object_type.name}"
|
|
225
266
|
residual_roles = fact_type_roles-[rsr.role]
|
|
226
267
|
residual_roles.detect{|rfr|
|
|
227
|
-
debug :pi, " Checking residual role #{rfr.
|
|
268
|
+
debug :pi, " Checking residual role #{rfr.object_type.object_id}=>#{rfr.object_type.name}"
|
|
228
269
|
# This next line looks right, but breaks things. Find out what and why:
|
|
229
270
|
# !rfr.unique or
|
|
230
|
-
!all_supertypes.include?(rfr.
|
|
271
|
+
!all_supertypes.include?(rfr.object_type)
|
|
231
272
|
}
|
|
232
273
|
}
|
|
233
274
|
debug :pi, " Discounting this role_sequence because it includes alien roles"
|
|
@@ -275,7 +316,7 @@ module ActiveFacts
|
|
|
275
316
|
end
|
|
276
317
|
end
|
|
277
318
|
raise "No PI found for #{name}" unless pi
|
|
278
|
-
pi
|
|
319
|
+
@preferred_identifier = pi
|
|
279
320
|
end
|
|
280
321
|
end
|
|
281
322
|
|
|
@@ -326,8 +367,8 @@ module ActiveFacts
|
|
|
326
367
|
def create_implicit_fact_types
|
|
327
368
|
fact_type.all_role.each do |role|
|
|
328
369
|
next if role.implicit_fact_type # Already exists
|
|
329
|
-
implicit_fact_type = @constellation.ImplicitFactType(:new, :
|
|
330
|
-
phantom_role = @constellation.Role(implicit_fact_type, 0, :
|
|
370
|
+
implicit_fact_type = @constellation.ImplicitFactType(:new, :implying_role => role)
|
|
371
|
+
phantom_role = @constellation.Role(implicit_fact_type, 0, :object_type => self)
|
|
331
372
|
# We could create a copy of the visible external role here, but there's no need yet...
|
|
332
373
|
# Nor is there a need for a presence constraint, readings, etc.
|
|
333
374
|
end
|
|
@@ -357,7 +398,7 @@ module ActiveFacts
|
|
|
357
398
|
|
|
358
399
|
expanded.gsub!(/\{#{i}\}/) {
|
|
359
400
|
role_ref = role_refs[i]
|
|
360
|
-
player = role_ref.role.
|
|
401
|
+
player = role_ref.role.object_type
|
|
361
402
|
role_name = role.role_name
|
|
362
403
|
role_name = nil if role_name == ""
|
|
363
404
|
if role_name && define_role_names == false
|
|
@@ -475,7 +516,7 @@ module ActiveFacts
|
|
|
475
516
|
[
|
|
476
517
|
((min && min > 0 && min != max) ? "at least #{min == 1 ? "one" : min.to_s}" : nil),
|
|
477
518
|
((max && min != max) ? "at most #{max == 1 ? "one" : max.to_s}" : nil),
|
|
478
|
-
((max && min == max) ? "#{max == 1 ? "one" : max.to_s}" : nil)
|
|
519
|
+
((max && min == max) ? "#{max == 1 ? "one" : "exactly "+max.to_s}" : nil)
|
|
479
520
|
].compact * " and "
|
|
480
521
|
end
|
|
481
522
|
|
|
@@ -492,11 +533,11 @@ module ActiveFacts
|
|
|
492
533
|
end
|
|
493
534
|
|
|
494
535
|
def supertype_role
|
|
495
|
-
(roles = all_role.to_a)[0].
|
|
536
|
+
(roles = all_role.to_a)[0].object_type == supertype ? roles[0] : roles[1]
|
|
496
537
|
end
|
|
497
538
|
|
|
498
539
|
def subtype_role
|
|
499
|
-
(roles = all_role.to_a)[0].
|
|
540
|
+
(roles = all_role.to_a)[0].object_type == subtype ? roles[0] : roles[1]
|
|
500
541
|
end
|
|
501
542
|
end
|
|
502
543
|
|
|
@@ -507,7 +548,8 @@ module ActiveFacts
|
|
|
507
548
|
(is_unary_step ? " (unary) " : "from #{input_join_role.describe} ") +
|
|
508
549
|
"#{is_anti && 'not '}" +
|
|
509
550
|
"to #{output_join_role.describe} " +
|
|
510
|
-
"over " +
|
|
551
|
+
"over " +
|
|
552
|
+
(is_objectification_step ? 'objectification ' : '') +
|
|
511
553
|
"'#{fact_type.default_reading}'"
|
|
512
554
|
end
|
|
513
555
|
|
|
@@ -531,11 +573,10 @@ module ActiveFacts
|
|
|
531
573
|
|
|
532
574
|
class JoinNode
|
|
533
575
|
def describe
|
|
534
|
-
|
|
576
|
+
object_type.name +
|
|
535
577
|
(subscript ? "(#{subscript})" : '') +
|
|
536
578
|
" JN#{ordinal}" +
|
|
537
|
-
|
|
538
|
-
(value ? ' = '+value.describe : '')
|
|
579
|
+
(value ? ' = '+value.to_s : '')
|
|
539
580
|
end
|
|
540
581
|
|
|
541
582
|
def all_join_step
|
|
@@ -550,7 +591,7 @@ module ActiveFacts
|
|
|
550
591
|
|
|
551
592
|
class JoinRole
|
|
552
593
|
def describe
|
|
553
|
-
"#{role.
|
|
594
|
+
"#{role.object_type.name} JN#{join_node.ordinal}" +
|
|
554
595
|
(role_ref ? " (projected)" : "")
|
|
555
596
|
end
|
|
556
597
|
|
|
@@ -595,9 +636,9 @@ module ActiveFacts
|
|
|
595
636
|
join_nodes = all_join_node.sort_by{|jn| jn.ordinal}
|
|
596
637
|
join_nodes.each_with_index do |join_node, i|
|
|
597
638
|
raise "Join node #{i} should have ordinal #{join_node.ordinal}" unless join_node.ordinal == i
|
|
598
|
-
raise "Join Node #{i} has missing
|
|
639
|
+
raise "Join Node #{i} has missing object_type" unless join_node.object_type
|
|
599
640
|
join_node.all_join_role do |join_role|
|
|
600
|
-
raise "Join Node for #{
|
|
641
|
+
raise "Join Node for #{object_type.name} includes role played by #{join_role.object_type.name}" unless join_role.object_type == object_type
|
|
601
642
|
end
|
|
602
643
|
join_steps += join_node.all_join_step
|
|
603
644
|
end
|
|
@@ -622,10 +663,10 @@ module ActiveFacts
|
|
|
622
663
|
def default_reading
|
|
623
664
|
# There are two cases, where role is in a unary fact type, and where the fact type is objectified
|
|
624
665
|
# If a unary fact type is objectified, only the ImplicitFactType for the objectification is asserted
|
|
625
|
-
if objectification =
|
|
626
|
-
"#{objectification.name} involves #{
|
|
666
|
+
if objectification = implying_role.fact_type.entity_type
|
|
667
|
+
"#{objectification.name} involves #{implying_role.object_type.name}"
|
|
627
668
|
else
|
|
628
|
-
|
|
669
|
+
implying_role.fact_type.default_reading+" Boolean" # Must be a unary FT
|
|
629
670
|
end
|
|
630
671
|
end
|
|
631
672
|
|
|
@@ -651,7 +692,7 @@ module ActiveFacts
|
|
|
651
692
|
def leading_adjective; nil; end
|
|
652
693
|
def trailing_adjective; nil; end
|
|
653
694
|
def describe
|
|
654
|
-
@role.
|
|
695
|
+
@role.object_type.name
|
|
655
696
|
end
|
|
656
697
|
end
|
|
657
698
|
|
|
@@ -671,16 +712,20 @@ module ActiveFacts
|
|
|
671
712
|
end
|
|
672
713
|
|
|
673
714
|
def role_sequence
|
|
674
|
-
ImplicitReadingRoleSequence.new([@fact_type.
|
|
715
|
+
ImplicitReadingRoleSequence.new([@fact_type.implying_role, @fact_type.all_role.single])
|
|
675
716
|
end
|
|
676
717
|
|
|
677
718
|
def ordinal; 0; end
|
|
719
|
+
|
|
720
|
+
def expand
|
|
721
|
+
@fact_type.default_reading
|
|
722
|
+
end
|
|
678
723
|
end
|
|
679
724
|
|
|
680
725
|
def all_reading
|
|
681
726
|
[@reading ||= ImplicitReading.new(
|
|
682
727
|
self,
|
|
683
|
-
|
|
728
|
+
implying_role.fact_type.entity_type ? "{0} involves {1}" : implying_role.fact_type.default_reading+" Boolean"
|
|
684
729
|
)]
|
|
685
730
|
end
|
|
686
731
|
end
|
|
@@ -694,16 +739,16 @@ module ActiveFacts
|
|
|
694
739
|
options != :counterpart && roles.map{|role| role.fact_type}.uniq.size == 1
|
|
695
740
|
proximate_sups, counterpart_sups, obj_sups, counterpart_roles, objectification_roles =
|
|
696
741
|
*roles.inject(nil) do |d_c_o, role|
|
|
697
|
-
|
|
742
|
+
object_type = role.object_type
|
|
698
743
|
fact_type = role.fact_type
|
|
699
744
|
|
|
700
|
-
proximate_role_supertypes =
|
|
745
|
+
proximate_role_supertypes = object_type.supertypes_transitive
|
|
701
746
|
|
|
702
747
|
# A role in an objectified fact type may indicate either the objectification or the counterpart player.
|
|
703
748
|
# This could be ambiguous. Figure out both and prefer the counterpart over the objectification.
|
|
704
749
|
counterpart_role_supertypes =
|
|
705
750
|
if fact_type.all_role.size > 2
|
|
706
|
-
possible_roles = fact_type.all_role.select{|r| d_c_o && d_c_o[1].include?(r.
|
|
751
|
+
possible_roles = fact_type.all_role.select{|r| d_c_o && d_c_o[1].include?(r.object_type) }
|
|
707
752
|
if possible_roles.size == 1 # Only one candidate matches the types of the possible join nodes
|
|
708
753
|
counterpart_role = possible_roles[0]
|
|
709
754
|
d_c_o[1] # No change
|
|
@@ -714,23 +759,23 @@ module ActiveFacts
|
|
|
714
759
|
if d_c_o
|
|
715
760
|
st = nil
|
|
716
761
|
counterpart_role =
|
|
717
|
-
fact_type.all_role.detect{|r| ((st = r.
|
|
762
|
+
fact_type.all_role.detect{|r| ((st = r.object_type.supertypes_transitive) & d_c_o[1]).size > 0}
|
|
718
763
|
st
|
|
719
764
|
else
|
|
720
765
|
counterpart_role = nil # This can't work, we don't have any basis for a decision (must be objectification)
|
|
721
766
|
[]
|
|
722
767
|
end
|
|
723
|
-
#fact_type.all_role.map{|r| r.
|
|
768
|
+
#fact_type.all_role.map{|r| r.object_type.supertypes_transitive}.flatten.uniq
|
|
724
769
|
end
|
|
725
770
|
else
|
|
726
771
|
# Get the supertypes of the counterpart role (care with unaries):
|
|
727
772
|
ftr = role.fact_type.all_role.to_a
|
|
728
|
-
(counterpart_role = ftr[0] == role ? ftr[-1] : ftr[0]).
|
|
773
|
+
(counterpart_role = ftr[0] == role ? ftr[-1] : ftr[0]).object_type.supertypes_transitive
|
|
729
774
|
end
|
|
730
775
|
|
|
731
776
|
if fact_type.entity_type
|
|
732
777
|
objectification_role_supertypes =
|
|
733
|
-
fact_type.entity_type.supertypes_transitive+
|
|
778
|
+
fact_type.entity_type.supertypes_transitive+object_type.supertypes_transitive
|
|
734
779
|
objectification_role = role.implicit_fact_type.all_role.single # Find the phantom role here
|
|
735
780
|
else
|
|
736
781
|
objectification_role_supertypes = counterpart_role_supertypes
|
|
@@ -755,7 +800,7 @@ module ActiveFacts
|
|
|
755
800
|
# if we can use an objectification join to an object type that is:
|
|
756
801
|
if counterpart_sups.size > 0 && obj_sups.size > 0 && counterpart_sups[0] != obj_sups[0]
|
|
757
802
|
debug :join, "ambiguous join, could be over #{counterpart_sups[0].name} or #{obj_sups[0].name}"
|
|
758
|
-
if !roles.detect{|r| r.
|
|
803
|
+
if !roles.detect{|r| r.object_type == counterpart_sups[0]} and roles.detect{|r| r.object_type == obj_sups[0]}
|
|
759
804
|
debug :join, "discounting #{counterpart_sups[0].name} in favour of direct objectification"
|
|
760
805
|
counterpart_sups = []
|
|
761
806
|
end
|
|
@@ -780,14 +825,14 @@ module ActiveFacts
|
|
|
780
825
|
if rv.instance.value
|
|
781
826
|
v = rv.instance.verbalise
|
|
782
827
|
else
|
|
783
|
-
if (c = rv.instance.
|
|
828
|
+
if (c = rv.instance.object_type).is_a?(EntityType)
|
|
784
829
|
if !c.preferred_identifier.role_sequence.all_role_ref.detect{|rr| rr.role.fact_type == fact_type}
|
|
785
830
|
v = rv.instance.verbalise
|
|
786
831
|
end
|
|
787
832
|
end
|
|
788
833
|
end
|
|
789
834
|
next nil unless v
|
|
790
|
-
v.to_s.sub(/(#{rv.instance.
|
|
835
|
+
v.to_s.sub(/(#{rv.instance.object_type.name}|\S*)\s/,'')
|
|
791
836
|
end
|
|
792
837
|
reading.expand([], false, instance_verbalisations)
|
|
793
838
|
end
|
|
@@ -795,15 +840,15 @@ module ActiveFacts
|
|
|
795
840
|
|
|
796
841
|
class Instance
|
|
797
842
|
def verbalise(context = nil)
|
|
798
|
-
return "#{
|
|
843
|
+
return "#{object_type.name} #{value}" if object_type.is_a?(ValueType)
|
|
799
844
|
|
|
800
|
-
return "#{
|
|
845
|
+
return "#{object_type.name} where #{fact.verbalise(context)}" if object_type.fact_type
|
|
801
846
|
|
|
802
847
|
# It's an entity that's not an objectified fact type
|
|
803
848
|
# REVISIT: If it has a simple identifier, there's no need to fully verbalise the identifying facts
|
|
804
|
-
pi =
|
|
849
|
+
pi = object_type.preferred_identifier
|
|
805
850
|
identifying_role_refs = pi.role_sequence.all_role_ref_in_order
|
|
806
|
-
"#{
|
|
851
|
+
"#{object_type.name}" +
|
|
807
852
|
" is identified by " + # REVISIT: Where the single fact type is TypeInheritance, we can shrink this
|
|
808
853
|
if identifying_role_refs.size == 1 &&
|
|
809
854
|
(role = identifying_role_refs[0].role) &&
|
|
@@ -816,13 +861,13 @@ module ActiveFacts
|
|
|
816
861
|
identifying_role_refs.map do |rr|
|
|
817
862
|
rr = rr.preferred_role_ref
|
|
818
863
|
[ (l = rr.leading_adjective) ? l+"-" : nil,
|
|
819
|
-
rr.role.role_name || rr.role.
|
|
864
|
+
rr.role.role_name || rr.role.object_type.name,
|
|
820
865
|
(t = rr.trailing_adjective) ? l+"-" : nil
|
|
821
866
|
].compact*""
|
|
822
867
|
end * " and " +
|
|
823
868
|
" where " +
|
|
824
869
|
identifying_role_refs.map do |rr| # Go through the identifying roles and emit the facts that define them
|
|
825
|
-
instance_role =
|
|
870
|
+
instance_role = object_type.all_role.detect{|r| r.fact_type == rr.role.fact_type}
|
|
826
871
|
identifying_fact = all_role_value.detect{|rv| rv.fact.fact_type == rr.role.fact_type}.fact
|
|
827
872
|
#counterpart_role = (rr.role.fact_type.all_role.to_a-[instance_role])[0]
|
|
828
873
|
#identifying_instance = counterpart_role.all_role_value.detect{|rv| rv.fact == identifying_fact}.instance
|