metanorma-mko 1.0.0
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 +7 -0
- data/LICENSE +25 -0
- data/README.adoc +44 -0
- data/lib/metanorma/mko/alignment.rb +53 -0
- data/lib/metanorma/mko/assets.rb +63 -0
- data/lib/metanorma/mko/bundle.rb +91 -0
- data/lib/metanorma/mko/diff.rb +97 -0
- data/lib/metanorma/mko/export.rb +27 -0
- data/lib/metanorma/mko/mcp.rb +162 -0
- data/lib/metanorma/mko/result.rb +29 -0
- data/lib/metanorma/mko/schema/collection.rb +44 -0
- data/lib/metanorma/mko/schema/document.rb +119 -0
- data/lib/metanorma/mko/schema/edge.rb +22 -0
- data/lib/metanorma/mko/schema/identifiers.rb +31 -0
- data/lib/metanorma/mko/schema/json_schema.rb +120 -0
- data/lib/metanorma/mko/schema/manifest.rb +59 -0
- data/lib/metanorma/mko/schema/unit.rb +221 -0
- data/lib/metanorma/mko/schema.rb +20 -0
- data/lib/metanorma/mko/units.rb +109 -0
- data/lib/metanorma/mko/version.rb +10 -0
- data/lib/metanorma/mko/writer.rb +62 -0
- data/lib/metanorma/mko.rb +53 -0
- metadata +148 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "unitsml/model"
|
|
4
|
+
|
|
5
|
+
module Metanorma
|
|
6
|
+
module Mko
|
|
7
|
+
# Units register (#55 GAP-1): every unit the document uses, carried
|
|
8
|
+
# by the model's typed UnitsML classes (or parsed from raw container
|
|
9
|
+
# XML). Emitted as unitsml.jsonl — one entry per line — and
|
|
10
|
+
# referenced by id from formula payloads and table columns.
|
|
11
|
+
# Consumers compare on quantity kinds, never unit strings.
|
|
12
|
+
module Units
|
|
13
|
+
Entry = Struct.new(:id, :symbol, :name, :quantity_kind, :dimension,
|
|
14
|
+
:dimension_url, :si_to, :si_expression,
|
|
15
|
+
keyword_init: true)
|
|
16
|
+
|
|
17
|
+
# SI base-quantity symbols in canonical vector order: L M T I Θ N J
|
|
18
|
+
# plus plane angle. Shared by the XML parser and the model
|
|
19
|
+
# projection so both compose identical vectors.
|
|
20
|
+
BASE_QUANTITY_SYMBOLS = {
|
|
21
|
+
"Length" => "L", "Mass" => "M", "Time" => "T",
|
|
22
|
+
"ElectricCurrent" => "I", "ThermodynamicTemperature" => "Θ",
|
|
23
|
+
"AmountOfSubstance" => "N", "LuminousIntensity" => "J",
|
|
24
|
+
"PlaneAngle" => "φ"
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
class << self
|
|
28
|
+
# unitsml_xml: the <UnitsML> container XML (or the inner
|
|
29
|
+
# <UnitSet>). Returns [Entry].
|
|
30
|
+
def parse(unitsml_xml)
|
|
31
|
+
require "nokogiri"
|
|
32
|
+
doc = Nokogiri::XML(unitsml_xml)
|
|
33
|
+
units = doc.remove_namespaces!
|
|
34
|
+
.xpath("//UnitSet/Unit").map do |node|
|
|
35
|
+
wrap_unit(node)
|
|
36
|
+
end
|
|
37
|
+
dimensions = parse_dimensions(doc)
|
|
38
|
+
units.each do |u|
|
|
39
|
+
dim = dimensions[u.dimension_url]
|
|
40
|
+
u.dimension = dim if dim
|
|
41
|
+
end
|
|
42
|
+
units
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Write unitsml.jsonl into a bundle; returns the entry list.
|
|
46
|
+
def write(entries, dir)
|
|
47
|
+
path = File.join(dir, "unitsml.jsonl")
|
|
48
|
+
File.write(path,
|
|
49
|
+
entries.map { |e| JSON.generate(entry_hash(e)) }
|
|
50
|
+
.join("\n") + "\n")
|
|
51
|
+
entries
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def entry_hash(entry)
|
|
55
|
+
{
|
|
56
|
+
"id" => entry.id,
|
|
57
|
+
"symbol" => entry.symbol,
|
|
58
|
+
"name" => entry.name,
|
|
59
|
+
"quantity_kind" => entry.quantity_kind,
|
|
60
|
+
"dimension" => entry.dimension,
|
|
61
|
+
"si_conversion" => entry.si_to || entry.si_expression ? {
|
|
62
|
+
"to" => entry.si_to,
|
|
63
|
+
"expression" => entry.si_expression,
|
|
64
|
+
} : nil,
|
|
65
|
+
}.compact
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# SI base-quantity vector from (symbol, powerNumerator,
|
|
69
|
+
# powerDenominator) triples: "M·L^2·T^-2", "Θ", ...
|
|
70
|
+
def vectorize(parts)
|
|
71
|
+
parts.filter_map do |sym, n, d|
|
|
72
|
+
next sym if n.nil? || (n == 1 && (d.nil? || d == 1))
|
|
73
|
+
|
|
74
|
+
exp = d && d != 1 ? "#{n || 1}/#{d}" : n.to_s
|
|
75
|
+
"#{sym}^#{exp}"
|
|
76
|
+
end.join("·")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def wrap_unit(node)
|
|
82
|
+
Entry.new(
|
|
83
|
+
id: node["id"],
|
|
84
|
+
symbol: node.xpath(".//UnitSymbol/text()").map(&:text).join,
|
|
85
|
+
name: node.xpath(".//UnitName/text()").map(&:text).join,
|
|
86
|
+
quantity_kind: nil,
|
|
87
|
+
dimension_url: node["dimensionURL"]&.sub("#", ""),
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def parse_dimensions(doc)
|
|
92
|
+
doc.xpath("//Dimension").each_with_object({}) do |d, h|
|
|
93
|
+
h[d["id"]] = dimension_vector(d)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# SI base-quantity vector: L M T I Θ N J + plane angle
|
|
98
|
+
def dimension_vector(dim)
|
|
99
|
+
parts = BASE_QUANTITY_SYMBOLS.map do |el, sym|
|
|
100
|
+
node = dim.xpath("./#{el}").first or next
|
|
101
|
+
[sym, node["powerNumerator"]&.to_i,
|
|
102
|
+
node["powerDenominator"]&.to_i]
|
|
103
|
+
end
|
|
104
|
+
vectorize(parts)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Mko
|
|
5
|
+
# Gem version — kept dependency-free so gemspecs can read it
|
|
6
|
+
# without activating the model stack. Tracks the MN 116 schema
|
|
7
|
+
# version: the wire contract and the gem release together.
|
|
8
|
+
VERSION = "1.0.0"
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Mko
|
|
5
|
+
# Maps a projection Result onto a bundle directory (or zip). The
|
|
6
|
+
# on-disk mechanics live in Bundle; this class only decides which
|
|
7
|
+
# components a document projection produces.
|
|
8
|
+
module Writer
|
|
9
|
+
class << self
|
|
10
|
+
def write(result, to:, zip: false)
|
|
11
|
+
bundle = Bundle.open(result.document.ids.short, to)
|
|
12
|
+
bundle.flavor = result.flavor
|
|
13
|
+
add_components(bundle, result)
|
|
14
|
+
bundle.write_manifest
|
|
15
|
+
zip ? bundle.zip! : bundle.dir
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def add_components(bundle, result)
|
|
21
|
+
bundle.add_json("document", "document.json", result.document)
|
|
22
|
+
# bibdata/glossary/identifiers carry the NATIVE object-model
|
|
23
|
+
# serializations (Relaton::Bib, Glossarist::Concept, Pubid),
|
|
24
|
+
# produced by Document::NativeModels at the model layer.
|
|
25
|
+
if result.bibdata
|
|
26
|
+
bundle.add_json("bibdata", "bibdata.json", result.bibdata)
|
|
27
|
+
end
|
|
28
|
+
bundle.add_json("identifiers", "identifiers.json", result.identifiers)
|
|
29
|
+
bundle.add_json("glossary", "glossary.json",
|
|
30
|
+
{ "concepts" => result.glossary.map do |c|
|
|
31
|
+
JSON.parse(c.to_json)
|
|
32
|
+
end })
|
|
33
|
+
# bibliography.jsonl: every cited document as native objects —
|
|
34
|
+
# Relaton item + pubid parse — keyed to its reference unit.
|
|
35
|
+
bib_lines = result.bibliography.map do |e|
|
|
36
|
+
{
|
|
37
|
+
"unit" => "u:#{e.key}",
|
|
38
|
+
"citeas" => e.citeas,
|
|
39
|
+
"pubid" => e.pubid && JSON.parse(e.pubid.to_json),
|
|
40
|
+
"pubid_render" => e.pubid&.to_s,
|
|
41
|
+
"bibitem" => e.item && JSON.parse(e.item.to_json),
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
bundle.add_lines("bibliography", "bibliography.jsonl", bib_lines) do |l|
|
|
45
|
+
JSON.generate(l)
|
|
46
|
+
end
|
|
47
|
+
bundle.add_lines("units", "units.jsonl", result.units)
|
|
48
|
+
bundle.add_lines("edges", "edges.jsonl", result.edges)
|
|
49
|
+
# unitsml.jsonl: the units register, referenced by id from
|
|
50
|
+
# formula payloads and table columns (#55). Absent when the
|
|
51
|
+
# source carries no UnitsML container.
|
|
52
|
+
unless result.unitsml.empty?
|
|
53
|
+
bundle.add_lines("unitsml", "unitsml.jsonl", result.unitsml) do |e|
|
|
54
|
+
JSON.generate(Units.entry_hash(e))
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
result.assets.each { |entry| bundle.add_asset(entry) }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
require "json"
|
|
5
|
+
require "digest"
|
|
6
|
+
|
|
7
|
+
module Metanorma
|
|
8
|
+
# Metanorma Knowledge Objects (MKO, MN 116) — the machine
|
|
9
|
+
# serialization of a Metanorma document as a bundle of typed,
|
|
10
|
+
# addressable knowledge objects (metanorma/metanorma#592). A derived
|
|
11
|
+
# projection over a typed document model: like a rendering, never a
|
|
12
|
+
# source format.
|
|
13
|
+
#
|
|
14
|
+
# This gem owns the FORMAT (the MN 116 contract): wire schema,
|
|
15
|
+
# bundle layout + manifest verification, assets, alignment, diffs,
|
|
16
|
+
# the generated JSON Schemas, and the reference MCP server.
|
|
17
|
+
# metanorma-document reopens this module with the MODEL side — the
|
|
18
|
+
# projection walk, collection orchestration, flavor resolution — and
|
|
19
|
+
# composes this gem to export.
|
|
20
|
+
module Mko
|
|
21
|
+
autoload :VERSION, "metanorma/mko/version"
|
|
22
|
+
autoload :Schema, "metanorma/mko/schema"
|
|
23
|
+
autoload :Bundle, "metanorma/mko/bundle"
|
|
24
|
+
autoload :Writer, "metanorma/mko/writer"
|
|
25
|
+
autoload :Export, "metanorma/mko/export"
|
|
26
|
+
autoload :Result, "metanorma/mko/result"
|
|
27
|
+
autoload :Assets, "metanorma/mko/assets"
|
|
28
|
+
autoload :Alignment, "metanorma/mko/alignment"
|
|
29
|
+
autoload :Diff, "metanorma/mko/diff"
|
|
30
|
+
autoload :Mcp, "metanorma/mko/mcp"
|
|
31
|
+
autoload :Units, "metanorma/mko/units"
|
|
32
|
+
|
|
33
|
+
autoload :VERSION, "metanorma/mko/version"
|
|
34
|
+
|
|
35
|
+
SCHEMA = "metanorma-mko"
|
|
36
|
+
SCHEMA_VERSION = VERSION
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
# The published wire contract, generated from the schema classes
|
|
40
|
+
# (single source of truth): name => JSON Schema draft 2020-12.
|
|
41
|
+
def json_schemas
|
|
42
|
+
Schema::JsonSchema.all
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Canonical short-id derivation (one rule, documents and
|
|
46
|
+
# collections alike).
|
|
47
|
+
def slug(canonical)
|
|
48
|
+
canonical.to_s.tr(" ", "-").gsub(/[^A-Za-z0-9.\-]/, "")
|
|
49
|
+
.squeeze("-").downcase
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: metanorma-mko
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ribose Inc.
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: lutaml-model
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.8'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.8'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rubyzip
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: unitsml
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.6'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.6'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rake
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '13.0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '13.0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rspec
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '3.0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '3.0'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: rubocop
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '1'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '1'
|
|
96
|
+
description: 'The MN 116 wire contract as code: typed unit schema, bundle layout with
|
|
97
|
+
manifest verification, hash-addressed assets, edition diffs, interlingual alignment,
|
|
98
|
+
generated JSON Schemas, and the reference MCP server. metanorma-document composes
|
|
99
|
+
this gem to export; polyglot consumers validate against the published schemas.'
|
|
100
|
+
email:
|
|
101
|
+
- open.source@ribose.com
|
|
102
|
+
executables: []
|
|
103
|
+
extensions: []
|
|
104
|
+
extra_rdoc_files: []
|
|
105
|
+
files:
|
|
106
|
+
- LICENSE
|
|
107
|
+
- README.adoc
|
|
108
|
+
- lib/metanorma/mko.rb
|
|
109
|
+
- lib/metanorma/mko/alignment.rb
|
|
110
|
+
- lib/metanorma/mko/assets.rb
|
|
111
|
+
- lib/metanorma/mko/bundle.rb
|
|
112
|
+
- lib/metanorma/mko/diff.rb
|
|
113
|
+
- lib/metanorma/mko/export.rb
|
|
114
|
+
- lib/metanorma/mko/mcp.rb
|
|
115
|
+
- lib/metanorma/mko/result.rb
|
|
116
|
+
- lib/metanorma/mko/schema.rb
|
|
117
|
+
- lib/metanorma/mko/schema/collection.rb
|
|
118
|
+
- lib/metanorma/mko/schema/document.rb
|
|
119
|
+
- lib/metanorma/mko/schema/edge.rb
|
|
120
|
+
- lib/metanorma/mko/schema/identifiers.rb
|
|
121
|
+
- lib/metanorma/mko/schema/json_schema.rb
|
|
122
|
+
- lib/metanorma/mko/schema/manifest.rb
|
|
123
|
+
- lib/metanorma/mko/schema/unit.rb
|
|
124
|
+
- lib/metanorma/mko/units.rb
|
|
125
|
+
- lib/metanorma/mko/version.rb
|
|
126
|
+
- lib/metanorma/mko/writer.rb
|
|
127
|
+
homepage: https://github.com/metanorma/metanorma-mko
|
|
128
|
+
licenses:
|
|
129
|
+
- BSD-2-Clause
|
|
130
|
+
metadata: {}
|
|
131
|
+
rdoc_options: []
|
|
132
|
+
require_paths:
|
|
133
|
+
- lib
|
|
134
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
139
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
|
+
requirements:
|
|
141
|
+
- - ">="
|
|
142
|
+
- !ruby/object:Gem::Version
|
|
143
|
+
version: '0'
|
|
144
|
+
requirements: []
|
|
145
|
+
rubygems_version: 4.0.16
|
|
146
|
+
specification_version: 4
|
|
147
|
+
summary: Metanorma Knowledge Objects (MN 116) — the machine serialization format
|
|
148
|
+
test_files: []
|