activefacts 0.7.2 → 0.7.3

Sign up to get free protection for your applications and to get access to all the features.
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