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.
- checksums.yaml +4 -4
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +21 -0
- data/README.md +60 -0
- data/Rakefile +3 -80
- data/activefacts.gemspec +36 -0
- data/bin/afgen +4 -2
- data/bin/cql +5 -1
- data/lib/activefacts.rb +3 -12
- data/lib/activefacts/{vocabulary/query_evaluator.rb → query/evaluator.rb} +0 -0
- data/lib/activefacts/version.rb +2 -2
- metadata +48 -296
- data/History.txt +0 -4
- data/LICENSE +0 -19
- data/Manifest.txt +0 -165
- data/README.rdoc +0 -81
- data/css/offline.css +0 -3
- data/css/orm2.css +0 -124
- data/css/print.css +0 -8
- data/css/style-print.css +0 -357
- data/css/style.css +0 -387
- data/download.html +0 -110
- data/examples/CQL/Address.cql +0 -44
- data/examples/CQL/Blog.cql +0 -54
- data/examples/CQL/CompanyDirectorEmployee.cql +0 -56
- data/examples/CQL/Death.cql +0 -17
- data/examples/CQL/Diplomacy.cql +0 -48
- data/examples/CQL/Genealogy.cql +0 -98
- data/examples/CQL/Insurance.cql +0 -320
- data/examples/CQL/Marriage.cql +0 -18
- data/examples/CQL/Metamodel.cql +0 -493
- data/examples/CQL/Monogamy.cql +0 -24
- data/examples/CQL/MultiInheritance.cql +0 -22
- data/examples/CQL/NonRoleId.cql +0 -14
- data/examples/CQL/OddIdentifier.cql +0 -18
- data/examples/CQL/OilSupply.cql +0 -53
- data/examples/CQL/OneToOnes.cql +0 -17
- data/examples/CQL/Orienteering.cql +0 -111
- data/examples/CQL/PersonPlaysGame.cql +0 -18
- data/examples/CQL/RedundantDependency.cql +0 -34
- data/examples/CQL/SchoolActivities.cql +0 -33
- data/examples/CQL/SeparateSubtype.cql +0 -30
- data/examples/CQL/ServiceDirector.cql +0 -276
- data/examples/CQL/SimplestUnary.cql +0 -12
- data/examples/CQL/Supervision.cql +0 -34
- data/examples/CQL/WaiterTips.cql +0 -33
- data/examples/CQL/Warehousing.cql +0 -101
- data/examples/CQL/WindowInRoomInBldg.cql +0 -28
- data/examples/CQL/unit.cql +0 -474
- data/examples/index.html +0 -420
- data/examples/intro.html +0 -327
- data/examples/local.css +0 -24
- data/index.html +0 -111
- data/lib/activefacts/cql.rb +0 -35
- data/lib/activefacts/cql/CQLParser.treetop +0 -158
- data/lib/activefacts/cql/Context.treetop +0 -48
- data/lib/activefacts/cql/Expressions.treetop +0 -67
- data/lib/activefacts/cql/FactTypes.treetop +0 -358
- data/lib/activefacts/cql/Language/English.treetop +0 -315
- data/lib/activefacts/cql/LexicalRules.treetop +0 -253
- data/lib/activefacts/cql/ObjectTypes.treetop +0 -210
- data/lib/activefacts/cql/Rakefile +0 -14
- data/lib/activefacts/cql/Terms.treetop +0 -183
- data/lib/activefacts/cql/ValueTypes.treetop +0 -202
- data/lib/activefacts/cql/compiler.rb +0 -156
- data/lib/activefacts/cql/compiler/clause.rb +0 -1137
- data/lib/activefacts/cql/compiler/constraint.rb +0 -581
- data/lib/activefacts/cql/compiler/entity_type.rb +0 -457
- data/lib/activefacts/cql/compiler/expression.rb +0 -443
- data/lib/activefacts/cql/compiler/fact.rb +0 -390
- data/lib/activefacts/cql/compiler/fact_type.rb +0 -421
- data/lib/activefacts/cql/compiler/query.rb +0 -106
- data/lib/activefacts/cql/compiler/shared.rb +0 -161
- data/lib/activefacts/cql/compiler/value_type.rb +0 -174
- data/lib/activefacts/cql/nodes.rb +0 -49
- data/lib/activefacts/cql/parser.rb +0 -241
- data/lib/activefacts/dependency_analyser.rb +0 -182
- data/lib/activefacts/generate/absorption.rb +0 -70
- data/lib/activefacts/generate/composition.rb +0 -118
- data/lib/activefacts/generate/cql.rb +0 -714
- data/lib/activefacts/generate/dm.rb +0 -279
- data/lib/activefacts/generate/help.rb +0 -64
- data/lib/activefacts/generate/helpers/inject.rb +0 -16
- data/lib/activefacts/generate/helpers/oo.rb +0 -162
- data/lib/activefacts/generate/helpers/ordered.rb +0 -605
- data/lib/activefacts/generate/helpers/rails.rb +0 -57
- data/lib/activefacts/generate/html/glossary.rb +0 -461
- data/lib/activefacts/generate/json.rb +0 -337
- data/lib/activefacts/generate/null.rb +0 -32
- data/lib/activefacts/generate/rails/models.rb +0 -246
- data/lib/activefacts/generate/rails/schema.rb +0 -216
- data/lib/activefacts/generate/records.rb +0 -46
- data/lib/activefacts/generate/ruby.rb +0 -133
- data/lib/activefacts/generate/sql/mysql.rb +0 -280
- data/lib/activefacts/generate/sql/server.rb +0 -273
- data/lib/activefacts/generate/stats.rb +0 -69
- data/lib/activefacts/generate/text.rb +0 -27
- data/lib/activefacts/generate/topics.rb +0 -265
- data/lib/activefacts/generate/traits/datavault.rb +0 -241
- data/lib/activefacts/generate/traits/oo.rb +0 -73
- data/lib/activefacts/generate/traits/ordered.rb +0 -33
- data/lib/activefacts/generate/traits/ruby.rb +0 -210
- data/lib/activefacts/generate/transform/datavault.rb +0 -266
- data/lib/activefacts/generate/transform/surrogate.rb +0 -214
- data/lib/activefacts/generate/version.rb +0 -26
- data/lib/activefacts/input/cql.rb +0 -43
- data/lib/activefacts/input/orm.rb +0 -1636
- data/lib/activefacts/mapping/rails.rb +0 -132
- data/lib/activefacts/persistence.rb +0 -15
- data/lib/activefacts/persistence/columns.rb +0 -446
- data/lib/activefacts/persistence/foreignkey.rb +0 -187
- data/lib/activefacts/persistence/index.rb +0 -240
- data/lib/activefacts/persistence/object_type.rb +0 -198
- data/lib/activefacts/persistence/reference.rb +0 -434
- data/lib/activefacts/persistence/tables.rb +0 -380
- data/lib/activefacts/registry.rb +0 -11
- data/lib/activefacts/support.rb +0 -132
- data/lib/activefacts/vocabulary.rb +0 -9
- data/lib/activefacts/vocabulary/extensions.rb +0 -1348
- data/lib/activefacts/vocabulary/metamodel.rb +0 -570
- data/lib/activefacts/vocabulary/verbaliser.rb +0 -804
- data/script/txt2html +0 -71
- data/spec/absorption_spec.rb +0 -95
- data/spec/cql/comparison_spec.rb +0 -89
- data/spec/cql/context_spec.rb +0 -94
- data/spec/cql/contractions_spec.rb +0 -224
- data/spec/cql/deontic_spec.rb +0 -88
- data/spec/cql/entity_type_spec.rb +0 -320
- data/spec/cql/expressions_spec.rb +0 -66
- data/spec/cql/fact_type_matching_spec.rb +0 -338
- data/spec/cql/french_spec.rb +0 -21
- data/spec/cql/parser/bad_literals_spec.rb +0 -86
- data/spec/cql/parser/constraints_spec.rb +0 -19
- data/spec/cql/parser/entity_types_spec.rb +0 -106
- data/spec/cql/parser/expressions_spec.rb +0 -199
- data/spec/cql/parser/fact_types_spec.rb +0 -44
- data/spec/cql/parser/literals_spec.rb +0 -312
- data/spec/cql/parser/pragmas_spec.rb +0 -89
- data/spec/cql/parser/value_types_spec.rb +0 -42
- data/spec/cql/role_matching_spec.rb +0 -148
- data/spec/cql/samples_spec.rb +0 -244
- data/spec/cql_cql_spec.rb +0 -73
- data/spec/cql_dm_spec.rb +0 -136
- data/spec/cql_mysql_spec.rb +0 -69
- data/spec/cql_parse_spec.rb +0 -34
- data/spec/cql_ruby_spec.rb +0 -73
- data/spec/cql_sql_spec.rb +0 -72
- data/spec/cql_symbol_tables_spec.rb +0 -261
- data/spec/cqldump_spec.rb +0 -170
- data/spec/helpers/array_matcher.rb +0 -23
- data/spec/helpers/ctrl_c_support.rb +0 -52
- data/spec/helpers/diff_matcher.rb +0 -39
- data/spec/helpers/file_matcher.rb +0 -34
- data/spec/helpers/parse_to_ast_matcher.rb +0 -80
- data/spec/helpers/string_matcher.rb +0 -30
- data/spec/helpers/test_parser.rb +0 -15
- data/spec/norma_cql_spec.rb +0 -66
- data/spec/norma_ruby_spec.rb +0 -62
- data/spec/norma_ruby_sql_spec.rb +0 -107
- data/spec/norma_sql_spec.rb +0 -57
- data/spec/norma_tables_spec.rb +0 -95
- data/spec/ruby_api_spec.rb +0 -23
- data/spec/spec_helper.rb +0 -35
- data/spec/transform_surrogate_spec.rb +0 -59
- data/status.html +0 -138
- 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)
|