activefacts-generators 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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +30 -0
- data/Rakefile +6 -0
- data/activefacts-generators.gemspec +26 -0
- data/lib/activefacts/dependency_analyser.rb +182 -0
- data/lib/activefacts/generators/absorption.rb +71 -0
- data/lib/activefacts/generators/composition.rb +119 -0
- data/lib/activefacts/generators/cql.rb +715 -0
- data/lib/activefacts/generators/diagrams/json.rb +340 -0
- data/lib/activefacts/generators/help.rb +64 -0
- data/lib/activefacts/generators/helpers/inject.rb +16 -0
- data/lib/activefacts/generators/helpers/oo.rb +162 -0
- data/lib/activefacts/generators/helpers/ordered.rb +605 -0
- data/lib/activefacts/generators/helpers/rails.rb +57 -0
- data/lib/activefacts/generators/html/glossary.rb +462 -0
- data/lib/activefacts/generators/metadata/json.rb +204 -0
- data/lib/activefacts/generators/null.rb +32 -0
- data/lib/activefacts/generators/rails/models.rb +247 -0
- data/lib/activefacts/generators/rails/schema.rb +217 -0
- data/lib/activefacts/generators/ruby.rb +134 -0
- data/lib/activefacts/generators/sql/mysql.rb +281 -0
- data/lib/activefacts/generators/sql/server.rb +274 -0
- data/lib/activefacts/generators/stats.rb +70 -0
- data/lib/activefacts/generators/text.rb +29 -0
- data/lib/activefacts/generators/traits/datavault.rb +241 -0
- data/lib/activefacts/generators/traits/oo.rb +73 -0
- data/lib/activefacts/generators/traits/ordered.rb +33 -0
- data/lib/activefacts/generators/traits/ruby.rb +210 -0
- data/lib/activefacts/generators/transform/datavault.rb +303 -0
- data/lib/activefacts/generators/transform/surrogate.rb +215 -0
- data/lib/activefacts/registry.rb +11 -0
- metadata +176 -0
@@ -0,0 +1,605 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts Generators.
|
3
|
+
# Generation support superclass that sequences entity types to avoid forward references.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
7
|
+
require 'activefacts/api'
|
8
|
+
require 'activefacts/generators/helpers/inject'
|
9
|
+
require 'activefacts/generators/traits/ordered'
|
10
|
+
|
11
|
+
module ActiveFacts
|
12
|
+
module Generators #:nodoc:
|
13
|
+
module Helpers #:nodoc:
|
14
|
+
class OrderedDumper #:nodoc:
|
15
|
+
# Base class for generators of object-oriented class libraries for an ActiveFacts vocabulary.
|
16
|
+
def initialize(vocabulary, *options)
|
17
|
+
@vocabulary = vocabulary
|
18
|
+
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
19
|
+
options.each{|option| set_option(option) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_option(option)
|
23
|
+
end
|
24
|
+
|
25
|
+
def puts(*a)
|
26
|
+
@out.puts *a
|
27
|
+
end
|
28
|
+
|
29
|
+
def print(*a)
|
30
|
+
@out.print *a
|
31
|
+
end
|
32
|
+
|
33
|
+
def generate(out = $>)
|
34
|
+
@out = out
|
35
|
+
vocabulary_start
|
36
|
+
units_dump
|
37
|
+
value_types_dump
|
38
|
+
entity_types_dump
|
39
|
+
fact_types_dump
|
40
|
+
constraints_dump
|
41
|
+
vocabulary_end
|
42
|
+
end
|
43
|
+
|
44
|
+
def units_dump
|
45
|
+
done_banner = false
|
46
|
+
units = @vocabulary.all_unit.to_a.sort_by{|u| u.name.gsub(/ /,'')}
|
47
|
+
while units.size > 0
|
48
|
+
i = 0
|
49
|
+
while i < units.size
|
50
|
+
unit = units[i]
|
51
|
+
i += 1
|
52
|
+
|
53
|
+
# Skip this one if the precursors haven't yet been dumped:
|
54
|
+
next if unit.all_derivation_as_derived_unit.detect{|d| units.include?(d.base_unit) }
|
55
|
+
|
56
|
+
# Even if we skip, we're done with this unit
|
57
|
+
units.delete(unit)
|
58
|
+
i -= 1
|
59
|
+
|
60
|
+
# Skip value-type derived units
|
61
|
+
next if unit.name =~ /\^/
|
62
|
+
|
63
|
+
if !done_banner
|
64
|
+
done_banner = true
|
65
|
+
units_banner
|
66
|
+
end
|
67
|
+
unit_dump(unit)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
units_end if done_banner
|
71
|
+
end
|
72
|
+
|
73
|
+
def value_type_fork(o)
|
74
|
+
if o.name == "_ImplicitBooleanValueType"
|
75
|
+
# do nothing
|
76
|
+
elsif
|
77
|
+
!o.supertype # No supertype, i.e. a base type
|
78
|
+
o.all_role.size == 0 && # No roles
|
79
|
+
!o.is_independent && # not independent
|
80
|
+
!o.value_constraint && # No value constraints
|
81
|
+
o.concept.all_context_note_as_relevant_concept.size == 0 && # No context notes
|
82
|
+
o.all_instance.size == 0 # No instances
|
83
|
+
data_type_dump(o)
|
84
|
+
else
|
85
|
+
super_type_name = o.supertype ? o.supertype.name : o.name
|
86
|
+
length = (l = o.length) && l > 0 ? "#{l}" : nil
|
87
|
+
scale = (s = o.scale) && s > 0 ? "#{s}" : nil
|
88
|
+
facets = { :length => length, :scale => scale }
|
89
|
+
value_type_dump(o, super_type_name, facets)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def value_types_dump
|
94
|
+
done_banner = false
|
95
|
+
@vocabulary.all_object_type.sort_by{|o| o.name.gsub(/ /,'')}.each{|o|
|
96
|
+
next unless o.is_a?(ActiveFacts::Metamodel::ValueType)
|
97
|
+
|
98
|
+
value_type_banner unless done_banner
|
99
|
+
done_banner = true
|
100
|
+
|
101
|
+
value_type_chain_dump(o)
|
102
|
+
# @object_types_dumped[o] = true
|
103
|
+
o.ordered_dumped!
|
104
|
+
}
|
105
|
+
value_type_end if done_banner
|
106
|
+
end
|
107
|
+
|
108
|
+
# Ensure that supertype gets dumped first
|
109
|
+
def value_type_chain_dump(o)
|
110
|
+
return if o.ordered_dumped
|
111
|
+
value_type_chain_dump(o.supertype) if (o.supertype && !o.supertype.ordered_dumped)
|
112
|
+
value_type_fork(o)
|
113
|
+
o.ordered_dumped!
|
114
|
+
end
|
115
|
+
|
116
|
+
# Try to dump entity types in order of name, but we need
|
117
|
+
# to dump ETs before they're referenced in preferred ids
|
118
|
+
# if possible (it's not always, there may be loops!)
|
119
|
+
def entity_types_dump
|
120
|
+
# Build hash tables of precursors and followers to use:
|
121
|
+
@precursors, @followers = *build_entity_dependencies
|
122
|
+
|
123
|
+
done_banner = false
|
124
|
+
sorted = @vocabulary.all_object_type.select{|o|
|
125
|
+
o.is_a?(ActiveFacts::Metamodel::EntityType) # and !o.fact_type
|
126
|
+
}.sort_by{|o| o.name.gsub(/ /,'')}
|
127
|
+
panic = nil
|
128
|
+
while true do
|
129
|
+
count_this_pass = 0
|
130
|
+
skipped_this_pass = 0
|
131
|
+
sorted.each{|o|
|
132
|
+
next if o.ordered_dumped # Already done
|
133
|
+
|
134
|
+
trace :ordered, "Panicing to dump #{panic.name}" if panic
|
135
|
+
# Can we do this yet?
|
136
|
+
remaining_precursors = Array(@precursors[o])-[o]
|
137
|
+
if (o != panic and # We don't *have* to do it (panic mode)
|
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
|
140
|
+
skipped_this_pass += 1
|
141
|
+
next
|
142
|
+
end
|
143
|
+
trace :ordered, "Dumping #{o.name} in panic mode, even though it still needs #{remaining_precursors.map(&:name)*', '}" if panic
|
144
|
+
|
145
|
+
entity_type_banner unless done_banner
|
146
|
+
done_banner = true
|
147
|
+
|
148
|
+
# We're going to emit o - remove it from precursors of others:
|
149
|
+
(@followers[o]||[]).each{|f|
|
150
|
+
@precursors[f] -= [o]
|
151
|
+
}
|
152
|
+
count_this_pass += 1
|
153
|
+
panic = nil
|
154
|
+
|
155
|
+
if (o.fact_type)
|
156
|
+
fact_type_dump_with_dependents(o.fact_type)
|
157
|
+
released_fact_types_dump(o)
|
158
|
+
else
|
159
|
+
entity_type_dump(o)
|
160
|
+
released_fact_types_dump(o)
|
161
|
+
end
|
162
|
+
|
163
|
+
entity_type_group_end
|
164
|
+
}
|
165
|
+
|
166
|
+
# Check that we made progress if there's any to make:
|
167
|
+
if count_this_pass == 0 && skipped_this_pass > 0
|
168
|
+
=begin
|
169
|
+
if panic # We were already panicing... what to do now?
|
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
|
+
|
178
|
+
raise "Unresolvable cycle of forward references: " +
|
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
|
191
|
+
# Find the object that has the most followers and no fwd-ref'd supertypes:
|
192
|
+
# This selection might be better if we allow PI roles to be fwd-ref'd...
|
193
|
+
panic = sorted.
|
194
|
+
select{|o| !o.ordered_dumped }.
|
195
|
+
sort_by{|o|
|
196
|
+
f = (@followers[o] || []) - [o];
|
197
|
+
o.supertypes.detect{|s| !s.ordered_dumped } ? 0 : -f.size
|
198
|
+
}[0]
|
199
|
+
trace :ordered, "Panic mode, selected #{panic.name} next"
|
200
|
+
# end
|
201
|
+
end
|
202
|
+
|
203
|
+
break if skipped_this_pass == 0 # All done.
|
204
|
+
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def identified_by(o, pi)
|
209
|
+
# Different adjectives might be used for different readings.
|
210
|
+
# Here, we must find the role_ref containing the adjectives that we need for each identifier,
|
211
|
+
# which will be attached to the uniqueness constraint on this object in the binary FT that
|
212
|
+
# attaches that identifying role.
|
213
|
+
identifying_role_refs = pi.role_sequence.all_role_ref.sort_by{|role_ref| role_ref.ordinal}
|
214
|
+
|
215
|
+
# We need to get the adjectives for the roles from the identifying fact's preferred readings:
|
216
|
+
identifying_facts = ([o.fact_type]+identifying_role_refs.map{|rr| rr.role.fact_type }).compact.uniq
|
217
|
+
|
218
|
+
identification = identified_by_roles_and_facts(o, identifying_role_refs, identifying_facts)
|
219
|
+
|
220
|
+
identification
|
221
|
+
end
|
222
|
+
|
223
|
+
def describe_fact_type(fact_type, highlight = nil)
|
224
|
+
(fact_type.entity_type ? fact_type.entity_type.name : "")+
|
225
|
+
describe_roles(fact_type.all_role, highlight)
|
226
|
+
end
|
227
|
+
|
228
|
+
def describe_roles(roles, highlight = nil)
|
229
|
+
"("+
|
230
|
+
roles.map{|role| role.object_type.name + (role == highlight ? "*" : "")}*", "+
|
231
|
+
")"
|
232
|
+
end
|
233
|
+
|
234
|
+
def describe_role_sequence(role_sequence)
|
235
|
+
"("+
|
236
|
+
role_sequence.all_role_ref.map{|role_ref| role_ref.role.object_type.name }*", "+
|
237
|
+
")"
|
238
|
+
end
|
239
|
+
|
240
|
+
# This returns an array of two hash tables each keyed by an EntityType.
|
241
|
+
# The values of each hash entry are the precursors and followers (respectively) of that entity.
|
242
|
+
def build_entity_dependencies
|
243
|
+
@vocabulary.all_object_type.inject([{},{}]) { |a, o|
|
244
|
+
if o.is_a?(ActiveFacts::Metamodel::EntityType)
|
245
|
+
precursor = a[0]
|
246
|
+
follower = a[1]
|
247
|
+
blocked = false
|
248
|
+
pi = o.preferred_identifier
|
249
|
+
if pi
|
250
|
+
pi.role_sequence.all_role_ref.each{|rr|
|
251
|
+
role = rr.role
|
252
|
+
player = role.object_type
|
253
|
+
# REVISIT: If we decide to emit value types on demand, need to remove this:
|
254
|
+
next unless player.is_a?(ActiveFacts::Metamodel::EntityType)
|
255
|
+
# player is a precursor of o
|
256
|
+
(precursor[o] ||= []) << player if (player != o)
|
257
|
+
(follower[player] ||= []) << o if (player != o)
|
258
|
+
}
|
259
|
+
end
|
260
|
+
if o.fact_type
|
261
|
+
o.fact_type.all_role.each do |role|
|
262
|
+
next unless role.object_type.is_a?(ActiveFacts::Metamodel::EntityType)
|
263
|
+
(precursor[o] ||= []) << role.object_type
|
264
|
+
(follower[role.object_type] ||= []) << o
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Supertypes are precursors too:
|
269
|
+
subtyping = o.all_type_inheritance_as_supertype
|
270
|
+
next a if subtyping.size == 0
|
271
|
+
subtyping.each{|ti|
|
272
|
+
# debug ti.class.roles.verbalise; trace "all_type_inheritance_as_supertype"; exit
|
273
|
+
s = ti.subtype
|
274
|
+
(precursor[s] ||= []) << o
|
275
|
+
(follower[o] ||= []) << s
|
276
|
+
}
|
277
|
+
# REVISIT: Need to use this to order ValueTypes after their supertypes
|
278
|
+
# else
|
279
|
+
# o.all_value_type_as_supertype.each { |s|
|
280
|
+
# (precursor[s] ||= []) << o
|
281
|
+
# (follower[o] ||= []) << s
|
282
|
+
# }
|
283
|
+
end
|
284
|
+
a
|
285
|
+
}
|
286
|
+
end
|
287
|
+
|
288
|
+
# Dump all fact types for which all precursors (of which "o" is one) have been emitted:
|
289
|
+
def released_fact_types_dump(o)
|
290
|
+
roles = o.all_role
|
291
|
+
begin
|
292
|
+
progress = false
|
293
|
+
roles.map(&:fact_type).uniq.select{|fact_type|
|
294
|
+
# The fact type hasn't already been dumped but all its role players have
|
295
|
+
!fact_type.ordered_dumped &&
|
296
|
+
!fact_type.is_a?(ActiveFacts::Metamodel::LinkFactType) &&
|
297
|
+
!fact_type.all_role.detect{|r| !r.object_type.ordered_dumped } &&
|
298
|
+
!fact_type.entity_type &&
|
299
|
+
derivation_precursors_complete(fact_type)
|
300
|
+
# REVISIT: A derived fact type must not be dumped before its dependent fact types have
|
301
|
+
}.sort_by{|fact_type|
|
302
|
+
fact_type_key(fact_type)
|
303
|
+
}.each{|fact_type|
|
304
|
+
fact_type_dump_with_dependents(fact_type)
|
305
|
+
# Objectified Fact Types may release additional fact types
|
306
|
+
roles += fact_type.entity_type.all_role.sort_by{|role| role.ordinal} if fact_type.entity_type
|
307
|
+
progress = true
|
308
|
+
}
|
309
|
+
end while progress
|
310
|
+
end
|
311
|
+
|
312
|
+
def derivation_precursors_complete(fact_type)
|
313
|
+
pr = fact_type.preferred_reading
|
314
|
+
return true unless jr = pr.role_sequence.all_role_ref.to_a[0].play
|
315
|
+
query = jr.variable.query
|
316
|
+
return false if query.all_step.detect{|js| !js.fact_type.ordered_dumped }
|
317
|
+
return false if query.all_variable.detect{|jn| !jn.object_type.ordered_dumped }
|
318
|
+
true
|
319
|
+
end
|
320
|
+
|
321
|
+
def skip_fact_type(f)
|
322
|
+
return true if f.is_a?(ActiveFacts::Metamodel::TypeInheritance)
|
323
|
+
return false if f.entity_type && !f.entity_type.ordered_dumped
|
324
|
+
|
325
|
+
# REVISIT: There might be constraints we have to merge into the nested entity or subtype.
|
326
|
+
# These will come up as un-handled constraints:
|
327
|
+
# Dump this fact type only if it contains a presence constraint we've missed:
|
328
|
+
pcs = @presence_constraints_by_fact[f]
|
329
|
+
pcs && pcs.size > 0 && !pcs.detect{|c| !c.ordered_dumped }
|
330
|
+
end
|
331
|
+
|
332
|
+
# Dump one fact type.
|
333
|
+
# Include as many as possible internal constraints in the fact type readings.
|
334
|
+
def fact_type_dump_with_dependents(fact_type)
|
335
|
+
fact_type.ordered_dumped!
|
336
|
+
return if skip_fact_type(fact_type)
|
337
|
+
|
338
|
+
if (et = fact_type.entity_type) &&
|
339
|
+
(pi = et.preferred_identifier) &&
|
340
|
+
pi.role_sequence.all_role_ref.detect{|rr| rr.role.fact_type != fact_type }
|
341
|
+
# trace "Dumping objectified FT #{et.name} as an entity, non-fact PI"
|
342
|
+
entity_type_dump(et)
|
343
|
+
released_fact_types_dump(et)
|
344
|
+
return
|
345
|
+
end
|
346
|
+
|
347
|
+
# trace "#{fact_type.name} has readings:\n\t#{fact_type.readings.map(&:name)*"\n\t"}"
|
348
|
+
# trace "Dumping #{fact_type.concept.guid} as a fact type"
|
349
|
+
|
350
|
+
# Fact types that aren't nested have no names
|
351
|
+
name = fact_type.entity_type && fact_type.entity_type.name
|
352
|
+
|
353
|
+
fact_type_dump(fact_type, name)
|
354
|
+
|
355
|
+
# REVISIT: Go through the residual constraints and re-process appropriate readings to show them
|
356
|
+
|
357
|
+
#CJH: Necessary?
|
358
|
+
fact_type.ordered_dumped!
|
359
|
+
fact_type.entity_type.ordered_dumped! if fact_type.entity_type
|
360
|
+
end
|
361
|
+
|
362
|
+
# Dump fact types.
|
363
|
+
def fact_types_dump
|
364
|
+
# REVISIT: Uniqueness on the LHS of a binary can be coded using "distinct"
|
365
|
+
|
366
|
+
# The only fact types that can be remaining are those involving only value types,
|
367
|
+
# since we dumped every fact type as soon as all relevant entities were dumped.
|
368
|
+
# Iterate over all fact types of all value types, looking for these strays.
|
369
|
+
|
370
|
+
done_banner = false
|
371
|
+
fact_collection = @vocabulary.constellation.FactType
|
372
|
+
fact_collection.keys.select{|fact_id|
|
373
|
+
fact_type = fact_collection[fact_id] and
|
374
|
+
!fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) and
|
375
|
+
!fact_type.is_a?(ActiveFacts::Metamodel::LinkFactType) and
|
376
|
+
!fact_type.ordered_dumped and
|
377
|
+
!skip_fact_type(fact_type) and
|
378
|
+
!fact_type.all_role.detect{|r| r.object_type.is_a?(ActiveFacts::Metamodel::EntityType) }
|
379
|
+
}.sort_by{|fact_id|
|
380
|
+
fact_type = fact_collection[fact_id]
|
381
|
+
fact_type_key(fact_type)
|
382
|
+
}.each{|fact_id|
|
383
|
+
fact_type = fact_collection[fact_id]
|
384
|
+
|
385
|
+
fact_type_banner unless done_banner
|
386
|
+
done_banner = true
|
387
|
+
fact_type_dump_with_dependents(fact_type)
|
388
|
+
}
|
389
|
+
|
390
|
+
# REVISIT: Find out why some fact types are missed during entity dumping:
|
391
|
+
@vocabulary.constellation.FactType.values.select{|fact_type|
|
392
|
+
!fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) &&
|
393
|
+
!fact_type.is_a?(ActiveFacts::Metamodel::LinkFactType)
|
394
|
+
}.sort_by{|fact_type|
|
395
|
+
fact_type_key(fact_type)
|
396
|
+
}.each{|fact_type|
|
397
|
+
next if fact_type.ordered_dumped
|
398
|
+
# trace "Not dumped #{fact_type.verbalise}(#{fact_type.all_role.map{|r| r.object_type.name}*", "})"
|
399
|
+
fact_type_banner unless done_banner
|
400
|
+
done_banner = true
|
401
|
+
fact_type_dump_with_dependents(fact_type)
|
402
|
+
}
|
403
|
+
|
404
|
+
fact_type_end if done_banner
|
405
|
+
end
|
406
|
+
|
407
|
+
def fact_instances_dump
|
408
|
+
@vocabulary.fact_types.each{|f|
|
409
|
+
# Dump the instances:
|
410
|
+
f.facts.each{|i|
|
411
|
+
raise "REVISIT: Not dumping fact instances"
|
412
|
+
trace "\t\t"+i.to_s
|
413
|
+
}
|
414
|
+
}
|
415
|
+
end
|
416
|
+
|
417
|
+
# Arrange for objectified fact types to appear in order of name, after other fact types.
|
418
|
+
# Facts are ordered alphabetically by the names of their role players,
|
419
|
+
# then by preferred_reading (subtyping fact types have no preferred_reading).
|
420
|
+
def fact_type_key(fact_type)
|
421
|
+
role_names =
|
422
|
+
if (pr = fact_type.preferred_reading)
|
423
|
+
pr.role_sequence.
|
424
|
+
all_role_ref.
|
425
|
+
sort_by{|role_ref| role_ref.ordinal}.
|
426
|
+
map{|role_ref| [ role_ref.leading_adjective, role_ref.role.object_type.name, role_ref.trailing_adjective ].compact*"-" } +
|
427
|
+
[pr.text]
|
428
|
+
else
|
429
|
+
fact_type.all_role.map{|role| role.object_type.name }
|
430
|
+
end
|
431
|
+
|
432
|
+
(fact_type.entity_type ? [fact_type.entity_type.name] : [""]) + role_names
|
433
|
+
end
|
434
|
+
|
435
|
+
def role_ref_key(role_ref)
|
436
|
+
[ role_ref.leading_adjective, role_ref.role.object_type.name, role_ref.trailing_adjective ].compact*"-" +
|
437
|
+
" in " +
|
438
|
+
role_ref.role.fact_type.preferred_reading.expand
|
439
|
+
end
|
440
|
+
|
441
|
+
def constraint_sort_key(c)
|
442
|
+
case c
|
443
|
+
when ActiveFacts::Metamodel::RingConstraint
|
444
|
+
[ 1,
|
445
|
+
c.ring_type,
|
446
|
+
c.role.object_type.name,
|
447
|
+
c.other_role.object_type.name,
|
448
|
+
c.name||""
|
449
|
+
]
|
450
|
+
when ActiveFacts::Metamodel::SetExclusionConstraint
|
451
|
+
[ 2+(c.is_mandatory ? 0 : 1),
|
452
|
+
c.all_set_comparison_roles.map{|scrs|
|
453
|
+
scrs.role_sequence.all_role_ref.map{|rr|
|
454
|
+
role_ref_key(rr)
|
455
|
+
}
|
456
|
+
},
|
457
|
+
c.name||""
|
458
|
+
]
|
459
|
+
when ActiveFacts::Metamodel::SetEqualityConstraint
|
460
|
+
[ 4,
|
461
|
+
c.all_set_comparison_roles.map{|scrs|
|
462
|
+
scrs.role_sequence.all_role_ref.map{|rr|
|
463
|
+
role_ref_key(rr)
|
464
|
+
}
|
465
|
+
},
|
466
|
+
c.name||""
|
467
|
+
]
|
468
|
+
when ActiveFacts::Metamodel::SubsetConstraint
|
469
|
+
[ 5,
|
470
|
+
[c.superset_role_sequence, c.subset_role_sequence].map{|rs|
|
471
|
+
rs.all_role_ref.map{|rr|
|
472
|
+
role_ref_key(rr)
|
473
|
+
}
|
474
|
+
},
|
475
|
+
c.name||""
|
476
|
+
]
|
477
|
+
when ActiveFacts::Metamodel::PresenceConstraint
|
478
|
+
[ 6,
|
479
|
+
c.role_sequence.all_role_ref.map{|rr|
|
480
|
+
role_ref_key(rr)
|
481
|
+
},
|
482
|
+
c.name||""
|
483
|
+
]
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
def constraints_dump
|
488
|
+
heading = false
|
489
|
+
@vocabulary.
|
490
|
+
all_constraint.
|
491
|
+
reject{|c| c.ordered_dumped}.
|
492
|
+
sort_by{ |c| constraint_sort_key(c) }.
|
493
|
+
each do |c|
|
494
|
+
# Skip some PresenceConstraints:
|
495
|
+
if c.is_a?(ActiveFacts::Metamodel::PresenceConstraint)
|
496
|
+
# Skip uniqueness constraints that cover all roles of a fact type, they're implicit
|
497
|
+
fact_types = c.role_sequence.all_role_ref.map{|rr| rr.role.fact_type}.uniq
|
498
|
+
if fact_types.size == 1 &&
|
499
|
+
!c.role_sequence.all_role_ref.detect{|rr| rr.play } &&
|
500
|
+
c.max_frequency == 1 && # Uniqueness
|
501
|
+
fact_types[0].all_role.size == c.role_sequence.all_role_ref.size
|
502
|
+
next
|
503
|
+
end
|
504
|
+
|
505
|
+
# Skip internal PresenceConstraints over TypeInheritances:
|
506
|
+
next if c.role_sequence.all_role_ref.size == 1 &&
|
507
|
+
fact_types[0].is_a?(ActiveFacts::Metamodel::TypeInheritance)
|
508
|
+
end
|
509
|
+
|
510
|
+
constraint_banner unless heading
|
511
|
+
heading = true
|
512
|
+
|
513
|
+
# Skip presence constraints on value types:
|
514
|
+
# next if ActiveFacts::PresenceConstraint === c &&
|
515
|
+
# ActiveFacts::ValueType === c.object_type
|
516
|
+
constraint_dump(c)
|
517
|
+
end
|
518
|
+
constraint_end if heading
|
519
|
+
end
|
520
|
+
|
521
|
+
def vocabulary_start
|
522
|
+
trace "Should override vocabulary_start"
|
523
|
+
end
|
524
|
+
|
525
|
+
def vocabulary_end
|
526
|
+
trace "Should override vocabulary_end"
|
527
|
+
end
|
528
|
+
|
529
|
+
def units_banner
|
530
|
+
end
|
531
|
+
|
532
|
+
def units_end
|
533
|
+
end
|
534
|
+
|
535
|
+
def unit_dump unit
|
536
|
+
end
|
537
|
+
|
538
|
+
def value_type_banner
|
539
|
+
trace "Should override value_type_banner"
|
540
|
+
end
|
541
|
+
|
542
|
+
def value_type_end
|
543
|
+
trace "Should override value_type_end"
|
544
|
+
end
|
545
|
+
|
546
|
+
def data_type_dump(o)
|
547
|
+
trace "Should override data_type_dump"
|
548
|
+
end
|
549
|
+
|
550
|
+
def value_type_dump(o, super_type_name, facets)
|
551
|
+
trace "Should override value_type_dump"
|
552
|
+
end
|
553
|
+
|
554
|
+
def entity_type_banner
|
555
|
+
trace "Should override entity_type_banner"
|
556
|
+
end
|
557
|
+
|
558
|
+
def entity_type_group_end
|
559
|
+
trace "Should override entity_type_group_end"
|
560
|
+
end
|
561
|
+
|
562
|
+
def non_subtype_dump(o, pi)
|
563
|
+
trace "Should override non_subtype_dump"
|
564
|
+
end
|
565
|
+
|
566
|
+
def subtype_dump(o, supertypes, pi = nil)
|
567
|
+
trace "Should override subtype_dump"
|
568
|
+
end
|
569
|
+
|
570
|
+
def append_ring_to_reading(reading, ring)
|
571
|
+
trace "Should override append_ring_to_reading"
|
572
|
+
end
|
573
|
+
|
574
|
+
def fact_type_banner
|
575
|
+
trace "Should override fact_type_banner"
|
576
|
+
end
|
577
|
+
|
578
|
+
def fact_type_end
|
579
|
+
trace "Should override fact_type_end"
|
580
|
+
end
|
581
|
+
|
582
|
+
def fact_type_dump(fact_type, name)
|
583
|
+
trace "Should override fact_type_dump"
|
584
|
+
end
|
585
|
+
|
586
|
+
def constraint_banner
|
587
|
+
trace "Should override constraint_banner"
|
588
|
+
end
|
589
|
+
|
590
|
+
def constraint_end
|
591
|
+
trace "Should override constraint_end"
|
592
|
+
end
|
593
|
+
|
594
|
+
def constraint_dump(c)
|
595
|
+
trace "Should override constraint_dump"
|
596
|
+
end
|
597
|
+
|
598
|
+
end
|
599
|
+
|
600
|
+
def dump(vocabulary, out = $>)
|
601
|
+
OrderedDumper.new(vocabulary).dump(out)
|
602
|
+
end
|
603
|
+
end
|
604
|
+
end
|
605
|
+
end
|