activefacts-compositions 1.9.22 → 1.9.23

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +1 -1
  3. data/activefacts-compositions.gemspec +1 -1
  4. data/bin/schema_compositor +71 -27
  5. data/lib/activefacts/compositions/binary.rb +4 -0
  6. data/lib/activefacts/compositions/datavault.rb +4 -0
  7. data/lib/activefacts/compositions/relational.rb +4 -0
  8. data/lib/activefacts/compositions/staging.rb +4 -0
  9. data/lib/activefacts/compositions/version.rb +1 -1
  10. data/lib/activefacts/generator/doc/css/glossary-print.css +72 -0
  11. data/lib/activefacts/generator/doc/css/glossary.css +194 -0
  12. data/lib/activefacts/generator/doc/css/ldm.css +12 -17
  13. data/lib/activefacts/generator/doc/css/orm2-print.css +19 -0
  14. data/lib/activefacts/generator/doc/css/orm2.css +28 -0
  15. data/lib/activefacts/generator/doc/css/reset.css +18 -0
  16. data/lib/activefacts/generator/doc/css/treetable.css +83 -0
  17. data/lib/activefacts/generator/doc/cwm.rb +60 -54
  18. data/lib/activefacts/generator/doc/glossary.rb +261 -137
  19. data/lib/activefacts/generator/doc/graphviz.rb +6 -2
  20. data/lib/activefacts/generator/doc/ldm.rb +7 -3
  21. data/lib/activefacts/generator/etl/unidex.rb +7 -2
  22. data/lib/activefacts/generator/oo.rb +2 -1
  23. data/lib/activefacts/generator/population.rb +174 -0
  24. data/lib/activefacts/generator/rails/active_admin.rb +81 -0
  25. data/lib/activefacts/generator/rails/application_record_shell.rb +78 -0
  26. data/lib/activefacts/generator/rails/models.rb +31 -72
  27. data/lib/activefacts/generator/rails/ruby_folder_generator.rb +87 -0
  28. data/lib/activefacts/generator/rails/schema.rb +12 -4
  29. data/lib/activefacts/generator/ruby.rb +7 -3
  30. data/lib/activefacts/generator/sql.rb +2 -1
  31. data/lib/activefacts/generator/summary.rb +24 -19
  32. data/lib/activefacts/generator/traits/sql.rb +4 -0
  33. data/lib/activefacts/generator/transgen.rb +7 -1
  34. data/lib/activefacts/generator/validate.rb +10 -2
  35. metadata +15 -5
@@ -13,19 +13,24 @@ module ActiveFacts
13
13
  module Generators #:nodoc:
14
14
  module Doc
15
15
  class Glossary #:nodoc:
16
+ MM = ActiveFacts::Metamodel
17
+
16
18
  # Options are comma or space separated:
17
- # * gen_bootstrap Generate bootstrap styled glossary html
18
19
  def self.options
19
20
  {
20
- gen_bootstrap: ['Boolean', "Generate bootstrap styled glossary html"],
21
21
  }
22
22
  end
23
-
23
+
24
+ def self.compatibility
25
+ [0, nil] # no composition is required
26
+ end
27
+
24
28
  # Base class for generators of object-oriented class libraries for an ActiveFacts vocabulary.
25
- def initialize vocabulary, options = {}
26
- @vocabulary = vocabulary # REVISIT: This should be a Composition here
29
+ def initialize constellation, composition, options = {}
30
+ @constellation = constellation
31
+ @compositions = Array(composition)
32
+ @vocabulary = constellation.Vocabulary.values[0]
27
33
  @options = options
28
- @gen_bootstrap = options.has_key?("gen_bootstrap")
29
34
  end
30
35
 
31
36
  def puts(*a)
@@ -41,121 +46,103 @@ module ActiveFacts
41
46
  @vocabulary.
42
47
  all_object_type.
43
48
  sort_by{|o| o.name.gsub(/ /,'').downcase}
44
-
49
+
50
+ "<html><head>" +
45
51
  glossary_start +
52
+ "</head><body>" +
46
53
  glossary_body +
47
- glossary_end
54
+ glossary_end +
55
+ "</body>"
48
56
  end
49
57
 
50
58
  def glossary_start
51
- if !@gen_bootstrap
52
- # puts "<link rel='stylesheet' href='css/orm2.css' media='screen' type='text/css'/>"
53
- css_file = "css/orm2.css"
54
-
55
- File.open(File.dirname(__FILE__)+css_file) do |f|
56
- "<style media='screen' type='text/css'>\n" +
57
- f.read +
58
- %Q{
59
- .glossary-facttype, .glossary-constraints { display: block; }\n
60
- .glossary-doc.hide-alternates .glossary-alternates { display: none; }\n
61
- .glossary-doc.hide-constraints .glossary-constraints { display: none; }\n
62
- .glossary-doc.hide-examples .glossary-example { display: none; }\n
63
- }.gsub(/^\s+/, '') +
64
- "</style>\n"
65
- end +
66
-
67
- %Q{
68
- <style media='print' type='text/css'>\n
69
- .keyword { color: #0000CC; font-style: italic; display: inline; }\n
70
- .vocabulary, .object_type { color: #8A0092; font-weight: bold; }\n
71
- .copula { color: #0E5400; }\n
72
- .value { color: #FF990E; display: inline; }\n
73
- .glossary-toc { display: none; }\n
74
- .glossary-facttype, .glossary-reading { display: inline; }\n
75
- </style>\n
76
- }.gsub(/^\s+/, '')
77
- else
78
- ''
79
- end
59
+ # Inline the following CSS files:
60
+ {
61
+ all: ["reset.css", "treetable.css"],
62
+ screen: ["orm2.css", "glossary.css"],
63
+ print: ["orm2-print.css", "glossary-print.css"]
64
+ }.
65
+ flat_map do |media, css_files|
66
+ css_files.map do |css_file|
67
+ File.open(filename = File.dirname(__FILE__)+"/css/"+css_file) do |f|
68
+ "<!-- #{css_file} -->\n"+
69
+ "<style media='#{media}' type='text/css'>\n#{f.read}</style>\n"
70
+ end
71
+ end
72
+ end*''.gsub(/^\s+/, '')
80
73
  end
81
74
 
82
75
  def glossary_body
83
- if @gen_bootstrap
84
- object_types_dump_toc()
85
- object_types_dump_def()
86
- else
87
- object_types_dump_def()
88
- object_types_dump_toc()
89
- end
76
+ div(
77
+ object_types_dump_toc +
78
+ object_types_dump_def +
79
+ dump_compositions +
80
+ controls,
81
+ 'glossary'
82
+ )
90
83
  end
91
-
84
+
92
85
  def glossary_end
93
- if !@gen_bootstrap
94
- %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
- else
120
- ''
121
- end
86
+ %Q{
87
+ <script type="text/javascript">
88
+ function toggle_class(e, c) {
89
+ if (!e) return;
90
+ var n = e.className;
91
+ var i = n.indexOf(c);
92
+ if (i == -1) {
93
+ e.className = n+' '+c;
94
+ } else {
95
+ e.className = n.slice(0, i)+n.slice(i+c.length);
96
+ }
97
+ if (document.location.toString().indexOf('#') >= 0)
98
+ document.location = document.location; // Re-scroll to the current fragment
99
+ }
100
+ function toggle_facts() {
101
+ toggle_class(document.getElementById('glossary-doc'), 'hide-facts');
102
+ }
103
+ function toggle_constraints() {
104
+ toggle_class(document.getElementById('glossary-doc'), 'hide-constraints');
105
+ }
106
+ function toggle_alternates() {
107
+ toggle_class(document.getElementById('glossary-doc'), 'hide-alternates');
108
+ }
109
+ function toggle_examples() {
110
+ toggle_class(document.getElementById('glossary-doc'), 'hide-examples');
111
+ }
112
+ </script>
113
+ }.gsub(/^\s+/, '')
122
114
  end
123
115
 
124
116
  def object_types_dump_toc
125
- if @gen_bootstrap
126
- '<div class="col-md-3 glossary-sidebar">' + "\n"
127
- else
128
- '<div class="glossary-sidebar">' + "\n"
129
- end +
130
- '<h1 style="visibility: hidden">X</h1>' +"\n" +
131
- '<ol class="glossary-toc">' + "\n"
117
+ %Q{\n<div class="glossary-toc#{@compositions.size > 0 ? ' glossary-is-toc' : ' glossary-toc-right'}">\n} +
118
+ # Don't show schema name here '<h1 style="visibility: hidden">X</h1>' +"\n" +
119
+ '<ol class="glossary-toc-list">' + "\n" +
132
120
  @all_object_type.
133
- reject do |o|
134
- o.name == '_ImplicitBooleanValueType' or
135
- o.kind_of?(ActiveFacts::Metamodel::ValueType) && o.all_role.size == 0 or
136
- o.kind_of?(ActiveFacts::Metamodel::TypeInheritance)
137
- end.
138
- map do |o|
139
- "<li>#{termref(o.name)}</li>"
140
- end *"\n" + "\n" +
121
+ reject do |o|
122
+ o.name == '_ImplicitBooleanValueType' or
123
+ o.kind_of?(ActiveFacts::Metamodel::ValueType) && o.all_role.size == 0 or
124
+ o.kind_of?(ActiveFacts::Metamodel::TypeInheritance)
125
+ end.
126
+ map do |o|
127
+ "<li>#{termref(o.name)}</li>"
128
+ end*"\n" + "\n</div>\n\n"
129
+ end
130
+
131
+ def controls
141
132
  %Q{
142
133
  </ol>
143
134
  <div class="glossary-controls">
135
+ <input type="button" onclick="toggle_facts()" value="Facts" class="glossary-toggle-facts">
144
136
  <input type="button" onclick="toggle_constraints()" value="Constraints" class="glossary-toggle-constraint">
145
137
  <input type="button" onclick="toggle_alternates()" value="Alternates" class="glossary-toggle-alternates">
146
138
  <input type="button" onclick="toggle_examples()" value="Examples" class="glossary-toggle-examples">
147
139
  </div>
148
- </div>
149
140
  }
150
141
  end
151
-
142
+
152
143
  def object_types_dump_def
153
- if @gen_bootstrap
154
- '<div class="col-md-5 glossary-doc hide-alternates hide-constraints" id="glossary-doc">' + "\n"
155
- else
156
- '<div class="glossary-doc hide-alternates hide-constraints" id="glossary-doc">' + "\n"
157
- end +
158
- '<h1>#{@vocabulary.name}</h1>' + "\n" +
144
+ %Q{<div class="glossary-doc #{@compositions.size > 0 ? 'glossary-is-toc' : 'glossary-toc-right'} hide-facts hide-alternates hide-constraints" id="glossary-doc">} + "\n" +
145
+ "<h1>#{@vocabulary.name}</h1>\n" +
159
146
  "<dl>\n" +
160
147
  @all_object_type.
161
148
  map do |o|
@@ -171,11 +158,146 @@ module ActiveFacts
171
158
  entity_type_dump(o)
172
159
  end
173
160
  end
174
- end +
161
+ end*"\n" +
175
162
  "</dl>\n" +
176
163
  "</div>\n"
177
164
  end
178
165
 
166
+ # Each component has
167
+ # * a span for the title,
168
+ # * a tt-type if it's a value type
169
+ # * an tt-desc if it has an associated fact type
170
+ # * child nodes
171
+ def component c, klass = ''
172
+ name = c.name
173
+ title = span(name, 'term'+(c.is_mandatory ? ' mandatory' : ''))
174
+ desc = ''
175
+ type = ''
176
+
177
+ case c
178
+ when MM::Indicator
179
+ ft = c.role.fact_type
180
+ desc = div(div(expand_reading(ft.preferred_reading, false), 'glossary-reading'), 'tt-desc')
181
+ type = div(div('boolean', 'term'), 'tt-type')
182
+
183
+ when MM::Discriminator,
184
+ MM::Injection,
185
+ MM::ComputedValue,
186
+ MM::HashValue,
187
+ MM::SurrogateKey,
188
+ # MM::Scoping,
189
+ MM::ValidFrom # This should be an Injection
190
+ p c
191
+ debugger
192
+ print ''
193
+ # REVISIT
194
+
195
+ when MM::Absorption
196
+ ft = c.parent_role.fact_type
197
+ preferred_reading = ft.reading_preferably_starting_with_role(c.parent_role)
198
+ desc = div(div(expand_reading(preferred_reading, false), 'glossary-reading'), 'tt-desc')
199
+ if MM::ValueType === c.object_type
200
+ name = c.column_name*''
201
+ title = span(name, 'term'+(c.is_mandatory ? ' mandatory' : ''))
202
+ type = div(div(c.child_role.object_type.name, 'term'), 'tt-type')
203
+ #elsif c.all_member.size == 0
204
+ # title = span(name, 'term'+(c.is_mandatory ? ' mandatory' : ''))
205
+ #else
206
+ title = span(name, 'term'+(c.is_mandatory ? ' mandatory' : ''))
207
+ end
208
+ if MM::TypeInheritance === ft
209
+ title = "as a "+title
210
+ elsif c.full_absorption
211
+ title = "fully absorbing "+title
212
+ end
213
+ if to = (c.foreign_key && c.foreign_key.composite) or
214
+ (
215
+ composite_mappings = c.object_type.all_mapping.select{|m| m.composite} and
216
+ composite_mappings.size == 1 and # In a binary mapping, there aren't any ForeignKeys
217
+ to = composite_mappings[0].composite
218
+ )
219
+ title = element(title, {href: '#'+composite_anchor(to)}, 'a')
220
+ end
221
+ klass = klass+' tt-list' unless c.parent_role.is_unique
222
+
223
+ # when MM::ValueField ... Mapping works here
224
+ when MM::Mapping # A mapping that's not an absorption; usually a Composite
225
+ if MM::EntityType === (o = c.object_type)
226
+ if o.fact_type
227
+ objectified_reading = o.fact_type.preferred_reading
228
+ desc = div(
229
+ span('is where ', :keyword) + expand_reading(objectified_reading, false),
230
+ 'tt-desc'
231
+ )
232
+ else
233
+ desc = div(
234
+ span('is identified by ', :keyword) +
235
+ o.preferred_identifier_roles.map{|r| span(r.role_name || r.name, 'term') }*', ',
236
+ 'tt-desc'
237
+ )
238
+ end
239
+ else
240
+ desc = div('', 'tt-desc')
241
+ end
242
+
243
+ when MM::Indicator
244
+ desc = div(
245
+ expand_reading(c.role.fact_type.preferred_reading, false),
246
+ 'tt-desc'
247
+ )
248
+ else
249
+ # Add other special cases here
250
+ desc = div('', 'tt-desc')
251
+ end
252
+
253
+
254
+ div(
255
+ title +
256
+ type +
257
+ desc +
258
+ c.
259
+ all_member.
260
+ sort_by{|m| m.ordinal}.
261
+ map do |member|
262
+ component(member)
263
+ end*'',
264
+ 'tt-node'+klass
265
+ )+"\n"
266
+ end
267
+
268
+ def dump_compositions
269
+ return '' if @compositions.empty?
270
+
271
+ element(
272
+ @compositions.map do |c|
273
+ "\n"+
274
+ element(
275
+ element(element(c.compositor_name + ' Composition', {href: "#{'#'}#{c.compositor_name}-composition"}, 'a'), {}, 'h2') + "\n" +
276
+ element(dump_composition(c), {}, 'div'),
277
+ {id: "#{c.compositor_name}-composition"},
278
+ 'section'
279
+ )
280
+ end*'',
281
+ {class: 'tabs glossary-compositions'},
282
+ 'article'
283
+ )
284
+ end
285
+
286
+ def composite_anchor composite
287
+ "#{composite.composition.compositor_name}_#{composite.mapping.name.words.titlecase}"
288
+ end
289
+
290
+ def dump_composition c
291
+ c.all_composite_by_name.map do |composite|
292
+ composite.mapping.re_rank
293
+ element(
294
+ component(composite.mapping, ' tt-outer'),
295
+ {name: composite_anchor(composite)},
296
+ 'a'
297
+ )
298
+ end*"&nbsp;\n"
299
+ end
300
+
179
301
  def element(text, attrs, tag = 'span')
180
302
  "<#{tag}#{attrs.empty? ? '' : attrs.map{|k,v| " #{k}='#{v}'"}*''}>#{text}</#{tag}>"
181
303
  end
@@ -198,31 +320,31 @@ module ActiveFacts
198
320
 
199
321
  # A definition of a term
200
322
  def termdef(name)
201
- element(name, {:name => name, :class => 'object_type'}, 'a')
323
+ element(name, {:name => name, :class=>:term}, 'a')
202
324
  end
203
325
 
204
326
  # A reference to a defined term (excluding role adjectives)
205
327
  def termref(name, role_name = nil)
206
328
  role_name ||= name
207
- element(role_name, {:href=>'#'+name, :class=>:object_type}, 'a')
329
+ element(role_name, {:href=>'#'+name, :class=>:term}, 'a')
208
330
  end
209
331
 
210
332
  # Text that should appear as part of a term (including role adjectives)
211
333
  def term(name)
212
- element(name, :class=>:object_type)
334
+ element(name, :class=>:term)
213
335
  end
214
336
 
215
337
  def value_type_dump(o, include_alternate = true, include_facts = true, include_constraints = true)
216
338
  return '' if o.all_role.size == 0 or # Skip value types that are only used as supertypes
217
339
  o.name == '_ImplicitBooleanValueType'
218
-
340
+
219
341
  defn_term =
220
342
  ' <dt>' +
221
343
  "#{termdef(o.name)} " +
222
344
  (if o.supertype
223
345
  span('is written as ', :keyword) + termref(o.supertype.name)
224
346
  else
225
- " (a fundamental data type)"
347
+ " (fundamental)"
226
348
  end) +
227
349
  "</dt>\n"
228
350
 
@@ -232,7 +354,7 @@ module ActiveFacts
232
354
  relevant_facts_and_constraints(o, include_alternate, include_facts, include_constraints) +
233
355
  (include_facts ? values(o) : '') +
234
356
  " </dd>\n"
235
-
357
+
236
358
  defn_term + defn_detail
237
359
  end
238
360
 
@@ -269,14 +391,14 @@ module ActiveFacts
269
391
  end
270
392
 
271
393
  def relevant_facts_and_constraints(o, include_alternate = true, include_facts = true, include_constraints = true)
272
- o.
273
- all_role.
274
- map{|r| r.fact_type}.
275
- uniq.
276
- reject do |ft| ft.is_a?(ActiveFacts::Metamodel::LinkFactType) end.
277
- map { |ft| [ft, " #{fact_type_with_constraints(ft, include_alternate, o, include_constraints)}"] }.
278
- sort_by{|ft, text| [ ft.is_a?(ActiveFacts::Metamodel::TypeInheritance) ? 0 : 1, text]}.
279
- map{|ft, text| text} * "\n"
394
+ o.
395
+ all_role.
396
+ map{|r| r.fact_type}.
397
+ uniq.
398
+ reject do |ft| ft.is_a?(ActiveFacts::Metamodel::LinkFactType) end.
399
+ map { |ft| [ft, " #{fact_type_with_constraints(ft, include_alternate, o, include_constraints)}"] }.
400
+ sort_by{|ft, text| [ ft.is_a?(ActiveFacts::Metamodel::TypeInheritance) ? 0 : 1, text]}.
401
+ map{|ft, text| text} * "\n"
280
402
  end
281
403
 
282
404
  def role_ref(rr, freq_con, l_adj, name, t_adj, role_name_def, literal)
@@ -299,7 +421,7 @@ module ActiveFacts
299
421
  end
300
422
  role_ref rr, freq_con, l_adj, name, t_adj, role_name_def, literal
301
423
  end,
302
- {:class => 'copula'}
424
+ {:class => 'reading'}
303
425
  )
304
426
  end
305
427
 
@@ -336,8 +458,9 @@ module ActiveFacts
336
458
  def fact_type_with_constraints(ft, include_alternates = true, wrt = nil, include_constraints = true)
337
459
  if ft.entity_type
338
460
  div(
339
- div(termref(ft.entity_type.name) + span(' is where ', 'keyword')) +
340
- div(fact_type(ft, include_alternates, wrt)),
461
+ (ft.entity_type == wrt ? '' : termref(ft.entity_type.name)) +
462
+ span(' is where ', 'keyword') +
463
+ fact_type(ft, include_alternates, wrt),
341
464
  'glossary-objectification'
342
465
  )
343
466
  else
@@ -367,7 +490,7 @@ module ActiveFacts
367
490
  div(
368
491
  element(
369
492
  reading.expand_with_final_presence_constraint { |*a| role_ref(*a) },
370
- {:class => 'copula'}
493
+ {:class => 'reading'}
371
494
  ),
372
495
  'glossary-constraint'
373
496
  ) + "\n"
@@ -378,20 +501,21 @@ module ActiveFacts
378
501
  defn_term =
379
502
  " <dt>" +
380
503
  "#{termdef(o.name)}" +
381
- # " (#{span('in which', 'keyword')} #{fact_type(o.fact_type, false, nil, nil)})" +
504
+ " (objectification#{o.supertypes.size > 0 ? ', subtype' : ''})" +
505
+ # Don't display OFT inline " (#{span('in which', 'keyword')} #{fact_type(o.fact_type, false, o, nil)})" +
382
506
  "</dt>\n"
383
507
  # REVISIT: Handle separate identification
384
508
 
385
509
  defn_detail =
386
- " <dd>\n" +
387
- fact_type_with_constraints(o.fact_type, include_alternate, nil, include_constraints) + "\n" +
510
+ " <dd>" +
511
+ fact_type_with_constraints(o.fact_type, include_alternate, o, include_constraints) + "\n" +
388
512
 
389
513
  o.fact_type.all_role_in_order.map do |r|
390
514
  n = r.object_type.name
391
515
  div("#{termref(o.name)} involves #{span('one', 'keyword')} #{termref(r.role_name || n, n)}", "glossary-facttype")
392
516
  end * "\n" + "\n" +
393
517
  relevant_facts_and_constraints(o, include_alternate, include_facts, include_constraints) + "\n" +
394
- " </dd>"
518
+ " </dd>\n"
395
519
 
396
520
  defn_term + defn_detail
397
521
  end
@@ -399,36 +523,35 @@ module ActiveFacts
399
523
  def entity_type_dump(o, include_alternate = true, include_facts = true, include_constraints = true)
400
524
  pi = o.preferred_identifier
401
525
  supers = o.supertypes
402
- if (supers.size > 0) # Ignore identification by a supertype:
403
- pi = nil if pi && pi.role_sequence.all_role_ref.detect{|rr| rr.role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) }
404
- end
526
+ pi = nil if pi && o.identifying_supertype
405
527
 
406
528
  defn_term =
407
529
  " <dt>" +
408
530
  "#{termdef(o.name)} " +
409
- "</dt>\n"
410
-
411
- defn_detail =
412
- " <dd>" +
413
- (supers.size > 0 ? "#{span('is a kind of', 'keyword')} #{supers.map{|s| termref(s.name)}*', '}\n" : '') +
414
531
  if pi
415
532
  "#{span('is identified by', 'keyword')} " +
416
533
  pi.role_sequence.all_role_ref_in_order.map do |rr|
417
- termref(
418
- rr.role.object_type.name,
419
- [ rr.leading_adjective,
420
- rr.role.role_name || rr.role.object_type.name,
421
- rr.trailing_adjective
422
- ].compact * '-'
534
+ preferred_reading = rr.role.fact_type.preferred_reading
535
+ preferred_role_ref = preferred_reading.role_sequence.all_role_ref.detect{|rrp| rrp.role == rr.role}
536
+ term(
537
+ [ preferred_role_ref.leading_adjective,
538
+ termref(rr.role.object_type.name, preferred_role_ref.role.role_name),
539
+ preferred_role_ref.trailing_adjective
540
+ ].compact*'-'
423
541
  )
424
542
  end * ", " + "\n"
425
543
  else
426
- ''
544
+ ' (subtype)'
427
545
  end +
546
+ "</dt>\n"
547
+
548
+ defn_detail =
549
+ " <dd>\n" +
550
+ (supers.size > 0 ? "#{span('is a kind of', 'keyword')} #{supers.map{|s| termref(s.name)}*', '}\n" : '') +
428
551
  relevant_facts_and_constraints(o, include_alternate, include_facts, include_constraints) +
429
552
  (include_facts ? entities(o) : '') +
430
- " </dd>\n"
431
-
553
+ "\n </dd>"
554
+
432
555
  defn_term + defn_detail
433
556
  end
434
557
 
@@ -466,6 +589,7 @@ module ActiveFacts
466
589
  end
467
590
  end
468
591
  end
592
+ publish_generator Doc::Glossary, "Glossary generator"
469
593
  end
470
594
  end
471
595