activefacts-metamodel 1.9.6 → 1.9.8
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 +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
|