activefacts-metamodel 1.7.2 → 1.8.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 255af1969756fd0c43b4a96ff9696734df2f73b0
4
- data.tar.gz: 8a46b3345d01ad3ed385fc86721a11304a814715
3
+ metadata.gz: e18edf07c9d30ebe02c45d63b87d1dabc328bcc0
4
+ data.tar.gz: 775315e7031db7f4b6f30cdf0efbac0cef5e2547
5
5
  SHA512:
6
- metadata.gz: 2b253061160435d1a1f87795abe35051b3fae10ecd3d8649782aa2e3be2778c3ecddc8d8dddca3fc90d79ffa56a2b25c0767dd904abc1192bacc524bcc07603f
7
- data.tar.gz: f3cdadbe305c6aadbbc92952f88ac46014e0216fb9032f7d6bac33928d9925d553f342b019d46847f9b5db8d7e3c0e176619cb699a3df3ff96d72c979f96608a
6
+ metadata.gz: 37f78ca38e9bfa027e90114db134f23b022ae0bb6d8b45f28460cb63ce4d728a54df61f9d0fe104ad55a5b80a8b8c776fd4dd70b24e863144c55f3911d0c519c
7
+ data.tar.gz: 1efd187a64997af03320b3efb48e068f4ea7ac037bffcf5765e92eded11b88ff1ba136fd196cd6cdf78d0a89b382f6a89485189d2c24e3198a6f2d8a70f9d341
data/.gitignore CHANGED
@@ -9,3 +9,7 @@
9
9
  /tmp/
10
10
  bin
11
11
  *.swp
12
+ .DS_Store
13
+ *.rej
14
+ *.orig
15
+ *.diff
data/Gemfile CHANGED
@@ -1,8 +1,9 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in activefacts-metamodel.gemspec
4
3
  gemspec
5
- if ENV['PWD'] =~ %r{\A/Users/cjh/work/activefacts}
4
+
5
+ if ENV['PWD'] =~ %r{\A#{ENV['HOME']}/work}
6
+ $stdout.puts "Using work area gems for #{File.basename(File.dirname(__FILE__))} from activefacts-metamodel"
6
7
  gem 'activefacts-api', path: '/Users/cjh/work/activefacts/api'
7
8
  # gem 'activefacts-api', git: 'git://github.com/cjheath/activefacts-api.git'
8
9
  end
data/Rakefile CHANGED
@@ -1,6 +1,23 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
+ require "pp"
3
4
 
4
5
  RSpec::Core::RakeTask.new(:spec)
5
6
 
6
7
  task :default => :spec
8
+
9
+ task :bump do
10
+ path = File.expand_path('../lib/activefacts/metamodel/version.rb', __FILE__)
11
+ lines = File.open(path) do |fp| fp.readlines; end
12
+ File.open(path, "w") do |fp|
13
+ fp.write(
14
+ lines.map do |line|
15
+ line.gsub(/(VERSION *= *"[0-9.]*\.)([0-9]+)"\n/) do
16
+ version = "#{$1}#{$2.to_i+1}"
17
+ puts "Version bumped to #{version}\""
18
+ version+"\"\n"
19
+ end
20
+ end*''
21
+ )
22
+ end
23
+ end
@@ -20,9 +20,9 @@ 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.a"
23
+ spec.add_development_dependency "bundler", ">= 1.10", "~> 1.10.6"
24
24
  spec.add_development_dependency "rake", "~> 10.0"
25
- spec.add_development_dependency "rspec"
25
+ spec.add_development_dependency "rspec", "~> 3.3"
26
26
 
27
- spec.add_runtime_dependency "activefacts-api"
27
+ spec.add_runtime_dependency "activefacts-api", ">= 1.8", "~> 1.8.4"
28
28
  end
data/cql/Metamodel.cql CHANGED
@@ -53,6 +53,17 @@ Coefficient is identified by Numerator and Denominator and Coefficient is precis
53
53
  Coefficient has one Denominator,
54
54
  Coefficient is precise;
55
55
 
56
+ Component is identified by Guid where
57
+ Component has one Guid,
58
+ Guid is of at most one Component;
59
+ Component projects at most one Name;
60
+
61
+ Composition is identified by Guid where
62
+ Composition has one Guid,
63
+ Guid is of at most one Composition;
64
+ Composition is called one Name,
65
+ Name is of at most one Composition;
66
+
56
67
  Concept is identified by Guid where
57
68
  Concept has one Guid,
58
69
  Guid is of at most one Concept;
@@ -75,6 +86,8 @@ Context Note has one Discussion,
75
86
  Context Note applies to at most one relevant-Concept,
76
87
  Concept has Context Note;
77
88
 
89
+ Discriminator is a kind of Component;
90
+
78
91
  Enforcement is identified by Constraint where
79
92
  Constraint requires at most one Enforcement,
80
93
  Enforcement applies to one Constraint;
@@ -92,6 +105,8 @@ Fact is of one Fact Type;
92
105
  Implication Rule is identified by its Name;
93
106
  Concept is implied by at most one Implication Rule;
94
107
 
108
+ Indicator is a kind of Component;
109
+
95
110
  Instance is identified by Concept where
96
111
  Instance is an instance of one Concept;
97
112
  Instance objectifies at most one Fact,
@@ -103,6 +118,10 @@ Location is identified by X and Y where
103
118
  Location is at one X,
104
119
  Location is at one Y;
105
120
 
121
+ Mapping is a kind of Component;
122
+ Mapping (as Parent) contains Component (as Member) [acyclic, stronglyintransitive],
123
+ Member belongs to at most one Parent;
124
+
106
125
  Presence Constraint is a kind of Constraint;
107
126
  Presence Constraint has at most one max-Frequency restricted to {1..};
108
127
  Presence Constraint has at most one min-Frequency restricted to {2..};
@@ -128,6 +147,7 @@ Role is identified by Fact Type and Ordinal where
128
147
  Fact Type contains Role,
129
148
  Role fills one Ordinal,
130
149
  Ordinal applies to Role;
150
+ Indicator indicates one Role played;
131
151
  Link Fact Type has one implying-Role,
132
152
  implying-Role implies at most one Link Fact Type;
133
153
  Ring Constraint has at most one other-Role,
@@ -154,6 +174,8 @@ Role Value is identified by Fact and Role where
154
174
  Instance plays Role Value,
155
175
  Role Value is of one Instance;
156
176
 
177
+ Scoping is a kind of Mapping;
178
+
157
179
  Set Constraint is a kind of Constraint;
158
180
 
159
181
  Shape is identified by Guid where
@@ -229,6 +251,12 @@ Vocabulary contains Constraint,
229
251
  Vocabulary includes Unit,
230
252
  Unit is in one Vocabulary;
231
253
 
254
+ Absorption is a kind of Mapping;
255
+ Absorption traverses to one child-Role;
256
+ Absorption traverses from one parent-Role;
257
+ Absorption is matched by at most one reverse-Absorption;
258
+ Absorption flattens;
259
+
232
260
  Aggregation is where
233
261
  Variable is bound to Aggregate over aggregated-Variable,
234
262
  Aggregate of aggregated-Variable is bound as one Variable;
@@ -244,6 +272,12 @@ Bound is identified by Value and Bound is inclusive where
244
272
  Value is of at least one Bound,
245
273
  Bound is inclusive;
246
274
 
275
+ Composite is identified by Mapping where
276
+ Mapping projects at most one Composite,
277
+ Composite consists of one Mapping;
278
+ Composition contains Composite,
279
+ Composite belongs to one Composition;
280
+
247
281
  Constraint Shape is a kind of Shape;
248
282
  Constraint Shape is for one Constraint;
249
283
 
@@ -266,15 +300,30 @@ Diagram is identified by Vocabulary and Name where
266
300
  Diagram is called one Name,
267
301
  Name is of Diagram;
268
302
 
303
+ Discriminated Role is where
304
+ Discriminator distinguishes Role using one Value,
305
+ Role is indicated by Value for Discriminator;
306
+
269
307
  Fact Type Shape is a kind of Shape;
270
308
  Fact Type Shape has at most one Display Role Names Setting;
271
309
  Fact Type Shape is for one Fact Type,
272
310
  Fact Type has Fact Type Shape;
273
311
  Fact Type Shape has at most one Rotation Setting;
274
312
 
313
+ Injection is a kind of Mapping;
314
+
315
+ Mirror Role is a kind of Role;
316
+ Mirror Role is for at most one Role (as Base Role),
317
+ Base Role implies at most one Mirror Role;
318
+
275
319
  Model Note Shape is a kind of Shape;
276
320
  Model Note Shape is for one Context Note;
277
321
 
322
+ Nesting is where
323
+ Absorption is nested under index-Role in Ordinal position,
324
+ Absorption in Ordinal position is nested under one Role,
325
+ Role keys nesting of Absorption at Ordinal priority;
326
+
278
327
  ORM Diagram is a kind of Diagram;
279
328
  Shape is in one ORM Diagram,
280
329
  ORM Diagram includes Shape;
@@ -283,6 +332,7 @@ Object Type is identified by Vocabulary and Name where
283
332
  Object Type belongs to one Vocabulary,
284
333
  Object Type is called one Name;
285
334
  Instance is of one Object Type;
335
+ Mapping represents one Object Type;
286
336
  Object Type is an instance of one Concept;
287
337
  Object Type uses at most one Pronoun;
288
338
  Object Type plays Role,
@@ -374,8 +424,8 @@ Allowed Range is where
374
424
  Domain Object Type is a kind of Object Type;
375
425
 
376
426
  Entity Type is a kind of Domain Object Type;
377
- Entity Type nests at most one Fact Type,
378
- Fact Type is nested as at most one Entity Type;
427
+ Entity Type objectifies at most one Fact Type,
428
+ Fact Type is objectified as at most one Entity Type;
379
429
 
380
430
  Type Inheritance is a kind of Fact Type identified by Subtype and Supertype where
381
431
  Entity Type (as Subtype) is subtype of super-Entity Type (as Supertype) [acyclic, intransitive],
@@ -408,11 +458,14 @@ Value Type Parameter Restriction is where
408
458
  Value Type Parameter applies to Value Type;
409
459
  Value Type Parameter Restriction has one Value;
410
460
 
411
- Implicit Boolean Value Type is a kind of Value Type;
412
-
413
461
  /*
414
462
  * Constraints:
415
463
  */
464
+ either Component belongs to Mapping(1) or Component is a Mapping(2) that projects Composite but not both;
465
+ for each Component exactly one of these holds:
466
+ Component is a Mapping,
467
+ Component is an Indicator,
468
+ Component is a Discriminator;
416
469
  for each Concept exactly one of these holds:
417
470
  Object Type is an instance of Concept,
418
471
  Fact Type is an instance of Concept,
@@ -424,12 +477,18 @@ for each Concept exactly one of these holds:
424
477
  Fact is an instance of Concept,
425
478
  Population is an instance of Concept,
426
479
  Query is an instance of Concept;
480
+ either Constraint Shape is a Ring Constraint Shape or Constraint Shape is a Value Constraint Shape but not both;
427
481
  for each Constraint exactly one of these holds:
428
482
  Constraint is a Set Constraint,
429
483
  Constraint is a Presence Constraint,
430
484
  Constraint is a Ring Constraint,
431
485
  Constraint is a Value Constraint;
432
486
  either Domain Object Type is a Value Type or Domain Object Type is an Entity Type but not both;
487
+ for each Mapping exactly one of these holds:
488
+ Mapping is an Absorption,
489
+ Mapping is an Injection,
490
+ Mapping is a Scoping,
491
+ Mapping projects Composite;
433
492
  for each Role Sequence exactly one of these holds:
434
493
  Role Sequence is for Reading,
435
494
  Presence Constraint covers Role Sequence,
@@ -451,6 +510,9 @@ either Unit is fundamental or Unit is derived from base-Unit but not both;
451
510
  either Value Constraint Shape is for Object Type Shape or Value Constraint Shape is for Role Display but not both;
452
511
  either Value Constraint allows Value Range or Value Constraint requires matching Regular Expression but not both;
453
512
  either Value Constraint constrains Value Type or Value Constraint applies to Role but not both;
513
+ for each Mapping at most one of these holds:
514
+ Mapping is an Absorption that flattens,
515
+ Mapping projects Composite;
454
516
  for each Instance at most one of these holds:
455
517
  Instance has Value,
456
518
  Instance objectifies Fact;
@@ -466,19 +528,32 @@ Step specifies Fact Type that contains Role
466
528
  Variable is for Object Type that plays Role
467
529
  if and only if
468
530
  Variable is restricted by Role of Step;
531
+ /*
532
+ Absorption traverses from parent Role that is played by Object Type and Mapping(1) is a kind of Component(2) and Mapping(1) is an Absorption
533
+ only if Member belongs to Mapping(2) that represents Object Type;
534
+ Absorption traverses to child Role that is played by Object Type and Absorption is a kind of Mapping(2)
535
+ only if Mapping(1) represents Object Type;
536
+ */
469
537
  Presence Constraint is preferred identifier
470
538
  only if Presence Constraint has max Frequency;
471
539
  Value Type has Scale
472
540
  only if Value Type has Length;
473
541
  Variable matches nesting over Step
474
542
  only if Variable is for Object Type and Step specifies Fact Type;
543
+ each Absorption(1) occurs at most one time in
544
+ Absorption(2) is matched by reverse Absorption(1);
475
545
  either Agreement was reached by Agent or Agreement was on Date;
546
+ // either Component projects Name or Component is some Absorption that flattens;
476
547
  each Concept occurs at most one time in
477
548
  Object Type is an instance of Concept;
478
549
  each Concept occurs at most one time in
479
550
  Population is an instance of Concept;
480
551
  each Concept occurs at most one time in
481
552
  Role is an instance of Concept;
553
+ each Discriminator occurs at least 2 times in
554
+ Discriminator distinguishes Role using Value;
555
+ each combination Discriminator, Value occurs one time in
556
+ Discriminator distinguishes Role using Value;
482
557
  each combination Entity Type(1), Type Inheritance occurs at most one time in
483
558
  Entity Type(1) is subtype of super Entity Type(2),
484
559
  Type Inheritance provides identification;
@@ -500,68 +575,3 @@ each combination Vocabulary, Name occurs at most one time in
500
575
  each combination Vocabulary, Name occurs at most one time in
501
576
  Unit is in Vocabulary,
502
577
  Unit is called Name;
503
-
504
- /*
505
- * Compositions
506
- */
507
- Component is identified by Guid where
508
- Component has one Guid,
509
- Guid is of at most one Component;
510
- Component projects at most one Name;
511
-
512
- Composition is identified by Guid where
513
- Composition has one Guid,
514
- Guid is of at most one Composition;
515
- Composition is called one Name,
516
- Name is of at most one Composition;
517
-
518
- Discriminator is a kind of Component;
519
-
520
- Indicator is a kind of Component;
521
- Indicator indicates one Role played;
522
-
523
- Mapping is a kind of Component;
524
- Mapping contains Component (as Member) [acyclic, stronglyintransitive],
525
- Member belongs to at most one parent-Mapping;
526
- Mapping represents one Object Type;
527
-
528
- Nesting is a kind of Mapping;
529
-
530
- Absorption is a kind of Mapping;
531
- Absorption traverses to one child-Role;
532
- Absorption is nested under at most one index-Role;
533
- Absorption traverses from one parent-Role;
534
- Absorption flattens;
535
-
536
- Composite is identified by Mapping where
537
- Mapping projects at most one Composite,
538
- Composite consists of one Mapping;
539
- Composition contains Composite,
540
- Composite belongs to one Composition;
541
-
542
- Discriminated Role is where
543
- Discriminator distinguishes Role using one Value,
544
- Role is indicated by Value for Discriminator;
545
-
546
- Injection is a kind of Mapping;
547
- for each Component exactly one of these holds:
548
- that Component is a Mapping,
549
- that Component is an Indicator,
550
- that Component is a Discriminator;
551
- either Component is a Mapping(2) that projects some Composite or Component belongs to parent Mapping(1) but not both;
552
- for each Mapping exactly one of these holds:
553
- that Mapping is an Absorption,
554
- that Mapping is an Injection,
555
- that Mapping is a Nesting,
556
- that Mapping projects some Composite;
557
- for each Mapping at most one of these holds:
558
- that Mapping projects some Composite,
559
- that Mapping is an Absorption and that Absorption flattens;
560
- some Mapping represents some Object Type
561
- if and only if
562
- that Mapping is an Absorption that traverses to some child- Role and that Role is played by that Object Type;
563
- either Component projects some Name or that Component flattens;
564
- each Discriminator occurs at least 2 times in
565
- Discriminator distinguishes Role using Value;
566
- each combination Discriminator, Value occurs one time in
567
- Discriminator distinguishes Role using Value;
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
data/images/Units.png ADDED
Binary file
Binary file
Binary file
@@ -232,27 +232,10 @@ module ActiveFacts
232
232
  end.flatten.compact.uniq
233
233
  end
234
234
 
235
- def implicit_boolean_type vocabulary
236
- @constellation.ImplicitBooleanValueType[[vocabulary.identifying_role_values, "_ImplicitBooleanValueType"]] or
237
- @constellation.ImplicitBooleanValueType(vocabulary.identifying_role_values, "_ImplicitBooleanValueType", :concept => [:new, :implication_rule => 'unary'])
238
- end
239
-
240
- # This entity type has just objectified a fact type. Create the necessary ImplicitFactTypes with phantom roles
241
- def create_implicit_fact_type_for_unary
242
- role = all_role.single
243
- return if role.link_fact_type # Already exists
244
- # NORMA doesn't create an implicit fact type here, rather the fact type has an implicit extra role, so looks like a binary
245
- # We only do it when the unary fact type is not objectified
246
- link_fact_type = @constellation.LinkFactType(:new, :implying_role => role)
247
- link_fact_type.concept.implication_rule = 'unary'
248
- entity_type = @entity_type || implicit_boolean_type(role.object_type.vocabulary)
249
- phantom_role = @constellation.Role(link_fact_type, 0, :object_type => entity_type, :concept => :new)
250
- end
251
-
252
235
  def reading_preferably_starting_with_role role, negated = false
253
236
  all_reading_by_ordinal.detect do |reading|
254
- reading.text =~ /\{\d\}/ and
255
- reading.role_sequence.all_role_ref_in_order[$1.to_i].role == role and
237
+ reading.text =~ /\{(\d+)\}/ and
238
+ reading.role_sequence.all_role_ref_in_order[$1.to_i].role.base_role == role and
256
239
  reading.is_negative == !!negated
257
240
  end || preferred_reading(negated)
258
241
  end
@@ -273,28 +256,19 @@ module ActiveFacts
273
256
  end
274
257
 
275
258
  class Role
276
- def describe(highlight = nil)
277
- object_type.name + (self == highlight ? "*" : "")
259
+ # Mirror Role defines this, but it's more convenient not to have to type-check.
260
+ # A Role that's not a Mirror Role is its own base role.
261
+ def base_role
262
+ self
278
263
  end
279
264
 
280
- # Find any internal uniqueness constraint on this role only
281
- def uniqueness_constraint
282
- all_role_ref.detect{|rr|
283
- rs = rr.role_sequence
284
- rs.all_role_ref.size == 1 and
285
- rs.all_presence_constraint.each{|pc|
286
- return pc if pc.max_frequency == 1
287
- }
288
- }
289
- nil
290
- end
291
-
292
- # Is there are internal uniqueness constraint on this role only?
293
- def unique
294
- uniqueness_constraint ? true : false
265
+ def describe(highlight = nil)
266
+ object_type.name + (self == highlight ? "*" : "")
295
267
  end
296
268
 
297
269
  def is_mandatory
270
+ # REVISIT: An objectification role is always mandatory
271
+ # REVISIT: Handle mirror roles
298
272
  return fact_type.implying_role.is_mandatory if fact_type.is_a?(LinkFactType)
299
273
  all_role_ref.detect{|rr|
300
274
  rs = rr.role_sequence
@@ -312,18 +286,31 @@ module ActiveFacts
312
286
  # Return true if this role is functional (has only one instance wrt its player)
313
287
  # A role in an objectified fact type is deemed to refer to the implicit role of the objectification.
314
288
  def is_functional
289
+ # REVISIT: Handle mirror and objectification roles
315
290
  fact_type.entity_type or
316
291
  fact_type.all_role.size != 2 or
317
- is_unique
292
+ uniqueness_constraint
318
293
  end
319
294
 
320
- def is_unique
321
- all_role_ref.detect do |rr|
322
- rr.role_sequence.all_role_ref.size == 1 and
323
- rr.role_sequence.all_presence_constraint.detect do |pc|
295
+ # Find any internal uniqueness constraint on this role only
296
+ def uniqueness_constraint
297
+ base_role.all_role_ref.detect{|rr|
298
+ rs = rr.role_sequence
299
+ rs.all_role_ref.size == 1 and
300
+ rs.all_presence_constraint.detect do |pc|
324
301
  pc.max_frequency == 1 and !pc.enforcement # Alethic uniqueness constraint
325
302
  end
326
- end
303
+ }
304
+ end
305
+
306
+ # Is there are internal uniqueness constraint on this role only?
307
+ def is_unique
308
+ # REVISIT: Handle objectification roles
309
+ uniqueness_constraint ? true : false
310
+ end
311
+
312
+ def unique
313
+ raise "REVISIT: unique is deprecated. Call is_unique instead"
327
314
  end
328
315
 
329
316
  def name
@@ -346,7 +333,7 @@ module ActiveFacts
346
333
  name_array =
347
334
  if role.fact_type.all_role.size == 1
348
335
  if role.fact_type.is_a?(LinkFactType)
349
- "#{role.object_type.name} phantom for #{role.fact_type.role.object_type.name}"
336
+ "#{role.object_type.name} objectification role for #{role.fact_type.role.object_type.name}"
350
337
  else
351
338
  role.fact_type.preferred_reading.text.gsub(/\{[0-9]\}/,'').strip.split(/\s/)
352
339
  end
@@ -516,7 +503,7 @@ module ActiveFacts
516
503
  all_supertypes = supertypes_transitive
517
504
  trace :pi, "PI roles must be played by one of #{all_supertypes.map(&:name)*", "}" if all_supertypes.size > 1
518
505
  all_role.each{|role|
519
- next unless role.unique || fact_type
506
+ next unless role.is_unique || fact_type
520
507
  ftroles = Array(role.fact_type.all_role)
521
508
 
522
509
  # Skip roles in ternary and higher fact types, they're objectified, and in unaries, they can't identify us.
@@ -604,7 +591,7 @@ module ActiveFacts
604
591
  pi = possible_pi
605
592
  end
606
593
  else
607
- byebug
594
+ debugger
608
595
  trace :pi, "No PI found for #{name}"
609
596
  end
610
597
  end
@@ -663,15 +650,69 @@ module ActiveFacts
663
650
  candidates[0] # REVISIT: This might not be the closest supertype
664
651
  end
665
652
 
666
- # This entity type has just objectified a fact type. Create the necessary ImplicitFactTypes with phantom roles
667
- def create_implicit_fact_types
653
+ def add_supertype(supertype, is_identifying_supertype, assimilation)
654
+ inheritance_fact = constellation.TypeInheritance(self, supertype, :concept => :new)
655
+
656
+ inheritance_fact.assimilation = assimilation
657
+
658
+ # Create a reading:
659
+ sub_role = constellation.Role(inheritance_fact, 0, :object_type => self, :concept => :new)
660
+ super_role = constellation.Role(inheritance_fact, 1, :object_type => supertype, :concept => :new)
661
+
662
+ rs = constellation.RoleSequence(:new)
663
+ constellation.RoleRef(rs, 0, :role => sub_role)
664
+ constellation.RoleRef(rs, 1, :role => super_role)
665
+ constellation.Reading(inheritance_fact, 0, :role_sequence => rs, :text => "{0} is a kind of {1}", :is_negative => false)
666
+
667
+ rs2 = constellation.RoleSequence(:new)
668
+ constellation.RoleRef(rs2, 0, :role => super_role)
669
+ constellation.RoleRef(rs2, 1, :role => sub_role)
670
+ # Decide in which order to include is a/is an. Provide both, but in order.
671
+ n = 'aeioh'.include?(sub_role.object_type.name.downcase[0]) ? 'n' : ''
672
+ constellation.Reading(inheritance_fact, 2, :role_sequence => rs2, :text => "{0} is a#{n} {1}", :is_negative => false)
673
+
674
+ if is_identifying_supertype
675
+ inheritance_fact.provides_identification = true
676
+ end
677
+
678
+ # Create uniqueness constraints over the subtyping fact type.
679
+ p1rs = constellation.RoleSequence(:new)
680
+ constellation.RoleRef(p1rs, 0).role = sub_role
681
+ pc1 = constellation.PresenceConstraint(:new, :vocabulary => vocabulary)
682
+ pc1.name = "#{name}MustHaveSupertype#{supertype.name}"
683
+ pc1.role_sequence = p1rs
684
+ pc1.is_mandatory = true # A subtype instance must have a supertype instance
685
+ pc1.min_frequency = 1
686
+ pc1.max_frequency = 1
687
+ pc1.is_preferred_identifier = false
688
+ trace :constraint, "Made new subtype PC GUID=#{pc1.concept.guid} min=1 max=1 over #{p1rs.describe}"
689
+
690
+ p2rs = constellation.RoleSequence(:new)
691
+ constellation.RoleRef(p2rs, 0).role = super_role
692
+ pc2 = constellation.PresenceConstraint(:new, :vocabulary => vocabulary)
693
+ pc2.name = "#{supertype.name}MayBeA#{name}"
694
+ pc2.role_sequence = p2rs
695
+ pc2.is_mandatory = false
696
+ pc2.min_frequency = 0
697
+ pc2.max_frequency = 1
698
+ # The supertype role often identifies the subtype:
699
+ pc2.is_preferred_identifier = inheritance_fact.provides_identification
700
+ trace :supertype, "identification of #{name} via supertype #{supertype.name} was #{inheritance_fact.provides_identification ? '' : 'not '}added"
701
+ trace :constraint, "Made new supertype PC GUID=#{pc2.concept.guid} min=1 max=1 over #{p2rs.describe}"
702
+ end
703
+
704
+ # This entity type has just objectified a fact type.
705
+ # Create the necessary ImplicitFactTypes with objectification and mirror roles
706
+ def create_link_fact_types
668
707
  fact_type.all_role.map do |role|
669
- next if role.link_fact_type # Already exists
708
+ next if role.mirror_role_as_base_role # Already exists
670
709
  link_fact_type = @constellation.LinkFactType(:new, :implying_role => role)
671
- link_fact_type.concept.implication_rule = 'objectification'
672
- phantom_role = @constellation.Role(link_fact_type, 0, :object_type => self, :concept => :new)
673
- # We could create a copy of the visible external role here, but there's no need yet...
674
- # Nor is there a need for a presence constraint, readings, etc.
710
+ objectification_role = @constellation.Role(link_fact_type, 0, :object_type => self, :concept => :new)
711
+ mirror_role = @constellation.MirrorRole(link_fact_type, 1, :concept => :new, :object_type => role.object_type, :base_role => role)
712
+
713
+ link_fact_type.concept.implication_rule =
714
+ objectification_role.concept.implication_rule =
715
+ mirror_role.concept.implication_rule = 'objectification'
675
716
  link_fact_type
676
717
  end
677
718
  end
@@ -968,10 +1009,6 @@ module ActiveFacts
968
1009
  def is_objectification_step
969
1010
  !!objectification_variable
970
1011
  end
971
-
972
- def external_fact_type
973
- fact_type.is_a?(LinkFactType) ? fact_type.role.fact_type : fact_type
974
- end
975
1012
  end
976
1013
 
977
1014
  class Variable
@@ -1066,99 +1103,50 @@ module ActiveFacts
1066
1103
  end
1067
1104
 
1068
1105
  class LinkFactType
1069
- def default_reading
1070
- # There are two cases, where role is in a unary fact type, and where the fact type is objectified
1071
- # If a unary fact type is objectified, only the LinkFactType for the objectification is asserted
1072
- if objectification = implying_role.fact_type.entity_type
1073
- "#{objectification.name} involves #{implying_role.object_type.name}"
1074
- else
1075
- implying_role.fact_type.default_reading+" Boolean" # Must be a unary FT
1076
- end
1106
+ def all_reading
1107
+ if super.size == 0
1108
+ # REVISIT: Should we create reading orders independently?
1109
+ # No user-defined readings have been defined, so it's time to stop being lazy:
1110
+ objectification_role, mirror_role = *all_role_in_order
1111
+ rs = constellation.RoleSequence(:new)
1112
+ rr0 = constellation.RoleRef(rs, 0, :role => objectification_role)
1113
+ rr1 = constellation.RoleRef(rs, 1, :role => mirror_role)
1114
+ r0 = constellation.Reading(self, 0, :role_sequence => rs, :text => "{0} involves {1}", :is_negative => false) # REVISIT: This assumes English!
1115
+ r1 = constellation.Reading(self, 1, :role_sequence => rs, :text => "{1} is involved in {0}", :is_negative => false)
1116
+ end
1117
+ @all_reading
1077
1118
  end
1119
+ end
1078
1120
 
1079
- def add_reading implicit_reading
1080
- @readings ||= []
1081
- @readings << implicit_reading
1121
+ class MirrorRole
1122
+ def is_mandatory
1123
+ true # An objectified fact type must have a player for each role
1082
1124
  end
1083
1125
 
1084
- def all_reading
1085
- @readings ||=
1086
- [ ImplicitReading.new(
1087
- self,
1088
- implying_role.fact_type.entity_type ? "{0} involves {1}" : implying_role.fact_type.default_reading+" Boolean"
1089
- )
1090
- ] +
1091
- Array(implying_role.fact_type.entity_type ? ImplicitReading.new(self, "{1} is involved in {0}") : nil)
1126
+ def is_unique
1127
+ true # A mirror role exists is played exactly once for each objectification
1092
1128
  end
1093
1129
 
1094
- def reading_preferably_starting_with_role role, negated = false
1095
- all_reading[role == implying_role ? 1 : 0]
1130
+ def is_functional
1131
+ true
1096
1132
  end
1097
1133
 
1098
- # This is only used for debugging, from RoleRef#describe
1099
- class ImplicitReading
1100
- attr_accessor :fact_type, :text
1101
- attr_reader :is_negative # Never true
1102
-
1103
- def initialize(fact_type, text)
1104
- @fact_type = fact_type
1105
- @text = text
1106
- end
1107
-
1108
- class ImplicitReadingRoleSequence
1109
- class ImplicitReadingRoleRef
1110
- attr_reader :role
1111
- attr_reader :role_sequence
1112
- def initialize(role, role_sequence)
1113
- @role = role
1114
- @role_sequence = role_sequence
1115
- end
1116
- def variable; nil; end
1117
- def play; nil; end
1118
- def leading_adjective; nil; end
1119
- def trailing_adjective; nil; end
1120
- def describe
1121
- @role.object_type.name
1122
- end
1123
- end
1124
-
1125
- def initialize roles
1126
- @role_refs = roles.map{|role| ImplicitReadingRoleRef.new(role, self) }
1127
- end
1128
-
1129
- def all_role_ref
1130
- @role_refs
1131
- end
1132
- def all_role_ref_in_order
1133
- @role_refs
1134
- end
1135
- def describe
1136
- '('+@role_refs.map(&:describe)*', '+')'
1137
- end
1138
- def all_reading
1139
- []
1140
- end
1141
- end
1142
-
1143
- def role_sequence
1144
- ImplicitReadingRoleSequence.new([@fact_type.implying_role, @fact_type.all_role.single])
1145
- end
1146
-
1147
- def ordinal; 0; end
1134
+ def uniqueness_constraint
1135
+ raise "A MirrorRole should not be asked for its uniqueness constraints"
1136
+ end
1148
1137
 
1149
- def expand
1150
- text.gsub(/\{([01])\}/) do
1151
- if $1 == '0'
1152
- fact_type.all_role[0].object_type.name
1153
- else
1154
- fact_type.implying_role.object_type.name
1155
- end
1156
- end
1157
- end
1138
+ %w{all_ring_constraint_as_other_role all_ring_constraint all_role_value role_value_constraint mirror_role_as_base_role
1139
+ }.each do |accessor|
1140
+ define_method(accessor.to_sym) do
1141
+ base_role.send(accessor.to_sym)
1142
+ end
1143
+ define_method("#{accessor}=".to_sym) do |*a|
1144
+ raise "REVISIT: It's a bad idea to try to set #{accessor} for a MirrorRole"
1145
+ end
1158
1146
  end
1159
1147
  end
1160
1148
 
1161
- # Some queries must be over the proximate roles, some over the counterpart roles.
1149
+ # Some queries in constraints must be over the proximate roles, some over the counterpart roles.
1162
1150
  # Return the common superclass of the appropriate roles, and the actual roles
1163
1151
  def self.plays_over roles, options = :both # Or :proximate, :counterpart
1164
1152
  # If we can stay inside this objectified FT, there's no query:
@@ -1204,7 +1192,8 @@ module ActiveFacts
1204
1192
  if fact_type.entity_type
1205
1193
  objectification_role_supertypes =
1206
1194
  fact_type.entity_type.supertypes_transitive+object_type.supertypes_transitive
1207
- objectification_role = role.link_fact_type.all_role.single # Find the phantom role here
1195
+ # Find the objectification role here:
1196
+ objectification_role = role.link_fact_type.all_role.detect{|r| !r.is_a?(MirrorRole)}
1208
1197
  else
1209
1198
  objectification_role_supertypes = counterpart_role_supertypes
1210
1199
  objectification_role = counterpart_role