lib/activefacts/generator/doc/glossary.rb in activefacts-compositions-1.9.22 vs lib/activefacts/generator/doc/glossary.rb in activefacts-compositions-1.9.23

- old
+ new

@@ -11,23 +11,28 @@ module ActiveFacts module Generators #:nodoc: module Doc class Glossary #:nodoc: + MM = ActiveFacts::Metamodel + # Options are comma or space separated: - # * gen_bootstrap Generate bootstrap styled glossary html def self.options { - gen_bootstrap: ['Boolean', "Generate bootstrap styled glossary html"], } end - + + def self.compatibility + [0, nil] # no composition is required + end + # Base class for generators of object-oriented class libraries for an ActiveFacts vocabulary. - def initialize vocabulary, options = {} - @vocabulary = vocabulary # REVISIT: This should be a Composition here + def initialize constellation, composition, options = {} + @constellation = constellation + @compositions = Array(composition) + @vocabulary = constellation.Vocabulary.values[0] @options = options - @gen_bootstrap = options.has_key?("gen_bootstrap") end def puts(*a) @out.puts *a end @@ -39,125 +44,107 @@ def generate @all_object_type = @vocabulary. all_object_type. sort_by{|o| o.name.gsub(/ /,'').downcase} - + + "<html><head>" + glossary_start + + "</head><body>" + glossary_body + - glossary_end + glossary_end + + "</body>" end def glossary_start - if !@gen_bootstrap - # puts "<link rel='stylesheet' href='css/orm2.css' media='screen' type='text/css'/>" - css_file = "css/orm2.css" - - File.open(File.dirname(__FILE__)+css_file) do |f| - "<style media='screen' type='text/css'>\n" + - f.read + - %Q{ - .glossary-facttype, .glossary-constraints { display: block; }\n - .glossary-doc.hide-alternates .glossary-alternates { display: none; }\n - .glossary-doc.hide-constraints .glossary-constraints { display: none; }\n - .glossary-doc.hide-examples .glossary-example { display: none; }\n - }.gsub(/^\s+/, '') + - "</style>\n" - end + - - %Q{ - <style media='print' type='text/css'>\n - .keyword { color: #0000CC; font-style: italic; display: inline; }\n - .vocabulary, .object_type { color: #8A0092; font-weight: bold; }\n - .copula { color: #0E5400; }\n - .value { color: #FF990E; display: inline; }\n - .glossary-toc { display: none; }\n - .glossary-facttype, .glossary-reading { display: inline; }\n - </style>\n - }.gsub(/^\s+/, '') - else - '' - end + # Inline the following CSS files: + { + all: ["reset.css", "treetable.css"], + screen: ["orm2.css", "glossary.css"], + print: ["orm2-print.css", "glossary-print.css"] + }. + flat_map do |media, css_files| + css_files.map do |css_file| + File.open(filename = File.dirname(__FILE__)+"/css/"+css_file) do |f| + "<!-- #{css_file} -->\n"+ + "<style media='#{media}' type='text/css'>\n#{f.read}</style>\n" + end + end + end*''.gsub(/^\s+/, '') end def glossary_body - if @gen_bootstrap - object_types_dump_toc() - object_types_dump_def() - else - object_types_dump_def() - object_types_dump_toc() - end + div( + object_types_dump_toc + + object_types_dump_def + + dump_compositions + + controls, + 'glossary' + ) end - + def glossary_end - if !@gen_bootstrap - %Q{ - <script type="text/javascript"> - function toggle_class(e, c) { - if (!e) return; - var n = e.className; - var i = n.indexOf(c); - if (i == -1) { - e.className = n+' '+c; - } else { - e.className = n.slice(0, i)+n.slice(i+c.length); - } - if (document.location.toString().indexOf('#') >= 0) - document.location = document.location; // Re-scroll to the current fragment - } - function toggle_constraints() { - toggle_class(document.getElementById('glossary-doc'), 'hide-constraints'); - } - function toggle_alternates() { - toggle_class(document.getElementById('glossary-doc'), 'hide-alternates'); - } - function toggle_examples() { - toggle_class(document.getElementById('glossary-doc'), 'hide-examples'); - } - </script> - }.gsub(/^\s+/, '') - else - '' - end + %Q{ + <script type="text/javascript"> + function toggle_class(e, c) { + if (!e) return; + var n = e.className; + var i = n.indexOf(c); + if (i == -1) { + e.className = n+' '+c; + } else { + e.className = n.slice(0, i)+n.slice(i+c.length); + } + if (document.location.toString().indexOf('#') >= 0) + document.location = document.location; // Re-scroll to the current fragment + } + function toggle_facts() { + toggle_class(document.getElementById('glossary-doc'), 'hide-facts'); + } + function toggle_constraints() { + toggle_class(document.getElementById('glossary-doc'), 'hide-constraints'); + } + function toggle_alternates() { + toggle_class(document.getElementById('glossary-doc'), 'hide-alternates'); + } + function toggle_examples() { + toggle_class(document.getElementById('glossary-doc'), 'hide-examples'); + } + </script> + }.gsub(/^\s+/, '') end def object_types_dump_toc - if @gen_bootstrap - '<div class="col-md-3 glossary-sidebar">' + "\n" - else - '<div class="glossary-sidebar">' + "\n" - end + - '<h1 style="visibility: hidden">X</h1>' +"\n" + - '<ol class="glossary-toc">' + "\n" + %Q{\n<div class="glossary-toc#{@compositions.size > 0 ? ' glossary-is-toc' : ' glossary-toc-right'}">\n} + + # Don't show schema name here '<h1 style="visibility: hidden">X</h1>' +"\n" + + '<ol class="glossary-toc-list">' + "\n" + @all_object_type. - reject do |o| - o.name == '_ImplicitBooleanValueType' or - o.kind_of?(ActiveFacts::Metamodel::ValueType) && o.all_role.size == 0 or - o.kind_of?(ActiveFacts::Metamodel::TypeInheritance) - end. - map do |o| - "<li>#{termref(o.name)}</li>" - end *"\n" + "\n" + + reject do |o| + o.name == '_ImplicitBooleanValueType' or + o.kind_of?(ActiveFacts::Metamodel::ValueType) && o.all_role.size == 0 or + o.kind_of?(ActiveFacts::Metamodel::TypeInheritance) + end. + map do |o| + "<li>#{termref(o.name)}</li>" + end*"\n" + "\n</div>\n\n" + end + + def controls %Q{ </ol> <div class="glossary-controls"> + <input type="button" onclick="toggle_facts()" value="Facts" class="glossary-toggle-facts"> <input type="button" onclick="toggle_constraints()" value="Constraints" class="glossary-toggle-constraint"> <input type="button" onclick="toggle_alternates()" value="Alternates" class="glossary-toggle-alternates"> <input type="button" onclick="toggle_examples()" value="Examples" class="glossary-toggle-examples"> </div> - </div> } end - + def object_types_dump_def - if @gen_bootstrap - '<div class="col-md-5 glossary-doc hide-alternates hide-constraints" id="glossary-doc">' + "\n" - else - '<div class="glossary-doc hide-alternates hide-constraints" id="glossary-doc">' + "\n" - end + - '<h1>#{@vocabulary.name}</h1>' + "\n" + + %Q{<div class="glossary-doc #{@compositions.size > 0 ? 'glossary-is-toc' : 'glossary-toc-right'} hide-facts hide-alternates hide-constraints" id="glossary-doc">} + "\n" + + "<h1>#{@vocabulary.name}</h1>\n" + "<dl>\n" + @all_object_type. map do |o| case o when ActiveFacts::Metamodel::TypeInheritance @@ -169,15 +156,150 @@ objectified_fact_type_dump(o) else entity_type_dump(o) end end - end + + end*"\n" + "</dl>\n" + "</div>\n" end + # Each component has + # * a span for the title, + # * a tt-type if it's a value type + # * an tt-desc if it has an associated fact type + # * child nodes + def component c, klass = '' + name = c.name + title = span(name, 'term'+(c.is_mandatory ? ' mandatory' : '')) + desc = '' + type = '' + + case c + when MM::Indicator + ft = c.role.fact_type + desc = div(div(expand_reading(ft.preferred_reading, false), 'glossary-reading'), 'tt-desc') + type = div(div('boolean', 'term'), 'tt-type') + + when MM::Discriminator, + MM::Injection, + MM::ComputedValue, + MM::HashValue, + MM::SurrogateKey, + # MM::Scoping, + MM::ValidFrom # This should be an Injection + p c + debugger + print '' + # REVISIT + + when MM::Absorption + ft = c.parent_role.fact_type + preferred_reading = ft.reading_preferably_starting_with_role(c.parent_role) + desc = div(div(expand_reading(preferred_reading, false), 'glossary-reading'), 'tt-desc') + if MM::ValueType === c.object_type + name = c.column_name*'' + title = span(name, 'term'+(c.is_mandatory ? ' mandatory' : '')) + type = div(div(c.child_role.object_type.name, 'term'), 'tt-type') + #elsif c.all_member.size == 0 + # title = span(name, 'term'+(c.is_mandatory ? ' mandatory' : '')) + #else + title = span(name, 'term'+(c.is_mandatory ? ' mandatory' : '')) + end + if MM::TypeInheritance === ft + title = "as a "+title + elsif c.full_absorption + title = "fully absorbing "+title + end + if to = (c.foreign_key && c.foreign_key.composite) or + ( + composite_mappings = c.object_type.all_mapping.select{|m| m.composite} and + composite_mappings.size == 1 and # In a binary mapping, there aren't any ForeignKeys + to = composite_mappings[0].composite + ) + title = element(title, {href: '#'+composite_anchor(to)}, 'a') + end + klass = klass+' tt-list' unless c.parent_role.is_unique + + # when MM::ValueField ... Mapping works here + when MM::Mapping # A mapping that's not an absorption; usually a Composite + if MM::EntityType === (o = c.object_type) + if o.fact_type + objectified_reading = o.fact_type.preferred_reading + desc = div( + span('is where ', :keyword) + expand_reading(objectified_reading, false), + 'tt-desc' + ) + else + desc = div( + span('is identified by ', :keyword) + + o.preferred_identifier_roles.map{|r| span(r.role_name || r.name, 'term') }*', ', + 'tt-desc' + ) + end + else + desc = div('', 'tt-desc') + end + + when MM::Indicator + desc = div( + expand_reading(c.role.fact_type.preferred_reading, false), + 'tt-desc' + ) + else + # Add other special cases here + desc = div('', 'tt-desc') + end + + + div( + title + + type + + desc + + c. + all_member. + sort_by{|m| m.ordinal}. + map do |member| + component(member) + end*'', + 'tt-node'+klass + )+"\n" + end + + def dump_compositions + return '' if @compositions.empty? + + element( + @compositions.map do |c| + "\n"+ + element( + element(element(c.compositor_name + ' Composition', {href: "#{'#'}#{c.compositor_name}-composition"}, 'a'), {}, 'h2') + "\n" + + element(dump_composition(c), {}, 'div'), + {id: "#{c.compositor_name}-composition"}, + 'section' + ) + end*'', + {class: 'tabs glossary-compositions'}, + 'article' + ) + end + + def composite_anchor composite + "#{composite.composition.compositor_name}_#{composite.mapping.name.words.titlecase}" + end + + def dump_composition c + c.all_composite_by_name.map do |composite| + composite.mapping.re_rank + element( + component(composite.mapping, ' tt-outer'), + {name: composite_anchor(composite)}, + 'a' + ) + end*"&nbsp;\n" + end + def element(text, attrs, tag = 'span') "<#{tag}#{attrs.empty? ? '' : attrs.map{|k,v| " #{k}='#{v}'"}*''}>#{text}</#{tag}>" end def span(text, klass = nil) @@ -196,45 +318,45 @@ element(text, klass ? {:class => klass} : {}, 'dl') end # A definition of a term def termdef(name) - element(name, {:name => name, :class => 'object_type'}, 'a') + element(name, {:name => name, :class=>:term}, 'a') end # A reference to a defined term (excluding role adjectives) def termref(name, role_name = nil) role_name ||= name - element(role_name, {:href=>'#'+name, :class=>:object_type}, 'a') + element(role_name, {:href=>'#'+name, :class=>:term}, 'a') end # Text that should appear as part of a term (including role adjectives) def term(name) - element(name, :class=>:object_type) + element(name, :class=>:term) end def value_type_dump(o, include_alternate = true, include_facts = true, include_constraints = true) return '' if o.all_role.size == 0 or # Skip value types that are only used as supertypes o.name == '_ImplicitBooleanValueType' - + defn_term = ' <dt>' + "#{termdef(o.name)} " + (if o.supertype span('is written as ', :keyword) + termref(o.supertype.name) else - " (a fundamental data type)" + " (fundamental)" end) + "</dt>\n" defn_detail = " <dd>\n" + value_sub_types(o) + relevant_facts_and_constraints(o, include_alternate, include_facts, include_constraints) + (include_facts ? values(o) : '') + " </dd>\n" - + defn_term + defn_detail end def value_sub_types(o) o. @@ -267,18 +389,18 @@ ) end * "\n" + "\n" end def relevant_facts_and_constraints(o, include_alternate = true, include_facts = true, include_constraints = true) - o. - all_role. - map{|r| r.fact_type}. - uniq. - reject do |ft| ft.is_a?(ActiveFacts::Metamodel::LinkFactType) end. - map { |ft| [ft, " #{fact_type_with_constraints(ft, include_alternate, o, include_constraints)}"] }. - sort_by{|ft, text| [ ft.is_a?(ActiveFacts::Metamodel::TypeInheritance) ? 0 : 1, text]}. - map{|ft, text| text} * "\n" + o. + all_role. + map{|r| r.fact_type}. + uniq. + reject do |ft| ft.is_a?(ActiveFacts::Metamodel::LinkFactType) end. + map { |ft| [ft, " #{fact_type_with_constraints(ft, include_alternate, o, include_constraints)}"] }. + sort_by{|ft, text| [ ft.is_a?(ActiveFacts::Metamodel::TypeInheritance) ? 0 : 1, text]}. + map{|ft, text| text} * "\n" end def role_ref(rr, freq_con, l_adj, name, t_adj, role_name_def, literal) term_parts = [l_adj, termref(name), t_adj].compact [ @@ -297,11 +419,11 @@ span("(as #{ termref(rr.role.object_type.name, $1) })", 'keyword') } end role_ref rr, freq_con, l_adj, name, t_adj, role_name_def, literal end, - {:class => 'copula'} + {:class => 'reading'} ) end def fact_type_block(ft, include_alternates = true, wrt = nil, include_rolenames = true) div(fact_type(ft, include_alternates, wrt, include_rolenames), 'glossary-facttype') @@ -334,12 +456,13 @@ end def fact_type_with_constraints(ft, include_alternates = true, wrt = nil, include_constraints = true) if ft.entity_type div( - div(termref(ft.entity_type.name) + span(' is where ', 'keyword')) + - div(fact_type(ft, include_alternates, wrt)), + (ft.entity_type == wrt ? '' : termref(ft.entity_type.name)) + + span(' is where ', 'keyword') + + fact_type(ft, include_alternates, wrt), 'glossary-objectification' ) else fact_type_block(ft, include_alternates, wrt) end + @@ -365,72 +488,72 @@ } next '' unless reading div( element( reading.expand_with_final_presence_constraint { |*a| role_ref(*a) }, - {:class => 'copula'} + {:class => 'reading'} ), 'glossary-constraint' ) + "\n" end.compact * '' end def objectified_fact_type_dump(o, include_alternate = true, include_facts = true, include_constraints = true) defn_term = " <dt>" + "#{termdef(o.name)}" + - # " (#{span('in which', 'keyword')} #{fact_type(o.fact_type, false, nil, nil)})" + + " (objectification#{o.supertypes.size > 0 ? ', subtype' : ''})" + + # Don't display OFT inline " (#{span('in which', 'keyword')} #{fact_type(o.fact_type, false, o, nil)})" + "</dt>\n" # REVISIT: Handle separate identification defn_detail = - " <dd>\n" + - fact_type_with_constraints(o.fact_type, include_alternate, nil, include_constraints) + "\n" + + " <dd>" + + fact_type_with_constraints(o.fact_type, include_alternate, o, include_constraints) + "\n" + o.fact_type.all_role_in_order.map do |r| n = r.object_type.name div("#{termref(o.name)} involves #{span('one', 'keyword')} #{termref(r.role_name || n, n)}", "glossary-facttype") end * "\n" + "\n" + relevant_facts_and_constraints(o, include_alternate, include_facts, include_constraints) + "\n" + - " </dd>" + " </dd>\n" defn_term + defn_detail end def entity_type_dump(o, include_alternate = true, include_facts = true, include_constraints = true) pi = o.preferred_identifier supers = o.supertypes - if (supers.size > 0) # Ignore identification by a supertype: - pi = nil if pi && pi.role_sequence.all_role_ref.detect{|rr| rr.role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) } - end + pi = nil if pi && o.identifying_supertype defn_term = " <dt>" + "#{termdef(o.name)} " + - "</dt>\n" - - defn_detail = - " <dd>" + - (supers.size > 0 ? "#{span('is a kind of', 'keyword')} #{supers.map{|s| termref(s.name)}*', '}\n" : '') + if pi "#{span('is identified by', 'keyword')} " + pi.role_sequence.all_role_ref_in_order.map do |rr| - termref( - rr.role.object_type.name, - [ rr.leading_adjective, - rr.role.role_name || rr.role.object_type.name, - rr.trailing_adjective - ].compact * '-' + preferred_reading = rr.role.fact_type.preferred_reading + preferred_role_ref = preferred_reading.role_sequence.all_role_ref.detect{|rrp| rrp.role == rr.role} + term( + [ preferred_role_ref.leading_adjective, + termref(rr.role.object_type.name, preferred_role_ref.role.role_name), + preferred_role_ref.trailing_adjective + ].compact*'-' ) end * ", " + "\n" else - '' + ' (subtype)' end + + "</dt>\n" + + defn_detail = + " <dd>\n" + + (supers.size > 0 ? "#{span('is a kind of', 'keyword')} #{supers.map{|s| termref(s.name)}*', '}\n" : '') + relevant_facts_and_constraints(o, include_alternate, include_facts, include_constraints) + (include_facts ? entities(o) : '') + - " </dd>\n" - + "\n </dd>" + defn_term + defn_detail end def entities(o) return '' if o.preferred_identifier.role_sequence.all_role_ref.size > 1 # REVISIT: Composite identification @@ -464,8 +587,9 @@ ) end * "\n" + "\n" end end end + publish_generator Doc::Glossary, "Glossary generator" end end