activefacts 1.6.0 → 1.7.1

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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +14 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +60 -0
  8. data/Rakefile +3 -80
  9. data/activefacts.gemspec +36 -0
  10. data/bin/afgen +4 -2
  11. data/bin/cql +5 -1
  12. data/lib/activefacts.rb +3 -12
  13. data/lib/activefacts/{vocabulary/query_evaluator.rb → query/evaluator.rb} +0 -0
  14. data/lib/activefacts/version.rb +2 -2
  15. metadata +48 -296
  16. data/History.txt +0 -4
  17. data/LICENSE +0 -19
  18. data/Manifest.txt +0 -165
  19. data/README.rdoc +0 -81
  20. data/css/offline.css +0 -3
  21. data/css/orm2.css +0 -124
  22. data/css/print.css +0 -8
  23. data/css/style-print.css +0 -357
  24. data/css/style.css +0 -387
  25. data/download.html +0 -110
  26. data/examples/CQL/Address.cql +0 -44
  27. data/examples/CQL/Blog.cql +0 -54
  28. data/examples/CQL/CompanyDirectorEmployee.cql +0 -56
  29. data/examples/CQL/Death.cql +0 -17
  30. data/examples/CQL/Diplomacy.cql +0 -48
  31. data/examples/CQL/Genealogy.cql +0 -98
  32. data/examples/CQL/Insurance.cql +0 -320
  33. data/examples/CQL/Marriage.cql +0 -18
  34. data/examples/CQL/Metamodel.cql +0 -493
  35. data/examples/CQL/Monogamy.cql +0 -24
  36. data/examples/CQL/MultiInheritance.cql +0 -22
  37. data/examples/CQL/NonRoleId.cql +0 -14
  38. data/examples/CQL/OddIdentifier.cql +0 -18
  39. data/examples/CQL/OilSupply.cql +0 -53
  40. data/examples/CQL/OneToOnes.cql +0 -17
  41. data/examples/CQL/Orienteering.cql +0 -111
  42. data/examples/CQL/PersonPlaysGame.cql +0 -18
  43. data/examples/CQL/RedundantDependency.cql +0 -34
  44. data/examples/CQL/SchoolActivities.cql +0 -33
  45. data/examples/CQL/SeparateSubtype.cql +0 -30
  46. data/examples/CQL/ServiceDirector.cql +0 -276
  47. data/examples/CQL/SimplestUnary.cql +0 -12
  48. data/examples/CQL/Supervision.cql +0 -34
  49. data/examples/CQL/WaiterTips.cql +0 -33
  50. data/examples/CQL/Warehousing.cql +0 -101
  51. data/examples/CQL/WindowInRoomInBldg.cql +0 -28
  52. data/examples/CQL/unit.cql +0 -474
  53. data/examples/index.html +0 -420
  54. data/examples/intro.html +0 -327
  55. data/examples/local.css +0 -24
  56. data/index.html +0 -111
  57. data/lib/activefacts/cql.rb +0 -35
  58. data/lib/activefacts/cql/CQLParser.treetop +0 -158
  59. data/lib/activefacts/cql/Context.treetop +0 -48
  60. data/lib/activefacts/cql/Expressions.treetop +0 -67
  61. data/lib/activefacts/cql/FactTypes.treetop +0 -358
  62. data/lib/activefacts/cql/Language/English.treetop +0 -315
  63. data/lib/activefacts/cql/LexicalRules.treetop +0 -253
  64. data/lib/activefacts/cql/ObjectTypes.treetop +0 -210
  65. data/lib/activefacts/cql/Rakefile +0 -14
  66. data/lib/activefacts/cql/Terms.treetop +0 -183
  67. data/lib/activefacts/cql/ValueTypes.treetop +0 -202
  68. data/lib/activefacts/cql/compiler.rb +0 -156
  69. data/lib/activefacts/cql/compiler/clause.rb +0 -1137
  70. data/lib/activefacts/cql/compiler/constraint.rb +0 -581
  71. data/lib/activefacts/cql/compiler/entity_type.rb +0 -457
  72. data/lib/activefacts/cql/compiler/expression.rb +0 -443
  73. data/lib/activefacts/cql/compiler/fact.rb +0 -390
  74. data/lib/activefacts/cql/compiler/fact_type.rb +0 -421
  75. data/lib/activefacts/cql/compiler/query.rb +0 -106
  76. data/lib/activefacts/cql/compiler/shared.rb +0 -161
  77. data/lib/activefacts/cql/compiler/value_type.rb +0 -174
  78. data/lib/activefacts/cql/nodes.rb +0 -49
  79. data/lib/activefacts/cql/parser.rb +0 -241
  80. data/lib/activefacts/dependency_analyser.rb +0 -182
  81. data/lib/activefacts/generate/absorption.rb +0 -70
  82. data/lib/activefacts/generate/composition.rb +0 -118
  83. data/lib/activefacts/generate/cql.rb +0 -714
  84. data/lib/activefacts/generate/dm.rb +0 -279
  85. data/lib/activefacts/generate/help.rb +0 -64
  86. data/lib/activefacts/generate/helpers/inject.rb +0 -16
  87. data/lib/activefacts/generate/helpers/oo.rb +0 -162
  88. data/lib/activefacts/generate/helpers/ordered.rb +0 -605
  89. data/lib/activefacts/generate/helpers/rails.rb +0 -57
  90. data/lib/activefacts/generate/html/glossary.rb +0 -461
  91. data/lib/activefacts/generate/json.rb +0 -337
  92. data/lib/activefacts/generate/null.rb +0 -32
  93. data/lib/activefacts/generate/rails/models.rb +0 -246
  94. data/lib/activefacts/generate/rails/schema.rb +0 -216
  95. data/lib/activefacts/generate/records.rb +0 -46
  96. data/lib/activefacts/generate/ruby.rb +0 -133
  97. data/lib/activefacts/generate/sql/mysql.rb +0 -280
  98. data/lib/activefacts/generate/sql/server.rb +0 -273
  99. data/lib/activefacts/generate/stats.rb +0 -69
  100. data/lib/activefacts/generate/text.rb +0 -27
  101. data/lib/activefacts/generate/topics.rb +0 -265
  102. data/lib/activefacts/generate/traits/datavault.rb +0 -241
  103. data/lib/activefacts/generate/traits/oo.rb +0 -73
  104. data/lib/activefacts/generate/traits/ordered.rb +0 -33
  105. data/lib/activefacts/generate/traits/ruby.rb +0 -210
  106. data/lib/activefacts/generate/transform/datavault.rb +0 -266
  107. data/lib/activefacts/generate/transform/surrogate.rb +0 -214
  108. data/lib/activefacts/generate/version.rb +0 -26
  109. data/lib/activefacts/input/cql.rb +0 -43
  110. data/lib/activefacts/input/orm.rb +0 -1636
  111. data/lib/activefacts/mapping/rails.rb +0 -132
  112. data/lib/activefacts/persistence.rb +0 -15
  113. data/lib/activefacts/persistence/columns.rb +0 -446
  114. data/lib/activefacts/persistence/foreignkey.rb +0 -187
  115. data/lib/activefacts/persistence/index.rb +0 -240
  116. data/lib/activefacts/persistence/object_type.rb +0 -198
  117. data/lib/activefacts/persistence/reference.rb +0 -434
  118. data/lib/activefacts/persistence/tables.rb +0 -380
  119. data/lib/activefacts/registry.rb +0 -11
  120. data/lib/activefacts/support.rb +0 -132
  121. data/lib/activefacts/vocabulary.rb +0 -9
  122. data/lib/activefacts/vocabulary/extensions.rb +0 -1348
  123. data/lib/activefacts/vocabulary/metamodel.rb +0 -570
  124. data/lib/activefacts/vocabulary/verbaliser.rb +0 -804
  125. data/script/txt2html +0 -71
  126. data/spec/absorption_spec.rb +0 -95
  127. data/spec/cql/comparison_spec.rb +0 -89
  128. data/spec/cql/context_spec.rb +0 -94
  129. data/spec/cql/contractions_spec.rb +0 -224
  130. data/spec/cql/deontic_spec.rb +0 -88
  131. data/spec/cql/entity_type_spec.rb +0 -320
  132. data/spec/cql/expressions_spec.rb +0 -66
  133. data/spec/cql/fact_type_matching_spec.rb +0 -338
  134. data/spec/cql/french_spec.rb +0 -21
  135. data/spec/cql/parser/bad_literals_spec.rb +0 -86
  136. data/spec/cql/parser/constraints_spec.rb +0 -19
  137. data/spec/cql/parser/entity_types_spec.rb +0 -106
  138. data/spec/cql/parser/expressions_spec.rb +0 -199
  139. data/spec/cql/parser/fact_types_spec.rb +0 -44
  140. data/spec/cql/parser/literals_spec.rb +0 -312
  141. data/spec/cql/parser/pragmas_spec.rb +0 -89
  142. data/spec/cql/parser/value_types_spec.rb +0 -42
  143. data/spec/cql/role_matching_spec.rb +0 -148
  144. data/spec/cql/samples_spec.rb +0 -244
  145. data/spec/cql_cql_spec.rb +0 -73
  146. data/spec/cql_dm_spec.rb +0 -136
  147. data/spec/cql_mysql_spec.rb +0 -69
  148. data/spec/cql_parse_spec.rb +0 -34
  149. data/spec/cql_ruby_spec.rb +0 -73
  150. data/spec/cql_sql_spec.rb +0 -72
  151. data/spec/cql_symbol_tables_spec.rb +0 -261
  152. data/spec/cqldump_spec.rb +0 -170
  153. data/spec/helpers/array_matcher.rb +0 -23
  154. data/spec/helpers/ctrl_c_support.rb +0 -52
  155. data/spec/helpers/diff_matcher.rb +0 -39
  156. data/spec/helpers/file_matcher.rb +0 -34
  157. data/spec/helpers/parse_to_ast_matcher.rb +0 -80
  158. data/spec/helpers/string_matcher.rb +0 -30
  159. data/spec/helpers/test_parser.rb +0 -15
  160. data/spec/norma_cql_spec.rb +0 -66
  161. data/spec/norma_ruby_spec.rb +0 -62
  162. data/spec/norma_ruby_sql_spec.rb +0 -107
  163. data/spec/norma_sql_spec.rb +0 -57
  164. data/spec/norma_tables_spec.rb +0 -95
  165. data/spec/ruby_api_spec.rb +0 -23
  166. data/spec/spec_helper.rb +0 -35
  167. data/spec/transform_surrogate_spec.rb +0 -59
  168. data/status.html +0 -138
  169. data/why.html +0 -60
@@ -1,266 +0,0 @@
1
- #
2
- # Data Vault Transform
3
- # Transform a loaded ActiveFacts vocabulary to suit Data Vault
4
- #
5
- # Copyright (c) 2015 Infinuendo. Read the LICENSE file.
6
- #
7
- require 'activefacts/vocabulary'
8
- require 'activefacts/persistence'
9
-
10
- require 'activefacts/generate/traits/datavault'
11
-
12
- module ActiveFacts
13
-
14
- module Generate #:nodoc:
15
- module Transform #:nodoc:
16
- class DataVault
17
- def initialize(vocabulary, *options)
18
- @vocabulary = vocabulary
19
- @constellation = vocabulary.constellation
20
- end
21
-
22
- def classify_tables
23
- initial_tables = @vocabulary.tables
24
- non_reference_tables = initial_tables.reject do |table|
25
- table.concept.all_concept_annotation.detect{|ca| ca.mapping_annotation == 'static'} or
26
- !table.is_a?(ActiveFacts::Metamodel::EntityType)
27
- end
28
- @reference_tables = initial_tables-non_reference_tables
29
-
30
- @link_tables, @hub_tables = non_reference_tables.partition do |table|
31
- identifying_references = table.identifier_columns.map{|c| c.references.first}.uniq
32
- # Which identifying_references are played by other tables?
33
- ir_tables =
34
- identifying_references.select do |r|
35
- table_referred_to = r.to
36
- # I have no examples of multi-level absorption, but it's possible, so loop
37
- while av = table_referred_to.absorbed_via
38
- table_referred_to = av.from
39
- end
40
- table_referred_to.is_table
41
- end
42
- ir_tables.size > 1
43
- end
44
- trace_table_classifications
45
- end
46
-
47
- def trace_table_classifications
48
- # Trace the decisions about table types:
49
- if trace :datavault
50
- [@reference_tables, @hub_tables, @link_tables].zip(['Reference', 'Hub', 'Link']).each do |tables, kind|
51
- trace :datavault, kind+' tables: ' do
52
- tables.each do |table|
53
- identifying_references = table.identifier_columns.map{|c| c.references.first}.uniq
54
- trace :datavault, "#{table.name}(#{identifying_references.map{|r| (t = r.to) && t.name || 'self'}*', '})"
55
- end
56
- end
57
- end
58
- end
59
- end
60
-
61
- def detect_required_surrogates
62
- trace :datavault, "Detecting required surrogates" do
63
- @required_surrogates =
64
- (@hub_tables+@link_tables).select do |table|
65
- table.dv_needs_surrogate
66
- end
67
- end
68
- end
69
-
70
- def inject_required_surrogates
71
- trace :datavault, "Injecting any required surrogates" do
72
- trace :datavault, "Need to inject surrogates into #{@required_surrogates.map(&:name)*', '}"
73
- @required_surrogates.each do |table|
74
- table.dv_inject_surrogate
75
- end
76
- end
77
- end
78
-
79
- def classify_satellite_references table
80
- identifying_references = table.identifier_columns.map{|c| c.references.first}.uniq
81
- non_identifying_references = table.columns.map{|c| c.references[0]}.uniq - identifying_references
82
-
83
- # Skip this table if no satellite data is needed
84
- # REVISIT: Needed anyway for a link?
85
- if non_identifying_references.size == 0
86
- return nil
87
- end
88
-
89
- satellites = non_identifying_references.inject({}) do |hash, ref|
90
- # Extract the declared satellite name, or use just "satellite"
91
- satellite_subname =
92
- ref.fact_type.internal_presence_constraints.map do |pc|
93
- next if !pc.max_frequency || pc.max_frequency > 1 # Not a Uniqueness Constraint
94
- next if pc.role_sequence.all_role_ref.size > 1 # Covers more than one role
95
- next if pc.role_sequence.all_role_ref.single.role.object_type != table # Not a unique attribute
96
- pc.concept.all_concept_annotation.map do |ca|
97
- if ca.mapping_annotation =~ /^satellite */
98
- ca.mapping_annotation.sub(/^satellite +/, '')
99
- else
100
- nil
101
- end
102
- end
103
- end.flatten.compact.uniq[0] || "satellite"
104
- satellite_name = "#{satellite_subname}"
105
- (hash[satellite_name] ||= []) << ref
106
- hash
107
- end
108
- trace :datavault, "#{table.name} satellites are #{satellites.inspect}"
109
- satellites
110
- end
111
-
112
- def create_one_to_many(one, many, predicate_1 = 'has', predicate_2 = 'is of', one_adj = nil)
113
- # Create a fact type
114
- fact_type = @constellation.FactType(:concept => :new)
115
- one_role = @constellation.Role(:concept => :new, :fact_type => fact_type, :ordinal => 0, :object_type => one)
116
- many_role = @constellation.Role(:concept => :new, :fact_type => fact_type, :ordinal => 1, :object_type => many)
117
-
118
- # Create two readings
119
- reading2 = @constellation.Reading(:fact_type => fact_type, :ordinal => 0, :role_sequence => [:new], :text => "{0} #{predicate_2} {1}")
120
- @constellation.RoleRef(:role_sequence => reading2.role_sequence, :ordinal => 0, :role => many_role)
121
- @constellation.RoleRef(:role_sequence => reading2.role_sequence, :ordinal => 1, :role => one_role, :leading_adjective => one_adj)
122
-
123
- reading1 = @constellation.Reading(:fact_type => fact_type, :ordinal => 1, :role_sequence => [:new], :text => "{0} #{predicate_1} {1}")
124
- @constellation.RoleRef(:role_sequence => reading1.role_sequence, :ordinal => 0, :role => one_role, :leading_adjective => one_adj)
125
- @constellation.RoleRef(:role_sequence => reading1.role_sequence, :ordinal => 1, :role => many_role)
126
-
127
- one_id = @constellation.PresenceConstraint(
128
- :concept => :new,
129
- :vocabulary => @vocabulary,
130
- :name => one.name+'HasOne'+many.name,
131
- :role_sequence => [:new],
132
- :is_mandatory => true,
133
- :min_frequency => 1,
134
- :max_frequency => 1,
135
- :is_preferred_identifier => false
136
- )
137
- @constellation.RoleRef(:role_sequence => one_id.role_sequence, :ordinal => 0, :role => many_role)
138
- one_role
139
- end
140
-
141
- def assert_value_type name, supertype = nil
142
- @vocabulary.valid_value_type_name(name) ||
143
- @constellation.ValueType(:vocabulary => @vocabulary, :name => name, :supertype => supertype, :concept => :new)
144
- end
145
-
146
- def assert_record_source
147
- assert_value_type('Record Source', assert_value_type('String'))
148
- end
149
-
150
- def assert_date_time
151
- assert_value_type('Date Time')
152
- end
153
-
154
- # Create a PresenceConstraint with two roles, marked as preferred_identifier
155
- def create_two_role_identifier(r1, r2)
156
- pc = @constellation.PresenceConstraint(
157
- :concept => :new,
158
- :vocabulary => @vocabulary,
159
- :name => r1.object_type.name+' '+r1.object_type.name+'PK',
160
- :role_sequence => [:new],
161
- :is_mandatory => true,
162
- :min_frequency => 1,
163
- :max_frequency => 1,
164
- :is_preferred_identifier => true
165
- )
166
- @constellation.RoleRef(:role_sequence => pc.role_sequence, :ordinal => 0, :role => r1)
167
- @constellation.RoleRef(:role_sequence => pc.role_sequence, :ordinal => 1, :role => r2)
168
- end
169
-
170
- def create_satellite(table, satellite_name, references)
171
- satellite_name = satellite_name.words.titlewords*' '
172
- trace :datavault, "Creating #{satellite_name} for #{table.name} with #{references.size} references" do
173
- # Create a new entity type with record-date fields in its identifier
174
-
175
- satellite = @constellation.EntityType(:vocabulary => @vocabulary, :name => "#{table.name} #{satellite_name}", :concept => [:new, :implication_rule => "datavault"])
176
- satellite.definitely_table
177
-
178
- table_role = create_one_to_many(table, satellite)
179
-
180
- date_time = assert_date_time
181
- date_time_role = create_one_to_many(date_time, satellite, 'is of', 'was loaded at', 'load')
182
- create_two_role_identifier(table_role, date_time_role)
183
-
184
- record_source = assert_record_source
185
- record_source.length = 64
186
- record_source_role = create_one_to_many(record_source, satellite, 'is of', 'was loaded from')
187
-
188
- # Move all roles across to it from the parent table.
189
- references.each do |ref|
190
- trace :datavault, "Moving #{ref} across to #{table.name}_#{satellite_name}" do
191
- table_role = ref.fact_type.all_role.detect{|r| r.object_type == table}
192
- # Reassign the role player to the satellite:
193
- if table_role
194
- table_role.object_type = satellite
195
- else
196
- #debugger # Bum, the crappy Reference object bites again.
197
- $stderr.puts "REVISIT: Can't move the role for #{ref.inspect} without mangling the Reference"
198
- end
199
- end
200
- end
201
- end
202
- end
203
-
204
- def generate(out = $stdout)
205
- @out = out
206
-
207
- # Strategy:
208
- # Determine list of ER tables
209
- # Partition tables into reference tables (annotated), link tables (two+ FKs in PK), and hub tables
210
- # For each hub and link table
211
- # Apply a surrogate key if needed (all links, hubs lacking a simple surrogate)
212
- # Detect references (fact types) leading to all attributes (non-identifying columns)
213
- # Group attribute facts into satellites (use the satellite annotation if present)
214
- # For each satellite
215
- # Create a new entity type with a (hub-key, record-date key)
216
- # Make new one->many fact type between hub and satellite
217
- # Modify all attribute facts in this group to attach to the satellite
218
- # Compute a gresh relational mapping
219
- # Exclude reference tables and disable enforcement to them
220
-
221
- classify_tables
222
-
223
- detect_required_surrogates
224
-
225
- trace :datavault, "Creating satellites" do
226
- (@hub_tables+@link_tables).each do |table|
227
- satellites = classify_satellite_references table
228
- next unless satellites
229
-
230
- trace :datavault, "Creating #{satellites.size} satellites for #{table.name}" do
231
- satellites.each do |satellite_name, references|
232
- create_satellite(table, satellite_name, references)
233
- end
234
- end
235
- end
236
- end
237
-
238
- inject_required_surrogates
239
-
240
- trace :datavault, "Adding standard fields to hubs and links" do
241
- (@hub_tables+@link_tables).each do |table|
242
- date_time = assert_date_time
243
- date_time_role = create_one_to_many(date_time, table, 'is of', 'was loaded at', 'load')
244
-
245
- record_source = assert_record_source
246
- record_source_role = create_one_to_many(record_source, table, 'is of', 'was loaded from')
247
- end
248
- end
249
-
250
- # Now, redo the E-R mapping using the revised schema:
251
- @vocabulary.decide_tables
252
-
253
- # Before departing, ensure we don't emit the reference tables!
254
- @reference_tables.each do |table|
255
- table.definitely_not_table
256
- @vocabulary.tables.delete(table)
257
- end
258
-
259
- end # generate
260
-
261
- end
262
- end
263
- end
264
- end
265
-
266
- ActiveFacts::Registry.generator('transform/datavault', ActiveFacts::Generate::Transform::DataVault)
@@ -1,214 +0,0 @@
1
- #
2
- # ActiveFacts Schema Transform
3
- # Transform a loaded ActiveFacts vocabulary to suit ActiveRecord
4
- #
5
- # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
- #
7
- require 'activefacts/vocabulary'
8
- require 'activefacts/persistence'
9
-
10
- module ActiveFacts
11
- module Metamodel
12
- class ObjectType
13
-
14
- def add_surrogate type_name = 'Auto Counter', suffix = 'ID'
15
- # Find or assert the surrogate value type
16
- auto_counter = vocabulary.valid_value_type_name(type_name) ||
17
- constellation.ValueType(:vocabulary => vocabulary, :name => type_name, :concept => :new)
18
-
19
- # Create a subtype to identify this entity type:
20
- vt_name = self.name + ' '+suffix
21
- my_id = @vocabulary.valid_value_type_name(vt_name) ||
22
- constellation.ValueType(:vocabulary => vocabulary, :name => vt_name, :concept => :new, :supertype => auto_counter)
23
-
24
- # Create a fact type
25
- identifying_fact_type = constellation.FactType(:concept => :new)
26
- my_role = constellation.Role(:concept => :new, :fact_type => identifying_fact_type, :ordinal => 0, :object_type => self)
27
- @injected_surrogate_role = my_role
28
- id_role = constellation.Role(:concept => :new, :fact_type => identifying_fact_type, :ordinal => 1, :object_type => my_id)
29
-
30
- # Create a reading (which needs a RoleSequence)
31
- reading = constellation.Reading(
32
- :fact_type => identifying_fact_type,
33
- :ordinal => 0,
34
- :role_sequence => [:new],
35
- :text => "{0} has {1}"
36
- )
37
- constellation.RoleRef(:role_sequence => reading.role_sequence, :ordinal => 0, :role => my_role)
38
- constellation.RoleRef(:role_sequence => reading.role_sequence, :ordinal => 1, :role => id_role)
39
-
40
- # Create two uniqueness constraints for the one-to-one. Each needs a RoleSequence (two RoleRefs)
41
- one_id = constellation.PresenceConstraint(
42
- :concept => :new,
43
- :vocabulary => vocabulary,
44
- :name => self.name+'HasOne'+suffix,
45
- :role_sequence => [:new],
46
- :is_mandatory => true,
47
- :min_frequency => 1,
48
- :max_frequency => 1,
49
- :is_preferred_identifier => false
50
- )
51
- @constellation.RoleRef(:role_sequence => one_id.role_sequence, :ordinal => 0, :role => my_role)
52
-
53
- one_me = constellation.PresenceConstraint(
54
- :concept => :new,
55
- :vocabulary => vocabulary,
56
- :name => self.name+suffix+'IsOfOne'+self.name,
57
- :role_sequence => [:new],
58
- :is_mandatory => false,
59
- :min_frequency => 0,
60
- :max_frequency => 1,
61
- :is_preferred_identifier => true
62
- )
63
- @constellation.RoleRef(:role_sequence => one_me.role_sequence, :ordinal => 0, :role => id_role)
64
- end
65
- end
66
-
67
- class ValueType
68
- def needs_surrogate
69
- !is_auto_assigned
70
- end
71
-
72
- def inject_surrogate
73
- trace :transform_surrogate, "Adding surrogate ID to Value Type #{name}"
74
- add_surrogate('Auto Counter', 'ID')
75
- end
76
- end
77
-
78
- class EntityType
79
- def identifying_refs_from
80
- pi = preferred_identifier
81
- rrs = pi.role_sequence.all_role_ref
82
-
83
- # REVISIT: This is actually a ref to us, not from
84
- # if absorbed_via
85
- # return [absorbed_via]
86
- # end
87
-
88
- rrs.map do |rr|
89
- r = references_from.detect{|ref| rr.role == ref.to_role }
90
- raise "failed to find #{name} identifying reference for #{rr.role.object_type.name} in #{references_from.inspect}" unless r
91
- r
92
- end
93
- end
94
-
95
- def needs_surrogate
96
-
97
- # A recursive proc to replace any reference to an Entity Type by its identifying references:
98
- trace :transform_surrogate_expansion, "Expanding key for #{name}"
99
- substitute_identifying_refs = proc do |object|
100
- if ref = object.absorbed_via
101
- # This shouldn't be necessary, but see the absorbed_via comment above.
102
- absorbed_into = ref.from
103
- trace :transform_surrogate_expansion, "recursing to handle absorption of #{object.name} into #{absorbed_into.name}"
104
- [substitute_identifying_refs.call(absorbed_into)]
105
- else
106
- irf = object.identifying_refs_from
107
- trace :transform_surrogate_expansion, "Iterating for #{object.name} over #{irf.inspect}" do
108
- irf.each_with_index do |ref, i|
109
- next if ref.is_unary
110
- next if ref.to_role.object_type.kind_of?(ActiveFacts::Metamodel::ValueType)
111
- recurse_to = ref.to_role.object_type
112
-
113
- trace :transform_surrogate_expansion, "#{i}: recursing to expand #{recurse_to.name} key in #{ref}" do
114
- irf[i] = substitute_identifying_refs.call(recurse_to)
115
- end
116
- end
117
- end
118
- irf
119
- end
120
- end
121
- irf = substitute_identifying_refs.call(self)
122
-
123
- trace :transform_surrogate, "Does #{name} need a surrogate? it's identified by #{irf.inspect}" do
124
-
125
- pk_fks = identifying_refs_from.map do |ref|
126
- ref.to && ref.to.is_table ? ref.to : nil
127
- end
128
-
129
- irf.flatten!
130
-
131
- # Multi-part identifiers are only allowed if:
132
- # * each part is a foreign key (i.e. it's a join table),
133
- # * there are no other columns (that might require updating) and
134
- # * the object is not the target of a foreign key:
135
- if irf.size >= 2
136
- if pk_fks.include?(nil)
137
- trace :transform_surrogate, "#{self.name} needs a surrogate because its multi-part key contains a non-table"
138
- return true
139
- elsif references_to.size != 0
140
- trace :transform_surrogate, "#{self.name} is a join table between #{pk_fks.map(&:name).inspect} but is also an FK target"
141
- return true
142
- elsif (references_from-identifying_refs_from).size > 0
143
- # There are other attributes to worry about
144
- return true
145
- else
146
- trace :transform_surrogate, "#{self.name} is a join table between #{pk_fks.map(&:name).inspect}"
147
- return false
148
- end
149
- return true
150
- end
151
-
152
- # Single-part key. It must be an Auto Counter, or we will add a surrogate
153
-
154
- identifying_type = irf[0].to
155
- if identifying_type.needs_surrogate
156
- trace :transform_surrogate, "#{self.name} needs a surrogate because #{irf[0].to.name} is not an AutoCounter, but #{identifying_type.supertypes_transitive.map(&:name).inspect}"
157
- return true
158
- end
159
-
160
- false
161
- end
162
- end
163
-
164
- def inject_surrogate
165
- trace :transform_surrogate, "Injecting a surrogate key into #{self.name}"
166
-
167
- # Disable the preferred identifier:
168
- pi = preferred_identifier
169
- trace :transform_surrogate, "pi for #{name} was '#{pi.describe}'"
170
- pi.is_preferred_identifier = false
171
- @preferred_identifier = nil # Kill the cache
172
-
173
- add_surrogate
174
-
175
- trace :transform_surrogate, "pi for #{name} is now '#{preferred_identifier.describe}'"
176
- end
177
-
178
- end
179
- end
180
-
181
- module Persistence
182
- class Column
183
- def is_injected_surrogate
184
- references.size == 1 and
185
- references[0].from_role == references[0].from.injected_surrogate_role
186
- end
187
- end
188
- end
189
-
190
- module Generate #:nodoc:
191
- module Transform #:nodoc:
192
- class Surrogate
193
- def initialize(vocabulary, *options)
194
- @vocabulary = vocabulary
195
- end
196
-
197
- def generate(out = $stdout)
198
- @out = out
199
- injections =
200
- @vocabulary.tables.select do |table|
201
- table.needs_surrogate
202
- end
203
- injections.each do |table|
204
- table.inject_surrogate
205
- end
206
-
207
- @vocabulary.decide_tables
208
- end
209
- end
210
- end
211
- end
212
- end
213
-
214
- ActiveFacts::Registry.generator('transform/surrogate', ActiveFacts::Generate::Transform::Surrogate)