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.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +10 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +30 -0
  8. data/Rakefile +6 -0
  9. data/activefacts-generators.gemspec +26 -0
  10. data/lib/activefacts/dependency_analyser.rb +182 -0
  11. data/lib/activefacts/generators/absorption.rb +71 -0
  12. data/lib/activefacts/generators/composition.rb +119 -0
  13. data/lib/activefacts/generators/cql.rb +715 -0
  14. data/lib/activefacts/generators/diagrams/json.rb +340 -0
  15. data/lib/activefacts/generators/help.rb +64 -0
  16. data/lib/activefacts/generators/helpers/inject.rb +16 -0
  17. data/lib/activefacts/generators/helpers/oo.rb +162 -0
  18. data/lib/activefacts/generators/helpers/ordered.rb +605 -0
  19. data/lib/activefacts/generators/helpers/rails.rb +57 -0
  20. data/lib/activefacts/generators/html/glossary.rb +462 -0
  21. data/lib/activefacts/generators/metadata/json.rb +204 -0
  22. data/lib/activefacts/generators/null.rb +32 -0
  23. data/lib/activefacts/generators/rails/models.rb +247 -0
  24. data/lib/activefacts/generators/rails/schema.rb +217 -0
  25. data/lib/activefacts/generators/ruby.rb +134 -0
  26. data/lib/activefacts/generators/sql/mysql.rb +281 -0
  27. data/lib/activefacts/generators/sql/server.rb +274 -0
  28. data/lib/activefacts/generators/stats.rb +70 -0
  29. data/lib/activefacts/generators/text.rb +29 -0
  30. data/lib/activefacts/generators/traits/datavault.rb +241 -0
  31. data/lib/activefacts/generators/traits/oo.rb +73 -0
  32. data/lib/activefacts/generators/traits/ordered.rb +33 -0
  33. data/lib/activefacts/generators/traits/ruby.rb +210 -0
  34. data/lib/activefacts/generators/transform/datavault.rb +303 -0
  35. data/lib/activefacts/generators/transform/surrogate.rb +215 -0
  36. data/lib/activefacts/registry.rb +11 -0
  37. metadata +176 -0
@@ -0,0 +1,57 @@
1
+ module ActiveFacts
2
+ module Generators
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 RMap
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
@@ -0,0 +1,462 @@
1
+ #
2
+ # ActiveFacts Generators.
3
+ #
4
+ # Generate a glossary in HTML
5
+ #
6
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
7
+ #
8
+ require 'activefacts/api'
9
+ require 'activefacts/registry'
10
+
11
+ module ActiveFacts
12
+ module Generators #:nodoc:
13
+ class HTML #:nodoc:
14
+ class GLOSSARY #: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
+ @gen_bootstrap = false
24
+ case option
25
+ when 'help', '?'
26
+ $stderr.puts "Usage:\t\tafgen --html/glossary[=option] input_file.cql\n"+
27
+ "\t\tbootstrap\tGenerate bootstrap styled glossary html"
28
+ exit 0
29
+ when /bootstrap/
30
+ @gen_bootstrap = true
31
+ else super
32
+ end
33
+ end
34
+
35
+ def puts(*a)
36
+ @out.puts *a
37
+ end
38
+
39
+ def print(*a)
40
+ @out.print *a
41
+ end
42
+
43
+ def generate(out = $>)
44
+ @out = out
45
+
46
+ @all_object_type =
47
+ @vocabulary.
48
+ all_object_type.
49
+ sort_by{|o| o.name.gsub(/ /,'').downcase}
50
+
51
+ vocabulary_start
52
+
53
+ if @gen_bootstrap
54
+ object_types_dump_toc()
55
+ object_types_dump_def()
56
+ else
57
+ object_types_dump_def()
58
+ object_types_dump_toc()
59
+ end
60
+ vocabulary_end
61
+ end
62
+
63
+ def vocabulary_start
64
+ if !@gen_bootstrap
65
+ # puts "<link rel='stylesheet' href='css/orm2.css' media='screen' type='text/css'/>"
66
+ css_file = "/../../../../css/orm2.css"
67
+
68
+ File.open(File.dirname(__FILE__)+css_file) do |f|
69
+ puts "<style media='screen' type='text/css'>"
70
+ puts f.read
71
+ puts %Q{
72
+ .glossary-facttype, .glossary-constraints { display: block; }
73
+ .glossary-doc.hide-alternates .glossary-alternates { display: none; }
74
+ .glossary-doc.hide-constraints .glossary-constraints { display: none; }
75
+ .glossary-doc.hide-examples .glossary-example { display: none; }
76
+ }.gsub(/^\s+/, '')
77
+ puts "</style>"
78
+ end
79
+
80
+ puts %Q{
81
+ <style media='print' type='text/css'>
82
+ .keyword { color: #0000CC; font-style: italic; display: inline; }
83
+ .vocabulary, .object_type { color: #8A0092; font-weight: bold; }
84
+ .copula { color: #0E5400; }
85
+ .value { color: #FF990E; display: inline; }
86
+ .glossary-toc { display: none; }
87
+ .glossary-facttype, .glossary-reading { display: inline; }
88
+ </style>
89
+ }.gsub(/^\s+/, '')
90
+ end
91
+ end
92
+
93
+ def vocabulary_end
94
+ if !@gen_bootstrap
95
+ puts %Q{
96
+ <script type="text/javascript">
97
+ function toggle_class(e, c) {
98
+ if (!e) return;
99
+ var n = e.className;
100
+ var i = n.indexOf(c);
101
+ if (i == -1) {
102
+ e.className = n+' '+c;
103
+ } else {
104
+ e.className = n.slice(0, i)+n.slice(i+c.length);
105
+ }
106
+ if (document.location.toString().indexOf('#') >= 0)
107
+ document.location = document.location; // Re-scroll to the current fragment
108
+ }
109
+ function toggle_constraints() {
110
+ toggle_class(document.getElementById('glossary-doc'), 'hide-constraints');
111
+ }
112
+ function toggle_alternates() {
113
+ toggle_class(document.getElementById('glossary-doc'), 'hide-alternates');
114
+ }
115
+ function toggle_examples() {
116
+ toggle_class(document.getElementById('glossary-doc'), 'hide-examples');
117
+ }
118
+ </script>
119
+ }.gsub(/^\s+/, '')
120
+ end
121
+ end
122
+
123
+ def object_types_dump_toc
124
+ if @gen_bootstrap
125
+ puts '<div class="col-md-3 glossary-sidebar">'
126
+ else
127
+ puts '<div class="glossary-sidebar">'
128
+ end
129
+ puts '<h1 style="visibility: hidden">X</h1>'
130
+ puts '<ol class="glossary-toc">'
131
+ @all_object_type.
132
+ reject do |o|
133
+ o.name == '_ImplicitBooleanValueType' or
134
+ o.kind_of?(ActiveFacts::Metamodel::ValueType) && o.all_role.size == 0 or
135
+ o.kind_of?(ActiveFacts::Metamodel::TypeInheritance)
136
+ end.
137
+ each do |o|
138
+ puts "<li>#{termref(o.name)}</li>"
139
+ end
140
+ puts '</ol>'
141
+ puts '<div class="glossary-controls">'
142
+ puts ' <input type="button" onclick="toggle_constraints()" value="Constraints" class="glossary-toggle-constraint">'
143
+ puts ' <input type="button" onclick="toggle_alternates()" value="Alternates" class="glossary-toggle-alternates">'
144
+ puts ' <input type="button" onclick="toggle_examples()" value="Examples" class="glossary-toggle-examples">'
145
+ puts '</div>'
146
+ puts '</div>'
147
+ end
148
+
149
+ def object_types_dump_def
150
+ if @gen_bootstrap
151
+ puts '<div class="col-md-5 glossary-doc hide-alternates hide-constraints" id="glossary-doc">'
152
+ else
153
+ puts '<div class="glossary-doc hide-alternates hide-constraints" id="glossary-doc">'
154
+ end
155
+ puts "<h1>#{@vocabulary.name}</h1>"
156
+ puts '<dl>'
157
+ @all_object_type.
158
+ each do |o|
159
+ case o
160
+ when ActiveFacts::Metamodel::TypeInheritance
161
+ nil
162
+ when ActiveFacts::Metamodel::ValueType
163
+ value_type_dump(o)
164
+ else
165
+ if o.fact_type
166
+ objectified_fact_type_dump(o)
167
+ else
168
+ entity_type_dump(o)
169
+ end
170
+ end
171
+ end
172
+ puts '</dl>'
173
+ puts '</div>'
174
+ end
175
+
176
+ def element(text, attrs, tag = 'span')
177
+ "<#{tag}#{attrs.empty? ? '' : attrs.map{|k,v| " #{k}='#{v}'"}*''}>#{text}</#{tag}>"
178
+ end
179
+
180
+ def span(text, klass = nil)
181
+ element(text, klass ? {:class => klass} : {})
182
+ end
183
+
184
+ def div(text, klass = nil)
185
+ element(text, klass ? {:class => klass} : {}, 'div')
186
+ end
187
+
188
+ def h1(text, klass = nil)
189
+ element(text, klass ? {:class => klass} : {}, 'h1')
190
+ end
191
+
192
+ def dl(text, klass = nil)
193
+ element(text, klass ? {:class => klass} : {}, 'dl')
194
+ end
195
+
196
+ # A definition of a term
197
+ def termdef(name)
198
+ element(name, {:name => name, :class => 'object_type'}, 'a')
199
+ end
200
+
201
+ # A reference to a defined term (excluding role adjectives)
202
+ def termref(name, role_name = nil)
203
+ role_name ||= name
204
+ element(role_name, {:href=>'#'+name, :class=>:object_type}, 'a')
205
+ end
206
+
207
+ # Text that should appear as part of a term (including role adjectives)
208
+ def term(name)
209
+ element(name, :class=>:object_type)
210
+ end
211
+
212
+ def value_type_dump(o)
213
+ return if o.all_role.size == 0 or # Skip value types that are only used as supertypes
214
+ o.name == '_ImplicitBooleanValueType'
215
+ puts " <dt>" +
216
+ "#{termdef(o.name)} " +
217
+ (if o.supertype
218
+ span('is written as ', :keyword) + termref(o.supertype.name)
219
+ else
220
+ " (a fundamental data type)"
221
+ end) +
222
+ "</dt>"
223
+
224
+ puts " <dd>"
225
+ value_sub_types(o)
226
+ relevant_facts_and_constraints(o)
227
+ values(o)
228
+ puts " </dd>"
229
+ end
230
+
231
+ def value_sub_types(o)
232
+ o.
233
+ all_value_type_as_supertype. # All value types for which o is a supertype
234
+ sort_by{|sub| sub.name}.
235
+ each do |sub|
236
+ puts div(
237
+ "#{termref(sub.name)} #{span('is written as', 'keyword')} #{termref(o.name)}",
238
+ 'glossary-facttype'
239
+ )
240
+ end
241
+ end
242
+
243
+ def values(o)
244
+ o.all_instance.
245
+ sort_by{|i|
246
+ [i.population.name, i.value.literal]
247
+ }.
248
+ each do |i|
249
+ v = i.value
250
+ puts div(
251
+ (i.population.name.empty? ? '' : i.population.name+': ') +
252
+ termref(o.name) + ' ' +
253
+ div(
254
+ # v.is_literal_string ? v.literal.inspect : v.literal,
255
+ v.literal.inspect,
256
+ 'value'
257
+ ),
258
+ 'glossary-example'
259
+ )
260
+ end
261
+ end
262
+
263
+ def relevant_facts_and_constraints(o)
264
+ puts(
265
+ o.
266
+ all_role.
267
+ map{|r| r.fact_type}.
268
+ uniq.
269
+ reject do |ft|
270
+ ft.is_a?(ActiveFacts::Metamodel::LinkFactType)
271
+ end.
272
+ map { |ft| [ft, " #{fact_type_with_constraints(ft, o)}"] }.
273
+ sort_by{|ft, text|
274
+ [ ft.is_a?(ActiveFacts::Metamodel::TypeInheritance) ? 0 : 1, text]
275
+ }.
276
+ map{|ft, text| text}.
277
+ join "\n"
278
+ )
279
+ end
280
+
281
+ def role_ref(rr, freq_con, l_adj, name, t_adj, role_name_def, literal)
282
+ term_parts = [l_adj, termref(name), t_adj].compact
283
+ [
284
+ freq_con ? element(freq_con, :class=>:keyword) : nil,
285
+ term_parts.size > 1 ? term([l_adj, termref(name), t_adj].compact*' ') : term_parts[0],
286
+ role_name_def,
287
+ literal
288
+ ]
289
+ end
290
+
291
+ def expand_reading(reading, include_rolenames = true)
292
+ element(
293
+ reading.expand([], include_rolenames) do |rr, freq_con, l_adj, name, t_adj, role_name_def, literal|
294
+ if role_name_def
295
+ role_name_def = role_name_def.gsub(/\(as ([^)]+)\)/) {
296
+ span("(as #{ termref(rr.role.object_type.name, $1) })", 'keyword')
297
+ }
298
+ end
299
+ role_ref rr, freq_con, l_adj, name, t_adj, role_name_def, literal
300
+ end,
301
+ {:class => 'copula'}
302
+ )
303
+ end
304
+
305
+ def fact_type_block(ft, include_alternates = true, wrt = nil, include_rolenames = true)
306
+ div(fact_type(ft, include_alternates, wrt, include_rolenames), 'glossary-facttype')
307
+ end
308
+
309
+ def fact_type(ft, include_alternates = true, wrt = nil, include_rolenames = true)
310
+ role = ft.all_role.detect{|r| r.object_type == wrt}
311
+ preferred_reading = ft.reading_preferably_starting_with_role(role)
312
+ alternate_readings = ft.all_reading.reject{|r| r == preferred_reading}
313
+
314
+ div(
315
+ expand_reading(preferred_reading, include_rolenames),
316
+ 'glossary-reading'
317
+ )+
318
+ (if include_alternates and alternate_readings.size > 0
319
+ div(
320
+ "(alternatively: " +
321
+ alternate_readings.map do |reading|
322
+ div(
323
+ expand_reading(reading, include_rolenames),
324
+ 'glossary-reading'
325
+ )
326
+ end*",\n"+')',
327
+ 'glossary-alternates'
328
+ )
329
+ else
330
+ ''
331
+ end
332
+ )
333
+ end
334
+
335
+ def fact_type_with_constraints(ft, wrt = nil)
336
+ if ft.entity_type
337
+ div(
338
+ termref(ft.entity_type.name) +
339
+ div(' is where ', 'keyword') +
340
+ fact_type(ft, true, wrt),
341
+ 'glossary-objectification'
342
+ )
343
+ else
344
+ fact_type_block(ft, true, wrt)
345
+ end +
346
+ %Q{\n<ul class="glossary-constraints">\n}+
347
+ (unless ft.is_a?(ActiveFacts::Metamodel::TypeInheritance)
348
+ fact_type_constraints(ft)
349
+ else
350
+ ''
351
+ end) +
352
+ "</ul>"
353
+ end
354
+
355
+ def fact_type_constraints(ft)
356
+ ft.internal_presence_constraints.map do |pc|
357
+ residual_role = ft.all_role.detect{|r| !pc.role_sequence.all_role_ref.detect{|rr| rr.role == r}}
358
+ next nil unless residual_role
359
+ reading = ft.all_reading.detect{|reading|
360
+ reading.role_sequence.all_role_ref_in_order[reading.role_numbers[-1]].role == residual_role
361
+ }
362
+ next nil unless reading
363
+ div(
364
+ element(
365
+ reading.expand_with_final_presence_constraint { |*a| role_ref(*a) },
366
+ {:class => 'copula'}
367
+ ),
368
+ 'glossary-constraint'
369
+ )+"\n"
370
+ end.compact*''
371
+ end
372
+
373
+ def objectified_fact_type_dump(o)
374
+ puts " <dt>" +
375
+ "#{termdef(o.name)}" +
376
+ # " (#{span('in which', 'keyword')} #{fact_type(o.fact_type, false, nil, nil)})" +
377
+ "</dt>"
378
+ # REVISIT: Handle separate identification
379
+
380
+ puts " <dd>"
381
+ puts fact_type_with_constraints(o.fact_type)
382
+
383
+ o.fact_type.all_role_in_order.each do |r|
384
+ n = r.object_type.name
385
+ puts div("#{termref(o.name)} involves #{span('one', 'keyword')} #{termref(r.role_name || n, n)}", "glossary-facttype")
386
+ end
387
+ relevant_facts_and_constraints(o)
388
+ puts " </dd>"
389
+ end
390
+
391
+ def entity_type_dump(o)
392
+ pi = o.preferred_identifier
393
+ supers = o.supertypes
394
+ if (supers.size > 0) # Ignore identification by a supertype:
395
+ pi = nil if pi && pi.role_sequence.all_role_ref.detect{|rr| rr.role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) }
396
+ end
397
+
398
+ puts " <dt>" +
399
+ "#{termdef(o.name)} " +
400
+ [
401
+ (supers.size > 0 ? "#{span('is a kind of', 'keyword')} #{supers.map{|s| termref(s.name)}*', '}" : nil),
402
+ (if pi
403
+ "#{span('is identified by', 'keyword')} " +
404
+ pi.role_sequence.all_role_ref_in_order.map do |rr|
405
+ termref(
406
+ rr.role.object_type.name,
407
+ [ rr.leading_adjective,
408
+ rr.role.role_name || rr.role.object_type.name,
409
+ rr.trailing_adjective
410
+ ].compact*'-'
411
+ )
412
+ end*", "
413
+ else
414
+ nil
415
+ end)
416
+ ].compact*', '
417
+ "</dt>"
418
+
419
+ puts " <dd>"
420
+ relevant_facts_and_constraints(o)
421
+ entities(o)
422
+ puts " </dd>"
423
+ end
424
+
425
+ def entities(o)
426
+ return if o.preferred_identifier.role_sequence.all_role_ref.size > 1 # REVISIT: Composite identification
427
+ o.all_instance.each do |i|
428
+ v = i.value
429
+ ii = i # The identifying instance
430
+
431
+ until v
432
+ pi = ii.object_type.preferred_identifier # ii is an Entity Type
433
+ break if pi.role_sequence.all_role_ref.size > 1 # REVISIT: Composite identification
434
+
435
+ identifying_fact_type = pi.role_sequence.all_role_ref.single.role.fact_type
436
+ # Find the role played by this instance through which it is identified:
437
+ irv = i.all_role_value.detect{|rv| rv.fact.fact_type == identifying_fact_type }
438
+ # Get the other RoleValue in what must be a binary fact type:
439
+ orv = irv.fact.all_role_value.detect{|rv| rv != irv}
440
+ ii = orv.instance
441
+ v = ii.value # Does this instance have a value? If so, we're done.
442
+ end
443
+
444
+ next unless v
445
+ puts div(
446
+ (i.population.name.empty? ? '' : i.population.name+': ') +
447
+ termref(o.name) + ' ' +
448
+ div(
449
+ # v.is_literal_string ? v.literal.inspect : v.literal,
450
+ v.literal.inspect,
451
+ 'value'),
452
+ 'glossary-example'
453
+ )
454
+ end
455
+ end
456
+
457
+ end
458
+ end
459
+ end
460
+ end
461
+
462
+ ActiveFacts::Registry.generator('html/glossary', ActiveFacts::Generators::HTML::GLOSSARY)