metanorma 1.1.2 → 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +0 -4
- data/lib/metanorma.rb +6 -1
- data/lib/metanorma/collection.rb +182 -0
- data/lib/metanorma/collection_manifest.rb +111 -0
- data/lib/metanorma/collection_renderer.rb +312 -0
- data/lib/metanorma/document.rb +75 -5
- data/lib/metanorma/output/xslfo.rb +7 -1
- data/lib/metanorma/version.rb +1 -1
- data/metanorma.gemspec +3 -1
- metadata +35 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cb71b62025d938d52251d5bfb6d1911b3de32299500e12b8fa414a63520a86c3
|
4
|
+
data.tar.gz: df9deea25ea6e374426ad40b3146c63610251873ec652142acffc9274af5856c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6404a68d6371fb24f27e36cc69dc0479da638ec79ef3bb48edf1939d6ec3beffb36c5b86f1402bc238a01ed05481dac255a30ac043cf8a806b24d464e5a16592
|
7
|
+
data.tar.gz: 1881fb5c6c4e3c1d6239b1a4c34dca109f4bc489d33942d42c8e09c9177aef29ce557219aeff7656458bd6e2de918859155728b908ba64c7d6c68c8f1bbdb942
|
data/.rubocop.yml
CHANGED
data/lib/metanorma.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "metanorma/version"
|
2
4
|
require "asciidoctor"
|
3
5
|
require "metanorma/util"
|
@@ -8,7 +10,10 @@ require "metanorma/registry"
|
|
8
10
|
require "metanorma/processor"
|
9
11
|
require "metanorma/asciidoctor_extensions"
|
10
12
|
require "metanorma/compile"
|
13
|
+
require "metanorma/collection"
|
14
|
+
require "metanorma/collection_renderer"
|
15
|
+
require "metanorma/document"
|
11
16
|
|
17
|
+
# Metanorma module
|
12
18
|
module Metanorma
|
13
|
-
|
14
19
|
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "relaton"
|
4
|
+
require "relaton/cli"
|
5
|
+
require "metanorma/collection_manifest"
|
6
|
+
|
7
|
+
module Metanorma
|
8
|
+
# Metanorma collection of documents
|
9
|
+
class Collection
|
10
|
+
# @return [String]
|
11
|
+
attr_reader :file
|
12
|
+
|
13
|
+
# @return [Array<String>] documents-inline to inject the XML into
|
14
|
+
# the collection manifest; documents-external to keeps them outside
|
15
|
+
attr_reader :directives
|
16
|
+
|
17
|
+
# @return [Hash<String, Metanorma::Document>]
|
18
|
+
attr_reader :documents
|
19
|
+
|
20
|
+
# @param file [String] path to source file
|
21
|
+
# @param directives [Array<String>] documents-inline to inject the XML into
|
22
|
+
# the collection manifest; documents-external to keeps them outside
|
23
|
+
# @param bibdata [RelatonBib::BibliographicItem]
|
24
|
+
# @param manifest [Metanorma::CollectionManifest]
|
25
|
+
# @param documents [Hash<String, Metanorma::Document>]
|
26
|
+
# @param prefatory [String]
|
27
|
+
# @param final [String]
|
28
|
+
# rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
29
|
+
def initialize(**args)
|
30
|
+
@file = args[:file]
|
31
|
+
@directives = args[:directives] || []
|
32
|
+
@bibdata = args[:bibdata]
|
33
|
+
@manifest = args[:manifest]
|
34
|
+
@manifest.collection = self
|
35
|
+
@documents = args[:documents] || {}
|
36
|
+
if @documents.any? && !@directives.include?("documents-inline")
|
37
|
+
@directives << "documents-inline"
|
38
|
+
end
|
39
|
+
@documents.merge! @manifest.documents(File.dirname(@file))
|
40
|
+
@prefatory = args[:prefatory]
|
41
|
+
@final = args[:final]
|
42
|
+
end
|
43
|
+
# rubocop:enable Metrics/AbcSize,Metrics/MethodLength
|
44
|
+
|
45
|
+
# @return [String] XML
|
46
|
+
def to_xml
|
47
|
+
Nokogiri::XML::Builder.new do |xml|
|
48
|
+
xml.send("metanorma-collection",
|
49
|
+
"xmlns" => "http://metanorma.org") do |mc|
|
50
|
+
@bibdata.to_xml mc, bibdata: true, date_format: :full
|
51
|
+
@manifest.to_xml mc
|
52
|
+
content_to_xml "prefatory", mc
|
53
|
+
doccontainer mc
|
54
|
+
content_to_xml "final", mc
|
55
|
+
end
|
56
|
+
end.to_xml
|
57
|
+
end
|
58
|
+
|
59
|
+
def render(opts)
|
60
|
+
CollectionRenderer.render self, opts
|
61
|
+
end
|
62
|
+
|
63
|
+
class << self
|
64
|
+
# @param file [String]
|
65
|
+
# @return [RelatonBib::BibliographicItem,
|
66
|
+
# RelatonIso::IsoBibliographicItem]
|
67
|
+
def parse(file)
|
68
|
+
case file
|
69
|
+
when /\.xml$/ then parse_xml(file)
|
70
|
+
when /.ya?ml$/ then parse_yaml(file)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def parse_xml(file)
|
77
|
+
xml = Nokogiri::XML File.read(file, encoding: "UTF-8")
|
78
|
+
if (b = xml.at("/xmlns:metanorma-collection/xmlns:bibdata"))
|
79
|
+
bd = Relaton::Cli.parse_xml b
|
80
|
+
end
|
81
|
+
mnf_xml = xml.at("/xmlns:metanorma-collection/xmlns:manifest")
|
82
|
+
mnf = CollectionManifest.from_xml mnf_xml
|
83
|
+
pref = pref_final_content xml.at("//xmlns:prefatory-content")
|
84
|
+
fnl = pref_final_content xml.at("//xmlns:final-content")
|
85
|
+
new(file: file, bibdata: bd, manifest: mnf,
|
86
|
+
documents: docs_from_xml(xml, mnf), prefatory: pref, final: fnl)
|
87
|
+
end
|
88
|
+
|
89
|
+
def parse_yaml(file)
|
90
|
+
yaml = YAML.load_file file
|
91
|
+
if yaml["bibdata"]
|
92
|
+
bd = Relaton::Cli::YAMLConvertor.convert_single_file yaml["bibdata"]
|
93
|
+
end
|
94
|
+
mnf = CollectionManifest.from_yaml yaml["manifest"]
|
95
|
+
dirs = yaml["directives"]
|
96
|
+
pref = yaml["prefatory-content"]
|
97
|
+
fnl = yaml["final-content"]
|
98
|
+
new(file: file, directives: dirs, bibdata: bd, manifest: mnf,
|
99
|
+
prefatory: pref, final: fnl)
|
100
|
+
end
|
101
|
+
|
102
|
+
# @param xml [Nokogiri::XML::Document]
|
103
|
+
# @parma mnf [Metanorma::CollectionManifest]
|
104
|
+
# @return [Hash{String=>Metanorma::Document}]
|
105
|
+
def docs_from_xml(xml, mnf)
|
106
|
+
xml.xpath("//xmlns:doc-container/*/xmlns:bibdata")
|
107
|
+
.each_with_object({}) do |b, m|
|
108
|
+
bd = Relaton::Cli.parse_xml b
|
109
|
+
docref = mnf.docref_by_id bd.docidentifier.first.id
|
110
|
+
m[docref["identifier"]] = Document.new bd, docref["fileref"]
|
111
|
+
m
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# @param xml [Nokogiri::XML::Element, nil]
|
116
|
+
# @return [String, nil]
|
117
|
+
def pref_final_content(xml)
|
118
|
+
return unless xml
|
119
|
+
|
120
|
+
<<~CONT
|
121
|
+
|
122
|
+
== #{xml.at('title')&.text}
|
123
|
+
#{xml.at('p')&.text}
|
124
|
+
CONT
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
# @return [String, nil]
|
131
|
+
attr_reader :prefatory, :final
|
132
|
+
|
133
|
+
# @return [String]
|
134
|
+
def dummy_header
|
135
|
+
<<~DUMMY
|
136
|
+
= X
|
137
|
+
A
|
138
|
+
|
139
|
+
DUMMY
|
140
|
+
end
|
141
|
+
|
142
|
+
# @param elm [String] 'prefatory' or 'final'
|
143
|
+
# @param builder [Nokogiri::XML::Builder]
|
144
|
+
def content_to_xml(elm, builder)
|
145
|
+
return unless (cnt = send(elm))
|
146
|
+
|
147
|
+
require "metanorma-#{doctype}"
|
148
|
+
out = sections(dummy_header + cnt)
|
149
|
+
builder.send(elm + "-content") { |b| b << out }
|
150
|
+
end
|
151
|
+
|
152
|
+
# @param cnt [String] prefatory/final content
|
153
|
+
# @return [String] XML
|
154
|
+
def sections(cnt)
|
155
|
+
c = Asciidoctor.convert(cnt, backend: doctype.to_sym, header_footer: true)
|
156
|
+
Nokogiri::XML(c).at("//xmlns:sections").children.to_xml
|
157
|
+
end
|
158
|
+
|
159
|
+
# @param builder [Nokogiri::XML::Builder]
|
160
|
+
def doccontainer(builder)
|
161
|
+
return unless Array(@directives).include? "documents-inline"
|
162
|
+
|
163
|
+
documents.each_with_index do |(_, d), i|
|
164
|
+
id = format("doc%<index>09d", index: i)
|
165
|
+
builder.send("doc-container", id: id) { |b| d.to_xml b }
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# @return [String]
|
170
|
+
def doctype
|
171
|
+
@doctype ||= fetch_doctype || "standoc"
|
172
|
+
end
|
173
|
+
|
174
|
+
# @return [String]
|
175
|
+
def fetch_doctype
|
176
|
+
docid = @bibdata.docidentifier.first
|
177
|
+
return unless docid
|
178
|
+
|
179
|
+
docid.type&.downcase || docid.id&.sub(/\s.*$/, "")&.downcase
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Metanorma
|
4
|
+
# Metanorma collection's manifest
|
5
|
+
class CollectionManifest
|
6
|
+
# @return [Metanorma::Collection]
|
7
|
+
attr_reader :collection
|
8
|
+
|
9
|
+
# @param level [String]
|
10
|
+
# @param title [String, nil]
|
11
|
+
# @param docref [Array<Hash{String=>String}>]
|
12
|
+
# @param manifest [Array<Metanorma::CollectionManifest>]
|
13
|
+
def initialize(level, title = nil, docref = [], manifest = [])
|
14
|
+
@level = level
|
15
|
+
@title = title
|
16
|
+
@docref = docref
|
17
|
+
@manifest = manifest
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# @param mnf [Nokogiri::XML::Element]
|
22
|
+
# @return [Metanorma::CollectionManifest]
|
23
|
+
def from_yaml(mnf)
|
24
|
+
manifest = RelatonBib::HashConverter.array(mnf["manifest"]).map do |m|
|
25
|
+
from_yaml m
|
26
|
+
end
|
27
|
+
docref = RelatonBib::HashConverter.array mnf["docref"]
|
28
|
+
new(mnf["level"], mnf["title"], docref, manifest)
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param mnf [Nokogiri::XML::Element]
|
32
|
+
# @return [Metanorma::CollectionManifest]
|
33
|
+
def from_xml(mnf)
|
34
|
+
level = mnf.at("level").text
|
35
|
+
title = mnf.at("title")&.text
|
36
|
+
manifest = mnf.xpath("xmlns:manifest").map { |m| from_xml(m) }
|
37
|
+
new(level, title, parse_docref(mnf), manifest)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# @param mnf [Nokogiri::XML::Element]
|
43
|
+
# @return [Hash{String=>String}]
|
44
|
+
def parse_docref(mnf)
|
45
|
+
mnf.xpath("xmlns:docref").map do |dr|
|
46
|
+
h = { "identifier" => dr.at("identifier").text }
|
47
|
+
h["fileref"] = dr[:fileref] if dr[:fileref]
|
48
|
+
h
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# @param col [Metanorma::Collection]
|
54
|
+
def collection=(col)
|
55
|
+
@collection = col
|
56
|
+
@manifest.each { |mnf| mnf.collection = col }
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param dir [String] path to coolection
|
60
|
+
# @return [Hash<String, Metanorma::Document>]
|
61
|
+
def documents(dir = "")
|
62
|
+
docs = @docref.each_with_object({}) do |dr, m|
|
63
|
+
next m unless dr["fileref"]
|
64
|
+
|
65
|
+
m[dr["identifier"]] = Document.parse_file File.join(dir, dr["fileref"])
|
66
|
+
m
|
67
|
+
end
|
68
|
+
@manifest.reduce(docs) do |mem, mnf|
|
69
|
+
mem.merge mnf.documents(dir)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# @param builder [Nokogiri::XML::Builder]
|
74
|
+
def to_xml(builder)
|
75
|
+
builder.manifest do |b|
|
76
|
+
b.level @level
|
77
|
+
b.title @title if @title
|
78
|
+
docref_to_xml b
|
79
|
+
@manifest.each { |m| m.to_xml b }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [Array<Hash{String=>String}>]
|
84
|
+
def docrefs
|
85
|
+
return @docrefs if @docrefs
|
86
|
+
|
87
|
+
drfs = @docref.map { |dr| dr }
|
88
|
+
@manifest.reduce(drfs) { |mem, mnf| mem + mnf.docrefs }
|
89
|
+
end
|
90
|
+
|
91
|
+
def docref_by_id(docid)
|
92
|
+
refs = docrefs
|
93
|
+
dref = refs.detect { |k| k["identifier"] == docid }
|
94
|
+
dref || docrefs.detect { |k| /^#{k["identifier"]}/ =~ docid }
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
# @param builder [Nokogiri::XML::Builder]
|
100
|
+
def docref_to_xml(builder)
|
101
|
+
@docref.each do |dr|
|
102
|
+
drf = builder.docref { |b| b.identifier dr["identifier"] }
|
103
|
+
drf[:fileref] = dr["fileref"]
|
104
|
+
if collection.directives.include?("documents-inline")
|
105
|
+
id = collection.documents.find_index { |k, _| k == dr["identifier"] }
|
106
|
+
drf[:id] = format("doc%<index>09d", index: id)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,312 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "isodoc"
|
4
|
+
|
5
|
+
module Metanorma
|
6
|
+
# XML collection renderer
|
7
|
+
class CollectionRenderer
|
8
|
+
FORMATS = %i[html xml doc pdf].freeze
|
9
|
+
|
10
|
+
# This is only going to render the HTML collection
|
11
|
+
# @param xml [Metanorma::Collection] input XML collection
|
12
|
+
# @param folder [String] input folder
|
13
|
+
# @param options [Hash]
|
14
|
+
# @option options [String] :coverpage cover page HTML (Liquid template)
|
15
|
+
# @option options [Array<Symbol>] :format list of formats (xml,html,doc,pdf)
|
16
|
+
# @option options [String] :ourput_folder output directory
|
17
|
+
#
|
18
|
+
# We presuppose that the bibdata of the document is equivalent to that of
|
19
|
+
# the collection, and that the flavour gem can sensibly process it. We may
|
20
|
+
# need to enhance metadata in the flavour gems isodoc/metadata.rb with
|
21
|
+
# collection metadata
|
22
|
+
def initialize(xml, folder, options = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
23
|
+
check_options options
|
24
|
+
@xml = Nokogiri::XML xml # @xml is the collection manifest
|
25
|
+
@lang = @xml&.at(ns("//bibdata/language"))&.text || "en"
|
26
|
+
@script = @xml&.at(ns("//bibdata/script"))&.text || "Latn"
|
27
|
+
@doctype = doctype
|
28
|
+
require "metanorma-#{@doctype}"
|
29
|
+
|
30
|
+
# output processor for flavour
|
31
|
+
@isodoc = isodoc
|
32
|
+
|
33
|
+
@outdir = options[:output_folder]
|
34
|
+
@coverpage = options[:coverpage]
|
35
|
+
@format = options[:format]
|
36
|
+
|
37
|
+
# list of files in the collection
|
38
|
+
@files = read_files folder
|
39
|
+
FileUtils.rm_rf @outdir
|
40
|
+
FileUtils.mkdir_p @outdir
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param col [Metanorma::Collection] XML collection
|
44
|
+
# @param options [Hash]
|
45
|
+
# @option options [String] :coverpage cover page HTML (Liquid template)
|
46
|
+
# @option options [Array<Synbol>] :format list of formats
|
47
|
+
# @option options [Strong] :ourput_folder output directory
|
48
|
+
def self.render(col, options = {})
|
49
|
+
folder = File.dirname col.file
|
50
|
+
cr = new(col.to_xml, folder, options)
|
51
|
+
cr.files
|
52
|
+
cr.coverpage if options[:format]&.include?(:html)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Dummy class
|
56
|
+
class Dummy
|
57
|
+
def attr(_xyz); end
|
58
|
+
end
|
59
|
+
|
60
|
+
# The isodoc class for the metanorma flavour we are using
|
61
|
+
def isodoc # rubocop:disable Metrics/MethodLength
|
62
|
+
x = Asciidoctor.load nil, backend: @doctype.to_sym
|
63
|
+
isodoc = x.converter.html_converter(Dummy.new)
|
64
|
+
isodoc.i18n_init(@lang, @script) # read in internationalisation
|
65
|
+
# create the @meta class of isodoc, with "navigation" set to the index bar
|
66
|
+
# extracted from the manifest
|
67
|
+
nav = indexfile(@xml.at(ns("//manifest")))
|
68
|
+
i18n = isodoc.i18n
|
69
|
+
i18n.set(:navigation, nav)
|
70
|
+
isodoc.metadata_init(@lang, @script, i18n)
|
71
|
+
# populate the @meta class of isodoc with the various metadata fields
|
72
|
+
# native to the flavour; used to populate Liquid
|
73
|
+
isodoc.info(@xml, nil)
|
74
|
+
isodoc
|
75
|
+
end
|
76
|
+
|
77
|
+
# infer the flavour from the first document identifier; relaton does that
|
78
|
+
def doctype
|
79
|
+
if (docid = @xml&.at(ns("//bibdata/docidentifier/@type"))&.text)
|
80
|
+
dt = docid.downcase
|
81
|
+
elsif (docid = @xml&.at(ns("//bibdata/docidentifier"))&.text)
|
82
|
+
dt = docid.sub(/\s.*$/, "").lowercase
|
83
|
+
else return "standoc"
|
84
|
+
end
|
85
|
+
@registry = Metanorma::Registry.instance
|
86
|
+
@registry.alias(dt.to_sym)&.to_s || dt
|
87
|
+
end
|
88
|
+
|
89
|
+
def ns(xpath)
|
90
|
+
IsoDoc::Convert.new({}).ns(xpath)
|
91
|
+
end
|
92
|
+
|
93
|
+
# hash for each document in collection of document identifier to:
|
94
|
+
# document reference (fileref or id), type of document reference,
|
95
|
+
# and bibdata entry for that file
|
96
|
+
# @param path [String] path to collection
|
97
|
+
# @return [Hash{String=>Hash}]
|
98
|
+
def read_files(path) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
99
|
+
files = {}
|
100
|
+
@xml.xpath(ns("//docref")).each do |d|
|
101
|
+
identifier = d.at(ns("./identifier")).text
|
102
|
+
files[identifier] = if d["fileref"]
|
103
|
+
{ type: "fileref",
|
104
|
+
ref: File.join(path, d["fileref"]) }
|
105
|
+
else { type: "id", ref: d["id"] }
|
106
|
+
end
|
107
|
+
file, _filename = targetfile(files[identifier], true)
|
108
|
+
xml = Nokogiri::XML(file)
|
109
|
+
files[identifier][:anchors] = read_anchors(xml)
|
110
|
+
files[identifier][:bibdata] = xml.at(ns("//bibdata"))
|
111
|
+
end
|
112
|
+
files
|
113
|
+
end
|
114
|
+
|
115
|
+
# map locality type and label (e.g. "clause" "1") to id = anchor for
|
116
|
+
# a document
|
117
|
+
def read_anchors(xml)
|
118
|
+
ret = {}
|
119
|
+
xrefs = @isodoc.xref_init(@lang, @script, @isodoc, @isodoc.i18n, {})
|
120
|
+
xrefs.parse xml
|
121
|
+
xrefs.get.each do |k, v|
|
122
|
+
v[:label] && v[:type] || next
|
123
|
+
ret[v[:type]] ||= {}
|
124
|
+
ret[v[:type]][v[:label]] = k
|
125
|
+
end
|
126
|
+
ret
|
127
|
+
end
|
128
|
+
|
129
|
+
# populate liquid template of ARGV[1] with metadata extracted from
|
130
|
+
# collection manifest
|
131
|
+
def coverpage
|
132
|
+
File.open(File.join(@outdir, "index.html"), "w:UTF-8") do |f|
|
133
|
+
f.write @isodoc.populate_template(File.read(@coverpage))
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# @param elm [Nokogiri::XML::Element]
|
138
|
+
# @return [String]
|
139
|
+
def indexfile_title(elm)
|
140
|
+
lvl = elm&.at(ns("./level"))&.text&.capitalize
|
141
|
+
lbl = elm&.at(ns("./title"))&.text
|
142
|
+
"#{lvl}#{lvl && lbl ? ': ' : ''}#{lbl}"
|
143
|
+
end
|
144
|
+
|
145
|
+
# uses the identifier to label documents; other attributes (title) can be
|
146
|
+
# looked up in @files[id][:bibdata]
|
147
|
+
#
|
148
|
+
# @param elm [Nokogiri::XML::Element]
|
149
|
+
# @param builder [Nokogiri::XML::Builder]
|
150
|
+
def indexfile_docref(elm, builder)
|
151
|
+
return "" unless elm.at(ns("./docref"))
|
152
|
+
|
153
|
+
builder.ul { |b| docrefs(elm, b) }
|
154
|
+
end
|
155
|
+
|
156
|
+
# @param elm [Nokogiri::XML::Element]
|
157
|
+
# @param builder [Nokogiri::XML::Builder]
|
158
|
+
def docrefs(elm, builder)
|
159
|
+
elm.xpath(ns("./docref")).each do |d|
|
160
|
+
identifier = d.at(ns("./identifier")).text
|
161
|
+
link = if d["fileref"] then d["fileref"].sub(/\.xml$/, ".html")
|
162
|
+
else d["id"] + ".html"
|
163
|
+
end
|
164
|
+
builder.li { builder.a identifier, href: link }
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# single level navigation list, with hierarchical nesting
|
169
|
+
# if multiple lists are needed as separate HTML fragments, multiple
|
170
|
+
# instances of this function will be needed,
|
171
|
+
# and associated to different variables in the call to @isodoc.metadata_init
|
172
|
+
# (including possibly an array of HTML fragments)
|
173
|
+
#
|
174
|
+
# @param elm [Nokogiri::XML::Element]
|
175
|
+
# @return [String] XML
|
176
|
+
def indexfile(elm)
|
177
|
+
Nokogiri::HTML::Builder.new do |b|
|
178
|
+
b.ul do
|
179
|
+
b.li indexfile_title(elm)
|
180
|
+
indexfile_docref(elm, b)
|
181
|
+
elm.xpath(ns("./manifest")).each do |d|
|
182
|
+
b << indexfile(d)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end.doc.root.to_html
|
186
|
+
end
|
187
|
+
|
188
|
+
# return file contents + output filename for each file in the collection,
|
189
|
+
# given a docref entry
|
190
|
+
# @param data [Hash]
|
191
|
+
# @param read [Boolean]
|
192
|
+
# @return [Array<String, nil>]
|
193
|
+
def targetfile(data, read = false)
|
194
|
+
if data[:type] == "fileref" then ref_file data[:ref], read
|
195
|
+
else xml_file data[:id], read
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# @param ref [String]
|
200
|
+
# @param read [Boolean]
|
201
|
+
# @return [Array<String, nil>]
|
202
|
+
def ref_file(ref, read)
|
203
|
+
file = File.read(ref, encoding: "utf-8") if read
|
204
|
+
filename = ref.sub(/\.xml$/, ".html")
|
205
|
+
[file, filename]
|
206
|
+
end
|
207
|
+
|
208
|
+
# @param id [String]
|
209
|
+
# @param read [Boolean]
|
210
|
+
# @return [Array<String, nil>]
|
211
|
+
def xml_file(id, read)
|
212
|
+
file = @xml.at(ns("//doc-container[@id = '#{id}']")).to_xml if read
|
213
|
+
filename = id + ".html"
|
214
|
+
[file, filename]
|
215
|
+
end
|
216
|
+
|
217
|
+
# @param bib [Nokogiri::XML::Element]
|
218
|
+
# @param identifier [String]
|
219
|
+
def update_bibitem(bib, identifier) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
220
|
+
docid = bib&.at(ns("./docidentifier"))&.text
|
221
|
+
unless @files[docid]
|
222
|
+
warn "Cannot find crossreference to document #{docid} in document "\
|
223
|
+
"#{identifier}!"
|
224
|
+
abort
|
225
|
+
end
|
226
|
+
id = bib["id"]
|
227
|
+
newbib = bib.replace(@files[docid][:bibdata])
|
228
|
+
newbib.name = "bibitem"
|
229
|
+
newbib["id"] = id
|
230
|
+
newbib&.at(ns("./ext"))&.remove
|
231
|
+
_file, url = targetfile(@files[docid], false)
|
232
|
+
uri_node = Nokogiri::XML::Node.new "uri", newbib
|
233
|
+
uri_node[:type] = "citation"
|
234
|
+
uri_node.content = url
|
235
|
+
newbib.at(ns("./docidentifier")).previous = uri_node
|
236
|
+
end
|
237
|
+
|
238
|
+
# TODO: update crossreferences to other files in the selection
|
239
|
+
# repo(current-metanorma-collection/ISO 17301-1:2016)
|
240
|
+
# replaced by
|
241
|
+
# bibdata of "ISO 17301-1:2016" in situ as bibitem
|
242
|
+
# Any erefs to that bibitem id are replaced with relative URL
|
243
|
+
# Preferably with anchor, and is a job to realise dynamic lookup of
|
244
|
+
# localities
|
245
|
+
# @param file [String] XML content
|
246
|
+
# @param identifier [String] docid
|
247
|
+
# @return [String] XML content
|
248
|
+
def update_xrefs(file, identifier)
|
249
|
+
docxml = Nokogiri::XML(file)
|
250
|
+
docxml.xpath(ns("//bibitem[not(ancestor::bibitem)]")).each do |b|
|
251
|
+
docid = b&.at(ns("./docidentifier[@type = 'repository']"))&.text
|
252
|
+
next unless docid && %r{^current-metanorma-collection/}.match(docid)
|
253
|
+
|
254
|
+
update_bibitem(b, identifier)
|
255
|
+
update_anchors(b, docxml, docid)
|
256
|
+
end
|
257
|
+
docxml.to_xml
|
258
|
+
end
|
259
|
+
|
260
|
+
# if there is a crossref to another document, with no anchor, retrieve the
|
261
|
+
# anchor given the locality, and insert it into the crossref
|
262
|
+
def update_anchors(bib, docxml, _id) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
263
|
+
docid = bib&.at(ns("./docidentifier"))&.text
|
264
|
+
docxml.xpath("//xmlns:eref[@citeas = '#{docid}']").each do |e|
|
265
|
+
e.at(ns(".//locality[@type = 'anchor']")).nil? || next
|
266
|
+
ins = e.at(ns("./localityStack")) || next
|
267
|
+
type = ins&.at(ns("./locality/@type"))&.text
|
268
|
+
ref = ins&.at(ns("./locality/referenceFrom"))&.text
|
269
|
+
(anchor = @files[docid][:anchors][type][ref]) || next
|
270
|
+
ref_from = Nokogiri::XML::Node.new "referenceFrom", bib
|
271
|
+
ref_from.content = anchor.sub(/^_/, "")
|
272
|
+
locality = Nokogiri::XML::Node.new "locality", bib
|
273
|
+
locality[:type] = "anchor"
|
274
|
+
locality.add_child ref_from
|
275
|
+
ins << locality
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# process each file in the collection
|
280
|
+
# files are held in memory, and altered as postprocessing
|
281
|
+
def files # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
282
|
+
@files.each do |identifier, x|
|
283
|
+
file, filename = targetfile(x, true)
|
284
|
+
file = update_xrefs(file, identifier)
|
285
|
+
Tempfile.open(["collection", ".xml"], encoding: "utf-8") do |f|
|
286
|
+
f.write(file)
|
287
|
+
f.close
|
288
|
+
# warn "metanorma compile -x html #{f.path}"
|
289
|
+
c = Compile.new
|
290
|
+
c.compile f.path, format: :asciidoc, extension_keys: @format
|
291
|
+
@format.each do |ext|
|
292
|
+
fn = File.basename(filename).sub(/(?<=\.)[^\.]+$/, ext.to_s)
|
293
|
+
FileUtils.mv f.path.sub(/\.xml$/, ".#{ext}"), File.join(@outdir, fn)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
private
|
300
|
+
|
301
|
+
# @param options [Hash]
|
302
|
+
# @raise [ArgumentError]
|
303
|
+
def check_options(options)
|
304
|
+
unless options[:format].is_a?(Array) && (FORMATS & options[:format]).any?
|
305
|
+
raise ArgumentError, "Need to specify formats (xml,html,pdf,doc)"
|
306
|
+
end
|
307
|
+
return if !options[:format].include?(:html) || options[:coverpage]
|
308
|
+
|
309
|
+
raise ArgumentError, "Need to specify a coverpage to render HTML"
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
data/lib/metanorma/document.rb
CHANGED
@@ -1,12 +1,82 @@
|
|
1
1
|
module Metanorma
|
2
2
|
class Document
|
3
|
+
# @return [Strin]
|
4
|
+
attr_reader :file
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
-
@
|
7
|
-
@
|
8
|
-
@output_formats
|
6
|
+
# @param bibitem [RelatonBib::BibliographicItem]
|
7
|
+
def initialize(bibitem, file)
|
8
|
+
@bibitem = bibitem
|
9
|
+
@file = file
|
9
10
|
end
|
10
11
|
|
12
|
+
class << self
|
13
|
+
# @param file [String] file path
|
14
|
+
# @return [Metanorma::Document]
|
15
|
+
def parse_file(file)
|
16
|
+
new bibitem(file), file
|
17
|
+
end
|
18
|
+
|
19
|
+
# #param xml [Nokogiri::XML::Document, Nokogiri::XML::Element]
|
20
|
+
# @return [Metanorma::Document]
|
21
|
+
def parse_xml(xml)
|
22
|
+
new from_xml(xml)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# #param xml [Nokogiri::XML::Document, Nokogiri::XML::Element]
|
28
|
+
# @return [RelatonBib::BibliographicItem,RelatonIso::IsoBibliographicItem]
|
29
|
+
def from_xml(xml)
|
30
|
+
Relaton::Cli.parse_xml xml.at("//xmlns:bibitem|//xmlns:bibdata")
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param file [String]
|
34
|
+
# @return [Symbol] file type
|
35
|
+
def format(file)
|
36
|
+
case file
|
37
|
+
when /\.xml$/ then :xml
|
38
|
+
when /.ya?ml$/ then :yaml
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param file [String]
|
43
|
+
# @return [RelatonBib::BibliographicItem,
|
44
|
+
# RelatonIso::IsoBibliographicItem]
|
45
|
+
def bibitem(file)
|
46
|
+
case format(file)
|
47
|
+
when :xml
|
48
|
+
from_xml Nokogiri::XML(File.read(file, encoding: "UTF-8"))
|
49
|
+
when :yaml
|
50
|
+
yaml = File.read(file, ecoding: "UTF-8")
|
51
|
+
Relaton::Cli::YAMLConvertor.convert_single_file(yaml)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# @param builder [Nokogiri::XML::Builder, nil]
|
57
|
+
# @return [Nokogiri::XML::Builder, String]
|
58
|
+
def to_xml(builder = nil)
|
59
|
+
if builder
|
60
|
+
render_xml builder
|
61
|
+
else
|
62
|
+
Nokogiri::XML::Builder.new do |b|
|
63
|
+
root = render_xml b
|
64
|
+
root["xmlns"] = "http://metanorma.org"
|
65
|
+
end.to_xml
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [String]
|
70
|
+
def type
|
71
|
+
@type ||= (@bibitem.docidentifier.first&.type ||
|
72
|
+
@bibitem.docidentifier.first&.id&.match(/^[^\s]+/)&.to_s)&.downcase ||
|
73
|
+
"standoc"
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def render_xml(builder)
|
79
|
+
builder.send(type + "-standard") { |b| @bibitem.to_xml b, bibdata: true }
|
80
|
+
end
|
11
81
|
end
|
12
82
|
end
|
@@ -7,7 +7,13 @@ module Metanorma
|
|
7
7
|
def convert(url_path, output_path, xsl_stylesheet)
|
8
8
|
return if url_path.nil? || output_path.nil? || xsl_stylesheet.nil?
|
9
9
|
|
10
|
-
Mn2pdf.convert(url_path, output_path, xsl_stylesheet)
|
10
|
+
Mn2pdf.convert(quote(url_path), quote(output_path), quote(xsl_stylesheet))
|
11
|
+
end
|
12
|
+
|
13
|
+
def quote(x)
|
14
|
+
return x if /^'.*'$/.match(x)
|
15
|
+
return x if /^".*"$/.match(x)
|
16
|
+
%("#{x}")
|
11
17
|
end
|
12
18
|
end
|
13
19
|
end
|
data/lib/metanorma/version.rb
CHANGED
data/metanorma.gemspec
CHANGED
@@ -27,11 +27,13 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.add_runtime_dependency 'htmlentities'
|
28
28
|
spec.add_runtime_dependency 'nokogiri'
|
29
29
|
spec.add_runtime_dependency 'mn2pdf', "~> 1"
|
30
|
+
spec.add_dependency "relaton-cli", "~> 1.2.0"
|
30
31
|
|
31
32
|
spec.add_development_dependency "rake", "~> 12.0"
|
32
33
|
spec.add_development_dependency "rspec", "~> 3.0"
|
33
34
|
spec.add_development_dependency "byebug", "~> 10.0"
|
34
35
|
spec.add_development_dependency "rspec-command", "~> 1.0"
|
35
36
|
spec.add_development_dependency "equivalent-xml", "~> 0.6"
|
36
|
-
spec.add_development_dependency "metanorma-iso", "~> 1.
|
37
|
+
spec.add_development_dependency "metanorma-iso", "~> 1.5"
|
38
|
+
spec.add_development_dependency "isodoc", "~> 1.2.1"
|
37
39
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metanorma
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ribose Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: asciidoctor
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: relaton-cli
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.2.0
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.2.0
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: rake
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -142,14 +156,28 @@ dependencies:
|
|
142
156
|
requirements:
|
143
157
|
- - "~>"
|
144
158
|
- !ruby/object:Gem::Version
|
145
|
-
version: '1.
|
159
|
+
version: '1.5'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '1.5'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: isodoc
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - "~>"
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: 1.2.1
|
146
174
|
type: :development
|
147
175
|
prerelease: false
|
148
176
|
version_requirements: !ruby/object:Gem::Requirement
|
149
177
|
requirements:
|
150
178
|
- - "~>"
|
151
179
|
- !ruby/object:Gem::Version
|
152
|
-
version:
|
180
|
+
version: 1.2.1
|
153
181
|
description: Library to process any Metanorma standard.
|
154
182
|
email:
|
155
183
|
- open.source@ribose.com
|
@@ -182,6 +210,9 @@ files:
|
|
182
210
|
- lib/metanorma.rb
|
183
211
|
- lib/metanorma/asciidoctor_extensions.rb
|
184
212
|
- lib/metanorma/asciidoctor_extensions/glob_include_processor.rb
|
213
|
+
- lib/metanorma/collection.rb
|
214
|
+
- lib/metanorma/collection_manifest.rb
|
215
|
+
- lib/metanorma/collection_renderer.rb
|
185
216
|
- lib/metanorma/compile.rb
|
186
217
|
- lib/metanorma/config.rb
|
187
218
|
- lib/metanorma/document.rb
|