activefacts-metamodel 1.9.20 → 1.9.22
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/.gitignore +1 -0
- data/cql/Metamodel.cql +25 -23
- data/images/Absorption.png +0 -0
- data/images/Components.png +0 -0
- data/images/Compositions.png +0 -0
- data/images/Concepts.png +0 -0
- data/images/Nesting.png +0 -0
- data/images/Queries.png +0 -0
- data/images/Transformations.png +0 -0
- data/images/ValueTypes.png +0 -0
- data/lib/activefacts/metamodel/datatypes.rb +32 -22
- data/lib/activefacts/metamodel/extensions.rb +181 -130
- data/lib/activefacts/metamodel/metamodel.rb +115 -93
- data/lib/activefacts/metamodel/validate/composition.rb +9 -10
- data/lib/activefacts/metamodel/version.rb +1 -1
- data/lib/activefacts/support.rb +6 -1
- data/orm/Metamodel.cql.diffs +22 -22
- data/orm/Metamodel.orm +4954 -4577
- metadata +3 -3
- data/images/Transforms.png +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8f78f93ff86302b754f0170041f1e5118a08dbbe
|
4
|
+
data.tar.gz: d6ade4f75f9960d866088c9c0fc8d6def107bd4e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 423622558e664532cff04bc8fa9f8ef418e7f0d437894336d6715063744980110c87df94b4308a8aac0908f6c69995d249f970f14c460aaa75722348a18def1a
|
7
|
+
data.tar.gz: 837e537f5da6ca45fb8b5590222844e75124a1a80533173773ac3ea7ae05027de4e7b4c193a4a36be4d4f0e08907d445a0a4053f5efae20b88c491b0a25a1933
|
data/.gitignore
CHANGED
data/cql/Metamodel.cql
CHANGED
@@ -28,6 +28,7 @@ Offset is written as Decimal;
|
|
28
28
|
Ordinal is written as Unsigned Integer(16);
|
29
29
|
Pronoun is written as String(20) restricted to {'feminine', 'masculine', 'neuter', 'personal'};
|
30
30
|
Regular Expression is written as String;
|
31
|
+
Restriction Style is written as String restricted to {'ascending', 'descending', 'fixed'};
|
31
32
|
Ring Type is written as String;
|
32
33
|
Rotation Setting is written as String restricted to {'left', 'right'};
|
33
34
|
Scale is written as Unsigned Integer(32);
|
@@ -362,12 +363,12 @@ Value Type is subtype of at most one super-Value Type (as Supertype) [acyclic, t
|
|
362
363
|
Value Type Parameter is where
|
363
364
|
Value Type has parameter called Name,
|
364
365
|
Name identifies parameter of Value Type;
|
366
|
+
Value Type Parameter has at most one Restriction Style;
|
365
367
|
Value Type Parameter requires value of one parameter-Value Type;
|
366
368
|
|
367
369
|
Value Type Parameter Restriction is where
|
368
|
-
Value Type
|
369
|
-
|
370
|
-
Value Type Parameter Restriction has one Value;
|
370
|
+
Value Type allows Value Type Parameter to have Value Range;
|
371
|
+
|
371
372
|
|
372
373
|
/*
|
373
374
|
* Compositions
|
@@ -387,6 +388,7 @@ Component (as Member) belongs to at most one Component (as Parent) [acyclic, str
|
|
387
388
|
Component projects at most one Name;
|
388
389
|
Component has at most one Ordinal rank;
|
389
390
|
Component has absolute name;
|
391
|
+
Component has at most one injection-Annotation;
|
390
392
|
|
391
393
|
Indicator is a kind of Component;
|
392
394
|
Indicator indicates one Role played;
|
@@ -398,9 +400,20 @@ Discriminated Role is where
|
|
398
400
|
Discriminator distinguishes Role using one Value,
|
399
401
|
Role is indicated by Value for Discriminator;
|
400
402
|
|
403
|
+
Injection is a kind of Component;
|
404
|
+
Surrogate Key is a kind of Injection;
|
405
|
+
Computed Value is a kind of Injection;
|
406
|
+
Hash Value is a kind of Computed Value;
|
407
|
+
Component Hash is where
|
408
|
+
Hash Value covers at least one Component;
|
409
|
+
either Injection is a Surrogate Key
|
410
|
+
or Injection is a Computed Value but not both;
|
411
|
+
|
401
412
|
Mapping is a kind of Component;
|
402
413
|
Mapping represents one Object Type;
|
403
414
|
Mapping uses at most one native- type Name;
|
415
|
+
forward-Mapping is matched by at most one reverse-Mapping,
|
416
|
+
reverse-Mapping is reverse of at most one forward-Mapping;
|
404
417
|
|
405
418
|
Composite is identified by Mapping where
|
406
419
|
Mapping projects at most one Composite,
|
@@ -411,14 +424,9 @@ Composition contains Composite,
|
|
411
424
|
each Composite Group is identified by Name;
|
412
425
|
Composite belongs to at most one Composite Group;
|
413
426
|
|
414
|
-
Temporal Mapping is a kind of Mapping;
|
415
|
-
Temporal Mapping records time using one Value Type;
|
416
|
-
|
417
427
|
Absorption is a kind of Mapping;
|
418
428
|
Absorption traverses to one child-Role;
|
419
429
|
Absorption traverses from one parent-Role;
|
420
|
-
forward-Absorption is matched by at most one reverse-Absorption,
|
421
|
-
reverse-Absorption is reverse of at most one forward-Absorption;
|
422
430
|
Absorption flattens;
|
423
431
|
|
424
432
|
some Absorption traverses from some parent- Role(1) and that Role(1) is played by some Object Type
|
@@ -429,8 +437,8 @@ some Absorption traverses to some child- Role(1) and that Role(1) is played by s
|
|
429
437
|
|
430
438
|
Full Absorption is where
|
431
439
|
Composition fully absorbs Object Type;
|
432
|
-
Full Absorption applies to one
|
433
|
-
|
440
|
+
Full Absorption applies to one Mapping,
|
441
|
+
Mapping creates at most one Full Absorption;
|
434
442
|
/*
|
435
443
|
some Object Type is involved in some Full Absorption that applies to some Absorption that is a kind of Mapping
|
436
444
|
if and only if
|
@@ -453,10 +461,8 @@ Spanning Constraint is where
|
|
453
461
|
Constraint spans Composite;
|
454
462
|
|
455
463
|
Scoping is a kind of Mapping;
|
456
|
-
|
457
|
-
|
458
|
-
Surrogate Key is a kind of Injection;
|
459
|
-
Valid From is a kind of Injection;
|
464
|
+
Value Field is a kind of Mapping;
|
465
|
+
Valid From is a kind of Mapping;
|
460
466
|
|
461
467
|
/* Access Paths */
|
462
468
|
Access Path is identified by Guid where
|
@@ -469,7 +475,7 @@ Access Path is to one Composite,
|
|
469
475
|
/* Indices */
|
470
476
|
Index is a kind of Access Path;
|
471
477
|
Index is unique;
|
472
|
-
Index derives from one Presence Constraint;
|
478
|
+
Index derives from at most one Presence Constraint;
|
473
479
|
Composite has at most one natural-Index,
|
474
480
|
Index is natural for at most one Composite;
|
475
481
|
Composite has primary-Index; // Avoid ambiguity; this is a new fact type
|
@@ -493,8 +499,8 @@ Foreign Key is a kind of Access Path;
|
|
493
499
|
either Access Path is an Index or Access Path is a Foreign Key but not both;
|
494
500
|
Foreign Key traverses from one source-Composite,
|
495
501
|
Composite contains Foreign Key;
|
496
|
-
Foreign Key derives from one
|
497
|
-
|
502
|
+
Foreign Key derives from at most one Mapping,
|
503
|
+
Mapping gives rise to at most one Foreign Key;
|
498
504
|
|
499
505
|
Foreign Key Field is where
|
500
506
|
Foreign Key for Ordinal field uses one Component,
|
@@ -660,7 +666,8 @@ either Component(1) belongs to Component(2) or Component(1) is a Mapping that pr
|
|
660
666
|
for each Component exactly one of these holds:
|
661
667
|
Component is a Mapping,
|
662
668
|
Component is an Indicator,
|
663
|
-
Component is a Discriminator
|
669
|
+
Component is a Discriminator,
|
670
|
+
Component is an Injection;
|
664
671
|
for each Concept exactly one of these holds:
|
665
672
|
Object Type is an instance of Concept,
|
666
673
|
Fact Type is an instance of Concept,
|
@@ -678,11 +685,6 @@ for each Constraint exactly one of these holds:
|
|
678
685
|
Constraint is a Ring Constraint,
|
679
686
|
Constraint is a Value Constraint;
|
680
687
|
either Domain Object Type is a Value Type or Domain Object Type is an Entity Type but not both;
|
681
|
-
for each Mapping exactly one of these holds:
|
682
|
-
Mapping is an Absorption,
|
683
|
-
Mapping is an Injection,
|
684
|
-
Mapping is a Scoping,
|
685
|
-
Mapping projects Composite;
|
686
688
|
for each Role Sequence exactly one of these holds:
|
687
689
|
Role Sequence is for Reading,
|
688
690
|
Presence Constraint covers Role Sequence,
|
data/images/Absorption.png
CHANGED
Binary file
|
data/images/Components.png
CHANGED
Binary file
|
data/images/Compositions.png
CHANGED
Binary file
|
data/images/Concepts.png
CHANGED
Binary file
|
data/images/Nesting.png
CHANGED
Binary file
|
data/images/Queries.png
CHANGED
Binary file
|
Binary file
|
data/images/ValueTypes.png
CHANGED
Binary file
|
@@ -25,7 +25,7 @@ module ActiveFacts
|
|
25
25
|
TYPE_Boolean =>
|
26
26
|
%w{ bit },
|
27
27
|
TYPE_Integer =>
|
28
|
-
%w{ auto_counter int tiny_int small_int big_int unsigned unsigned_int unsigned_integer signed_int signed_integer},
|
28
|
+
%w{ auto_counter int tiny_int small_int big_int long_integer unsigned unsigned_int unsigned_integer signed_int signed_integer},
|
29
29
|
TYPE_Real =>
|
30
30
|
%w{ float double },
|
31
31
|
TYPE_Decimal =>
|
@@ -47,7 +47,7 @@ module ActiveFacts
|
|
47
47
|
TYPE_Timestamp =>
|
48
48
|
%w{ time_stamp auto_time_stamp },
|
49
49
|
TYPE_Binary =>
|
50
|
-
%w{ guid picture_raw_data variable_length_raw_data },
|
50
|
+
%w{ binary blob guid picture_raw_data variable_length_raw_data },
|
51
51
|
}
|
52
52
|
TypeParameters = {
|
53
53
|
TYPE_Integer => [:length], # Length is the number of bits
|
@@ -63,13 +63,17 @@ module ActiveFacts
|
|
63
63
|
# A DataType Context class should refine this class.
|
64
64
|
# The default context might work for you.
|
65
65
|
class Context
|
66
|
+
def initialize options = {}
|
67
|
+
raise "Unused options in DataType::Context: #{options.inspect}" unless options.empty?
|
68
|
+
end
|
69
|
+
|
66
70
|
def integer_ranges
|
67
71
|
end
|
68
72
|
|
69
73
|
def default_length data_type, type_name
|
70
74
|
end
|
71
75
|
|
72
|
-
def
|
76
|
+
def choose_integer_range min, max
|
73
77
|
integer_ranges.
|
74
78
|
select{|type_name, vmin, vmax| min >= vmin && max <= vmax}.
|
75
79
|
sort_by{|type_name, vmin, vmax| vmax-vmin}[0] # Choose the smallest range
|
@@ -103,7 +107,7 @@ module ActiveFacts
|
|
103
107
|
when /([a-z ]|\b)Small([a-z ]|\b)/i,
|
104
108
|
/([a-z ]|\b)Short([a-z ]|\b)/i
|
105
109
|
16
|
106
|
-
when /([a-z ]|\b)Big([a-z ]|\b)/i
|
110
|
+
when /([a-z ]|\b)(Big|Long)([a-z ]|\b)/i
|
107
111
|
64
|
108
112
|
else
|
109
113
|
32
|
@@ -122,12 +126,13 @@ module ActiveFacts
|
|
122
126
|
end
|
123
127
|
end
|
124
128
|
|
125
|
-
def self.
|
129
|
+
def self.intrinsic_type type_name
|
126
130
|
data_type, = type_mapping.detect{|t, names| names.detect{|n| n === type_name}}
|
127
131
|
data_type
|
128
132
|
end
|
129
133
|
|
130
|
-
|
134
|
+
# Integers are available in multiple sizes. Choose the most appropriate one.
|
135
|
+
def self.choose_integer type_name, length = nil, value_constraint = nil, context = DefaultContext.new
|
131
136
|
int_length = length || context.default_length(TYPE_Integer, type_name)
|
132
137
|
if int_length
|
133
138
|
if value_constraint
|
@@ -145,14 +150,18 @@ module ActiveFacts
|
|
145
150
|
int_max = unsigned ? 2**(int_length-1) - 1 : 2**(int_length-1)-1
|
146
151
|
max = int_max if !max || length && int_max < max
|
147
152
|
end
|
148
|
-
best = context.
|
153
|
+
best = context.choose_integer_range(min, max)
|
149
154
|
unless best || length
|
150
155
|
# Use the largest available integer size
|
151
156
|
best = context.integer_ranges.last
|
152
157
|
end
|
153
158
|
|
154
159
|
# Use a context-defined integer size if one suits, otherwise the requested size:
|
155
|
-
|
160
|
+
if best
|
161
|
+
best[0]
|
162
|
+
else
|
163
|
+
nil # No integer seems suitable
|
164
|
+
end
|
156
165
|
end
|
157
166
|
|
158
167
|
private
|
@@ -180,6 +189,11 @@ module ActiveFacts
|
|
180
189
|
primary_index.all_index_field.detect{|ixf| ixf.component == self}
|
181
190
|
end
|
182
191
|
|
192
|
+
def in_natural_index
|
193
|
+
natural_index = root.natural_index and
|
194
|
+
natural_index.all_index_field.detect{|ixf| ixf.component == self}
|
195
|
+
end
|
196
|
+
|
183
197
|
def in_foreign_key
|
184
198
|
all_foreign_key_field.detect{|fkf| fkf.foreign_key.source_composite == root}
|
185
199
|
end
|
@@ -190,17 +204,15 @@ module ActiveFacts
|
|
190
204
|
context.boolean_type
|
191
205
|
|
192
206
|
when SurrogateKey
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
]
|
199
|
-
|
200
|
-
when ValidFrom
|
201
|
-
object_type.name
|
207
|
+
type_name, options = *context.surrogate_type
|
208
|
+
options ||= {}
|
209
|
+
# Flag but disable auto-assignment for a surrogate that's an FK (assigned elsewhere)
|
210
|
+
options[:auto_assign] ||= 'commit'
|
211
|
+
options[:auto_assign] = nil if in_foreign_key
|
212
|
+
options[:mandatory] = path_mandatory
|
213
|
+
[ type_name, options ]
|
202
214
|
|
203
|
-
when ValueField,
|
215
|
+
when ValidFrom, ValueField, Mapping
|
204
216
|
# Walk up the entity type identifiers (must be single-part) to a value type:
|
205
217
|
vt = self.object_type
|
206
218
|
while vt.is_a?(EntityType)
|
@@ -248,8 +260,6 @@ module ActiveFacts
|
|
248
260
|
merge!(value_constraint ? {value_constraint: value_constraint} : {})
|
249
261
|
]
|
250
262
|
|
251
|
-
when Injection
|
252
|
-
object_type.name
|
253
263
|
else
|
254
264
|
raise "Can't make a column from #{component}"
|
255
265
|
end
|
@@ -265,7 +275,7 @@ end
|
|
265
275
|
|
266
276
|
if $0 == __FILE__
|
267
277
|
D = ActiveFacts::Metamodel::DataType
|
268
|
-
D.
|
278
|
+
D.intrinsic_type('Auto Timestamp')
|
269
279
|
|
270
280
|
class ModContext < D::DefaultContext
|
271
281
|
def integer_ranges
|
@@ -277,5 +287,5 @@ if $0 == __FILE__
|
|
277
287
|
end
|
278
288
|
end
|
279
289
|
puts "Normalising a tiny"
|
280
|
-
p D.
|
290
|
+
p D.choose_integer('tiny', nil, nil, ModContext.new)
|
281
291
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'pp'
|
2
1
|
#
|
3
2
|
# ActiveFacts Vocabulary Metamodel.
|
4
3
|
# Extensions to the ActiveFacts Vocabulary classes (which are generated from the Metamodel)
|
@@ -534,6 +533,33 @@ module ActiveFacts
|
|
534
533
|
end
|
535
534
|
nil
|
536
535
|
end
|
536
|
+
|
537
|
+
def all_value_type_parameter_transitive
|
538
|
+
supertypes_transitive.flat_map{|st| st.all_value_type_parameter.to_a}
|
539
|
+
end
|
540
|
+
|
541
|
+
def applicable_parameter_restrictions parameter_name, include_base_type = false
|
542
|
+
vtp = all_value_type_parameter_transitive.detect{|vtp| vtp.name == parameter_name }
|
543
|
+
return [] if !vtp || vtp.value_type == self
|
544
|
+
vtpr = all_value_type_parameter_restriction.select{|vtpr| vtpr.value_type_parameter == vtp }
|
545
|
+
if vtpr.empty?
|
546
|
+
supertype.applicable_parameter_restrictions parameter_name
|
547
|
+
else
|
548
|
+
vtpr
|
549
|
+
end
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
class ValueTypeParameter
|
554
|
+
def describe
|
555
|
+
"ValueType '#{value_type.name}' Parameter '#{name}' is of type '#{parameter_value_type.name}'"
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
class ValueTypeParameterRestriction
|
560
|
+
def inspect
|
561
|
+
value_range.inspect
|
562
|
+
end
|
537
563
|
end
|
538
564
|
|
539
565
|
class EntityType
|
@@ -555,101 +581,8 @@ module ActiveFacts
|
|
555
581
|
assimilation == 'partitioned'
|
556
582
|
end
|
557
583
|
|
558
|
-
def new_pi
|
559
|
-
trace :newpi, "Looking for New PI for #{name}" do
|
560
|
-
# An objectified fact type is identified by the object which plays it.
|
561
|
-
if fact_type && fact_type.all_role.size == 1
|
562
|
-
# REVISIT: If the OFT is separate (is this even possible?), this probably won't work:
|
563
|
-
return fact_type.all_role.single.object_type.preferred_identifier
|
564
|
-
end
|
565
|
-
|
566
|
-
# All roles in a preferred identifier must be a counterpart
|
567
|
-
# to a role played by this type or any of its supertypes.
|
568
|
-
# Here, a unary role is deemed to be its own counterpart.
|
569
|
-
# If this is an objectified fact type, its roles are deemed
|
570
|
-
# to be counterparts per virtue of the mirror role.
|
571
|
-
|
572
|
-
# if name == 'User'
|
573
|
-
# pp all_role_transitive.map{|a| a.fact_type.describe(a)}
|
574
|
-
# debugger
|
575
|
-
# end
|
576
|
-
|
577
|
-
art = all_role_transitive.map do |role|
|
578
|
-
# No TypeInheritance roles unless we play the subtype
|
579
|
-
if (ti = role.fact_type).is_a?(TypeInheritance) && role != ti.subtype_role
|
580
|
-
nil
|
581
|
-
elsif role.is_a?(MirrorRole)
|
582
|
-
nil
|
583
|
-
elsif role.fact_type.all_role.size == 1
|
584
|
-
role.base_role
|
585
|
-
else
|
586
|
-
x = (c = role.counterpart and c.base_role)
|
587
|
-
# debugger if x && x.object_type == self
|
588
|
-
x
|
589
|
-
end
|
590
|
-
end.compact
|
591
|
-
|
592
|
-
all_role.each do |role|
|
593
|
-
# Is this the subtype role in a supertype that identifies us?
|
594
|
-
if (ti = role.base_role.fact_type).is_a?(TypeInheritance)
|
595
|
-
if role == ti.subtype_role and ti.provides_identification
|
596
|
-
trace :newpi, "#{name} is identified by supertype #{ti.supertype.name}"
|
597
|
-
|
598
|
-
pcs = ti.supertype_role.all_role_ref.to_a.flat_map(&:role_sequence).flat_map(&:all_presence_constraint).to_a
|
599
|
-
debugger if pcs.size > 1
|
600
|
-
return pcs[0]
|
601
|
-
|
602
|
-
return ti.supertype_role.object_type.preferred_identifier
|
603
|
-
end
|
604
|
-
next # It can't be this TypeInheritance
|
605
|
-
end
|
606
|
-
|
607
|
-
# The fact type must be binary or unary
|
608
|
-
next if role.fact_type.all_role.size > 2
|
609
|
-
|
610
|
-
base_role = role.counterpart.base_role # Go to the OFT role that implied the link fact type
|
611
|
-
base_role.all_role_ref.each do |rr|
|
612
|
-
if rr.role_sequence.all_presence_constraint.size > 0
|
613
|
-
trace :newpi, "Looking for New PI that includes '#{base_role.fact_type.describe(base_role)}'"
|
614
|
-
end
|
615
|
-
rr.role_sequence.all_presence_constraint.each do |pc|
|
616
|
-
next unless pc.max_frequency == 1 and
|
617
|
-
pc.is_preferred_identifier and
|
618
|
-
!pc.enforcement # Alethic uniqueness constraint
|
619
|
-
# We have a uniqueness constraint that is alethic and preferred.
|
620
|
-
# If all its role are valid counterparts, this is our preferred identifier.
|
621
|
-
next if pc.role_sequence.all_role_ref.to_a.detect do |nrr|
|
622
|
-
if !art.include?(nrr.role)
|
623
|
-
trace :newpi, "Rejecting New PI with excluded role '#{nrr.role.fact_type.describe(nrr.role)}'"
|
624
|
-
end
|
625
|
-
!art.include?(nrr.role) # Nope, not included in the valid counterparts
|
626
|
-
end
|
627
|
-
trace :newpi, "Accepting New PI #{pc.describe}"
|
628
|
-
return pc
|
629
|
-
end
|
630
|
-
end
|
631
|
-
end
|
632
|
-
|
633
|
-
debugger
|
634
|
-
trace :newpi, "No New PI found for #{name}"
|
635
|
-
nil
|
636
|
-
|
637
|
-
end
|
638
|
-
end
|
639
|
-
|
640
584
|
def preferred_identifier
|
641
585
|
return @preferred_identifier if @preferred_identifier
|
642
|
-
|
643
|
-
@old_preferred_identifier = old_preferred_identifier
|
644
|
-
# @new_preferred_identifier = new_pi
|
645
|
-
# if @old_preferred_identifier != @new_preferred_identifier
|
646
|
-
## debugger
|
647
|
-
# new_pi
|
648
|
-
# end
|
649
|
-
return @preferred_identifier = @old_preferred_identifier
|
650
|
-
end
|
651
|
-
|
652
|
-
def old_preferred_identifier
|
653
586
|
if fact_type
|
654
587
|
# Objectified unaries are identified by the ID of the object that plays the role:
|
655
588
|
if fact_type.all_role.size == 1
|
@@ -1101,11 +1034,17 @@ module ActiveFacts
|
|
1101
1034
|
|
1102
1035
|
class AllowedRange
|
1103
1036
|
def to_s(infinity = true)
|
1104
|
-
|
1105
|
-
|
1037
|
+
value_range.to_s infinity
|
1038
|
+
end
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
class ValueRange
|
1042
|
+
def to_s(infinity = true)
|
1043
|
+
min = minimum_bound
|
1044
|
+
max = maximum_bound
|
1106
1045
|
# Open-ended string ranges will fail in Ruby
|
1107
1046
|
|
1108
|
-
if min =
|
1047
|
+
if min = minimum_bound
|
1109
1048
|
min = min.value
|
1110
1049
|
if min.is_literal_string
|
1111
1050
|
min_literal = min.literal.inspect.gsub(/\A"|"\Z/,"'") # Escape string characters
|
@@ -1115,7 +1054,7 @@ module ActiveFacts
|
|
1115
1054
|
else
|
1116
1055
|
min_literal = infinity ? "-Infinity" : ""
|
1117
1056
|
end
|
1118
|
-
if max =
|
1057
|
+
if max = maximum_bound
|
1119
1058
|
max = max.value
|
1120
1059
|
if max.is_literal_string
|
1121
1060
|
max_literal = max.literal.inspect.gsub(/\A"|"\Z/,"'") # Escape string characters
|
@@ -1129,6 +1068,40 @@ module ActiveFacts
|
|
1129
1068
|
min_literal +
|
1130
1069
|
(min_literal != (max&&max_literal) ? (".." + max_literal) : "")
|
1131
1070
|
end
|
1071
|
+
|
1072
|
+
def inspect
|
1073
|
+
to_s
|
1074
|
+
end
|
1075
|
+
|
1076
|
+
# Note the mismatched return types here:
|
1077
|
+
def effective_maximum
|
1078
|
+
maximum_bound ? maximum_bound.value : Infinity
|
1079
|
+
end
|
1080
|
+
|
1081
|
+
def effective_minimum
|
1082
|
+
minimum_bound ? minimum_bound.value : -Infinity
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
def includes? range
|
1086
|
+
if ValueRange === range
|
1087
|
+
range.effective_minimum >= effective_minimum && range.effective_maximum <= effective_maximum
|
1088
|
+
# elsif Bound === range # REVISIT: use self.includes?(range.value) with is_literal_string, etc
|
1089
|
+
else
|
1090
|
+
range >= effective_minimum && range <= effective_maximum
|
1091
|
+
end
|
1092
|
+
end
|
1093
|
+
end
|
1094
|
+
|
1095
|
+
class Bound
|
1096
|
+
def <=(other)
|
1097
|
+
return true if other == Infinity
|
1098
|
+
is_inclusive ? value <= other : value < other
|
1099
|
+
end
|
1100
|
+
|
1101
|
+
def >=(other)
|
1102
|
+
return true if other == -Infinity
|
1103
|
+
is_inclusive ? value >= other : value > other
|
1104
|
+
end
|
1132
1105
|
end
|
1133
1106
|
|
1134
1107
|
class Value
|
@@ -1149,6 +1122,38 @@ module ActiveFacts
|
|
1149
1122
|
def inspect
|
1150
1123
|
to_s
|
1151
1124
|
end
|
1125
|
+
|
1126
|
+
def effective_value
|
1127
|
+
is_literal_string ? literal : eval(literal)
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
def ==(other)
|
1131
|
+
effective_value == (Value === other ? other.effective_value : other)
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
def !=(other)
|
1135
|
+
!(self == other)
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
def <=(other)
|
1139
|
+
return true if other == Infinity
|
1140
|
+
effective_value <= other.effective_value rescue nil
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
def >=(other)
|
1144
|
+
return true if other == -Infinity
|
1145
|
+
effective_value >= other.effective_value rescue nil
|
1146
|
+
end
|
1147
|
+
|
1148
|
+
def <(other)
|
1149
|
+
v = self >= other
|
1150
|
+
v == nil ? v : !v
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
def >(other)
|
1154
|
+
v = self >= other
|
1155
|
+
v == nil ? v : !v
|
1156
|
+
end
|
1152
1157
|
end
|
1153
1158
|
|
1154
1159
|
class PresenceConstraint
|
@@ -1671,7 +1676,7 @@ module ActiveFacts
|
|
1671
1676
|
|
1672
1677
|
inbound = all_access_path.
|
1673
1678
|
select{|ap| ap.is_a?(ForeignKey)}.
|
1674
|
-
sort_by{|fk| [fk.source_composite.mapping.name, fk.
|
1679
|
+
sort_by{|fk| [fk.source_composite.mapping.name, fk.mapping.inspect]+fk.all_foreign_key_field.map(&:inspect)+fk.all_index_field.map(&:inspect) }
|
1675
1680
|
unless inbound.empty?
|
1676
1681
|
trace :composition, "Foreign keys inbound" do
|
1677
1682
|
inbound.each do |fk|
|
@@ -1682,7 +1687,7 @@ module ActiveFacts
|
|
1682
1687
|
|
1683
1688
|
outbound =
|
1684
1689
|
all_foreign_key_as_source_composite.
|
1685
|
-
sort_by{|fk| [fk.source_composite.mapping.name, fk.
|
1690
|
+
sort_by{|fk| [fk.source_composite.mapping.name, fk.mapping.inspect]+fk.all_index_field.map(&:inspect)+fk.all_foreign_key_field.map(&:inspect) }
|
1686
1691
|
unless outbound.empty?
|
1687
1692
|
trace :composition, "Foreign keys outbound" do
|
1688
1693
|
outbound.each do |fk|
|
@@ -1697,7 +1702,7 @@ module ActiveFacts
|
|
1697
1702
|
def all_indices_by_rank
|
1698
1703
|
all_access_path.
|
1699
1704
|
reject{|ap| ap.is_a?(ActiveFacts::Metamodel::ForeignKey)}.
|
1700
|
-
sort_by{|ap| ap.all_index_field.to_a.flat_map{|ixf| ixf.component.rank_path}.compact }
|
1705
|
+
sort_by{|ap| ap.all_index_field.to_a.flat_map{|ixf| (c = ixf.component) ? c.rank_path : 0}.compact }
|
1701
1706
|
end
|
1702
1707
|
|
1703
1708
|
def all_foreign_key_as_target_composite
|
@@ -1736,12 +1741,14 @@ module ActiveFacts
|
|
1736
1741
|
'Non-unique index'
|
1737
1742
|
when composite_as_primary_index
|
1738
1743
|
'Primary index'
|
1744
|
+
when composite_as_natural_index
|
1745
|
+
'Natural index'
|
1739
1746
|
else
|
1740
1747
|
'Unique index'
|
1741
1748
|
end +
|
1742
1749
|
(name ? " #{name.inspect}" : '') +
|
1743
1750
|
" to #{composite.mapping.name}" +
|
1744
|
-
(presence_constraint ? "
|
1751
|
+
(presence_constraint ? "#{presence_constraint.role_sequence.describe}" : '')
|
1745
1752
|
end
|
1746
1753
|
end
|
1747
1754
|
|
@@ -1750,13 +1757,14 @@ module ActiveFacts
|
|
1750
1757
|
"Foreign Key" +
|
1751
1758
|
(name ? " #{name.inspect}" : '') +
|
1752
1759
|
" from #{(sc = source_composite) ? sc.mapping.name : 'NO-SOURCE'} to #{composite.mapping.name}" +
|
1753
|
-
(
|
1760
|
+
(mapping ? " over #{mapping.inspect}" : '')
|
1754
1761
|
end
|
1755
1762
|
end
|
1756
1763
|
|
1757
1764
|
class IndexField
|
1758
1765
|
def inspect
|
1759
|
-
|
1766
|
+
c_name = component ? component.root.mapping.name : "WARNING: IndexField without Component"
|
1767
|
+
"IndexField part #{ordinal} in #{c_name} references #{component.inspect}" +
|
1760
1768
|
(value ? " discriminated by #{value.inspect}" : '')
|
1761
1769
|
end
|
1762
1770
|
end
|
@@ -1770,6 +1778,10 @@ module ActiveFacts
|
|
1770
1778
|
end
|
1771
1779
|
|
1772
1780
|
class Mapping
|
1781
|
+
def inspect_reason
|
1782
|
+
"#{parent.name} contains mapping of #{name}"
|
1783
|
+
end
|
1784
|
+
|
1773
1785
|
def inspect
|
1774
1786
|
"#{self.class.basename} (#{rank_kind})#{parent ? " in #{parent.name}" :''} of #{name && name != '' ? name : '<anonymous>'}"
|
1775
1787
|
end
|
@@ -1777,12 +1789,20 @@ module ActiveFacts
|
|
1777
1789
|
def show_trace
|
1778
1790
|
trace :composition, "#{ordinal ? "#{ordinal}: " : ''}#{inspect}" do
|
1779
1791
|
yield if block_given?
|
1792
|
+
if m = all_member.detect{|m| !m.ordinal}
|
1793
|
+
trace :composition, "WARNING: show_trace needed to re_rank #{name} because of unranked #{m.inspect}"
|
1794
|
+
re_rank
|
1795
|
+
end
|
1780
1796
|
all_member.sort_by{|member| [member.ordinal, member.name]}.each do |member|
|
1781
1797
|
member.show_trace
|
1782
1798
|
end
|
1783
1799
|
end
|
1784
1800
|
end
|
1785
1801
|
|
1802
|
+
def is_type_inheritance
|
1803
|
+
false
|
1804
|
+
end
|
1805
|
+
|
1786
1806
|
# Recompute a contiguous member ranking fron zero, based on current membership:
|
1787
1807
|
def re_rank
|
1788
1808
|
all_member.each(&:uncache_rank_key)
|
@@ -1815,7 +1835,7 @@ module ActiveFacts
|
|
1815
1835
|
end
|
1816
1836
|
|
1817
1837
|
def path_mandatory
|
1818
|
-
|
1838
|
+
!parent || parent.path_mandatory
|
1819
1839
|
end
|
1820
1840
|
|
1821
1841
|
def is_auto_assigned
|
@@ -1829,6 +1849,10 @@ module ActiveFacts
|
|
1829
1849
|
nil
|
1830
1850
|
end
|
1831
1851
|
end
|
1852
|
+
|
1853
|
+
def is_partitioned_here
|
1854
|
+
false # Must be an absorption to be partitioned here
|
1855
|
+
end
|
1832
1856
|
end
|
1833
1857
|
|
1834
1858
|
class Nesting
|
@@ -1840,19 +1864,19 @@ module ActiveFacts
|
|
1840
1864
|
end
|
1841
1865
|
|
1842
1866
|
class Absorption
|
1843
|
-
def
|
1867
|
+
def inspect_reason
|
1844
1868
|
parent_role.fact_type.reading_preferably_starting_with_role(parent_role).expand.inspect
|
1845
1869
|
end
|
1846
1870
|
|
1847
1871
|
def inspect
|
1848
1872
|
"#{super}#{full_absorption ? ' (full)' : ''
|
1849
|
-
} in #{
|
1873
|
+
} in #{inspect_reason}#{
|
1850
1874
|
# If we have a related forward absorption, we're by definition a reverse absorption
|
1851
|
-
if
|
1875
|
+
if forward_mapping
|
1852
1876
|
' (reverse)'
|
1853
1877
|
else
|
1854
1878
|
# If we have a related reverse absorption, we're by definition a forward absorption
|
1855
|
-
|
1879
|
+
reverse_mapping ? ' (forward)' : ''
|
1856
1880
|
end
|
1857
1881
|
}"
|
1858
1882
|
end
|
@@ -1879,6 +1903,14 @@ module ActiveFacts
|
|
1879
1903
|
is_type_inheritance && parent_role.fact_type.supertype == object_type
|
1880
1904
|
end
|
1881
1905
|
|
1906
|
+
def is_partitioned_here
|
1907
|
+
(ft = child_role.fact_type).is_a?(TypeInheritance) && ft.assimilation == 'partitioned'
|
1908
|
+
end
|
1909
|
+
|
1910
|
+
def is_one_to_one
|
1911
|
+
parent_role.is_unique and child_role.is_unique
|
1912
|
+
end
|
1913
|
+
|
1882
1914
|
def is_preferred_direction
|
1883
1915
|
return child_role.is_mirror_role if child_role.is_mirror_role != parent_role.is_mirror_role
|
1884
1916
|
|
@@ -1932,15 +1964,15 @@ module ActiveFacts
|
|
1932
1964
|
end
|
1933
1965
|
|
1934
1966
|
def flip!
|
1935
|
-
raise "REVISIT: Need to flip FullAbsorption on #{inspect}" if full_absorption or
|
1936
|
-
if (other =
|
1967
|
+
raise "REVISIT: Need to flip FullAbsorption on #{inspect}" if full_absorption or reverse_mapping && reverse_mapping.full_absorption or forward_mapping && forward_mapping.full_absorption
|
1968
|
+
if (other = forward_mapping)
|
1937
1969
|
# We point at them - make them point at us instead
|
1938
|
-
self.
|
1939
|
-
self.
|
1940
|
-
elsif (other =
|
1970
|
+
self.forward_mapping = nil
|
1971
|
+
self.reverse_mapping = other
|
1972
|
+
elsif (other = reverse_mapping)
|
1941
1973
|
# They point at us - make us point at them instead
|
1942
|
-
self.
|
1943
|
-
self.
|
1974
|
+
self.reverse_mapping = nil
|
1975
|
+
self.forward_mapping = other
|
1944
1976
|
else
|
1945
1977
|
raise "Absorption cannot be flipped as it has no reverse"
|
1946
1978
|
end
|
@@ -2053,8 +2085,10 @@ module ActiveFacts
|
|
2053
2085
|
when SurrogateKey
|
2054
2086
|
if is_identifying
|
2055
2087
|
[RANK_SURROGATE] # an injected PK
|
2088
|
+
elsif injection_annotation
|
2089
|
+
[RANK_INJECTION, name]
|
2056
2090
|
else
|
2057
|
-
[
|
2091
|
+
[RANK_FOREIGN, name] # an FK
|
2058
2092
|
end
|
2059
2093
|
|
2060
2094
|
when Indicator
|
@@ -2070,10 +2104,11 @@ module ActiveFacts
|
|
2070
2104
|
[RANK_DISCRIMINATOR, name || child_role.name]
|
2071
2105
|
|
2072
2106
|
when ValueField
|
2073
|
-
|
2074
|
-
|
2075
|
-
|
2076
|
-
|
2107
|
+
if injection_annotation
|
2108
|
+
[RANK_INJECTION, name]
|
2109
|
+
else
|
2110
|
+
[RANK_IDENT]
|
2111
|
+
end
|
2077
2112
|
|
2078
2113
|
when Absorption
|
2079
2114
|
if is_type_inheritance
|
@@ -2089,6 +2124,8 @@ module ActiveFacts
|
|
2089
2124
|
end
|
2090
2125
|
elsif (p = parent_entity_type) and (position = p.rank_in_preferred_identifier(child_role.base_role))
|
2091
2126
|
[RANK_IDENT, position]
|
2127
|
+
elsif injection_annotation
|
2128
|
+
[RANK_INJECTION, name]
|
2092
2129
|
else
|
2093
2130
|
if parent_role.is_unique
|
2094
2131
|
[parent_role.is_mandatory ? RANK_MANDATORY : RANK_NON_MANDATORY, name || child_role.name]
|
@@ -2100,8 +2137,16 @@ module ActiveFacts
|
|
2100
2137
|
when Scoping
|
2101
2138
|
[RANK_SCOPING, name || object_type.name]
|
2102
2139
|
|
2140
|
+
when ValidFrom
|
2141
|
+
[RANK_INJECTION, name]
|
2142
|
+
|
2103
2143
|
when Mapping
|
2104
|
-
|
2144
|
+
# bare Mappings are always injected
|
2145
|
+
if root.mapping.object_type == object_type
|
2146
|
+
[RANK_IDENT, 0]
|
2147
|
+
else
|
2148
|
+
[RANK_INJECTION, name]
|
2149
|
+
end
|
2105
2150
|
|
2106
2151
|
else
|
2107
2152
|
raise "unexpected #{self.class.basename} in Component#rank_key"
|
@@ -2118,6 +2163,10 @@ module ActiveFacts
|
|
2118
2163
|
ixfs.map(&:component)
|
2119
2164
|
end
|
2120
2165
|
|
2166
|
+
def is_in_primary
|
2167
|
+
!!(root and p = root.primary_index and p.all_index_field.detect{|f| f.component == self})
|
2168
|
+
end
|
2169
|
+
|
2121
2170
|
def parent_entity_type
|
2122
2171
|
parent &&
|
2123
2172
|
parent.object_type.is_a?(EntityType) &&
|
@@ -2195,7 +2244,7 @@ module ActiveFacts
|
|
2195
2244
|
end
|
2196
2245
|
|
2197
2246
|
def fork_to_new_parent parent
|
2198
|
-
@constellation.fork self, guid: :new, parent: parent
|
2247
|
+
@constellation.fork self, guid: :new, parent: parent, injection_annotation: nil
|
2199
2248
|
end
|
2200
2249
|
end
|
2201
2250
|
|
@@ -2205,12 +2254,6 @@ module ActiveFacts
|
|
2205
2254
|
end
|
2206
2255
|
end
|
2207
2256
|
|
2208
|
-
class Injection
|
2209
|
-
def path_mandatory
|
2210
|
-
parent.path_mandatory
|
2211
|
-
end
|
2212
|
-
end
|
2213
|
-
|
2214
2257
|
class ValueField
|
2215
2258
|
def inspect
|
2216
2259
|
"#{self.class.basename} #{object_type.name.inspect}"
|
@@ -2226,7 +2269,7 @@ module ActiveFacts
|
|
2226
2269
|
|
2227
2270
|
def fork_to_new_parent parent
|
2228
2271
|
# When we fork from a ValueField, we want to use the name of the ValueType, not the ValueField name
|
2229
|
-
@constellation.fork self, guid: :new, parent: parent, name: object_type.name
|
2272
|
+
@constellation.fork self, guid: :new, parent: parent, name: object_type.name, injection_annotation: nil
|
2230
2273
|
end
|
2231
2274
|
end
|
2232
2275
|
|
@@ -2254,6 +2297,14 @@ module ActiveFacts
|
|
2254
2297
|
end
|
2255
2298
|
(parent ? parent.name + ' ' : '') + 'surrogate key'
|
2256
2299
|
end
|
2300
|
+
|
2301
|
+
def inspect
|
2302
|
+
"#{self.class.basename} (#{rank_kind})#{parent ? " in #{parent.name}" :''} of #{name && name != '' ? name : '<anonymous>'}"
|
2303
|
+
end
|
2304
|
+
|
2305
|
+
def show_trace
|
2306
|
+
trace :composition, "#{ordinal ? "#{ordinal}: " : ''}#{inspect}"
|
2307
|
+
end
|
2257
2308
|
end
|
2258
2309
|
|
2259
2310
|
#
|