activefacts-metamodel 1.9.0 → 1.9.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -3
- data/Rakefile +7 -3
- data/activefacts-metamodel.gemspec +2 -2
- data/cql/Metamodel.cql +114 -58
- data/images/Access Paths.png +0 -0
- data/images/Compositions.png +0 -0
- data/lib/activefacts/metamodel/extensions.rb +220 -92
- data/lib/activefacts/metamodel/metamodel.rb +48 -3
- data/lib/activefacts/metamodel/version.rb +1 -1
- data/orm/Metamodel.cql.diffs +70 -0
- data/orm/Metamodel.orm +7410 -4986
- metadata +10 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 239e2d9123f73a9176d115db67c86c68ed9a6567
|
4
|
+
data.tar.gz: 0fee1c5e78e43da17e3c72732efa7de2ac0d7c94
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1cf4a18f8354ea5d3f68535c8dc0150da9c82434da62d589a0968e131fcae45108a83ee3dfcfd3e2de05065c45f7045738e597f6a75762ecf859e9f3b977b37b
|
7
|
+
data.tar.gz: b7a96af8283b16f3253451b0db7630a13aa51a5a813e24ba97333baa6801a4296198ed5b085a655588335edb26e7a31c63a701880588936e5bc27fd14af2bfa1
|
data/Gemfile
CHANGED
@@ -2,8 +2,7 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
gemspec
|
4
4
|
|
5
|
-
if ENV['PWD'] =~ %r{\A#{ENV['HOME']}/work}
|
5
|
+
if ENV['PWD'] =~ %r{\A#{ENV['HOME']}/work}i
|
6
6
|
$stderr.puts "Using work area gems for #{File.basename(File.dirname(__FILE__))} from activefacts-metamodel"
|
7
|
-
gem 'activefacts-api', path: '
|
8
|
-
# gem 'activefacts-api', git: 'git://github.com/cjheath/activefacts-api.git'
|
7
|
+
gem 'activefacts-api', path: '../api'
|
9
8
|
end
|
data/Rakefile
CHANGED
@@ -23,13 +23,17 @@ task :bump do
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
desc "Generate new CQL from the ORM file"
|
26
|
+
desc "Generate new CQL from the ORM file (to verify version similarity)"
|
27
27
|
task :cql do
|
28
|
+
# Note: Import from ORM is broken on the implicit derivations in constraints, and the result is unstable.
|
29
|
+
# This means that SOME PATCHES WILL FAIL
|
28
30
|
system "afgen --cql orm/Metamodel.orm > Metamodel.cql"
|
29
|
-
system "
|
31
|
+
system "patch < orm/Metamodel.cql.diffs"
|
32
|
+
system "afgen --cql cql/Metamodel.cql 2>/dev/null >Metamodel.cql.cql"
|
33
|
+
system "diff -b -C 1 Metamodel.cql Metamodel.cql.cql | tee Metamodel.cql.diffs"
|
30
34
|
end
|
31
35
|
|
32
|
-
desc "Generate new Ruby from the
|
36
|
+
desc "Generate new Ruby from the CQL file"
|
33
37
|
task :ruby do
|
34
38
|
system %q{
|
35
39
|
afgen --ruby cql/Metamodel.cql 2>/dev/null |
|
@@ -24,6 +24,6 @@ This gem provides the core representations for the Fact Modeling tools of Active
|
|
24
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
25
25
|
spec.add_development_dependency "rspec", "~> 3.3"
|
26
26
|
|
27
|
-
spec.add_runtime_dependency "activefacts-api", ">= 1.
|
28
|
-
spec.add_development_dependency "activefacts", "~> 1.8", "~> 1
|
27
|
+
spec.add_runtime_dependency "activefacts-api", ">= 1.8", "~> 1"
|
28
|
+
spec.add_development_dependency "activefacts", "~> 1.8", "~> 1"
|
29
29
|
end
|
data/cql/Metamodel.cql
CHANGED
@@ -54,18 +54,6 @@ Coefficient is identified by Numerator and Denominator and Coefficient is precis
|
|
54
54
|
Coefficient has one Denominator,
|
55
55
|
Coefficient is precise;
|
56
56
|
|
57
|
-
Component is identified by Guid where
|
58
|
-
Component has one Guid,
|
59
|
-
Guid is of at most one Component;
|
60
|
-
Component projects at most one Name;
|
61
|
-
Component has at most one Ordinal rank;
|
62
|
-
|
63
|
-
Composition is identified by Guid where
|
64
|
-
Composition has one Guid,
|
65
|
-
Guid is of at most one Composition;
|
66
|
-
Composition is called one Name,
|
67
|
-
Name is of at most one Composition;
|
68
|
-
|
69
57
|
Concept is identified by Guid where
|
70
58
|
Concept has one Guid,
|
71
59
|
Guid is of at most one Concept;
|
@@ -88,8 +76,6 @@ Context Note has one Discussion,
|
|
88
76
|
Context Note applies to at most one relevant-Concept,
|
89
77
|
Concept has Context Note;
|
90
78
|
|
91
|
-
Discriminator is a kind of Component;
|
92
|
-
|
93
79
|
Enforcement is identified by Constraint where
|
94
80
|
Constraint requires at most one Enforcement,
|
95
81
|
Enforcement applies to one Constraint;
|
@@ -107,8 +93,6 @@ Fact is of one Fact Type;
|
|
107
93
|
Implication Rule is identified by its Name;
|
108
94
|
Concept is implied by at most one Implication Rule;
|
109
95
|
|
110
|
-
Indicator is a kind of Component;
|
111
|
-
|
112
96
|
Instance is identified by Concept where
|
113
97
|
Instance is an instance of one Concept;
|
114
98
|
Instance objectifies at most one Fact,
|
@@ -120,10 +104,6 @@ Location is identified by X and Y where
|
|
120
104
|
Location is at one X,
|
121
105
|
Location is at one Y;
|
122
106
|
|
123
|
-
Mapping is a kind of Component;
|
124
|
-
Mapping (as Parent) contains Component (as Member) [acyclic, stronglyintransitive],
|
125
|
-
Member belongs to at most one Parent;
|
126
|
-
|
127
107
|
Presence Constraint is a kind of Constraint;
|
128
108
|
Presence Constraint has at most one max-Frequency restricted to {1..};
|
129
109
|
Presence Constraint has at most one min-Frequency restricted to {2..};
|
@@ -149,7 +129,7 @@ Role is identified by Fact Type and Ordinal where
|
|
149
129
|
Fact Type contains Role,
|
150
130
|
Role fills one Ordinal,
|
151
131
|
Ordinal applies to Role;
|
152
|
-
|
132
|
+
|
153
133
|
Link Fact Type has one implying-Role,
|
154
134
|
implying-Role implies at most one Link Fact Type;
|
155
135
|
Ring Constraint has at most one other-Role,
|
@@ -176,8 +156,6 @@ Role Value is identified by Fact and Role where
|
|
176
156
|
Instance plays Role Value,
|
177
157
|
Role Value is of one Instance;
|
178
158
|
|
179
|
-
Scoping is a kind of Mapping;
|
180
|
-
|
181
159
|
Set Constraint is a kind of Constraint;
|
182
160
|
|
183
161
|
Shape is identified by Guid where
|
@@ -253,12 +231,6 @@ Vocabulary contains Constraint,
|
|
253
231
|
Vocabulary includes Unit,
|
254
232
|
Unit is in one Vocabulary;
|
255
233
|
|
256
|
-
Absorption is a kind of Mapping;
|
257
|
-
Absorption traverses to one child-Role;
|
258
|
-
Absorption traverses from one parent-Role;
|
259
|
-
Absorption is matched by at most one reverse-Absorption;
|
260
|
-
Absorption flattens;
|
261
|
-
|
262
234
|
Aggregation is where
|
263
235
|
Variable is bound to Aggregate over aggregated-Variable,
|
264
236
|
Aggregate of aggregated-Variable is bound as one Variable;
|
@@ -274,12 +246,6 @@ Bound is identified by Value and Bound is inclusive where
|
|
274
246
|
Value is of at least one Bound,
|
275
247
|
Bound is inclusive;
|
276
248
|
|
277
|
-
Composite is identified by Mapping where
|
278
|
-
Mapping projects at most one Composite,
|
279
|
-
Composite consists of one Mapping;
|
280
|
-
Composition contains Composite,
|
281
|
-
Composite belongs to one Composition;
|
282
|
-
|
283
249
|
Constraint Shape is a kind of Shape;
|
284
250
|
Constraint Shape is for one Constraint;
|
285
251
|
|
@@ -302,19 +268,12 @@ Diagram is identified by Vocabulary and Name where
|
|
302
268
|
Diagram is called one Name,
|
303
269
|
Name is of Diagram;
|
304
270
|
|
305
|
-
Discriminated Role is where
|
306
|
-
Discriminator distinguishes Role using one Value,
|
307
|
-
Role is indicated by Value for Discriminator;
|
308
|
-
|
309
271
|
Fact Type Shape is a kind of Shape;
|
310
272
|
Fact Type Shape has at most one Display Role Names Setting;
|
311
273
|
Fact Type Shape is for one Fact Type,
|
312
274
|
Fact Type has Fact Type Shape;
|
313
275
|
Fact Type Shape has at most one Rotation Setting;
|
314
276
|
|
315
|
-
Injection is a kind of Mapping;
|
316
|
-
ValueField is a kind of Injection;
|
317
|
-
|
318
277
|
Mirror Role is a kind of Role;
|
319
278
|
Mirror Role is for at most one Role (as Base Role),
|
320
279
|
Base Role implies at most one Mirror Role;
|
@@ -322,16 +281,6 @@ Mirror Role is for at most one Role (as Base Role),
|
|
322
281
|
Model Note Shape is a kind of Shape;
|
323
282
|
Model Note Shape is for one Context Note;
|
324
283
|
|
325
|
-
Nesting is where
|
326
|
-
Absorption is nested under index-Role in Ordinal position,
|
327
|
-
Absorption in Ordinal position is nested under one Role,
|
328
|
-
Role keys nesting of Absorption at Ordinal priority;
|
329
|
-
Absorption uses at most one Nesting Mode;
|
330
|
-
Nesting has at most one key-Name;
|
331
|
-
Absorption is nested under index Role in Ordinal position
|
332
|
-
if and only if
|
333
|
-
that Absorption uses some Nesting Mode;
|
334
|
-
|
335
284
|
ORM Diagram is a kind of Diagram;
|
336
285
|
Shape is in one ORM Diagram,
|
337
286
|
ORM Diagram includes Shape;
|
@@ -340,7 +289,6 @@ Object Type is identified by Vocabulary and Name where
|
|
340
289
|
Object Type belongs to one Vocabulary,
|
341
290
|
Object Type is called one Name;
|
342
291
|
Instance is of one Object Type;
|
343
|
-
Mapping represents one Object Type;
|
344
292
|
Object Type is an instance of one Concept;
|
345
293
|
Object Type uses at most one Pronoun;
|
346
294
|
Object Type plays Role,
|
@@ -457,15 +405,125 @@ Value Type is subtype of at most one super-Value Type (as Supertype) [acyclic, t
|
|
457
405
|
Supertype is supertype of Value Type;
|
458
406
|
|
459
407
|
Value Type Parameter is where
|
460
|
-
Value Type has
|
461
|
-
Name
|
462
|
-
Value Type Parameter requires value of one
|
408
|
+
Value Type has parameter called Name,
|
409
|
+
Name identifies parameter of Value Type;
|
410
|
+
Value Type Parameter requires value of one parameter-Value Type;
|
463
411
|
|
464
412
|
Value Type Parameter Restriction is where
|
465
413
|
Value Type receives Value Type Parameter,
|
466
414
|
Value Type Parameter applies to Value Type;
|
467
415
|
Value Type Parameter Restriction has one Value;
|
468
416
|
|
417
|
+
/* Compositions */
|
418
|
+
Composition is identified by Guid where
|
419
|
+
Composition has one Guid,
|
420
|
+
Guid is of at most one Composition;
|
421
|
+
Composition is called one Name,
|
422
|
+
Name is of at most one Composition;
|
423
|
+
|
424
|
+
Component is identified by Guid where
|
425
|
+
Component has one Guid,
|
426
|
+
Guid is of at most one Component;
|
427
|
+
Component projects at most one Name;
|
428
|
+
Component has at most one Ordinal rank;
|
429
|
+
|
430
|
+
Indicator is a kind of Component;
|
431
|
+
Indicator indicates one Role played;
|
432
|
+
|
433
|
+
Discriminator is a kind of Component;
|
434
|
+
Discriminated Role is where
|
435
|
+
Discriminator distinguishes Role using one Value,
|
436
|
+
Role is indicated by Value for Discriminator;
|
437
|
+
|
438
|
+
Mapping is a kind of Component;
|
439
|
+
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
|
+
Composite is identified by Mapping where
|
444
|
+
Mapping projects at most one Composite,
|
445
|
+
Composite consists of one Mapping;
|
446
|
+
Composition contains Composite,
|
447
|
+
Composite belongs to one Composition;
|
448
|
+
|
449
|
+
Scoping is a kind of Mapping;
|
450
|
+
Injection is a kind of Mapping;
|
451
|
+
Value Field is a kind of Injection;
|
452
|
+
Surrogate Key is a kind of Injection;
|
453
|
+
|
454
|
+
Absorption is a kind of Mapping;
|
455
|
+
Absorption traverses to one child-Role;
|
456
|
+
Absorption traverses from one parent-Role;
|
457
|
+
forward-Absorption is matched by at most one reverse-Absorption,
|
458
|
+
reverse-Absorption is reverse of at most one forward-Absorption;
|
459
|
+
Absorption flattens;
|
460
|
+
|
461
|
+
some Absorption traverses from some parent- Role(1) and that Role(1) is played by some Object Type
|
462
|
+
only if that Absorption is a kind of Mapping(1) that contains some Component that is a Mapping(2) that represents that Object Type;
|
463
|
+
some Absorption traverses to some child- Role(1) and that Role(1) is played by some Object Type
|
464
|
+
only if that Absorption is a kind of Mapping that represents that Object Type;
|
465
|
+
|
466
|
+
Full Absorption is where
|
467
|
+
Composition fully absorbs Object Type;
|
468
|
+
Full Absorption applies to one Absorption,
|
469
|
+
Absorption creates at most one Full Absorption;
|
470
|
+
/*
|
471
|
+
some Object Type is involved in some Full Absorption that applies to some Absorption that is a kind of Mapping
|
472
|
+
if and only if
|
473
|
+
that Mapping represents that Object Type;
|
474
|
+
*/
|
475
|
+
|
476
|
+
/* Access Paths */
|
477
|
+
Access Path is identified by Guid where
|
478
|
+
Access Path has one Guid,
|
479
|
+
Guid is of at most one Access Path;
|
480
|
+
Access Path is called at most one Name;
|
481
|
+
Access Path is to one Composite,
|
482
|
+
Composite is reached through Access Path;
|
483
|
+
|
484
|
+
/* Indices */
|
485
|
+
Index is a kind of Access Path;
|
486
|
+
Index is unique;
|
487
|
+
Index derives from one Presence Constraint;
|
488
|
+
Composite has at most one primary-Index,
|
489
|
+
Index is primary for at most one Composite;
|
490
|
+
Index is primary for Composite
|
491
|
+
only if Index is unique;
|
492
|
+
identified Composite has primary Access Path
|
493
|
+
only if Composite is reached through Access Path;
|
494
|
+
|
495
|
+
Index Field is where
|
496
|
+
Access Path for Ordinal field uses one Component,
|
497
|
+
Component provides Ordinal field for Access Path;
|
498
|
+
Index Field is discriminated by at most one Value;
|
499
|
+
each Access Path occurs at least one time in
|
500
|
+
Access Path for Ordinal field uses Component;
|
501
|
+
|
502
|
+
/* Foreign keys */
|
503
|
+
Foreign Key is a kind of Access Path;
|
504
|
+
either Access Path is an Index or Access Path is a Foreign Key but not both;
|
505
|
+
Foreign Key traverses from one source-Composite,
|
506
|
+
Composite contains Foreign Key;
|
507
|
+
Foreign Key derives from one Absorption;
|
508
|
+
|
509
|
+
Foreign Key Field is where
|
510
|
+
Foreign Key for Ordinal field uses one Component,
|
511
|
+
Component provides Ordinal field for Foreign Key;
|
512
|
+
each Foreign Key occurs at least one time in
|
513
|
+
Foreign Key for Ordinal field uses Component;
|
514
|
+
Foreign Key Field is discriminated by at most one Value;
|
515
|
+
|
516
|
+
/* Nesting is a non-first-normal form used for SQL ARRAY and Map (hstore) fields */
|
517
|
+
Nesting is where
|
518
|
+
Absorption is nested under index-Role in Ordinal position,
|
519
|
+
Absorption in Ordinal position is nested under one Role,
|
520
|
+
Role keys nesting of Absorption at Ordinal priority;
|
521
|
+
Absorption uses at most one Nesting Mode;
|
522
|
+
Nesting has at most one key-Name;
|
523
|
+
Absorption is nested under index Role in Ordinal position
|
524
|
+
if and only if
|
525
|
+
that Absorption uses some Nesting Mode;
|
526
|
+
|
469
527
|
/*
|
470
528
|
* Constraints:
|
471
529
|
*/
|
@@ -548,8 +606,6 @@ Value Type has Scale
|
|
548
606
|
only if Value Type has Length;
|
549
607
|
Variable matches nesting over Step
|
550
608
|
only if Variable is for Object Type and Step specifies Fact Type;
|
551
|
-
each Absorption(1) occurs at most one time in
|
552
|
-
Absorption(2) is matched by reverse Absorption(1);
|
553
609
|
either Agreement was reached by Agent or Agreement was on Date;
|
554
610
|
// either Component projects Name or Component is some Absorption that flattens;
|
555
611
|
each Concept occurs at most one time in
|
Binary file
|
data/images/Compositions.png
CHANGED
Binary file
|
@@ -303,14 +303,20 @@ module ActiveFacts
|
|
303
303
|
rs = rr.role_sequence
|
304
304
|
rs.all_role_ref.size == 1 and
|
305
305
|
rs.all_presence_constraint.detect do |pc|
|
306
|
-
pc.max_frequency == 1 and !pc.enforcement # Alethic uniqueness constraint
|
306
|
+
return pc if pc.max_frequency == 1 and !pc.enforcement # Alethic uniqueness constraint
|
307
307
|
end
|
308
308
|
}
|
309
|
+
nil
|
310
|
+
end
|
311
|
+
|
312
|
+
def is_identifying
|
313
|
+
uc = uniqueness_constraint and uc.is_preferred_identifier
|
309
314
|
end
|
310
315
|
|
311
316
|
# Is there are internal uniqueness constraint on this role only?
|
312
317
|
def is_unique
|
313
|
-
return true if fact_type.is_a?(LinkFactType) # Handle objectification roles
|
318
|
+
return true if fact_type.is_a?(LinkFactType) or # Handle objectification roles
|
319
|
+
fact_type.all_role.size == 1 # and unary roles
|
314
320
|
|
315
321
|
uniqueness_constraint ? true : false
|
316
322
|
end
|
@@ -350,58 +356,8 @@ module ActiveFacts
|
|
350
356
|
when 2
|
351
357
|
(fact_type.all_role.to_a-[self])[0]
|
352
358
|
else
|
353
|
-
raise "counterpart roles are undefined in n-ary fact types"
|
354
|
-
end
|
355
|
-
end
|
356
|
-
|
357
|
-
def role_type
|
358
|
-
raise "Incorrectly implemented"
|
359
|
-
if fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
|
360
|
-
return object_type == fact_type.supertype ? :supertype : :subtype
|
359
|
+
nil # raise "counterpart roles are undefined in n-ary fact types"
|
361
360
|
end
|
362
|
-
|
363
|
-
if fact_type.is_a?(ActiveFacts::Metamodel::LinkFactType)
|
364
|
-
# Prevent an unnecessary from-1 search:
|
365
|
-
from_1 = true
|
366
|
-
# Change the to_1 search to detect a one-to-one:
|
367
|
-
return fact_type.implying_role.role_type
|
368
|
-
elsif fact_type.all_role.size == 1
|
369
|
-
return :unary
|
370
|
-
end
|
371
|
-
|
372
|
-
# List the UCs on this fact type:
|
373
|
-
all_uniqueness_constraints =
|
374
|
-
fact_type.all_role.map do |fact_role|
|
375
|
-
fact_role.all_role_ref.map do |rr|
|
376
|
-
rr.role_sequence.all_presence_constraint.select do |pc|
|
377
|
-
pc.max_frequency == 1
|
378
|
-
end
|
379
|
-
end
|
380
|
-
end.flatten.uniq
|
381
|
-
|
382
|
-
# It's to-1 if a UC exists over exactly this role:
|
383
|
-
to_1 =
|
384
|
-
all_uniqueness_constraints.
|
385
|
-
detect do |c|
|
386
|
-
(rr = c.role_sequence.all_role_ref.single) and
|
387
|
-
rr.role == role
|
388
|
-
end
|
389
|
-
|
390
|
-
if from_1 || fact_type.entity_type
|
391
|
-
# This is a role in an objectified fact type
|
392
|
-
from_1 = true
|
393
|
-
else
|
394
|
-
# It's from-1 if a UC exists over roles of this FT that doesn't cover this role:
|
395
|
-
from_1 = all_uniqueness_constraints.detect{|uc|
|
396
|
-
!uc.role_sequence.all_role_ref.detect{|rr| rr.role == role || rr.role.fact_type != fact_type}
|
397
|
-
}
|
398
|
-
end
|
399
|
-
|
400
|
-
if from_1
|
401
|
-
return to_1 ? :one_one : :one_many
|
402
|
-
else
|
403
|
-
return to_1 ? :many_one : :many_many
|
404
|
-
end
|
405
361
|
end
|
406
362
|
end
|
407
363
|
|
@@ -494,6 +450,10 @@ module ActiveFacts
|
|
494
450
|
def is_separate
|
495
451
|
is_independent or concept.all_concept_annotation.detect{|ca| ca.mapping_annotation == 'separate'}
|
496
452
|
end
|
453
|
+
|
454
|
+
def all_role_transitive
|
455
|
+
supertypes_transitive.flat_map(&:all_role)
|
456
|
+
end
|
497
457
|
end
|
498
458
|
|
499
459
|
class ValueType
|
@@ -501,9 +461,10 @@ module ActiveFacts
|
|
501
461
|
[self] + (supertype ? supertype.supertypes_transitive : [])
|
502
462
|
end
|
503
463
|
|
504
|
-
def
|
464
|
+
def all_subtype
|
505
465
|
all_value_type_as_supertype
|
506
466
|
end
|
467
|
+
alias_method :subtypes, :all_subtype # REVISIT: Delete legacy name
|
507
468
|
|
508
469
|
def subtypes_transitive
|
509
470
|
[self] + subtypes.map{|st| st.subtypes_transitive}.flatten
|
@@ -516,14 +477,14 @@ module ActiveFacts
|
|
516
477
|
nil
|
517
478
|
end
|
518
479
|
|
519
|
-
# Is this ValueType auto-assigned either
|
480
|
+
# Is this ValueType auto-assigned? Returns either 'assert', 'commit', otherwise nil.
|
520
481
|
def is_auto_assigned
|
521
482
|
type = self
|
522
483
|
while type
|
523
|
-
return
|
484
|
+
return type.transaction_phase || 'commit' if type.name =~ /^Auto/ || type.transaction_phase
|
524
485
|
type = type.supertype
|
525
486
|
end
|
526
|
-
|
487
|
+
nil
|
527
488
|
end
|
528
489
|
end
|
529
490
|
|
@@ -534,11 +495,7 @@ module ActiveFacts
|
|
534
495
|
end
|
535
496
|
|
536
497
|
def assimilation
|
537
|
-
|
538
|
-
rr.role.fact_type.assimilation
|
539
|
-
else
|
540
|
-
nil
|
541
|
-
end
|
498
|
+
ti = identifying_type_inheritance and ti.assimilation
|
542
499
|
end
|
543
500
|
|
544
501
|
def is_separate
|
@@ -548,6 +505,11 @@ module ActiveFacts
|
|
548
505
|
def preferred_identifier
|
549
506
|
return @preferred_identifier if @preferred_identifier
|
550
507
|
if fact_type
|
508
|
+
# Objectified unaries are identified by the ID of the object that plays the role:
|
509
|
+
if fact_type.all_role.size == 1
|
510
|
+
return @preferred_identifier = fact_type.all_role.single.object_type.preferred_identifier
|
511
|
+
end
|
512
|
+
|
551
513
|
# When compiling a fact instance, the delayed creation of a preferred identifier might be necessary
|
552
514
|
if c = fact_type.check_and_add_spanning_uniqueness_constraint
|
553
515
|
fact_type.check_and_add_spanning_uniqueness_constraint = nil
|
@@ -606,7 +568,8 @@ module ActiveFacts
|
|
606
568
|
next unless role.is_unique || fact_type
|
607
569
|
ftroles = Array(role.fact_type.all_role)
|
608
570
|
|
609
|
-
# Skip roles in ternary and higher fact types, they're objectified
|
571
|
+
# Skip roles in ternary and higher fact types, they're objectified
|
572
|
+
# REVISIT: This next line prevents a unary being used as a preferred_identifier:
|
610
573
|
next if ftroles.size != 2
|
611
574
|
|
612
575
|
trace :pi, "Considering role in #{role.fact_type.describe(role)}"
|
@@ -691,8 +654,8 @@ module ActiveFacts
|
|
691
654
|
pi = possible_pi
|
692
655
|
end
|
693
656
|
else
|
694
|
-
debugger
|
695
657
|
trace :pi, "No PI found for #{name}"
|
658
|
+
debugger if respond_to?(:debugger)
|
696
659
|
end
|
697
660
|
end
|
698
661
|
raise "No PI found for #{name}" unless pi
|
@@ -724,13 +687,18 @@ module ActiveFacts
|
|
724
687
|
}
|
725
688
|
end
|
726
689
|
|
727
|
-
# An array all direct supertypes
|
690
|
+
# An array of all direct supertypes
|
728
691
|
def supertypes
|
729
692
|
all_supertype_inheritance.map{|ti|
|
730
693
|
ti.supertype
|
731
694
|
}
|
732
695
|
end
|
733
696
|
|
697
|
+
# An array of all direct subtypes
|
698
|
+
def all_subtype
|
699
|
+
all_type_inheritance_as_supertype.map(&:subtype)
|
700
|
+
end
|
701
|
+
|
734
702
|
# An array of self followed by all supertypes in order:
|
735
703
|
def supertypes_transitive
|
736
704
|
([self] + all_type_inheritance_as_subtype.map{|ti|
|
@@ -738,21 +706,19 @@ module ActiveFacts
|
|
738
706
|
}).flatten.uniq
|
739
707
|
end
|
740
708
|
|
709
|
+
def identifying_type_inheritance
|
710
|
+
all_type_inheritance_as_subtype.detect do |ti|
|
711
|
+
ti.provides_identification
|
712
|
+
end
|
713
|
+
end
|
714
|
+
|
741
715
|
# A subtype does not have a identifying_supertype if it defines its own identifier
|
742
716
|
def identifying_supertype
|
743
|
-
|
744
|
-
all_type_inheritance_as_subtype.detect{|ti|
|
745
|
-
trace "considering supertype #{ti.supertype.name}"
|
746
|
-
next unless ti.provides_identification
|
747
|
-
trace "found identifying supertype of #{name}, it's #{ti.supertype.name}"
|
748
|
-
return ti.supertype
|
749
|
-
}
|
750
|
-
trace "Failed to find identifying supertype of #{name}"
|
751
|
-
return nil
|
717
|
+
ti = identifying_type_inheritance and ti.supertype
|
752
718
|
end
|
753
719
|
|
754
720
|
def common_supertype(other)
|
755
|
-
return nil unless other.
|
721
|
+
return nil unless other.is_a?(ActiveFacts::Metamodel::EntityType)
|
756
722
|
candidates = supertypes_transitive & other.supertypes_transitive
|
757
723
|
return candidates[0] if candidates.size <= 1
|
758
724
|
candidates[0] # REVISIT: This might not be the closest supertype
|
@@ -1039,6 +1005,10 @@ module ActiveFacts
|
|
1039
1005
|
max = max_frequency
|
1040
1006
|
'PresenceConstraint over '+role_sequence.describe + " occurs " + frequency + " time#{(min&&min>1)||(max&&max>1) ? 's' : ''}"
|
1041
1007
|
end
|
1008
|
+
|
1009
|
+
def covers_role role
|
1010
|
+
role_sequence.all_role_ref.map(&:role).include?(role)
|
1011
|
+
end
|
1042
1012
|
end
|
1043
1013
|
|
1044
1014
|
class SubsetConstraint
|
@@ -1463,6 +1433,112 @@ module ActiveFacts
|
|
1463
1433
|
end
|
1464
1434
|
end
|
1465
1435
|
|
1436
|
+
class Composite
|
1437
|
+
def inspect
|
1438
|
+
"Composite #{mapping.inspect}"
|
1439
|
+
end
|
1440
|
+
|
1441
|
+
def show_trace
|
1442
|
+
trace :composition, inspect do
|
1443
|
+
trace :composition?, "Columns" do
|
1444
|
+
mapping.show_trace
|
1445
|
+
end
|
1446
|
+
|
1447
|
+
indices =
|
1448
|
+
all_access_path.
|
1449
|
+
select{|ap| ap.is_a?(Index)}.
|
1450
|
+
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
|
1451
|
+
unless indices.empty?
|
1452
|
+
trace :composition, "Indices" do
|
1453
|
+
indices.each do |ap|
|
1454
|
+
ap.show_trace
|
1455
|
+
end
|
1456
|
+
end
|
1457
|
+
end
|
1458
|
+
|
1459
|
+
inbound = all_access_path.
|
1460
|
+
select{|ap| ap.is_a?(ForeignKey)}.
|
1461
|
+
sort_by{|fk| [fk.source_composite.mapping.name, fk.absorption.inspect]+fk.all_foreign_key_field.map(&:inspect)+fk.all_index_field.map(&:inspect) }
|
1462
|
+
unless inbound.empty?
|
1463
|
+
trace :composition, "Foreign keys inbound" do
|
1464
|
+
inbound.each do |fk|
|
1465
|
+
fk.show_trace
|
1466
|
+
end
|
1467
|
+
end
|
1468
|
+
end
|
1469
|
+
|
1470
|
+
outbound =
|
1471
|
+
all_foreign_key_as_source_composite.
|
1472
|
+
sort_by{|fk| [fk.source_composite.mapping.name, fk.absorption.inspect]+fk.all_index_field.map(&:inspect)+fk.all_foreign_key_field.map(&:inspect) }
|
1473
|
+
unless outbound.empty?
|
1474
|
+
trace :composition, "Foreign keys outbound" do
|
1475
|
+
outbound.each do |fk|
|
1476
|
+
fk.show_trace
|
1477
|
+
end
|
1478
|
+
end
|
1479
|
+
end
|
1480
|
+
end
|
1481
|
+
end
|
1482
|
+
end
|
1483
|
+
|
1484
|
+
class AccessPath
|
1485
|
+
def show_trace
|
1486
|
+
trace :composition, inspect do
|
1487
|
+
if is_a?(ForeignKey)
|
1488
|
+
# First list any fields in a foreign key
|
1489
|
+
all_foreign_key_field.sort_by(&:ordinal).each do |fk|
|
1490
|
+
raise "Internal error: Foreign key not in foreign table!" if fk.component.root != source_composite
|
1491
|
+
trace :composition, fk.inspect
|
1492
|
+
end
|
1493
|
+
end
|
1494
|
+
# Now list the fields in the primary key
|
1495
|
+
all_index_field.sort_by(&:ordinal).each do |ak|
|
1496
|
+
trace :composition, ak.inspect
|
1497
|
+
end
|
1498
|
+
end
|
1499
|
+
end
|
1500
|
+
end
|
1501
|
+
|
1502
|
+
class Index
|
1503
|
+
def inspect
|
1504
|
+
case
|
1505
|
+
when !is_unique
|
1506
|
+
'Non-unique index'
|
1507
|
+
when composite_as_primary_index
|
1508
|
+
'Primary index'
|
1509
|
+
else
|
1510
|
+
'Unique index'
|
1511
|
+
end +
|
1512
|
+
(name ? " #{name.inspect}" : '') +
|
1513
|
+
" to #{composite.mapping.name}" +
|
1514
|
+
(presence_constraint ? " over #{presence_constraint.describe}" : '')
|
1515
|
+
end
|
1516
|
+
end
|
1517
|
+
|
1518
|
+
class ForeignKey
|
1519
|
+
def inspect
|
1520
|
+
"Foreign Key" +
|
1521
|
+
(name ? " #{name.inspect}" : '') +
|
1522
|
+
" from #{source_composite.mapping.name} to #{composite.mapping.name}" +
|
1523
|
+
(absorption ? " over #{absorption.inspect}" : '')
|
1524
|
+
end
|
1525
|
+
end
|
1526
|
+
|
1527
|
+
class IndexField
|
1528
|
+
def inspect
|
1529
|
+
"IndexField part #{ordinal} in #{component.root.mapping.name} references #{component.inspect}" +
|
1530
|
+
(value ? " discriminated by #{value.inspect}" : '')
|
1531
|
+
end
|
1532
|
+
end
|
1533
|
+
|
1534
|
+
class ForeignKeyField
|
1535
|
+
def inspect
|
1536
|
+
operation = value ? "filters by value #{value} of" : "is"
|
1537
|
+
"ForeignKeyField part #{ordinal} in #{component.root.mapping.name} #{operation} #{component.inspect}" +
|
1538
|
+
(value ? " discriminated by #{value.inspect}" : '')
|
1539
|
+
end
|
1540
|
+
end
|
1541
|
+
|
1466
1542
|
class Mapping
|
1467
1543
|
def inspect
|
1468
1544
|
"#{self.class.basename} (#{rank_kind})#{parent ? " in #{parent.name}" :''} of #{name && name != '' ? name : '<anonymous>'}"
|
@@ -1479,6 +1555,7 @@ module ActiveFacts
|
|
1479
1555
|
|
1480
1556
|
# Recompute a contiguous member ranking fron zero, based on current membership:
|
1481
1557
|
def re_rank
|
1558
|
+
all_member.each(&:uncache_rank_key)
|
1482
1559
|
next_rank = 0
|
1483
1560
|
all_member.
|
1484
1561
|
sort_by(&:rank_key).
|
@@ -1488,6 +1565,9 @@ module ActiveFacts
|
|
1488
1565
|
end
|
1489
1566
|
end
|
1490
1567
|
|
1568
|
+
def root
|
1569
|
+
composite || parent && parent.root
|
1570
|
+
end
|
1491
1571
|
end
|
1492
1572
|
|
1493
1573
|
class Nesting
|
@@ -1504,7 +1584,16 @@ module ActiveFacts
|
|
1504
1584
|
end
|
1505
1585
|
|
1506
1586
|
def inspect
|
1507
|
-
"#{super}
|
1587
|
+
"#{super}#{full_absorption ? ' (full)' : ''
|
1588
|
+
} in #{inspect_reading}#{
|
1589
|
+
# If we have a related forward absorption, we're by definition a reverse absorption
|
1590
|
+
if forward_absorption
|
1591
|
+
' (reverse)'
|
1592
|
+
else
|
1593
|
+
# If we have a related reverse absorption, we're by definition a forward absorption
|
1594
|
+
reverse_absorption ? ' (forward)' : ''
|
1595
|
+
end
|
1596
|
+
}"
|
1508
1597
|
end
|
1509
1598
|
|
1510
1599
|
def show_trace
|
@@ -1555,7 +1644,9 @@ module ActiveFacts
|
|
1555
1644
|
return cvt if pvt != cvt
|
1556
1645
|
|
1557
1646
|
if !pvt
|
1558
|
-
#
|
1647
|
+
# Force the decision if one EntityType identifies another
|
1648
|
+
return true if child_role.base_role.is_identifying # Parent is identified by child role, correct
|
1649
|
+
return false if parent_role.base_role.is_identifying # Child is identified by parent role, incorrect
|
1559
1650
|
end
|
1560
1651
|
|
1561
1652
|
# Primary absorption absorbs the object playing the mandatory role into the non-mandatory:
|
@@ -1580,14 +1671,15 @@ module ActiveFacts
|
|
1580
1671
|
end
|
1581
1672
|
|
1582
1673
|
def flip!
|
1583
|
-
if
|
1674
|
+
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
|
1675
|
+
if (other = forward_absorption)
|
1584
1676
|
# We point at them - make them point at us instead
|
1585
|
-
self.
|
1677
|
+
self.forward_absorption = nil
|
1586
1678
|
self.reverse_absorption = other
|
1587
1679
|
elsif (other = reverse_absorption)
|
1588
1680
|
# They point at us - make us point at them instead
|
1589
1681
|
self.reverse_absorption = nil
|
1590
|
-
self.
|
1682
|
+
self.forward_absorption = other
|
1591
1683
|
else
|
1592
1684
|
raise "Absorption cannot be flipped as it has no reverse"
|
1593
1685
|
end
|
@@ -1595,6 +1687,12 @@ module ActiveFacts
|
|
1595
1687
|
|
1596
1688
|
end
|
1597
1689
|
|
1690
|
+
class FullAbsorption
|
1691
|
+
def inspect
|
1692
|
+
"Full #{absorption.inspect}"
|
1693
|
+
end
|
1694
|
+
end
|
1695
|
+
|
1598
1696
|
class Indicator
|
1599
1697
|
def inspect
|
1600
1698
|
"#{self.class.basename} #{role.fact_type.default_reading.inspect}"
|
@@ -1628,22 +1726,34 @@ module ActiveFacts
|
|
1628
1726
|
class Component
|
1629
1727
|
# The ranking key of a component indicates its importance to its parent:
|
1630
1728
|
# Ranking assigns a total order, but is computed in groups:
|
1631
|
-
|
1632
|
-
|
1633
|
-
|
1634
|
-
|
1635
|
-
|
1636
|
-
|
1637
|
-
|
1638
|
-
|
1639
|
-
|
1640
|
-
|
1641
|
-
|
1642
|
-
|
1729
|
+
RANK_SURROGATE = 0
|
1730
|
+
RANK_SUPER = 1 # Supertypes, with the identifying supertype first, others alphabetical
|
1731
|
+
RANK_IDENT = 2 # Identifying components (absorptions, indicator), in order of the identifier
|
1732
|
+
RANK_VALUE = 3 # A ValueField
|
1733
|
+
RANK_INJECTION = 4 # Injections, in alphabetical order
|
1734
|
+
RANK_DISCRIMINATOR = 5 # Discriminator components, in alphabetical order
|
1735
|
+
RANK_FOREIGN = 6 # REVISIT: Foreign key components
|
1736
|
+
RANK_INDICATOR = 7 # Indicators in alphabetical order
|
1737
|
+
RANK_MANDATORY = 8 # Absorption: unique mandatory
|
1738
|
+
RANK_NON_MANDATORY = 9 # Absorption: unique optional
|
1739
|
+
RANK_MULTIPLE = 10 # Absorption: manifold
|
1740
|
+
RANK_SUBTYPE = 11 # Subtypes in alphabetical order
|
1741
|
+
RANK_SCOPING = 12 # Scoping in alphabetical order
|
1742
|
+
|
1743
|
+
def uncache_rank_key
|
1744
|
+
@rank_key = nil
|
1745
|
+
end
|
1643
1746
|
|
1644
1747
|
def rank_key
|
1645
1748
|
@rank_key ||=
|
1646
1749
|
case self
|
1750
|
+
when SurrogateKey
|
1751
|
+
if !parent.parent
|
1752
|
+
[RANK_SURROGATE] # an injected PK
|
1753
|
+
else
|
1754
|
+
[RANK_MANDATORY, name] # an FK
|
1755
|
+
end
|
1756
|
+
|
1647
1757
|
when Indicator
|
1648
1758
|
if (p = parent_entity_type) and (position = p.rank_in_preferred_identifier(role.base_role))
|
1649
1759
|
[RANK_IDENT, position] # An identifying unary
|
@@ -1690,6 +1800,15 @@ module ActiveFacts
|
|
1690
1800
|
end
|
1691
1801
|
end
|
1692
1802
|
|
1803
|
+
def primary_index_components
|
1804
|
+
root and
|
1805
|
+
ix = root.primary_index and # Primary index has been decided
|
1806
|
+
root.primary_index.all_index_field.size > 0 and # has been populated and
|
1807
|
+
ix = root.primary_index and
|
1808
|
+
ixfs = ix.all_index_field.sort_by(&:ordinal) and
|
1809
|
+
ixfs.map(&:component)
|
1810
|
+
end
|
1811
|
+
|
1693
1812
|
def parent_entity_type
|
1694
1813
|
parent &&
|
1695
1814
|
parent.object_type.is_a?(EntityType) &&
|
@@ -1699,6 +1818,7 @@ module ActiveFacts
|
|
1699
1818
|
def rank_kind
|
1700
1819
|
return "top" unless parent # E.g. a Mapping that is a Composite
|
1701
1820
|
case rank_key[0]
|
1821
|
+
when RANK_SURROGATE; "surrogate"
|
1702
1822
|
when RANK_SUPER; "supertype"
|
1703
1823
|
when RANK_IDENT; "existential"
|
1704
1824
|
when RANK_VALUE; "self-value"
|
@@ -1714,6 +1834,14 @@ module ActiveFacts
|
|
1714
1834
|
end
|
1715
1835
|
end
|
1716
1836
|
|
1837
|
+
def root
|
1838
|
+
parent.root
|
1839
|
+
end
|
1840
|
+
|
1841
|
+
def depth
|
1842
|
+
parent ? 1+parent.depth : 0
|
1843
|
+
end
|
1844
|
+
|
1717
1845
|
def inspect
|
1718
1846
|
"#{self.class.basename}"
|
1719
1847
|
end
|