activefacts 0.8.16 → 0.8.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +15 -0
  2. data/Manifest.txt +10 -4
  3. data/bin/afgen +26 -20
  4. data/bin/cql +1 -1
  5. data/css/orm2.css +89 -9
  6. data/examples/CQL/CompanyDirectorEmployee.cql +4 -4
  7. data/examples/CQL/Genealogy.cql +5 -5
  8. data/examples/CQL/Metamodel.cql +121 -91
  9. data/examples/CQL/MonthInSeason.cql +2 -6
  10. data/examples/CQL/SeparateSubtype.cql +11 -9
  11. data/examples/CQL/ServiceDirector.cql +21 -33
  12. data/examples/CQL/Supervision.cql +0 -3
  13. data/examples/CQL/WindowInRoomInBldg.cql +10 -4
  14. data/examples/CQL/unit.cql +1 -1
  15. data/lib/activefacts.rb +1 -0
  16. data/lib/activefacts/cql/CQLParser.treetop +5 -1
  17. data/lib/activefacts/cql/Context.treetop +2 -7
  18. data/lib/activefacts/cql/Expressions.treetop +2 -2
  19. data/lib/activefacts/cql/FactTypes.treetop +37 -31
  20. data/lib/activefacts/cql/Language/English.treetop +21 -4
  21. data/lib/activefacts/cql/LexicalRules.treetop +59 -1
  22. data/lib/activefacts/cql/ObjectTypes.treetop +22 -12
  23. data/lib/activefacts/cql/Terms.treetop +13 -9
  24. data/lib/activefacts/cql/ValueTypes.treetop +30 -11
  25. data/lib/activefacts/cql/compiler.rb +34 -5
  26. data/lib/activefacts/cql/compiler/clause.rb +207 -116
  27. data/lib/activefacts/cql/compiler/constraint.rb +129 -105
  28. data/lib/activefacts/cql/compiler/entity_type.rb +49 -27
  29. data/lib/activefacts/cql/compiler/expression.rb +71 -42
  30. data/lib/activefacts/cql/compiler/fact.rb +70 -64
  31. data/lib/activefacts/cql/compiler/fact_type.rb +108 -57
  32. data/lib/activefacts/cql/compiler/query.rb +178 -0
  33. data/lib/activefacts/cql/compiler/shared.rb +13 -12
  34. data/lib/activefacts/cql/compiler/value_type.rb +10 -4
  35. data/lib/activefacts/cql/nodes.rb +1 -1
  36. data/lib/activefacts/cql/parser.rb +6 -2
  37. data/lib/activefacts/generate/absorption.rb +6 -3
  38. data/lib/activefacts/generate/cql.rb +140 -84
  39. data/lib/activefacts/generate/dm.rb +12 -6
  40. data/lib/activefacts/generate/help.rb +25 -6
  41. data/lib/activefacts/generate/helpers/oo.rb +195 -0
  42. data/lib/activefacts/generate/helpers/ordered.rb +589 -0
  43. data/lib/activefacts/generate/helpers/rails.rb +57 -0
  44. data/lib/activefacts/generate/html/glossary.rb +274 -54
  45. data/lib/activefacts/generate/json.rb +25 -22
  46. data/lib/activefacts/generate/null.rb +1 -0
  47. data/lib/activefacts/generate/rails/models.rb +244 -0
  48. data/lib/activefacts/generate/rails/schema.rb +185 -0
  49. data/lib/activefacts/generate/records.rb +1 -0
  50. data/lib/activefacts/generate/ruby.rb +51 -30
  51. data/lib/activefacts/generate/sql/mysql.rb +5 -3
  52. data/lib/activefacts/generate/sql/server.rb +8 -4
  53. data/lib/activefacts/generate/text.rb +1 -0
  54. data/lib/activefacts/generate/transform/surrogate.rb +209 -0
  55. data/lib/activefacts/generate/version.rb +1 -0
  56. data/lib/activefacts/input/orm.rb +234 -181
  57. data/lib/activefacts/mapping/rails.rb +122 -0
  58. data/lib/activefacts/persistence/columns.rb +34 -18
  59. data/lib/activefacts/persistence/foreignkey.rb +129 -71
  60. data/lib/activefacts/persistence/index.rb +42 -12
  61. data/lib/activefacts/persistence/reference.rb +37 -23
  62. data/lib/activefacts/persistence/tables.rb +53 -19
  63. data/lib/activefacts/registry.rb +11 -0
  64. data/lib/activefacts/support.rb +28 -10
  65. data/lib/activefacts/version.rb +1 -1
  66. data/lib/activefacts/vocabulary/extensions.rb +246 -117
  67. data/lib/activefacts/vocabulary/metamodel.rb +105 -65
  68. data/lib/activefacts/vocabulary/verbaliser.rb +226 -194
  69. data/spec/absorption_spec.rb +1 -0
  70. data/spec/cql/comparison_spec.rb +8 -8
  71. data/spec/cql/contractions_spec.rb +16 -43
  72. data/spec/cql/entity_type_spec.rb +2 -1
  73. data/spec/cql/expressions_spec.rb +2 -2
  74. data/spec/cql/fact_type_matching_spec.rb +4 -1
  75. data/spec/cql/parser/bad_literals_spec.rb +30 -30
  76. data/spec/cql/parser/entity_types_spec.rb +6 -6
  77. data/spec/cql/parser/expressions_spec.rb +25 -19
  78. data/spec/cql/samples_spec.rb +5 -4
  79. data/spec/cql_cql_spec.rb +2 -1
  80. data/spec/cql_dm_spec.rb +4 -0
  81. data/spec/cql_mysql_spec.rb +4 -0
  82. data/spec/cql_parse_spec.rb +2 -0
  83. data/spec/cql_ruby_spec.rb +4 -0
  84. data/spec/cql_sql_spec.rb +4 -0
  85. data/spec/cqldump_spec.rb +7 -4
  86. data/spec/helpers/parse_to_ast_matcher.rb +7 -3
  87. data/spec/helpers/test_parser.rb +2 -0
  88. data/spec/norma_cql_spec.rb +5 -2
  89. data/spec/norma_ruby_spec.rb +4 -1
  90. data/spec/norma_ruby_sql_spec.rb +4 -1
  91. data/spec/norma_sql_spec.rb +4 -1
  92. data/spec/norma_tables_spec.rb +2 -2
  93. data/spec/ruby_api_spec.rb +1 -1
  94. data/spec/spec_helper.rb +2 -0
  95. data/spec/transform_surrogate_spec.rb +59 -0
  96. metadata +70 -60
  97. data/TODO +0 -308
  98. data/lib/activefacts/cql/compiler/join.rb +0 -162
  99. data/lib/activefacts/generate/oo.rb +0 -176
  100. data/lib/activefacts/generate/ordered.rb +0 -602
@@ -0,0 +1,57 @@
1
+ module ActiveFacts
2
+ module Generate
3
+ module Rails
4
+ module Helpers
5
+ def rails_plural_name name
6
+ # Crunch spaces and pluralise the first part, all in snake_case
7
+ name.pop if name.is_a?(Array) and name.last == []
8
+ name = name[0]*'_' if name.is_a?(Array) and name.size == 1
9
+ if name.is_a?(Array)
10
+ name = ActiveSupport::Inflector.tableize((name[0]*'_').gsub(/\s+/, '_')) +
11
+ '_' +
12
+ ActiveSupport::Inflector.underscore((name[1..-1].flatten*'_').gsub(/\s+/, '_'))
13
+ else
14
+ ActiveSupport::Inflector.tableize(name.gsub(/\s+/, '_'))
15
+ end
16
+ end
17
+
18
+ def rails_singular_name name
19
+ # Crunch spaces and convert to snake_case
20
+ name = name.flatten*'_' if name.is_a?(Array)
21
+ ActiveSupport::Inflector.underscore(name.gsub(/\s+/, '_'))
22
+ end
23
+
24
+ def rails_class_name name
25
+ name = name*'_' if name.is_a?(Array)
26
+ ActiveSupport::Inflector.camelize(name.gsub(/\s+/, '_'))
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+
33
+ module Metamodel
34
+ class ObjectType
35
+ end
36
+ end
37
+
38
+ module Persistence
39
+ class ForeignKey
40
+ include Generate::Rails::Helpers
41
+
42
+ def rails_from_association_name
43
+ rails_singular_name(to_name.join('_'))
44
+ end
45
+
46
+ def rails_to_association
47
+ jump = jump_reference
48
+ if jump.is_one_to_one
49
+ [ "has_one", rails_singular_name(from_name)]
50
+ else
51
+ [ "has_many", rails_plural_name(from_name)]
52
+ end
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -39,39 +39,127 @@ module ActiveFacts
39
39
  end
40
40
 
41
41
  def vocabulary_start
42
- puts "<link rel='stylesheet' href='css/orm2.css' media='screen' type='text/css'/>"
43
- puts "<h1>#{@vocabulary.name}</h1>"
44
- puts "<dl>"
42
+ # puts "<link rel='stylesheet' href='css/orm2.css' media='screen' type='text/css'/>"
43
+ File.open(File.dirname(__FILE__)+"/../../../../css/orm2.css") do |f|
44
+ puts "<style media='screen' type='text/css'>"
45
+ puts f.read
46
+ puts %Q{
47
+ .glossary-facttype, .glossary-constraints { display: block; }
48
+ .glossary-doc.hide-alternates .glossary-alternates { display: none; }
49
+ .glossary-doc.hide-constraints .glossary-constraints { display: none; }
50
+ .glossary-doc.hide-examples .glossary-example { display: none; }
51
+ }.gsub(/^\s+/, '')
52
+ puts "</style>"
53
+ end
54
+
55
+ puts %Q{
56
+ <style media='print' type='text/css'>
57
+ .keyword { color: #0000CC; font-style: italic; display: inline; }
58
+ .vocabulary, .object_type { color: #8A0092; font-weight: bold; }
59
+ .copula { color: #0E5400; }
60
+ .value { color: #FF990E; display: inline; }
61
+ .glossary-toc { display: none; }
62
+ .glossary-facttype, .glossary-reading { display: inline; }
63
+ </style>
64
+ }.gsub(/^\s+/, '')
45
65
  end
46
66
 
47
67
  def vocabulary_end
48
- puts "</dl>"
68
+ puts %Q{
69
+ <script type="text/javascript">
70
+ function toggle_class(e, c) {
71
+ if (!e) return;
72
+ var n = e.className;
73
+ var i = n.indexOf(c);
74
+ if (i == -1) {
75
+ e.className = n+' '+c;
76
+ } else {
77
+ e.className = n.slice(0, i)+n.slice(i+c.length);
78
+ }
79
+ if (document.location.toString().indexOf('#') >= 0)
80
+ document.location = document.location; // Re-scroll to the current fragment
81
+ }
82
+ function toggle_constraints() {
83
+ toggle_class(document.getElementById('glossary-doc'), 'hide-constraints');
84
+ }
85
+ function toggle_alternates() {
86
+ toggle_class(document.getElementById('glossary-doc'), 'hide-alternates');
87
+ }
88
+ function toggle_examples() {
89
+ toggle_class(document.getElementById('glossary-doc'), 'hide-examples');
90
+ }
91
+ </script>
92
+ }.gsub(/^\s+/, '')
49
93
  end
50
94
 
51
95
  def object_types_dump
52
- @vocabulary.
53
- all_object_type.
54
- sort_by{|o| o.name.gsub(/ /,'').downcase}.
55
- each do |o|
56
- case o
57
- when ActiveFacts::Metamodel::TypeInheritance
58
- nil
59
- when ActiveFacts::Metamodel::ValueType
60
- value_type_dump(o)
61
- else
62
- if o.fact_type
63
- objectified_fact_type_dump(o)
64
- else
65
- entity_type_dump(o)
66
- end
67
- end
68
- end
69
- end
96
+ all_object_type =
97
+ @vocabulary.
98
+ all_object_type.
99
+ sort_by{|o| o.name.gsub(/ /,'').downcase}
100
+
101
+ # Put out a table of contents first:
102
+ puts '<div class="glossary-sidebar">'
103
+ puts '<ol class="glossary-toc">'
104
+ all_object_type.
105
+ reject do |o|
106
+ o.name == '_ImplicitBooleanValueType' or
107
+ o.kind_of?(ActiveFacts::Metamodel::ValueType) && o.all_role.size == 0 or
108
+ o.kind_of?(ActiveFacts::Metamodel::TypeInheritance)
109
+ end.
110
+ each do |o|
111
+ puts "<li>#{termref(o.name)}</li>"
112
+ end
113
+ puts '</ol>'
114
+ puts '<div class="glossary-controls">'
115
+ puts ' <input type="button" onclick="toggle_constraints()" value="Constraints" class="glossary-toggle-constraint">'
116
+ puts ' <input type="button" onclick="toggle_alternates()" value="Alternates" class="glossary-toggle-alternates">'
117
+ puts ' <input type="button" onclick="toggle_examples()" value="Examples" class="glossary-toggle-examples">'
118
+ puts '</div>'
119
+ puts '</div>'
120
+
121
+ puts '<div class="glossary-doc hide-alternates hide-constraints" id="glossary-doc">'
122
+ puts "<h1>#{@vocabulary.name}</h1>"
123
+ puts '<dl>'
124
+ all_object_type.
125
+ each do |o|
126
+ case o
127
+ when ActiveFacts::Metamodel::TypeInheritance
128
+ nil
129
+ when ActiveFacts::Metamodel::ValueType
130
+ value_type_dump(o)
131
+ else
132
+ if o.fact_type
133
+ objectified_fact_type_dump(o)
134
+ else
135
+ entity_type_dump(o)
136
+ end
137
+ end
138
+ end
139
+ puts '</dl>'
140
+ puts '</div>'
141
+ end
70
142
 
71
143
  def element(text, attrs, tag = 'span')
72
144
  "<#{tag}#{attrs.empty? ? '' : attrs.map{|k,v| " #{k}='#{v}'"}*''}>#{text}</#{tag}>"
73
145
  end
74
146
 
147
+ def span(text, klass = nil)
148
+ element(text, klass ? {:class => klass} : {})
149
+ end
150
+
151
+ def div(text, klass = nil)
152
+ element(text, klass ? {:class => klass} : {}, 'div')
153
+ end
154
+
155
+ def h1(text, klass = nil)
156
+ element(text, klass ? {:class => klass} : {}, 'h1')
157
+ end
158
+
159
+ def dl(text, klass = nil)
160
+ element(text, klass ? {:class => klass} : {}, 'dl')
161
+ end
162
+
75
163
  # A definition of a term
76
164
  def termdef(name)
77
165
  element(name, {:name => name, :class => 'object_type'}, 'a')
@@ -89,30 +177,75 @@ module ActiveFacts
89
177
  end
90
178
 
91
179
  def value_type_dump(o)
92
- return if o.all_role.size == 0 # Skip value types that are only used as supertypes
180
+ return if o.all_role.size == 0 or # Skip value types that are only used as supertypes
181
+ o.name == '_ImplicitBooleanValueType'
93
182
  puts " <dt>" +
94
- "Value Type: #{termdef(o.name)}" +
95
- (o.supertype ? " (written as #{termref(o.supertype.name)})" : "") +
183
+ "#{termdef(o.name)} " +
184
+ (if o.supertype
185
+ span('is written as ', :keyword) + termref(o.supertype.name)
186
+ else
187
+ " (a fundamental data type)"
188
+ end) +
96
189
  "</dt>"
97
190
 
98
191
  puts " <dd>"
192
+ value_sub_types(o)
99
193
  relevant_facts_and_constraints(o)
194
+ values(o)
100
195
  puts " </dd>"
101
196
  end
102
197
 
198
+ def value_sub_types(o)
199
+ o.
200
+ all_value_type_as_supertype. # All value types for which o is a supertype
201
+ sort_by{|sub| sub.name}.
202
+ each do |sub|
203
+ puts div(
204
+ "#{termref(sub.name)} #{span('is written as', 'keyword')} #{termref(o.name)}",
205
+ 'glossary-facttype'
206
+ )
207
+ end
208
+ end
209
+
210
+ def values(o)
211
+ o.all_instance.
212
+ sort_by{|i|
213
+ [i.population.name, i.value.literal]
214
+ }.
215
+ each do |i|
216
+ v = i.value
217
+ puts div(
218
+ (i.population.name.empty? ? '' : i.population.name+': ') +
219
+ termref(o.name) + ' ' +
220
+ div(
221
+ # v.is_literal_string ? v.literal.inspect : v.literal,
222
+ v.literal.inspect,
223
+ 'value'
224
+ ),
225
+ 'glossary-example'
226
+ )
227
+ end
228
+ end
229
+
103
230
  def relevant_facts_and_constraints(o)
104
231
  puts(
105
232
  o.
106
233
  all_role.
107
234
  map{|r| r.fact_type}.
108
235
  uniq.
109
- reject{|ft| ft.is_a?(ActiveFacts::Metamodel::TypeInheritance) || ft.is_a?(ActiveFacts::Metamodel::ImplicitFactType) }.
110
- map { |ft| " #{fact_type_with_constraints(ft, o)}</br>" }.
111
- sort * "\n"
236
+ reject do |ft|
237
+ ft.is_a?(ActiveFacts::Metamodel::LinkFactType)
238
+ end.
239
+ map { |ft| [ft, " #{fact_type_with_constraints(ft, o)}"] }.
240
+ sort_by{|ft, text|
241
+ [ ft.is_a?(ActiveFacts::Metamodel::TypeInheritance) ? 0 : 1, text]
242
+ }.
243
+ map{|ft, text| text}.
244
+ join "\n"
112
245
  )
113
246
  end
114
247
 
115
- def role_ref rr, freq_con, l_adj, name, t_adj, role_name_def, literal
248
+ def role_ref(rr, freq_con, l_adj, name, t_adj, role_name_def, literal)
116
249
  term_parts = [l_adj, termref(name), t_adj].compact
117
250
  [
118
251
  freq_con ? element(freq_con, :class=>:keyword) : nil,
@@ -122,30 +255,67 @@ module ActiveFacts
122
255
  ]
123
256
  end
124
257
 
125
- def expand_reading(r)
258
+ def expand_reading(reading, include_rolenames = true)
126
259
  element(
127
- r.expand do |*a|
128
- role_ref(*a)
260
+ reading.expand([], include_rolenames) do |rr, freq_con, l_adj, name, t_adj, role_name_def, literal|
261
+ if role_name_def
262
+ role_name_def = role_name_def.gsub(/\(as ([^)]+)\)/) {
263
+ span("(as #{ termref(rr.role.object_type.name, $1) })", 'keyword')
264
+ }
265
+ end
266
+ role_ref rr, freq_con, l_adj, name, t_adj, role_name_def, literal
129
267
  end,
130
268
  {:class => 'copula'}
131
269
  )
132
270
  end
133
271
 
134
- def fact_type(ft, wrt = nil)
272
+ def fact_type_block(ft, include_alternates = true, wrt = nil, include_rolenames = true)
273
+ div(fact_type(ft, include_alternates, wrt, include_rolenames), 'glossary-facttype')
274
+ end
275
+
276
+ def fact_type(ft, include_alternates = true, wrt = nil, include_rolenames = true)
135
277
  role = ft.all_role.detect{|r| r.object_type == wrt}
136
278
  preferred_reading = ft.reading_preferably_starting_with_role(role)
137
279
  alternate_readings = ft.all_reading.reject{|r| r == preferred_reading}
138
- expand_reading(preferred_reading) +
139
- (alternate_readings.size > 0 ?
140
- ' (alternatively, ' +
141
- alternate_readings.map { |r| expand_reading(r)}*', ' +
142
- ')' : '')
280
+
281
+ div(
282
+ expand_reading(preferred_reading, include_rolenames),
283
+ 'glossary-reading'
284
+ )+
285
+ (if include_alternates and alternate_readings.size > 0
286
+ div(
287
+ "(alternatively: " +
288
+ alternate_readings.map do |reading|
289
+ div(
290
+ expand_reading(reading, include_rolenames),
291
+ 'glossary-reading'
292
+ )
293
+ end*",\n"+')',
294
+ 'glossary-alternates'
295
+ )
296
+ else
297
+ ''
298
+ end
299
+ )
143
300
  end
144
301
 
145
302
  def fact_type_with_constraints(ft, wrt = nil)
146
- fact_type(ft, wrt) +
147
- "<br/>\n<ul>\n" +
148
- fact_type_constraints(ft) +
303
+ if ft.entity_type
304
+ div(
305
+ termref(ft.entity_type.name) +
306
+ div(' is where ', 'keyword') +
307
+ fact_type(ft, true, wrt),
308
+ 'glossary-objectification'
309
+ )
310
+ else
311
+ fact_type_block(ft, true, wrt)
312
+ end +
313
+ %Q{\n<ul class="glossary-constraints">\n}+
314
+ (unless ft.is_a?(ActiveFacts::Metamodel::TypeInheritance)
315
+ fact_type_constraints(ft)
316
+ else
317
+ ''
318
+ end) +
149
319
  "</ul>"
150
320
  end
151
321
 
@@ -157,25 +327,29 @@ module ActiveFacts
157
327
  reading.role_sequence.all_role_ref_in_order[reading.role_numbers[-1]].role == residual_role
158
328
  }
159
329
  next nil unless reading
160
- element(
161
- reading.expand_with_final_presence_constraint { |*a| role_ref(*a) },
162
- {:class => 'copula'}
163
- )+"<br/>\n"
330
+ div(
331
+ element(
332
+ reading.expand_with_final_presence_constraint { |*a| role_ref(*a) },
333
+ {:class => 'copula'}
334
+ ),
335
+ 'glossary-constraint'
336
+ )+"\n"
164
337
  end.compact*''
165
338
  end
166
339
 
167
340
  def objectified_fact_type_dump(o)
168
341
  puts " <dt>" +
169
- "Entity Type: #{termdef(o.name)}" +
170
- " (objectification of #{fact_type(o.fact_type)})" +
342
+ "#{termdef(o.name)}" +
343
+ # " (#{span('in which', 'keyword')} #{fact_type(o.fact_type, false, nil, nil)})" +
171
344
  "</dt>"
172
345
  # REVISIT: Handle separate identification
173
346
 
174
347
  puts " <dd>"
175
- puts fact_type_constraints(o.fact_type)
348
+ puts fact_type_with_constraints(o.fact_type)
349
+
176
350
  o.fact_type.all_role_in_order.each do |r|
177
351
  n = r.object_type.name
178
- puts "#{termref(o.name)} involves exactly one #{termref(r.role_name || n, n)}<br/>"
352
+ puts div("#{termref(o.name)} involves #{span('one', 'keyword')} #{termref(r.role_name || n, n)}", "glossary-facttype")
179
353
  end
180
354
  relevant_facts_and_constraints(o)
181
355
  puts " </dd>"
@@ -189,21 +363,67 @@ module ActiveFacts
189
363
  end
190
364
 
191
365
  puts " <dt>" +
192
- "Entity Type: #{termdef(o.name)}" +
193
- " (" +
366
+ "#{termdef(o.name)} " +
194
367
  [
195
- (supers.size > 0 ? "Subtype of #{supers.map{|s| s.name}*', '})" : nil),
196
- (pi ? "identified by "+pi.role_sequence.describe : nil)
368
+ (supers.size > 0 ? "#{span('is a kind of', 'keyword')} #{supers.map{|s| termref(s.name)}*', '}" : nil),
369
+ (if pi
370
+ "#{span('is identified by', 'keyword')} " +
371
+ pi.role_sequence.all_role_ref_in_order.map do |rr|
372
+ termref(
373
+ rr.role.object_type.name,
374
+ [ rr.leading_adjective,
375
+ rr.role.role_name || rr.role.object_type.name,
376
+ rr.trailing_adjective
377
+ ].compact*'-'
378
+ )
379
+ end*", "
380
+ else
381
+ nil
382
+ end)
197
383
  ].compact*', '
198
- ")" +
199
384
  "</dt>"
200
385
 
201
386
  puts " <dd>"
202
387
  relevant_facts_and_constraints(o)
388
+ entities(o)
203
389
  puts " </dd>"
204
390
  end
205
391
 
392
+ def entities(o)
393
+ return if o.preferred_identifier.role_sequence.all_role_ref.size > 1 # REVISIT: Composite identification
394
+ o.all_instance.each do |i|
395
+ v = i.value
396
+ ii = i # The identifying instance
397
+
398
+ until v
399
+ pi = ii.object_type.preferred_identifier # ii is an Entity Type
400
+ break if pi.role_sequence.all_role_ref.size > 1 # REVISIT: Composite identification
401
+
402
+ identifying_fact_type = pi.role_sequence.all_role_ref.single.role.fact_type
403
+ # Find the role played by this instance through which it is identified:
404
+ irv = i.all_role_value.detect{|rv| rv.fact.fact_type == identifying_fact_type }
405
+ # Get the other RoleValue in what must be a binary fact type:
406
+ orv = irv.fact.all_role_value.detect{|rv| rv != irv}
407
+ ii = orv.instance
408
+ v = ii.value # Does this instance have a value? If so, we're done.
409
+ end
410
+
411
+ next unless v
412
+ puts div(
413
+ (i.population.name.empty? ? '' : i.population.name+': ') +
414
+ termref(o.name) + ' ' +
415
+ div(
416
+ # v.is_literal_string ? v.literal.inspect : v.literal,
417
+ v.literal.inspect,
418
+ 'value'),
419
+ 'glossary-example'
420
+ )
421
+ end
422
+ end
423
+
206
424
  end
207
425
  end
208
426
  end
209
427
  end
428
+
429
+ ActiveFacts::Registry.generator('html/glossary', ActiveFacts::Generate::HTML::GLOSSARY)