activefacts 1.6.0 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,182 +0,0 @@
|
|
1
|
-
module ActiveFacts
|
2
|
-
class DependencyAnalyser
|
3
|
-
def initialize enumerable, &block
|
4
|
-
@enumerable = enumerable
|
5
|
-
analyse_precursors &block
|
6
|
-
end
|
7
|
-
|
8
|
-
def analyse_precursors &block
|
9
|
-
@precursors = {}
|
10
|
-
@enumerable.each do |item|
|
11
|
-
@precursors[item] = block.call(item)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def analyse_precursors_transitive
|
16
|
-
all_precursors = proc do |item|
|
17
|
-
p = @precursors[item]
|
18
|
-
all =
|
19
|
-
p + p.map do |precursor|
|
20
|
-
p.include?(precursor) ? [] : all_precursors.call(precursor)
|
21
|
-
end.flatten
|
22
|
-
all.uniq
|
23
|
-
end
|
24
|
-
|
25
|
-
@precursors_transitive = {}
|
26
|
-
@enumerable.each do |item|
|
27
|
-
@precursors_transitive[item] = all_precursors.call(item)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def analyse_followers
|
32
|
-
@followers = Hash.new{|h, k| h[k] = [] }
|
33
|
-
@enumerable.each do |item|
|
34
|
-
@precursors[item].each do |precursor|
|
35
|
-
@followers[precursor] << item
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def analyse_chasers
|
41
|
-
analyse_precursors_transitive unless @precursors_transitive
|
42
|
-
analyse_followers unless @followers
|
43
|
-
|
44
|
-
# A follower is an object with us as a precursor, that has no new precursors of its own
|
45
|
-
@chasers = {}
|
46
|
-
@enumerable.each do |item|
|
47
|
-
@chasers[item] =
|
48
|
-
@enumerable.select do |follower|
|
49
|
-
@precursors[follower].include?(item) and
|
50
|
-
(@precursors_transitive[follower] - @precursors_transitive[item] - [item]).size == 0
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def tsort &block
|
56
|
-
analyse_precursors unless @precursors
|
57
|
-
emitted = {}
|
58
|
-
pass = 0
|
59
|
-
until emitted.size == @enumerable.size
|
60
|
-
next_items = []
|
61
|
-
blocked =
|
62
|
-
@enumerable.inject({}) do |hash, item|
|
63
|
-
next hash if emitted[item]
|
64
|
-
blockers = item.precursors.select{|precursor| !emitted[precursor]}
|
65
|
-
if blockers.size > 0
|
66
|
-
hash[item] = blockers
|
67
|
-
else
|
68
|
-
next_items << item
|
69
|
-
end
|
70
|
-
hash
|
71
|
-
end
|
72
|
-
return blocked if next_items.size == 0 # Cannot make progress
|
73
|
-
# puts "PASS #{pass += 1}"
|
74
|
-
next_items.each do |item|
|
75
|
-
block.call(item)
|
76
|
-
emitted[item] = true
|
77
|
-
end
|
78
|
-
end
|
79
|
-
nil
|
80
|
-
end
|
81
|
-
|
82
|
-
def each &b
|
83
|
-
if block_given?
|
84
|
-
@enumerable.each { |item| yield item}
|
85
|
-
else
|
86
|
-
@enumerable
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def precursors item = nil, &b
|
91
|
-
analyse_precursors unless @precursors
|
92
|
-
if item
|
93
|
-
if block_given?
|
94
|
-
Array(@precursors[item]).each { |precursor| yield precursor, item }
|
95
|
-
else
|
96
|
-
Array(@precursors[item])
|
97
|
-
end
|
98
|
-
else
|
99
|
-
@enumerable.each do |item|
|
100
|
-
precursors(item, &b)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def precursors_transitive item, &b
|
106
|
-
analyse_precursors_transitive unless @precursors_transitive
|
107
|
-
if item
|
108
|
-
if block_given?
|
109
|
-
Array(@precursors_transitive[item]).each { |precursor| yield precursor, item }
|
110
|
-
else
|
111
|
-
Array(@precursors_transitive[item])
|
112
|
-
end
|
113
|
-
else
|
114
|
-
@enumerable.each do |item|
|
115
|
-
precursors_transitive(item, &b)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def followers item = nil, &b
|
121
|
-
analyse_followers unless @followers
|
122
|
-
if item
|
123
|
-
if block_given?
|
124
|
-
Array(@followers[item]).each { |follower| yield follower, item }
|
125
|
-
else
|
126
|
-
Array(@followers[item])
|
127
|
-
end
|
128
|
-
else
|
129
|
-
@enumerable.each do |item|
|
130
|
-
followers(item, &b)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
def chasers item, &b
|
136
|
-
analyse_chasers unless @chasers
|
137
|
-
if item
|
138
|
-
if block_given?
|
139
|
-
Array(@chasers[item]).each { |follower| yield follower, item }
|
140
|
-
else
|
141
|
-
Array(@chasers[item])
|
142
|
-
end
|
143
|
-
else
|
144
|
-
@enumerable.each do |item|
|
145
|
-
follower(item, &b)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
# Compute the page rank of the objects
|
151
|
-
# If used, the block shold return the starting weight
|
152
|
-
def page_rank damping = 0.85, &weight
|
153
|
-
weight ||= proc {|item| 1.0}
|
154
|
-
|
155
|
-
@total = 0
|
156
|
-
@rank = {}
|
157
|
-
@enumerable.each do |item|
|
158
|
-
@total +=
|
159
|
-
(@rank[item] = weight.call(item) * 1.0)
|
160
|
-
end
|
161
|
-
# Normalize:
|
162
|
-
@enumerable.each do |item|
|
163
|
-
@rank[item] /= @total
|
164
|
-
end
|
165
|
-
|
166
|
-
50.times do |iteration|
|
167
|
-
@enumerable.each do |item|
|
168
|
-
links = (precursors(item) + followers(item)).uniq
|
169
|
-
linked_rank = links.map do |l|
|
170
|
-
onward_links = (precursors(l) + followers(l)).uniq || @enumerable.size
|
171
|
-
@rank[l] / onward_links.size
|
172
|
-
end.inject(&:+) || 0
|
173
|
-
@rank[item] = (1.0-damping) + damping*linked_rank
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
@rank
|
178
|
-
end
|
179
|
-
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
@@ -1,70 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# ActiveFacts Generators.
|
3
|
-
# Absorption generator.
|
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
|
-
# Emit the absorption (Relational summary) for vocabulary.
|
13
|
-
# Not currently working, it relies on the old relational composition code.
|
14
|
-
# Invoke as
|
15
|
-
# afgen --absorption[=options] <file>.cql"
|
16
|
-
# Options are comma or space separated:
|
17
|
-
# * no_columns Don't emit the columns
|
18
|
-
# * all Show ObjectTypes that are not tables as well
|
19
|
-
# * paths Show the references paths through which each column was defined
|
20
|
-
# * no_identifier Don't show the identified_by columns for an EntityType
|
21
|
-
|
22
|
-
class Absorption
|
23
|
-
def initialize(vocabulary, *options) #:nodoc:
|
24
|
-
@vocabulary = vocabulary
|
25
|
-
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
26
|
-
@no_columns = options.include? "no_columns"
|
27
|
-
@paths = options.include? "paths"
|
28
|
-
@no_identifier = options.include? "no_identifier"
|
29
|
-
end
|
30
|
-
|
31
|
-
def generate(out = $>) #:nodoc:
|
32
|
-
@out = out
|
33
|
-
no_absorption = 0
|
34
|
-
single_absorption_vts = 0
|
35
|
-
single_absorption_ets = 0
|
36
|
-
multi_absorption_vts = 0
|
37
|
-
multi_absorption_ets = 0
|
38
|
-
@vocabulary.tables
|
39
|
-
@vocabulary.all_object_type.sort_by{|c| c.name}.each do |o|
|
40
|
-
next if !o.is_table
|
41
|
-
show(o)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def show object_type #:nodoc:
|
46
|
-
indices = object_type.indices
|
47
|
-
pk = indices.select(&:is_primary)[0]
|
48
|
-
indices = indices.clone
|
49
|
-
indices.delete pk
|
50
|
-
@out.puts "#{object_type.name}: #{
|
51
|
-
# "[#{object_type.indices.size} indices] "
|
52
|
-
# } #{
|
53
|
-
object_type.columns.sort_by do |column|
|
54
|
-
column.name(nil)
|
55
|
-
end.map do |column|
|
56
|
-
index_nrs =
|
57
|
-
[pk && pk.columns.include?(column) ? "*" : nil] +
|
58
|
-
(0...indices.size).select{|i| indices[i].columns.include?(column)}.map{|i| (i+1).to_i }
|
59
|
-
index_nrs.compact!
|
60
|
-
(@paths ? column.references.map{|r| r.to_names}.flatten : column.name(nil)) * '.' +
|
61
|
-
(index_nrs.empty? ? "" : "["+index_nrs*""+"]")
|
62
|
-
end*", "
|
63
|
-
}"
|
64
|
-
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
ActiveFacts::Registry.generator('absorption', ActiveFacts::Generate::Absorption)
|
@@ -1,118 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# ActiveFacts Generators.
|
3
|
-
# Generate a Relational Composition (for activefacts/composition).
|
4
|
-
#
|
5
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
-
#
|
7
|
-
require 'activefacts/vocabulary'
|
8
|
-
require 'activefacts/generate/helpers/inject'
|
9
|
-
require 'activefacts/persistence'
|
10
|
-
require 'activefacts/generate/traits/ruby'
|
11
|
-
|
12
|
-
module ActiveFacts
|
13
|
-
module Generate
|
14
|
-
# afgen --composition[=options] <file>.cql
|
15
|
-
# Options are comma or space separated:
|
16
|
-
class Composition #:nodoc:
|
17
|
-
private
|
18
|
-
include Persistence
|
19
|
-
|
20
|
-
def initialize(vocabulary, *options)
|
21
|
-
@vocabulary = vocabulary
|
22
|
-
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
23
|
-
@underscore = options.include?("underscore") ? "_" : ""
|
24
|
-
end
|
25
|
-
|
26
|
-
def puts s
|
27
|
-
@out.puts s
|
28
|
-
end
|
29
|
-
|
30
|
-
public
|
31
|
-
def generate(out = $>) #:nodoc:
|
32
|
-
@out = out
|
33
|
-
|
34
|
-
tables_emitted = {}
|
35
|
-
|
36
|
-
puts "require '#{@vocabulary.name}'"
|
37
|
-
puts "require 'activefacts/composition'"
|
38
|
-
puts "\n#{@vocabulary.name}_ER = ActiveFacts::Composition.new(#{@vocabulary.name}) do"
|
39
|
-
@vocabulary.tables.each do |table|
|
40
|
-
puts " composite :\"#{table.name.gsub(' ',@underscore)}\" do"
|
41
|
-
|
42
|
-
pk = table.identifier_columns
|
43
|
-
identity_column = pk[0] if pk[0].is_auto_assigned
|
44
|
-
|
45
|
-
fk_refs = table.references_from.select{|ref| ref.is_simple_reference }
|
46
|
-
fk_columns = table.columns.select do |column|
|
47
|
-
column.references[0].is_simple_reference
|
48
|
-
end
|
49
|
-
|
50
|
-
columns =
|
51
|
-
table.columns.map do |column|
|
52
|
-
[column, column.references.map{|r| r.to_names }]
|
53
|
-
end.sort_by do |column, refnames|
|
54
|
-
refnames
|
55
|
-
end
|
56
|
-
previous_flattening = []
|
57
|
-
ref_prefix = []
|
58
|
-
columns.each do |column, refnames|
|
59
|
-
ref_prefix = column.references[0...previous_flattening.size]
|
60
|
-
# Pop back. Not a succinct algorithm, but easy to check
|
61
|
-
while previous_flattening.size > ref_prefix.size
|
62
|
-
previous_flattening.pop
|
63
|
-
puts ' '+' '*previous_flattening.size+"end\n"
|
64
|
-
end
|
65
|
-
while ref_prefix.size > 0 and previous_flattening != ref_prefix
|
66
|
-
previous_flattening.pop
|
67
|
-
ref_prefix.pop
|
68
|
-
puts ' '+' '*previous_flattening.size+"end\n"
|
69
|
-
end
|
70
|
-
loop do
|
71
|
-
ref = column.references[ref_prefix.size]
|
72
|
-
if ref.is_self_value
|
73
|
-
# REVISIT: I think these should be 'insert :value, :as => "XYZ"'
|
74
|
-
role_name = "value".snakecase
|
75
|
-
reading = "Intrinsic value of #{role_name}"
|
76
|
-
elsif ref.is_to_objectified_fact
|
77
|
-
# REVISIT: It's ugly to have to handle these special cases here
|
78
|
-
role_name = ref.to.name.words.snakecase
|
79
|
-
reading = ref.from_role.link_fact_type.default_reading
|
80
|
-
else
|
81
|
-
if ref.is_unary && ref.is_from_objectified_fact && ref != column.references.last
|
82
|
-
# Use the name of the objectification on the path to other absorbed fact types:
|
83
|
-
role_name = ref.to_role.fact_type.entity_type.name.words.snakecase
|
84
|
-
else
|
85
|
-
role_name = ref.to_role.preferred_role_name
|
86
|
-
end
|
87
|
-
# puts ">>>>> #{ref.inspect}: #{role_name} <<<<<<"
|
88
|
-
reading = ref.fact_type.default_reading
|
89
|
-
end
|
90
|
-
if ref == column.references.last
|
91
|
-
# REVISIT: Avoid the "as" here when the value is implied by the role_name:
|
92
|
-
puts ' '+' '*ref_prefix.size+"nest :#{role_name}, :as => \"#{column.name}\"\t\t# #{reading}"
|
93
|
-
break
|
94
|
-
else
|
95
|
-
puts ' '+' '*ref_prefix.size+"flatten :#{role_name} do\t\t# #{reading}"
|
96
|
-
ref_prefix.push ref
|
97
|
-
end
|
98
|
-
end
|
99
|
-
previous_flattening = ref_prefix
|
100
|
-
end
|
101
|
-
|
102
|
-
while previous_flattening.size > 0
|
103
|
-
previous_flattening.pop
|
104
|
-
puts ' '+' '*previous_flattening.size+"end\n"
|
105
|
-
end
|
106
|
-
puts " end\n\n"
|
107
|
-
|
108
|
-
tables_emitted[table] = true
|
109
|
-
|
110
|
-
end
|
111
|
-
puts "end\n"
|
112
|
-
end
|
113
|
-
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
ActiveFacts::Registry.generator('composition', ActiveFacts::Generate::Composition)
|
@@ -1,714 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# ActiveFacts Generators.
|
3
|
-
# Generate CQL from an ActiveFacts vocabulary.
|
4
|
-
#
|
5
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
-
#
|
7
|
-
require 'activefacts/vocabulary'
|
8
|
-
require 'activefacts/registry'
|
9
|
-
require 'activefacts/generate/helpers/ordered'
|
10
|
-
|
11
|
-
module ActiveFacts
|
12
|
-
module Generate #:nodoc:
|
13
|
-
# Generate CQL for an ActiveFacts vocabulary.
|
14
|
-
# Invoke as
|
15
|
-
# afgen --cql <file>.cql
|
16
|
-
class CQL < Helpers::OrderedDumper
|
17
|
-
private
|
18
|
-
def vocabulary_start
|
19
|
-
puts "vocabulary #{@vocabulary.name};\n\n"
|
20
|
-
build_indices
|
21
|
-
end
|
22
|
-
|
23
|
-
def vocabulary_end
|
24
|
-
end
|
25
|
-
|
26
|
-
def units_banner
|
27
|
-
puts "/*\n * Units\n */"
|
28
|
-
end
|
29
|
-
|
30
|
-
def unit_dump unit
|
31
|
-
puts unit.as_cql
|
32
|
-
end
|
33
|
-
|
34
|
-
def units_end
|
35
|
-
puts "\n"
|
36
|
-
end
|
37
|
-
|
38
|
-
def value_type_banner
|
39
|
-
puts "/*\n * Value Types\n */"
|
40
|
-
end
|
41
|
-
|
42
|
-
def value_type_end
|
43
|
-
puts "\n"
|
44
|
-
end
|
45
|
-
|
46
|
-
def data_type_dump(o)
|
47
|
-
value_type_dump(o, o.name, {}) if o.all_role.size > 0
|
48
|
-
end
|
49
|
-
|
50
|
-
def value_type_dump(o, super_type_name, facets)
|
51
|
-
# No need to dump it if the only thing it does is be a supertype; it'll be created automatically
|
52
|
-
# return if o.all_value_type_as_supertype.size == 0
|
53
|
-
|
54
|
-
# REVISIT: A ValueType that is only used as a reference mode need not be emitted here.
|
55
|
-
|
56
|
-
puts o.as_cql
|
57
|
-
end
|
58
|
-
|
59
|
-
def entity_type_dump(o)
|
60
|
-
o.ordered_dumped!
|
61
|
-
pi = o.preferred_identifier
|
62
|
-
|
63
|
-
supers = o.supertypes
|
64
|
-
if (supers.size > 0)
|
65
|
-
# Ignore identification by a supertype:
|
66
|
-
pi = nil if pi && pi.role_sequence.all_role_ref.detect{|rr| rr.role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) }
|
67
|
-
subtype_dump(o, supers, pi)
|
68
|
-
else
|
69
|
-
non_subtype_dump(o, pi)
|
70
|
-
end
|
71
|
-
pi.ordered_dumped! if pi
|
72
|
-
end
|
73
|
-
|
74
|
-
def append_ring_to_reading(reading, ring)
|
75
|
-
reading << " [#{(ring.ring_type.scan(/StronglyIntransitive|[A-Z][a-z]*/)*", ").downcase}]"
|
76
|
-
end
|
77
|
-
|
78
|
-
def mapping_pragma(entity_type, ignore_independence = false)
|
79
|
-
ti = entity_type.all_type_inheritance_as_subtype
|
80
|
-
assimilation = ti.map{|t| t.assimilation }.compact[0]
|
81
|
-
return "" unless (entity_type.is_independent && !ignore_independence) || assimilation
|
82
|
-
" [" +
|
83
|
-
[
|
84
|
-
entity_type.is_independent && !ignore_independence ? "independent" : nil,
|
85
|
-
assimilation || nil
|
86
|
-
].compact*", " +
|
87
|
-
"]"
|
88
|
-
end
|
89
|
-
|
90
|
-
# If this entity_type is identified by a single value, return four relevant objects:
|
91
|
-
def value_role_identification(entity_type, identifying_facts)
|
92
|
-
external_identifying_facts = identifying_facts - [entity_type.fact_type]
|
93
|
-
fact_type = external_identifying_facts[0]
|
94
|
-
ftr = fact_type && fact_type.all_role.sort_by{|role| role.ordinal}
|
95
|
-
if external_identifying_facts.size == 1 and
|
96
|
-
entity_role = ftr[n = (ftr[0].object_type == entity_type ? 0 : 1)] and
|
97
|
-
value_role = ftr[1-n] and
|
98
|
-
value_player = value_role.object_type and
|
99
|
-
value_player.is_a?(ActiveFacts::Metamodel::ValueType) and
|
100
|
-
value_name = value_player.name and
|
101
|
-
value_residual = value_name.sub(%r{^#{entity_role.object_type.name} ?},'') and
|
102
|
-
value_residual != '' and
|
103
|
-
value_residual != value_name
|
104
|
-
[fact_type, entity_role, value_role, value_residual]
|
105
|
-
else
|
106
|
-
[]
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
# This entity is identified by a single value, so find whether standard refmode readings were used
|
111
|
-
def detect_standard_refmode_readings fact_type, entity_role, value_role
|
112
|
-
forward_reading = reverse_reading = nil
|
113
|
-
fact_type.all_reading.each do |reading|
|
114
|
-
if reading.text =~ /^\{(\d)\} has \{\d\}$/
|
115
|
-
if reading.role_sequence.all_role_ref.detect{|rr| rr.ordinal == $1.to_i}.role == entity_role
|
116
|
-
forward_reading = reading
|
117
|
-
else
|
118
|
-
reverse_reading = reading
|
119
|
-
end
|
120
|
-
elsif reading.text =~ /^\{(\d)\} is of \{\d\}$/
|
121
|
-
if reading.role_sequence.all_role_ref.detect{|rr| rr.ordinal == $1.to_i}.role == value_role
|
122
|
-
reverse_reading = reading
|
123
|
-
else
|
124
|
-
forward_reading = reading
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
trace :mode, "Didn't find standard forward reading" unless forward_reading
|
129
|
-
trace :mode, "Didn't find standard reverse reading" unless reverse_reading
|
130
|
-
[forward_reading, reverse_reading]
|
131
|
-
end
|
132
|
-
|
133
|
-
# If this entity_type is identified by a reference mode, return the verbalisation
|
134
|
-
def identified_by_ref_mode(entity_type, identifying_facts)
|
135
|
-
fact_type, entity_role, value_role, value_residual =
|
136
|
-
*value_role_identification(entity_type, identifying_facts)
|
137
|
-
return nil unless fact_type
|
138
|
-
|
139
|
-
# This EntityType is identified by its association with a single ValueType
|
140
|
-
# whose name is an extension (the value_residual) of the EntityType's name.
|
141
|
-
# If we have at least one of the standard refmode readings, dump it that way,
|
142
|
-
# else exit and use the long-hand verbalisation instead.
|
143
|
-
|
144
|
-
forward_reading, reverse_reading =
|
145
|
-
*detect_standard_refmode_readings(fact_type, entity_role, value_role)
|
146
|
-
return nil unless (forward_reading || reverse_reading)
|
147
|
-
|
148
|
-
# We can't subscript reference modes.
|
149
|
-
# If an objectified fact type has a role played by its identifying player, go long-hand.
|
150
|
-
return nil if entity_type.fact_type and
|
151
|
-
entity_type.fact_type.all_role.detect{|role| role.object_type == value_role.object_type }
|
152
|
-
|
153
|
-
fact_type.ordered_dumped! # We've covered this fact type
|
154
|
-
|
155
|
-
# Elide the constraints that would have been emitted on the standard readings.
|
156
|
-
# If there is a UC that's not in the standard form for a reference mode,
|
157
|
-
# we have to emit the standard reading anyhow.
|
158
|
-
fact_constraints = @presence_constraints_by_fact[fact_type]
|
159
|
-
fact_constraints.each do |pc|
|
160
|
-
if (pc.role_sequence.all_role_ref.size == 1 and pc.max_frequency == 1)
|
161
|
-
# It's a uniqueness constraint, and will be regenerated
|
162
|
-
pc.ordered_dumped!
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
# Figure out which non-standard readings exist, if any:
|
167
|
-
nonstandard_readings = fact_type.all_reading - [forward_reading, reverse_reading]
|
168
|
-
trace :mode, "--- nonstandard_readings.size now = #{nonstandard_readings.size}" if nonstandard_readings.size > 0
|
169
|
-
|
170
|
-
verbaliser = ActiveFacts::Metamodel::Verbaliser.new
|
171
|
-
|
172
|
-
# The verbaliser needs to have a Player for the roles of entity_type, so it doesn't get subscripted.
|
173
|
-
entity_roles =
|
174
|
-
nonstandard_readings.map{|r| r.role_sequence.all_role_ref.detect{|rr| rr.role.object_type == entity_type}}.compact
|
175
|
-
verbaliser.role_refs_have_same_player entity_roles
|
176
|
-
|
177
|
-
verbaliser.alternate_readings nonstandard_readings
|
178
|
-
if entity_type.fact_type
|
179
|
-
verbaliser.alternate_readings entity_type.fact_type.all_reading
|
180
|
-
end
|
181
|
-
|
182
|
-
verbaliser.create_subscripts(:rolenames) # Ok, the Verbaliser is ready to fly
|
183
|
-
|
184
|
-
fact_readings =
|
185
|
-
nonstandard_readings.map { |reading| expanded_reading(verbaliser, reading, fact_constraints, true) }
|
186
|
-
fact_readings +=
|
187
|
-
fact_readings_with_constraints(verbaliser, entity_type.fact_type) if entity_type.fact_type
|
188
|
-
|
189
|
-
# If we emitted a reading for the refmode, it'll include any role_value_constraint already
|
190
|
-
if nonstandard_readings.size == 0 and c = value_role.role_value_constraint
|
191
|
-
constraint_text = " "+c.as_cql
|
192
|
-
end
|
193
|
-
(entity_type.is_independent ? ' independent' : '') +
|
194
|
-
" identified by its #{value_residual}#{constraint_text}#{mapping_pragma(entity_type, true)}" +
|
195
|
-
entity_type.concept.all_context_note_as_relevant_concept.map do |cn|
|
196
|
-
cn.verbalise
|
197
|
-
end.join("\n") +
|
198
|
-
(fact_readings.size > 0 ? " where\n\t" : "") +
|
199
|
-
fact_readings*",\n\t"
|
200
|
-
end
|
201
|
-
|
202
|
-
def identified_by_roles_and_facts(entity_type, identifying_role_refs, identifying_facts)
|
203
|
-
# Detect standard reference-mode scenarios:
|
204
|
-
if srm = identified_by_ref_mode(entity_type, identifying_facts)
|
205
|
-
return srm
|
206
|
-
end
|
207
|
-
|
208
|
-
verbaliser = ActiveFacts::Metamodel::Verbaliser.new
|
209
|
-
|
210
|
-
# Announce all the identifying fact roles to the verbaliser so it can decide on any necessary subscripting.
|
211
|
-
# The verbaliser needs to have a Player for the roles of entity_type, so it doesn't get subscripted.
|
212
|
-
entity_roles =
|
213
|
-
identifying_facts.map{|ft| ft.preferred_reading.role_sequence.all_role_ref.detect{|rr| rr.role.object_type == entity_type}}.compact
|
214
|
-
verbaliser.role_refs_have_same_player entity_roles
|
215
|
-
identifying_facts.each do |fact_type|
|
216
|
-
# The RoleRefs for corresponding roles across all readings are for the same player.
|
217
|
-
verbaliser.alternate_readings fact_type.all_reading
|
218
|
-
fact_type.ordered_dumped! unless fact_type.entity_type # Must dump objectification still!
|
219
|
-
end
|
220
|
-
verbaliser.create_subscripts(:rolenames)
|
221
|
-
|
222
|
-
irn = verbaliser.identifying_role_names identifying_role_refs
|
223
|
-
|
224
|
-
identifying_fact_text =
|
225
|
-
identifying_facts.map{|f|
|
226
|
-
fact_readings_with_constraints(verbaliser, f)
|
227
|
-
}.flatten*",\n\t"
|
228
|
-
|
229
|
-
(entity_type.is_independent ? ' independent' : '') +
|
230
|
-
" identified by #{ irn*" and " }" +
|
231
|
-
mapping_pragma(entity_type, true) +
|
232
|
-
entity_type.concept.all_context_note_as_relevant_concept.map do |cn|
|
233
|
-
cn.verbalise
|
234
|
-
end.join("\n") +
|
235
|
-
" where\n\t"+identifying_fact_text
|
236
|
-
end
|
237
|
-
|
238
|
-
def entity_type_banner
|
239
|
-
puts "/*\n * Entity Types\n */"
|
240
|
-
end
|
241
|
-
|
242
|
-
def entity_type_group_end
|
243
|
-
puts "\n"
|
244
|
-
end
|
245
|
-
|
246
|
-
def subtype_dump(o, supertypes, pi)
|
247
|
-
print "#{o.name} is a kind of #{
|
248
|
-
o.is_independent ? 'independent ' : ''
|
249
|
-
}#{ o.supertypes.map(&:name)*", " }"
|
250
|
-
if pi
|
251
|
-
puts identified_by(o, pi)+';'
|
252
|
-
return
|
253
|
-
end
|
254
|
-
|
255
|
-
print mapping_pragma(o, true)
|
256
|
-
|
257
|
-
if o.fact_type
|
258
|
-
verbaliser = ActiveFacts::Metamodel::Verbaliser.new
|
259
|
-
# Announce all the objectified fact roles to the verbaliser so it can decide on any necessary subscripting.
|
260
|
-
# The RoleRefs for corresponding roles across all readings are for the same player.
|
261
|
-
verbaliser.alternate_readings o.fact_type.all_reading
|
262
|
-
verbaliser.create_subscripts(:rolenames)
|
263
|
-
|
264
|
-
print " where\n\t" + fact_readings_with_constraints(verbaliser, o.fact_type)*",\n\t"
|
265
|
-
end
|
266
|
-
puts ";\n"
|
267
|
-
end
|
268
|
-
|
269
|
-
def non_subtype_dump(o, pi)
|
270
|
-
puts "#{o.name} is" + identified_by(o, pi) + ';'
|
271
|
-
end
|
272
|
-
|
273
|
-
def naiive_expand(reading)
|
274
|
-
role_refs = reading.role_sequence.all_role_ref_in_order
|
275
|
-
reading.text.gsub(/\{(\d+)\}/) do
|
276
|
-
role_refs[$1.to_i].role.object_type.name
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
def fact_type_dump(fact_type, name)
|
281
|
-
|
282
|
-
if (o = fact_type.entity_type)
|
283
|
-
print "#{o.name} is"
|
284
|
-
supertypes = o.supertypes
|
285
|
-
if supertypes.empty?
|
286
|
-
print ' independent' if o.is_independent
|
287
|
-
else
|
288
|
-
print " a kind of#{
|
289
|
-
o.is_independent ? ' independent' : ''
|
290
|
-
} #{ supertypes.map(&:name)*', ' }"
|
291
|
-
end
|
292
|
-
|
293
|
-
# Alternate identification of objectified fact type?
|
294
|
-
primary_supertype = supertypes[0]
|
295
|
-
pi = fact_type.entity_type.preferred_identifier
|
296
|
-
if pi && primary_supertype && primary_supertype.preferred_identifier != pi
|
297
|
-
puts identified_by(o, pi) + ';'
|
298
|
-
return
|
299
|
-
end
|
300
|
-
print " where\n\t"
|
301
|
-
end
|
302
|
-
|
303
|
-
# Check whether this fact type has readings which could be confused for a previously-dumped one:
|
304
|
-
reading_texts = fact_type.all_reading.map{|r| naiive_expand(r)}
|
305
|
-
if reading_texts.size > 1
|
306
|
-
ambiguity =
|
307
|
-
fact_type.all_role.to_a[0].object_type.all_role.map{|r| r.fact_type}.
|
308
|
-
select{|f| f != fact_type && f.ordered_dumped }.
|
309
|
-
detect do |dft|
|
310
|
-
ambiguous_readings =
|
311
|
-
reading_texts & dft.all_reading.map{|r| naiive_expand(r)}
|
312
|
-
ambiguous_readings.size > 0
|
313
|
-
end
|
314
|
-
if ambiguity
|
315
|
-
puts fact_type.default_reading([], true)+'; // Avoid ambiguity; this is a new fact type'
|
316
|
-
end
|
317
|
-
end
|
318
|
-
|
319
|
-
# There can be no roles of the objectified fact type in the readings, so no need to tell the Verbaliser anything special
|
320
|
-
verbaliser = ActiveFacts::Metamodel::Verbaliser.new
|
321
|
-
verbaliser.alternate_readings fact_type.all_reading
|
322
|
-
pr = fact_type.preferred_reading
|
323
|
-
if (pr.role_sequence.all_role_ref.to_a[0].play)
|
324
|
-
verbaliser.prepare_role_sequence pr.role_sequence
|
325
|
-
end
|
326
|
-
verbaliser.create_subscripts(:rolenames)
|
327
|
-
|
328
|
-
print(fact_readings_with_constraints(verbaliser, fact_type)*",\n\t")
|
329
|
-
if (pr.role_sequence.all_role_ref.to_a[0].play)
|
330
|
-
print " where\n\t"+verbaliser.verbalise_over_role_sequence(pr.role_sequence)
|
331
|
-
end
|
332
|
-
puts(';')
|
333
|
-
end
|
334
|
-
|
335
|
-
def fact_type_banner
|
336
|
-
puts "/*\n * Fact Types\n */"
|
337
|
-
end
|
338
|
-
|
339
|
-
def fact_type_end
|
340
|
-
puts "\n"
|
341
|
-
end
|
342
|
-
|
343
|
-
def constraint_banner
|
344
|
-
puts "/*\n * Constraints:"
|
345
|
-
puts " */"
|
346
|
-
end
|
347
|
-
|
348
|
-
def constraint_end
|
349
|
-
end
|
350
|
-
|
351
|
-
# Of the players of a set of roles, return the one that's a subclass of (or same as) all others, else nil
|
352
|
-
def roleplayer_subclass(roles)
|
353
|
-
roles[1..-1].inject(roles[0].object_type){|subclass, role|
|
354
|
-
next nil unless subclass and EntityType === role.object_type
|
355
|
-
role.object_type.supertypes_transitive.include?(subclass) ? role.object_type : nil
|
356
|
-
}
|
357
|
-
end
|
358
|
-
|
359
|
-
def dump_presence_constraint(c)
|
360
|
-
# Loose binding in PresenceConstraints is limited to explicit role players (in an occurs list)
|
361
|
-
# having no exact match, but having instead exactly one role of the same player in the readings.
|
362
|
-
|
363
|
-
verbaliser = ActiveFacts::Metamodel::Verbaliser.new
|
364
|
-
# For a mandatory constraint (min_frequency == 1, max == nil or 1) any subtyping step is over the proximate role player
|
365
|
-
# For all other presence constraints any subtyping step is over the counterpart player
|
366
|
-
role_proximity = c.min_frequency == 1 && [nil, 1].include?(c.max_frequency) ? :proximate : :counterpart
|
367
|
-
if role_proximity == :proximate
|
368
|
-
verbaliser.role_refs_have_subtype_steps(c.role_sequence)
|
369
|
-
else
|
370
|
-
join_over, joined_roles = ActiveFacts::Metamodel.plays_over(c.role_sequence.all_role_ref.map{|rr|rr.role}, role_proximity)
|
371
|
-
verbaliser.roles_have_same_player(joined_roles) if join_over
|
372
|
-
end
|
373
|
-
|
374
|
-
verbaliser.prepare_role_sequence(c.role_sequence, join_over)
|
375
|
-
# REVISIT: Need to discount role_adjuncts in here, since this constraint uses loose binding:
|
376
|
-
verbaliser.create_subscripts :loose
|
377
|
-
|
378
|
-
expanded_readings = verbaliser.verbalise_over_role_sequence(c.role_sequence, nil, role_proximity)
|
379
|
-
if c.min_frequency == 1 && c.max_frequency == nil and c.role_sequence.all_role_ref.size == 2
|
380
|
-
puts "either #{expanded_readings*' or '};"
|
381
|
-
else
|
382
|
-
roles = c.role_sequence.all_role_ref.map{|rr| rr.role }
|
383
|
-
players = c.role_sequence.all_role_ref.map{|rr| verbaliser.subscripted_player(rr) }
|
384
|
-
players.uniq! if role_proximity == :proximate
|
385
|
-
min, max = c.min_frequency, c.max_frequency
|
386
|
-
pl = (min&&min>1)||(max&&max>1) ? 's' : ''
|
387
|
-
puts \
|
388
|
-
"each #{players.size > 1 ? "combination " : ""}#{players*", "} occurs #{c.frequency} time#{pl} in\n\t"+
|
389
|
-
"#{Array(expanded_readings)*",\n\t"};"
|
390
|
-
end
|
391
|
-
end
|
392
|
-
|
393
|
-
def dump_set_comparison_constraint(c)
|
394
|
-
scrs = c.all_set_comparison_roles.sort_by{|scr| scr.ordinal}
|
395
|
-
role_sequences = scrs.map{|scr|scr.role_sequence}
|
396
|
-
transposed_role_refs = scrs.map{|scr| scr.role_sequence.all_role_ref_in_order.to_a}.transpose
|
397
|
-
verbaliser = ActiveFacts::Metamodel::Verbaliser.new
|
398
|
-
|
399
|
-
# Tell the verbaliser all we know, so it can figure out which players to subscript:
|
400
|
-
players = []
|
401
|
-
trace :subscript, "Preparing query across projected roles in set comparison constraint" do
|
402
|
-
transposed_role_refs.each do |role_refs|
|
403
|
-
verbaliser.role_refs_have_subtype_steps role_refs
|
404
|
-
join_over, = ActiveFacts::Metamodel.plays_over(role_refs.map{|rr| rr.role})
|
405
|
-
players << join_over
|
406
|
-
end
|
407
|
-
end
|
408
|
-
trace :subscript, "Preparing query between roles in set comparison constraint" do
|
409
|
-
role_sequences.each do |role_sequence|
|
410
|
-
trace :subscript, "role sequence is #{role_sequence.describe}" do
|
411
|
-
verbaliser.prepare_role_sequence role_sequence
|
412
|
-
end
|
413
|
-
end
|
414
|
-
end
|
415
|
-
verbaliser.create_subscripts :normal
|
416
|
-
|
417
|
-
if role_sequences.detect{|scr| scr.all_role_ref.detect{|rr| rr.play}}
|
418
|
-
# This set constraint has an explicit query. Verbalise it.
|
419
|
-
|
420
|
-
readings_list = role_sequences.
|
421
|
-
map do |rs|
|
422
|
-
verbaliser.verbalise_over_role_sequence(rs)
|
423
|
-
end
|
424
|
-
if c.is_a?(ActiveFacts::Metamodel::SetEqualityConstraint)
|
425
|
-
puts readings_list.join("\n\tif and only if\n\t") + ';'
|
426
|
-
return
|
427
|
-
end
|
428
|
-
if readings_list.size == 2 && c.is_mandatory # XOR constraint
|
429
|
-
puts "either " + readings_list.join(" or ") + " but not both;"
|
430
|
-
return
|
431
|
-
end
|
432
|
-
|
433
|
-
# Internal check: We must have located the players here
|
434
|
-
if i = players.index(nil)
|
435
|
-
rrs = transposed_role_refs[i]
|
436
|
-
raise "Internal error detecting constrained object types in query involving #{rrs.map{|rr| rr.role.fact_type.default_reading}.uniq*', '}"
|
437
|
-
end
|
438
|
-
|
439
|
-
# Loose binding will apply only to the constrained roles, not to all roles. Not handled here.
|
440
|
-
mode = c.is_mandatory ? "exactly one" : "at most one"
|
441
|
-
puts "for each #{players.map{|p| p.name}*", "} #{mode} of these holds:\n\t" +
|
442
|
-
readings_list.join(",\n\t") +
|
443
|
-
';'
|
444
|
-
return
|
445
|
-
end
|
446
|
-
|
447
|
-
if c.is_a?(ActiveFacts::Metamodel::SetEqualityConstraint)
|
448
|
-
puts \
|
449
|
-
scrs.map{|scr|
|
450
|
-
verbaliser.verbalise_over_role_sequence(scr.role_sequence)
|
451
|
-
} * "\n\tif and only if\n\t" + ";"
|
452
|
-
return
|
453
|
-
end
|
454
|
-
|
455
|
-
# A constrained role may involve a subtyping step. We substitute the name of the supertype for all occurrences.
|
456
|
-
players = transposed_role_refs.map{|role_refs| common_supertype(role_refs.map{|rr| rr.role.object_type})}
|
457
|
-
raise "Constraint must cover matching roles" if players.compact.size < players.size
|
458
|
-
|
459
|
-
readings_expanded = scrs.
|
460
|
-
map do |scr|
|
461
|
-
# verbaliser.verbalise_over_role_sequence(scr.role_sequence)
|
462
|
-
# REVISIT: verbalise_over_role_sequence cannot do what we need here, because of the
|
463
|
-
# possibility of subtyping steps in the constrained roles across the different scr's
|
464
|
-
# The following code uses "players" and "constrained_roles" to create substitutions.
|
465
|
-
# These should instead be passed to the verbaliser (one variable per index, role_refs for each).
|
466
|
-
fact_types_processed = {}
|
467
|
-
constrained_roles = scr.role_sequence.all_role_ref_in_order.map{|rr| rr.role}
|
468
|
-
join_over, joined_roles = *Metamodel.plays_over(constrained_roles)
|
469
|
-
constrained_roles.map do |constrained_role|
|
470
|
-
fact_type = constrained_role.fact_type
|
471
|
-
next nil if fact_types_processed[fact_type] # Don't emit the same fact type twice (in case of objectification step)
|
472
|
-
fact_types_processed[fact_type] = true
|
473
|
-
reading = fact_type.reading_preferably_starting_with_role(constrained_role)
|
474
|
-
expand_constrained(verbaliser, reading, constrained_roles, players)
|
475
|
-
end.compact * " and "
|
476
|
-
end
|
477
|
-
|
478
|
-
if scrs.size == 2 && c.is_mandatory
|
479
|
-
puts "either " + readings_expanded*" or " + " but not both;"
|
480
|
-
else
|
481
|
-
mode = c.is_mandatory ? "exactly one" : "at most one"
|
482
|
-
puts "for each #{players.map{|p| p.name}*", "} #{mode} of these holds:\n\t" +
|
483
|
-
readings_expanded*",\n\t" + ';'
|
484
|
-
end
|
485
|
-
end
|
486
|
-
|
487
|
-
def dump_subset_constraint(c)
|
488
|
-
# If the role players are identical and not duplicated, we can simply say "reading1 only if reading2"
|
489
|
-
subset_roles, subset_fact_types =
|
490
|
-
c.subset_role_sequence.all_role_ref_in_order.map{|rr| [rr.role, rr.role.fact_type]}.transpose
|
491
|
-
superset_roles, superset_fact_types =
|
492
|
-
c.superset_role_sequence.all_role_ref_in_order.map{|rr| [rr.role, rr.role.fact_type]}.transpose
|
493
|
-
transposed_role_refs = [c.subset_role_sequence, c.superset_role_sequence].map{|rs| rs.all_role_ref_in_order.to_a}.transpose
|
494
|
-
|
495
|
-
verbaliser = ActiveFacts::Metamodel::Verbaliser.new
|
496
|
-
transposed_role_refs.each { |role_refs| verbaliser.role_refs_have_subtype_steps role_refs }
|
497
|
-
verbaliser.prepare_role_sequence c.subset_role_sequence
|
498
|
-
verbaliser.prepare_role_sequence c.superset_role_sequence
|
499
|
-
verbaliser.create_subscripts :normal
|
500
|
-
|
501
|
-
puts \
|
502
|
-
verbaliser.verbalise_over_role_sequence(c.subset_role_sequence) +
|
503
|
-
"\n\tonly if " +
|
504
|
-
verbaliser.verbalise_over_role_sequence(c.superset_role_sequence) +
|
505
|
-
";"
|
506
|
-
end
|
507
|
-
|
508
|
-
def dump_ring_constraint(c)
|
509
|
-
# At present, no ring constraint can be missed to be handled in this pass
|
510
|
-
puts "// #{c.ring_type} ring over #{c.role.fact_type.default_reading}"
|
511
|
-
end
|
512
|
-
|
513
|
-
def constraint_dump(c)
|
514
|
-
case c
|
515
|
-
when ActiveFacts::Metamodel::PresenceConstraint
|
516
|
-
dump_presence_constraint(c)
|
517
|
-
when ActiveFacts::Metamodel::RingConstraint
|
518
|
-
dump_ring_constraint(c)
|
519
|
-
when ActiveFacts::Metamodel::SetComparisonConstraint # includes SetExclusionConstraint, SetEqualityConstraint
|
520
|
-
dump_set_comparison_constraint(c)
|
521
|
-
when ActiveFacts::Metamodel::SubsetConstraint
|
522
|
-
dump_subset_constraint(c)
|
523
|
-
else
|
524
|
-
"#{c.class.basename} #{c.name}: unhandled constraint type"
|
525
|
-
end
|
526
|
-
end
|
527
|
-
|
528
|
-
# Find the common supertype of these object_types.
|
529
|
-
def common_supertype(object_types)
|
530
|
-
common = object_types[0].supertypes_transitive
|
531
|
-
object_types[1..-1].each do |object_type|
|
532
|
-
common &= object_type.supertypes_transitive
|
533
|
-
end
|
534
|
-
common[0]
|
535
|
-
end
|
536
|
-
|
537
|
-
#============================================================
|
538
|
-
# Verbalisation functions for fact type and entity type definitions
|
539
|
-
#============================================================
|
540
|
-
|
541
|
-
def fact_readings_with_constraints(verbaliser, fact_type)
|
542
|
-
fact_constraints = @presence_constraints_by_fact[fact_type]
|
543
|
-
readings = []
|
544
|
-
define_role_names = true
|
545
|
-
fact_type.all_reading_by_ordinal.each do |reading|
|
546
|
-
readings << expanded_reading(verbaliser, reading, fact_constraints, define_role_names)
|
547
|
-
define_role_names = false # No need to define role names in subsequent readings
|
548
|
-
end
|
549
|
-
readings
|
550
|
-
end
|
551
|
-
|
552
|
-
def expanded_reading(verbaliser, reading, fact_constraints, define_role_names)
|
553
|
-
# Arrange the roles in order they occur in this reading:
|
554
|
-
role_refs = reading.role_sequence.all_role_ref_in_order
|
555
|
-
role_numbers = reading.text.scan(/\{(\d)\}/).flatten.map{|m| Integer(m) }
|
556
|
-
roles = role_numbers.map{|m| role_refs[m].role }
|
557
|
-
|
558
|
-
# Find the constraints that constrain frequency over each role we can verbalise:
|
559
|
-
frequency_constraints = []
|
560
|
-
value_constraints = []
|
561
|
-
roles.each do |role|
|
562
|
-
value_constraints <<
|
563
|
-
if vc = role.role_value_constraint and !vc.ordered_dumped
|
564
|
-
vc.ordered_dumped!
|
565
|
-
vc.describe
|
566
|
-
else
|
567
|
-
nil
|
568
|
-
end
|
569
|
-
|
570
|
-
frequency_constraints <<
|
571
|
-
if (role == roles.last) # On the last role of the reading, emit any presence constraint
|
572
|
-
constraint = fact_constraints.
|
573
|
-
detect do |c| # Find a UC that spans all other Roles
|
574
|
-
c.is_a?(ActiveFacts::Metamodel::PresenceConstraint) &&
|
575
|
-
!c.ordered_dumped && # Already verbalised
|
576
|
-
roles-c.role_sequence.all_role_ref.map(&:role) == [role]
|
577
|
-
end
|
578
|
-
constraint.ordered_dumped! if constraint
|
579
|
-
constraint && constraint.frequency
|
580
|
-
else
|
581
|
-
nil
|
582
|
-
end
|
583
|
-
end
|
584
|
-
|
585
|
-
expanded = verbaliser.expand_reading(reading, frequency_constraints, define_role_names, value_constraints)
|
586
|
-
expanded = "it is not the case that "+expanded if (reading.is_negative)
|
587
|
-
|
588
|
-
if (ft_rings = @ring_constraints_by_fact[reading.fact_type]) &&
|
589
|
-
(ring = ft_rings.detect{|rc| !rc.ordered_dumped})
|
590
|
-
ring.ordered_dumped!
|
591
|
-
append_ring_to_reading(expanded, ring)
|
592
|
-
end
|
593
|
-
expanded
|
594
|
-
end
|
595
|
-
|
596
|
-
# Expand this reading, substituting players[i].name for the each role in the i'th position in constrained_roles
|
597
|
-
def expand_constrained(verbaliser, reading, constrained_roles, players)
|
598
|
-
# Make sure that we refer to the constrained players by their common supertype (as passed in)
|
599
|
-
frequency_constraints = reading.role_sequence.all_role_ref.
|
600
|
-
map do |role_ref|
|
601
|
-
player = role_ref.role.object_type
|
602
|
-
i = constrained_roles.index(role_ref.role)
|
603
|
-
player = players[i] if i
|
604
|
-
[ nil, player.name ]
|
605
|
-
end
|
606
|
-
frequency_constraints = [] unless frequency_constraints.detect{|fc| fc[0] != "some" }
|
607
|
-
|
608
|
-
expanded = verbaliser.expand_reading(reading, frequency_constraints)
|
609
|
-
expanded = "it is not the case that "+expanded if (reading.is_negative)
|
610
|
-
expanded
|
611
|
-
end
|
612
|
-
|
613
|
-
def build_indices
|
614
|
-
@presence_constraints_by_fact = Hash.new{ |h, k| h[k] = [] }
|
615
|
-
@ring_constraints_by_fact = Hash.new{ |h, k| h[k] = [] }
|
616
|
-
|
617
|
-
@vocabulary.all_constraint.each { |c|
|
618
|
-
case c
|
619
|
-
when ActiveFacts::Metamodel::PresenceConstraint
|
620
|
-
fact_types = c.role_sequence.all_role_ref.map{|rr| rr.role.fact_type}.uniq # All fact types spanned by this constraint
|
621
|
-
if fact_types.size == 1 # There's only one, save it:
|
622
|
-
# trace "Single-fact constraint on #{fact_types[0].concept.guid}: #{c.name}"
|
623
|
-
(@presence_constraints_by_fact[fact_types[0]] ||= []) << c
|
624
|
-
end
|
625
|
-
when ActiveFacts::Metamodel::RingConstraint
|
626
|
-
(@ring_constraints_by_fact[c.role.fact_type] ||= []) << c
|
627
|
-
else
|
628
|
-
# trace "Found unhandled constraint #{c.class} #{c.name}"
|
629
|
-
end
|
630
|
-
}
|
631
|
-
end
|
632
|
-
|
633
|
-
end
|
634
|
-
end
|
635
|
-
|
636
|
-
module Metamodel
|
637
|
-
class ValueType
|
638
|
-
def as_cql
|
639
|
-
parameters =
|
640
|
-
[ length != 0 || scale != 0 ? length : nil,
|
641
|
-
scale != 0 ? scale : nil
|
642
|
-
].compact
|
643
|
-
parameters = parameters.length > 0 ? "("+parameters.join(",")+")" : ""
|
644
|
-
|
645
|
-
"#{name
|
646
|
-
} #{
|
647
|
-
(is_independent ? '[independent] ' : '')
|
648
|
-
}is written as #{
|
649
|
-
(supertype || self).name
|
650
|
-
}#{
|
651
|
-
parameters
|
652
|
-
}#{
|
653
|
-
unit && " "+unit.name
|
654
|
-
}#{
|
655
|
-
transaction_phase && " auto-assigned at "+transaction_phase
|
656
|
-
}#{
|
657
|
-
concept.all_context_note_as_relevant_concept.map do |cn|
|
658
|
-
cn.verbalise
|
659
|
-
end.join("\n")
|
660
|
-
}#{
|
661
|
-
value_constraint && " "+value_constraint.describe
|
662
|
-
};"
|
663
|
-
end
|
664
|
-
end
|
665
|
-
|
666
|
-
class Unit
|
667
|
-
def as_cql
|
668
|
-
if !ephemera_url
|
669
|
-
if coefficient
|
670
|
-
# REVISIT: Use a smarter algorithm to switch to exponential form when there'd be lots of zeroes.
|
671
|
-
coefficient.numerator.to_s('F') +
|
672
|
-
|
673
|
-
if d = coefficient.denominator and d != 1
|
674
|
-
"/#{d}"
|
675
|
-
else
|
676
|
-
''
|
677
|
-
end +
|
678
|
-
|
679
|
-
' '
|
680
|
-
else
|
681
|
-
'1 '
|
682
|
-
end
|
683
|
-
else
|
684
|
-
''
|
685
|
-
end +
|
686
|
-
|
687
|
-
all_derivation_as_derived_unit.
|
688
|
-
sort_by{|d| d.base_unit.name}.
|
689
|
-
# REVISIT: Sort base units
|
690
|
-
# REVISIT: convert negative powers to division?
|
691
|
-
map do |der|
|
692
|
-
base = der.base_unit
|
693
|
-
"#{base.name}#{der.exponent and der.exponent != 1 ? "^#{der.exponent}" : ''} "
|
694
|
-
end*'' +
|
695
|
-
|
696
|
-
if o = offset and o != 0
|
697
|
-
"+ #{o.to_s('F')} "
|
698
|
-
else
|
699
|
-
''
|
700
|
-
end +
|
701
|
-
|
702
|
-
"converts to #{name}#{plural_name ? '/'+plural_name : ''}" +
|
703
|
-
|
704
|
-
(coefficient && !coefficient.is_precise ? ' approximately' : '') +
|
705
|
-
|
706
|
-
(ephemera_url ? " ephemera #{ephemera_url}" : '') +
|
707
|
-
|
708
|
-
';'
|
709
|
-
end
|
710
|
-
end
|
711
|
-
end
|
712
|
-
end
|
713
|
-
|
714
|
-
ActiveFacts::Registry.generator('cql', ActiveFacts::Generate::CQL)
|