activefacts 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -13
- data/Rakefile +2 -2
- data/bin/afgen +1 -1
- data/bin/cql +118 -27
- data/examples/CQL/Insurance.cql +2 -2
- data/examples/CQL/Metamodel.cql +3 -3
- data/examples/CQL/SchoolActivities.cql +1 -1
- data/examples/CQL/Warehousing.cql +5 -4
- data/lib/activefacts/cql.rb +1 -1
- data/lib/activefacts/cql/Language/English.treetop +2 -1
- data/lib/activefacts/cql/compiler.rb +6 -6
- data/lib/activefacts/cql/compiler/clause.rb +69 -46
- data/lib/activefacts/cql/compiler/constraint.rb +14 -14
- data/lib/activefacts/cql/compiler/entity_type.rb +24 -24
- data/lib/activefacts/cql/compiler/fact.rb +40 -27
- data/lib/activefacts/cql/compiler/fact_type.rb +16 -16
- data/lib/activefacts/cql/compiler/query.rb +12 -12
- data/lib/activefacts/cql/compiler/shared.rb +9 -0
- data/lib/activefacts/cql/compiler/value_type.rb +4 -4
- data/lib/activefacts/cql/parser.rb +9 -9
- data/lib/activefacts/generate/cql.rb +41 -20
- data/lib/activefacts/generate/helpers/oo.rb +33 -70
- data/lib/activefacts/generate/helpers/ordered.rb +61 -87
- data/lib/activefacts/generate/ruby.rb +12 -72
- data/lib/activefacts/generate/transform/surrogate.rb +13 -13
- data/lib/activefacts/input/orm.rb +72 -71
- data/lib/activefacts/persistence/columns.rb +66 -31
- data/lib/activefacts/persistence/foreignkey.rb +6 -6
- data/lib/activefacts/persistence/index.rb +12 -12
- data/lib/activefacts/persistence/object_type.rb +15 -12
- data/lib/activefacts/persistence/reference.rb +20 -18
- data/lib/activefacts/persistence/tables.rb +40 -36
- data/lib/activefacts/support.rb +69 -123
- data/lib/activefacts/version.rb +2 -2
- data/lib/activefacts/vocabulary/extensions.rb +42 -39
- data/lib/activefacts/vocabulary/metamodel.rb +11 -1
- data/lib/activefacts/vocabulary/verbaliser.rb +28 -28
- data/spec/cql/contractions_spec.rb +1 -1
- data/spec/cql/entity_type_spec.rb +1 -1
- data/spec/cql/fact_type_matching_spec.rb +3 -3
- data/spec/cql/role_matching_spec.rb +4 -4
- data/spec/cql/samples_spec.rb +2 -2
- data/spec/cql_cql_spec.rb +1 -1
- data/spec/helpers/array_matcher.rb +1 -1
- data/spec/norma_ruby_sql_spec.rb +2 -2
- data/spec/norma_tables_spec.rb +3 -2
- metadata +47 -68
@@ -78,7 +78,7 @@ module ActiveFacts
|
|
78
78
|
ref.to.is_a?(ActiveFacts::Metamodel::EntityType) and
|
79
79
|
(role_ref = ref.to.preferred_identifier.role_sequence.all_role_ref.single) and
|
80
80
|
role_ref.role == ref.from_role
|
81
|
-
|
81
|
+
trace :columns, "Skipping #{ref}, identifies non-initial object"
|
82
82
|
next a
|
83
83
|
end
|
84
84
|
|
@@ -87,19 +87,19 @@ module ActiveFacts
|
|
87
87
|
# When traversing type inheritances, keep the subtype name, not the supertype names as well:
|
88
88
|
if a.size > 0 && ref.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
|
89
89
|
if ref.to != ref.fact_type.subtype # Did we already have the subtype?
|
90
|
-
|
90
|
+
trace :columns, "Skipping supertype #{ref}"
|
91
91
|
next a
|
92
92
|
end
|
93
|
-
|
93
|
+
trace :columns, "Eliding supertype in #{ref}"
|
94
94
|
last_names.size.times { a.pop } # Remove the last names added
|
95
95
|
elsif last_names.last && last_names.last == names[0][0...last_names.last.size]
|
96
96
|
# When Xyz is followed by XyzID, truncate that to just ID
|
97
|
-
|
97
|
+
trace :columns, "truncating repeated #{last_names.last} in #{names[0]}"
|
98
98
|
names[0] = names[0][last_names.last.size..-1]
|
99
99
|
names.shift if names[0] == ''
|
100
100
|
elsif last_names.last == names[0]
|
101
101
|
# Same, but where an underscore split up the words
|
102
|
-
|
102
|
+
trace :columns, "truncating repeated name in #{names.inspect}"
|
103
103
|
names.shift
|
104
104
|
end
|
105
105
|
|
@@ -113,7 +113,7 @@ module ActiveFacts
|
|
113
113
|
role_ref.role == ref.to_role and
|
114
114
|
names[0][0...et.name.size].downcase == et.name.downcase
|
115
115
|
|
116
|
-
|
116
|
+
trace :columns, "truncating transitive identifying role #{names.inspect}"
|
117
117
|
names[0] = names[0][et.name.size..-1]
|
118
118
|
names.shift if names[0] == ""
|
119
119
|
end
|
@@ -177,10 +177,18 @@ module ActiveFacts
|
|
177
177
|
# The comment is the readings from the References expressed as a series of steps (not a full verbalisation)
|
178
178
|
def comment
|
179
179
|
@references.map do |ref|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
180
|
+
# REVISIT: Some contraction would be nice here
|
181
|
+
objectification = ref.fact_type && ref.fact_type.entity_type
|
182
|
+
involves = ''
|
183
|
+
if objectification && ref == @references.last
|
184
|
+
objectification = nil if ref.fact_type.all_role.size == 1 # Disregard the objectification of a trailing unary
|
185
|
+
involves = ref.to_role.link_fact_type.default_reading[ref.fact_type.entity_type.name.size..-1]
|
186
|
+
end
|
187
|
+
(objectification ? objectification.name + ' (in which ' : '') +
|
188
|
+
(ref.is_mandatory || ref.is_unary ? '' : 'maybe ') +
|
189
|
+
ref.reading +
|
190
|
+
(objectification ? ')'+involves : '')
|
191
|
+
end.compact * " and "
|
184
192
|
end
|
185
193
|
|
186
194
|
def to_s #:nodoc:
|
@@ -194,8 +202,34 @@ module ActiveFacts
|
|
194
202
|
cols =
|
195
203
|
if is_unary
|
196
204
|
kind = "unary "
|
197
|
-
|
198
|
-
|
205
|
+
objectified_unary_columns =
|
206
|
+
((@to && @to.fact_type) ? @to.all_columns(excluded_supertypes) : [])
|
207
|
+
|
208
|
+
=begin
|
209
|
+
# This code omits the unary if it's objectified and that plays a mandatory role
|
210
|
+
first_mandatory_column = nil
|
211
|
+
if (@to && @to.fact_type)
|
212
|
+
trace :unary_col, "Deciding whether to skip unary column for #{inspect}" do
|
213
|
+
first_mandatory_column =
|
214
|
+
objectified_unary_columns.detect do |col| # Detect a mandatory column for the unary
|
215
|
+
trace :unary_col, "checking column #{col.name}" do
|
216
|
+
!col.references.detect do |ref|
|
217
|
+
trace :unary_col, "#{ref} is mandatory=#{ref.is_mandatory.inspect}"
|
218
|
+
!ref.is_mandatory
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
if is_from_objectified_fact && first_mandatory_column
|
223
|
+
trace :unary_col, "Skipping unary column for #{inspect} because #{first_mandatory_column.name} is mandatory"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
(is_from_objectified_fact && first_mandatory_column ? [] : [Column.new()]) + # The unary itself, unless its objectified
|
229
|
+
=end
|
230
|
+
|
231
|
+
[Column.new()] + # The unary itself
|
232
|
+
objectified_unary_columns
|
199
233
|
elsif is_self_value
|
200
234
|
kind = "self-role "
|
201
235
|
[Column.new()]
|
@@ -210,9 +244,9 @@ module ActiveFacts
|
|
210
244
|
c.prepend self
|
211
245
|
end
|
212
246
|
|
213
|
-
|
247
|
+
trace :columns, "Columns from #{kind}#{self}" do
|
214
248
|
cols.each {|c|
|
215
|
-
|
249
|
+
trace :columns, "#{c}"
|
216
250
|
}
|
217
251
|
end
|
218
252
|
end
|
@@ -239,7 +273,7 @@ module ActiveFacts
|
|
239
273
|
class ValueType < DomainObjectType
|
240
274
|
# The identifier_columns for a ValueType can only ever be the self-value role that was injected
|
241
275
|
def identifier_columns
|
242
|
-
|
276
|
+
trace :columns, "Identifier Columns for #{name}" do
|
243
277
|
raise "Illegal call to identifier_columns for absorbed ValueType #{name}" unless is_table
|
244
278
|
if isr = injected_surrogate_role
|
245
279
|
columns.select{|column| column.references[0].from_role == isr }
|
@@ -252,7 +286,7 @@ module ActiveFacts
|
|
252
286
|
# When creating a foreign key to this ValueType, what columns must we include?
|
253
287
|
# This must be a fresh copy, because the columns will have References prepended
|
254
288
|
def reference_columns(excluded_supertypes) #:nodoc:
|
255
|
-
|
289
|
+
trace :columns, "Reference Columns for #{name}" do
|
256
290
|
if is_table
|
257
291
|
if isr = injected_surrogate_role
|
258
292
|
ref_from = references_from.detect{|ref| ref.from_role == isr}
|
@@ -270,14 +304,14 @@ module ActiveFacts
|
|
270
304
|
# This must be a fresh copy, because the columns will have References prepended.
|
271
305
|
def all_columns(excluded_supertypes) #:nodoc:
|
272
306
|
columns = []
|
273
|
-
|
307
|
+
trace :columns, "All Columns for #{name}" do
|
274
308
|
if is_table
|
275
309
|
self_value_reference
|
276
310
|
else
|
277
311
|
columns << ActiveFacts::Persistence::Column.new
|
278
312
|
end
|
279
313
|
references_from.each do |ref|
|
280
|
-
|
314
|
+
trace :columns, "Columns absorbed via #{ref}" do
|
281
315
|
columns += ref.columns({})
|
282
316
|
end
|
283
317
|
end
|
@@ -297,7 +331,7 @@ module ActiveFacts
|
|
297
331
|
class EntityType < DomainObjectType
|
298
332
|
# The identifier_columns for an EntityType are the columns that result from the identifying roles
|
299
333
|
def identifier_columns
|
300
|
-
|
334
|
+
trace :columns, "Identifier Columns for #{name}" do
|
301
335
|
if absorbed_via and
|
302
336
|
# If this is a subtype that has its own identification, use that.
|
303
337
|
(all_type_inheritance_as_subtype.size == 0 ||
|
@@ -316,16 +350,17 @@ module ActiveFacts
|
|
316
350
|
# When creating a foreign key to this EntityType, what columns must we include (the identifier columns)?
|
317
351
|
# This must be a fresh copy, because the columns will have References prepended
|
318
352
|
def reference_columns(excluded_supertypes) #:nodoc:
|
319
|
-
|
353
|
+
trace :columns, "Reference Columns for #{name}" do
|
320
354
|
|
321
355
|
if absorbed_via and
|
322
356
|
# If this is not a subtype, or is a subtype that has its own identification, use the id.
|
323
357
|
(all_type_inheritance_as_subtype.size == 0 ||
|
324
358
|
all_type_inheritance_as_subtype.detect{|ti| ti.provides_identification })
|
325
359
|
rc = absorbed_via.from.reference_columns(excluded_supertypes)
|
326
|
-
# The absorbed_via reference gets skipped here,
|
327
|
-
|
328
|
-
|
360
|
+
# The absorbed_via reference gets skipped here, and also in object_type.rb
|
361
|
+
trace :columns, "Skipping #{absorbed_via}"
|
362
|
+
absorbed_mirror ||= absorbed_via.reversed
|
363
|
+
rc.each{|col| col.prepend(absorbed_mirror)}
|
329
364
|
return rc
|
330
365
|
end
|
331
366
|
|
@@ -344,7 +379,7 @@ module ActiveFacts
|
|
344
379
|
# When absorbing this EntityType, what columns must be absorbed?
|
345
380
|
# This must be a fresh copy, because the columns will have References prepended.
|
346
381
|
def all_columns(excluded_supertypes) #:nodoc:
|
347
|
-
|
382
|
+
trace :columns, "All Columns for #{name}" do
|
348
383
|
columns = []
|
349
384
|
sups = supertypes
|
350
385
|
pi_roles = preferred_identifier.role_sequence.all_role_ref.map{|rr| rr.role}
|
@@ -357,10 +392,10 @@ module ActiveFacts
|
|
357
392
|
end
|
358
393
|
[3, ref.to_names]
|
359
394
|
end.each do |ref|
|
360
|
-
|
395
|
+
trace :columns, "Columns absorbed via #{ref}" do
|
361
396
|
if (ref.role_type == :supertype)
|
362
397
|
if excluded_supertypes[ref.to]
|
363
|
-
|
398
|
+
trace :columns, "Exclude #{ref.to.name}, we already inherited it"
|
364
399
|
next
|
365
400
|
end
|
366
401
|
|
@@ -392,20 +427,20 @@ module ActiveFacts
|
|
392
427
|
# REVISIT: Is now a good time to apply schema transforms or should this be more explicit?
|
393
428
|
finish_schema
|
394
429
|
|
395
|
-
|
430
|
+
trace :columns, "Populating all columns" do
|
396
431
|
all_object_type.each do |object_type|
|
397
432
|
next if !object_type.is_table
|
398
|
-
|
433
|
+
trace :columns, "Populating columns for table #{object_type.name}" do
|
399
434
|
object_type.populate_columns
|
400
435
|
end
|
401
436
|
end
|
402
437
|
end
|
403
|
-
|
438
|
+
trace :columns, "Finished columns" do
|
404
439
|
all_object_type.each do |object_type|
|
405
440
|
next if !object_type.is_table
|
406
|
-
|
441
|
+
trace :columns, "Finished columns for table #{object_type.name}" do
|
407
442
|
object_type.columns.each do |column|
|
408
|
-
|
443
|
+
trace :columns, "#{column}"
|
409
444
|
end
|
410
445
|
end
|
411
446
|
end
|
@@ -107,7 +107,7 @@ module ActiveFacts
|
|
107
107
|
ref.fk_jump = true
|
108
108
|
array << [ref]
|
109
109
|
elsif ref.is_absorbing or (ref.to && !ref.to.is_table)
|
110
|
-
|
110
|
+
trace :fk, "getting fks absorbed into #{name} via #{ref}" do
|
111
111
|
ref.to.all_absorbed_foreign_key_reference_path.each do |aref|
|
112
112
|
array << aref.insert(0, ref)
|
113
113
|
end
|
@@ -129,24 +129,24 @@ module ActiveFacts
|
|
129
129
|
begin
|
130
130
|
fk_ref_paths = all_absorbed_foreign_key_reference_path
|
131
131
|
fk_ref_paths.map do |fk_ref_path|
|
132
|
-
|
132
|
+
trace :fk, "\nFK: " + fk_ref_path.map{|fk_ref| fk_ref.reading }*" and " do
|
133
133
|
|
134
134
|
from_columns = (columns||all_columns({})).select{|column|
|
135
135
|
column.references[0...fk_ref_path.size] == fk_ref_path
|
136
136
|
}
|
137
|
-
|
137
|
+
trace :fk, "from_columns = #{from_columns.map { |column| column.name }*", "}"
|
138
138
|
|
139
139
|
# Figure out absorption on the target end:
|
140
140
|
to = fk_ref_path.last.to
|
141
141
|
if to.absorbed_via
|
142
|
-
|
142
|
+
trace :fk, "Reference target #{fk_ref_path.last.to.name} is absorbed via:" do
|
143
143
|
while (r = to.absorbed_via)
|
144
144
|
m = r.reversed
|
145
|
-
|
145
|
+
trace :fk, "#{m.reading}"
|
146
146
|
fk_ref_path << m
|
147
147
|
to = m.from == to ? m.to : m.from
|
148
148
|
end
|
149
|
-
|
149
|
+
trace :fk, "Absorption ends at #{to.name}"
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
@@ -124,13 +124,13 @@ module ActiveFacts
|
|
124
124
|
# Note also that this produces columns ordered for each refpath the same as the
|
125
125
|
# order of the columns, not the same as the columns in the PK for which they might be an FK.
|
126
126
|
all_column_by_ref_path =
|
127
|
-
|
127
|
+
trace :index2, "Indexing columns by ref_path" do
|
128
128
|
columns.inject({}) do |hash, column|
|
129
|
-
|
129
|
+
trace :index2, "References in column #{name}.#{column.name}" do
|
130
130
|
ref_path = column.absorption_references
|
131
131
|
raise "No absorption_references for #{column.name} from #{column.references.map(&:to_s)*" and "}" if !ref_path || ref_path.empty?
|
132
132
|
(hash[ref_path] ||= []) << column
|
133
|
-
|
133
|
+
trace :index2, "#{column.name} involves #{ref_path.map(&:to_s)*" and "}"
|
134
134
|
end
|
135
135
|
hash
|
136
136
|
end
|
@@ -143,7 +143,7 @@ module ActiveFacts
|
|
143
143
|
inject({}) do |hash, ref_path|
|
144
144
|
ref_path.each do |ref|
|
145
145
|
next unless ref.to_role
|
146
|
-
#
|
146
|
+
# trace :index2, "Considering #{ref_path.map(&:to_s)*" and "} yielding columns #{all_column_by_ref_path[ref_path].map{|c| c.name('.')}*", "}"
|
147
147
|
ref.to_role.all_role_ref.each do |role_ref|
|
148
148
|
all_pcs = role_ref.role_sequence.all_presence_constraint
|
149
149
|
# puts "pcs over #{ref_path.map{|r| r.to_names}.flatten*'.'}: #{role_ref.role_sequence.all_presence_constraint.map(&:describe)*"; "}" if all_pcs.size > 0
|
@@ -171,9 +171,9 @@ module ActiveFacts
|
|
171
171
|
hash
|
172
172
|
end
|
173
173
|
|
174
|
-
|
174
|
+
trace :index, "All Indices in #{name}:" do
|
175
175
|
@indices = columns_by_unique_constraint.map do |uc, columns_with_ordinal|
|
176
|
-
|
176
|
+
trace :index, "Index due to uc #{uc.concept.guid} on #{name} over (#{columns_with_ordinal.sort_by{|onc|onc[0]}.map{|ca| ca[2].name}.inspect})"
|
177
177
|
columns = columns_with_ordinal.sort_by{|ca| [ca[0,2], ca[2].name]}.map{|ca| ca[2]}
|
178
178
|
absorption_level = columns.map(&:absorption_level).min
|
179
179
|
over = columns[0].references[absorption_level].from
|
@@ -193,7 +193,7 @@ module ActiveFacts
|
|
193
193
|
columns,
|
194
194
|
uc.is_preferred_identifier
|
195
195
|
)
|
196
|
-
|
196
|
+
trace :index, index
|
197
197
|
index
|
198
198
|
end.
|
199
199
|
compact.
|
@@ -211,24 +211,24 @@ module ActiveFacts
|
|
211
211
|
|
212
212
|
class Vocabulary
|
213
213
|
def populate_all_indices #:nodoc:
|
214
|
-
|
214
|
+
trace :index, "Populating all object_type indices" do
|
215
215
|
all_object_type.each do |object_type|
|
216
216
|
object_type.clear_indices
|
217
217
|
end
|
218
218
|
all_object_type.each do |object_type|
|
219
219
|
next unless object_type.is_table
|
220
|
-
|
220
|
+
trace :index, "Populating indices for #{object_type.name}" do
|
221
221
|
object_type.populate_indices
|
222
222
|
end
|
223
223
|
end
|
224
224
|
end
|
225
|
-
|
225
|
+
trace :index, "Finished object_type indices" do
|
226
226
|
all_object_type.each do |object_type|
|
227
227
|
next unless object_type.is_table
|
228
228
|
next unless object_type.indices.size > 0
|
229
|
-
|
229
|
+
trace :index, "#{object_type.name}:" do
|
230
230
|
object_type.indices.each do |index|
|
231
|
-
|
231
|
+
trace :index, index
|
232
232
|
end
|
233
233
|
end
|
234
234
|
end
|
@@ -12,12 +12,14 @@ module ActiveFacts
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def columns
|
15
|
+
raise "This method is no longer in use"
|
16
|
+
=begin
|
15
17
|
return @columns if @columns
|
16
|
-
|
18
|
+
trace :persistence, "Calculating columns for #{basename}" do
|
17
19
|
@columns = (
|
18
20
|
if superclass.is_entity_type
|
19
21
|
# REVISIT: Need keys to secondary supertypes as well, but no duplicates.
|
20
|
-
|
22
|
+
trace :persistence, "Separate subtype has a foreign key to its supertype" do
|
21
23
|
superclass.__absorb([[superclass.basename]], self)
|
22
24
|
end
|
23
25
|
else
|
@@ -28,7 +30,7 @@ module ActiveFacts
|
|
28
30
|
role.unique && !role.counterpart_unary_has_precedence
|
29
31
|
end.inject([]) do |columns, role|
|
30
32
|
rn = role.name.to_s.split(/_/)
|
31
|
-
|
33
|
+
trace :persistence, "Role #{rn*'.'}" do
|
32
34
|
columns += role.counterpart_object_type.__absorb([rn], role.counterpart)
|
33
35
|
end
|
34
36
|
end +
|
@@ -38,7 +40,7 @@ module ActiveFacts
|
|
38
40
|
inject([]) do |columns, subtype|
|
39
41
|
# Pass self as 2nd param here, not a role, standing for the supertype role
|
40
42
|
subtype_name = subtype.basename
|
41
|
-
|
43
|
+
trace :persistence, "Absorbing subtype #{subtype_name}" do
|
42
44
|
columns += subtype.__absorb([[subtype_name]], self)
|
43
45
|
end
|
44
46
|
end
|
@@ -55,6 +57,7 @@ module ActiveFacts
|
|
55
57
|
end*"."
|
56
58
|
end
|
57
59
|
end
|
60
|
+
=end
|
58
61
|
end
|
59
62
|
|
60
63
|
# Return an array of the absorbed columns, using prefix for name truncation
|
@@ -68,7 +71,7 @@ module ActiveFacts
|
|
68
71
|
# counterpart_object_type = role.counterpart_object_type
|
69
72
|
# This omission matches the one in columns.rb, see EntityType#reference_columns
|
70
73
|
# new_prefix = prefix + [role.name.to_s.split(/_/)]
|
71
|
-
|
74
|
+
trace :persistence, "Reference to #{role.name} (absorbed elsewhere)" do
|
72
75
|
role.counterpart_object_type.__absorb(prefix, role.counterpart)
|
73
76
|
end
|
74
77
|
else
|
@@ -86,7 +89,7 @@ module ActiveFacts
|
|
86
89
|
inject([]) do |columns, subtype|
|
87
90
|
# Pass self as 2nd param here, not a role, standing for the supertype role
|
88
91
|
new_prefix = prefix[0..-2] + [[subtype.basename]]
|
89
|
-
|
92
|
+
trace :persistence, "Absorbed subtype #{subtype.basename}" do
|
90
93
|
columns += subtype.__absorb(new_prefix, self)
|
91
94
|
end
|
92
95
|
end
|
@@ -98,7 +101,7 @@ module ActiveFacts
|
|
98
101
|
# Create a foreign key to the table
|
99
102
|
if is_entity_type
|
100
103
|
ir = identifying_role_names.map{|role_name| roles(role_name) }
|
101
|
-
|
104
|
+
trace :persistence, "Reference to #{basename} with #{prefix.inspect}" do
|
102
105
|
ic = identifying_role_names.map{|role_name| role_name.to_s.split(/_/)}
|
103
106
|
ir.inject([]) do |columns, role|
|
104
107
|
columns += __absorb_role(prefix, role)
|
@@ -107,7 +110,7 @@ module ActiveFacts
|
|
107
110
|
else
|
108
111
|
# Reference to value type which is a table
|
109
112
|
col = prefix.clone
|
110
|
-
|
113
|
+
trace :persistence, "Self-value #{col[-1]}.Value"
|
111
114
|
col[-1] += ["Value"]
|
112
115
|
col
|
113
116
|
end
|
@@ -122,23 +125,23 @@ module ActiveFacts
|
|
122
125
|
(n = irn[0].to_s.split(/_/)).size > 1 and
|
123
126
|
(owner = role.owner.basename.snakecase.split(/_/)) and
|
124
127
|
n[0...owner.size] == owner
|
125
|
-
|
128
|
+
trace :persistence, "truncating transitive identifying role #{n.inspect}"
|
126
129
|
owner.size.times { n.shift }
|
127
130
|
new_prefix = prefix + [n]
|
128
131
|
elsif (c = role.counterpart_object_type).is_entity_type and
|
129
132
|
(irn = c.identifying_role_names).size == 1 and
|
130
133
|
#irn[0].to_s.split(/_/)[0] == role.owner.basename.downcase
|
131
134
|
irn[0] == role.counterpart.name
|
132
|
-
#
|
135
|
+
#trace :persistence, "=== #{irn[0].to_s.split(/_/)[0]} elided ==="
|
133
136
|
new_prefix = prefix
|
134
137
|
elsif (fa_role = fully_absorbed) && fa_role == role
|
135
138
|
new_prefix = prefix
|
136
139
|
else
|
137
140
|
new_prefix = prefix + [role.name.to_s.split(/_/)]
|
138
141
|
end
|
139
|
-
#
|
142
|
+
#trace :persistence, "new_prefix is #{new_prefix*"."}"
|
140
143
|
|
141
|
-
|
144
|
+
trace :persistence, "Absorbing role #{role.name} as #{new_prefix[prefix.size..-1]*"."}" do
|
142
145
|
role.counterpart_object_type.__absorb(new_prefix, role.counterpart)
|
143
146
|
end
|
144
147
|
end
|
@@ -74,8 +74,9 @@ module ActiveFacts
|
|
74
74
|
|
75
75
|
# Is this Reference covered by a mandatory constraint (implicitly or explicitly)
|
76
76
|
def is_mandatory
|
77
|
-
|
78
|
-
|
77
|
+
!is_unary &&
|
78
|
+
(!@from_role || # All phantom roles of fact types are mandatory
|
79
|
+
@from_role.is_mandatory)
|
79
80
|
end
|
80
81
|
|
81
82
|
# Is this Reference from a unary Role?
|
@@ -188,7 +189,7 @@ module ActiveFacts
|
|
188
189
|
@from.references_from << self
|
189
190
|
@to.references_to << self if @to # Guard against self-values
|
190
191
|
|
191
|
-
|
192
|
+
trace :references, "Adding #{to_s}"
|
192
193
|
self
|
193
194
|
end
|
194
195
|
|
@@ -196,7 +197,7 @@ module ActiveFacts
|
|
196
197
|
# Remove from @to and @from's reference lists if present
|
197
198
|
return unless @from.references_from.delete(self)
|
198
199
|
@to.references_to.delete self if @to # Guard against self-values
|
199
|
-
|
200
|
+
trace :references, "Dropping #{to_s}"
|
200
201
|
self
|
201
202
|
end
|
202
203
|
|
@@ -207,7 +208,7 @@ module ActiveFacts
|
|
207
208
|
|
208
209
|
# The reading for the fact type underlying this Reference
|
209
210
|
def reading
|
210
|
-
is_self_value ? "#{from.name} has value" : @fact_type.
|
211
|
+
is_self_value ? "#{from.name} has value" : @fact_type.reading_preferably_starting_with_role(@from_role).expand
|
211
212
|
end
|
212
213
|
|
213
214
|
def inspect #:nodoc:
|
@@ -284,7 +285,7 @@ module ActiveFacts
|
|
284
285
|
|
285
286
|
def populate_reference role #:nodoc:
|
286
287
|
role_type = role.role_type
|
287
|
-
|
288
|
+
trace :references, "#{name} has #{role_type} role in '#{role.fact_type.describe}'"
|
288
289
|
case role_type
|
289
290
|
when :many_one
|
290
291
|
ActiveFacts::Persistence::Reference.new(self, role).tabulate # A simple reference
|
@@ -294,7 +295,7 @@ module ActiveFacts
|
|
294
295
|
ActiveFacts::Persistence::Reference.new(self, role).tabulate # A simple reference; check that
|
295
296
|
else
|
296
297
|
# Can't absorb many of these into one of those
|
297
|
-
#
|
298
|
+
#trace :references, "Ignoring #{role_type} reference from #{name} to #{Reference.new(self, role).to.name}"
|
298
299
|
end
|
299
300
|
|
300
301
|
when :unary
|
@@ -304,11 +305,11 @@ module ActiveFacts
|
|
304
305
|
# REVISIT: Or when partitioned
|
305
306
|
raise hell unless role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
|
306
307
|
if role.fact_type.assimilation # assimilation == 'separate' or assimilation == 'partitioned'
|
307
|
-
|
308
|
+
trace :references, "supertype #{name} doesn't absorb a reference to separate subtype #{role.fact_type.subtype.name}"
|
308
309
|
else
|
309
310
|
r = ActiveFacts::Persistence::Reference.new(self, role)
|
310
311
|
r.to.absorbed_via = r
|
311
|
-
|
312
|
+
trace :references, "supertype #{name} absorbs subtype #{r.to.name}"
|
312
313
|
r.tabulate
|
313
314
|
end
|
314
315
|
|
@@ -317,7 +318,7 @@ module ActiveFacts
|
|
317
318
|
ActiveFacts::Persistence::Reference.new(self, role).tabulate
|
318
319
|
# If partitioned, the supertype is absorbed into *each* subtype; a reference to the supertype needs to know which
|
319
320
|
else
|
320
|
-
#
|
321
|
+
# trace :references, "subtype #{name} is absorbed into #{role.fact_type.supertype.name}"
|
321
322
|
end
|
322
323
|
|
323
324
|
when :one_one
|
@@ -340,11 +341,11 @@ module ActiveFacts
|
|
340
341
|
|
341
342
|
# Force the decision if one EntityType identifies another:
|
342
343
|
if preferred_identifier.role_sequence.all_role_ref.detect{|rr| rr.role == r.to_role}
|
343
|
-
|
344
|
+
trace :references, "EntityType #{name} is identified by EntityType #{r.to.name}, so gets absorbed elsewhere"
|
344
345
|
return
|
345
346
|
end
|
346
347
|
if r.to.preferred_identifier.role_sequence.all_role_ref.detect{|rr| rr.role == role}
|
347
|
-
|
348
|
+
trace :references, "EntityType #{name} identifies EntityType #{r.to.name}, so absorbs it"
|
348
349
|
r.to.absorbed_via = r
|
349
350
|
# We can't be absorbed into our supertype!
|
350
351
|
# REVISIT: We might need to flip all one-to-ones as well
|
@@ -362,6 +363,7 @@ module ActiveFacts
|
|
362
363
|
r.tabulate
|
363
364
|
end
|
364
365
|
else
|
366
|
+
# REVISIT: Should we implicitly objectify this fact type here and add a spanning UC?
|
365
367
|
raise "Role #{role.object_type.name} in '#{role.fact_type.default_reading}' lacks a uniqueness constraint"
|
366
368
|
end
|
367
369
|
end
|
@@ -381,9 +383,9 @@ module ActiveFacts
|
|
381
383
|
|
382
384
|
class Vocabulary
|
383
385
|
def populate_all_references #:nodoc:
|
384
|
-
|
386
|
+
trace :references, "Populating all object_type references" do
|
385
387
|
all_object_type.each do |object_type|
|
386
|
-
|
388
|
+
trace :references, "Populating references for #{object_type.name}" do
|
387
389
|
object_type.populate_references
|
388
390
|
end
|
389
391
|
end
|
@@ -392,13 +394,13 @@ module ActiveFacts
|
|
392
394
|
end
|
393
395
|
|
394
396
|
def show_all_references
|
395
|
-
if
|
396
|
-
|
397
|
+
if trace :references
|
398
|
+
trace :references, "Finished object_type references" do
|
397
399
|
all_object_type.each do |object_type|
|
398
400
|
next unless object_type.references_from.size > 0
|
399
|
-
|
401
|
+
trace :references, "#{object_type.name}:" do
|
400
402
|
object_type.references_from.each do |ref|
|
401
|
-
|
403
|
+
trace :references, "#{ref}"
|
402
404
|
end
|
403
405
|
end
|
404
406
|
end
|