activefacts-compositions 1.9.10 → 1.9.12

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.
@@ -10,6 +10,12 @@ require "activefacts/compositions/relational"
10
10
  module ActiveFacts
11
11
  module Compositions
12
12
  class DataVault < Relational
13
+ BDV_ANNOTATIONS = /same as link|hierarchy link|computed link|exploration link|computed satellite|point in time|bridge/
14
+ BDV_LINK_ANNOTATIONS = /same as link|hierarchy link|computed link|exploration link/
15
+ BDV_SAT_ANNOTATIONS = /computed satellite/
16
+ BDV_PIT_ANNOTATIONS = /point in time/
17
+ BDV_BRIDGE_ANNOTATIONS = /bridge/
18
+
13
19
  public
14
20
  def self.options
15
21
  {
@@ -19,8 +25,11 @@ module ActiveFacts
19
25
  hubname: ['String', "Suffix or pattern for naming hub tables. Include a + to insert the name. Default 'HUB'"],
20
26
  linkname: ['String', "Suffix or pattern for naming link tables. Include a + to insert the name. Default 'LINK'"],
21
27
  satname: ['String', "Suffix or pattern for naming satellite tables. Include a + to insert the name. Default 'SAT'"],
28
+ pitname: ['String', "Suffix or pattern for naming point in time tables. Include a + to insert the name. Default 'PIT'"],
29
+ bridgename: ['String', "Suffix or pattern for naming bridge tables. Include a + to insert the name. Default 'BRIDGE'"],
22
30
  refname: ['String', "Suffix or pattern for naming reference tables. Include a + to insert the name. Default '+'"],
23
- }
31
+ }.merge(Relational.options).
32
+ reject{|k,v| [:surrogates].include?(k) } # Datavault surrogates are not optional
24
33
  end
25
34
 
26
35
  def initialize constellation, name, options = {}
@@ -34,10 +43,14 @@ module ActiveFacts
34
43
  @option_link_name.sub!(/^/,'+ ') unless @option_link_name =~ /\+/
35
44
  @option_sat_name = options.delete('satname') || 'SAT'
36
45
  @option_sat_name.sub!(/^/,'+ ') unless @option_sat_name =~ /\+/
46
+ @option_pit_name = options.delete('refname') || 'PIT'
47
+ @option_pit_name.sub!(/^/,'+ ') unless @option_ref_name =~ /\+/
48
+ @option_bridge_name = options.delete('refname') || 'BRIDGE'
49
+ @option_bridge_name.sub!(/^/,'+ ') unless @option_ref_name =~ /\+/
37
50
  @option_ref_name = options.delete('refname') || 'REF'
38
51
  @option_ref_name.sub!(/^/,'+ ') unless @option_ref_name =~ /\+/
39
52
 
40
- super constellation, name, options
53
+ super constellation, name, options, 'DataVault'
41
54
 
42
55
  @option_surrogates = true # Always inject surrogates regardless of superclass
43
56
  end
@@ -51,10 +64,20 @@ module ActiveFacts
51
64
 
52
65
  def composite_is_reference composite
53
66
  object_type = composite.mapping.object_type
54
- object_type.concept.all_concept_annotation.detect{|ca| ca.mapping_annotation == 'static'} or
67
+ all_ca = object_type.concept.all_concept_annotation
68
+
69
+ all_ca.detect{|ca| ca.mapping_annotation == 'static'} or
55
70
  !object_type.is_a?(ActiveFacts::Metamodel::EntityType)
56
71
  end
57
72
 
73
+ def composite_is_bdv composite
74
+ object_type = composite.mapping.object_type
75
+ all_ca = object_type.concept.all_concept_annotation
76
+
77
+ trace :datavault, "composite #{composite.mapping.name} annotations #{all_ca.map{|ca| ca.mapping_annotation} *' '}"
78
+ all_ca.detect{|ca| ca.mapping_annotation =~ BDV_ANNOTATIONS}
79
+ end
80
+
58
81
  # Data Vaults need a surrogate key on every Hub and Link.
59
82
  # Don't add a surrogate on a Reference table!
60
83
  def needs_surrogate(composite)
@@ -74,72 +97,151 @@ module ActiveFacts
74
97
  def classify_composites
75
98
  detect_reference_tables
76
99
 
77
- trace :datavault, "Classify non-reference tables into hubs and links" do
100
+ @bdv_composites, @rdv_composites =
101
+ @non_reference_composites.partition { |composite| composite_is_bdv(composite) }
102
+
103
+ trace :datavault, "Classify non-reference composites into hubs, links, pits and bridges" do
78
104
  # Make an initial determination, then adjust for foreign keys to links afterwards
105
+ @hub_composites = []
106
+ @link_composites = []
107
+ @sat_composites = []
108
+ @bdv_link_composites = []
109
+ @bdv_sat_composites = []
110
+ @pit_composites = []
111
+ @bridge_composites = []
79
112
  @key_structure = {}
80
- @link_composites, @hub_composites =
81
- @non_reference_composites.
82
- sort_by{|c| c.mapping.name}.
83
- partition do |composite|
84
- trace :datavault, "Decide whether #{composite.mapping.name} is a link or a hub" do
85
- @key_structure[composite] =
86
- mapped_to =
87
- composite_key_structure composite
88
-
89
- # It's a Link if the preferred identifier includes more than non_reference_composite.
90
- mapped_to.compact.size > 1
91
- end
92
- end
113
+ @links_as_hubs = {}
114
+ @pit_hub = {}
115
+ @pit_members = {}
116
+ @pit_satellite = {}
117
+
118
+ rdv_classify_composites
119
+ bdv_classify_composites
120
+
121
+ trace :datavault_classification!, "Data Vault classification of composites:" do
122
+ trace :datavault, "Reference: #{@reference_composites.map(&:mapping).map(&:object_type).map(&:name)*', '}"
123
+ trace :datavault, "Raw: #{@rdv_composites.map(&:mapping).map(&:object_type).map(&:name)*', '}"
124
+ trace :datavault, "Business: #{@bdv_composites.map(&:mapping).map(&:object_type).map(&:name)*', '}"
125
+ trace :datavault, "Hub: #{@hub_composites.map(&:mapping).map(&:object_type).map(&:name)*', '}"
126
+ trace :datavault, "Link: #{@link_composites.map(&:mapping).map(&:object_type).map(&:name)*', '}"
127
+ trace :datavault, "BDV Link: #{@bdv_link_composites.map(&:mapping).map(&:object_type).map(&:name)*', '}"
128
+ trace :datavault, "BDV Sat: #{@bdv_sat_composites.map(&:mapping).map(&:object_type).map(&:name)*', '}"
129
+ trace :datavault, "PIT: #{@pit_composites.map(&:mapping).map(&:object_type).map(&:name)*', '}"
130
+ trace :datavault, "Bridge: #{@bridge_composites.map(&:mapping).map(&:object_type).map(&:name)*', '}"
131
+ end
132
+ end
133
+ end
93
134
 
94
- trace :datavault, "Checking for foreign keys that reference links" do
95
- # Links may never be the target of a foreign key.
96
- # Any such links must be defined as hubs instead.
97
- @links_as_hubs = {}
98
- fk_dependencies_by_target = {}
99
- fk_dependencies_by_source = {}
100
- (@hub_composites+@link_composites).
101
- each do |composite|
102
- target_composites = enumerate_foreign_keys composite.mapping
103
- target_composites.each do |target_composite|
104
- next if @reference_composites.include?(target_composite)
105
- (fk_dependencies_by_target[target_composite] ||= []) << composite
106
- (fk_dependencies_by_source[composite] ||= []) << target_composite
135
+ def bdv_classify_composites
136
+ # Classify the business links and bridges
137
+ @bdv_composites.sort_by{|c| c.mapping.name}.each do |composite|
138
+ trace :datavault, "Decide whether #{composite.mapping.name} is a link or bridge"
139
+ object_type = composite.mapping.object_type
140
+ composite.composite_group = 'bdv'
141
+ mapped_to = object_type.fact_type.all_role.to_a
142
+ trace :datavault, "#{composite.mapping.name} encloses foreign keys to #{mapped_to.inspect}" unless mapped_to.compact.empty?
143
+
144
+ all_ca = composite.mapping.object_type.concept.all_concept_annotation
145
+ if all_ca.detect{ |ca| ca.mapping_annotation =~ BDV_LINK_ANNOTATIONS}
146
+ @bdv_link_composites << composite
147
+ elsif all_ca.detect{ |ca| ca.mapping_annotation =~ BDV_BRIDGE_ANNOTATIONS}
148
+ @bridge_composites << composite
149
+ end
150
+ end
151
+
152
+ # Classify the point in time tables
153
+ @rdv_composites.sort_by{|c| c.mapping.name}.each do |composite|
154
+ composite.composite_group = 'rdv'
155
+ trace :datavault, "Decide whether #{composite.mapping.name} has point in time table"
156
+
157
+ pit_members = composite.mapping.all_member.select do |member|
158
+ if member.is_a?(MM::Absorption)
159
+ if found_pit = check_pit(member)
160
+ trace :datavault, "Found PIT member #{member.child_role.object_type.name}"
107
161
  end
162
+ found_pit
163
+ else
164
+ false
108
165
  end
166
+ end
109
167
 
110
- fk_dependencies_by_target.keys.each do |target_composite|
111
- if @link_composites.delete(target_composite)
112
- trace :datavault, "Link #{target_composite.inspect} must be a hub because foreign keys reference it"
113
- @hub_composites << target_composite
114
- @links_as_hubs[target_composite] = true
115
- end
168
+ if pit_members.size > 0
169
+ pit_composite = create_pit(composite.mapping.name, composite)
170
+ @pit_composites << pit_composite
171
+ @pit_hub[pit_composite] = composite
172
+ @pit_members[pit_composite] = pit_members
173
+ end
174
+ end
175
+ end
176
+
177
+ def check_pit component
178
+ all_pc = component.parent_role.all_role_ref.map(&:role_sequence).uniq.flat_map(&:all_presence_constraint).uniq
179
+ all_pc.detect do |pc|
180
+ pc.concept.all_concept_annotation.detect{ |ca| ca.mapping_annotation =~ BDV_PIT_ANNOTATIONS}
181
+ end
182
+ end
183
+
184
+ # Create a new PIT for the same object_type as this composite
185
+ def create_pit name, composite
186
+ mapping = @constellation.Mapping(:new, name: name, object_type: composite.mapping.object_type)
187
+ @constellation.Composite(mapping, composition: @composition, composite_group: 'bdv')
188
+ end
189
+
190
+ def rdv_classify_composites
191
+ @link_composites, @hub_composites =
192
+ @rdv_composites.
193
+ sort_by{|c| c.mapping.name}.
194
+ partition do |composite|
195
+ trace :datavault, "Decide whether #{composite.mapping.name} is a link or a hub" do
196
+ @key_structure[composite] =
197
+ mapped_to =
198
+ composite_key_structure composite
199
+
200
+ # It's a Link if the preferred identifier includes more than non_reference_composite.
201
+ mapped_to.compact.size > 1
116
202
  end
203
+ end
117
204
 
118
- begin
119
- converted =
120
- @link_composites.select do |composite|
121
- targets = fk_dependencies_by_source[composite]
122
- id_targets = composite_key_structure(composite).compact
123
- next if targets.size == id_targets.size
124
- trace :datavault, "Link #{composite.mapping.name} must be a hub because it has non-identifying FK references"
125
- @link_composites.delete(composite)
126
- @hub_composites << composite
127
- @links_as_hubs[composite] = true
128
- end
129
- end while converted.size > 0
205
+ trace :datavault, "Checking for foreign keys that reference links" do
206
+ # Links may never be the target of a foreign key.
207
+ # Any such links must be defined as hubs instead.
208
+
209
+ fk_dependencies_by_target = {}
210
+ fk_dependencies_by_source = {}
211
+ (@hub_composites+@link_composites).each do |composite|
212
+ target_composites = enumerate_foreign_keys composite.mapping
213
+ target_composites.each do |target_composite|
214
+ next if @reference_composites.include?(target_composite)
215
+ (fk_dependencies_by_target[target_composite] ||= []) << composite
216
+ (fk_dependencies_by_source[composite] ||= []) << target_composite
217
+ end
218
+ end
130
219
 
220
+ fk_dependencies_by_target.keys.each do |target_composite|
221
+ if @link_composites.delete(target_composite)
222
+ trace :datavault, "Link #{target_composite.inspect} must be a hub because foreign keys reference it"
223
+ @hub_composites << target_composite
224
+ @links_as_hubs[target_composite] = true
225
+ end
131
226
  end
132
227
 
228
+ begin
229
+ converted =
230
+ @link_composites.select do |composite|
231
+ targets = fk_dependencies_by_source[composite]
232
+ id_targets = composite_key_structure(composite).compact
233
+ next if targets.size == id_targets.size
234
+ trace :datavault, "Link #{composite.mapping.name} must be a hub because it has non-identifying FK references"
235
+ @link_composites.delete(composite)
236
+ @hub_composites << composite
237
+ @links_as_hubs[composite] = true
238
+ end
239
+ end while converted.size > 0
240
+
133
241
  # Note: We may still have hubs whose identifiers contain foreign keys to one or more other hubs.
134
242
  # REVISIT: These foreign keys will be deleted so these hubs stand alone,
135
243
  # but have been re-instated as new links to the referenced hubs.
136
244
  end
137
-
138
- trace :datavault_classification!, "Data Vault classification of composites:" do
139
- trace :datavault, "Reference: #{@reference_composites.map(&:mapping).map(&:object_type).map(&:name)*', '}"
140
- trace :datavault, "Hub: #{@hub_composites.map(&:mapping).map(&:object_type).map(&:name)*', '}"
141
- trace :datavault, "Link: #{@link_composites.map(&:mapping).map(&:object_type).map(&:name)*', '}"
142
- end
143
245
  end
144
246
 
145
247
  def detect_reference_tables
@@ -148,19 +250,57 @@ module ActiveFacts
148
250
  initial_composites.partition { |composite| composite_is_reference(composite) }
149
251
  end
150
252
 
253
+ def composite_key_structure composite
254
+ # We know that composite.mapping.object_type is an EntityType because all ValueType composites are reference tables
255
+ object_type = composite.mapping.object_type
256
+
257
+ mapped_to =
258
+ object_type.preferred_identifier.role_sequence.all_role_ref_in_order.map do |role_ref|
259
+ player = role_ref.role.object_type
260
+ next nil if player == object_type && role_ref.role.fact_type.all_role.size == 1 # Unaries.
261
+ candidate = @candidates[player]
262
+ next nil unless candidate
263
+ # Take care of full absorption
264
+ while candidate.full_absorption
265
+ candidate = candidate.full_absorption.composition
266
+ end
267
+ @non_reference_composites.include?(c = candidate.mapping.composite) ? c : nil
268
+ end
269
+
270
+ trace :datavault, "Preferred identifier for #{composite.mapping.name} encloses foreign keys to #{mapped_to.inspect}" unless mapped_to.compact.empty?
271
+
272
+ number_of_keys = mapped_to.compact.size
273
+ number_of_values = mapped_to.size-number_of_keys
274
+ trace :datavault_classify,
275
+ if number_of_keys > 1
276
+ # Links have more than one FK to a hub in their key
277
+ "Link #{composite.mapping.name} links #{mapped_to.compact.inspect} with #{number_of_values} values"
278
+ elsif number_of_keys == 1 && number_of_values > 0
279
+ # This is a new hub with a composite key - but we will have to eliminate the foreign key to the base hub
280
+ "Augmented Hub #{composite.mapping.name} has a hub link to #{mapped_to.compact[0].inspect} and #{number_of_values} values"
281
+ elsif number_of_keys == 1
282
+ # This is a new hub with a single-part key that references another hub.
283
+ "Dependent Hub #{composite.mapping.name} is identified by another hub: #{mapped_to.compact[0].inspect}"
284
+ else
285
+ "Hub #{composite.mapping.name} has #{mapped_to.size} parts in its key"
286
+ end
287
+
288
+ mapped_to
289
+ end
290
+
151
291
  def devolve_all
152
292
  delete_reference_table_foreign_keys
153
293
 
154
294
  # For each hub and link, move each non-identifying member
155
295
  # to a new satellite or promote it to a new link.
156
-
157
- @non_reference_composites.
158
- each do |composite|
296
+ (@hub_composites + @link_composites).each do |composite|
159
297
  devolve composite
160
298
  end
161
299
 
300
+ # Rename parents for rdv and bdv
162
301
  rename_parents
163
302
 
303
+ # inject datetime and record source for rdv hubs and links
164
304
  inject_all_datetime_recordsource
165
305
 
166
306
  unless @option_reference
@@ -180,6 +320,78 @@ module ActiveFacts
180
320
 
181
321
  @constellation.loggers.pop if trace :reference_retraction
182
322
  end
323
+
324
+ # Devolve point in time tables, if any
325
+ @pit_composites.each do |composite|
326
+ devolve_pit(composite)
327
+ end
328
+
329
+ end
330
+
331
+ def devolve_pit(pit_composite)
332
+ # inject standard PIT components: surrogate key, hub hash key and snapshot date time
333
+ inject_surrogate(pit_composite)
334
+
335
+ hub_composite = @pit_hub[pit_composite]
336
+ hub_hash_field = hub_composite.primary_index.all_index_field.single.component
337
+ pit_hub_field = hub_hash_field.fork_to_new_parent(pit_composite.mapping)
338
+ date_field = @constellation.ValidFrom(:new,
339
+ parent: pit_composite.mapping,
340
+ name: "Snapshot"+datestamp_type_name,
341
+ object_type: datestamp_type
342
+ )
343
+
344
+ natural_index =
345
+ @constellation.Index(:new, composite: pit_composite, is_unique: true,
346
+ presence_constraint: nil, composite_as_natural_index: pit_composite #, composite_as_primary_index: pit_composite
347
+ )
348
+ @constellation.IndexField(access_path: natural_index, ordinal: 0, component: pit_hub_field)
349
+ @constellation.IndexField(access_path: natural_index, ordinal: 1, component: date_field)
350
+
351
+ # Add a foreign key to the hub
352
+ fk = @constellation.ForeignKey(
353
+ :new,
354
+ source_composite: pit_composite,
355
+ composite: hub_composite,
356
+ absorption: nil # REVISIT: This is a ForeignKey without its mandatory Absorption. That's gonna hurt
357
+ )
358
+ @constellation.ForeignKeyField(foreign_key: fk, ordinal: 0, component: pit_hub_field)
359
+ # This should be filled in by complete_foreign_keys, but there is no Absorption
360
+ @constellation.IndexField(access_path: fk, ordinal: 0, component: hub_hash_field)
361
+
362
+ # inject hash and load date time for sats associated with all pit members
363
+ pit_members = @pit_members[pit_composite]
364
+ sat_composites = pit_members.map{|pm| @pit_satellite[pm]}.compact.uniq
365
+
366
+ sat_composites.each do |sat_composite|
367
+ sat_name = sat_composite.mapping.name
368
+ sat_hash_name = "#{sat_name}#{@option_id}"
369
+
370
+ src_hash_field = hub_hash_field.fork_to_new_parent(pit_composite.mapping)
371
+ src_hash_field.name = sat_hash_name
372
+ src_load_field = @constellation.ValidFrom(:new,
373
+ parent: pit_composite.mapping,
374
+ name: "#{sat_name} Load"+datestamp_type_name,
375
+ object_type: datestamp_type
376
+ )
377
+
378
+ sat_index_fields = sat_composite.primary_index.all_index_field.to_a
379
+ sat_hash_field = sat_index_fields[0].component
380
+ sat_load_field = sat_index_fields[1].component
381
+
382
+ # Add a foreign key to the satellite's primary key and load date time
383
+ fk = @constellation.ForeignKey(
384
+ :new,
385
+ source_composite: pit_composite,
386
+ composite: sat_composite,
387
+ absorption: nil # REVISIT: This is a ForeignKey without its mandatory Absorption. That's gonna hurt
388
+ )
389
+ @constellation.ForeignKeyField(foreign_key: fk, ordinal: 0, component: src_hash_field)
390
+ @constellation.IndexField(access_path: fk, ordinal: 0, component: sat_hash_field)
391
+ @constellation.ForeignKeyField(foreign_key: fk, ordinal: 1, component: src_load_field)
392
+ @constellation.IndexField(access_path: fk, ordinal: 1, component: sat_load_field)
393
+ end
394
+ pit_composite.mapping.re_rank
183
395
  end
184
396
 
185
397
  def delete_reference_table_foreign_keys
@@ -192,48 +404,10 @@ module ActiveFacts
192
404
  end
193
405
 
194
406
  def prefer_natural_key building_natural_key, source_composite, target_composite
195
- return false if building_natural_key && @link_composites.include?(source_composite)
407
+ return false if building_natural_key && (@link_composites.include?(source_composite) || @bdv_link_composites.include?(source_composite))
196
408
  building_natural_key && @hub_composites.include?(target_composite)
197
409
  end
198
410
 
199
- def composite_key_structure composite
200
- # We know that composite.mapping.object_type is an EntityType because all ValueType composites are reference tables
201
-
202
- object_type = composite.mapping.object_type
203
- mapped_to =
204
- object_type.preferred_identifier.role_sequence.all_role_ref_in_order.map do |role_ref|
205
- player = role_ref.role.object_type
206
- next nil if player == object_type && role_ref.role.fact_type.all_role.size == 1 # Unaries.
207
- candidate = @candidates[player]
208
- next nil unless candidate
209
- # Take care of full absorption
210
- while candidate.full_absorption
211
- candidate = candidate.full_absorption.composition
212
- end
213
- @non_reference_composites.include?(c = candidate.mapping.composite) ? c : nil
214
- end
215
-
216
- trace :datavault, "Preferred identifier for #{composite.mapping.name} encloses foreign keys to #{mapped_to.inspect}" unless mapped_to.compact.empty?
217
-
218
- number_of_keys = mapped_to.compact.size
219
- number_of_values = mapped_to.size-number_of_keys
220
- trace :datavault_classify,
221
- if number_of_keys > 1
222
- # Links have more than one FK to a hub in their key
223
- "Link #{composite.mapping.name} links #{mapped_to.compact.inspect} with #{number_of_values} values"
224
- elsif number_of_keys == 1 && number_of_values > 0
225
- # This is a new hub with a composite key - but we will have to eliminate the foreign key to the base hub
226
- "Augmented Hub #{composite.mapping.name} has a hub link to #{mapped_to.compact[0].inspect} and #{number_of_values} values"
227
- elsif number_of_keys == 1
228
- # This is a new hub with a single-part key that references another hub.
229
- "Dependent Hub #{composite.mapping.name} is identified by another hub: #{mapped_to.compact[0].inspect}"
230
- else
231
- "Hub #{composite.mapping.name} has #{mapped_to.size} parts in its key"
232
- end
233
-
234
- mapped_to
235
- end
236
-
237
411
  # For each member of this composite, decide whether to devolve it to a satellite
238
412
  # or to a new link. If it goes to a link that's still part of this natural key,
239
413
  # we need to leave that key intact, but remove the foreign key it entails.
@@ -273,12 +447,13 @@ module ActiveFacts
273
447
  # We may absorb a subtype that has no contents. There's no point moving these to a satellite.
274
448
  next if is_empty_inheritance member
275
449
 
276
- satellite_name = name_satellite member
277
- satellite = satellites[satellite_name]
278
- unless satellite
279
- satellite =
280
- satellites[satellite_name] =
281
- create_satellite satellite_name, composite
450
+ satellite_name, is_computed = *name_satellite(member)
451
+ if !(satellite = satellites[satellite_name])
452
+ satellite = satellites[satellite_name] = create_satellite(satellite_name, composite)
453
+ satellite.composite_group = (is_computed ? 'bdv' : 'rdv')
454
+ if member.is_a?(MM::Absorption) && check_pit(member)
455
+ @pit_satellite[member] = satellite
456
+ end
282
457
  end
283
458
 
284
459
  devolve_member_to_satellite satellite, member
@@ -332,20 +507,34 @@ module ActiveFacts
332
507
  satellite.classify_constraints
333
508
  satellite.all_local_constraint.map(&:local_constraint).each(&:retract)
334
509
  leaf_constraints = satellite.mapping.all_leaf.flat_map(&:all_leaf_constraint).map(&:leaf_constraint).each(&:retract)
510
+
511
+ if satellite.composite_group == 'bdv'
512
+ @bdv_sat_composites << satellite
513
+ else
514
+ @sat_composites << satellite
515
+ end
335
516
  end
336
517
  end
337
518
 
338
519
  # Decide what to call a new satellite that will adopt this component
339
- def name_satellite component
520
+ def satellite_base_name_and_type component
521
+ computed_name = nil
340
522
  satellite_name =
341
523
  if component.is_a?(MM::Absorption)
342
524
  pc = component.parent_role.base_role.uniqueness_constraint and
343
- pc.concept.all_concept_annotation.map{|ca| ca.mapping_annotation =~ /^satellite *(.*)/ && $1}.compact.uniq[0]
525
+ pc.concept.all_concept_annotation.map do |ca|
526
+ computed_name = ca.mapping_annotation =~ /^computed satellite *(.*)/ && "#{$1} Computed"
527
+ ca.mapping_annotation =~ /^satellite *(.*)/ && $1 or computed_name
528
+ end.compact.uniq[0]
344
529
  # REVISIT: How do we name the satellite for an Indicator? Add a Concept Annotation on the fact type?
345
530
  end
346
531
  satellite_name = satellite_name.words.capcase if satellite_name
347
- satellite_name ||= component.root.mapping.name
348
- satellite_name = apply_name(@option_sat_name, satellite_name)
532
+ [ satellite_name || component.root.mapping.name, computed_name ]
533
+ end
534
+
535
+ def name_satellite component
536
+ name, is_computed = *satellite_base_name_and_type(component)
537
+ [apply_name(@option_sat_name, name), is_computed != nil]
349
538
  end
350
539
 
351
540
  # Create a new satellite for the same object_type as this composite
@@ -502,14 +691,23 @@ module ActiveFacts
502
691
  end
503
692
 
504
693
  def rename_parents
505
- @link_composites.each do |composite|
506
- composite.mapping.name = apply_name(@option_link_name, composite.mapping.name)
694
+ @reference_composites.each do |composite|
695
+ composite.mapping.name = apply_name(@option_ref_name, composite.mapping.name)
507
696
  end
508
697
  @hub_composites.each do |composite|
509
698
  composite.mapping.name = apply_name(@option_hub_name, composite.mapping.name)
510
699
  end
511
- @reference_composites.each do |composite|
512
- composite.mapping.name = apply_name(@option_ref_name, composite.mapping.name)
700
+ @link_composites.each do |composite|
701
+ composite.mapping.name = apply_name(@option_link_name, composite.mapping.name)
702
+ end
703
+ @bdv_link_composites.each do |composite|
704
+ composite.mapping.name = apply_name(@option_link_name, composite.mapping.name)
705
+ end
706
+ @pit_composites.each do |composite|
707
+ composite.mapping.name = apply_name(@option_pit_name, composite.mapping.name)
708
+ end
709
+ @bridge_composites.each do |composite|
710
+ composite.mapping.name = apply_name(@option_bridge_name, composite.mapping.name)
513
711
  end
514
712
  end
515
713