Class: Metanorma::CollectionRenderer
- Inherits:
-
Object
- Object
- Metanorma::CollectionRenderer
- Defined in:
- lib/metanorma/collection_renderer.rb
Overview
XML collection renderer
Defined Under Namespace
Classes: Dummy
Constant Summary collapse
- FORMATS =
%i[html xml doc pdf].freeze
Class Method Summary collapse
Instance Method Summary collapse
- #concatenate(col, options) ⇒ Object
-
#coverpage ⇒ Object
populate liquid template of ARGV with metadata extracted from collection manifest.
- #docrefs(elm, builder) ⇒ Object
-
#doctype ⇒ Object
infer the flavour from the first document identifier; relaton does that.
-
#files ⇒ Object
process each file in the collection files are held in memory, and altered as postprocessing.
-
#indexfile(elm) ⇒ String
single level navigation list, with hierarchical nesting if multiple lists are needed as separate HTML fragments, multiple instances of this function will be needed, and associated to different variables in the call to @isodoc.metadata_init (including possibly an array of HTML fragments).
-
#indexfile_docref(elm, builder) ⇒ Object
uses the identifier to label documents; other attributes (title) can be looked up in @files[:bibdata].
- #indexfile_title(elm) ⇒ String
-
#initialize(xml, folder, options = {}) ⇒ CollectionRenderer
constructor
This is only going to render the HTML collection We presuppose that the bibdata of the document is equivalent to that of the collection, and that the flavour gem can sensibly process it.
-
#isodoc ⇒ Object
The isodoc class for the metanorma flavour we are using.
- #ns(xpath) ⇒ Object
-
#read_anchors(xml) ⇒ Object
map locality type and label (e.g. “clause” “1”) to id = anchor for a document.
-
#read_files(path) ⇒ Hash{String=>Hash}
hash for each document in collection of document identifier to: document reference (fileref or id), type of document reference, and bibdata entry for that file.
- #ref_file(ref, read) ⇒ Array<String, nil>
-
#targetfile(data, read = false) ⇒ Array<String, nil>
return file contents + output filename for each file in the collection, given a docref entry.
-
#update_anchors(bib, docxml, _id) ⇒ Object
if there is a crossref to another document, with no anchor, retrieve the anchor given the locality, and insert it into the crossref.
- #update_bibitem(bib, identifier) ⇒ Object
-
#update_xrefs(file, identifier) ⇒ String
TODO: update crossreferences to other files in the selection repo(current-metanorma-collection/ISO 17301-1:2016) replaced by bibdata of “ISO 17301-1:2016” in situ as bibitem Any erefs to that bibitem id are replaced with relative URL Preferably with anchor, and is a job to realise dynamic lookup of localities.
- #xml_file(id, read) ⇒ Array<String, nil>
Constructor Details
#initialize(xml, folder, options = {}) ⇒ CollectionRenderer
This is only going to render the HTML collection We presuppose that the bibdata of the document is equivalent to that of the collection, and that the flavour gem can sensibly process it. We may need to enhance metadata in the flavour gems isodoc/metadata.rb with collection metadata
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/metanorma/collection_renderer.rb', line 22 def initialize(xml, folder, = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength @xml = Nokogiri::XML xml # @xml is the collection manifest @lang = @xml&.at(ns("//bibdata/language"))&.text || "en" @script = @xml&.at(ns("//bibdata/script"))&.text || "Latn" @doctype = doctype require "metanorma-#{@doctype}" # output processor for flavour @isodoc = isodoc @outdir = [:output_folder] @coverpage = [:coverpage] @format = [:format] # list of files in the collection @files = read_files folder FileUtils.rm_rf @outdir FileUtils.mkdir_p @outdir end |
Class Method Details
.render(col, options = {}) ⇒ Object
48 49 50 51 52 53 54 |
# File 'lib/metanorma/collection_renderer.rb', line 48 def self.render(col, = {}) folder = File.dirname col.file cr = new(col.to_xml, folder, ) cr.files cr.concatenate(col, ) cr.coverpage if [:format]&.include?(:html) end |
Instance Method Details
#concatenate(col, options) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/metanorma/collection_renderer.rb', line 56 def concatenate(col, ) [:format].each do |e| next unless %i(presentation xml).include?(e) ext = e == :presentation ? "presentation.xml" : e.to_s out = col.clone out.directives << "documents-inline" out.documents.keys.each do |id| filename = @files[id][:outputs][e] out.documents[id] = Metanorma::Document.raw_file(filename) end File.open(File.join(@outdir, "collection.#{ext}"), "w:UTF-8") { |f| f.write(out.to_xml) } end end |
#coverpage ⇒ Object
populate liquid template of ARGV with metadata extracted from collection manifest
146 147 148 149 150 |
# File 'lib/metanorma/collection_renderer.rb', line 146 def coverpage File.open(File.join(@outdir, "index.html"), "w:UTF-8") do |f| f.write @isodoc.populate_template(File.read(@coverpage)) end end |
#docrefs(elm, builder) ⇒ Object
173 174 175 176 177 178 179 180 181 |
# File 'lib/metanorma/collection_renderer.rb', line 173 def docrefs(elm, builder) elm.xpath(ns("./docref")).each do |d| identifier = d.at(ns("./identifier")).text link = if d["fileref"] then d["fileref"].sub(/\.xml$/, ".html") else d["id"] + ".html" end builder.li { builder.a identifier, href: link } end end |
#doctype ⇒ Object
infer the flavour from the first document identifier; relaton does that
93 94 95 96 97 98 99 100 101 102 |
# File 'lib/metanorma/collection_renderer.rb', line 93 def doctype if (docid = @xml&.at(ns("//bibdata/docidentifier/@type"))&.text) dt = docid.downcase elsif (docid = @xml&.at(ns("//bibdata/docidentifier"))&.text) dt = docid.sub(/\s.*$/, "").lowercase else return "standoc" end @registry = Metanorma::Registry.instance @registry.alias(dt.to_sym)&.to_s || dt end |
#files ⇒ Object
process each file in the collection files are held in memory, and altered as postprocessing
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
# File 'lib/metanorma/collection_renderer.rb', line 296 def files # rubocop:disable Metrics/AbcSize, Metrics/MethodLength @files.each do |identifier, x| file, filename = targetfile(x, true) file = update_xrefs(file, identifier) Tempfile.open(["collection", ".xml"], encoding: "utf-8") do |f| f.write(file) f.close # warn "metanorma compile -x html #{f.path}" c = Compile.new c.compile f.path, format: :asciidoc, extension_keys: @format @files[identifier][:outputs] = {} @format.each do |e| ext = c.processor.output_formats[e] fn = File.basename(filename).sub(/(?<=\.)[^\.]+$/, ext.to_s) FileUtils.mv f.path.sub(/\.xml$/, ".#{ext}"), File.join(@outdir, fn) @files[identifier][:outputs][e] = File.join(@outdir, fn) end end end end |
#indexfile(elm) ⇒ String
single level navigation list, with hierarchical nesting if multiple lists are needed as separate HTML fragments, multiple instances of this function will be needed, and associated to different variables in the call to @isodoc.metadata_init (including possibly an array of HTML fragments)
191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/metanorma/collection_renderer.rb', line 191 def indexfile(elm) Nokogiri::HTML::Builder.new do |b| b.ul do b.li indexfile_title(elm) indexfile_docref(elm, b) elm.xpath(ns("./manifest")).each do |d| b << indexfile(d) end end end.doc.root.to_html end |
#indexfile_docref(elm, builder) ⇒ Object
uses the identifier to label documents; other attributes (title) can be looked up in @files[:bibdata]
165 166 167 168 169 |
# File 'lib/metanorma/collection_renderer.rb', line 165 def indexfile_docref(elm, builder) return "" unless elm.at(ns("./docref")) builder.ul { |b| docrefs(elm, b) } end |
#indexfile_title(elm) ⇒ String
154 155 156 157 158 |
# File 'lib/metanorma/collection_renderer.rb', line 154 def indexfile_title(elm) lvl = elm&.at(ns("./level"))&.text&.capitalize lbl = elm&.at(ns("./title"))&.text "#{lvl}#{lvl && lbl ? ': ' : ''}#{lbl}" end |
#isodoc ⇒ Object
The isodoc class for the metanorma flavour we are using
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/metanorma/collection_renderer.rb', line 76 def isodoc # rubocop:disable Metrics/MethodLength x = Asciidoctor.load nil, backend: @doctype.to_sym isodoc = x.converter.html_converter(Dummy.new) isodoc.i18n_init(@lang, @script) # read in internationalisation # create the @meta class of isodoc, with "navigation" set to the index bar # extracted from the manifest nav = indexfile(@xml.at(ns("//manifest"))) i18n = isodoc.i18n i18n.set(:navigation, nav) isodoc.(@lang, @script, i18n) # populate the @meta class of isodoc with the various metadata fields # native to the flavour; used to populate Liquid isodoc.info(@xml, nil) isodoc end |
#ns(xpath) ⇒ Object
104 105 106 |
# File 'lib/metanorma/collection_renderer.rb', line 104 def ns(xpath) IsoDoc::Convert.new({}).ns(xpath) end |
#read_anchors(xml) ⇒ Object
map locality type and label (e.g. “clause” “1”) to id = anchor for a document
132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/metanorma/collection_renderer.rb', line 132 def read_anchors(xml) ret = {} xrefs = @isodoc.xref_init(@lang, @script, @isodoc, @isodoc.i18n, {}) xrefs.parse xml xrefs.get.each do |k, v| v[:label] && v[:type] || next ret[v[:type]] ||= {} ret[v[:type]][v[:label]] = k end ret end |
#read_files(path) ⇒ Hash{String=>Hash}
hash for each document in collection of document identifier to: document reference (fileref or id), type of document reference, and bibdata entry for that file
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/metanorma/collection_renderer.rb', line 113 def read_files(path) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength files = {} @xml.xpath(ns("//docref")).each do |d| identifier = d.at(ns("./identifier")).text files[identifier] = if d["fileref"] { type: "fileref", ref: File.join(path, d["fileref"]) } else { type: "id", ref: d["id"] } end file, _filename = targetfile(files[identifier], true) xml = Nokogiri::XML(file) files[identifier][:anchors] = read_anchors(xml) files[identifier][:bibdata] = xml.at(ns("//bibdata")) end files end |
#ref_file(ref, read) ⇒ Array<String, nil>
217 218 219 220 221 |
# File 'lib/metanorma/collection_renderer.rb', line 217 def ref_file(ref, read) file = File.read(ref, encoding: "utf-8") if read filename = ref.sub(/\.xml$/, ".html") [file, filename] end |
#targetfile(data, read = false) ⇒ Array<String, nil>
return file contents + output filename for each file in the collection, given a docref entry
208 209 210 211 212 |
# File 'lib/metanorma/collection_renderer.rb', line 208 def targetfile(data, read = false) if data[:type] == "fileref" then ref_file data[:ref], read else xml_file data[:id], read end end |
#update_anchors(bib, docxml, _id) ⇒ Object
if there is a crossref to another document, with no anchor, retrieve the anchor given the locality, and insert it into the crossref
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/metanorma/collection_renderer.rb', line 277 def update_anchors(bib, docxml, _id) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize docid = bib&.at(ns("./docidentifier"))&.text docxml.xpath("//xmlns:eref[@citeas = '#{docid}']").each do |e| e.at(ns(".//locality[@type = 'anchor']")).nil? || next ins = e.at(ns("./localityStack")) || next type = ins&.at(ns("./locality/@type"))&.text ref = ins&.at(ns("./locality/referenceFrom"))&.text (anchor = @files[docid][:anchors][type][ref]) || next ref_from = Nokogiri::XML::Node.new "referenceFrom", bib ref_from.content = anchor.sub(/^_/, "") locality = Nokogiri::XML::Node.new "locality", bib locality[:type] = "anchor" locality.add_child ref_from ins << locality end end |
#update_bibitem(bib, identifier) ⇒ Object
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/metanorma/collection_renderer.rb', line 234 def update_bibitem(bib, identifier) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength docid = bib&.at(ns("./docidentifier"))&.text unless @files[docid] warn "Cannot find crossreference to document #{docid} in document "\ "#{identifier}!" abort end id = bib["id"] newbib = bib.replace(@files[docid][:bibdata]) newbib.name = "bibitem" newbib["id"] = id newbib&.at(ns("./ext"))&.remove _file, url = targetfile(@files[docid], false) uri_node = Nokogiri::XML::Node.new "uri", newbib uri_node[:type] = "citation" uri_node.content = url newbib.at(ns("./docidentifier")).previous = uri_node end |
#update_xrefs(file, identifier) ⇒ String
TODO: update crossreferences to other files in the selection repo(current-metanorma-collection/ISO 17301-1:2016) replaced by bibdata of “ISO 17301-1:2016” in situ as bibitem Any erefs to that bibitem id are replaced with relative URL Preferably with anchor, and is a job to realise dynamic lookup of localities
263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/metanorma/collection_renderer.rb', line 263 def update_xrefs(file, identifier) docxml = Nokogiri::XML(file) docxml.xpath(ns("//bibitem[not(ancestor::bibitem)]")).each do |b| docid = b&.at(ns("./docidentifier[@type = 'repository']"))&.text next unless docid && %r{^current-metanorma-collection/}.match(docid) update_bibitem(b, identifier) update_anchors(b, docxml, docid) end docxml.to_xml end |
#xml_file(id, read) ⇒ Array<String, nil>
226 227 228 229 230 |
# File 'lib/metanorma/collection_renderer.rb', line 226 def xml_file(id, read) file = @xml.at(ns("//doc-container[@id = '#{id}']")).to_xml if read filename = id + ".html" [file, filename] end |