metanorma-mko 1.1.1 → 1.1.2
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/lib/metanorma/mko/bundle.rb +1 -1
- data/lib/metanorma/mko/model/collection.rb +168 -0
- data/lib/metanorma/mko/model/exporter.rb +17 -0
- data/lib/metanorma/mko/model/project.rb +1110 -0
- data/lib/metanorma/mko/model.rb +121 -0
- data/lib/metanorma/mko/schema/json_schema.rb +3 -1
- data/lib/metanorma/mko/version.rb +1 -1
- data/lib/metanorma-mko.rb +1 -0
- metadata +37 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4e612f1678371d2e5f3ab58ef00e80918dcf419346ae7140244e5baa15b24c3e
|
|
4
|
+
data.tar.gz: 41519e76b5470d19f8823554386bbfac62c7e688ccaf5d263fe0f75a79f5a722
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 84a2bb13c2ac8f215785e613cce14810c8873fc86d8de11c77e3dee9f7f419fe0943d8fb8597cf8af0d59f785d6e57a7fedeef514561e5b71ab782c072feef4f
|
|
7
|
+
data.tar.gz: c9c724166f850ed54b8ef85a744d7a22c7b51290dec4f39db72f4de349239f4958d2c11d9f2736030355d8678e37df9914a44f2293ee9e1884494375f62c0b20
|
data/lib/metanorma/mko/bundle.rb
CHANGED
|
@@ -68,7 +68,7 @@ module Metanorma
|
|
|
68
68
|
def zip!
|
|
69
69
|
require "zip"
|
|
70
70
|
zip_path = "#{dir}.zip"
|
|
71
|
-
Zip::File.open(zip_path,
|
|
71
|
+
Zip::File.open(zip_path, create: true) do |zipfile|
|
|
72
72
|
Dir[File.join(dir, "**", "*")].each do |f|
|
|
73
73
|
next if File.directory?(f)
|
|
74
74
|
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
module Metanorma
|
|
6
|
+
module Mko
|
|
7
|
+
# Collection export (MN 116 §collections): a metanorma
|
|
8
|
+
# collection.yml declares a document family (OIML R 60 parts,
|
|
9
|
+
# amendments, R 129/138/144). Every member document exports as its
|
|
10
|
+
# own bundle (the document bundle is the unit of truth); the
|
|
11
|
+
# collection adds one small bundle with the membership contract:
|
|
12
|
+
# collection.json and the cross-document part_of edges binding the
|
|
13
|
+
# members to the family. Member identity comes from each member's
|
|
14
|
+
# exported document.json — the collection yml's identifiers are
|
|
15
|
+
# reference labels only.
|
|
16
|
+
class Collection
|
|
17
|
+
Member = Struct.new(:fileref, :xml_path, :identifier,
|
|
18
|
+
:bundle_path, :result, :skipped,
|
|
19
|
+
keyword_init: true)
|
|
20
|
+
|
|
21
|
+
class Result
|
|
22
|
+
attr_reader :collection_bundle, :members
|
|
23
|
+
|
|
24
|
+
def initialize(collection_bundle:, members:)
|
|
25
|
+
@collection_bundle = collection_bundle
|
|
26
|
+
@members = members
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def skipped
|
|
30
|
+
members.select(&:skipped)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class << self
|
|
35
|
+
def export(yml, to:, assets_from: nil, zip: false)
|
|
36
|
+
new(yml, to: to, assets_from: assets_from, zip: zip).export
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def initialize(yml, to:, assets_from: nil, zip: false)
|
|
41
|
+
@yml = if yml.is_a?(Hash)
|
|
42
|
+
yml
|
|
43
|
+
elsif File.exist?(yml.to_s)
|
|
44
|
+
YAML.safe_load_file(yml)
|
|
45
|
+
else
|
|
46
|
+
YAML.safe_load(yml)
|
|
47
|
+
end
|
|
48
|
+
@base = File.exist?(yml.to_s) ? File.dirname(File.expand_path(yml.to_s)) : Dir.pwd
|
|
49
|
+
@to = to
|
|
50
|
+
@assets_from = assets_from
|
|
51
|
+
@zip = zip
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def export
|
|
55
|
+
members = docrefs.filter_map do |ref|
|
|
56
|
+
member = resolve_member(ref)
|
|
57
|
+
next member if member.skipped
|
|
58
|
+
|
|
59
|
+
export = Mko.export(
|
|
60
|
+
File.read(member.xml_path), to: @to,
|
|
61
|
+
presentation_xml: presentation_for(member.xml_path),
|
|
62
|
+
assets_from: @assets_from || File.dirname(member.xml_path)
|
|
63
|
+
)
|
|
64
|
+
member.bundle_path = export.path
|
|
65
|
+
member.result = export.result
|
|
66
|
+
member
|
|
67
|
+
end
|
|
68
|
+
Result.new(collection_bundle: write_collection_bundle(members),
|
|
69
|
+
members: members)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def docrefs
|
|
75
|
+
manifest = @yml["manifest"]
|
|
76
|
+
manifest = manifest.first if manifest.is_a?(Array)
|
|
77
|
+
Array(manifest.is_a?(Hash) ? manifest["docref"] : nil)
|
|
78
|
+
rescue StandardError
|
|
79
|
+
[]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# The yml points members at .adoc sources; the compiled semantic
|
|
83
|
+
# XML sits beside them. Direct .xml filerefs work too.
|
|
84
|
+
def resolve_member(ref)
|
|
85
|
+
fileref = ref["fileref"].to_s
|
|
86
|
+
xml = fileref.end_with?(".xml") ? fileref : fileref.sub(/\.adoc$/, ".xml")
|
|
87
|
+
path = File.expand_path(xml, @base)
|
|
88
|
+
if File.file?(path)
|
|
89
|
+
Member.new(fileref: fileref, xml_path: path,
|
|
90
|
+
identifier: ref["identifier"])
|
|
91
|
+
else
|
|
92
|
+
Member.new(fileref: fileref, xml_path: path,
|
|
93
|
+
identifier: ref["identifier"], skipped: true)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def presentation_for(xml_path)
|
|
98
|
+
pres = xml_path.sub(/\.xml$/, ".presentation.xml")
|
|
99
|
+
File.file?(pres) ? File.read(pres) : nil
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def canonical
|
|
103
|
+
docid = @yml.dig("bibdata", "docid")
|
|
104
|
+
docid = docid.first if docid.is_a?(Array)
|
|
105
|
+
docid.is_a?(Hash) ? docid["id"].to_s : docid.to_s
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def title
|
|
109
|
+
t = @yml.dig("bibdata", "title")
|
|
110
|
+
t = t.first if t.is_a?(Array)
|
|
111
|
+
t.is_a?(Hash) ? t["content"].to_s : t.to_s
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def collection_short
|
|
115
|
+
Mko.slug(canonical.empty? ? "collection" : canonical)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Member identity from the export's own Result — the same truth
|
|
119
|
+
# the bundle was written from, never a read-back of our output.
|
|
120
|
+
def member_data(member)
|
|
121
|
+
document = member.result.document
|
|
122
|
+
{ "bundle" => "#{document.ids.short}.mko",
|
|
123
|
+
"docidentifier" => document.ids.canonical,
|
|
124
|
+
"identifier" => member.identifier,
|
|
125
|
+
"title" => document.titles.first&.text,
|
|
126
|
+
"edition" => document.edition }
|
|
127
|
+
rescue StandardError
|
|
128
|
+
nil
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def collection_member(member)
|
|
132
|
+
data = member_data(member) or return nil
|
|
133
|
+
|
|
134
|
+
Schema::CollectionMember.new(
|
|
135
|
+
bundle: data["bundle"], docidentifier: data["docidentifier"],
|
|
136
|
+
identifier: data["identifier"], title: data["title"],
|
|
137
|
+
edition: data["edition"]
|
|
138
|
+
)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def write_collection_bundle(members)
|
|
142
|
+
collection = Schema::Collection.new(
|
|
143
|
+
canonical: canonical, short: collection_short, title: title,
|
|
144
|
+
edition: @yml.dig("bibdata", "edition"),
|
|
145
|
+
members: members.filter_map { |m| collection_member(m) }
|
|
146
|
+
)
|
|
147
|
+
# A member whose identity collides with the family (e.g. a
|
|
148
|
+
# stale amendment bibdata — mn-samples-oiml#66) would emit a
|
|
149
|
+
# self-edge; skip those, they say nothing.
|
|
150
|
+
edges = collection.members.reject do |m|
|
|
151
|
+
m.docidentifier.to_s.empty? || m.docidentifier == collection.canonical
|
|
152
|
+
end.map do |m|
|
|
153
|
+
{ "from" => "doc:#{m.docidentifier}",
|
|
154
|
+
"to" => "doc:#{collection.canonical}",
|
|
155
|
+
"kind" => "part_of" }
|
|
156
|
+
end
|
|
157
|
+
bundle = Bundle.open(collection_short, @to)
|
|
158
|
+
bundle.flavor = nil
|
|
159
|
+
bundle.add_json("collection", "collection.json", collection)
|
|
160
|
+
bundle.add_lines("edges", "edges.jsonl", edges) do |edge|
|
|
161
|
+
JSON.generate(edge)
|
|
162
|
+
end
|
|
163
|
+
bundle.write_manifest
|
|
164
|
+
@zip ? bundle.zip! : bundle.dir
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Mko
|
|
5
|
+
# Registry-facing exporter: quacks like a renderer (generate_full_document)
|
|
6
|
+
# so the Core::Flavors format table and the compile pipeline can select
|
|
7
|
+
# mko like any output format.
|
|
8
|
+
class Exporter
|
|
9
|
+
def generate_full_document(document, **options)
|
|
10
|
+
output = options[:output] || options[:output_filename] || "."
|
|
11
|
+
to = File.directory?(output) ? output : File.dirname(output)
|
|
12
|
+
Mko.export(document, to: to,
|
|
13
|
+
presentation_xml: options[:presentation_xml])
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|