activefacts 0.7.2 → 0.7.3

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.
Files changed (67) hide show
  1. data/Manifest.txt +1 -0
  2. data/Rakefile +3 -0
  3. data/bin/afgen +9 -3
  4. data/bin/cql +0 -0
  5. data/examples/CQL/Address.cql +7 -7
  6. data/examples/CQL/Blog.cql +8 -8
  7. data/examples/CQL/CompanyDirectorEmployee.cql +3 -3
  8. data/examples/CQL/Death.cql +2 -2
  9. data/examples/CQL/Genealogy.cql +21 -21
  10. data/examples/CQL/Marriage.cql +1 -1
  11. data/examples/CQL/Metamodel.cql +34 -29
  12. data/examples/CQL/MultiInheritance.cql +3 -3
  13. data/examples/CQL/OilSupply.cql +9 -9
  14. data/examples/CQL/Orienteering.cql +27 -27
  15. data/examples/CQL/PersonPlaysGame.cql +2 -2
  16. data/examples/CQL/SchoolActivities.cql +3 -3
  17. data/examples/CQL/SimplestUnary.cql +1 -1
  18. data/examples/CQL/SubtypePI.cql +4 -4
  19. data/examples/CQL/Warehousing.cql +12 -12
  20. data/examples/CQL/WindowInRoomInBldg.cql +4 -4
  21. data/lib/activefacts/api/concept.rb +3 -2
  22. data/lib/activefacts/api/constellation.rb +1 -1
  23. data/lib/activefacts/api/entity.rb +12 -1
  24. data/lib/activefacts/api/instance.rb +1 -1
  25. data/lib/activefacts/api/role.rb +1 -1
  26. data/lib/activefacts/api/standard_types.rb +9 -1
  27. data/lib/activefacts/api/support.rb +4 -0
  28. data/lib/activefacts/api/value.rb +1 -0
  29. data/lib/activefacts/api/vocabulary.rb +2 -59
  30. data/lib/activefacts/cql/DataTypes.treetop +10 -1
  31. data/lib/activefacts/cql/Expressions.treetop +1 -1
  32. data/lib/activefacts/cql/FactTypes.treetop +1 -1
  33. data/lib/activefacts/cql/Language/English.treetop +2 -2
  34. data/lib/activefacts/generate/absorption.rb +0 -2
  35. data/lib/activefacts/generate/cql.rb +6 -8
  36. data/lib/activefacts/generate/cql/html.rb +1 -1
  37. data/lib/activefacts/generate/oo.rb +60 -40
  38. data/lib/activefacts/generate/ordered.rb +30 -21
  39. data/lib/activefacts/generate/ruby.rb +38 -15
  40. data/lib/activefacts/generate/sql/mysql.rb +257 -0
  41. data/lib/activefacts/generate/sql/server.rb +0 -1
  42. data/lib/activefacts/input/cql.rb +0 -2
  43. data/lib/activefacts/persistence/columns.rb +51 -24
  44. data/lib/activefacts/persistence/concept.rb +158 -36
  45. data/lib/activefacts/persistence/reference.rb +13 -8
  46. data/lib/activefacts/support.rb +40 -2
  47. data/lib/activefacts/version.rb +1 -1
  48. data/lib/activefacts/vocabulary/extensions.rb +5 -6
  49. data/spec/absorption_spec.rb +8 -11
  50. data/spec/api/autocounter.rb +1 -1
  51. data/spec/api/constellation.rb +1 -1
  52. data/spec/api/entity_type.rb +1 -1
  53. data/spec/api/instance.rb +1 -1
  54. data/spec/api/roles.rb +1 -1
  55. data/spec/api/value_type.rb +1 -1
  56. data/spec/cql_cql_spec.rb +2 -4
  57. data/spec/cql_parse_spec.rb +2 -4
  58. data/spec/cql_ruby_spec.rb +2 -4
  59. data/spec/cql_sql_spec.rb +4 -4
  60. data/spec/cql_symbol_tables_spec.rb +1 -1
  61. data/spec/cql_unit_spec.rb +6 -6
  62. data/spec/cqldump_spec.rb +6 -6
  63. data/spec/norma_cql_spec.rb +2 -4
  64. data/spec/norma_ruby_spec.rb +2 -4
  65. data/spec/norma_sql_spec.rb +2 -4
  66. data/spec/norma_tables_spec.rb +4 -7
  67. metadata +29 -6
@@ -3,18 +3,18 @@ vocabulary Warehousing;
3
3
  /*
4
4
  * Value Types
5
5
  */
6
- BinID is defined as AutoCounter();
7
- DispatchID is defined as AutoCounter();
8
- DispatchItemID is defined as AutoCounter();
9
- PartyID is defined as AutoCounter();
10
- ProductID is defined as AutoCounter();
11
- PurchaseOrderID is defined as AutoCounter();
12
- Quantity is defined as UnsignedInteger(32);
13
- ReceiptID is defined as AutoCounter();
14
- ReceivedItemID is defined as AutoCounter();
15
- SalesOrderID is defined as AutoCounter();
16
- TransferRequestID is defined as AutoCounter();
17
- WarehouseID is defined as AutoCounter();
6
+ BinID is written as AutoCounter();
7
+ DispatchID is written as AutoCounter();
8
+ DispatchItemID is written as AutoCounter();
9
+ PartyID is written as AutoCounter();
10
+ ProductID is written as AutoCounter();
11
+ PurchaseOrderID is written as AutoCounter();
12
+ Quantity is written as UnsignedInteger(32);
13
+ ReceiptID is written as AutoCounter();
14
+ ReceivedItemID is written as AutoCounter();
15
+ SalesOrderID is written as AutoCounter();
16
+ TransferRequestID is written as AutoCounter();
17
+ WarehouseID is written as AutoCounter();
18
18
 
19
19
  /*
20
20
  * Entity Types
@@ -3,10 +3,10 @@ vocabulary WindowInRoomInBldg;
3
3
  /*
4
4
  * Value Types
5
5
  */
6
- Building is defined as SignedInteger(32);
7
- RoomNumber is defined as SignedInteger(32);
8
- WallNumber is defined as SignedInteger(32);
9
- WindowNumber is defined as UnsignedInteger(32);
6
+ Building is written as SignedInteger(32);
7
+ RoomNumber is written as SignedInteger(32);
8
+ WallNumber is written as SignedInteger(32);
9
+ WindowNumber is written as UnsignedInteger(32);
10
10
 
11
11
  /*
12
12
  * Entity Types
@@ -172,7 +172,7 @@ module ActiveFacts
172
172
  def define_binary_fact_type(one_to_one, role_name, related, mandatory, related_role_name)
173
173
  # puts "#{self}.#{role_name} is to #{related.inspect}, #{mandatory ? :mandatory : :optional}, related role is #{related_role_name}"
174
174
 
175
- raise "#{self.class.basename} cannot have more than one role named #{role_name}" if roles[role_name]
175
+ raise "#{name} cannot have more than one role named #{role_name}" if roles[role_name]
176
176
  roles[role_name] = role = Role.new(self, related, nil, role_name, mandatory)
177
177
 
178
178
  # There may be a forward reference here where role_name is a Symbol,
@@ -350,6 +350,7 @@ module ActiveFacts
350
350
 
351
351
  # resolve the Symbol to a Class now if possible:
352
352
  resolved = vocabulary.concept(related) rescue nil
353
+ #puts "#{related} resolves to #{resolved}"
353
354
  related = resolved if resolved
354
355
  # puts "related = #{related.inspect}"
355
356
 
@@ -399,7 +400,7 @@ module ActiveFacts
399
400
  when String # Arrange for this to happen later
400
401
  vocabulary.__delay(concept, args, &block)
401
402
  else
402
- raise "Delayed binding not possible for #{concept.inspect}"
403
+ raise "Delayed binding not possible for #{concept.class.name} #{concept.inspect}"
403
404
  end
404
405
  end
405
406
  end
@@ -79,7 +79,7 @@ module ActiveFacts
79
79
 
80
80
  # REVISIT: It would be better not to rely on the role name pattern here:
81
81
  single_roles, multiple_roles = klass.roles.keys.sort_by(&:to_s).partition{|r| r.to_s !~ /\Aall_/ }
82
- single_roles -= klass.identifying_role_names if (klass.respond_to?(:identifying_role_names))
82
+ single_roles -= klass.identifying_role_names if (klass.is_entity_type)
83
83
  # REVISIT: Need to include superclass roles also.
84
84
 
85
85
  instances = send(concept.to_sym)
@@ -109,6 +109,16 @@ module ActiveFacts
109
109
  @identifying_role_names ||= []
110
110
  end
111
111
 
112
+ def identifying_roles
113
+ debug :persistence, "Identifying roles for #{basename}" do
114
+ @identifying_role_names.map{|name|
115
+ role = roles[name] || (!superclass.is_entity_type || superclass.roles[name])
116
+ debug :persistence, "#{name} -> #{role ? "found" : "NOT FOUND"}"
117
+ role
118
+ }
119
+ end
120
+ end
121
+
112
122
  # Convert the passed arguments into an array of Instance objects that can identify an instance of this Entity type:
113
123
  def identifying_role_values(*args)
114
124
  #puts "Getting identifying role values #{identifying_role_names.inspect} of #{basename} using #{args.inspect}"
@@ -211,7 +221,7 @@ module ActiveFacts
211
221
  # inherited from a superclass.
212
222
  def initialise_entity_type(*args) #:nodoc:
213
223
  #puts "Initialising entity type #{self} using #{args.inspect}"
214
- @identifying_role_names = superclass.identifying_role_names if superclass.respond_to?(:identifying_role_names)
224
+ @identifying_role_names = superclass.identifying_role_names if superclass.is_entity_type
215
225
  # REVISIT: @identifying_role_names here are the symbols passed in, not the Role objects we should use.
216
226
  # We'd need late binding to use Role objects...
217
227
  @identifying_role_names = args if args.size > 0 || !@identifying_role_names
@@ -235,6 +245,7 @@ module ActiveFacts
235
245
 
236
246
  # Register ourselves with the parent module, which has become a Vocabulary:
237
247
  vocabulary = other.modspace
248
+ # puts "Entity.included(#{other.inspect})"
238
249
  unless vocabulary.respond_to? :concept # Extend module with Vocabulary if necessary
239
250
  vocabulary.send :extend, Vocabulary
240
251
  end
@@ -14,7 +14,7 @@ module ActiveFacts
14
14
  attr_accessor :constellation
15
15
 
16
16
  def initialize(args = []) #:nodoc:
17
- unless (self.class.respond_to?(:identifying_role_names))
17
+ unless (self.class.is_entity_type)
18
18
  #if (self.class.superclass != Object)
19
19
  # puts "constructing #{self.class.superclass} with #{args.inspect}"
20
20
  super(*args)
@@ -30,7 +30,7 @@ module ActiveFacts
30
30
  @name = name
31
31
  @mandatory = mandatory
32
32
  @unique = unique
33
- @is_identifying = @owner.respond_to?(:identifying_role_names) && @owner.identifying_role_names.include?(@name)
33
+ @is_identifying = @owner.is_entity_type && @owner.identifying_role_names.include?(@name)
34
34
  end
35
35
 
36
36
  # Is this role a unary (created by maybe)? If so, it has no counterpart
@@ -43,10 +43,14 @@ class Class
43
43
  # Make this Class into a Concept and if necessary its module into a Vocabulary.
44
44
  # The parameters are the names (Symbols) of the identifying roles.
45
45
  def identified_by *args
46
- raise "not an entity type" if respond_to? :value_type # Don't make a ValueType into an EntityType
46
+ raise "#{basename} is not an entity type" if respond_to? :value_type # Don't make a ValueType into an EntityType
47
47
  include ActiveFacts::API::Entity
48
48
  initialise_entity_type(*args)
49
49
  end
50
+
51
+ def is_entity_type
52
+ respond_to?(:identifying_role_names)
53
+ end
50
54
  end
51
55
 
52
56
  # REVISIT: Fix these NORMA types
@@ -56,10 +60,14 @@ class SignedInteger < Int #:nodoc:
56
60
  end
57
61
  class SignedSmallInteger < Int #:nodoc:
58
62
  end
63
+ class SignedTinyInteger < Int #:nodoc:
64
+ end
59
65
  class UnsignedInteger < Int #:nodoc:
60
66
  end
61
67
  class UnsignedSmallInteger < Int #:nodoc:
62
68
  end
69
+ class UnsignedTinyInteger < Int #:nodoc:
70
+ end
63
71
  class LargeLengthText < String #:nodoc:
64
72
  end
65
73
  class FixedLengthText < String #:nodoc:
@@ -23,6 +23,10 @@ class String #:nodoc:
23
23
  def snakecase
24
24
  gsub(/([a-z])([A-Z])/,'\1_\2').downcase
25
25
  end
26
+
27
+ def camelwords
28
+ gsub(/([a-z])([A-Z])/,'\1_\2').split(/[_\s]+/)
29
+ end
26
30
  end
27
31
 
28
32
  class Module #:nodoc:
@@ -118,6 +118,7 @@ module ActiveFacts
118
118
 
119
119
  # Register ourselves with the parent module, which has become a Vocabulary:
120
120
  vocabulary = other.modspace
121
+ # puts "ValueType.included(#{other.inspect})"
121
122
  unless vocabulary.respond_to? :concept # Extend module with Vocabulary if necessary
122
123
  vocabulary.send :extend, Vocabulary
123
124
  end
@@ -28,7 +28,7 @@ module ActiveFacts
28
28
  __bind(camel)
29
29
  return c
30
30
  end
31
- return (const_get(camel) rescue nil)
31
+ return (const_get("#{name}::#{camel}") rescue nil)
32
32
  end
33
33
 
34
34
  def add_concept(klass) #:nodoc:
@@ -48,6 +48,7 @@ module ActiveFacts
48
48
  # __bind raises an error if the named class doesn't exist yet.
49
49
  def __bind(concept_name) #:nodoc:
50
50
  concept = const_get(concept_name)
51
+ # puts "#{name}.__bind #{concept_name} -> #{concept.name}" if concept
51
52
  if (@delayed && @delayed.include?(concept_name))
52
53
  # $stderr.puts "#{concept_name} was delayed, binding now"
53
54
  d = @delayed[concept_name]
@@ -67,64 +68,6 @@ module ActiveFacts
67
68
  }*"\n\t"
68
69
  end
69
70
 
70
- =begin
71
- # Create or find an instance of klass in constellation using value to identify it
72
- def adopt(klass, constellation, value) #:nodoc:
73
- puts "Adopting #{ value.verbalise rescue value.class.to_s+' '+value.inspect} as #{klass} into constellation #{constellation.object_id}"
74
-
75
- path = "unknown"
76
- # Create a value instance we can hack if the value isn't already in this constellation
77
- if (c = constellation)
78
- if value.is_a?(klass) # Right class?
79
- value = value.__getobj__ if RoleProxy === value
80
- vc = value.constellation rescue nil
81
- if (c == vc) # Right constellation?
82
- # Already right class, in the right constellation
83
- path = "right constellation, right class, just use it"
84
- else
85
- # We need a new object from our constellation, so copy the value.
86
- if klass.respond_to?(:identifying_role_names)
87
- # Make a new entity having only the identifying roles set.
88
- # Someone will complain that this is wrong, and all functional role values should also
89
- # be cloned, and I'm listening... but not there yet. Why just those?
90
- cloned = c.send(
91
- :"#{klass.basename}",
92
- *klass.identifying_role_names.map{|role| value.send(role) }
93
- )
94
- path = "wrong constellation, right class, cloned entity"
95
- else
96
- # Just copy a value:
97
- cloned = c.send(:"#{klass.basename}", *value)
98
- path = "wrong constellation, right class, copied value"
99
- end
100
- value.constellation = c
101
- end
102
- else
103
- # Wrong class, assume it's a valid constructor arg. Get our constellation to find/make it:
104
- value = [ value ] unless Array === value
105
- value = c.send(:"#{klass.basename}", *value)
106
- path = "right constellation but wrong class, constructed from args"
107
- end
108
- else
109
- # This object's not in a constellation
110
- if value.is_a?(klass) # Right class?
111
- value = value.__getobj__ if RoleProxy === value
112
- if vc = value.constellation rescue nil
113
- raise "REVISIT: Assigning to #{self.class.basename}.#{role_name} with constellation=#{c.inspect}: Can't dis-associate object from its constellation #{vc.object_id} yet"
114
- end
115
- # Right class, no constellation, just use it
116
- path = "no constellation, correct class"
117
- else
118
- # Wrong class, construct one
119
- value = klass.send(:new, *value)
120
- path = "no constellation, constructed from wrong class"
121
- end
122
- end
123
- # print "#{path}"; puts ", adopted as #{value.verbalise rescue value.inspect}"
124
- value
125
- end
126
- =end
127
-
128
71
  end
129
72
  end
130
73
  end
@@ -9,7 +9,7 @@ module ActiveFacts
9
9
  grammar DataTypes
10
10
  rule data_type
11
11
  s name:id
12
- ( s '=' s / defined_as )
12
+ ( s '=' s / written_as )
13
13
  base:id s
14
14
  '(' s tpl:type_parameter_list? ')' s
15
15
  u0:(!restricted u1:unit s)?
@@ -34,6 +34,15 @@ module ActiveFacts
34
34
  }
35
35
  end
36
36
 
37
+ rule unit_definition
38
+ numerator:number denominator:(s '/' s number)?
39
+ baseunits:(unit+ (s '/' s unit+)?) s
40
+ offset:(('+' / '-') s number)
41
+ 'makes' s ('approximately' s)?
42
+ singular:id plural:(s '/' s id)?
43
+ (s '[' s 'ephemeral' s ']')?
44
+ end
45
+
37
46
  rule type_parameter_list
38
47
  head:number s tail:( ',' s number s )*
39
48
  {
@@ -88,7 +88,7 @@ module ActiveFacts
88
88
  end
89
89
 
90
90
  rule variable
91
- id0:id o0:( !defined_as s id1:id )?
91
+ id0:id o0:( !written_as s id1:id )?
92
92
  {
93
93
  def value
94
94
  # Variable names may consist of one or two words (optional adjective and a noun):
@@ -8,7 +8,7 @@ module ActiveFacts
8
8
  module CQL
9
9
  grammar FactTypes
10
10
  rule named_fact_type
11
- s name:id ( s '=' s / defined_as / s is s where )
11
+ s name:id ( s '=' s / written_as / s is s where )
12
12
  anonymous_fact_type
13
13
  {
14
14
  def value
@@ -12,8 +12,8 @@ module ActiveFacts
12
12
  'is' s 'a' s ('kind'/'subtype') s 'of' S
13
13
  end
14
14
 
15
- rule defined_as
16
- s 'is' s 'defined' S as s
15
+ rule written_as
16
+ s 'is' s 'written' S as s
17
17
  end
18
18
 
19
19
  rule identified_by
@@ -19,8 +19,6 @@ module ActiveFacts
19
19
  # * no_identifier Don't show the identified_by columns for an EntityType
20
20
 
21
21
  class ABSORPTION
22
- include Metamodel
23
-
24
22
  def initialize(vocabulary, *options) #:nodoc:
25
23
  @vocabulary = vocabulary
26
24
  @vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
@@ -14,8 +14,6 @@ module ActiveFacts
14
14
  # afgen --cql <file>.cql
15
15
  class CQL < OrderedDumper
16
16
  private
17
- include Metamodel
18
-
19
17
  def vocabulary_start(vocabulary)
20
18
  puts "vocabulary #{vocabulary.name};\n\n"
21
19
  end
@@ -46,7 +44,7 @@ module ActiveFacts
46
44
 
47
45
  #" restricted to {#{(allowed_values.map{|r| r.inspect}*", ").gsub('"',"'")}}")
48
46
 
49
- puts "#{o.name} is defined as #{o.supertype.name}#{ parameters }#{
47
+ puts "#{o.name} is written as #{o.supertype.name}#{ parameters }#{
50
48
  o.value_restriction ? " restricted to {#{
51
49
  o.value_restriction.all_allowed_range.sort_by{|ar|
52
50
  ((min = ar.value_range.minimum_bound) && min.value) ||
@@ -342,7 +340,7 @@ module ActiveFacts
342
340
  end
343
341
  #puts "#{c.class.basename} has players #{players.map{|p| p.name}*", "}"
344
342
 
345
- if (SetEqualityConstraint === c)
343
+ if c.is_a?(ActiveFacts::Metamodel::SetEqualityConstraint)
346
344
  # REVISIT: Need a proper approach to some/that and adjective disambiguation:
347
345
  puts \
348
346
  scrs.map{|scr|
@@ -436,13 +434,13 @@ module ActiveFacts
436
434
 
437
435
  def constraint_dump(c)
438
436
  case c
439
- when PresenceConstraint
437
+ when ActiveFacts::Metamodel::PresenceConstraint
440
438
  dump_presence_constraint(c)
441
- when RingConstraint
439
+ when ActiveFacts::Metamodel::RingConstraint
442
440
  dump_ring_constraint(c)
443
- when SetComparisonConstraint # includes SetExclusionConstraint, SetEqualityConstraint
441
+ when ActiveFacts::Metamodel::SetComparisonConstraint # includes SetExclusionConstraint, SetEqualityConstraint
444
442
  dump_set_constraint(c)
445
- when SubsetConstraint
443
+ when ActiveFacts::Metamodel::SubsetConstraint
446
444
  dump_subset_constraint(c)
447
445
  else
448
446
  "#{c.class.basename} #{c.name}: unhandled constraint type"
@@ -72,7 +72,7 @@ module ActiveFacts
72
72
  ].compact
73
73
  parameters = parameters.length > 0 ? "("+parameters.join(",")+")" : "()"
74
74
 
75
- puts "#{concept o.name} #{keyword "is defined as"} #{concept o.supertype.name + parameters }#{
75
+ puts "#{concept o.name} #{keyword "is written as"} #{concept o.supertype.name + parameters }#{
76
76
  if (o.value_restriction)
77
77
  keyword("restricted to")+
78
78
  o.value_restriction.all_allowed_range.map{|ar|
@@ -11,8 +11,6 @@ module ActiveFacts
11
11
  module Generate
12
12
  # Base class for generators of object-oriented class libraries for an ActiveFacts vocabulary.
13
13
  class OO < OrderedDumper #:nodoc:
14
- include Metamodel
15
-
16
14
  def constraints_dump(constraints_used)
17
15
  # Stub, not needed.
18
16
  end
@@ -29,7 +27,7 @@ module ActiveFacts
29
27
  role.fact_type.all_role.size <= 2
30
28
  }.
31
29
  sort_by{|role|
32
- preferred_role_name(role.fact_type.all_role.select{|r2| r2 != role}[0] || role)
30
+ preferred_role_name(role.fact_type.all_role.select{|r2| r2 != role}[0] || role, o)
33
31
  }.each{|role|
34
32
  role_dump(role)
35
33
  }
@@ -38,54 +36,63 @@ module ActiveFacts
38
36
  def role_dump(role)
39
37
  fact_type = role.fact_type
40
38
  if fact_type.all_role.size == 1
41
- unary_dump(role, preferred_role_name(role))
39
+ unary_dump(role, preferred_role_name(role)) unless fact_type.entity_type
40
+ # REVISIT: If the objectified fact type has already been dumped, we'll get nothing.
42
41
  return
43
42
  elsif fact_type.all_role.size != 2
44
43
  return # ternaries and higher are always objectified
45
44
  end
46
45
 
47
46
  # REVISIT: TypeInheritance
48
- if TypeInheritance === fact_type
47
+ if fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
49
48
  # debug "Ignoring role #{role} in #{fact_type}, subtype fact type"
50
49
  return
51
50
  end
52
51
 
53
- other_role = fact_type.all_role.select{|r| r != role}[0]
54
- other_role_name = preferred_role_name(other_role)
55
- other_player = other_role.concept
56
-
57
52
  # Find any uniqueness constraint over this role:
58
53
  fact_constraints = @presence_constraints_by_fact[fact_type]
59
54
  #debug "Considering #{fact_constraints.size} fact constraints over fact role #{role.concept.name}"
60
- ucs = fact_constraints.select{|c| PresenceConstraint === c && c.max_frequency == 1 }
55
+ ucs = fact_constraints.select{|c| c.is_a?(ActiveFacts::Metamodel::PresenceConstraint) && c.max_frequency == 1 }
61
56
  # Emit "has_one/one_to_one..." only for functional roles here:
62
57
  #debug "Considering #{ucs.size} unique constraints over role #{role.concept.name}"
63
58
  unless ucs.find {|c|
64
- roles = c.role_sequence.all_role_ref.map(&:role)
65
- #debug "Unique constraint over role #{role.concept.name} has roles #{roles.map{|r| describe_fact_type(r.fact_type, r)}*", "}"
66
- roles == [role]
59
+ c.role_sequence.all_role_ref.map(&:role) == [role]
67
60
  }
68
61
  #debug "No uniqueness constraint found for #{role} in #{fact_type}"
69
62
  return
70
63
  end
71
64
 
72
- if ucs.find {|c| c.role_sequence.all_role_ref.map(&:role) == [other_role] } &&
73
- !@concept_types_dumped[other_role.concept]
74
- #debug "Will dump 1:1 later for #{role} in #{fact_type}"
75
- return
76
- end
77
-
78
- # It's a one_to_one if there's a uniqueness constraint on the other role:
79
- one_to_one = ucs.find {|c| c.role_sequence.all_role_ref.map(&:role) == [other_role] }
80
-
81
- # Find role name:
82
- role_method = preferred_role_name(role)
83
- as = other_role_name != other_player.name.snakecase ? "_as_#{other_role_name}" : ""
84
- other_role_method = one_to_one ? role_method : "all_"+role_method
85
- # puts "---"+role.role_name if role.role_name
86
- if other_role_name != other_player.name.snakecase and
87
- role_method == role.concept.name.snakecase
88
- other_role_method += "_as_#{other_role_name}"
65
+ if fact_type.entity_type
66
+ # other_role is a phantom played by the entity type that objectifies this fact type.
67
+ # REVISIT: No rolename can be provided for the phantom role, so _as_XYZ doesn't occur
68
+ other_player = fact_type.entity_type
69
+ return unless @concept_types_dumped[other_player]
70
+
71
+ other_role_name = other_player.name.snakecase
72
+ other_role_method = role.concept.name.snakecase
73
+ one_to_one = true
74
+ else
75
+
76
+ other_role = fact_type.all_role.select{|r| r != role}[0]
77
+ other_role_name = preferred_role_name(other_role)
78
+ other_player = other_role.concept
79
+
80
+ # It's a one_to_one if there's a uniqueness constraint on the other role:
81
+ one_to_one = ucs.find {|c| c.role_sequence.all_role_ref.map(&:role) == [other_role] }
82
+ if one_to_one &&
83
+ !@concept_types_dumped[other_role.concept]
84
+ #debug "Will dump 1:1 later for #{role} in #{fact_type}"
85
+ return
86
+ end
87
+
88
+ # Find role name:
89
+ role_method = preferred_role_name(role)
90
+ other_role_method = one_to_one ? role_method : "all_"+role_method
91
+ # puts "---"+role.role_name if role.role_name
92
+ if other_role_name != other_player.name.snakecase and
93
+ role_method == role.concept.name.snakecase
94
+ other_role_method += "_as_#{other_role_name}"
95
+ end
89
96
  end
90
97
 
91
98
  role_name = role_method
@@ -94,8 +101,13 @@ module ActiveFacts
94
101
  binary_dump(role, other_role_name, other_player, one_to_one, nil, role_name, other_role_method)
95
102
  end
96
103
 
97
- def preferred_role_name(role)
98
- return "" if TypeInheritance === role.fact_type
104
+ def preferred_role_name(role, is_for = nil)
105
+ return "" if role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
106
+
107
+ if is_for && role.fact_type.entity_type == is_for && role.fact_type.all_role.size == 1
108
+ return role.concept.name.snakecase
109
+ end
110
+
99
111
  # debug "Looking for preferred_role_name of #{describe_fact_type(role.fact_type, role)}"
100
112
  reading = role.fact_type.preferred_reading
101
113
  preferred_role_ref = reading.role_sequence.all_role_ref.detect{|reading_rr|
@@ -129,19 +141,27 @@ module ActiveFacts
129
141
  def skip_fact_type(f)
130
142
  # REVISIT: There might be constraints we have to merge into the nested entity or subtype. These will come up as un-handled constraints.
131
143
  !f.entity_type ||
132
- TypeInheritance === f
144
+ f.is_a?(ActiveFacts::Metamodel::TypeInheritance)
133
145
  end
134
146
 
135
147
  # An objectified fact type has internal roles that are always "has_one":
136
- def fact_roles_dump(fact)
137
- fact.all_role.sort_by{|role|
138
- preferred_role_name(role)
148
+ def fact_roles_dump(fact_type)
149
+ fact_type.all_role.sort_by{|role|
150
+ preferred_role_name(role, fact_type.entity_type)
139
151
  }.each{|role|
140
- role_name = preferred_role_name(role)
152
+ role_name = preferred_role_name(role, fact_type.entity_type)
153
+ one_to_one = role.all_role_ref.detect{|rr|
154
+ rr.role_sequence.all_role_ref.size == 1 &&
155
+ rr.role_sequence.all_presence_constraint.detect{|pc|
156
+ pc.max_frequency == 1
157
+ }
158
+ }
141
159
  as = role_name != role.concept.name.snakecase ? "_as_#{role_name}" : ""
142
- raise "Fact #{fact.describe} type is not objectified" unless fact.entity_type
143
- other_role_method = "all_"+fact.entity_type.name.snakecase+as
144
- binary_dump(role, role_name, role.concept, false, nil, nil, other_role_method)
160
+ raise "Fact #{fact_type.describe} type is not objectified" unless fact_type.entity_type
161
+ other_role_method = (one_to_one ? "" : "all_") +
162
+ fact_type.entity_type.name.snakecase +
163
+ as
164
+ binary_dump(role, role_name, role.concept, one_to_one, nil, nil, other_role_method)
145
165
  }
146
166
  end
147
167