activefacts 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +5 -13
  2. data/Rakefile +2 -2
  3. data/bin/afgen +1 -1
  4. data/bin/cql +118 -27
  5. data/examples/CQL/Insurance.cql +2 -2
  6. data/examples/CQL/Metamodel.cql +3 -3
  7. data/examples/CQL/SchoolActivities.cql +1 -1
  8. data/examples/CQL/Warehousing.cql +5 -4
  9. data/lib/activefacts/cql.rb +1 -1
  10. data/lib/activefacts/cql/Language/English.treetop +2 -1
  11. data/lib/activefacts/cql/compiler.rb +6 -6
  12. data/lib/activefacts/cql/compiler/clause.rb +69 -46
  13. data/lib/activefacts/cql/compiler/constraint.rb +14 -14
  14. data/lib/activefacts/cql/compiler/entity_type.rb +24 -24
  15. data/lib/activefacts/cql/compiler/fact.rb +40 -27
  16. data/lib/activefacts/cql/compiler/fact_type.rb +16 -16
  17. data/lib/activefacts/cql/compiler/query.rb +12 -12
  18. data/lib/activefacts/cql/compiler/shared.rb +9 -0
  19. data/lib/activefacts/cql/compiler/value_type.rb +4 -4
  20. data/lib/activefacts/cql/parser.rb +9 -9
  21. data/lib/activefacts/generate/cql.rb +41 -20
  22. data/lib/activefacts/generate/helpers/oo.rb +33 -70
  23. data/lib/activefacts/generate/helpers/ordered.rb +61 -87
  24. data/lib/activefacts/generate/ruby.rb +12 -72
  25. data/lib/activefacts/generate/transform/surrogate.rb +13 -13
  26. data/lib/activefacts/input/orm.rb +72 -71
  27. data/lib/activefacts/persistence/columns.rb +66 -31
  28. data/lib/activefacts/persistence/foreignkey.rb +6 -6
  29. data/lib/activefacts/persistence/index.rb +12 -12
  30. data/lib/activefacts/persistence/object_type.rb +15 -12
  31. data/lib/activefacts/persistence/reference.rb +20 -18
  32. data/lib/activefacts/persistence/tables.rb +40 -36
  33. data/lib/activefacts/support.rb +69 -123
  34. data/lib/activefacts/version.rb +2 -2
  35. data/lib/activefacts/vocabulary/extensions.rb +42 -39
  36. data/lib/activefacts/vocabulary/metamodel.rb +11 -1
  37. data/lib/activefacts/vocabulary/verbaliser.rb +28 -28
  38. data/spec/cql/contractions_spec.rb +1 -1
  39. data/spec/cql/entity_type_spec.rb +1 -1
  40. data/spec/cql/fact_type_matching_spec.rb +3 -3
  41. data/spec/cql/role_matching_spec.rb +4 -4
  42. data/spec/cql/samples_spec.rb +2 -2
  43. data/spec/cql_cql_spec.rb +1 -1
  44. data/spec/helpers/array_matcher.rb +1 -1
  45. data/spec/norma_ruby_sql_spec.rb +2 -2
  46. data/spec/norma_tables_spec.rb +3 -2
  47. metadata +47 -68
@@ -78,7 +78,7 @@ module ActiveFacts
78
78
  ref.to.is_a?(ActiveFacts::Metamodel::EntityType) and
79
79
  (role_ref = ref.to.preferred_identifier.role_sequence.all_role_ref.single) and
80
80
  role_ref.role == ref.from_role
81
- debug :columns, "Skipping #{ref}, identifies non-initial object"
81
+ trace :columns, "Skipping #{ref}, identifies non-initial object"
82
82
  next a
83
83
  end
84
84
 
@@ -87,19 +87,19 @@ module ActiveFacts
87
87
  # When traversing type inheritances, keep the subtype name, not the supertype names as well:
88
88
  if a.size > 0 && ref.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
89
89
  if ref.to != ref.fact_type.subtype # Did we already have the subtype?
90
- debug :columns, "Skipping supertype #{ref}"
90
+ trace :columns, "Skipping supertype #{ref}"
91
91
  next a
92
92
  end
93
- debug :columns, "Eliding supertype in #{ref}"
93
+ trace :columns, "Eliding supertype in #{ref}"
94
94
  last_names.size.times { a.pop } # Remove the last names added
95
95
  elsif last_names.last && last_names.last == names[0][0...last_names.last.size]
96
96
  # When Xyz is followed by XyzID, truncate that to just ID
97
- debug :columns, "truncating repeated #{last_names.last} in #{names[0]}"
97
+ trace :columns, "truncating repeated #{last_names.last} in #{names[0]}"
98
98
  names[0] = names[0][last_names.last.size..-1]
99
99
  names.shift if names[0] == ''
100
100
  elsif last_names.last == names[0]
101
101
  # Same, but where an underscore split up the words
102
- debug :columns, "truncating repeated name in #{names.inspect}"
102
+ trace :columns, "truncating repeated name in #{names.inspect}"
103
103
  names.shift
104
104
  end
105
105
 
@@ -113,7 +113,7 @@ module ActiveFacts
113
113
  role_ref.role == ref.to_role and
114
114
  names[0][0...et.name.size].downcase == et.name.downcase
115
115
 
116
- debug :columns, "truncating transitive identifying role #{names.inspect}"
116
+ trace :columns, "truncating transitive identifying role #{names.inspect}"
117
117
  names[0] = names[0][et.name.size..-1]
118
118
  names.shift if names[0] == ""
119
119
  end
@@ -177,10 +177,18 @@ module ActiveFacts
177
177
  # The comment is the readings from the References expressed as a series of steps (not a full verbalisation)
178
178
  def comment
179
179
  @references.map do |ref|
180
- (ref.is_mandatory ? "" : "maybe ") +
181
- (ref.fact_type && ref.fact_type.entity_type ? ref.fact_type.entity_type.name+" is where " : "") +
182
- ref.reading
183
- end * " and "
180
+ # REVISIT: Some contraction would be nice here
181
+ objectification = ref.fact_type && ref.fact_type.entity_type
182
+ involves = ''
183
+ if objectification && ref == @references.last
184
+ objectification = nil if ref.fact_type.all_role.size == 1 # Disregard the objectification of a trailing unary
185
+ involves = ref.to_role.link_fact_type.default_reading[ref.fact_type.entity_type.name.size..-1]
186
+ end
187
+ (objectification ? objectification.name + ' (in which ' : '') +
188
+ (ref.is_mandatory || ref.is_unary ? '' : 'maybe ') +
189
+ ref.reading +
190
+ (objectification ? ')'+involves : '')
191
+ end.compact * " and "
184
192
  end
185
193
 
186
194
  def to_s #:nodoc:
@@ -194,8 +202,34 @@ module ActiveFacts
194
202
  cols =
195
203
  if is_unary
196
204
  kind = "unary "
197
- [Column.new()] +
198
- ((@to && @to.fact_type) ? @to.all_columns(excluded_supertypes) : [])
205
+ objectified_unary_columns =
206
+ ((@to && @to.fact_type) ? @to.all_columns(excluded_supertypes) : [])
207
+
208
+ =begin
209
+ # This code omits the unary if it's objectified and that plays a mandatory role
210
+ first_mandatory_column = nil
211
+ if (@to && @to.fact_type)
212
+ trace :unary_col, "Deciding whether to skip unary column for #{inspect}" do
213
+ first_mandatory_column =
214
+ objectified_unary_columns.detect do |col| # Detect a mandatory column for the unary
215
+ trace :unary_col, "checking column #{col.name}" do
216
+ !col.references.detect do |ref|
217
+ trace :unary_col, "#{ref} is mandatory=#{ref.is_mandatory.inspect}"
218
+ !ref.is_mandatory
219
+ end
220
+ end
221
+ end
222
+ if is_from_objectified_fact && first_mandatory_column
223
+ trace :unary_col, "Skipping unary column for #{inspect} because #{first_mandatory_column.name} is mandatory"
224
+ end
225
+ end
226
+ end
227
+
228
+ (is_from_objectified_fact && first_mandatory_column ? [] : [Column.new()]) + # The unary itself, unless its objectified
229
+ =end
230
+
231
+ [Column.new()] + # The unary itself
232
+ objectified_unary_columns
199
233
  elsif is_self_value
200
234
  kind = "self-role "
201
235
  [Column.new()]
@@ -210,9 +244,9 @@ module ActiveFacts
210
244
  c.prepend self
211
245
  end
212
246
 
213
- debug :columns, "Columns from #{kind}#{self}" do
247
+ trace :columns, "Columns from #{kind}#{self}" do
214
248
  cols.each {|c|
215
- debug :columns, "#{c}"
249
+ trace :columns, "#{c}"
216
250
  }
217
251
  end
218
252
  end
@@ -239,7 +273,7 @@ module ActiveFacts
239
273
  class ValueType < DomainObjectType
240
274
  # The identifier_columns for a ValueType can only ever be the self-value role that was injected
241
275
  def identifier_columns
242
- debug :columns, "Identifier Columns for #{name}" do
276
+ trace :columns, "Identifier Columns for #{name}" do
243
277
  raise "Illegal call to identifier_columns for absorbed ValueType #{name}" unless is_table
244
278
  if isr = injected_surrogate_role
245
279
  columns.select{|column| column.references[0].from_role == isr }
@@ -252,7 +286,7 @@ module ActiveFacts
252
286
  # When creating a foreign key to this ValueType, what columns must we include?
253
287
  # This must be a fresh copy, because the columns will have References prepended
254
288
  def reference_columns(excluded_supertypes) #:nodoc:
255
- debug :columns, "Reference Columns for #{name}" do
289
+ trace :columns, "Reference Columns for #{name}" do
256
290
  if is_table
257
291
  if isr = injected_surrogate_role
258
292
  ref_from = references_from.detect{|ref| ref.from_role == isr}
@@ -270,14 +304,14 @@ module ActiveFacts
270
304
  # This must be a fresh copy, because the columns will have References prepended.
271
305
  def all_columns(excluded_supertypes) #:nodoc:
272
306
  columns = []
273
- debug :columns, "All Columns for #{name}" do
307
+ trace :columns, "All Columns for #{name}" do
274
308
  if is_table
275
309
  self_value_reference
276
310
  else
277
311
  columns << ActiveFacts::Persistence::Column.new
278
312
  end
279
313
  references_from.each do |ref|
280
- debug :columns, "Columns absorbed via #{ref}" do
314
+ trace :columns, "Columns absorbed via #{ref}" do
281
315
  columns += ref.columns({})
282
316
  end
283
317
  end
@@ -297,7 +331,7 @@ module ActiveFacts
297
331
  class EntityType < DomainObjectType
298
332
  # The identifier_columns for an EntityType are the columns that result from the identifying roles
299
333
  def identifier_columns
300
- debug :columns, "Identifier Columns for #{name}" do
334
+ trace :columns, "Identifier Columns for #{name}" do
301
335
  if absorbed_via and
302
336
  # If this is a subtype that has its own identification, use that.
303
337
  (all_type_inheritance_as_subtype.size == 0 ||
@@ -316,16 +350,17 @@ module ActiveFacts
316
350
  # When creating a foreign key to this EntityType, what columns must we include (the identifier columns)?
317
351
  # This must be a fresh copy, because the columns will have References prepended
318
352
  def reference_columns(excluded_supertypes) #:nodoc:
319
- debug :columns, "Reference Columns for #{name}" do
353
+ trace :columns, "Reference Columns for #{name}" do
320
354
 
321
355
  if absorbed_via and
322
356
  # If this is not a subtype, or is a subtype that has its own identification, use the id.
323
357
  (all_type_inheritance_as_subtype.size == 0 ||
324
358
  all_type_inheritance_as_subtype.detect{|ti| ti.provides_identification })
325
359
  rc = absorbed_via.from.reference_columns(excluded_supertypes)
326
- # The absorbed_via reference gets skipped here, ans also in object_type.rb
327
- debug :columns, "Skipping #{absorbed_via}"
328
- #rc.each{|col| col.prepend(absorbed_via)}
360
+ # The absorbed_via reference gets skipped here, and also in object_type.rb
361
+ trace :columns, "Skipping #{absorbed_via}"
362
+ absorbed_mirror ||= absorbed_via.reversed
363
+ rc.each{|col| col.prepend(absorbed_mirror)}
329
364
  return rc
330
365
  end
331
366
 
@@ -344,7 +379,7 @@ module ActiveFacts
344
379
  # When absorbing this EntityType, what columns must be absorbed?
345
380
  # This must be a fresh copy, because the columns will have References prepended.
346
381
  def all_columns(excluded_supertypes) #:nodoc:
347
- debug :columns, "All Columns for #{name}" do
382
+ trace :columns, "All Columns for #{name}" do
348
383
  columns = []
349
384
  sups = supertypes
350
385
  pi_roles = preferred_identifier.role_sequence.all_role_ref.map{|rr| rr.role}
@@ -357,10 +392,10 @@ module ActiveFacts
357
392
  end
358
393
  [3, ref.to_names]
359
394
  end.each do |ref|
360
- debug :columns, "Columns absorbed via #{ref}" do
395
+ trace :columns, "Columns absorbed via #{ref}" do
361
396
  if (ref.role_type == :supertype)
362
397
  if excluded_supertypes[ref.to]
363
- debug :columns, "Exclude #{ref.to.name}, we already inherited it"
398
+ trace :columns, "Exclude #{ref.to.name}, we already inherited it"
364
399
  next
365
400
  end
366
401
 
@@ -392,20 +427,20 @@ module ActiveFacts
392
427
  # REVISIT: Is now a good time to apply schema transforms or should this be more explicit?
393
428
  finish_schema
394
429
 
395
- debug :columns, "Populating all columns" do
430
+ trace :columns, "Populating all columns" do
396
431
  all_object_type.each do |object_type|
397
432
  next if !object_type.is_table
398
- debug :columns, "Populating columns for table #{object_type.name}" do
433
+ trace :columns, "Populating columns for table #{object_type.name}" do
399
434
  object_type.populate_columns
400
435
  end
401
436
  end
402
437
  end
403
- debug :columns, "Finished columns" do
438
+ trace :columns, "Finished columns" do
404
439
  all_object_type.each do |object_type|
405
440
  next if !object_type.is_table
406
- debug :columns, "Finished columns for table #{object_type.name}" do
441
+ trace :columns, "Finished columns for table #{object_type.name}" do
407
442
  object_type.columns.each do |column|
408
- debug :columns, "#{column}"
443
+ trace :columns, "#{column}"
409
444
  end
410
445
  end
411
446
  end
@@ -107,7 +107,7 @@ module ActiveFacts
107
107
  ref.fk_jump = true
108
108
  array << [ref]
109
109
  elsif ref.is_absorbing or (ref.to && !ref.to.is_table)
110
- debug :fk, "getting fks absorbed into #{name} via #{ref}" do
110
+ trace :fk, "getting fks absorbed into #{name} via #{ref}" do
111
111
  ref.to.all_absorbed_foreign_key_reference_path.each do |aref|
112
112
  array << aref.insert(0, ref)
113
113
  end
@@ -129,24 +129,24 @@ module ActiveFacts
129
129
  begin
130
130
  fk_ref_paths = all_absorbed_foreign_key_reference_path
131
131
  fk_ref_paths.map do |fk_ref_path|
132
- debug :fk, "\nFK: " + fk_ref_path.map{|fk_ref| fk_ref.reading }*" and " do
132
+ trace :fk, "\nFK: " + fk_ref_path.map{|fk_ref| fk_ref.reading }*" and " do
133
133
 
134
134
  from_columns = (columns||all_columns({})).select{|column|
135
135
  column.references[0...fk_ref_path.size] == fk_ref_path
136
136
  }
137
- debug :fk, "from_columns = #{from_columns.map { |column| column.name }*", "}"
137
+ trace :fk, "from_columns = #{from_columns.map { |column| column.name }*", "}"
138
138
 
139
139
  # Figure out absorption on the target end:
140
140
  to = fk_ref_path.last.to
141
141
  if to.absorbed_via
142
- debug :fk, "Reference target #{fk_ref_path.last.to.name} is absorbed via:" do
142
+ trace :fk, "Reference target #{fk_ref_path.last.to.name} is absorbed via:" do
143
143
  while (r = to.absorbed_via)
144
144
  m = r.reversed
145
- debug :fk, "#{m.reading}"
145
+ trace :fk, "#{m.reading}"
146
146
  fk_ref_path << m
147
147
  to = m.from == to ? m.to : m.from
148
148
  end
149
- debug :fk, "Absorption ends at #{to.name}"
149
+ trace :fk, "Absorption ends at #{to.name}"
150
150
  end
151
151
  end
152
152
 
@@ -124,13 +124,13 @@ module ActiveFacts
124
124
  # Note also that this produces columns ordered for each refpath the same as the
125
125
  # order of the columns, not the same as the columns in the PK for which they might be an FK.
126
126
  all_column_by_ref_path =
127
- debug :index2, "Indexing columns by ref_path" do
127
+ trace :index2, "Indexing columns by ref_path" do
128
128
  columns.inject({}) do |hash, column|
129
- debug :index2, "References in column #{name}.#{column.name}" do
129
+ trace :index2, "References in column #{name}.#{column.name}" do
130
130
  ref_path = column.absorption_references
131
131
  raise "No absorption_references for #{column.name} from #{column.references.map(&:to_s)*" and "}" if !ref_path || ref_path.empty?
132
132
  (hash[ref_path] ||= []) << column
133
- debug :index2, "#{column.name} involves #{ref_path.map(&:to_s)*" and "}"
133
+ trace :index2, "#{column.name} involves #{ref_path.map(&:to_s)*" and "}"
134
134
  end
135
135
  hash
136
136
  end
@@ -143,7 +143,7 @@ module ActiveFacts
143
143
  inject({}) do |hash, ref_path|
144
144
  ref_path.each do |ref|
145
145
  next unless ref.to_role
146
- # debug :index2, "Considering #{ref_path.map(&:to_s)*" and "} yielding columns #{all_column_by_ref_path[ref_path].map{|c| c.name('.')}*", "}"
146
+ # trace :index2, "Considering #{ref_path.map(&:to_s)*" and "} yielding columns #{all_column_by_ref_path[ref_path].map{|c| c.name('.')}*", "}"
147
147
  ref.to_role.all_role_ref.each do |role_ref|
148
148
  all_pcs = role_ref.role_sequence.all_presence_constraint
149
149
  # puts "pcs over #{ref_path.map{|r| r.to_names}.flatten*'.'}: #{role_ref.role_sequence.all_presence_constraint.map(&:describe)*"; "}" if all_pcs.size > 0
@@ -171,9 +171,9 @@ module ActiveFacts
171
171
  hash
172
172
  end
173
173
 
174
- debug :index, "All Indices in #{name}:" do
174
+ trace :index, "All Indices in #{name}:" do
175
175
  @indices = columns_by_unique_constraint.map do |uc, columns_with_ordinal|
176
- debug :index, "Index due to uc #{uc.concept.guid} on #{name} over (#{columns_with_ordinal.sort_by{|onc|onc[0]}.map{|ca| ca[2].name}.inspect})"
176
+ trace :index, "Index due to uc #{uc.concept.guid} on #{name} over (#{columns_with_ordinal.sort_by{|onc|onc[0]}.map{|ca| ca[2].name}.inspect})"
177
177
  columns = columns_with_ordinal.sort_by{|ca| [ca[0,2], ca[2].name]}.map{|ca| ca[2]}
178
178
  absorption_level = columns.map(&:absorption_level).min
179
179
  over = columns[0].references[absorption_level].from
@@ -193,7 +193,7 @@ module ActiveFacts
193
193
  columns,
194
194
  uc.is_preferred_identifier
195
195
  )
196
- debug :index, index
196
+ trace :index, index
197
197
  index
198
198
  end.
199
199
  compact.
@@ -211,24 +211,24 @@ module ActiveFacts
211
211
 
212
212
  class Vocabulary
213
213
  def populate_all_indices #:nodoc:
214
- debug :index, "Populating all object_type indices" do
214
+ trace :index, "Populating all object_type indices" do
215
215
  all_object_type.each do |object_type|
216
216
  object_type.clear_indices
217
217
  end
218
218
  all_object_type.each do |object_type|
219
219
  next unless object_type.is_table
220
- debug :index, "Populating indices for #{object_type.name}" do
220
+ trace :index, "Populating indices for #{object_type.name}" do
221
221
  object_type.populate_indices
222
222
  end
223
223
  end
224
224
  end
225
- debug :index, "Finished object_type indices" do
225
+ trace :index, "Finished object_type indices" do
226
226
  all_object_type.each do |object_type|
227
227
  next unless object_type.is_table
228
228
  next unless object_type.indices.size > 0
229
- debug :index, "#{object_type.name}:" do
229
+ trace :index, "#{object_type.name}:" do
230
230
  object_type.indices.each do |index|
231
- debug :index, index
231
+ trace :index, index
232
232
  end
233
233
  end
234
234
  end
@@ -12,12 +12,14 @@ module ActiveFacts
12
12
  end
13
13
 
14
14
  def columns
15
+ raise "This method is no longer in use"
16
+ =begin
15
17
  return @columns if @columns
16
- debug :persistence, "Calculating columns for #{basename}" do
18
+ trace :persistence, "Calculating columns for #{basename}" do
17
19
  @columns = (
18
20
  if superclass.is_entity_type
19
21
  # REVISIT: Need keys to secondary supertypes as well, but no duplicates.
20
- debug :persistence, "Separate subtype has a foreign key to its supertype" do
22
+ trace :persistence, "Separate subtype has a foreign key to its supertype" do
21
23
  superclass.__absorb([[superclass.basename]], self)
22
24
  end
23
25
  else
@@ -28,7 +30,7 @@ module ActiveFacts
28
30
  role.unique && !role.counterpart_unary_has_precedence
29
31
  end.inject([]) do |columns, role|
30
32
  rn = role.name.to_s.split(/_/)
31
- debug :persistence, "Role #{rn*'.'}" do
33
+ trace :persistence, "Role #{rn*'.'}" do
32
34
  columns += role.counterpart_object_type.__absorb([rn], role.counterpart)
33
35
  end
34
36
  end +
@@ -38,7 +40,7 @@ module ActiveFacts
38
40
  inject([]) do |columns, subtype|
39
41
  # Pass self as 2nd param here, not a role, standing for the supertype role
40
42
  subtype_name = subtype.basename
41
- debug :persistence, "Absorbing subtype #{subtype_name}" do
43
+ trace :persistence, "Absorbing subtype #{subtype_name}" do
42
44
  columns += subtype.__absorb([[subtype_name]], self)
43
45
  end
44
46
  end
@@ -55,6 +57,7 @@ module ActiveFacts
55
57
  end*"."
56
58
  end
57
59
  end
60
+ =end
58
61
  end
59
62
 
60
63
  # Return an array of the absorbed columns, using prefix for name truncation
@@ -68,7 +71,7 @@ module ActiveFacts
68
71
  # counterpart_object_type = role.counterpart_object_type
69
72
  # This omission matches the one in columns.rb, see EntityType#reference_columns
70
73
  # new_prefix = prefix + [role.name.to_s.split(/_/)]
71
- debug :persistence, "Reference to #{role.name} (absorbed elsewhere)" do
74
+ trace :persistence, "Reference to #{role.name} (absorbed elsewhere)" do
72
75
  role.counterpart_object_type.__absorb(prefix, role.counterpart)
73
76
  end
74
77
  else
@@ -86,7 +89,7 @@ module ActiveFacts
86
89
  inject([]) do |columns, subtype|
87
90
  # Pass self as 2nd param here, not a role, standing for the supertype role
88
91
  new_prefix = prefix[0..-2] + [[subtype.basename]]
89
- debug :persistence, "Absorbed subtype #{subtype.basename}" do
92
+ trace :persistence, "Absorbed subtype #{subtype.basename}" do
90
93
  columns += subtype.__absorb(new_prefix, self)
91
94
  end
92
95
  end
@@ -98,7 +101,7 @@ module ActiveFacts
98
101
  # Create a foreign key to the table
99
102
  if is_entity_type
100
103
  ir = identifying_role_names.map{|role_name| roles(role_name) }
101
- debug :persistence, "Reference to #{basename} with #{prefix.inspect}" do
104
+ trace :persistence, "Reference to #{basename} with #{prefix.inspect}" do
102
105
  ic = identifying_role_names.map{|role_name| role_name.to_s.split(/_/)}
103
106
  ir.inject([]) do |columns, role|
104
107
  columns += __absorb_role(prefix, role)
@@ -107,7 +110,7 @@ module ActiveFacts
107
110
  else
108
111
  # Reference to value type which is a table
109
112
  col = prefix.clone
110
- debug :persistence, "Self-value #{col[-1]}.Value"
113
+ trace :persistence, "Self-value #{col[-1]}.Value"
111
114
  col[-1] += ["Value"]
112
115
  col
113
116
  end
@@ -122,23 +125,23 @@ module ActiveFacts
122
125
  (n = irn[0].to_s.split(/_/)).size > 1 and
123
126
  (owner = role.owner.basename.snakecase.split(/_/)) and
124
127
  n[0...owner.size] == owner
125
- debug :persistence, "truncating transitive identifying role #{n.inspect}"
128
+ trace :persistence, "truncating transitive identifying role #{n.inspect}"
126
129
  owner.size.times { n.shift }
127
130
  new_prefix = prefix + [n]
128
131
  elsif (c = role.counterpart_object_type).is_entity_type and
129
132
  (irn = c.identifying_role_names).size == 1 and
130
133
  #irn[0].to_s.split(/_/)[0] == role.owner.basename.downcase
131
134
  irn[0] == role.counterpart.name
132
- #debug :persistence, "=== #{irn[0].to_s.split(/_/)[0]} elided ==="
135
+ #trace :persistence, "=== #{irn[0].to_s.split(/_/)[0]} elided ==="
133
136
  new_prefix = prefix
134
137
  elsif (fa_role = fully_absorbed) && fa_role == role
135
138
  new_prefix = prefix
136
139
  else
137
140
  new_prefix = prefix + [role.name.to_s.split(/_/)]
138
141
  end
139
- #debug :persistence, "new_prefix is #{new_prefix*"."}"
142
+ #trace :persistence, "new_prefix is #{new_prefix*"."}"
140
143
 
141
- debug :persistence, "Absorbing role #{role.name} as #{new_prefix[prefix.size..-1]*"."}" do
144
+ trace :persistence, "Absorbing role #{role.name} as #{new_prefix[prefix.size..-1]*"."}" do
142
145
  role.counterpart_object_type.__absorb(new_prefix, role.counterpart)
143
146
  end
144
147
  end
@@ -74,8 +74,9 @@ module ActiveFacts
74
74
 
75
75
  # Is this Reference covered by a mandatory constraint (implicitly or explicitly)
76
76
  def is_mandatory
77
- !@from_role || # All phantom roles of fact types are mandatory
78
- @from_role.is_mandatory
77
+ !is_unary &&
78
+ (!@from_role || # All phantom roles of fact types are mandatory
79
+ @from_role.is_mandatory)
79
80
  end
80
81
 
81
82
  # Is this Reference from a unary Role?
@@ -188,7 +189,7 @@ module ActiveFacts
188
189
  @from.references_from << self
189
190
  @to.references_to << self if @to # Guard against self-values
190
191
 
191
- debug :references, "Adding #{to_s}"
192
+ trace :references, "Adding #{to_s}"
192
193
  self
193
194
  end
194
195
 
@@ -196,7 +197,7 @@ module ActiveFacts
196
197
  # Remove from @to and @from's reference lists if present
197
198
  return unless @from.references_from.delete(self)
198
199
  @to.references_to.delete self if @to # Guard against self-values
199
- debug :references, "Dropping #{to_s}"
200
+ trace :references, "Dropping #{to_s}"
200
201
  self
201
202
  end
202
203
 
@@ -207,7 +208,7 @@ module ActiveFacts
207
208
 
208
209
  # The reading for the fact type underlying this Reference
209
210
  def reading
210
- is_self_value ? "#{from.name} has value" : @fact_type.default_reading
211
+ is_self_value ? "#{from.name} has value" : @fact_type.reading_preferably_starting_with_role(@from_role).expand
211
212
  end
212
213
 
213
214
  def inspect #:nodoc:
@@ -284,7 +285,7 @@ module ActiveFacts
284
285
 
285
286
  def populate_reference role #:nodoc:
286
287
  role_type = role.role_type
287
- debug :references, "#{name} has #{role_type} role in '#{role.fact_type.describe}'"
288
+ trace :references, "#{name} has #{role_type} role in '#{role.fact_type.describe}'"
288
289
  case role_type
289
290
  when :many_one
290
291
  ActiveFacts::Persistence::Reference.new(self, role).tabulate # A simple reference
@@ -294,7 +295,7 @@ module ActiveFacts
294
295
  ActiveFacts::Persistence::Reference.new(self, role).tabulate # A simple reference; check that
295
296
  else
296
297
  # Can't absorb many of these into one of those
297
- #debug :references, "Ignoring #{role_type} reference from #{name} to #{Reference.new(self, role).to.name}"
298
+ #trace :references, "Ignoring #{role_type} reference from #{name} to #{Reference.new(self, role).to.name}"
298
299
  end
299
300
 
300
301
  when :unary
@@ -304,11 +305,11 @@ module ActiveFacts
304
305
  # REVISIT: Or when partitioned
305
306
  raise hell unless role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
306
307
  if role.fact_type.assimilation # assimilation == 'separate' or assimilation == 'partitioned'
307
- debug :references, "supertype #{name} doesn't absorb a reference to separate subtype #{role.fact_type.subtype.name}"
308
+ trace :references, "supertype #{name} doesn't absorb a reference to separate subtype #{role.fact_type.subtype.name}"
308
309
  else
309
310
  r = ActiveFacts::Persistence::Reference.new(self, role)
310
311
  r.to.absorbed_via = r
311
- debug :references, "supertype #{name} absorbs subtype #{r.to.name}"
312
+ trace :references, "supertype #{name} absorbs subtype #{r.to.name}"
312
313
  r.tabulate
313
314
  end
314
315
 
@@ -317,7 +318,7 @@ module ActiveFacts
317
318
  ActiveFacts::Persistence::Reference.new(self, role).tabulate
318
319
  # If partitioned, the supertype is absorbed into *each* subtype; a reference to the supertype needs to know which
319
320
  else
320
- # debug :references, "subtype #{name} is absorbed into #{role.fact_type.supertype.name}"
321
+ # trace :references, "subtype #{name} is absorbed into #{role.fact_type.supertype.name}"
321
322
  end
322
323
 
323
324
  when :one_one
@@ -340,11 +341,11 @@ module ActiveFacts
340
341
 
341
342
  # Force the decision if one EntityType identifies another:
342
343
  if preferred_identifier.role_sequence.all_role_ref.detect{|rr| rr.role == r.to_role}
343
- debug :references, "EntityType #{name} is identified by EntityType #{r.to.name}, so gets absorbed elsewhere"
344
+ trace :references, "EntityType #{name} is identified by EntityType #{r.to.name}, so gets absorbed elsewhere"
344
345
  return
345
346
  end
346
347
  if r.to.preferred_identifier.role_sequence.all_role_ref.detect{|rr| rr.role == role}
347
- debug :references, "EntityType #{name} identifies EntityType #{r.to.name}, so absorbs it"
348
+ trace :references, "EntityType #{name} identifies EntityType #{r.to.name}, so absorbs it"
348
349
  r.to.absorbed_via = r
349
350
  # We can't be absorbed into our supertype!
350
351
  # REVISIT: We might need to flip all one-to-ones as well
@@ -362,6 +363,7 @@ module ActiveFacts
362
363
  r.tabulate
363
364
  end
364
365
  else
366
+ # REVISIT: Should we implicitly objectify this fact type here and add a spanning UC?
365
367
  raise "Role #{role.object_type.name} in '#{role.fact_type.default_reading}' lacks a uniqueness constraint"
366
368
  end
367
369
  end
@@ -381,9 +383,9 @@ module ActiveFacts
381
383
 
382
384
  class Vocabulary
383
385
  def populate_all_references #:nodoc:
384
- debug :references, "Populating all object_type references" do
386
+ trace :references, "Populating all object_type references" do
385
387
  all_object_type.each do |object_type|
386
- debug :references, "Populating references for #{object_type.name}" do
388
+ trace :references, "Populating references for #{object_type.name}" do
387
389
  object_type.populate_references
388
390
  end
389
391
  end
@@ -392,13 +394,13 @@ module ActiveFacts
392
394
  end
393
395
 
394
396
  def show_all_references
395
- if debug :references
396
- debug :references, "Finished object_type references" do
397
+ if trace :references
398
+ trace :references, "Finished object_type references" do
397
399
  all_object_type.each do |object_type|
398
400
  next unless object_type.references_from.size > 0
399
- debug :references, "#{object_type.name}:" do
401
+ trace :references, "#{object_type.name}:" do
400
402
  object_type.references_from.each do |ref|
401
- debug :references, "#{ref}"
403
+ trace :references, "#{ref}"
402
404
  end
403
405
  end
404
406
  end