metanorma-plugin-lutaml 0.7.30 → 0.7.31
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/Gemfile +10 -8
- data/README.adoc +16 -1030
- data/docs/usages/enterprise_architect.adoc +583 -0
- data/docs/usages/express.adoc +299 -0
- data/docs/usages/json_yaml.adoc +1034 -0
- data/docs/usages/lutaml-gml.adoc +73 -0
- data/docs/usages/lutaml-uml.adoc +73 -0
- data/lib/metanorma/plugin/lutaml/asciidoctor/preprocessor.rb +1 -1
- data/lib/metanorma/plugin/lutaml/base_structured_text_preprocessor.rb +192 -0
- data/lib/metanorma/plugin/lutaml/content.rb +89 -0
- data/lib/metanorma/plugin/lutaml/data2_text_preprocessor.rb +45 -0
- data/lib/metanorma/plugin/lutaml/express_remarks_decorator.rb +19 -6
- data/lib/metanorma/plugin/lutaml/json2_text_preprocessor.rb +43 -0
- data/lib/metanorma/plugin/lutaml/liquid/custom_blocks/key_iterator.rb +31 -0
- data/lib/metanorma/plugin/lutaml/liquid/custom_filters/loadfile.rb +18 -0
- data/lib/metanorma/plugin/lutaml/liquid/custom_filters/replace_regex.rb +14 -0
- data/lib/metanorma/plugin/lutaml/liquid/custom_filters/values.rb +13 -0
- data/lib/metanorma/plugin/lutaml/liquid/multiply_local_file_system.rb +29 -22
- data/lib/metanorma/plugin/lutaml/liquid_drops/gml_dictionary_drop.rb +1 -1
- data/lib/metanorma/plugin/lutaml/lutaml_diagram_base.rb +1 -1
- data/lib/metanorma/plugin/lutaml/lutaml_diagram_block_macro.rb +2 -1
- data/lib/metanorma/plugin/lutaml/lutaml_ea_diagram_block_macro.rb +2 -1
- data/lib/metanorma/plugin/lutaml/lutaml_ea_xmi_base.rb +48 -36
- data/lib/metanorma/plugin/lutaml/lutaml_figure_inline_macro.rb +2 -1
- data/lib/metanorma/plugin/lutaml/lutaml_gml_dictionary_block_macro.rb +3 -1
- data/lib/metanorma/plugin/lutaml/lutaml_klass_table_block_macro.rb +2 -1
- data/lib/metanorma/plugin/lutaml/lutaml_preprocessor.rb +15 -2
- data/lib/metanorma/plugin/lutaml/lutaml_table_inline_macro.rb +2 -1
- data/lib/metanorma/plugin/lutaml/source_extractor.rb +97 -0
- data/lib/metanorma/plugin/lutaml/utils.rb +59 -26
- data/lib/metanorma/plugin/lutaml/version.rb +1 -1
- data/lib/metanorma/plugin/lutaml/yaml2_text_preprocessor.rb +41 -0
- data/lib/metanorma-plugin-lutaml.rb +3 -0
- data/metanorma-plugin-lutaml.gemspec +7 -1
- metadata +35 -6
- data/lib/metanorma/plugin/lutaml/liquid_templates/test.rb +0 -1
- /data/lib/metanorma/plugin/lutaml/liquid/{custom_filters.rb → custom_filters/html2adoc.rb} +0 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
== Usage with LutaML GML Dictionary
|
3
|
+
|
4
|
+
=== Rendering a LutaML GML Dictionary: `lutaml_gml_dictionary`
|
5
|
+
|
6
|
+
This command allows to render a LutaML GML Dictionary by using Liquid Drop.
|
7
|
+
|
8
|
+
GmlDictionaryDrop has the following attributes:
|
9
|
+
|
10
|
+
* name
|
11
|
+
* file_name
|
12
|
+
* dictionary_entry
|
13
|
+
|
14
|
+
Each `dictionary_entry` has the following attributes:
|
15
|
+
|
16
|
+
* name
|
17
|
+
* description
|
18
|
+
|
19
|
+
[source,adoc]
|
20
|
+
----
|
21
|
+
lutaml_gml_dictionary::/path/to/dictionary.xml[template="/path/to/template.liquid",context=dict]
|
22
|
+
----
|
23
|
+
|
24
|
+
The command accepts the options listed below:
|
25
|
+
|
26
|
+
* `/path/to/dictionary.xml` specifies the path of xml file of the
|
27
|
+
GML Dictionary.
|
28
|
+
|
29
|
+
* `template="/path/to/template.liquid"` specifies the liquid template.
|
30
|
+
For example, you can create a liquid template and link it by `template`.
|
31
|
+
|
32
|
+
* `context=dict` define the context in the template.
|
33
|
+
|
34
|
+
[source,adoc]
|
35
|
+
----
|
36
|
+
[cols="3a,22a"]
|
37
|
+
|===
|
38
|
+
| Name | {{ dict.file_name }}
|
39
|
+
|
40
|
+
h| Code h| Description
|
41
|
+
{% for entry in dict.dictionary_entry %}
|
42
|
+
| {{ entry.name }} | {{ entry.description }}
|
43
|
+
{% endfor %}
|
44
|
+
|===
|
45
|
+
|
46
|
+
[.source]
|
47
|
+
<<source_link>>
|
48
|
+
----
|
49
|
+
|
50
|
+
In spite of specifying the path of the template, you can also define an inline
|
51
|
+
template within a block by
|
52
|
+
`[lutaml_gml_dictionary,"/path/to/dictionary.xml",context=dict]`.
|
53
|
+
|
54
|
+
[source,adoc]
|
55
|
+
----
|
56
|
+
[lutaml_gml_dictionary,"/path/to/dictionary.xml",context=dict]
|
57
|
+
--
|
58
|
+
{% capture link %}https://www.test.com/{{ dict.file_name }}{% endcapture %}
|
59
|
+
|
60
|
+
[cols="3a,22a"]
|
61
|
+
|===
|
62
|
+
| File Name | {{ dict.file_name }}
|
63
|
+
h| URL | {{ link }}
|
64
|
+
h| Help | Description
|
65
|
+
{% for entry in dict.dictionary_entry %}
|
66
|
+
| {{ entry.name }} | {{ entry.description }}
|
67
|
+
{% endfor %}
|
68
|
+
|===
|
69
|
+
|
70
|
+
[.source]
|
71
|
+
<<source_link>>
|
72
|
+
--
|
73
|
+
----
|
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
== Usage with Lutaml-UML
|
3
|
+
|
4
|
+
=== General
|
5
|
+
|
6
|
+
The LutaML plugin supports working with LutaML UML files to render UML diagrams
|
7
|
+
and class definitions.
|
8
|
+
|
9
|
+
|
10
|
+
=== Rendering a LutaML view: `lutaml_diagram`
|
11
|
+
|
12
|
+
This command allows to quickly render a LutaML view as an image file.
|
13
|
+
|
14
|
+
Given a file `example.lutaml` file with content:
|
15
|
+
|
16
|
+
[source,java]
|
17
|
+
----
|
18
|
+
diagram MyView {
|
19
|
+
title "my diagram"
|
20
|
+
|
21
|
+
enum AddressClassProfile {
|
22
|
+
imlicistAttributeProfile: CharacterString [0..1] {
|
23
|
+
definition
|
24
|
+
this is multiline with `asciidoc`
|
25
|
+
end definition
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
class AttributeProfile {
|
30
|
+
+addressClassProfile: CharacterString [0..1]
|
31
|
+
imlicistAttributeProfile: CharacterString [0..1] {
|
32
|
+
definition this is attribute definition
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
----
|
37
|
+
|
38
|
+
The `lutaml_diagram` command will add the image to the document.
|
39
|
+
|
40
|
+
[source,adoc]
|
41
|
+
----
|
42
|
+
lutaml_diagram::example.lutaml[]
|
43
|
+
----
|
44
|
+
|
45
|
+
The `lutaml_diagram` command can also be used to denote a block with an embedded
|
46
|
+
LutaML view.
|
47
|
+
|
48
|
+
For example:
|
49
|
+
|
50
|
+
[source,java]
|
51
|
+
----
|
52
|
+
[lutaml_diagram]
|
53
|
+
....
|
54
|
+
diagram MyView {
|
55
|
+
title "my diagram"
|
56
|
+
|
57
|
+
enum AddressClassProfile {
|
58
|
+
imlicistAttributeProfile: CharacterString [0..1] {
|
59
|
+
definition {
|
60
|
+
This is multiline AsciiDoc content.
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
class AttributeProfile {
|
66
|
+
+addressClassProfile: CharacterString [0..1]
|
67
|
+
imlicistAttributeProfile: CharacterString [0..1] {
|
68
|
+
definition this is attribute definition
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}
|
72
|
+
....
|
73
|
+
----
|
@@ -0,0 +1,192 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "liquid"
|
4
|
+
require "asciidoctor"
|
5
|
+
require "asciidoctor/reader"
|
6
|
+
require_relative "source_extractor"
|
7
|
+
require_relative "utils"
|
8
|
+
require "metanorma/plugin/lutaml/asciidoctor/preprocessor"
|
9
|
+
|
10
|
+
module Metanorma
|
11
|
+
module Plugin
|
12
|
+
module Lutaml
|
13
|
+
# Base class for processing structured data blocks(yaml, json)
|
14
|
+
class BaseStructuredTextPreprocessor <
|
15
|
+
::Asciidoctor::Extensions::Preprocessor
|
16
|
+
include Utils
|
17
|
+
|
18
|
+
BLOCK_START_REGEXP = /\{(.+?)\.\*,(.+),(.+)\}/.freeze
|
19
|
+
BLOCK_END_REGEXP = /\A\{[A-Z]+\}\z/.freeze
|
20
|
+
LOAD_FILE_REGEXP = /{% assign (.*) = (.*) \| load_file %}/.freeze
|
21
|
+
INCLUDE_PATH_OPTION = "include_path"
|
22
|
+
TEMPLATE_OPTION = "template"
|
23
|
+
|
24
|
+
def process(document, reader)
|
25
|
+
r = Asciidoctor::PreprocessorNoIfdefsReader
|
26
|
+
.new(document, reader.lines)
|
27
|
+
input_lines = r.readlines
|
28
|
+
Metanorma::Plugin::Lutaml::SourceExtractor
|
29
|
+
.extract(document, input_lines)
|
30
|
+
result_content = processed_lines(document, input_lines.to_enum)
|
31
|
+
Asciidoctor::PreprocessorNoIfdefsReader.new(document, result_content)
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def content_from_file(_document, _file_path)
|
37
|
+
raise ArgumentError, "Implement `content_from_file` in your class"
|
38
|
+
end
|
39
|
+
|
40
|
+
def content_from_anchor(_document, _file_path)
|
41
|
+
raise ArgumentError, "Implement `content_from_anchor` in your class"
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def process_text_blocks(document, input_lines)
|
47
|
+
line = input_lines.next
|
48
|
+
block_match = line.match(/^\[#{config[:block_name]},(.+?)\]/)
|
49
|
+
return [line] if block_match.nil?
|
50
|
+
|
51
|
+
end_mark = input_lines.next
|
52
|
+
parse_template(
|
53
|
+
document,
|
54
|
+
collect_internal_block_lines(document, input_lines, end_mark),
|
55
|
+
block_match,
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
def collect_internal_block_lines(document, input_lines, end_mark) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
60
|
+
current_block = []
|
61
|
+
while (block_line = input_lines.next) != end_mark
|
62
|
+
if block_line.match?(LOAD_FILE_REGEXP)
|
63
|
+
load_file_match = block_line.match(LOAD_FILE_REGEXP)
|
64
|
+
|
65
|
+
# Add parent folder as argument to loadfile filter
|
66
|
+
block_line = "{% assign #{load_file_match[1]} = "\
|
67
|
+
"#{load_file_match[2]} | loadfile: " \
|
68
|
+
"\"#{document.attributes['docdir']}\" %}"
|
69
|
+
end
|
70
|
+
|
71
|
+
current_block.push(block_line)
|
72
|
+
end
|
73
|
+
current_block
|
74
|
+
end
|
75
|
+
|
76
|
+
def data_file_type
|
77
|
+
@config[:block_name].split("2").first
|
78
|
+
end
|
79
|
+
|
80
|
+
def parse_template(document, current_block, block_match) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
81
|
+
transformed_liquid_lines = current_block.map do |x|
|
82
|
+
transform_line_liquid(x)
|
83
|
+
end
|
84
|
+
|
85
|
+
contexts, include_path, template_path = resolve_context(
|
86
|
+
document, block_match
|
87
|
+
)
|
88
|
+
|
89
|
+
parse_context_block(
|
90
|
+
document: document,
|
91
|
+
context_lines: transformed_liquid_lines,
|
92
|
+
contexts: contexts,
|
93
|
+
include_path: include_path,
|
94
|
+
template_path: template_path,
|
95
|
+
)
|
96
|
+
rescue StandardError => e
|
97
|
+
::Metanorma::Util.log("Failed to parse #{config[:block_name]} \
|
98
|
+
block: #{e.message}", :error)
|
99
|
+
[]
|
100
|
+
end
|
101
|
+
|
102
|
+
def resolve_context(document, block_match) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
103
|
+
include_path = nil
|
104
|
+
template_path = nil
|
105
|
+
|
106
|
+
contexts = if block_match[1].include?("=")
|
107
|
+
multiple_contexts, include_path, template_path =
|
108
|
+
content_from_multiple_contexts(
|
109
|
+
document, block_match[1].split(",")
|
110
|
+
)
|
111
|
+
multiple_contexts
|
112
|
+
elsif block_match[1].start_with?("#")
|
113
|
+
anchor = block_match[1].split(",").first.strip[1..-1]
|
114
|
+
{
|
115
|
+
block_match[1].split(",").last.strip =>
|
116
|
+
content_from_anchor(document, anchor),
|
117
|
+
}
|
118
|
+
else
|
119
|
+
path = block_match[1].split(",").first.strip
|
120
|
+
{
|
121
|
+
block_match[1].split(",").last.strip =>
|
122
|
+
content_from_file(document, path),
|
123
|
+
}
|
124
|
+
end
|
125
|
+
[contexts, include_path, template_path]
|
126
|
+
end
|
127
|
+
|
128
|
+
def content_from_multiple_contexts(document, context_and_paths) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
129
|
+
contexts = {}
|
130
|
+
include_path = nil
|
131
|
+
template_path = nil
|
132
|
+
|
133
|
+
context_and_paths.each do |context_and_path|
|
134
|
+
context_and_path.strip!
|
135
|
+
context_name, path = context_and_path.split("=")
|
136
|
+
|
137
|
+
if context_name == INCLUDE_PATH_OPTION
|
138
|
+
include_path = path
|
139
|
+
elsif context_name == TEMPLATE_OPTION
|
140
|
+
template_path = path
|
141
|
+
else
|
142
|
+
context_items = content_from_file(document, path)
|
143
|
+
contexts[context_name] = context_items
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
[contexts, include_path, template_path]
|
148
|
+
end
|
149
|
+
|
150
|
+
def transform_line_liquid(line) # rubocop:disable Metrics/MethodLength
|
151
|
+
if line.match?(BLOCK_START_REGEXP)
|
152
|
+
line.gsub!(BLOCK_START_REGEXP, '{% keyiterator \1, \2 %}')
|
153
|
+
end
|
154
|
+
|
155
|
+
if line.strip.match?(BLOCK_END_REGEXP)
|
156
|
+
line.gsub!(BLOCK_END_REGEXP, "{% endkeyiterator %}")
|
157
|
+
end
|
158
|
+
line
|
159
|
+
.gsub(/(?<!{){(?!%)([^{}]{1,900})(?<!%)}(?!})/, '{{\1}}')
|
160
|
+
.gsub(/[a-z\.]{1,900}\#/, "index")
|
161
|
+
.gsub(/{{([^}]{1,900})\s+\+\s+(\d+)\s*?}}/, '{{ \1 | plus: \2 }}')
|
162
|
+
.gsub(/{{([^}]{1,900})\s+-\s+(\d+)\s*?}}/, '{{ \1 | minus: \2 }}')
|
163
|
+
.gsub(/{{([^}]{1,500})\.values([^}]{0,500})}}/,
|
164
|
+
'{% assign custom_value = \1 | values %}{{custom_value\2}}')
|
165
|
+
end
|
166
|
+
|
167
|
+
def parse_context_block( # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
168
|
+
context_lines:, contexts:, document:,
|
169
|
+
include_path: nil, template_path: nil
|
170
|
+
)
|
171
|
+
if include_path
|
172
|
+
include_path = relative_file_path(document, include_path)
|
173
|
+
end
|
174
|
+
|
175
|
+
if template_path
|
176
|
+
template_path = relative_file_path(document, template_path)
|
177
|
+
end
|
178
|
+
|
179
|
+
render_result, errors = render_liquid_string(
|
180
|
+
template_string: context_lines.join("\n"),
|
181
|
+
contexts: contexts,
|
182
|
+
document: document,
|
183
|
+
include_path: include_path,
|
184
|
+
template_path: template_path,
|
185
|
+
)
|
186
|
+
notify_render_errors(document, errors)
|
187
|
+
render_result.split("\n")
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "yaml"
|
5
|
+
|
6
|
+
module Metanorma
|
7
|
+
module Plugin
|
8
|
+
module Lutaml
|
9
|
+
module Content
|
10
|
+
protected
|
11
|
+
|
12
|
+
# https://ruby-doc.org/stdlib-2.5.1/libdoc/psych/rdoc/Psych.html#method-c-safe_load
|
13
|
+
def yaml_content_from_file(resolved_file_path) # rubocop:disable Metrics/MethodLength
|
14
|
+
unless File.exist?(resolved_file_path)
|
15
|
+
::Metanorma::Util.log(
|
16
|
+
"YAML file referenced in [yaml2text] block not found: " \
|
17
|
+
"#{resolved_file_path}", :error
|
18
|
+
)
|
19
|
+
return
|
20
|
+
end
|
21
|
+
|
22
|
+
YAML.safe_load(
|
23
|
+
File.read(resolved_file_path, encoding: "UTF-8"),
|
24
|
+
permitted_classes: [Date, Time, Symbol],
|
25
|
+
permitted_symbols: [],
|
26
|
+
aliases: true,
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def yaml_content_from_anchor(document, anchor)
|
31
|
+
YAML.safe_load(
|
32
|
+
document.attributes["source_blocks"][anchor],
|
33
|
+
permitted_classes: [Date, Time, Symbol],
|
34
|
+
permitted_symbols: [],
|
35
|
+
aliases: true,
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def json_content_from_file(resolved_file_path)
|
40
|
+
JSON.parse(File.read(resolved_file_path, encoding: "UTF-8"))
|
41
|
+
end
|
42
|
+
|
43
|
+
def json_content_from_anchor(document, anchor)
|
44
|
+
JSON.parse(document.attributes["source_blocks"][anchor])
|
45
|
+
end
|
46
|
+
|
47
|
+
def content_from_file(document, file_path)
|
48
|
+
resolved_file_path = relative_file_path(document, file_path)
|
49
|
+
load_content_from_file(resolved_file_path)
|
50
|
+
end
|
51
|
+
|
52
|
+
def load_content_from_file(resolved_file_path)
|
53
|
+
unless File.exist?(resolved_file_path)
|
54
|
+
::Metanorma::Util
|
55
|
+
.log("Failed to load content from file: #{resolved_file_path}",
|
56
|
+
:error)
|
57
|
+
end
|
58
|
+
|
59
|
+
if json_file?(resolved_file_path)
|
60
|
+
json_content_from_file(resolved_file_path)
|
61
|
+
else
|
62
|
+
yaml_content_from_file(resolved_file_path)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def content_from_anchor(document, anchor)
|
67
|
+
source_block = document.attributes["source_blocks"][anchor]
|
68
|
+
if json_content?(source_block)
|
69
|
+
json_content_from_anchor(document, anchor)
|
70
|
+
else
|
71
|
+
yaml_content_from_anchor(document, anchor)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def json_or_yaml_filepath?(file_path)
|
76
|
+
file_path.end_with?(".json", ".yaml", ".yml")
|
77
|
+
end
|
78
|
+
|
79
|
+
def json_file?(file_path)
|
80
|
+
file_path.end_with?(".json")
|
81
|
+
end
|
82
|
+
|
83
|
+
def json_content?(content)
|
84
|
+
content.start_with?("{", "[")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "content"
|
4
|
+
require_relative "base_structured_text_preprocessor"
|
5
|
+
|
6
|
+
module Metanorma
|
7
|
+
module Plugin
|
8
|
+
module Lutaml
|
9
|
+
class Data2TextPreprocessor < BaseStructuredTextPreprocessor
|
10
|
+
include Content
|
11
|
+
# search document for block `data2text`
|
12
|
+
# after that take template from block and read file into this template
|
13
|
+
# example:
|
14
|
+
# [data2text,my_yaml=foobar.yaml,my_json=foobar.json]
|
15
|
+
# ----
|
16
|
+
# === {foobar.name}
|
17
|
+
# {foobar.desc}
|
18
|
+
#
|
19
|
+
# {my_json.symbol}:: {my_json.symbol_def}
|
20
|
+
# ----
|
21
|
+
#
|
22
|
+
# with content of `foobar.yaml` file equal to:
|
23
|
+
# - name: spaghetti
|
24
|
+
# desc: wheat noodles of 9mm diameter
|
25
|
+
#
|
26
|
+
# and content of `foobar.json` file equal to:
|
27
|
+
# {
|
28
|
+
# "symbol": "SPAG",
|
29
|
+
# "symbol_def": "the situation is message like spaghetti",
|
30
|
+
# }
|
31
|
+
#
|
32
|
+
# will produce:
|
33
|
+
# === spaghetti
|
34
|
+
# wheat noodles of 9mm diameter
|
35
|
+
#
|
36
|
+
# SPAG:: the situation is message like spaghetti
|
37
|
+
|
38
|
+
def initialize(config = {})
|
39
|
+
super
|
40
|
+
@config[:block_name] = "data2text"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -4,7 +4,19 @@ module Metanorma
|
|
4
4
|
module Plugin
|
5
5
|
module Lutaml
|
6
6
|
class ExpressRemarksDecorator
|
7
|
-
RELATIVE_PREFIX_MACRO_REGEXP =
|
7
|
+
RELATIVE_PREFIX_MACRO_REGEXP = %r{
|
8
|
+
^ # Start of line
|
9
|
+
(link|image|video|audio|include) # Capture group 1: content type
|
10
|
+
(:+)? # Capture group 2: optional colons
|
11
|
+
(?! # Negative lookahead
|
12
|
+
[^\/:]+://| # Don't match URLs (http://, etc.)
|
13
|
+
[A-Z]:/| # Don't match Windows paths
|
14
|
+
/ # Don't match absolute paths
|
15
|
+
) # End negative lookahead
|
16
|
+
([^:\[]+) # Capture group 3: the path/name
|
17
|
+
(\[.*\])? # Capture group 4: optional attribute
|
18
|
+
$ # End of line
|
19
|
+
}x.freeze
|
8
20
|
|
9
21
|
attr_reader :remark, :options
|
10
22
|
|
@@ -21,7 +33,7 @@ module Metanorma
|
|
21
33
|
result = remark
|
22
34
|
if options["relative_path_prefix"]
|
23
35
|
result = update_relative_paths(result,
|
24
|
-
|
36
|
+
options["relative_path_prefix"])
|
25
37
|
end
|
26
38
|
result
|
27
39
|
end
|
@@ -44,10 +56,11 @@ module Metanorma
|
|
44
56
|
def prefix_relative_paths(line, path_prefix)
|
45
57
|
line.gsub(RELATIVE_PREFIX_MACRO_REGEXP) do |_match|
|
46
58
|
prefixed_path = File.join(path_prefix, $3.strip)
|
47
|
-
#
|
48
|
-
#
|
49
|
-
|
50
|
-
|
59
|
+
# Transform relative path (../path/to/file) into the absolute path
|
60
|
+
# because `image::` macro wont understand it other way
|
61
|
+
if prefixed_path.start_with?("../")
|
62
|
+
prefixed_path = File.absolute_path(prefixed_path)
|
63
|
+
end
|
51
64
|
full_path = File.expand_path(prefixed_path)
|
52
65
|
"#{$1}#{$2}#{full_path}#{$4}"
|
53
66
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "content"
|
4
|
+
require_relative "base_structured_text_preprocessor"
|
5
|
+
|
6
|
+
module Metanorma
|
7
|
+
module Plugin
|
8
|
+
module Lutaml
|
9
|
+
class Json2TextPreprocessor < BaseStructuredTextPreprocessor
|
10
|
+
include Content
|
11
|
+
# search document for block `json2text`
|
12
|
+
# after that take template from block and read file into this template
|
13
|
+
# example:
|
14
|
+
# [json2text,foobar.json]
|
15
|
+
# ----
|
16
|
+
# === {item.name}
|
17
|
+
# {item.desc}
|
18
|
+
#
|
19
|
+
# {item.symbol}:: {item.symbol_def}
|
20
|
+
# ----
|
21
|
+
#
|
22
|
+
# with content of `foobar.json` file equal to:
|
23
|
+
# {
|
24
|
+
# "name": "spaghetti",
|
25
|
+
# "desc": "wheat noodles of 9mm diameter".
|
26
|
+
# "symbol": "SPAG",
|
27
|
+
# "symbol_def": "the situation is message like spaghetti",
|
28
|
+
# }
|
29
|
+
#
|
30
|
+
# will produce:
|
31
|
+
# === spaghetti
|
32
|
+
# wheat noodles of 9mm diameter
|
33
|
+
#
|
34
|
+
# SPAG:: the situation is message like spaghetti
|
35
|
+
|
36
|
+
def initialize(config = {})
|
37
|
+
super
|
38
|
+
@config[:block_name] = "json2text"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Metanorma
|
2
|
+
module Plugin
|
3
|
+
module Lutaml
|
4
|
+
module Liquid
|
5
|
+
module CustomBlocks
|
6
|
+
class KeyIterator < ::Liquid::Block
|
7
|
+
def initialize(tag_name, markup, tokens)
|
8
|
+
super
|
9
|
+
@context_name, @var_name = markup.split(",").map(&:strip)
|
10
|
+
end
|
11
|
+
|
12
|
+
def render(context) # rubocop:disable Metrics/MethodLength
|
13
|
+
res = ""
|
14
|
+
iterator = if context[@context_name].is_a?(Hash)
|
15
|
+
context[@context_name].keys
|
16
|
+
else
|
17
|
+
context[@context_name]
|
18
|
+
end
|
19
|
+
iterator.each.with_index do |key, index|
|
20
|
+
context["index"] = index
|
21
|
+
context[@var_name] = key
|
22
|
+
res += super
|
23
|
+
end
|
24
|
+
res
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative "../../content"
|
2
|
+
|
3
|
+
module Metanorma
|
4
|
+
module Plugin
|
5
|
+
module Lutaml
|
6
|
+
module Liquid
|
7
|
+
module CustomFilters
|
8
|
+
include ::Metanorma::Plugin::Lutaml::Content
|
9
|
+
|
10
|
+
def loadfile(path, parent_folder = ".")
|
11
|
+
resolved_file_path = File.expand_path(path, parent_folder)
|
12
|
+
load_content_from_file(resolved_file_path)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|