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