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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 969ed76602cb33778779c7ab04c8c4c4be5fd63e83b448c07f96e8fafa6c7afb
4
- data.tar.gz: cf467c6615d1e5840a8c3accdd36195f269d4624307e94d2db6d56744a95cacb
3
+ metadata.gz: cb71b62025d938d52251d5bfb6d1911b3de32299500e12b8fa414a63520a86c3
4
+ data.tar.gz: df9deea25ea6e374426ad40b3146c63610251873ec652142acffc9274af5856c
5
5
  SHA512:
6
- metadata.gz: 64d4f4a2f7375bb804a5e0acae3a538e4306a0f0a4cfa5c4b8688a320d0fb65e1b977099a5d413bcf87f74ab084abb11496ed20842daa476c92bb08aa57623d2
7
- data.tar.gz: eec76dfa55611c07335aa3c661b7926c0249338b9d6225fb3e58eb78d598b2cfee8fac19ebd1623721fa0fae441b451f04fd536dec7e728ae97d652626fca897
6
+ metadata.gz: 6404a68d6371fb24f27e36cc69dc0479da638ec79ef3bb48edf1939d6ec3beffb36c5b86f1402bc238a01ed05481dac255a30ac043cf8a806b24d464e5a16592
7
+ data.tar.gz: 1881fb5c6c4e3c1d6239b1a4c34dca109f4bc489d33942d42c8e09c9177aef29ce557219aeff7656458bd6e2de918859155728b908ba64c7d6c68c8f1bbdb942
@@ -4,7 +4,3 @@
4
4
 
5
5
  inherit_from:
6
6
  - https://raw.githubusercontent.com/riboseinc/oss-guides/master/ci/rubocop.yml
7
- AllCops:
8
- TargetRubyVersion: 2.3
9
- Rails:
10
- Enabled: true
@@ -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
@@ -1,12 +1,82 @@
1
1
  module Metanorma
2
2
  class Document
3
+ # @return [Strin]
4
+ attr_reader :file
3
5
 
4
- def initialize
5
- @input_file
6
- @input_format
7
- @document_type
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
@@ -1,3 +1,3 @@
1
1
  module Metanorma
2
- VERSION = "1.1.2"
2
+ VERSION = "1.1.3"
3
3
  end
@@ -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.4"
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.2
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-07-10 00:00:00.000000000 Z
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.4'
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: '1.4'
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