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,273 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# ActiveFacts Generators.
|
|
3
|
-
# Generate SQL for SQL Server from an ActiveFacts vocabulary.
|
|
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 Generate
|
|
12
|
-
module SQL #:nodoc:
|
|
13
|
-
# Generate SQL for SQL Server for an ActiveFacts vocabulary.
|
|
14
|
-
# Invoke as
|
|
15
|
-
# afgen --sql/server[=options] <file>.cql
|
|
16
|
-
# Options are comma or space separated:
|
|
17
|
-
# * delay_fks Leave all foreign keys until the end, not just those that contain forward-references
|
|
18
|
-
class SERVER
|
|
19
|
-
private
|
|
20
|
-
include Persistence
|
|
21
|
-
ColumnNameMax = 40
|
|
22
|
-
|
|
23
|
-
RESERVED_WORDS = %w{
|
|
24
|
-
ADD ALL ALTER AND ANY AS ASC AUTHORIZATION BACKUP BEGIN BETWEEN
|
|
25
|
-
BREAK BROWSE BULK BY CASCADE CASE CHECK CHECKPOINT CLOSE CLUSTERED
|
|
26
|
-
COALESCE COLLATE COLUMN COMMIT COMPUTE CONSTRAINT CONTAINS CONTAINSTABLE
|
|
27
|
-
CONTINUE CONVERT CREATE CROSS CURRENT CURRENT_DATE CURRENT_TIME
|
|
28
|
-
CURRENT_TIMESTAMP CURRENT_USER CURSOR DATABASE DBCC DEALLOCATE
|
|
29
|
-
DECLARE DEFAULT DELETE DENY DESC DISK DISTINCT DISTRIBUTED DOUBLE
|
|
30
|
-
DROP DUMMY DUMP ELSE END ERRLVL ESCAPE EXCEPT EXEC EXECUTE EXISTS
|
|
31
|
-
EXIT FETCH FILE FILLFACTOR FOR FOREIGN FREETEXT FREETEXTTABLE FROM
|
|
32
|
-
FULL FUNCTION GOTO GRANT GROUP HAVING HOLDLOCK IDENTITY IDENTITYCOL
|
|
33
|
-
IDENTITY_INSERT IF IN INDEX INNER INSERT INTERSECT INTO IS JOIN KEY
|
|
34
|
-
KILL LEFT LIKE LINENO LOAD NATIONAL NOCHECK NONCLUSTERED NOT NULL
|
|
35
|
-
NULLIF OF OFF OFFSETS ON OPEN OPENDATASOURCE OPENQUERY OPENROWSET
|
|
36
|
-
OPENXML OPTION OR ORDER OUTER OVER PERCENT PLAN PRECISION PRIMARY
|
|
37
|
-
PRINT PROC PROCEDURE PUBLIC RAISERROR READ READTEXT RECONFIGURE
|
|
38
|
-
REFERENCES REPLICATION RESTORE RESTRICT RETURN REVOKE RIGHT ROLLBACK
|
|
39
|
-
ROWCOUNT ROWGUIDCOL RULE SAVE SCHEMA SELECT SESSION_USER SET SETUSER
|
|
40
|
-
SHUTDOWN SOME STATISTICS SYSTEM_USER TABLE TEXTSIZE THEN TO TOP
|
|
41
|
-
TRAN TRANSACTION TRIGGER TRUNCATE TSEQUAL UNION UNIQUE UPDATE
|
|
42
|
-
UPDATETEXT USE USER VALUES VARYING VIEW WAITFOR WHEN WHERE WHILE
|
|
43
|
-
WITH WRITETEXT
|
|
44
|
-
}.inject({}){ |h,w| h[w] = true; h }
|
|
45
|
-
|
|
46
|
-
def initialize(vocabulary, *options)
|
|
47
|
-
@vocabulary = vocabulary
|
|
48
|
-
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
|
49
|
-
@delay_fks = options.include? "delay_fks"
|
|
50
|
-
@underscore = options.include?("underscore") ? "_" : ""
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def puts s
|
|
54
|
-
@out.puts s
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def go s
|
|
58
|
-
puts s
|
|
59
|
-
puts "GO\n\n"
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def escape s
|
|
63
|
-
# Escape SQL keywords and non-identifiers
|
|
64
|
-
s = s[0...120]
|
|
65
|
-
if s =~ /[^A-Za-z0-9_]/ || RESERVED_WORDS[s.upcase]
|
|
66
|
-
"[#{s}]"
|
|
67
|
-
else
|
|
68
|
-
s
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Return SQL type and (modified?) length for the passed base type
|
|
73
|
-
def normalise_type(type, length)
|
|
74
|
-
sql_type = case type
|
|
75
|
-
when /^Auto ?Counter$/
|
|
76
|
-
'int'
|
|
77
|
-
|
|
78
|
-
when /^Unsigned ?Integer$/,
|
|
79
|
-
/^Signed ?Integer$/,
|
|
80
|
-
/^Unsigned ?Small ?Integer$/,
|
|
81
|
-
/^Signed ?Small ?Integer$/,
|
|
82
|
-
/^Unsigned ?Tiny ?Integer$/
|
|
83
|
-
s = case
|
|
84
|
-
when length <= 8
|
|
85
|
-
'tinyint'
|
|
86
|
-
when length <= 16
|
|
87
|
-
'smallint'
|
|
88
|
-
when length <= 32
|
|
89
|
-
'int'
|
|
90
|
-
else
|
|
91
|
-
'bigint'
|
|
92
|
-
end
|
|
93
|
-
length = nil
|
|
94
|
-
s
|
|
95
|
-
|
|
96
|
-
when /^Decimal$/
|
|
97
|
-
'decimal'
|
|
98
|
-
|
|
99
|
-
when /^Fixed ?Length ?Text$/, /^Char$/
|
|
100
|
-
'char'
|
|
101
|
-
when /^Variable ?Length ?Text$/, /^String$/
|
|
102
|
-
'varchar'
|
|
103
|
-
when /^Large ?Length ?Text$/, /^Text$/
|
|
104
|
-
'text'
|
|
105
|
-
|
|
106
|
-
when /^Date ?And ?Time$/, /^Date ?Time$/
|
|
107
|
-
'datetime'
|
|
108
|
-
when /^Date$/
|
|
109
|
-
'datetime' # SQLSVR 2K5: 'date'
|
|
110
|
-
when /^Time$/
|
|
111
|
-
'datetime' # SQLSVR 2K5: 'time'
|
|
112
|
-
when /^Auto ?Time ?Stamp$/
|
|
113
|
-
'timestamp'
|
|
114
|
-
|
|
115
|
-
when /^Guid$/
|
|
116
|
-
'uniqueidentifier'
|
|
117
|
-
when /^Money$/
|
|
118
|
-
'decimal'
|
|
119
|
-
when /^Picture ?Raw ?Data$/, /^Image$/
|
|
120
|
-
'image'
|
|
121
|
-
when /^Variable ?Length ?Raw ?Data$/, /^Blob$/
|
|
122
|
-
'varbinary'
|
|
123
|
-
when /^BIT$/
|
|
124
|
-
'bit'
|
|
125
|
-
else type # raise "SQL type unknown for standard type #{type}"
|
|
126
|
-
end
|
|
127
|
-
[sql_type, length]
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
public
|
|
131
|
-
def generate(out = $>) #:nodoc:
|
|
132
|
-
@out = out
|
|
133
|
-
#go "CREATE SCHEMA #{@vocabulary.name}"
|
|
134
|
-
|
|
135
|
-
tables_emitted = {}
|
|
136
|
-
delayed_foreign_keys = []
|
|
137
|
-
|
|
138
|
-
@vocabulary.tables.each do |table|
|
|
139
|
-
puts "CREATE TABLE #{escape table.name.gsub(' ',@underscore)} ("
|
|
140
|
-
|
|
141
|
-
pk = table.identifier_columns
|
|
142
|
-
identity_column = pk[0] if pk[0].is_auto_assigned
|
|
143
|
-
|
|
144
|
-
fk_refs = table.references_from.select{|ref| ref.is_simple_reference }
|
|
145
|
-
fk_columns = table.columns.select do |column|
|
|
146
|
-
column.references[0].is_simple_reference
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
# We sort the columns here, not in the persistence layer, because it affects
|
|
150
|
-
# the ordering of columns in an index :-(.
|
|
151
|
-
columns = table.columns.sort_by { |column| column.name(@underscore) }.map do |column|
|
|
152
|
-
name = escape column.name(@underscore)
|
|
153
|
-
padding = " "*(name.size >= ColumnNameMax ? 1 : ColumnNameMax-name.size)
|
|
154
|
-
type, params, constraints = column.type
|
|
155
|
-
constraints = [] if (fk_columns.include?(column)) # Don't enforce VT constraints on FK columns
|
|
156
|
-
length = params[:length]
|
|
157
|
-
length &&= length.to_i
|
|
158
|
-
scale = params[:scale]
|
|
159
|
-
scale &&= scale.to_i
|
|
160
|
-
type, length = normalise_type(type, length)
|
|
161
|
-
sql_type = "#{type}#{
|
|
162
|
-
if !length
|
|
163
|
-
""
|
|
164
|
-
else
|
|
165
|
-
"(" + length.to_s + (scale ? ", #{scale}" : "") + ")"
|
|
166
|
-
end
|
|
167
|
-
}"
|
|
168
|
-
# Emit IDENTITY for auto-assigned columns, unless it's assigned at assert:
|
|
169
|
-
identity = column == identity_column && column.references[-1].to.transaction_phase != 'assert' ? " IDENTITY" : ""
|
|
170
|
-
null = (column.is_mandatory ? "NOT " : "") + "NULL"
|
|
171
|
-
check = check_clause(name, constraints)
|
|
172
|
-
comment = column.comment
|
|
173
|
-
[ "-- #{comment}", "#{name}#{padding}#{sql_type}#{identity} #{null}#{check}" ]
|
|
174
|
-
end.flatten
|
|
175
|
-
|
|
176
|
-
pk_def = (pk.detect{|column| !column.is_mandatory} ? "UNIQUE(" : "PRIMARY KEY(") +
|
|
177
|
-
pk.map{|column| escape column.name(@underscore)}*", " +
|
|
178
|
-
")"
|
|
179
|
-
|
|
180
|
-
inline_fks = []
|
|
181
|
-
table.foreign_keys.each do |fk|
|
|
182
|
-
fk_text = "FOREIGN KEY (" +
|
|
183
|
-
fk.from_columns.map{|column| column.name(@underscore)}*", " +
|
|
184
|
-
") REFERENCES #{escape fk.to.name.gsub(' ',@underscore)} (" +
|
|
185
|
-
fk.to_columns.map{|column| column.name(@underscore)}*", " +
|
|
186
|
-
")"
|
|
187
|
-
if !@delay_fks and # We don't want to delay all Fks
|
|
188
|
-
(tables_emitted[fk.to] or # The target table has been emitted
|
|
189
|
-
fk.to == table && !fk.to_columns.detect{|column| !column.is_mandatory}) # The reference columns already have the required indexes
|
|
190
|
-
inline_fks << fk_text
|
|
191
|
-
else
|
|
192
|
-
delayed_foreign_keys << ("ALTER TABLE #{escape fk.from.name.gsub(' ',@underscore)}\n\tADD " + fk_text)
|
|
193
|
-
end
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
indices = table.indices
|
|
197
|
-
inline_indices = []
|
|
198
|
-
delayed_indices = []
|
|
199
|
-
indices.each do |index|
|
|
200
|
-
next if index.over == table && index.is_primary # Already did the primary keys
|
|
201
|
-
abbreviated_column_names = index.abbreviated_column_names(@underscore)*""
|
|
202
|
-
column_names = index.column_names(@underscore)
|
|
203
|
-
column_name_list = column_names.map{|n| escape(n)}*", "
|
|
204
|
-
if index.columns.all?{|column| column.is_mandatory}
|
|
205
|
-
inline_indices << "UNIQUE(#{column_name_list})"
|
|
206
|
-
else
|
|
207
|
-
view_name = escape "#{index.view_name}_#{abbreviated_column_names}"
|
|
208
|
-
delayed_indices <<
|
|
209
|
-
%Q{CREATE VIEW dbo.#{view_name} (#{column_name_list}) WITH SCHEMABINDING AS
|
|
210
|
-
\tSELECT #{column_name_list} FROM dbo.#{escape index.on.name.gsub(' ',@underscore)}
|
|
211
|
-
\tWHERE\t#{
|
|
212
|
-
index.columns.
|
|
213
|
-
select{|column| !column.is_mandatory }.
|
|
214
|
-
map{|column|
|
|
215
|
-
escape(column.name(@underscore)) + " IS NOT NULL"
|
|
216
|
-
}*"\n\t AND\t"
|
|
217
|
-
}
|
|
218
|
-
GO
|
|
219
|
-
|
|
220
|
-
CREATE UNIQUE CLUSTERED INDEX #{escape index.name} ON dbo.#{view_name}(#{index.columns.map{|column| column.name(@underscore)}*", "})
|
|
221
|
-
}
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
tables_emitted[table] = true
|
|
226
|
-
|
|
227
|
-
puts("\t" + (columns + [pk_def] + inline_indices + inline_fks)*",\n\t")
|
|
228
|
-
go ")"
|
|
229
|
-
delayed_indices.each {|index_text|
|
|
230
|
-
go index_text
|
|
231
|
-
}
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
delayed_foreign_keys.each do |fk|
|
|
235
|
-
go fk
|
|
236
|
-
end
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
private
|
|
240
|
-
def sql_value(value)
|
|
241
|
-
value.is_literal_string ? sql_string(value.literal) : value.literal
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
def sql_string(str)
|
|
245
|
-
"'" + str.gsub(/'/,"''") + "'"
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
def check_clause(column_name, constraints)
|
|
249
|
-
return "" if constraints.empty?
|
|
250
|
-
# REVISIT: Merge all constraints (later; now just use the first)
|
|
251
|
-
" CHECK(" +
|
|
252
|
-
constraints[0].all_allowed_range_sorted.map do |ar|
|
|
253
|
-
vr = ar.value_range
|
|
254
|
-
min = vr.minimum_bound
|
|
255
|
-
max = vr.maximum_bound
|
|
256
|
-
if (min && max && max.value.literal == min.value.literal)
|
|
257
|
-
"#{column_name} = #{sql_value(min.value)}"
|
|
258
|
-
else
|
|
259
|
-
inequalities = [
|
|
260
|
-
min && "#{column_name} >#{min.is_inclusive ? "=" : ""} #{sql_value(min.value)}",
|
|
261
|
-
max && "#{column_name} <#{max.is_inclusive ? "=" : ""} #{sql_value(max.value)}"
|
|
262
|
-
].compact
|
|
263
|
-
inequalities.size > 1 ? "(" + inequalities*" AND " + ")" : inequalities[0]
|
|
264
|
-
end
|
|
265
|
-
end*" OR " +
|
|
266
|
-
")"
|
|
267
|
-
end
|
|
268
|
-
end
|
|
269
|
-
end
|
|
270
|
-
end
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
ActiveFacts::Registry.generator('sql/server', ActiveFacts::Generate::SQL::SERVER)
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# ActiveFacts Generators.
|
|
3
|
-
# Generate metamodel statistics fora compiled vocabulary
|
|
4
|
-
#
|
|
5
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
|
6
|
-
#
|
|
7
|
-
require 'activefacts/persistence'
|
|
8
|
-
|
|
9
|
-
module ActiveFacts
|
|
10
|
-
module Generate
|
|
11
|
-
# Generate a text verbalisation of the metamodel constellation created for an ActiveFacts vocabulary.
|
|
12
|
-
# Invoke as
|
|
13
|
-
# afgen --text <file>.cql
|
|
14
|
-
class Statistics
|
|
15
|
-
private
|
|
16
|
-
def initialize(vocabulary)
|
|
17
|
-
@vocabulary = vocabulary
|
|
18
|
-
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
public
|
|
22
|
-
def generate(out = $>)
|
|
23
|
-
constellation = @vocabulary.constellation
|
|
24
|
-
object_types = constellation.ObjectType.values
|
|
25
|
-
fact_types = constellation.FactType.values
|
|
26
|
-
|
|
27
|
-
# All metamodel object types:
|
|
28
|
-
object_count = 0
|
|
29
|
-
populated_object_type_count = 0
|
|
30
|
-
fact_types_processed = {}
|
|
31
|
-
fact_count = 0
|
|
32
|
-
role_played_count = 0
|
|
33
|
-
constellation.vocabulary.object_type.map do |object_type_name, object_type|
|
|
34
|
-
objects = constellation.send(object_type_name)
|
|
35
|
-
next unless objects.size > 0
|
|
36
|
-
puts "\t#{object_type_name}: #{objects.size} instances (which play #{object_type.all_role.size} roles)"
|
|
37
|
-
populated_object_type_count += 1
|
|
38
|
-
object_count += objects.size
|
|
39
|
-
|
|
40
|
-
#puts "#{object_type_name} has #{object_type.all_role.size} roles"
|
|
41
|
-
object_type.all_role.each do |name, role|
|
|
42
|
-
next unless role.unique
|
|
43
|
-
next if fact_types_processed[role.fact_type]
|
|
44
|
-
next if role.fact_type.is_a?(ActiveFacts::API::TypeInheritanceFactType)
|
|
45
|
-
role_population_count =
|
|
46
|
-
objects.values.inject(0) do |count, object|
|
|
47
|
-
count += 1 if object.send(role.name) != nil
|
|
48
|
-
count
|
|
49
|
-
end
|
|
50
|
-
puts "\t\t#{object_type_name}.#{role.name} has #{role_population_count} instances" if role_population_count > 0
|
|
51
|
-
fact_count += role_population_count
|
|
52
|
-
role_played_count += role_population_count*role.fact_type.all_role.size
|
|
53
|
-
|
|
54
|
-
fact_types_processed[role.fact_type] = true
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
end
|
|
58
|
-
puts "#{@vocabulary.name} has"
|
|
59
|
-
puts "\t#{object_types.size} object types"
|
|
60
|
-
puts "\t#{fact_types.size} fact types"
|
|
61
|
-
puts "\tcompiles to #{object_count} objects in total, of #{populated_object_type_count} metamodel types"
|
|
62
|
-
puts "\tcompiles to #{fact_count} facts in total, of #{fact_types_processed.size} metamodel fact types"
|
|
63
|
-
puts "\tcompiles to #{role_played_count} role instances in total"
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
ActiveFacts::Registry.generator('records', ActiveFacts::Generate::Statistics)
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# ActiveFacts Generators.
|
|
3
|
-
# Generate text output (verbalise the meta-vocabulary) for ActiveFacts vocabularies.
|
|
4
|
-
#
|
|
5
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
|
6
|
-
#
|
|
7
|
-
module ActiveFacts
|
|
8
|
-
module Generate
|
|
9
|
-
# Generate a text verbalisation of the metamodel constellation created for an ActiveFacts vocabulary.
|
|
10
|
-
# Invoke as
|
|
11
|
-
# afgen --text <file>.cql
|
|
12
|
-
class TEXT
|
|
13
|
-
private
|
|
14
|
-
def initialize(vocabulary)
|
|
15
|
-
@vocabulary = vocabulary
|
|
16
|
-
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
public
|
|
20
|
-
def generate(out = $>)
|
|
21
|
-
out.puts @vocabulary.constellation.verbalise
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
ActiveFacts::Registry.generator('text', ActiveFacts::Generate::TEXT)
|
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# ActiveFacts Generators.
|
|
3
|
-
# Generate metamodel topic hierarchy (topologically sorted) for a compiled vocabulary
|
|
4
|
-
#
|
|
5
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
|
6
|
-
#
|
|
7
|
-
require 'activefacts/dependency_analyser'
|
|
8
|
-
require 'activefacts/persistence'
|
|
9
|
-
|
|
10
|
-
module ActiveFacts
|
|
11
|
-
module Generate
|
|
12
|
-
# Generate a topic hierarchy of the metamodel constellation created for an ActiveFacts vocabulary.
|
|
13
|
-
# Invoke as
|
|
14
|
-
# afgen --topics <file>.cql
|
|
15
|
-
class Topics
|
|
16
|
-
private
|
|
17
|
-
def initialize(vocabulary)
|
|
18
|
-
@vocabulary = vocabulary
|
|
19
|
-
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
public
|
|
23
|
-
def generate(out = $>)
|
|
24
|
-
@constellation = @vocabulary.constellation
|
|
25
|
-
|
|
26
|
-
@concepts = @constellation.Concept.values.select do |c|
|
|
27
|
-
!c.embodied_as.is_a?(ActiveFacts::Metamodel::Role) &&
|
|
28
|
-
!c.implication_rule
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
@concept_deps = ActiveFacts::DependencyAnalyser.new(@concepts) {|c| c.precursors }
|
|
32
|
-
|
|
33
|
-
# list_concepts(@concept_deps)
|
|
34
|
-
# list_precursors(@concept_deps)
|
|
35
|
-
# list_followers(@concept_deps)
|
|
36
|
-
# list_chasers(@concept_deps)
|
|
37
|
-
# list_ranks(@concept_deps)
|
|
38
|
-
|
|
39
|
-
# @constellation.Topic.values.each do |topic|
|
|
40
|
-
# puts "#{topic.topic_name} depends on #{topic.precursors.map(&:topic_name)*', '}"
|
|
41
|
-
# end
|
|
42
|
-
|
|
43
|
-
# analyse_topic_sequence
|
|
44
|
-
|
|
45
|
-
list_groups_by_rank @concept_deps
|
|
46
|
-
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def list_concepts concept_deps
|
|
50
|
-
puts "Concepts are"
|
|
51
|
-
concept_deps.each do |concept|
|
|
52
|
-
puts "\t#{concept.describe}"
|
|
53
|
-
end
|
|
54
|
-
puts
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def list_precursors(concept_deps)
|
|
58
|
-
puts "Precursors are"
|
|
59
|
-
concept_deps.each do |concept|
|
|
60
|
-
next unless (p = concept_deps.precursors(concept)).size > 0
|
|
61
|
-
puts "#{concept.describe}:"
|
|
62
|
-
p.each do |precursor|
|
|
63
|
-
puts "\t#{precursor.describe}"
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def list_followers(concept_deps)
|
|
69
|
-
puts "Followers are:"
|
|
70
|
-
concept_deps.each do |concept|
|
|
71
|
-
next unless (f = concept_deps.followers(concept)).size > 0
|
|
72
|
-
puts "#{concept.describe}:"
|
|
73
|
-
f.each do |follower|
|
|
74
|
-
puts "\t#{follower.describe}"
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def list_chasers(concept_deps)
|
|
80
|
-
puts "Chasers are:"
|
|
81
|
-
concept_deps.each do |concept|
|
|
82
|
-
next unless (f = concept_deps.chasers(concept)).size > 0
|
|
83
|
-
puts "#{concept.describe}:"
|
|
84
|
-
f.each do |follower|
|
|
85
|
-
puts "\t#{follower.describe}"
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def list_ranks(concept_deps)
|
|
91
|
-
puts "Ranks are:"
|
|
92
|
-
page_rank = concept_deps.page_rank
|
|
93
|
-
page_rank.keys.sort_by{|k| -page_rank[k]}.each do |concept|
|
|
94
|
-
puts("%8.4f\t%s" % [page_rank[concept], concept.describe])
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def analyse_topic_sequence
|
|
99
|
-
if @constellation.Topic.size == 1
|
|
100
|
-
@topics = @constellation.Topic.values.first
|
|
101
|
-
else
|
|
102
|
-
puts "=== Analysing existing topics ==="
|
|
103
|
-
@topic_analyser = ActiveFacts::DependencyAnalyser.new(@constellation.Topic.values) do |topic|
|
|
104
|
-
topic.precursors
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
@topics = []
|
|
108
|
-
failed_topics = @topic_analyser.tsort { |topic|
|
|
109
|
-
@topics << topic
|
|
110
|
-
puts "#{topic.topic_name} including #{topic.all_concept.size} concepts"
|
|
111
|
-
}
|
|
112
|
-
if failed_topics
|
|
113
|
-
puts "Topological sort of topics is impossible"
|
|
114
|
-
|
|
115
|
-
# REVISIT: The strategy here should be to look at the structure of the dependency loop
|
|
116
|
-
# involving the highest-rank topic. Choose the loop member that has fewest dependencies
|
|
117
|
-
# involving individual concepts, and dump that (it will need to flip topic to dump
|
|
118
|
-
# those dependencies). That should break the loop in a minimal way, so we can continue.
|
|
119
|
-
|
|
120
|
-
failed_topics.each do |topic, precursor_topics|
|
|
121
|
-
puts "#{topic.topic_name} depends on:"
|
|
122
|
-
precursor_topics.each do |precursor_topic|
|
|
123
|
-
puts "\t#{precursor_topic.topic_name} because of:"
|
|
124
|
-
|
|
125
|
-
blocking_concept =
|
|
126
|
-
precursor_topic.all_concept.select{|concept|
|
|
127
|
-
concept.precursors.detect{|cp|
|
|
128
|
-
cp.topic == precursor_topic
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
blocking_concept.each do |concept|
|
|
133
|
-
puts "\t\t#{concept.describe} (depends on #{concept.precursors.map(&:topic).uniq.map(&:topic_name).sort.inspect})"
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
return
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def list_groups_by_rank concept_deps
|
|
143
|
-
puts "=== Concepts listed in Ranking Order with chasers ==="
|
|
144
|
-
# List the highest-ranked items that have no un-listed precursors.
|
|
145
|
-
# After each listed item, list the chasers *transitively* before
|
|
146
|
-
# choosing the next highest ranked item.
|
|
147
|
-
@listed = {}
|
|
148
|
-
|
|
149
|
-
start = Time.now
|
|
150
|
-
@page_rank = concept_deps.page_rank # Get the page_rank
|
|
151
|
-
# puts "done in #{Time.now-start}"
|
|
152
|
-
|
|
153
|
-
@ranked = @page_rank.keys.sort_by{|k| -@page_rank[k]} # Sort decreasing
|
|
154
|
-
@topic = nil
|
|
155
|
-
until @listed.size >= @ranked.size
|
|
156
|
-
vt = nil # Capture the first listable ValueType, which we'll choose if we must
|
|
157
|
-
chosen = @ranked.detect do |concept|
|
|
158
|
-
next if @listed[concept]
|
|
159
|
-
|
|
160
|
-
unlisted_precursors =
|
|
161
|
-
concept_deps.precursors_transitive(concept).
|
|
162
|
-
reject do |precursor|
|
|
163
|
-
is_value_type_concept(precursor) or
|
|
164
|
-
precursor == concept or
|
|
165
|
-
@listed[precursor] or
|
|
166
|
-
is_value_type_concept(precursor)
|
|
167
|
-
end
|
|
168
|
-
next if unlisted_precursors.size > 0 # Still has precursors
|
|
169
|
-
vt ||= concept
|
|
170
|
-
next if is_value_type_concept(concept)
|
|
171
|
-
concept
|
|
172
|
-
end
|
|
173
|
-
chosen ||= vt # Choose a value type if there's no other
|
|
174
|
-
|
|
175
|
-
@cluster = []
|
|
176
|
-
|
|
177
|
-
descend concept_deps, chosen
|
|
178
|
-
|
|
179
|
-
cluster_ranks = @cluster.sort_by{|c| -@page_rank[c]}
|
|
180
|
-
head = cluster_ranks.detect{|c| c.object_type} || cluster_ranks.first
|
|
181
|
-
puts "/*\n * #{head.object_type ? head.object_type.name : head.describe}\n */"
|
|
182
|
-
@cluster.each do |concept|
|
|
183
|
-
puts '*'*10+" TOPIC #{@topic = concept.topic.topic_name}" if @topic != concept.topic.topic_name
|
|
184
|
-
puts concept.describe
|
|
185
|
-
end
|
|
186
|
-
puts # Put a line between sections
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
# Descend the concept, also the items that are necessarily dumped with concept, so we don't select them again
|
|
191
|
-
def descend(concept_deps, concept, tab = '')
|
|
192
|
-
@causation ||= []
|
|
193
|
-
@cluster ||= []
|
|
194
|
-
return if @listed[concept] || @causation.include?(concept) # Recursion prevention
|
|
195
|
-
begin
|
|
196
|
-
@causation << concept # Reversed by the "ensure" below.
|
|
197
|
-
|
|
198
|
-
concept_deps.precursors(concept).each do |precursor|
|
|
199
|
-
descend(concept_deps, precursor, tab) unless @listed[precursor] || precursor == concept
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
#puts tab+concept.describe
|
|
203
|
-
@listed[concept] = true
|
|
204
|
-
@cluster << concept
|
|
205
|
-
|
|
206
|
-
body = concept.embodied_as
|
|
207
|
-
case body
|
|
208
|
-
when ActiveFacts::Metamodel::FactType
|
|
209
|
-
body.internal_presence_constraints.each do |pc|
|
|
210
|
-
# puts "Listing #{pc.concept.describe} as internal_presence_constraint of #{concept.describe}"
|
|
211
|
-
descend(concept_deps, pc.concept, tab+"\t")
|
|
212
|
-
end
|
|
213
|
-
if body.entity_type
|
|
214
|
-
# puts "Listing #{body.entity_type.concept.describe} as objectification of #{concept.describe}"
|
|
215
|
-
descend(concept_deps, body.entity_type.concept, tab+"\t")
|
|
216
|
-
end
|
|
217
|
-
body.all_role.each do |role|
|
|
218
|
-
role.all_ring_constraint.each do |rc|
|
|
219
|
-
# puts "Listing #{rc.concept.describe} as ring_constraint of #{concept.describe}"
|
|
220
|
-
descend(concept_deps, rc.concept, tab+"\t")
|
|
221
|
-
end
|
|
222
|
-
end
|
|
223
|
-
when ActiveFacts::Metamodel::EntityType
|
|
224
|
-
body.preferred_identifier.role_sequence.all_role_ref.each do |rr|
|
|
225
|
-
# puts "Listing #{rr.role.fact_type.concept.describe} as existential fact of #{concept.describe}"
|
|
226
|
-
descend(concept_deps, rr.role.fact_type.concept, tab+"\t")
|
|
227
|
-
end
|
|
228
|
-
descend(concept_deps, body.preferred_identifier.concept, tab+"\t")
|
|
229
|
-
when ActiveFacts::Metamodel::ValueType
|
|
230
|
-
if body.value_constraint
|
|
231
|
-
# puts "Listing #{body.value_constraint.concept.describe} as value constraint of #{concept.describe}"
|
|
232
|
-
descend(concept_deps, body.value_constraint.concept, tab+"\t")
|
|
233
|
-
end
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
# Follow up with unlisted chasers (that have no remaining precursors) in order of decreasing importance:
|
|
237
|
-
chasers =
|
|
238
|
-
concept_deps.followers(concept).reject do |follower|
|
|
239
|
-
@listed[follower] or
|
|
240
|
-
is_value_type_concept(follower) or
|
|
241
|
-
concept_deps.precursors(follower).detect{|p| !@listed[p] && !is_value_type_concept(p) } or
|
|
242
|
-
# Exclude subtypes of entity types from the followers:
|
|
243
|
-
concept.object_type && concept.object_type.is_a?(ActiveFacts::Metamodel::EntityType) && concept.object_type.subtypes.include?(follower.object_type)
|
|
244
|
-
end.
|
|
245
|
-
sort_by{|nc| -@page_rank[nc]}
|
|
246
|
-
|
|
247
|
-
chasers.each do |chaser|
|
|
248
|
-
# puts "**** Listing #{chaser.describe} as chaser of #{concept.describe}"
|
|
249
|
-
descend(concept_deps, chaser, tab)
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
ensure
|
|
253
|
-
@causation.pop
|
|
254
|
-
end
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
def is_value_type_concept concept
|
|
258
|
-
o = concept.object_type and o.is_a?(ActiveFacts::Metamodel::ValueType)
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
ActiveFacts::Registry.generator('topics', ActiveFacts::Generate::Topics)
|