metanorma-plugin-lutaml 0.1.0 → 0.2.4.alpha
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/.github/workflows/rake.yml +65 -0
- data/.gitignore +2 -0
- data/README.adoc +87 -1
- data/lib/metanorma-plugin-lutaml.rb +2 -0
- data/lib/metanorma/plugin/lutaml/express_remarks_decorator.rb +73 -0
- data/lib/metanorma/plugin/lutaml/lutaml_diagram_block.rb +87 -0
- data/lib/metanorma/plugin/lutaml/lutaml_preprocessor.rb +76 -45
- data/lib/metanorma/plugin/lutaml/lutaml_uml_attributes_table_preprocessor.rb +102 -0
- data/lib/metanorma/plugin/lutaml/utils.rb +74 -0
- data/lib/metanorma/plugin/lutaml/version.rb +1 -1
- data/metanorma-plugin-lutaml.gemspec +1 -1
- data/pkg/metanorma-plugin-lutaml-0.2.0.gem +0 -0
- metadata +14 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c060a247f01300f4fe46b58671a07a8f0e3a80f53e64974ccd07208e181e0eaa
|
4
|
+
data.tar.gz: dcf58ee6f298d24ad2493bd65c62a549a2673717ce8a9404695caad20d32e069
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5061cc833de5a8d4742095e5491f66ab608bdac3042ecc601cdc26e89f5ff258aa34650b3b19e3eb3b38b50be01b3f866e18f15adda02000278e6a5864cf1b34
|
7
|
+
data.tar.gz: 3be917c68c0fd6b0157f6578e40f61da433b262629950f76d09b2b2cf9e4cc5f7955d9066a0930f337ec7beb9426790d650bfb632cf9abf570f3431360855ca6
|
@@ -0,0 +1,65 @@
|
|
1
|
+
name: rake
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ main ]
|
6
|
+
tags: [ v* ]
|
7
|
+
pull_request:
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
rake:
|
11
|
+
name: Test on Ruby ${{ matrix.ruby }} ${{ matrix.os }}
|
12
|
+
runs-on: ${{ matrix.os }}
|
13
|
+
continue-on-error: ${{ matrix.experimental }}
|
14
|
+
strategy:
|
15
|
+
fail-fast: false
|
16
|
+
matrix:
|
17
|
+
ruby: [ '2.6', '2.5', '2.4' ]
|
18
|
+
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
19
|
+
experimental: [ false ]
|
20
|
+
include:
|
21
|
+
- ruby: '2.7'
|
22
|
+
os: 'ubuntu-latest'
|
23
|
+
experimental: true
|
24
|
+
- ruby: '2.7'
|
25
|
+
os: 'windows-latest'
|
26
|
+
experimental: true
|
27
|
+
- ruby: '2.7'
|
28
|
+
os: 'macos-latest'
|
29
|
+
experimental: true
|
30
|
+
steps:
|
31
|
+
- uses: actions/checkout@master
|
32
|
+
|
33
|
+
- name: Use Ruby
|
34
|
+
uses: ruby/setup-ruby@v1
|
35
|
+
with:
|
36
|
+
ruby-version: ${{ matrix.ruby }}
|
37
|
+
bundler-cache: true
|
38
|
+
|
39
|
+
- name: Update gems
|
40
|
+
run: bundle install --jobs 4 --retry 3
|
41
|
+
|
42
|
+
- name: Install Grpahviz Ubuntu
|
43
|
+
if: matrix.os == 'ubuntu-latest'
|
44
|
+
run: sudo apt-get install graphviz
|
45
|
+
|
46
|
+
- name: Install Grpahviz macOS
|
47
|
+
if: matrix.os == 'macos-latest'
|
48
|
+
run: brew install graphviz
|
49
|
+
|
50
|
+
- name: Install Grpahviz Windows
|
51
|
+
if: matrix.os == 'windows-latest'
|
52
|
+
uses: nick-invision/retry@v1
|
53
|
+
with:
|
54
|
+
polling_interval_seconds: 5
|
55
|
+
timeout_minutes: 5
|
56
|
+
max_attempts: 3
|
57
|
+
command: choco install --no-progress graphviz
|
58
|
+
|
59
|
+
- name: Check dot command
|
60
|
+
if: matrix.os == 'windows-latest'
|
61
|
+
run: |
|
62
|
+
dot -?
|
63
|
+
|
64
|
+
- name: Run specs
|
65
|
+
run: bundle exec rake
|
data/.gitignore
CHANGED
data/README.adoc
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
= metanorma-plugin-lutaml
|
2
2
|
|
3
|
+
image:https://github.com/metanorma/metanorma-plugin-lutaml/workflows/rake/badge.svg["Build Status", link="https://github.com/metanorma/metanorma-plugin-lutaml/actions?workflow=rake"]
|
4
|
+
|
3
5
|
== Functionality
|
4
6
|
|
5
7
|
Metanorma plugin that allows you to access lutaml objects from a Metanorma document
|
@@ -11,7 +13,7 @@ Metanorma plugin that allows you to access lutaml objects from a Metanorma docum
|
|
11
13
|
$ gem install metanorma-plugin-lutaml
|
12
14
|
----
|
13
15
|
|
14
|
-
=== Usage
|
16
|
+
=== Usage, `lutaml` macro
|
15
17
|
|
16
18
|
Given `example.exp` file with the content:
|
17
19
|
|
@@ -58,6 +60,14 @@ And the `lutaml` macro block:
|
|
58
60
|
----
|
59
61
|
-----
|
60
62
|
|
63
|
+
Where:
|
64
|
+
|
65
|
+
* content within the block is called the "`template`";
|
66
|
+
|
67
|
+
* `{example.exp}` is the location of the exp schema file that contains data to be loaded. Location of the file is computed relative to the source directory that `[lutaml]` is used (e.g., if `[lutaml,example.exp,my_context]` is invoked in an `.adoc` file located at `/foo/bar/doc.adoc`, the data file is expected to be found at `/foo/bar/example.exp`);
|
68
|
+
|
69
|
+
* `{my_context}` is the name where the EXPRESS Repository read from the .exp file can be accessed with. Context object is a serialized Expressir::Model::Repository object with all variable names available. See https://github.com/lutaml/expressir[Expressir] docs for reference. `{my_context}` has `schemas` method to access Expressir https://github.com/lutaml/expressir/blob/master/lib/expressir/model/schema.rb[schemas]
|
70
|
+
|
61
71
|
Will produce this output:
|
62
72
|
|
63
73
|
[source,adoc]
|
@@ -71,6 +81,82 @@ Will produce this output:
|
|
71
81
|
=== my_type5
|
72
82
|
-----
|
73
83
|
|
84
|
+
This macro also supports `.lutaml` files.
|
85
|
+
|
86
|
+
=== Usage, `lutaml_uml_attributes_table` macro
|
87
|
+
|
88
|
+
This macro allows to quickly render datamodel attributes/values tables. Given `example.lutaml` file with the content:
|
89
|
+
|
90
|
+
[source,java]
|
91
|
+
----
|
92
|
+
diagram MyView {
|
93
|
+
title "my diagram"
|
94
|
+
|
95
|
+
enum AddressClassProfile {
|
96
|
+
imlicistAttributeProfile: CharacterString [0..1] {
|
97
|
+
definition
|
98
|
+
this is multiline with `ascidoc`
|
99
|
+
end definition
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
class AttributeProfile {
|
104
|
+
+addressClassProfile: CharacterString [0..1]
|
105
|
+
imlicistAttributeProfile: CharacterString [0..1] {
|
106
|
+
definition this is attribute definition
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
----
|
111
|
+
|
112
|
+
And the `lutaml_uml_attributes_table` macro:
|
113
|
+
|
114
|
+
[source,adoc]
|
115
|
+
-----
|
116
|
+
[lutaml_uml_attributes_table, example.lutaml, AttributeProfile]
|
117
|
+
-----
|
118
|
+
|
119
|
+
Will produce this output:
|
120
|
+
|
121
|
+
[source,adoc]
|
122
|
+
-----
|
123
|
+
=== AttributeProfile
|
124
|
+
|
125
|
+
|
126
|
+
.AttributeProfile attributes
|
127
|
+
|===
|
128
|
+
|Name |Definition |Mandatory/ Optional/ Conditional |Max Occur |Data Type
|
129
|
+
|
130
|
+
|addressClassProfile |TODO: enum 's definition |M |1 | `CharacterString`
|
131
|
+
|
132
|
+
|imlicistAttributeProfile |this is attribute definition with multiply lines |M |1 | `CharacterString`
|
133
|
+
|
134
|
+
|===
|
135
|
+
-----
|
136
|
+
|
137
|
+
In case of "enumeration"(AddressClassProfile) entity:
|
138
|
+
|
139
|
+
[source,adoc]
|
140
|
+
-----
|
141
|
+
[lutaml_uml_attributes_table, example.lutaml, AddressClassProfile]
|
142
|
+
-----
|
143
|
+
|
144
|
+
Will produce this output:
|
145
|
+
|
146
|
+
[source,adoc]
|
147
|
+
-----
|
148
|
+
=== AddressClassProfile
|
149
|
+
|
150
|
+
|
151
|
+
.AddressClassProfile values
|
152
|
+
|===
|
153
|
+
|Name |Definition
|
154
|
+
|
155
|
+
|imlicistAttributeProfile |this is multiline with `ascidoc`
|
156
|
+
|
157
|
+
|===
|
158
|
+
-----
|
159
|
+
|
74
160
|
== Documentation
|
75
161
|
|
76
162
|
See https://www.metanorma.com.
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require "metanorma/plugin/lutaml/version"
|
2
2
|
require "metanorma/plugin/lutaml/lutaml_preprocessor"
|
3
|
+
require "metanorma/plugin/lutaml/lutaml_uml_attributes_table_preprocessor"
|
4
|
+
require "metanorma/plugin/lutaml/lutaml_diagram_block"
|
3
5
|
|
4
6
|
module Metanorma
|
5
7
|
module Plugin
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Metanorma
|
2
|
+
module Plugin
|
3
|
+
module Lutaml
|
4
|
+
class ExpressRemarksDecorator
|
5
|
+
RELATIVE_PREFIX_MACRO_REGEXP = /^(\* <<express.+?>>;|link|image|video|audio|include)(:+)?(?![^\/:]+:\/\/|[A-Z]:\/|\/)([^:\[]+)(\[.*\])?$/.freeze
|
6
|
+
|
7
|
+
attr_reader :remark, :options
|
8
|
+
|
9
|
+
def self.call(remark, options)
|
10
|
+
new(remark, options).call
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(remark, options)
|
14
|
+
@remark = remark
|
15
|
+
@options = options
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
result = remark
|
20
|
+
if options["leveloffset"]
|
21
|
+
result = process_remark_offsets(result, options["leveloffset"].to_i)
|
22
|
+
end
|
23
|
+
if options["relative_path_prefix"]
|
24
|
+
result = update_relative_paths(result,
|
25
|
+
options["relative_path_prefix"])
|
26
|
+
end
|
27
|
+
result
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def update_relative_paths(string, path_prefix)
|
33
|
+
string
|
34
|
+
.split("\n")
|
35
|
+
.map do |line|
|
36
|
+
if line.match?(RELATIVE_PREFIX_MACRO_REGEXP)
|
37
|
+
prefix_relative_paths(line, path_prefix)
|
38
|
+
else
|
39
|
+
line
|
40
|
+
end
|
41
|
+
end
|
42
|
+
.join("\n")
|
43
|
+
end
|
44
|
+
|
45
|
+
def prefix_relative_paths(line, path_prefix)
|
46
|
+
line.gsub(RELATIVE_PREFIX_MACRO_REGEXP) do |_match|
|
47
|
+
prefixed_path = File.join(path_prefix, $3.strip)
|
48
|
+
"#{$1}#{$2}#{prefixed_path}#{$4}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def process_remark_offsets(string, offset)
|
53
|
+
string
|
54
|
+
.split("\n")
|
55
|
+
.map do |line|
|
56
|
+
if line.match?(/^=/)
|
57
|
+
set_string_offsets(line, offset)
|
58
|
+
else
|
59
|
+
line
|
60
|
+
end
|
61
|
+
end
|
62
|
+
.join("\n")
|
63
|
+
end
|
64
|
+
|
65
|
+
def set_string_offsets(string, offset)
|
66
|
+
return "#{'=' * offset}#{string}" if offset.positive?
|
67
|
+
|
68
|
+
string.gsub(/^={#{offset * -1}}/, "")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "liquid"
|
4
|
+
require "asciidoctor"
|
5
|
+
require "asciidoctor/reader"
|
6
|
+
require "lutaml"
|
7
|
+
require "lutaml/uml"
|
8
|
+
require "metanorma/plugin/lutaml/utils"
|
9
|
+
|
10
|
+
module Metanorma
|
11
|
+
module Plugin
|
12
|
+
module Lutaml
|
13
|
+
class LutamlDiagramBlock < Asciidoctor::Extensions::BlockProcessor
|
14
|
+
use_dsl
|
15
|
+
named :lutaml_diagram
|
16
|
+
on_context :literal
|
17
|
+
parse_content_as :raw
|
18
|
+
|
19
|
+
def abort(parent, reader, attrs, msg)
|
20
|
+
warn(msg)
|
21
|
+
attrs["language"] = "lutaml"
|
22
|
+
create_listing_block(
|
23
|
+
parent,
|
24
|
+
reader.source,
|
25
|
+
attrs.reject { |k, _v| k == 1 }
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def process(parent, reader, attrs)
|
30
|
+
uml_document = ::Lutaml::Uml::Parsers::Dsl.parse(lutaml_temp(reader))
|
31
|
+
filename = generate_file(parent, reader, uml_document)
|
32
|
+
through_attrs = generate_attrs(attrs)
|
33
|
+
through_attrs["target"] = filename
|
34
|
+
through_attrs["title"] = uml_document.caption
|
35
|
+
create_image_block(parent, through_attrs)
|
36
|
+
rescue StandardError => e
|
37
|
+
abort(parent, reader, attrs, e.message)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def lutaml_temp(reader)
|
43
|
+
temp_file = Tempfile.new(["lutaml", ".lutaml"])
|
44
|
+
temp_file.puts(reader.read)
|
45
|
+
temp_file.rewind
|
46
|
+
temp_file
|
47
|
+
end
|
48
|
+
|
49
|
+
# if no :imagesdir: leave image file in lutaml
|
50
|
+
def generate_file(parent, _reader, uml_document)
|
51
|
+
formatter = ::Lutaml::Uml::Formatter::Graphviz.new
|
52
|
+
formatter.type = :png
|
53
|
+
|
54
|
+
imagesdir = if parent.document.attr("imagesdir")
|
55
|
+
File.join(parent.document.attr("imagesdir"), "lutaml")
|
56
|
+
else
|
57
|
+
"lutaml"
|
58
|
+
end
|
59
|
+
result_path = Utils.relative_file_path(parent.document, imagesdir)
|
60
|
+
result_pathname = Pathname.new(result_path)
|
61
|
+
result_pathname.mkpath
|
62
|
+
File.writable?(result_pathname) || raise("Destination path #{result_path} not writable for Lutaml!")
|
63
|
+
|
64
|
+
outfile = Tempfile.new(["lutaml", ".png"])
|
65
|
+
outfile.binmode
|
66
|
+
outfile.puts(formatter.format(uml_document))
|
67
|
+
|
68
|
+
# Warning: metanorma/metanorma-standoc#187
|
69
|
+
# Windows Ruby 2.4 will crash if a Tempfile is "mv"ed.
|
70
|
+
# This is why we need to copy and then unlink.
|
71
|
+
filename = File.basename(outfile.path)
|
72
|
+
FileUtils.cp(outfile, result_pathname) && outfile.unlink
|
73
|
+
|
74
|
+
File.join(result_pathname, filename)
|
75
|
+
end
|
76
|
+
|
77
|
+
def generate_attrs(attrs)
|
78
|
+
%w(id align float title role width height alt)
|
79
|
+
.reduce({}) do |memo, key|
|
80
|
+
memo[key] = attrs[key] if attrs.has_key? key
|
81
|
+
memo
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -4,48 +4,53 @@ require "liquid"
|
|
4
4
|
require "asciidoctor"
|
5
5
|
require "asciidoctor/reader"
|
6
6
|
require "lutaml"
|
7
|
+
require "metanorma/plugin/lutaml/utils"
|
8
|
+
require "metanorma/plugin/lutaml/utils"
|
9
|
+
require "metanorma/plugin/lutaml/express_remarks_decorator"
|
7
10
|
|
8
11
|
module Metanorma
|
9
12
|
module Plugin
|
10
13
|
module Lutaml
|
11
14
|
# Class for processing Lutaml files
|
12
15
|
class LutamlPreprocessor < Asciidoctor::Extensions::Preprocessor
|
16
|
+
REMARKS_ATTRIBUTE = "remarks".freeze
|
13
17
|
|
14
18
|
def process(document, reader)
|
15
19
|
input_lines = reader.readlines.to_enum
|
16
|
-
|
20
|
+
express_indexes = Utils.parse_document_express_indexes(
|
21
|
+
document,
|
22
|
+
input_lines
|
23
|
+
)
|
24
|
+
Asciidoctor::Reader
|
25
|
+
.new(processed_lines(document, input_lines, express_indexes))
|
17
26
|
end
|
18
27
|
|
19
28
|
protected
|
20
29
|
|
21
30
|
def content_from_file(document, file_path)
|
22
31
|
::Lutaml::Parser
|
23
|
-
.parse(File.new(relative_file_path(document, file_path),
|
32
|
+
.parse(File.new(Utils.relative_file_path(document, file_path),
|
24
33
|
encoding: "UTF-8"))
|
25
34
|
end
|
26
35
|
|
27
36
|
private
|
28
37
|
|
29
|
-
def processed_lines(document, input_lines)
|
38
|
+
def processed_lines(document, input_lines, express_indexes)
|
30
39
|
result = []
|
31
40
|
loop do
|
32
|
-
result
|
41
|
+
result
|
42
|
+
.push(*process_text_blocks(
|
43
|
+
document,
|
44
|
+
input_lines,
|
45
|
+
express_indexes
|
46
|
+
))
|
33
47
|
end
|
34
48
|
result
|
35
49
|
end
|
36
50
|
|
37
|
-
def
|
38
|
-
docfile_directory = File.dirname(
|
39
|
-
document.attributes["docfile"] || "."
|
40
|
-
)
|
41
|
-
document
|
42
|
-
.path_resolver
|
43
|
-
.system_path(file_path, docfile_directory)
|
44
|
-
end
|
45
|
-
|
46
|
-
def process_text_blocks(document, input_lines)
|
51
|
+
def process_text_blocks(document, input_lines, express_indexes)
|
47
52
|
line = input_lines.next
|
48
|
-
block_match = line.match(/^\[lutaml,(
|
53
|
+
block_match = line.match(/^\[lutaml,([^,]+)?,?([^,]+)?,?([^,]+)?\]/)
|
49
54
|
return [line] if block_match.nil?
|
50
55
|
|
51
56
|
end_mark = input_lines.next
|
@@ -53,7 +58,8 @@ module Metanorma
|
|
53
58
|
collect_internal_block_lines(document,
|
54
59
|
input_lines,
|
55
60
|
end_mark),
|
56
|
-
block_match
|
61
|
+
block_match,
|
62
|
+
express_indexes)
|
57
63
|
end
|
58
64
|
|
59
65
|
def collect_internal_block_lines(_document, input_lines, end_mark)
|
@@ -64,49 +70,74 @@ module Metanorma
|
|
64
70
|
current_block
|
65
71
|
end
|
66
72
|
|
67
|
-
def
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
+
def contexts_items(block_match, document, express_indexes)
|
74
|
+
contexts_names = block_match[1].split(";").map(&:strip)
|
75
|
+
contexts_names.each_with_object([]) do |path, res|
|
76
|
+
if express_indexes[path]
|
77
|
+
res.push(*express_indexes[path])
|
78
|
+
else
|
79
|
+
res.push(content_from_file(document, path).to_liquid)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def parse_template(document, current_block, block_match, express_indexes)
|
85
|
+
options = parse_options(block_match[3])
|
86
|
+
.merge("relative_path_prefix" => File.dirname(block_match[1]))
|
87
|
+
contexts_items(block_match, document, express_indexes)
|
88
|
+
.map do |context_items|
|
89
|
+
parse_context_block(document: document,
|
90
|
+
context_lines: current_block,
|
91
|
+
context_items: decorate_context_items(context_items, options),
|
92
|
+
context_name: block_match[2].strip)
|
93
|
+
end.flatten
|
73
94
|
rescue StandardError => e
|
74
|
-
document.logger
|
75
|
-
.warn("Failed to parse lutaml \
|
76
|
-
block: #{e.message}")
|
95
|
+
document.logger.warn("Failed to parse lutaml block: #{e.message}")
|
77
96
|
[]
|
78
97
|
end
|
79
98
|
|
99
|
+
def parse_options(options_string)
|
100
|
+
options_string
|
101
|
+
.to_s
|
102
|
+
.scan(/(.+?)=(\s?[^\s]+)/)
|
103
|
+
.map { |elem| elem.map(&:strip) }
|
104
|
+
.to_h
|
105
|
+
end
|
106
|
+
|
107
|
+
def decorate_context_items(context_items, options)
|
108
|
+
return context_items if !context_items.is_a?(Hash)
|
109
|
+
|
110
|
+
context_items
|
111
|
+
.map do |(key, val)|
|
112
|
+
if val.is_a?(Hash)
|
113
|
+
[key, decorate_context_items(val, options)]
|
114
|
+
elsif key == REMARKS_ATTRIBUTE
|
115
|
+
[key,
|
116
|
+
val&.map do |remark|
|
117
|
+
Metanorma::Plugin::Lutaml::ExpressRemarksDecorator
|
118
|
+
.call(remark, options)
|
119
|
+
end]
|
120
|
+
elsif val.is_a?(Array)
|
121
|
+
[key, val.map { |n| decorate_context_items(n, options) }]
|
122
|
+
else
|
123
|
+
[key, val]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
.to_h
|
127
|
+
end
|
128
|
+
|
80
129
|
def parse_context_block(context_lines:,
|
81
130
|
context_items:,
|
82
131
|
context_name:,
|
83
132
|
document:)
|
84
|
-
render_result, errors = render_liquid_string(
|
133
|
+
render_result, errors = Utils.render_liquid_string(
|
85
134
|
template_string: context_lines.join("\n"),
|
86
135
|
context_items: context_items,
|
87
136
|
context_name: context_name
|
88
137
|
)
|
89
|
-
notify_render_errors(document, errors)
|
138
|
+
Utils.notify_render_errors(document, errors)
|
90
139
|
render_result.split("\n")
|
91
140
|
end
|
92
|
-
|
93
|
-
def render_liquid_string(template_string:, context_items:,
|
94
|
-
context_name:)
|
95
|
-
liquid_template = Liquid::Template.parse(template_string)
|
96
|
-
rendered_string = liquid_template
|
97
|
-
.render(context_name => context_items,
|
98
|
-
strict_variables: true,
|
99
|
-
error_mode: :warn)
|
100
|
-
[rendered_string, liquid_template.errors]
|
101
|
-
end
|
102
|
-
|
103
|
-
def notify_render_errors(document, errors)
|
104
|
-
errors.each do |error_obj|
|
105
|
-
document
|
106
|
-
.logger
|
107
|
-
.warn("Liquid render error: #{error_obj.message}")
|
108
|
-
end
|
109
|
-
end
|
110
141
|
end
|
111
142
|
end
|
112
143
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "liquid"
|
4
|
+
require "asciidoctor"
|
5
|
+
require "asciidoctor/reader"
|
6
|
+
require "lutaml"
|
7
|
+
require "lutaml/uml"
|
8
|
+
require "metanorma/plugin/lutaml/utils"
|
9
|
+
|
10
|
+
module Metanorma
|
11
|
+
module Plugin
|
12
|
+
module Lutaml
|
13
|
+
# Macro for quick rendering of datamodel attributes/values table
|
14
|
+
# @example [lutaml_uml_attributes_table,path/to/lutaml,EntityName]
|
15
|
+
class LutamlUmlAttributesTablePreprocessor < Asciidoctor::Extensions::Preprocessor
|
16
|
+
MARCO_REGEXP =
|
17
|
+
/\[lutaml_uml_attributes_table,([^,]+),?(.+)?,([^,]+),?(.+)?\]/
|
18
|
+
# search document for block `datamodel_attributes_table`
|
19
|
+
# read include derectives that goes after that in block and transform
|
20
|
+
# into yaml2text blocks
|
21
|
+
def process(document, reader)
|
22
|
+
input_lines = reader.readlines.to_enum
|
23
|
+
Asciidoctor::Reader.new(processed_lines(document, input_lines))
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def lutaml_document_from_file(document, file_path)
|
29
|
+
::Lutaml::Parser
|
30
|
+
.parse(File.new(Utils.relative_file_path(document, file_path),
|
31
|
+
encoding: "UTF-8"))
|
32
|
+
end
|
33
|
+
|
34
|
+
def processed_lines(document, input_lines)
|
35
|
+
input_lines.each_with_object([]) do |line, result|
|
36
|
+
if match = line.match(MARCO_REGEXP)
|
37
|
+
lutaml_path = match[1]
|
38
|
+
entity_name = match[3]
|
39
|
+
result.push(*parse_marco(lutaml_path, entity_name, document))
|
40
|
+
else
|
41
|
+
result.push(line)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def parse_marco(lutaml_path, entity_name, document)
|
47
|
+
lutaml_document = lutaml_document_from_file(document, lutaml_path)
|
48
|
+
.serialized_document
|
49
|
+
entities = [lutaml_document["classes"], lutaml_document["enums"]]
|
50
|
+
.compact
|
51
|
+
.flatten
|
52
|
+
entity_definition = entities.detect do |klass|
|
53
|
+
klass["name"] == entity_name.strip
|
54
|
+
end
|
55
|
+
model_representation(entity_definition, document)
|
56
|
+
end
|
57
|
+
|
58
|
+
def model_representation(entity_definition, document)
|
59
|
+
render_result, errors = Utils.render_liquid_string(
|
60
|
+
template_string: table_template,
|
61
|
+
context_items: entity_definition,
|
62
|
+
context_name: "definition"
|
63
|
+
)
|
64
|
+
Utils.notify_render_errors(document, errors)
|
65
|
+
render_result.split("\n")
|
66
|
+
end
|
67
|
+
|
68
|
+
# rubocop:disable Layout/IndentHeredoc
|
69
|
+
def table_template
|
70
|
+
<<~TEMPLATE
|
71
|
+
=== {{ definition.name }}
|
72
|
+
{{ definition.definition }}
|
73
|
+
|
74
|
+
{% if definition.attributes %}
|
75
|
+
{% if definition.keyword == 'enumeration' %}
|
76
|
+
.{{ definition.name }} values
|
77
|
+
|===
|
78
|
+
|Name |Definition
|
79
|
+
|
80
|
+
{% for item in definition.attributes %}
|
81
|
+
|{{ item.name }} |{{ item.definition }}
|
82
|
+
{% endfor %}
|
83
|
+
|===
|
84
|
+
{% else %}
|
85
|
+
.{{ definition.name }} attributes
|
86
|
+
|===
|
87
|
+
|Name |Definition |Mandatory/ Optional/ Conditional |Max Occur |Data Type
|
88
|
+
|
89
|
+
{% for item in definition.attributes %}
|
90
|
+
|{{ item.name }} |{% if item.definition %}{{ item.definition }}{% else %}TODO: enum {{ key }}'s definition{% endif %} |{% if item.cardinality.min == "0" %}O{% else %}M{% endif %} |{% if item.cardinality.max == "*" %}N{% else %}1{% endif %} |{% if item.origin %}<<{{ item.origin }}>>{% endif %} `{{ item.type }}`
|
91
|
+
{% endfor %}
|
92
|
+
|===
|
93
|
+
{% endif %}
|
94
|
+
{% endif %}
|
95
|
+
|
96
|
+
TEMPLATE
|
97
|
+
end
|
98
|
+
# rubocop:enable Layout/IndentHeredoc
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Metanorma
|
2
|
+
module Plugin
|
3
|
+
module Lutaml
|
4
|
+
# Helpers for lutaml macroses
|
5
|
+
module Utils
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def relative_file_path(document, file_path)
|
9
|
+
docfile_directory = File.dirname(
|
10
|
+
document.attributes["docfile"] || "."
|
11
|
+
)
|
12
|
+
document
|
13
|
+
.path_resolver
|
14
|
+
.system_path(file_path, docfile_directory)
|
15
|
+
end
|
16
|
+
|
17
|
+
def render_liquid_string(template_string:, context_items:,
|
18
|
+
context_name:)
|
19
|
+
liquid_template = Liquid::Template.parse(template_string)
|
20
|
+
rendered_string = liquid_template
|
21
|
+
.render(context_name => context_items,
|
22
|
+
strict_variables: true,
|
23
|
+
error_mode: :warn)
|
24
|
+
[rendered_string, liquid_template.errors]
|
25
|
+
end
|
26
|
+
|
27
|
+
def notify_render_errors(document, errors)
|
28
|
+
errors.each do |error_obj|
|
29
|
+
document
|
30
|
+
.logger
|
31
|
+
.warn("Liquid render error: #{error_obj.message}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def process_express_index(folder, name, idxs, document)
|
36
|
+
idxs[name] = []
|
37
|
+
Dir["#{Utils.relative_file_path(document, folder)}/*"].each do |path|
|
38
|
+
next if [".", ".."].include?(path)
|
39
|
+
|
40
|
+
begin
|
41
|
+
idxs[name]
|
42
|
+
.push(::Lutaml::Parser.parse(File.new(path, encoding: "UTF-8")).to_liquid)
|
43
|
+
rescue StandardError => e
|
44
|
+
document.logger.warn("Failed to load #{path}: #{e.message}")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_document_express_indexes(document, input_lines)
|
50
|
+
express_indexes = {}
|
51
|
+
loop do
|
52
|
+
line = input_lines.next
|
53
|
+
break if line.length.zero?
|
54
|
+
|
55
|
+
_, name, folder = line.match(/^:lutaml-express-index:(.+?);(.+?)$/)&.to_a
|
56
|
+
if folder && name
|
57
|
+
process_express_index(
|
58
|
+
folder.strip.gsub(";", ""),
|
59
|
+
name.strip,
|
60
|
+
express_indexes,
|
61
|
+
document
|
62
|
+
)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
express_indexes
|
66
|
+
rescue StopIteration
|
67
|
+
express_indexes
|
68
|
+
ensure
|
69
|
+
input_lines.rewind
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.require_paths = ["lib"]
|
25
25
|
|
26
26
|
spec.add_dependency "liquid"
|
27
|
-
spec.add_dependency "lutaml", "
|
27
|
+
spec.add_dependency "lutaml", "0.4.1.pre.alpha"
|
28
28
|
spec.add_dependency "lutaml-uml", "~> 0.2.0"
|
29
29
|
spec.add_dependency "metanorma"
|
30
30
|
spec.add_dependency "relaton-cli"
|
Binary file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metanorma-plugin-lutaml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.4.alpha
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ribose Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: liquid
|
@@ -28,16 +28,16 @@ dependencies:
|
|
28
28
|
name: lutaml
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.4.1.pre.alpha
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.
|
40
|
+
version: 0.4.1.pre.alpha
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: lutaml-uml
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -241,6 +241,7 @@ executables: []
|
|
241
241
|
extensions: []
|
242
242
|
extra_rdoc_files: []
|
243
243
|
files:
|
244
|
+
- ".github/workflows/rake.yml"
|
244
245
|
- ".gitignore"
|
245
246
|
- CODE_OF_CONDUCT.md
|
246
247
|
- Gemfile
|
@@ -250,9 +251,14 @@ files:
|
|
250
251
|
- bin/console
|
251
252
|
- bin/setup
|
252
253
|
- lib/metanorma-plugin-lutaml.rb
|
254
|
+
- lib/metanorma/plugin/lutaml/express_remarks_decorator.rb
|
255
|
+
- lib/metanorma/plugin/lutaml/lutaml_diagram_block.rb
|
253
256
|
- lib/metanorma/plugin/lutaml/lutaml_preprocessor.rb
|
257
|
+
- lib/metanorma/plugin/lutaml/lutaml_uml_attributes_table_preprocessor.rb
|
258
|
+
- lib/metanorma/plugin/lutaml/utils.rb
|
254
259
|
- lib/metanorma/plugin/lutaml/version.rb
|
255
260
|
- metanorma-plugin-lutaml.gemspec
|
261
|
+
- pkg/metanorma-plugin-lutaml-0.2.0.gem
|
256
262
|
homepage: https://github.com/metanorma/metanorma-plugin-lutaml
|
257
263
|
licenses:
|
258
264
|
- BSD-2-Clause
|
@@ -268,9 +274,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
268
274
|
version: '0'
|
269
275
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
270
276
|
requirements:
|
271
|
-
- - "
|
277
|
+
- - ">"
|
272
278
|
- !ruby/object:Gem::Version
|
273
|
-
version:
|
279
|
+
version: 1.3.1
|
274
280
|
requirements: []
|
275
281
|
rubygems_version: 3.0.3
|
276
282
|
signing_key:
|