activefacts-metamodel 1.9.6 → 1.9.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/activefacts-metamodel.gemspec +1 -1
- data/cql/Metamodel.cql +18 -8
- data/lib/activefacts/metamodel/extensions.rb +507 -473
- data/lib/activefacts/metamodel/metamodel.rb +131 -122
- data/lib/activefacts/metamodel/validate/composition.rb +146 -0
- data/lib/activefacts/metamodel/version.rb +1 -1
- data/lib/activefacts/support.rb +8 -8
- data/orm/Metamodel.cql.diffs +15 -21
- data/orm/Metamodel.orm +3669 -3341
- metadata +4 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7afe6a26853ed01f535f4d7f5790f79ec9be7f5
|
4
|
+
data.tar.gz: 746e2147513f5095bd03e1d16eaa3639ec7b7983
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4d217b455a600eb0899906962bad4f8fff3c0da763526bfd6251df02873fb6582ccdac618d43dc7fb882d8326c9a92dc1f8d29fa70f95ce5d867e6515f01690
|
7
|
+
data.tar.gz: 71068e4a2c568f2f82c671412d37e11bf6eb9c0c35c58c26ec97508e64dc1065869197aae468ffd866f1273ec343bad98045af63f988ca9a4944cc31f4d43511
|
data/Gemfile
CHANGED
@@ -20,7 +20,7 @@ This gem provides the core representations for the Fact Modeling tools of Active
|
|
20
20
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
21
21
|
spec.require_paths = ["lib"]
|
22
22
|
|
23
|
-
spec.add_development_dependency "bundler", ">= 1.10"
|
23
|
+
spec.add_development_dependency "bundler", ">= 1.10"
|
24
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
25
25
|
spec.add_development_dependency "rspec", "~> 3.3"
|
26
26
|
|
data/cql/Metamodel.cql
CHANGED
@@ -382,6 +382,7 @@ Domain Object Type is a kind of Object Type;
|
|
382
382
|
Entity Type is a kind of Domain Object Type;
|
383
383
|
Entity Type objectifies at most one Fact Type,
|
384
384
|
Fact Type is objectified as at most one Entity Type;
|
385
|
+
Entity Type implicitly objectifies at most one implicitly- objectified Fact Type;
|
385
386
|
|
386
387
|
Type Inheritance is a kind of Fact Type identified by Subtype and Supertype where
|
387
388
|
Entity Type (as Subtype) is subtype of super-Entity Type (as Supertype) [acyclic, intransitive],
|
@@ -424,6 +425,8 @@ Composition is called one Name,
|
|
424
425
|
Component is identified by Guid where
|
425
426
|
Component has one Guid,
|
426
427
|
Guid is of at most one Component;
|
428
|
+
Component (as Member) belongs to at most one Component (as Parent) [acyclic, stronglyintransitive],
|
429
|
+
Parent contains Member;
|
427
430
|
Component projects at most one Name;
|
428
431
|
Component has at most one Ordinal rank;
|
429
432
|
|
@@ -437,8 +440,6 @@ Discriminated Role is where
|
|
437
440
|
|
438
441
|
Mapping is a kind of Component;
|
439
442
|
Mapping represents one Object Type;
|
440
|
-
Component (as Member) belongs to at most one Mapping (as Parent),
|
441
|
-
Parent contains Member [acyclic, stronglyintransitive];
|
442
443
|
|
443
444
|
Composite is identified by Mapping where
|
444
445
|
Mapping projects at most one Composite,
|
@@ -446,6 +447,9 @@ Composite is identified by Mapping where
|
|
446
447
|
Composition contains Composite,
|
447
448
|
Composite belongs to one Composition;
|
448
449
|
|
450
|
+
Temporal Mapping is a kind of Mapping;
|
451
|
+
Temporal Mapping records time using one Value Type;
|
452
|
+
|
449
453
|
Absorption is a kind of Mapping;
|
450
454
|
Absorption traverses to one child-Role;
|
451
455
|
Absorption traverses from one parent-Role;
|
@@ -453,8 +457,10 @@ forward-Absorption is matched by at most one reverse-Absorption,
|
|
453
457
|
reverse-Absorption is reverse of at most one forward-Absorption;
|
454
458
|
Absorption flattens;
|
455
459
|
|
460
|
+
/*
|
456
461
|
some Absorption traverses from some parent- Role(1) and that Role(1) is played by some Object Type
|
457
|
-
only if that Absorption is a kind of
|
462
|
+
only if that Absorption is a kind of Component(2) that contains some Component(3) that is a Mapping that represents that Object Type;
|
463
|
+
*/
|
458
464
|
some Absorption traverses to some child- Role(1) and that Role(1) is played by some Object Type
|
459
465
|
only if that Absorption is a kind of Mapping that represents that Object Type;
|
460
466
|
|
@@ -487,6 +493,7 @@ Scoping is a kind of Mapping;
|
|
487
493
|
Injection is a kind of Mapping;
|
488
494
|
Value Field is a kind of Injection;
|
489
495
|
Surrogate Key is a kind of Injection;
|
496
|
+
Valid From is a kind of Injection;
|
490
497
|
|
491
498
|
/* Access Paths */
|
492
499
|
Access Path is identified by Guid where
|
@@ -500,6 +507,9 @@ Access Path is to one Composite,
|
|
500
507
|
Index is a kind of Access Path;
|
501
508
|
Index is unique;
|
502
509
|
Index derives from one Presence Constraint;
|
510
|
+
Composite has at most one natural-Index,
|
511
|
+
Index is natural for at most one Composite;
|
512
|
+
Composite has primary-Index; // Avoid ambiguity; this is a new fact type
|
503
513
|
Composite has at most one primary-Index,
|
504
514
|
Index is primary for at most one Composite;
|
505
515
|
Index is primary for Composite
|
@@ -543,7 +553,7 @@ Absorption is nested under index Role in Ordinal position
|
|
543
553
|
/*
|
544
554
|
* Constraints:
|
545
555
|
*/
|
546
|
-
either Component belongs to
|
556
|
+
either Component(1) belongs to Component(2) or Component(1) is a Mapping that projects some Composite but not both;
|
547
557
|
for each Component exactly one of these holds:
|
548
558
|
Component is a Mapping,
|
549
559
|
Component is an Indicator,
|
@@ -611,10 +621,10 @@ Variable is for Object Type that plays Role
|
|
611
621
|
if and only if
|
612
622
|
Variable is restricted by Role of Step;
|
613
623
|
/*
|
614
|
-
Absorption traverses from parent Role that is played by Object Type
|
615
|
-
only if
|
616
|
-
Absorption traverses to child Role that is played by Object Type
|
617
|
-
only if Mapping
|
624
|
+
some Absorption traverses from some parent Role that is played by some Object Type
|
625
|
+
only if that Absorption contains some Component that is a Mapping(2) that represents that Object Type;
|
626
|
+
some Absorption traverses to some child Role that is played by some Object Type
|
627
|
+
only if that Absorption is a kind of Mapping that represents that Object Type;
|
618
628
|
*/
|
619
629
|
Presence Constraint is preferred identifier
|
620
630
|
only if Presence Constraint has max Frequency;
|
@@ -126,7 +126,7 @@ module ActiveFacts
|
|
126
126
|
rr.role.fact_type
|
127
127
|
end
|
128
128
|
when ActiveFacts::Metamodel::ValueConstraint
|
129
|
-
[ body.
|
129
|
+
[ body.role_as_role_value_constraint ? body.role_as_role_value_constraint.fact_type : nil, body.value_type ] +
|
130
130
|
body.all_allowed_range.map do |ar|
|
131
131
|
[ ar.value_range.minimum_bound, ar.value_range.maximum_bound ].compact.map{|b| b.value.unit}
|
132
132
|
end
|
@@ -163,8 +163,8 @@ module ActiveFacts
|
|
163
163
|
|
164
164
|
class Topic
|
165
165
|
def precursors
|
166
|
-
|
167
|
-
|
166
|
+
# Precursors of a topic are the topics of all precursors of items in this topic
|
167
|
+
all_concept.map{|c| c.precursors }.flatten.uniq.map{|c| c.topic}.uniq-[self]
|
168
168
|
end
|
169
169
|
end
|
170
170
|
|
@@ -225,7 +225,7 @@ module ActiveFacts
|
|
225
225
|
end
|
226
226
|
|
227
227
|
def is_unary
|
228
|
-
|
228
|
+
all_role.size == 1
|
229
229
|
end
|
230
230
|
|
231
231
|
def internal_presence_constraints
|
@@ -265,7 +265,7 @@ module ActiveFacts
|
|
265
265
|
# Mirror Role defines this, but it's more convenient not to have to type-check.
|
266
266
|
# A Role that's not a Mirror Role is its own base role.
|
267
267
|
def base_role
|
268
|
-
|
268
|
+
self
|
269
269
|
end
|
270
270
|
|
271
271
|
def describe(highlight = nil)
|
@@ -273,7 +273,7 @@ module ActiveFacts
|
|
273
273
|
end
|
274
274
|
|
275
275
|
def is_mandatory
|
276
|
-
|
276
|
+
return true if fact_type.is_a?(LinkFactType) # Handle objectification roles
|
277
277
|
all_role_ref.detect{|rr|
|
278
278
|
rs = rr.role_sequence
|
279
279
|
rs.all_role_ref.size == 1 and
|
@@ -290,7 +290,7 @@ module ActiveFacts
|
|
290
290
|
# Return true if this role is functional (has only one instance wrt its player)
|
291
291
|
# A role in an objectified fact type is deemed to refer to the implicit role of the objectification.
|
292
292
|
def is_functional
|
293
|
-
|
293
|
+
return true if fact_type.is_a?(LinkFactType) # Handle objectification roles
|
294
294
|
|
295
295
|
fact_type.entity_type or
|
296
296
|
fact_type.all_role.size != 2 or
|
@@ -306,46 +306,46 @@ module ActiveFacts
|
|
306
306
|
return pc if pc.max_frequency == 1 and !pc.enforcement # Alethic uniqueness constraint
|
307
307
|
end
|
308
308
|
}
|
309
|
-
|
309
|
+
nil
|
310
310
|
end
|
311
311
|
|
312
312
|
def is_identifying
|
313
|
-
|
313
|
+
uc = uniqueness_constraint and uc.is_preferred_identifier
|
314
314
|
end
|
315
315
|
|
316
316
|
# Is there are internal uniqueness constraint on this role only?
|
317
317
|
def is_unique
|
318
|
-
|
319
|
-
|
318
|
+
return true if fact_type.is_a?(LinkFactType) or # Handle objectification roles
|
319
|
+
fact_type.all_role.size == 1 # and unary roles
|
320
320
|
|
321
|
-
|
321
|
+
uniqueness_constraint ? true : false
|
322
322
|
end
|
323
323
|
|
324
324
|
def unique
|
325
|
-
|
325
|
+
raise "REVISIT: unique is deprecated. Call is_unique instead"
|
326
326
|
end
|
327
327
|
|
328
328
|
def name
|
329
329
|
role_name or
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
330
|
+
is_mirror_role && base_role.role_name or
|
331
|
+
fact_type.is_unary && unary_name or
|
332
|
+
String::Words.new(preferred_reference.role_name nil).capwords*' ' or
|
333
|
+
object_type.name
|
334
334
|
end
|
335
335
|
|
336
336
|
def unary_name
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
337
|
+
fact_type.
|
338
|
+
preferred_reading.
|
339
|
+
text.
|
340
|
+
gsub(/(.*)\{[0-9]\}(.*)/) do
|
341
|
+
if $1.empty? or $2.empty?
|
342
|
+
"#{$1} #{$2}"
|
343
|
+
else
|
344
|
+
"#{$1} #{object_type.name} #{$2}"
|
345
|
+
end
|
346
|
+
end.
|
347
|
+
words.
|
348
|
+
titlewords*' '
|
349
349
|
end
|
350
350
|
|
351
351
|
def is_link_role
|
@@ -353,37 +353,37 @@ module ActiveFacts
|
|
353
353
|
end
|
354
354
|
|
355
355
|
def is_mirror_role
|
356
|
-
|
356
|
+
is_a?(MirrorRole)
|
357
357
|
end
|
358
358
|
|
359
359
|
def is_objectification_role
|
360
|
-
|
360
|
+
is_link_role && !is_mirror_role
|
361
361
|
end
|
362
362
|
|
363
363
|
def counterpart
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
364
|
+
case fact_type.all_role.size
|
365
|
+
when 1
|
366
|
+
self
|
367
|
+
when 2
|
368
|
+
(fact_type.all_role.to_a-[self])[0]
|
369
|
+
else
|
370
|
+
nil # raise "counterpart roles are undefined in n-ary fact types"
|
371
|
+
end
|
372
372
|
end
|
373
373
|
|
374
374
|
# return an array of all the constraints on this role (not including ValueConstraint on a ValueType player)
|
375
375
|
def all_constraint
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
376
|
+
(
|
377
|
+
Array(role_value_constraint) +
|
378
|
+
all_role_ref.to_a.flat_map do |rr|
|
379
|
+
rr.role_sequence.all_presence_constraint.to_a +
|
380
|
+
rr.role_sequence.all_subset_constraint_as_superset_role_sequence +
|
381
|
+
rr.role_sequence.all_subset_constraint_as_subset_role_sequence +
|
382
|
+
rr.role_sequence.all_set_comparison_roles.map(&:set_comparison_constraint)
|
383
|
+
end +
|
384
|
+
all_ring_constraint.to_a +
|
385
|
+
all_ring_constraint_as_other_role.to_a
|
386
|
+
).uniq
|
387
387
|
end
|
388
388
|
end
|
389
389
|
|
@@ -412,35 +412,35 @@ module ActiveFacts
|
|
412
412
|
end
|
413
413
|
|
414
414
|
def cql_leading_adjective
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
415
|
+
if leading_adjective
|
416
|
+
# 'foo' => "foo-"
|
417
|
+
# 'foo bar' => "foo- bar "
|
418
|
+
# 'foo-bar' => "foo-- bar "
|
419
|
+
# 'foo-bar baz' => "foo-- bar baz "
|
420
|
+
# 'bat foo-bar baz' => "bat- foo-bar baz "
|
421
|
+
leading_adjective.strip.
|
422
|
+
sub(/[- ]|$/, '-\0 ').sub(/ /, ' ').sub(/[^-]$/, '\0 ').sub(/- $/,'-')
|
423
|
+
else
|
424
|
+
''
|
425
|
+
end
|
426
426
|
end
|
427
427
|
|
428
428
|
def cql_trailing_adjective
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
429
|
+
if trailing_adjective
|
430
|
+
# 'foo' => "-foo"
|
431
|
+
# 'foo bar' => " foo -bar"
|
432
|
+
# 'foo-bar' => " foo --bar"
|
433
|
+
# 'foo-bar baz' => " foo-bar -baz"
|
434
|
+
# 'bat foo-bar baz' => " bat foo-bar -baz"
|
435
|
+
trailing_adjective.
|
436
|
+
strip.
|
437
|
+
sub(/(?<a>.*) (?<b>[^- ]+$)|(?<a>.*)(?<b>-[^- ]*)$|(?<a>)(?<b>.*)/) {
|
438
|
+
" #{$~[:a]} -#{$~[:b]}"
|
439
|
+
}.
|
440
|
+
sub(/^ *-/, '-') # A leading space is not needed if the hyphen is at the start
|
441
|
+
else
|
442
|
+
''
|
443
|
+
end
|
444
444
|
end
|
445
445
|
|
446
446
|
def cql_name
|
@@ -450,9 +450,9 @@ module ActiveFacts
|
|
450
450
|
role.role_name
|
451
451
|
else
|
452
452
|
# Where an adjective has multiple words, the hyphen is inserted outside the outermost space, leaving the space
|
453
|
-
|
453
|
+
cql_leading_adjective +
|
454
454
|
role.object_type.name+
|
455
|
-
|
455
|
+
cql_trailing_adjective
|
456
456
|
end
|
457
457
|
end
|
458
458
|
end
|
@@ -461,8 +461,8 @@ module ActiveFacts
|
|
461
461
|
def describe(highlighted_role_ref = nil)
|
462
462
|
"("+
|
463
463
|
all_role_ref_in_order.map{|rr| rr.role.name + (highlighted_role_ref == rr ? '*' : '') }*", " +
|
464
|
-
|
465
|
-
|
464
|
+
" in " +
|
465
|
+
all_role_ref.map(&:role).map(&:fact_type).uniq.map(&:default_reading).map(&:inspect)*', ' +
|
466
466
|
")"
|
467
467
|
end
|
468
468
|
|
@@ -476,17 +476,21 @@ module ActiveFacts
|
|
476
476
|
attr_reader :injected_surrogate_role
|
477
477
|
|
478
478
|
def is_separate
|
479
|
-
|
479
|
+
is_independent or concept.all_concept_annotation.detect{|ca| ca.mapping_annotation == 'separate'}
|
480
|
+
end
|
481
|
+
|
482
|
+
def is_static
|
483
|
+
concept.all_concept_annotation.detect{|ca| ca.mapping_annotation == 'static'}
|
480
484
|
end
|
481
485
|
|
482
486
|
def all_role_transitive
|
483
|
-
|
487
|
+
supertypes_transitive.flat_map(&:all_role)
|
484
488
|
end
|
485
489
|
end
|
486
490
|
|
487
491
|
class ValueType
|
488
492
|
def all_supertype
|
489
|
-
|
493
|
+
Array(supertype)
|
490
494
|
end
|
491
495
|
|
492
496
|
def supertypes_transitive
|
@@ -527,20 +531,20 @@ module ActiveFacts
|
|
527
531
|
end
|
528
532
|
|
529
533
|
def assimilation
|
530
|
-
|
534
|
+
ti = identifying_type_inheritance and ti.assimilation
|
531
535
|
end
|
532
536
|
|
533
537
|
def is_separate
|
534
|
-
|
538
|
+
super || !['absorbed', nil].include?(assimilation)
|
535
539
|
end
|
536
540
|
|
537
541
|
def preferred_identifier
|
538
542
|
return @preferred_identifier if @preferred_identifier
|
539
543
|
if fact_type
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
+
# Objectified unaries are identified by the ID of the object that plays the role:
|
545
|
+
if fact_type.all_role.size == 1
|
546
|
+
return @preferred_identifier = fact_type.all_role.single.object_type.preferred_identifier
|
547
|
+
end
|
544
548
|
|
545
549
|
# When compiling a fact instance, the delayed creation of a preferred identifier might be necessary
|
546
550
|
if c = fact_type.check_and_add_spanning_uniqueness_constraint
|
@@ -601,7 +605,7 @@ module ActiveFacts
|
|
601
605
|
ftroles = Array(role.fact_type.all_role)
|
602
606
|
|
603
607
|
# Skip roles in ternary and higher fact types, they're objectified
|
604
|
-
|
608
|
+
# REVISIT: This next line prevents a unary being used as a preferred_identifier:
|
605
609
|
next if ftroles.size != 2
|
606
610
|
|
607
611
|
trace :pi, "Considering role in #{role.fact_type.describe(role)}"
|
@@ -696,11 +700,11 @@ module ActiveFacts
|
|
696
700
|
end
|
697
701
|
|
698
702
|
def preferred_identifier_roles
|
699
|
-
|
703
|
+
preferred_identifier.role_sequence.all_role_ref_in_order.map(&:role)
|
700
704
|
end
|
701
705
|
|
702
706
|
def rank_in_preferred_identifier(role)
|
703
|
-
|
707
|
+
preferred_identifier_roles.index(role)
|
704
708
|
end
|
705
709
|
|
706
710
|
# An array of all direct subtypes:
|
@@ -727,7 +731,7 @@ module ActiveFacts
|
|
727
731
|
end
|
728
732
|
|
729
733
|
def all_supertype
|
730
|
-
|
734
|
+
supertypes
|
731
735
|
end
|
732
736
|
|
733
737
|
# An array of all direct subtypes
|
@@ -744,13 +748,13 @@ module ActiveFacts
|
|
744
748
|
|
745
749
|
def identifying_type_inheritance
|
746
750
|
all_type_inheritance_as_subtype.detect do |ti|
|
747
|
-
|
748
|
-
|
751
|
+
ti.provides_identification
|
752
|
+
end
|
749
753
|
end
|
750
754
|
|
751
755
|
# A subtype does not have a identifying_supertype if it defines its own identifier
|
752
756
|
def identifying_supertype
|
753
|
-
|
757
|
+
ti = identifying_type_inheritance and ti.supertype
|
754
758
|
end
|
755
759
|
|
756
760
|
def common_supertype(other)
|
@@ -761,54 +765,54 @@ module ActiveFacts
|
|
761
765
|
end
|
762
766
|
|
763
767
|
def add_supertype(supertype, is_identifying_supertype, assimilation)
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
768
|
+
inheritance_fact = constellation.TypeInheritance(self, supertype, :concept => :new)
|
769
|
+
|
770
|
+
inheritance_fact.assimilation = assimilation
|
771
|
+
|
772
|
+
# Create a reading:
|
773
|
+
sub_role = constellation.Role(inheritance_fact, 0, :object_type => self, :concept => :new)
|
774
|
+
super_role = constellation.Role(inheritance_fact, 1, :object_type => supertype, :concept => :new)
|
775
|
+
|
776
|
+
rs = constellation.RoleSequence(:new)
|
777
|
+
constellation.RoleRef(rs, 0, :role => sub_role)
|
778
|
+
constellation.RoleRef(rs, 1, :role => super_role)
|
779
|
+
constellation.Reading(inheritance_fact, 0, :role_sequence => rs, :text => "{0} is a kind of {1}", :is_negative => false)
|
780
|
+
|
781
|
+
rs2 = constellation.RoleSequence(:new)
|
782
|
+
constellation.RoleRef(rs2, 0, :role => super_role)
|
783
|
+
constellation.RoleRef(rs2, 1, :role => sub_role)
|
784
|
+
# Decide in which order to include is a/is an. Provide both, but in order.
|
785
|
+
n = 'aeioh'.include?(sub_role.object_type.name.downcase[0]) ? 'n' : ''
|
786
|
+
constellation.Reading(inheritance_fact, 2, :role_sequence => rs2, :text => "{0} is a#{n} {1}", :is_negative => false)
|
787
|
+
|
788
|
+
if is_identifying_supertype
|
789
|
+
inheritance_fact.provides_identification = true
|
790
|
+
end
|
791
|
+
|
792
|
+
# Create uniqueness constraints over the subtyping fact type.
|
793
|
+
p1rs = constellation.RoleSequence(:new)
|
794
|
+
constellation.RoleRef(p1rs, 0).role = sub_role
|
795
|
+
pc1 = constellation.PresenceConstraint(:new, :vocabulary => vocabulary)
|
796
|
+
pc1.name = "#{name}MustHaveSupertype#{supertype.name}"
|
797
|
+
pc1.role_sequence = p1rs
|
798
|
+
pc1.is_mandatory = true # A subtype instance must have a supertype instance
|
799
|
+
pc1.min_frequency = 1
|
800
|
+
pc1.max_frequency = 1
|
801
|
+
pc1.is_preferred_identifier = false
|
802
|
+
trace :constraint, "Made new subtype PC GUID=#{pc1.concept.guid} min=1 max=1 over #{p1rs.describe}"
|
803
|
+
|
804
|
+
p2rs = constellation.RoleSequence(:new)
|
805
|
+
constellation.RoleRef(p2rs, 0).role = super_role
|
806
|
+
pc2 = constellation.PresenceConstraint(:new, :vocabulary => vocabulary)
|
807
|
+
pc2.name = "#{supertype.name}MayBeA#{name}"
|
808
|
+
pc2.role_sequence = p2rs
|
809
|
+
pc2.is_mandatory = false
|
810
|
+
pc2.min_frequency = 0
|
811
|
+
pc2.max_frequency = 1
|
812
|
+
# The supertype role often identifies the subtype:
|
813
|
+
pc2.is_preferred_identifier = inheritance_fact.provides_identification
|
814
|
+
trace :supertype, "identification of #{name} via supertype #{supertype.name} was #{inheritance_fact.provides_identification ? '' : 'not '}added"
|
815
|
+
trace :constraint, "Made new supertype PC GUID=#{pc2.concept.guid} min=1 max=1 over #{p2rs.describe}"
|
812
816
|
end
|
813
817
|
|
814
818
|
# This entity type has just objectified a fact type.
|
@@ -818,7 +822,13 @@ module ActiveFacts
|
|
818
822
|
next if role.mirror_role_as_base_role # Already exists
|
819
823
|
link_fact_type = @constellation.LinkFactType(:new, :implying_role => role)
|
820
824
|
objectification_role = @constellation.Role(link_fact_type, 0, :object_type => self, :concept => :new)
|
821
|
-
mirror_role = @constellation.MirrorRole(
|
825
|
+
mirror_role = @constellation.MirrorRole(
|
826
|
+
link_fact_type, 1,
|
827
|
+
:concept => :new,
|
828
|
+
:object_type => role.object_type,
|
829
|
+
:base_role => role,
|
830
|
+
:role_name => role.role_name
|
831
|
+
)
|
822
832
|
|
823
833
|
link_fact_type.concept.implication_rule =
|
824
834
|
objectification_role.concept.implication_rule =
|
@@ -973,7 +983,7 @@ module ActiveFacts
|
|
973
983
|
end
|
974
984
|
|
975
985
|
def all_constrained_role
|
976
|
-
|
986
|
+
Array(role_as_role_value_constraint) # Empty unless it's a role value constraint
|
977
987
|
end
|
978
988
|
end
|
979
989
|
|
@@ -1047,11 +1057,11 @@ module ActiveFacts
|
|
1047
1057
|
end
|
1048
1058
|
|
1049
1059
|
def covers_role role
|
1050
|
-
|
1060
|
+
role_sequence.all_role_ref.map(&:role).include?(role)
|
1051
1061
|
end
|
1052
1062
|
|
1053
1063
|
def all_constrained_role
|
1054
|
-
|
1064
|
+
role_sequence.all_role_ref.map(&:role)
|
1055
1065
|
end
|
1056
1066
|
end
|
1057
1067
|
|
@@ -1065,8 +1075,8 @@ module ActiveFacts
|
|
1065
1075
|
end
|
1066
1076
|
|
1067
1077
|
def all_constrained_role
|
1068
|
-
|
1069
|
-
|
1078
|
+
subset_role_sequence.all_role_ref.map(&:role) +
|
1079
|
+
superset_role_sequence.all_role_ref.map(&:role)
|
1070
1080
|
end
|
1071
1081
|
end
|
1072
1082
|
|
@@ -1074,8 +1084,8 @@ module ActiveFacts
|
|
1074
1084
|
def describe
|
1075
1085
|
"("+
|
1076
1086
|
role_sequence.all_role_ref_in_order.map{|rr| rr.role.name }*", " +
|
1077
|
-
|
1078
|
-
|
1087
|
+
" in " +
|
1088
|
+
role_sequence.all_role_ref.map(&:role).map(&:fact_type).uniq.map(&:default_reading).map(&:inspect)*', ' +
|
1079
1089
|
")"
|
1080
1090
|
end
|
1081
1091
|
end
|
@@ -1084,30 +1094,30 @@ module ActiveFacts
|
|
1084
1094
|
def describe
|
1085
1095
|
self.class.basename+'(' +
|
1086
1096
|
all_set_comparison_roles.map do |scr|
|
1087
|
-
|
1097
|
+
'['+
|
1088
1098
|
scr.role_sequence.all_role_ref.map{|rr|
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1099
|
+
rr.role.fact_type.describe(rr.role)
|
1100
|
+
}*',' +
|
1101
|
+
']'
|
1092
1102
|
end*',' +
|
1093
1103
|
')'
|
1094
1104
|
end
|
1095
1105
|
|
1096
1106
|
def all_constrained_role
|
1097
|
-
|
1107
|
+
all_set_comparison_roles.map(&:role_sequence).flat_map(&:all_role_ref).map(&:role).uniq
|
1098
1108
|
end
|
1099
1109
|
end
|
1100
1110
|
|
1101
1111
|
class SetEqualityConstraint
|
1102
1112
|
def describe
|
1103
|
-
|
1113
|
+
all_set_comparison_roles.map(&:describe) * " if and only if "
|
1104
1114
|
end
|
1105
1115
|
end
|
1106
1116
|
|
1107
1117
|
class SetExclusionConstraint
|
1108
1118
|
def describe
|
1109
|
-
|
1110
|
-
|
1119
|
+
(is_mandatory ? "exactly one of " : "at most one of ") +
|
1120
|
+
all_set_comparison_roles.map(&:describe) * " or "
|
1111
1121
|
end
|
1112
1122
|
end
|
1113
1123
|
|
@@ -1123,7 +1133,7 @@ module ActiveFacts
|
|
1123
1133
|
end
|
1124
1134
|
|
1125
1135
|
def all_constrained_role
|
1126
|
-
|
1136
|
+
[role, other_role]
|
1127
1137
|
end
|
1128
1138
|
end
|
1129
1139
|
|
@@ -1263,45 +1273,51 @@ module ActiveFacts
|
|
1263
1273
|
|
1264
1274
|
class LinkFactType
|
1265
1275
|
def all_reading
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1276
|
+
if super.size == 0
|
1277
|
+
# No user-defined readings have been defined, so it's time to stop being lazy:
|
1278
|
+
objectification_role, mirror_role = *all_role_in_order
|
1279
|
+
|
1280
|
+
preferred_role_ref = mirror_role.base_role.preferred_reference
|
1281
|
+
|
1282
|
+
rs = constellation.RoleSequence(:new)
|
1283
|
+
rr0 = constellation.RoleRef(rs, 0, :role => objectification_role)
|
1284
|
+
rr1 = constellation.RoleRef(rs, 1, :role => mirror_role)
|
1285
|
+
|
1286
|
+
rr1.leading_adjective = preferred_role_ref.leading_adjective
|
1287
|
+
rr1.trailing_adjective = preferred_role_ref.trailing_adjective
|
1288
|
+
|
1289
|
+
r0 = constellation.Reading(self, 0, :role_sequence => rs, :text => "{0} involves {1}", :is_negative => false) # REVISIT: This assumes English!
|
1290
|
+
r1 = constellation.Reading(self, 1, :role_sequence => rs, :text => "{1} is involved in {0}", :is_negative => false)
|
1291
|
+
end
|
1292
|
+
@all_reading
|
1277
1293
|
end
|
1278
1294
|
end
|
1279
1295
|
|
1280
1296
|
class MirrorRole
|
1281
1297
|
def is_mandatory
|
1282
|
-
|
1298
|
+
base_role.is_mandatory
|
1283
1299
|
end
|
1284
1300
|
|
1285
1301
|
def is_unique
|
1286
|
-
|
1302
|
+
base_role.is_unique
|
1287
1303
|
end
|
1288
1304
|
|
1289
1305
|
def is_functional
|
1290
|
-
|
1306
|
+
base_role.is_functional
|
1291
1307
|
end
|
1292
1308
|
|
1293
1309
|
def uniqueness_constraint
|
1294
|
-
|
1310
|
+
raise "A MirrorRole should not be asked for its uniqueness constraints"
|
1295
1311
|
end
|
1296
1312
|
|
1297
1313
|
%w{all_ring_constraint_as_other_role all_ring_constraint all_role_value role_value_constraint
|
1298
1314
|
}.each do |accessor|
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1315
|
+
define_method(accessor.to_sym) do
|
1316
|
+
base_role.send(accessor.to_sym)
|
1317
|
+
end
|
1318
|
+
define_method("#{accessor}=".to_sym) do |*a|
|
1319
|
+
raise "REVISIT: It's a bad idea to try to set #{accessor} for a MirrorRole"
|
1320
|
+
end
|
1305
1321
|
end
|
1306
1322
|
end
|
1307
1323
|
|
@@ -1351,7 +1367,8 @@ module ActiveFacts
|
|
1351
1367
|
if fact_type.entity_type
|
1352
1368
|
objectification_role_supertypes =
|
1353
1369
|
fact_type.entity_type.supertypes_transitive+object_type.supertypes_transitive
|
1354
|
-
|
1370
|
+
# Find the objectification role here:
|
1371
|
+
return nil unless role.link_fact_type # REVISIT: Link Fact Types are missing; happens from half-baked surrogate transform
|
1355
1372
|
objectification_role = role.link_fact_type.all_role.detect{|r| !r.is_a?(MirrorRole)}
|
1356
1373
|
else
|
1357
1374
|
objectification_role_supertypes = counterpart_role_supertypes
|
@@ -1504,64 +1521,64 @@ module ActiveFacts
|
|
1504
1521
|
|
1505
1522
|
class Composition
|
1506
1523
|
def all_composite_by_name
|
1507
|
-
|
1524
|
+
all_composite.keys.sort_by do |key|
|
1508
1525
|
@constellation.Composite[key].mapping.name
|
1509
1526
|
end.map do |key|
|
1510
1527
|
composite = @constellation.Composite[key]
|
1511
|
-
|
1512
|
-
|
1513
|
-
|
1528
|
+
yield composite if block_given?
|
1529
|
+
composite
|
1530
|
+
end
|
1514
1531
|
end
|
1515
1532
|
end
|
1516
1533
|
|
1517
1534
|
class Composite
|
1518
1535
|
def inspect
|
1519
|
-
|
1536
|
+
"Composite #{mapping.inspect}"
|
1520
1537
|
end
|
1521
1538
|
|
1522
1539
|
def all_index
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1540
|
+
all_access_path.
|
1541
|
+
select{|ap| ap.is_a?(Index)}.
|
1542
|
+
sort_by{|ap| [ap.composite_as_primary_index ? 0 : 1] + Array(ap.name)+ap.all_index_field.map(&:inspect) } # REVISIT: Fix hack for stable ordering
|
1526
1543
|
end
|
1527
1544
|
|
1528
1545
|
def show_trace
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1546
|
+
trace :composition, inspect do
|
1547
|
+
trace :composition?, "Columns" do
|
1548
|
+
mapping.show_trace
|
1549
|
+
end
|
1550
|
+
|
1551
|
+
indices = all_index
|
1552
|
+
unless indices.empty?
|
1553
|
+
trace :composition, "Indices" do
|
1554
|
+
indices.each do |ap|
|
1555
|
+
ap.show_trace
|
1556
|
+
end
|
1557
|
+
end
|
1558
|
+
end
|
1559
|
+
|
1560
|
+
inbound = all_access_path.
|
1561
|
+
select{|ap| ap.is_a?(ForeignKey)}.
|
1562
|
+
sort_by{|fk| [fk.source_composite.mapping.name, fk.absorption.inspect]+fk.all_foreign_key_field.map(&:inspect)+fk.all_index_field.map(&:inspect) }
|
1563
|
+
unless inbound.empty?
|
1564
|
+
trace :composition, "Foreign keys inbound" do
|
1565
|
+
inbound.each do |fk|
|
1566
|
+
fk.show_trace
|
1567
|
+
end
|
1568
|
+
end
|
1569
|
+
end
|
1570
|
+
|
1571
|
+
outbound =
|
1572
|
+
all_foreign_key_as_source_composite.
|
1573
|
+
sort_by{|fk| [fk.source_composite.mapping.name, fk.absorption.inspect]+fk.all_index_field.map(&:inspect)+fk.all_foreign_key_field.map(&:inspect) }
|
1574
|
+
unless outbound.empty?
|
1575
|
+
trace :composition, "Foreign keys outbound" do
|
1576
|
+
outbound.each do |fk|
|
1577
|
+
fk.show_trace
|
1578
|
+
end
|
1579
|
+
end
|
1580
|
+
end
|
1581
|
+
end
|
1565
1582
|
end
|
1566
1583
|
|
1567
1584
|
# Provide a stable ordering for indices, based on the ordering of columns by rank:
|
@@ -1570,82 +1587,88 @@ module ActiveFacts
|
|
1570
1587
|
reject{|ap| ap.is_a?(ActiveFacts::Metamodel::ForeignKey)}.
|
1571
1588
|
sort_by{|ap| ap.all_index_field.to_a.flat_map{|ixf| ixf.component.rank_path}.compact }
|
1572
1589
|
end
|
1590
|
+
|
1591
|
+
def all_foreign_key_as_target_composite
|
1592
|
+
all_access_path.
|
1593
|
+
select{|ap| ap.is_a?(ForeignKey)}.
|
1594
|
+
sort_by{|ap| ap.all_foreign_key_field.map(&:inspect) }
|
1595
|
+
end
|
1573
1596
|
end
|
1574
1597
|
|
1575
1598
|
class AccessPath
|
1576
1599
|
def show_trace
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1583
|
-
|
1584
|
-
|
1585
|
-
|
1586
|
-
|
1587
|
-
|
1588
|
-
|
1589
|
-
|
1600
|
+
trace :composition, inspect do
|
1601
|
+
if is_a?(ForeignKey)
|
1602
|
+
# First list any fields in a foreign key
|
1603
|
+
all_foreign_key_field.sort_by(&:ordinal).each do |fkf|
|
1604
|
+
$stderr.puts "Internal error: Foreign key field to #{fkf.component.column_name} is in #{fkf.component.root.mapping.name} not #{source_composite.mapping.name}!" if fkf.component.root != source_composite
|
1605
|
+
trace :composition, fkf.inspect
|
1606
|
+
end
|
1607
|
+
end
|
1608
|
+
# Now list the fields in the primary key
|
1609
|
+
all_index_field.sort_by(&:ordinal).each do |ak|
|
1610
|
+
trace :composition, ak.inspect
|
1611
|
+
end
|
1612
|
+
end
|
1590
1613
|
end
|
1591
1614
|
|
1592
1615
|
def position_in_index component
|
1593
|
-
|
1616
|
+
all_index_field.sort_by(&:ordinal).map(&:component).index(component)
|
1594
1617
|
end
|
1595
1618
|
end
|
1596
1619
|
|
1597
1620
|
class Index
|
1598
1621
|
def inspect
|
1599
|
-
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
|
1604
|
-
|
1605
|
-
|
1606
|
-
|
1607
|
-
|
1608
|
-
|
1609
|
-
|
1622
|
+
case
|
1623
|
+
when !is_unique
|
1624
|
+
'Non-unique index'
|
1625
|
+
when composite_as_primary_index
|
1626
|
+
'Primary index'
|
1627
|
+
else
|
1628
|
+
'Unique index'
|
1629
|
+
end +
|
1630
|
+
(name ? " #{name.inspect}" : '') +
|
1631
|
+
" to #{composite.mapping.name}" +
|
1632
|
+
(presence_constraint ? " over #{presence_constraint.describe}" : '')
|
1610
1633
|
end
|
1611
1634
|
end
|
1612
1635
|
|
1613
1636
|
class ForeignKey
|
1614
1637
|
def inspect
|
1615
|
-
|
1616
|
-
|
1617
|
-
|
1618
|
-
|
1638
|
+
"Foreign Key" +
|
1639
|
+
(name ? " #{name.inspect}" : '') +
|
1640
|
+
" from #{(sc = source_composite) ? sc.mapping.name : 'NO-SOURCE'} to #{composite.mapping.name}" +
|
1641
|
+
(absorption ? " over #{absorption.inspect}" : '')
|
1619
1642
|
end
|
1620
1643
|
end
|
1621
1644
|
|
1622
1645
|
class IndexField
|
1623
1646
|
def inspect
|
1624
|
-
|
1625
|
-
|
1647
|
+
"IndexField part #{ordinal} in #{component.root.mapping.name} references #{component.inspect}" +
|
1648
|
+
(value ? " discriminated by #{value.inspect}" : '')
|
1626
1649
|
end
|
1627
1650
|
end
|
1628
1651
|
|
1629
1652
|
class ForeignKeyField
|
1630
1653
|
def inspect
|
1631
|
-
|
1632
|
-
|
1633
|
-
|
1654
|
+
operation = value ? "filters by value #{value} of" : "is"
|
1655
|
+
"ForeignKeyField part #{ordinal} in #{component.root.mapping.name} #{operation} #{component.inspect}" +
|
1656
|
+
(value ? " discriminated by #{value.inspect}" : '')
|
1634
1657
|
end
|
1635
1658
|
end
|
1636
1659
|
|
1637
1660
|
class Mapping
|
1638
1661
|
def inspect
|
1639
|
-
|
1662
|
+
"#{self.class.basename} (#{rank_kind})#{parent ? " in #{parent.name}" :''} of #{name && name != '' ? name : '<anonymous>'}"
|
1640
1663
|
end
|
1641
1664
|
|
1642
1665
|
def show_trace
|
1643
|
-
|
1644
|
-
|
1645
|
-
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1666
|
+
trace :composition, "#{ordinal ? "#{ordinal}: " : ''}#{inspect}" do
|
1667
|
+
yield if block_given?
|
1668
|
+
all_member.sort_by{|member| [member.ordinal, member.name]}.each do |member|
|
1669
|
+
member.show_trace
|
1670
|
+
end
|
1671
|
+
end
|
1649
1672
|
end
|
1650
1673
|
|
1651
1674
|
# Recompute a contiguous member ranking fron zero, based on current membership:
|
@@ -1653,7 +1676,7 @@ module ActiveFacts
|
|
1653
1676
|
all_member.each(&:uncache_rank_key)
|
1654
1677
|
next_rank = 0
|
1655
1678
|
all_member.
|
1656
|
-
|
1679
|
+
sort_by(&:rank_key).
|
1657
1680
|
each do |member|
|
1658
1681
|
member.ordinal = next_rank
|
1659
1682
|
next_rank += 1
|
@@ -1661,14 +1684,14 @@ module ActiveFacts
|
|
1661
1684
|
end
|
1662
1685
|
|
1663
1686
|
def root
|
1664
|
-
|
1687
|
+
composite || parent && parent.root
|
1665
1688
|
end
|
1666
1689
|
|
1667
|
-
def
|
1690
|
+
def all_leaf
|
1668
1691
|
re_rank
|
1669
1692
|
all_member.sort_by(&:ordinal).flat_map do |member|
|
1670
1693
|
if member.is_a?(Mapping) && member.all_member.size > 0
|
1671
|
-
member.
|
1694
|
+
member.all_leaf
|
1672
1695
|
else
|
1673
1696
|
member
|
1674
1697
|
end
|
@@ -1676,192 +1699,201 @@ module ActiveFacts
|
|
1676
1699
|
end
|
1677
1700
|
|
1678
1701
|
def is_mandatory
|
1679
|
-
|
1702
|
+
true
|
1680
1703
|
end
|
1681
1704
|
|
1682
1705
|
def path_mandatory
|
1683
|
-
|
1706
|
+
true
|
1684
1707
|
end
|
1685
1708
|
end
|
1686
1709
|
|
1687
1710
|
class Nesting
|
1688
1711
|
def show_trace
|
1689
|
-
|
1690
|
-
|
1691
|
-
|
1712
|
+
# The index role has a counterpart played by the parent object in the enclosing Absorption
|
1713
|
+
reading = index_role.fact_type.default_reading
|
1714
|
+
trace :composition, "#{ordinal}: Nesting under #{index_role.object_type.name}#{key_name ? " (as #{key_name.inspect})" : ''} in #{reading.inspect}}"
|
1692
1715
|
end
|
1693
1716
|
end
|
1694
1717
|
|
1695
1718
|
class Absorption
|
1696
1719
|
def inspect_reading
|
1697
|
-
|
1720
|
+
parent_role.fact_type.reading_preferably_starting_with_role(parent_role).expand.inspect
|
1698
1721
|
end
|
1699
1722
|
|
1700
1723
|
def inspect
|
1701
|
-
|
1702
|
-
|
1703
|
-
|
1704
|
-
|
1705
|
-
|
1706
|
-
|
1707
|
-
|
1708
|
-
|
1709
|
-
|
1710
|
-
|
1724
|
+
"#{super}#{full_absorption ? ' (full)' : ''
|
1725
|
+
} in #{inspect_reading}#{
|
1726
|
+
# If we have a related forward absorption, we're by definition a reverse absorption
|
1727
|
+
if forward_absorption
|
1728
|
+
' (reverse)'
|
1729
|
+
else
|
1730
|
+
# If we have a related reverse absorption, we're by definition a forward absorption
|
1731
|
+
reverse_absorption ? ' (forward)' : ''
|
1732
|
+
end
|
1733
|
+
}"
|
1711
1734
|
end
|
1712
1735
|
|
1713
1736
|
def show_trace
|
1714
|
-
|
1715
|
-
|
1716
|
-
|
1717
|
-
|
1718
|
-
|
1719
|
-
|
1720
|
-
|
1737
|
+
super() do
|
1738
|
+
if nesting_mode || all_nesting.size > 0
|
1739
|
+
trace :composition, "Nested using #{nesting_mode || 'unspecified'} mode" do
|
1740
|
+
all_nesting.sort_by(&:ordinal).each(&:show_trace)
|
1741
|
+
end
|
1742
|
+
end
|
1743
|
+
end
|
1721
1744
|
end
|
1722
1745
|
|
1723
1746
|
def is_type_inheritance
|
1724
|
-
|
1747
|
+
child_role.fact_type.is_a?(TypeInheritance) && child_role.fact_type
|
1725
1748
|
end
|
1726
1749
|
|
1727
1750
|
def is_supertype_absorption
|
1728
|
-
|
1751
|
+
is_type_inheritance && child_role.fact_type.supertype == object_type
|
1729
1752
|
end
|
1730
1753
|
|
1731
1754
|
def is_subtype_absorption
|
1732
|
-
|
1755
|
+
is_type_inheritance && parent_role.fact_type.supertype == object_type
|
1733
1756
|
end
|
1734
1757
|
|
1735
1758
|
def is_preferred_direction
|
1736
|
-
|
1737
|
-
|
1738
|
-
|
1739
|
-
|
1740
|
-
|
1741
|
-
|
1742
|
-
|
1743
|
-
|
1744
|
-
|
1745
|
-
|
1746
|
-
|
1747
|
-
|
1748
|
-
|
1749
|
-
|
1750
|
-
|
1751
|
-
|
1752
|
-
|
1753
|
-
|
1754
|
-
|
1755
|
-
|
1756
|
-
|
1757
|
-
|
1758
|
-
|
1759
|
-
|
1760
|
-
|
1761
|
-
|
1762
|
-
|
1763
|
-
|
1764
|
-
|
1765
|
-
|
1766
|
-
|
1767
|
-
|
1768
|
-
|
1769
|
-
|
1770
|
-
|
1771
|
-
|
1772
|
-
|
1773
|
-
|
1774
|
-
|
1775
|
-
|
1776
|
-
|
1777
|
-
|
1778
|
-
|
1779
|
-
|
1780
|
-
|
1781
|
-
|
1782
|
-
|
1783
|
-
|
1784
|
-
|
1759
|
+
return child_role.is_mirror_role if child_role.is_mirror_role != parent_role.is_mirror_role
|
1760
|
+
|
1761
|
+
# Prefer to absorb the one into the many:
|
1762
|
+
p_un = parent_role.is_unique
|
1763
|
+
c_un = child_role.is_unique
|
1764
|
+
return p_un if p_un != c_un
|
1765
|
+
|
1766
|
+
# Prefer to absorb a subtype into the supertype (opposite if separate or partitioned)
|
1767
|
+
if (ti = child_role.fact_type).is_a?(TypeInheritance)
|
1768
|
+
is_subtype = child_role == ti.subtype_role # Supertype absorbing subtype
|
1769
|
+
subtype = ti.subtype_role.object_type # Subtype doesn't want to be absorbed?
|
1770
|
+
# REVISIT: We need fewer ways to say this:
|
1771
|
+
child_separate = ["separate", "partitioned"].include?(ti.assimilation) ||
|
1772
|
+
subtype.is_independent ||
|
1773
|
+
subtype.concept.all_concept_annotation.detect{|ca| ca.mapping_annotation == 'separate'}
|
1774
|
+
return !is_subtype != !child_separate
|
1775
|
+
end
|
1776
|
+
|
1777
|
+
if p_un && c_un
|
1778
|
+
# Prefer to absorb a ValueType into an EntityType rather than the other way around:
|
1779
|
+
pvt = parent_role.object_type.is_a?(ActiveFacts::Metamodel::ValueType)
|
1780
|
+
cvt = child_role.object_type.is_a?(ActiveFacts::Metamodel::ValueType)
|
1781
|
+
return cvt if pvt != cvt
|
1782
|
+
|
1783
|
+
if !pvt
|
1784
|
+
# Force the decision if one EntityType identifies another
|
1785
|
+
return true if child_role.base_role.is_identifying # Parent is identified by child role, correct
|
1786
|
+
return false if parent_role.base_role.is_identifying # Child is identified by parent role, incorrect
|
1787
|
+
end
|
1788
|
+
|
1789
|
+
# Primary absorption absorbs the object playing the mandatory role into the non-mandatory:
|
1790
|
+
return child_role.is_mandatory if !parent_role.is_mandatory != !child_role.is_mandatory
|
1791
|
+
end
|
1792
|
+
|
1793
|
+
if parent_role.object_type.is_a?(ActiveFacts::Metamodel::EntityType) &&
|
1794
|
+
child_role.object_type.is_a?(ActiveFacts::Metamodel::EntityType)
|
1795
|
+
# Prefer to absorb an identifying element into the EntityType it identifies
|
1796
|
+
return true if parent_role.object_type.preferred_identifier.
|
1797
|
+
role_sequence.all_role_ref.map(&:role).detect{|r|
|
1798
|
+
r.object_type == child_role.object_type
|
1799
|
+
}
|
1800
|
+
return false if child_role.object_type.preferred_identifier.
|
1801
|
+
role_sequence.all_role_ref.map(&:role).detect{|r|
|
1802
|
+
r.object_type == parent_role.object_type
|
1803
|
+
}
|
1804
|
+
end
|
1805
|
+
|
1806
|
+
# For stability, absorb a later-named role into an earlier-named one:
|
1807
|
+
return parent_role.name < child_role.name
|
1785
1808
|
end
|
1786
1809
|
|
1787
1810
|
def flip!
|
1788
|
-
|
1789
|
-
|
1790
|
-
|
1791
|
-
|
1792
|
-
|
1793
|
-
|
1794
|
-
|
1795
|
-
|
1796
|
-
|
1797
|
-
|
1798
|
-
|
1799
|
-
|
1811
|
+
raise "REVISIT: Need to flip FullAbsorption on #{inspect}" if full_absorption or reverse_absorption && reverse_absorption.full_absorption or forward_absorption && forward_absorption.full_absorption
|
1812
|
+
if (other = forward_absorption)
|
1813
|
+
# We point at them - make them point at us instead
|
1814
|
+
self.forward_absorption = nil
|
1815
|
+
self.reverse_absorption = other
|
1816
|
+
elsif (other = reverse_absorption)
|
1817
|
+
# They point at us - make us point at them instead
|
1818
|
+
self.reverse_absorption = nil
|
1819
|
+
self.forward_absorption = other
|
1820
|
+
else
|
1821
|
+
raise "Absorption cannot be flipped as it has no reverse"
|
1822
|
+
end
|
1800
1823
|
end
|
1801
1824
|
|
1802
1825
|
def all_role
|
1803
|
-
|
1826
|
+
([child_role, parent_role] + all_nesting.map(&:index_role)).flat_map{|role| [role, role.base_role]}.uniq
|
1804
1827
|
end
|
1805
1828
|
|
1806
1829
|
def value_constraints
|
1807
|
-
|
1808
|
-
|
1830
|
+
return [] unless object_type.is_a?(ValueType)
|
1831
|
+
object_type.supertypes_transitive.flat_map{|vt| Array(vt.value_constraint)}
|
1809
1832
|
end
|
1810
1833
|
|
1811
1834
|
def is_mandatory
|
1812
|
-
|
1835
|
+
parent_role.is_mandatory
|
1813
1836
|
end
|
1814
1837
|
|
1815
1838
|
def path_mandatory
|
1816
|
-
|
1839
|
+
is_mandatory && parent.path_mandatory
|
1817
1840
|
end
|
1818
1841
|
end
|
1819
1842
|
|
1820
1843
|
class FullAbsorption
|
1821
1844
|
def inspect
|
1822
|
-
|
1845
|
+
"Full #{absorption.inspect}"
|
1823
1846
|
end
|
1824
1847
|
end
|
1825
1848
|
|
1826
1849
|
class Indicator
|
1827
1850
|
def inspect
|
1828
|
-
|
1851
|
+
"#{self.class.basename} #{role.fact_type.default_reading.inspect}"
|
1829
1852
|
end
|
1830
1853
|
|
1831
1854
|
def show_trace
|
1832
|
-
|
1855
|
+
trace :composition, "#{ordinal ? "#{ordinal}: " : ''}#{inspect} #{name ? "(as #{name.inspect})" : ''}"
|
1833
1856
|
end
|
1834
1857
|
|
1835
1858
|
def all_role
|
1836
|
-
|
1859
|
+
[role, role.base_role].uniq
|
1837
1860
|
end
|
1838
1861
|
|
1839
1862
|
def is_mandatory
|
1840
|
-
|
1863
|
+
false
|
1841
1864
|
end
|
1842
1865
|
end
|
1843
1866
|
|
1844
1867
|
class Discriminator
|
1845
1868
|
def inspect
|
1846
|
-
|
1869
|
+
"#{self.class.basename} between #{all_discriminated_role.map{|dr|dr.fact_type.default_reading.inspect}*', '}"
|
1847
1870
|
end
|
1848
1871
|
|
1849
1872
|
def show_trace
|
1850
|
-
|
1873
|
+
trace :composition, "#{ordinal ? "#{ordinal}: " : ''}#{inspect} #{name ? " (as #{name.inspect})" : ''}"
|
1851
1874
|
end
|
1852
1875
|
|
1853
1876
|
def all_role
|
1854
|
-
|
1877
|
+
all_discriminated_role.map(&:role).flat_map{|role| [role, role.base_role]}.uniq
|
1878
|
+
end
|
1879
|
+
end
|
1880
|
+
|
1881
|
+
class SurrogateKey
|
1882
|
+
def is_identifying
|
1883
|
+
if pk = root.primary_index
|
1884
|
+
return pk.all_index_field.detect{|ixf| ixf.component == self}
|
1885
|
+
end
|
1886
|
+
!parent.parent
|
1855
1887
|
end
|
1856
1888
|
end
|
1857
1889
|
|
1858
1890
|
class ValueField
|
1859
1891
|
def inspect
|
1860
|
-
|
1892
|
+
"#{self.class.basename} #{object_type.name.inspect}"
|
1861
1893
|
end
|
1862
1894
|
|
1863
1895
|
def show_trace
|
1864
|
-
|
1896
|
+
trace :composition, "#{ordinal}: #{inspect}#{name ? " (as #{name.inspect})" : ''}"
|
1865
1897
|
end
|
1866
1898
|
end
|
1867
1899
|
|
@@ -1869,128 +1901,130 @@ module ActiveFacts
|
|
1869
1901
|
# The ranking key of a component indicates its importance to its parent:
|
1870
1902
|
# Ranking assigns a total order, but is computed in groups:
|
1871
1903
|
RANK_SURROGATE = 0
|
1872
|
-
RANK_SUPER = 1
|
1873
|
-
RANK_IDENT = 2
|
1874
|
-
RANK_VALUE = 3
|
1875
|
-
RANK_INJECTION = 4
|
1876
|
-
RANK_DISCRIMINATOR = 5
|
1877
|
-
RANK_FOREIGN = 6
|
1878
|
-
RANK_INDICATOR = 7
|
1879
|
-
RANK_MANDATORY = 8
|
1880
|
-
RANK_NON_MANDATORY = 9
|
1881
|
-
RANK_MULTIPLE = 10
|
1882
|
-
RANK_SUBTYPE = 11
|
1883
|
-
RANK_SCOPING = 12
|
1904
|
+
RANK_SUPER = 1 # Supertypes, with the identifying supertype first, others alphabetical
|
1905
|
+
RANK_IDENT = 2 # Identifying components (absorptions, indicator), in order of the identifier
|
1906
|
+
RANK_VALUE = 3 # A ValueField
|
1907
|
+
RANK_INJECTION = 4 # Injections, in alphabetical order
|
1908
|
+
RANK_DISCRIMINATOR = 5 # Discriminator components, in alphabetical order
|
1909
|
+
RANK_FOREIGN = 6 # REVISIT: Foreign key components
|
1910
|
+
RANK_INDICATOR = 7 # Indicators in alphabetical order
|
1911
|
+
RANK_MANDATORY = 8 # Absorption: unique mandatory
|
1912
|
+
RANK_NON_MANDATORY = 9 # Absorption: unique optional
|
1913
|
+
RANK_MULTIPLE = 10 # Absorption: manifold
|
1914
|
+
RANK_SUBTYPE = 11 # Subtypes in alphabetical order
|
1915
|
+
RANK_SCOPING = 12 # Scoping in alphabetical order
|
1884
1916
|
|
1885
1917
|
def uncache_rank_key
|
1886
|
-
|
1918
|
+
@rank_key = nil
|
1887
1919
|
end
|
1888
1920
|
|
1889
1921
|
def rank_key
|
1890
|
-
|
1891
|
-
|
1892
|
-
|
1893
|
-
|
1894
|
-
|
1895
|
-
|
1896
|
-
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
1900
|
-
|
1901
|
-
|
1902
|
-
|
1903
|
-
|
1904
|
-
|
1905
|
-
|
1906
|
-
|
1907
|
-
|
1908
|
-
|
1909
|
-
|
1910
|
-
|
1911
|
-
|
1912
|
-
|
1913
|
-
|
1914
|
-
|
1915
|
-
|
1916
|
-
|
1917
|
-
|
1918
|
-
|
1919
|
-
|
1920
|
-
|
1921
|
-
|
1922
|
-
|
1923
|
-
|
1924
|
-
|
1925
|
-
|
1926
|
-
|
1927
|
-
|
1928
|
-
|
1929
|
-
|
1930
|
-
|
1931
|
-
|
1932
|
-
|
1933
|
-
|
1934
|
-
|
1935
|
-
|
1936
|
-
|
1937
|
-
|
1938
|
-
|
1939
|
-
|
1940
|
-
|
1941
|
-
|
1942
|
-
|
1922
|
+
@rank_key ||=
|
1923
|
+
case self
|
1924
|
+
when SurrogateKey
|
1925
|
+
if is_identifying
|
1926
|
+
[RANK_SURROGATE] # an injected PK
|
1927
|
+
else
|
1928
|
+
[RANK_MANDATORY, name] # an FK
|
1929
|
+
end
|
1930
|
+
|
1931
|
+
when Indicator
|
1932
|
+
if (p = parent_entity_type) and
|
1933
|
+
p.preferred_identifier and
|
1934
|
+
(position = p.rank_in_preferred_identifier(role.base_role))
|
1935
|
+
[RANK_IDENT, position] # An identifying unary
|
1936
|
+
else
|
1937
|
+
[RANK_INDICATOR, name || role.name] # A non-identifying unary
|
1938
|
+
end
|
1939
|
+
|
1940
|
+
when Discriminator
|
1941
|
+
[RANK_DISCRIMINATOR, name || child_role.name]
|
1942
|
+
|
1943
|
+
when ValueField
|
1944
|
+
[RANK_IDENT]
|
1945
|
+
|
1946
|
+
when Injection
|
1947
|
+
[RANK_INJECTION, name] # REVISIT: Injection not fully elaborated. A different sub-key for ranking may be needed
|
1948
|
+
|
1949
|
+
when Absorption
|
1950
|
+
if is_type_inheritance
|
1951
|
+
# We are traversing a type inheritance fact type. Is this object_type the subtype or supertype?
|
1952
|
+
if is_supertype_absorption
|
1953
|
+
# What's the rank of this supertype?
|
1954
|
+
tis = parent_role.object_type.all_type_inheritance_as_subtype.sort_by{|ti| ti.provides_identification ? '' : ti.supertype.name }
|
1955
|
+
[RANK_SUPER, child_role.fact_type.provides_identification ? 0 : 1+tis.index(parent_role.fact_type)]
|
1956
|
+
else
|
1957
|
+
# What's the rank of this subtype?
|
1958
|
+
tis = parent_role.object_type.all_type_inheritance_as_supertype.sort_by{|ti| ti.subtype.name }
|
1959
|
+
[RANK_SUBTYPE, tis.index(parent_role.fact_type)]
|
1960
|
+
end
|
1961
|
+
elsif (p = parent_entity_type) and (position = p.rank_in_preferred_identifier(child_role.base_role))
|
1962
|
+
[RANK_IDENT, position]
|
1963
|
+
else
|
1964
|
+
if parent_role.is_unique
|
1965
|
+
[parent_role.is_mandatory ? RANK_MANDATORY : RANK_NON_MANDATORY, name || child_role.name]
|
1966
|
+
else
|
1967
|
+
[RANK_MULTIPLE, name || child_role.name, parent_role.name]
|
1968
|
+
end
|
1969
|
+
end
|
1970
|
+
|
1971
|
+
when Scoping
|
1972
|
+
[RANK_SCOPING, name || object_type.name]
|
1973
|
+
|
1974
|
+
else
|
1975
|
+
raise "unexpected #{self.class.basename} in Component#rank_key"
|
1976
|
+
end
|
1943
1977
|
end
|
1944
1978
|
|
1945
1979
|
def primary_index_components
|
1946
|
-
|
1947
|
-
|
1948
|
-
|
1949
|
-
|
1950
|
-
|
1951
|
-
|
1980
|
+
root and
|
1981
|
+
ix = root.primary_index and # Primary index has been decided
|
1982
|
+
root.primary_index.all_index_field.size > 0 and # has been populated and
|
1983
|
+
ix = root.primary_index and
|
1984
|
+
ixfs = ix.all_index_field.sort_by(&:ordinal) and
|
1985
|
+
ixfs.map(&:component)
|
1952
1986
|
end
|
1953
1987
|
|
1954
1988
|
def parent_entity_type
|
1955
|
-
|
1956
|
-
|
1957
|
-
|
1989
|
+
parent &&
|
1990
|
+
parent.object_type.is_a?(EntityType) &&
|
1991
|
+
parent.object_type
|
1958
1992
|
end
|
1959
1993
|
|
1960
1994
|
def rank_kind
|
1961
|
-
|
1962
|
-
|
1963
|
-
|
1964
|
-
|
1965
|
-
|
1966
|
-
|
1967
|
-
|
1968
|
-
|
1969
|
-
|
1970
|
-
|
1971
|
-
|
1972
|
-
|
1973
|
-
|
1974
|
-
|
1975
|
-
|
1976
|
-
|
1995
|
+
return "top" unless parent # E.g. a Mapping that is a Composite
|
1996
|
+
case rank_key[0]
|
1997
|
+
when RANK_SURROGATE; "surrogate"
|
1998
|
+
when RANK_SUPER; "supertype"
|
1999
|
+
when RANK_IDENT; "existential"
|
2000
|
+
when RANK_VALUE; "self-value"
|
2001
|
+
when RANK_INJECTION; "injection"
|
2002
|
+
when RANK_DISCRIMINATOR;"discriminator"
|
2003
|
+
when RANK_FOREIGN; "foreignkey"
|
2004
|
+
when RANK_INDICATOR; "indicator"
|
2005
|
+
when RANK_MANDATORY; "mandatory"
|
2006
|
+
when RANK_NON_MANDATORY;"optional"
|
2007
|
+
when RANK_MULTIPLE; "multiple"
|
2008
|
+
when RANK_SUBTYPE; "subtype"
|
2009
|
+
when RANK_SCOPING; "scoping"
|
2010
|
+
end
|
1977
2011
|
end
|
1978
2012
|
|
1979
2013
|
def root
|
1980
|
-
|
2014
|
+
parent.root
|
1981
2015
|
end
|
1982
2016
|
|
1983
2017
|
def depth
|
1984
|
-
|
2018
|
+
parent ? 1+parent.depth : 0
|
1985
2019
|
end
|
1986
2020
|
|
1987
2021
|
def inspect
|
1988
|
-
|
2022
|
+
"#{self.class.basename}"
|
1989
2023
|
end
|
1990
2024
|
|
1991
2025
|
def show_trace
|
1992
|
-
|
1993
|
-
|
2026
|
+
raise "Implemented in subclasses"
|
2027
|
+
# trace :composition, "#{ordinal ? "#{ordinal}: " : ''}#{inspect}#{name ? " (as #{name.inspect})" : ''}"
|
1994
2028
|
end
|
1995
2029
|
|
1996
2030
|
def leaves
|
@@ -2002,15 +2036,15 @@ module ActiveFacts
|
|
2002
2036
|
end
|
2003
2037
|
|
2004
2038
|
def is_mandatory
|
2005
|
-
|
2039
|
+
parent.is_mandatory
|
2006
2040
|
end
|
2007
2041
|
|
2008
2042
|
def path_mandatory
|
2009
|
-
|
2043
|
+
parent.path_mandatory
|
2010
2044
|
end
|
2011
2045
|
|
2012
2046
|
def all_role
|
2013
|
-
|
2047
|
+
[]
|
2014
2048
|
end
|
2015
2049
|
|
2016
2050
|
def rank_path
|