activefacts 1.2.1 → 1.3.0
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/examples/CQL/Metamodel.cql +1 -1
- data/lib/activefacts/cql/compiler/entity_type.rb +2 -2
- data/lib/activefacts/cql/compiler/fact_type.rb +1 -0
- data/lib/activefacts/generate/helpers/ordered.rb +29 -13
- data/lib/activefacts/generate/html/glossary.rb +294 -262
- data/lib/activefacts/generate/rails/schema.rb +2 -1
- data/lib/activefacts/generate/transform/surrogate.rb +7 -1
- data/lib/activefacts/input/cql.rb +7 -3
- data/lib/activefacts/input/orm.rb +34 -30
- data/lib/activefacts/mapping/rails.rb +8 -5
- data/lib/activefacts/persistence/reference.rb +3 -2
- data/lib/activefacts/version.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e9276d073a9b3444de848bf033b76f373784a51
|
4
|
+
data.tar.gz: 0ab5602a9298cc84ce688168a3635bd7f4d976a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a984577de5c0aa667d5975f70b35c76f2bb4ab65555fc6a19ca0b7a8bae635cd77fb6d3fbac3959f9f9d76bc53c81846214940ea5109d3e58c6ae5b06197c6c5
|
7
|
+
data.tar.gz: 169adf1025e3c1bb46fa58fd45e79d05c2eb45e4ffb069c25bec1c10b786f3dddca32c97f54556353bd48fb9580e2becc279f5a11185373857af4dff64694635
|
data/examples/CQL/Metamodel.cql
CHANGED
@@ -16,7 +16,7 @@ Enforcement Code is written as String(16);
|
|
16
16
|
Ephemera URL is written as String;
|
17
17
|
Exponent is written as Signed Integer(16);
|
18
18
|
Frequency is written as Unsigned Integer(32);
|
19
|
-
Guid is written as Guid;
|
19
|
+
Guid is written as Guid auto-assigned at assert;
|
20
20
|
Implication Rule Name is written as String;
|
21
21
|
Length is written as Unsigned Integer(32);
|
22
22
|
Literal is written as String;
|
@@ -122,7 +122,7 @@ module ActiveFacts
|
|
122
122
|
if (pc)
|
123
123
|
pc.is_preferred_identifier = true
|
124
124
|
pc.name = "#{@entity_type.name}PK" unless pc.name
|
125
|
-
trace "Existing PC #{pc.verbalise} is now PK for #{@entity_type.name}"
|
125
|
+
trace :constraint, "Existing PC #{pc.verbalise} is now PK for #{@entity_type.name}"
|
126
126
|
else
|
127
127
|
# Add a unique constraint over all identifying roles
|
128
128
|
pc = @constellation.PresenceConstraint(
|
@@ -254,7 +254,7 @@ module ActiveFacts
|
|
254
254
|
@constellation.RoleRef(rs2, 0, :role => super_role)
|
255
255
|
@constellation.RoleRef(rs2, 1, :role => sub_role)
|
256
256
|
# Decide in which order to include is a/is an. Provide both, but in order.
|
257
|
-
n = '
|
257
|
+
n = 'aeioh'.include?(sub_role.object_type.name.downcase[0]) ? 'n' : ''
|
258
258
|
@constellation.Reading(inheritance_fact, 2, :role_sequence => rs2, :text => "{0} is a#{n} {1}", :is_negative => false)
|
259
259
|
|
260
260
|
if is_identifying_supertype
|
@@ -287,6 +287,7 @@ module ActiveFacts
|
|
287
287
|
epc = ref.embedded_presence_constraint or next
|
288
288
|
epc.max_frequency == 1 or next
|
289
289
|
next if epc.enforcement
|
290
|
+
trace :constraint, "Converting UC into PI for #{@fact_type.entity_type.name}"
|
290
291
|
epc.is_preferred_identifier = true
|
291
292
|
return
|
292
293
|
end
|
@@ -131,13 +131,16 @@ module ActiveFacts
|
|
131
131
|
sorted.each{|o|
|
132
132
|
next if o.ordered_dumped # Already done
|
133
133
|
|
134
|
+
trace :ordered, "Panicing to dump #{panic.name}" if panic
|
134
135
|
# Can we do this yet?
|
136
|
+
remaining_precursors = Array(@precursors[o])-[o]
|
135
137
|
if (o != panic and # We don't *have* to do it (panic mode)
|
136
|
-
|
137
|
-
|
138
|
+
remaining_precursors.size > 0) # precursors - still blocked
|
139
|
+
trace :ordered, "Can't dump #{o.name} despite panic for #{panic.name}, it still needs #{remaining_precursors.map(&:name)*', '}" if panic
|
138
140
|
skipped_this_pass += 1
|
139
141
|
next
|
140
142
|
end
|
143
|
+
trace :ordered, "Dumping #{o.name} in panic mode, even though it still needs #{remaining_precursors.map(&:name)*', '}" if panic
|
141
144
|
|
142
145
|
entity_type_banner unless done_banner
|
143
146
|
done_banner = true
|
@@ -162,26 +165,39 @@ module ActiveFacts
|
|
162
165
|
|
163
166
|
# Check that we made progress if there's any to make:
|
164
167
|
if count_this_pass == 0 && skipped_this_pass > 0
|
168
|
+
=begin
|
165
169
|
if panic # We were already panicing... what to do now?
|
166
170
|
# This won't happen again unless the above code is changed to decide it can't dump "panic".
|
171
|
+
bad = sorted.select do |o|
|
172
|
+
o.is_a?(ActiveFacts::Metamodel::EntityType) &&
|
173
|
+
!o.ordered_dumped &&
|
174
|
+
(Array(@precursors[o])-[o]).size > 0 &&
|
175
|
+
(Array(@followers[o])-[o]).size > 0
|
176
|
+
end
|
177
|
+
|
167
178
|
raise "Unresolvable cycle of forward references: " +
|
168
|
-
|
169
|
-
":\n\t" +
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
179
|
+
bad.map { |o| o.name }*', ' +
|
180
|
+
":\n\t" +
|
181
|
+
(
|
182
|
+
bad.map do |o|
|
183
|
+
o.name +
|
184
|
+
" depends on " +
|
185
|
+
(@precursors[o].uniq.map{|p| p.name}.sort*', ')
|
186
|
+
end
|
187
|
+
) * "\n\t" +
|
188
|
+
"\n"
|
189
|
+
=end
|
190
|
+
# else
|
175
191
|
# Find the object that has the most followers and no fwd-ref'd supertypes:
|
176
192
|
# This selection might be better if we allow PI roles to be fwd-ref'd...
|
177
193
|
panic = sorted.
|
178
|
-
select{|o| o.ordered_dumped }.
|
194
|
+
select{|o| !o.ordered_dumped }.
|
179
195
|
sort_by{|o|
|
180
|
-
f = @followers[o] || [];
|
196
|
+
f = (@followers[o] || []) - [o];
|
181
197
|
o.supertypes.detect{|s| !s.ordered_dumped } ? 0 : -f.size
|
182
198
|
}[0]
|
183
|
-
|
184
|
-
end
|
199
|
+
trace :ordered, "Panic mode, selected #{panic.name} next"
|
200
|
+
# end
|
185
201
|
end
|
186
202
|
|
187
203
|
break if skipped_this_pass == 0 # All done.
|
@@ -19,6 +19,16 @@ module ActiveFacts
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def set_option(option)
|
22
|
+
@gen_bootstrap = false
|
23
|
+
case option
|
24
|
+
when 'help', '?'
|
25
|
+
$stderr.puts "Usage:\t\tafgen --html/glossary[=option] input_file.cql\n"+
|
26
|
+
"\t\tbootstrap\tGenerate bootstrap styled glossary html"
|
27
|
+
exit 0
|
28
|
+
when /bootstrap/
|
29
|
+
@gen_bootstrap = true
|
30
|
+
else super
|
31
|
+
end
|
22
32
|
end
|
23
33
|
|
24
34
|
def puts(*a)
|
@@ -31,134 +41,156 @@ module ActiveFacts
|
|
31
41
|
|
32
42
|
def generate(out = $>)
|
33
43
|
@out = out
|
44
|
+
|
45
|
+
@all_object_type =
|
46
|
+
@vocabulary.
|
47
|
+
all_object_type.
|
48
|
+
sort_by{|o| o.name.gsub(/ /,'').downcase}
|
49
|
+
|
34
50
|
vocabulary_start
|
35
51
|
|
36
|
-
|
37
|
-
|
52
|
+
if @gen_bootstrap
|
53
|
+
object_types_dump_toc()
|
54
|
+
object_types_dump_def()
|
55
|
+
else
|
56
|
+
object_types_dump_def()
|
57
|
+
object_types_dump_toc()
|
58
|
+
end
|
38
59
|
vocabulary_end
|
39
60
|
end
|
40
61
|
|
41
62
|
def vocabulary_start
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
63
|
+
if !@gen_bootstrap
|
64
|
+
# puts "<link rel='stylesheet' href='css/orm2.css' media='screen' type='text/css'/>"
|
65
|
+
css_file = "/../../../../css/orm2.css"
|
66
|
+
|
67
|
+
File.open(File.dirname(__FILE__)+css_file) do |f|
|
68
|
+
puts "<style media='screen' type='text/css'>"
|
69
|
+
puts f.read
|
70
|
+
puts %Q{
|
71
|
+
.glossary-facttype, .glossary-constraints { display: block; }
|
72
|
+
.glossary-doc.hide-alternates .glossary-alternates { display: none; }
|
73
|
+
.glossary-doc.hide-constraints .glossary-constraints { display: none; }
|
74
|
+
.glossary-doc.hide-examples .glossary-example { display: none; }
|
75
|
+
}.gsub(/^\s+/, '')
|
76
|
+
puts "</style>"
|
77
|
+
end
|
78
|
+
|
79
|
+
puts %Q{
|
80
|
+
<style media='print' type='text/css'>
|
81
|
+
.keyword { color: #0000CC; font-style: italic; display: inline; }
|
82
|
+
.vocabulary, .object_type { color: #8A0092; font-weight: bold; }
|
83
|
+
.copula { color: #0E5400; }
|
84
|
+
.value { color: #FF990E; display: inline; }
|
85
|
+
.glossary-toc { display: none; }
|
86
|
+
.glossary-facttype, .glossary-reading { display: inline; }
|
87
|
+
</style>
|
88
|
+
}.gsub(/^\s+/, '')
|
89
|
+
end
|
65
90
|
end
|
66
91
|
|
67
92
|
def vocabulary_end
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
+
if !@gen_bootstrap
|
94
|
+
puts %Q{
|
95
|
+
<script type="text/javascript">
|
96
|
+
function toggle_class(e, c) {
|
97
|
+
if (!e) return;
|
98
|
+
var n = e.className;
|
99
|
+
var i = n.indexOf(c);
|
100
|
+
if (i == -1) {
|
101
|
+
e.className = n+' '+c;
|
102
|
+
} else {
|
103
|
+
e.className = n.slice(0, i)+n.slice(i+c.length);
|
104
|
+
}
|
105
|
+
if (document.location.toString().indexOf('#') >= 0)
|
106
|
+
document.location = document.location; // Re-scroll to the current fragment
|
107
|
+
}
|
108
|
+
function toggle_constraints() {
|
109
|
+
toggle_class(document.getElementById('glossary-doc'), 'hide-constraints');
|
110
|
+
}
|
111
|
+
function toggle_alternates() {
|
112
|
+
toggle_class(document.getElementById('glossary-doc'), 'hide-alternates');
|
113
|
+
}
|
114
|
+
function toggle_examples() {
|
115
|
+
toggle_class(document.getElementById('glossary-doc'), 'hide-examples');
|
116
|
+
}
|
117
|
+
</script>
|
118
|
+
}.gsub(/^\s+/, '')
|
119
|
+
end
|
93
120
|
end
|
94
121
|
|
95
|
-
def
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
122
|
+
def object_types_dump_toc
|
123
|
+
if @gen_bootstrap
|
124
|
+
puts '<div class="col-md-3 glossary-sidebar">'
|
125
|
+
else
|
126
|
+
puts '<div class="glossary-sidebar">'
|
127
|
+
end
|
128
|
+
puts '<h1 style="visibility: hidden">X</h1>'
|
129
|
+
puts '<ol class="glossary-toc">'
|
130
|
+
@all_object_type.
|
131
|
+
reject do |o|
|
132
|
+
o.name == '_ImplicitBooleanValueType' or
|
133
|
+
o.kind_of?(ActiveFacts::Metamodel::ValueType) && o.all_role.size == 0 or
|
134
|
+
o.kind_of?(ActiveFacts::Metamodel::TypeInheritance)
|
135
|
+
end.
|
136
|
+
each do |o|
|
137
|
+
puts "<li>#{termref(o.name)}</li>"
|
138
|
+
end
|
139
|
+
puts '</ol>'
|
140
|
+
puts '<div class="glossary-controls">'
|
141
|
+
puts ' <input type="button" onclick="toggle_constraints()" value="Constraints" class="glossary-toggle-constraint">'
|
142
|
+
puts ' <input type="button" onclick="toggle_alternates()" value="Alternates" class="glossary-toggle-alternates">'
|
143
|
+
puts ' <input type="button" onclick="toggle_examples()" value="Examples" class="glossary-toggle-examples">'
|
144
|
+
puts '</div>'
|
145
|
+
puts '</div>'
|
146
|
+
end
|
147
|
+
|
148
|
+
def object_types_dump_def
|
149
|
+
if @gen_bootstrap
|
150
|
+
puts '<div class="col-md-5 glossary-doc hide-alternates hide-constraints" id="glossary-doc">'
|
151
|
+
else
|
152
|
+
puts '<div class="glossary-doc hide-alternates hide-constraints" id="glossary-doc">'
|
153
|
+
end
|
154
|
+
puts "<h1>#{@vocabulary.name}</h1>"
|
155
|
+
puts '<dl>'
|
156
|
+
@all_object_type.
|
157
|
+
each do |o|
|
158
|
+
case o
|
159
|
+
when ActiveFacts::Metamodel::TypeInheritance
|
160
|
+
nil
|
161
|
+
when ActiveFacts::Metamodel::ValueType
|
162
|
+
value_type_dump(o)
|
163
|
+
else
|
164
|
+
if o.fact_type
|
165
|
+
objectified_fact_type_dump(o)
|
166
|
+
else
|
167
|
+
entity_type_dump(o)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
puts '</dl>'
|
172
|
+
puts '</div>'
|
173
|
+
end
|
142
174
|
|
143
175
|
def element(text, attrs, tag = 'span')
|
144
176
|
"<#{tag}#{attrs.empty? ? '' : attrs.map{|k,v| " #{k}='#{v}'"}*''}>#{text}</#{tag}>"
|
145
177
|
end
|
146
178
|
|
147
|
-
|
148
|
-
|
149
|
-
|
179
|
+
def span(text, klass = nil)
|
180
|
+
element(text, klass ? {:class => klass} : {})
|
181
|
+
end
|
150
182
|
|
151
|
-
|
152
|
-
|
153
|
-
|
183
|
+
def div(text, klass = nil)
|
184
|
+
element(text, klass ? {:class => klass} : {}, 'div')
|
185
|
+
end
|
154
186
|
|
155
|
-
|
156
|
-
|
157
|
-
|
187
|
+
def h1(text, klass = nil)
|
188
|
+
element(text, klass ? {:class => klass} : {}, 'h1')
|
189
|
+
end
|
158
190
|
|
159
|
-
|
160
|
-
|
161
|
-
|
191
|
+
def dl(text, klass = nil)
|
192
|
+
element(text, klass ? {:class => klass} : {}, 'dl')
|
193
|
+
end
|
162
194
|
|
163
195
|
# A definition of a term
|
164
196
|
def termdef(name)
|
@@ -178,54 +210,54 @@ module ActiveFacts
|
|
178
210
|
|
179
211
|
def value_type_dump(o)
|
180
212
|
return if o.all_role.size == 0 or # Skip value types that are only used as supertypes
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
213
|
+
o.name == '_ImplicitBooleanValueType'
|
214
|
+
puts " <dt>" +
|
215
|
+
"#{termdef(o.name)} " +
|
216
|
+
(if o.supertype
|
217
|
+
span('is written as ', :keyword) + termref(o.supertype.name)
|
218
|
+
else
|
219
|
+
" (a fundamental data type)"
|
220
|
+
end) +
|
221
|
+
"</dt>"
|
222
|
+
|
223
|
+
puts " <dd>"
|
224
|
+
value_sub_types(o)
|
225
|
+
relevant_facts_and_constraints(o)
|
226
|
+
values(o)
|
227
|
+
puts " </dd>"
|
196
228
|
end
|
197
229
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
230
|
+
def value_sub_types(o)
|
231
|
+
o.
|
232
|
+
all_value_type_as_supertype. # All value types for which o is a supertype
|
233
|
+
sort_by{|sub| sub.name}.
|
234
|
+
each do |sub|
|
235
|
+
puts div(
|
236
|
+
"#{termref(sub.name)} #{span('is written as', 'keyword')} #{termref(o.name)}",
|
237
|
+
'glossary-facttype'
|
238
|
+
)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def values(o)
|
243
|
+
o.all_instance.
|
244
|
+
sort_by{|i|
|
245
|
+
[i.population.name, i.value.literal]
|
246
|
+
}.
|
247
|
+
each do |i|
|
248
|
+
v = i.value
|
249
|
+
puts div(
|
250
|
+
(i.population.name.empty? ? '' : i.population.name+': ') +
|
251
|
+
termref(o.name) + ' ' +
|
252
|
+
div(
|
253
|
+
# v.is_literal_string ? v.literal.inspect : v.literal,
|
254
|
+
v.literal.inspect,
|
255
|
+
'value'
|
256
|
+
),
|
257
|
+
'glossary-example'
|
258
|
+
)
|
259
|
+
end
|
260
|
+
end
|
229
261
|
|
230
262
|
def relevant_facts_and_constraints(o)
|
231
263
|
puts(
|
@@ -234,14 +266,14 @@ module ActiveFacts
|
|
234
266
|
map{|r| r.fact_type}.
|
235
267
|
uniq.
|
236
268
|
reject do |ft|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
269
|
+
ft.is_a?(ActiveFacts::Metamodel::LinkFactType)
|
270
|
+
end.
|
271
|
+
map { |ft| [ft, " #{fact_type_with_constraints(ft, o)}"] }.
|
272
|
+
sort_by{|ft, text|
|
273
|
+
[ ft.is_a?(ActiveFacts::Metamodel::TypeInheritance) ? 0 : 1, text]
|
274
|
+
}.
|
275
|
+
map{|ft, text| text}.
|
276
|
+
join "\n"
|
245
277
|
)
|
246
278
|
end
|
247
279
|
|
@@ -259,9 +291,9 @@ module ActiveFacts
|
|
259
291
|
element(
|
260
292
|
reading.expand([], include_rolenames) do |rr, freq_con, l_adj, name, t_adj, role_name_def, literal|
|
261
293
|
if role_name_def
|
262
|
-
|
263
|
-
|
264
|
-
|
294
|
+
role_name_def = role_name_def.gsub(/\(as ([^)]+)\)/) {
|
295
|
+
span("(as #{ termref(rr.role.object_type.name, $1) })", 'keyword')
|
296
|
+
}
|
265
297
|
end
|
266
298
|
role_ref rr, freq_con, l_adj, name, t_adj, role_name_def, literal
|
267
299
|
end,
|
@@ -270,53 +302,53 @@ module ActiveFacts
|
|
270
302
|
end
|
271
303
|
|
272
304
|
def fact_type_block(ft, include_alternates = true, wrt = nil, include_rolenames = true)
|
273
|
-
|
274
|
-
|
305
|
+
div(fact_type(ft, include_alternates, wrt, include_rolenames), 'glossary-facttype')
|
306
|
+
end
|
275
307
|
|
276
308
|
def fact_type(ft, include_alternates = true, wrt = nil, include_rolenames = true)
|
277
309
|
role = ft.all_role.detect{|r| r.object_type == wrt}
|
278
310
|
preferred_reading = ft.reading_preferably_starting_with_role(role)
|
279
311
|
alternate_readings = ft.all_reading.reject{|r| r == preferred_reading}
|
280
312
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
313
|
+
div(
|
314
|
+
expand_reading(preferred_reading, include_rolenames),
|
315
|
+
'glossary-reading'
|
316
|
+
)+
|
317
|
+
(if include_alternates and alternate_readings.size > 0
|
318
|
+
div(
|
319
|
+
"(alternatively: " +
|
320
|
+
alternate_readings.map do |reading|
|
321
|
+
div(
|
322
|
+
expand_reading(reading, include_rolenames),
|
323
|
+
'glossary-reading'
|
324
|
+
)
|
325
|
+
end*",\n"+')',
|
326
|
+
'glossary-alternates'
|
327
|
+
)
|
328
|
+
else
|
329
|
+
''
|
330
|
+
end
|
331
|
+
)
|
300
332
|
end
|
301
333
|
|
302
334
|
def fact_type_with_constraints(ft, wrt = nil)
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
335
|
+
if ft.entity_type
|
336
|
+
div(
|
337
|
+
termref(ft.entity_type.name) +
|
338
|
+
div(' is where ', 'keyword') +
|
339
|
+
fact_type(ft, true, wrt),
|
340
|
+
'glossary-objectification'
|
341
|
+
)
|
342
|
+
else
|
343
|
+
fact_type_block(ft, true, wrt)
|
344
|
+
end +
|
345
|
+
%Q{\n<ul class="glossary-constraints">\n}+
|
346
|
+
(unless ft.is_a?(ActiveFacts::Metamodel::TypeInheritance)
|
347
|
+
fact_type_constraints(ft)
|
348
|
+
else
|
349
|
+
''
|
350
|
+
end) +
|
351
|
+
"</ul>"
|
320
352
|
end
|
321
353
|
|
322
354
|
def fact_type_constraints(ft)
|
@@ -327,14 +359,14 @@ module ActiveFacts
|
|
327
359
|
reading.role_sequence.all_role_ref_in_order[reading.role_numbers[-1]].role == residual_role
|
328
360
|
}
|
329
361
|
next nil unless reading
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
362
|
+
div(
|
363
|
+
element(
|
364
|
+
reading.expand_with_final_presence_constraint { |*a| role_ref(*a) },
|
365
|
+
{:class => 'copula'}
|
366
|
+
),
|
367
|
+
'glossary-constraint'
|
368
|
+
)+"\n"
|
369
|
+
end.compact*''
|
338
370
|
end
|
339
371
|
|
340
372
|
def objectified_fact_type_dump(o)
|
@@ -345,7 +377,7 @@ module ActiveFacts
|
|
345
377
|
# REVISIT: Handle separate identification
|
346
378
|
|
347
379
|
puts " <dd>"
|
348
|
-
|
380
|
+
puts fact_type_with_constraints(o.fact_type)
|
349
381
|
|
350
382
|
o.fact_type.all_role_in_order.each do |r|
|
351
383
|
n = r.object_type.name
|
@@ -367,59 +399,59 @@ module ActiveFacts
|
|
367
399
|
[
|
368
400
|
(supers.size > 0 ? "#{span('is a kind of', 'keyword')} #{supers.map{|s| termref(s.name)}*', '}" : nil),
|
369
401
|
(if pi
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
402
|
+
"#{span('is identified by', 'keyword')} " +
|
403
|
+
pi.role_sequence.all_role_ref_in_order.map do |rr|
|
404
|
+
termref(
|
405
|
+
rr.role.object_type.name,
|
406
|
+
[ rr.leading_adjective,
|
407
|
+
rr.role.role_name || rr.role.object_type.name,
|
408
|
+
rr.trailing_adjective
|
409
|
+
].compact*'-'
|
410
|
+
)
|
411
|
+
end*", "
|
412
|
+
else
|
413
|
+
nil
|
414
|
+
end)
|
383
415
|
].compact*', '
|
384
|
-
|
416
|
+
"</dt>"
|
385
417
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
418
|
+
puts " <dd>"
|
419
|
+
relevant_facts_and_constraints(o)
|
420
|
+
entities(o)
|
421
|
+
puts " </dd>"
|
390
422
|
end
|
391
423
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
424
|
+
def entities(o)
|
425
|
+
return if o.preferred_identifier.role_sequence.all_role_ref.size > 1 # REVISIT: Composite identification
|
426
|
+
o.all_instance.each do |i|
|
427
|
+
v = i.value
|
428
|
+
ii = i # The identifying instance
|
429
|
+
|
430
|
+
until v
|
431
|
+
pi = ii.object_type.preferred_identifier # ii is an Entity Type
|
432
|
+
break if pi.role_sequence.all_role_ref.size > 1 # REVISIT: Composite identification
|
433
|
+
|
434
|
+
identifying_fact_type = pi.role_sequence.all_role_ref.single.role.fact_type
|
435
|
+
# Find the role played by this instance through which it is identified:
|
436
|
+
irv = i.all_role_value.detect{|rv| rv.fact.fact_type == identifying_fact_type }
|
437
|
+
# Get the other RoleValue in what must be a binary fact type:
|
438
|
+
orv = irv.fact.all_role_value.detect{|rv| rv != irv}
|
439
|
+
ii = orv.instance
|
440
|
+
v = ii.value # Does this instance have a value? If so, we're done.
|
441
|
+
end
|
442
|
+
|
443
|
+
next unless v
|
444
|
+
puts div(
|
445
|
+
(i.population.name.empty? ? '' : i.population.name+': ') +
|
446
|
+
termref(o.name) + ' ' +
|
447
|
+
div(
|
448
|
+
# v.is_literal_string ? v.literal.inspect : v.literal,
|
449
|
+
v.literal.inspect,
|
450
|
+
'value'),
|
451
|
+
'glossary-example'
|
452
|
+
)
|
453
|
+
end
|
454
|
+
end
|
423
455
|
|
424
456
|
end
|
425
457
|
end
|
@@ -143,13 +143,14 @@ module ActiveFacts
|
|
143
143
|
|
144
144
|
foreign_keys.concat(
|
145
145
|
if (from_columns.length == 1)
|
146
|
+
index_name = Persistence.rails_name_trunc('index_'+fk.from.rails_name+'_on_'+from_columns[0])
|
146
147
|
[
|
147
148
|
" add_foreign_key :#{fk.from.rails_name}, :#{fk.to.rails_name}, :column => :#{from_columns[0]}, :primary_key => :#{to_columns[0]}, :on_delete => :cascade"
|
148
149
|
]+
|
149
150
|
Array(
|
150
151
|
# Index it non-uniquely only if it's not unique already:
|
151
152
|
fk.jump_reference.to_role.unique ? nil :
|
152
|
-
" add_index :#{fk.from.rails_name}, [:#{from_columns[0]}], :unique => false"
|
153
|
+
" add_index :#{fk.from.rails_name}, [:#{from_columns[0]}], :unique => false, :name => :#{index_name}"
|
153
154
|
)
|
154
155
|
else
|
155
156
|
# This probably isn't going to work without Dr Nic's CPK gem:
|
@@ -128,7 +128,10 @@ module ActiveFacts
|
|
128
128
|
|
129
129
|
irf.flatten!
|
130
130
|
|
131
|
-
# Multi-part identifiers are only allowed if
|
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:
|
132
135
|
if irf.size >= 2
|
133
136
|
if pk_fks.include?(nil)
|
134
137
|
trace :transform_surrogate, "#{self.name} needs a surrogate because its multi-part key contains a non-table"
|
@@ -136,6 +139,9 @@ module ActiveFacts
|
|
136
139
|
elsif references_to.size != 0
|
137
140
|
trace :transform_surrogate, "#{self.name} is a join table between #{pk_fks.map(&:name).inspect} but is also an FK target"
|
138
141
|
return true
|
142
|
+
elsif (references_from-identifying_refs_from).size > 0
|
143
|
+
# There are other attributes to worry about
|
144
|
+
return true
|
139
145
|
else
|
140
146
|
trace :transform_surrogate, "#{self.name} is a join table between #{pk_fks.map(&:name).inspect}"
|
141
147
|
return false
|
@@ -14,9 +14,13 @@ module ActiveFacts
|
|
14
14
|
class CQL
|
15
15
|
# Read the specified file
|
16
16
|
def self.readfile(filename)
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
if File.basename(filename, '.cql') == "-"
|
18
|
+
read(STDIN, "<standard input>")
|
19
|
+
else
|
20
|
+
File.open(filename) {|file|
|
21
|
+
read(file, filename)
|
22
|
+
}
|
23
|
+
end
|
20
24
|
rescue => e
|
21
25
|
# Augment the exception message, but preserve the backtrace
|
22
26
|
ne = StandardError.new("In #{filename} #{e.message.strip}")
|
@@ -72,9 +72,13 @@ module ActiveFacts
|
|
72
72
|
|
73
73
|
private
|
74
74
|
def self.readfile(filename, *options)
|
75
|
-
|
76
|
-
|
77
|
-
|
75
|
+
if File.basename(filename, '.orm') == "-"
|
76
|
+
self.read(STDIN, "<standard input>", options)
|
77
|
+
else
|
78
|
+
File.open(filename) {|file|
|
79
|
+
self.read(file, filename, *options)
|
80
|
+
}
|
81
|
+
end
|
78
82
|
end
|
79
83
|
|
80
84
|
def self.read(file, filename = "stdin", *options)
|
@@ -378,7 +382,7 @@ module ActiveFacts
|
|
378
382
|
rs2 = @constellation.RoleSequence(:new)
|
379
383
|
@constellation.RoleRef(rs2, 0, :role => supertype_role)
|
380
384
|
@constellation.RoleRef(rs2, 1, :role => subtype_role)
|
381
|
-
n = '
|
385
|
+
n = 'aeioh'.include?(subtype_role.object_type.name.downcase[0]) ? 1 : 0
|
382
386
|
@constellation.Reading(inheritance_fact, 2+n, :role_sequence => rs2, :text => "{0} is a {1}", :is_negative => false)
|
383
387
|
@constellation.Reading(inheritance_fact, 3-n, :role_sequence => rs2, :text => "{0} is an {1}", :is_negative => false)
|
384
388
|
}
|
@@ -835,12 +839,12 @@ module ActiveFacts
|
|
835
839
|
@constellation.Variable(query, query.all_variable.size, :object_type => ti.subtype)
|
836
840
|
supertype_node = query.all_variable.detect{|jn| jn.object_type == ti.supertype } ||
|
837
841
|
@constellation.Variable(query, query.all_variable.size, :object_type => ti.supertype)
|
842
|
+
step = @constellation.Step(:guid => :new, :fact_type => ti)
|
838
843
|
rs = @constellation.RoleSequence(:new)
|
839
844
|
@constellation.RoleRef(rs, 0, :role => ti.subtype_role)
|
840
|
-
sub_play = @constellation.Play(subtype_node, ti.subtype_role)
|
845
|
+
sub_play = @constellation.Play(:step => step, :variable => subtype_node, :role => ti.subtype_role)
|
841
846
|
@constellation.RoleRef(rs, 1, :role => ti.supertype_role)
|
842
|
-
sup_play = @constellation.Play(supertype_node, ti.supertype_role)
|
843
|
-
step = @constellation.Step(sub_play, sup_play, :fact_type => ti)
|
847
|
+
sup_play = @constellation.Play(:step => step, :variable => supertype_node, :role => ti.supertype_role, :is_input => true)
|
844
848
|
trace :query, "New subtyping step #{step.describe}"
|
845
849
|
step
|
846
850
|
end
|
@@ -972,11 +976,11 @@ module ActiveFacts
|
|
972
976
|
# There may be more than one supertyping level. Make the steps:
|
973
977
|
subtyping_steps = subtype_steps(query, subtype, end_point)
|
974
978
|
step = subtyping_steps[0]
|
975
|
-
constrained_play = subtyping_steps[-1].
|
979
|
+
constrained_play = subtyping_steps[-1].all_play.detect{|p| p.role.object_type == end_point}
|
976
980
|
|
977
981
|
# Replace the constrained role and node with the supertype ones:
|
978
982
|
end_node = query.all_variable.detect{|jn| jn.object_type == end_point }
|
979
|
-
projecting_play = step.
|
983
|
+
projecting_play = step.all_play.detect{|p| p.role.object_type == subtype}
|
980
984
|
end_role = step.fact_type.all_role.detect{|r| r.object_type == end_point }
|
981
985
|
role_node = query.all_variable.detect{|jn| jn.object_type == role_ref.role.object_type }
|
982
986
|
end
|
@@ -984,7 +988,7 @@ module ActiveFacts
|
|
984
988
|
|
985
989
|
raise "Internal error in #{constraint_type} #{name}: making illegal reference to variable, end role mismatch" if end_role.object_type != end_node.object_type
|
986
990
|
rr = @constellation.RoleRef(replacement_rs, replacement_rs.all_role_ref.size, :role => end_role)
|
987
|
-
projecting_play ||= (constrained_play = @constellation.Play(end_node, end_role))
|
991
|
+
projecting_play ||= (constrained_play = @constellation.Play(:variable => end_node, :role => end_role))
|
988
992
|
projecting_play.role_ref = rr # Project this RoleRef
|
989
993
|
# projecting_play.variable.projection = rr.role # REVISIT: The variable should project a role, not the Play a RoleRef
|
990
994
|
|
@@ -997,13 +1001,12 @@ module ActiveFacts
|
|
997
1001
|
rs = @constellation.RoleSequence(:new)
|
998
1002
|
# Detect the fact type over which we're stepping (may involve objectification)
|
999
1003
|
raise "Internal error in #{constraint_type} #{name}: making illegal reference to variable, object type mismatch" if role_ref.role.object_type != role_node.object_type
|
1004
|
+
step = @constellation.Step(:guid => :new, :fact_type => joined_role.fact_type)
|
1000
1005
|
@constellation.RoleRef(rs, 0, :role => role_ref.role)
|
1001
|
-
role_play = @constellation.Play(role_node, role_ref.role)
|
1006
|
+
role_play = @constellation.Play(:step => step, :variable => role_node, :role => role_ref.role, :is_input => true)
|
1002
1007
|
raise "Internal error in #{constraint_type} #{name}: making illegal reference to variable, joined_role mismatch" if joined_role.object_type != variable.object_type
|
1003
1008
|
@constellation.RoleRef(rs, 1, :role => joined_role)
|
1004
|
-
join_play = @constellation.Play(variable, joined_role)
|
1005
|
-
|
1006
|
-
step = @constellation.Step(role_play, join_play, :fact_type => joined_role.fact_type)
|
1009
|
+
join_play = @constellation.Play(:step => step, :variable => variable, :role => joined_role)
|
1007
1010
|
trace :query, "New step #{step.describe}"
|
1008
1011
|
end
|
1009
1012
|
else
|
@@ -1012,13 +1015,13 @@ module ActiveFacts
|
|
1012
1015
|
# Here we have an end join (step already created) but no sequence join
|
1013
1016
|
if variable
|
1014
1017
|
raise "Internal error in #{constraint_type} #{name}: making illegal step" if role_ref.role.object_type != role_node.object_type
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
+
step = @constellation.Step(:guid => :new, :fact_type => role_ref.role.fact_type)
|
1019
|
+
join_play = @constellation.Play(:step => step, :variable => variable, :role => query_role, :is_input => true)
|
1020
|
+
role_play = @constellation.Play(:step => step, :variable => role_node, :role => role_ref.role)
|
1018
1021
|
roles -= [query_role, role_ref.role]
|
1019
1022
|
roles.each do |incidental_role|
|
1020
1023
|
jn = @constellation.Variable(query, query.all_variable.size, :object_type => incidental_role.object_type)
|
1021
|
-
play = @constellation.Play(jn, incidental_role, :step => step)
|
1024
|
+
play = @constellation.Play(:step => step, :variable => jn, :role => incidental_role, :step => step)
|
1022
1025
|
end
|
1023
1026
|
else
|
1024
1027
|
if role_sequence.all_role_ref.size > 1
|
@@ -1028,25 +1031,20 @@ module ActiveFacts
|
|
1028
1031
|
# There's no query in this role sequence, so we'd drop off the bottom without doing the right things. Why?
|
1029
1032
|
# Without this case, Supervision.orm omits "that runs Company" from the exclusion constraint, and I'm not sure why.
|
1030
1033
|
# I think the "then" code causes it to drop out the bottom without making the step (which is otherwise made in every case, see CompanyDirectorEmployee for example)
|
1031
|
-
|
1032
|
-
|
1034
|
+
step = @constellation.Step(:guid => :new, :fact_type => role_ref.role.fact_type)
|
1035
|
+
role_play = @constellation.Play(:step => step, :variable => role_node, :role => role_ref.role, :is_input => true)
|
1033
1036
|
role_ref.role.fact_type.all_role.each do |role|
|
1034
1037
|
next if role == role_play.role
|
1035
1038
|
next if role_sequence.all_role_ref.detect{|rr| rr.role == role}
|
1036
1039
|
jn = @constellation.Variable(query, query.all_variable.size, :object_type => role.object_type)
|
1037
|
-
play = @constellation.Play(jn, role)
|
1038
|
-
if step
|
1039
|
-
play.step = step # Incidental role
|
1040
|
-
else
|
1041
|
-
step = @constellation.Step(role_play, play, :fact_type => role_ref.role.fact_type)
|
1042
|
-
end
|
1040
|
+
play = @constellation.Play(:step => step, :variable => jn, :role => role)
|
1043
1041
|
end
|
1044
1042
|
end
|
1045
1043
|
end
|
1046
1044
|
else
|
1047
1045
|
# Unary fact type, make a Step from and to the constrained_play
|
1048
|
-
|
1049
|
-
|
1046
|
+
step = @constellation.Step(:guid => :new, :fact_type => role_ref.role.fact_type)
|
1047
|
+
play = @constellation.Play(:step => step, :variable => constrained_play.variable, :role => role_ref.role, :is_input => true)
|
1050
1048
|
end
|
1051
1049
|
end
|
1052
1050
|
end
|
@@ -1092,9 +1090,15 @@ module ActiveFacts
|
|
1092
1090
|
@mandatory_constraints_by_rs.delete(mc_rs)
|
1093
1091
|
end
|
1094
1092
|
|
1095
|
-
|
1093
|
+
if role_sequences.compact.size != role_sequences.size # Role sequence missing; includes a derived fact type role
|
1094
|
+
trace :orm, "skipped exclusion constraint #{id}, missing role sequence"
|
1095
|
+
next
|
1096
|
+
end
|
1096
1097
|
|
1097
|
-
|
1098
|
+
unless make_queries('exclusion', name+(x_mandatory ? '/'+x_mandatory['Name'] : ''), role_sequences)
|
1099
|
+
trace :orm, "skipped exclusion constraint #{id}, can't make_queries"
|
1100
|
+
next
|
1101
|
+
end
|
1098
1102
|
|
1099
1103
|
ec = @constellation.SetExclusionConstraint(id_of(x))
|
1100
1104
|
ec.vocabulary = @vocabulary
|
@@ -5,6 +5,13 @@ require 'digest/sha1'
|
|
5
5
|
|
6
6
|
module ActiveFacts
|
7
7
|
module Persistence
|
8
|
+
def self.rails_name_trunc name
|
9
|
+
if name.length > 63
|
10
|
+
hash = Digest::SHA1.hexdigest name
|
11
|
+
name = name[0, 53] + '__' + hash[0, 8]
|
12
|
+
end
|
13
|
+
name
|
14
|
+
end
|
8
15
|
|
9
16
|
def self.rails_plural_name name
|
10
17
|
# Crunch spaces and pluralise the first part, all in snake_case
|
@@ -87,11 +94,7 @@ module ActiveFacts
|
|
87
94
|
def rails_name
|
88
95
|
column_names = columns.map{|c| c.rails_name }
|
89
96
|
index_name = "index_#{on.rails_name+'_on_'+column_names*'_'}"
|
90
|
-
|
91
|
-
hash = Digest::SHA1.hexdigest index_name
|
92
|
-
index_name = index_name[0, 53] + '__' + hash[0, 8]
|
93
|
-
end
|
94
|
-
index_name
|
97
|
+
Persistence.rails_name_trunc index_name
|
95
98
|
end
|
96
99
|
end
|
97
100
|
|
@@ -304,7 +304,8 @@ module ActiveFacts
|
|
304
304
|
when :supertype # A subtype absorbs a reference to its supertype when separate, or all when partitioned
|
305
305
|
# REVISIT: Or when partitioned
|
306
306
|
raise hell unless role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
|
307
|
-
|
307
|
+
counterpart_role = (role.fact_type.all_role.to_a-[role])[0]
|
308
|
+
if role.fact_type.assimilation or counterpart_role.object_type.is_independent # assimilation == 'separate' or assimilation == 'partitioned'
|
308
309
|
trace :references, "supertype #{name} doesn't absorb a reference to separate subtype #{role.fact_type.subtype.name}"
|
309
310
|
else
|
310
311
|
r = ActiveFacts::Persistence::Reference.new(self, role)
|
@@ -314,7 +315,7 @@ module ActiveFacts
|
|
314
315
|
end
|
315
316
|
|
316
317
|
when :subtype # This object is a supertype, which can absorb the subtype unless that's independent
|
317
|
-
if role.fact_type.assimilation
|
318
|
+
if role.fact_type.assimilation or is_independent
|
318
319
|
ActiveFacts::Persistence::Reference.new(self, role).tabulate
|
319
320
|
# If partitioned, the supertype is absorbed into *each* subtype; a reference to the supertype needs to know which
|
320
321
|
else
|
data/lib/activefacts/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activefacts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Clifford Heath
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06-
|
11
|
+
date: 2015-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activefacts-api
|