metanorma-plugin-lutaml 0.1.0 → 0.2.4.alpha
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|