metanorma 1.1.2 → 1.1.3
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.
- 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
|