metanorma-plugin-glossarist 0.3.3 → 0.3.7
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/.gitignore +6 -6
- data/.rubocop_todo.yml +90 -196
- data/Gemfile +6 -5
- data/README.adoc +11 -1
- data/lib/metanorma/plugin/glossarist/bibliography_renderer.rb +54 -12
- data/lib/metanorma/plugin/glossarist/concept_filter.rb +22 -40
- data/lib/metanorma/plugin/glossarist/concept_path_resolver.rb +117 -0
- data/lib/metanorma/plugin/glossarist/dataset_preprocessor.rb +98 -82
- data/lib/metanorma/plugin/glossarist/dataset_registry.rb +108 -0
- data/lib/metanorma/plugin/glossarist/document.rb +9 -20
- data/lib/metanorma/plugin/glossarist/liquid/custom_blocks/with_glossarist_context.rb +47 -60
- data/lib/metanorma/plugin/glossarist/liquid/custom_filters/filters.rb +10 -4
- data/lib/metanorma/plugin/glossarist/liquid/custom_filters.rb +14 -0
- data/lib/metanorma/plugin/glossarist/liquid/drop_bracket_access.rb +36 -0
- data/lib/metanorma/plugin/glossarist/liquid/drops/localization_collection_drop.rb +47 -0
- data/lib/metanorma/plugin/glossarist/liquid/drops/managed_concept_data_drop.rb +29 -0
- data/lib/metanorma/plugin/glossarist/liquid/drops/managed_concept_drop.rb +45 -0
- data/lib/metanorma/plugin/glossarist/liquid/multiply_local_file_system.rb +1 -1
- data/lib/metanorma/plugin/glossarist/liquid.rb +26 -0
- data/lib/metanorma/plugin/glossarist/liquid_rendering.rb +26 -0
- data/lib/metanorma/plugin/glossarist/liquid_templates/_concept.liquid +52 -0
- data/lib/metanorma/plugin/glossarist/sanitize.rb +6 -4
- data/lib/metanorma/plugin/glossarist/section_filter.rb +50 -0
- data/lib/metanorma/plugin/glossarist/template_renderer.rb +112 -0
- data/lib/metanorma/plugin/glossarist/version.rb +1 -1
- data/lib/metanorma-plugin-glossarist.rb +30 -8
- data/metanorma-plugin-glossarist.gemspec +1 -1
- metadata +18 -9
- data/lib/metanorma/plugin/glossarist/citation_helper.rb +0 -13
- data/lib/metanorma/plugin/glossarist/concept_renderer.rb +0 -93
- data/lib/metanorma/plugin/glossarist/concept_serializer.rb +0 -27
|
@@ -4,11 +4,12 @@ module Metanorma
|
|
|
4
4
|
module Plugin
|
|
5
5
|
module Glossarist
|
|
6
6
|
class ConceptFilter
|
|
7
|
-
COLLECTION_FILTERS = %w[lang domain group sort_by].freeze
|
|
7
|
+
COLLECTION_FILTERS = %w[lang domain group section tag sort_by].freeze
|
|
8
8
|
SORT_LAST = [""].freeze
|
|
9
9
|
|
|
10
10
|
def initialize(filters)
|
|
11
11
|
@filters = filters || {}
|
|
12
|
+
@resolver = ConceptPathResolver.new
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def apply(collection)
|
|
@@ -17,6 +18,8 @@ module Metanorma
|
|
|
17
18
|
if @filters.key?("domain") || @filters.key?("group")
|
|
18
19
|
result = filter_by_domain(result)
|
|
19
20
|
end
|
|
21
|
+
result = filter_by_section(result) if @filters.key?("section")
|
|
22
|
+
result = filter_by_tag(result) if @filters.key?("tag")
|
|
20
23
|
result = filter_by_field(result) if field_filter?
|
|
21
24
|
result = sort(result) if @filters.key?("sort_by")
|
|
22
25
|
result
|
|
@@ -44,6 +47,20 @@ module Metanorma
|
|
|
44
47
|
end
|
|
45
48
|
end
|
|
46
49
|
|
|
50
|
+
def filter_by_tag(collection)
|
|
51
|
+
tag = @filters["tag"]
|
|
52
|
+
collection.select do |c|
|
|
53
|
+
c.data.tags&.include?(tag)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def filter_by_section(collection)
|
|
58
|
+
section_id = @filters["section"]
|
|
59
|
+
collection.select do |c|
|
|
60
|
+
c.data.domains&.any? { |d| d.concept_id == "section-#{section_id}" }
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
47
64
|
def sort(collection)
|
|
48
65
|
field = @filters["sort_by"]
|
|
49
66
|
return collection unless field
|
|
@@ -52,14 +69,12 @@ module Metanorma
|
|
|
52
69
|
when "term", "default_designation"
|
|
53
70
|
collection.sort_by { |c| c.default_designation.to_s.downcase }
|
|
54
71
|
else
|
|
55
|
-
|
|
56
|
-
collection.sort_by { |c| sort_key(c, parts) }
|
|
72
|
+
collection.sort_by { |c| sort_key(c, field) }
|
|
57
73
|
end
|
|
58
74
|
end
|
|
59
75
|
|
|
60
|
-
def sort_key(concept,
|
|
61
|
-
|
|
62
|
-
value = dig_path(hash, parts)
|
|
76
|
+
def sort_key(concept, field)
|
|
77
|
+
value = @resolver.resolve(concept, field)
|
|
63
78
|
value.nil? ? SORT_LAST : natural_sort_key(value.to_s)
|
|
64
79
|
end
|
|
65
80
|
|
|
@@ -75,10 +90,8 @@ module Metanorma
|
|
|
75
90
|
start_with = path.include?(".start_with(")
|
|
76
91
|
path, match_value = extract_start_with(path, value, start_with)
|
|
77
92
|
|
|
78
|
-
parts = parse_path(path)
|
|
79
93
|
collection.select do |concept|
|
|
80
|
-
|
|
81
|
-
actual = dig_path(hash, parts)
|
|
94
|
+
actual = @resolver.resolve(concept, path)
|
|
82
95
|
if start_with
|
|
83
96
|
actual&.start_with?(match_value)
|
|
84
97
|
else
|
|
@@ -95,37 +108,6 @@ module Metanorma
|
|
|
95
108
|
|
|
96
109
|
[match[1], match[2]]
|
|
97
110
|
end
|
|
98
|
-
|
|
99
|
-
def parse_path(path)
|
|
100
|
-
path.split(".").flat_map do |segment|
|
|
101
|
-
if segment.include?("[")
|
|
102
|
-
parse_indexed_segment(segment)
|
|
103
|
-
else
|
|
104
|
-
[segment]
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def parse_indexed_segment(segment)
|
|
110
|
-
field, index_part = segment.split("[", 2)
|
|
111
|
-
index = index_part&.delete("]'\"")
|
|
112
|
-
if index.match?(/\A\d+\z/)
|
|
113
|
-
[field, index.to_i]
|
|
114
|
-
else
|
|
115
|
-
[field, index]
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def dig_path(hash, parts)
|
|
120
|
-
parts.reduce(hash) do |current, key|
|
|
121
|
-
case current
|
|
122
|
-
when Hash
|
|
123
|
-
current[key] || current[key.to_s]
|
|
124
|
-
when Array
|
|
125
|
-
key.is_a?(Integer) ? current[key] : nil
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
111
|
end
|
|
130
112
|
end
|
|
131
113
|
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Plugin
|
|
5
|
+
module Glossarist
|
|
6
|
+
class ConceptPathResolver
|
|
7
|
+
DELEGATED_TO_DATA = %w[localizations tags].freeze
|
|
8
|
+
DATA_ALIASES = { "identifier" => :id }.freeze
|
|
9
|
+
|
|
10
|
+
def resolve(concept, path)
|
|
11
|
+
parts = parse_path(path)
|
|
12
|
+
value = navigate(concept, parts)
|
|
13
|
+
value.is_a?(String) ? value : value.to_s
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def parse_path(path)
|
|
19
|
+
path.split(".").flat_map do |segment|
|
|
20
|
+
if segment.include?("[")
|
|
21
|
+
parse_indexed_segment(segment)
|
|
22
|
+
else
|
|
23
|
+
[segment]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def parse_indexed_segment(segment)
|
|
29
|
+
field, index_part = segment.split("[", 2)
|
|
30
|
+
index = index_part&.delete("]'\"")
|
|
31
|
+
if index.match?(/\A\d+\z/)
|
|
32
|
+
[field, index.to_i]
|
|
33
|
+
else
|
|
34
|
+
[field, index]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def navigate(obj, parts)
|
|
39
|
+
parts.reduce(obj) do |current, key|
|
|
40
|
+
return nil if current.nil?
|
|
41
|
+
|
|
42
|
+
access(current, key)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def access(obj, key)
|
|
47
|
+
case key
|
|
48
|
+
when String then access_string_key(obj, key)
|
|
49
|
+
when Integer then access_index(obj, key)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def access_string_key(obj, key)
|
|
54
|
+
case obj
|
|
55
|
+
when ::Glossarist::ManagedConcept
|
|
56
|
+
resolve_managed_concept(obj, key)
|
|
57
|
+
when ::Glossarist::ManagedConceptData
|
|
58
|
+
resolve_data_attribute(obj, key)
|
|
59
|
+
when ::Glossarist::Collections::LocalizationCollection
|
|
60
|
+
obj.find_by(:language_code, key)
|
|
61
|
+
when Array
|
|
62
|
+
nil
|
|
63
|
+
when ::Lutaml::Model::Serializable
|
|
64
|
+
read_attribute(obj, key)
|
|
65
|
+
when Hash
|
|
66
|
+
obj[key] || obj[key.to_s]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def resolve_managed_concept(concept, key)
|
|
71
|
+
if DELEGATED_TO_DATA.include?(key)
|
|
72
|
+
return read_attribute(concept.data,
|
|
73
|
+
key)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
read_attribute(concept, key) { read_method(concept, key) }
|
|
77
|
+
rescue NoMethodError
|
|
78
|
+
nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def resolve_data_attribute(data, key)
|
|
82
|
+
aliased = DATA_ALIASES[key]
|
|
83
|
+
if aliased && data.class.attributes.key?(aliased)
|
|
84
|
+
return read_attribute(data, aliased)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
read_attribute(data, key)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Reads a validated public attribute from a Lutaml::Model::Serializable
|
|
91
|
+
# object. Uses public_send because the attribute name is determined at
|
|
92
|
+
# runtime from path strings. Falls back to the block for non-attribute
|
|
93
|
+
# public methods (e.g., ManagedConcept#default_designation).
|
|
94
|
+
def read_attribute(obj, key)
|
|
95
|
+
sym = key.to_sym
|
|
96
|
+
if obj.class.respond_to?(:attributes) && obj.class.attributes.key?(sym)
|
|
97
|
+
obj.public_send(sym)
|
|
98
|
+
elsif block_given?
|
|
99
|
+
yield
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Calls a public method by name on the object. Used as a fallback for
|
|
104
|
+
# methods that aren't lutaml-model attributes (e.g., computed accessors).
|
|
105
|
+
def read_method(obj, key)
|
|
106
|
+
obj.public_send(key.to_sym)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def access_index(obj, index)
|
|
110
|
+
obj[index]
|
|
111
|
+
rescue NoMethodError
|
|
112
|
+
nil
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -4,38 +4,35 @@ require "asciidoctor"
|
|
|
4
4
|
require "asciidoctor/reader"
|
|
5
5
|
require "glossarist"
|
|
6
6
|
|
|
7
|
-
require_relative "sanitize"
|
|
8
|
-
require_relative "concept_renderer"
|
|
9
|
-
require_relative "bibliography_renderer"
|
|
10
|
-
require_relative "concept_serializer"
|
|
11
|
-
require_relative "document"
|
|
12
|
-
require_relative "liquid/custom_filters/filters"
|
|
13
|
-
require_relative "liquid/custom_blocks/with_glossarist_context"
|
|
14
|
-
|
|
15
7
|
module Metanorma
|
|
16
8
|
module Plugin
|
|
17
9
|
module Glossarist
|
|
18
10
|
class DatasetPreprocessor < Asciidoctor::Extensions::Preprocessor
|
|
19
11
|
DATASET_ATTR_REGEX = /^:glossarist-dataset:\s*(.*?)$/m
|
|
20
12
|
IMPORT_REGEX = /^glossarist::import\[(.*?)\]$/m
|
|
13
|
+
IMPORT_SECTIONS_REGEX = /^glossarist::import_sections\[(.*?)\]$/m
|
|
21
14
|
RENDER_REGEX = /^glossarist::render\[(.*?)\]$/m
|
|
22
15
|
BLOCK_REGEX = /^\[glossarist,(.+?),(.+?)\]$/m
|
|
23
16
|
BIBLIOGRAPHY_REGEX = /^glossarist::render_bibliography\[(.*?)\]$/m
|
|
24
17
|
BIBLIOGRAPHY_ENTRY_REGEX = /^glossarist::render_bibliography_entry\[(.*?)\]$/m
|
|
25
18
|
|
|
19
|
+
BIB_ANCHOR_REGEX = /^\*\s*\[\[\[([^,]+)/
|
|
20
|
+
|
|
26
21
|
def initialize(config = {})
|
|
27
22
|
super
|
|
28
23
|
@config = config
|
|
29
|
-
@datasets = {}
|
|
30
|
-
@title_depth = 2
|
|
31
|
-
@bibliography_renderer = BibliographyRenderer.new
|
|
32
|
-
@seen_glossarist = false
|
|
33
|
-
@context_names = []
|
|
34
24
|
end
|
|
35
25
|
|
|
36
26
|
def process(document, reader)
|
|
37
27
|
input_lines = reader.lines.to_enum
|
|
38
28
|
@config[:file_system] = relative_file_path(document, "")
|
|
29
|
+
@registry = DatasetRegistry.new
|
|
30
|
+
@renderer = TemplateRenderer.new(file_system: @config[:file_system])
|
|
31
|
+
@rendered_concepts = []
|
|
32
|
+
@title_depth = 2
|
|
33
|
+
@existing_bib_anchors = []
|
|
34
|
+
@seen_glossarist = false
|
|
35
|
+
|
|
39
36
|
processed_doc = prepare_document(document, input_lines)
|
|
40
37
|
log(document, processed_doc.to_s) if @seen_glossarist
|
|
41
38
|
Asciidoctor::PreprocessorReader.new(document,
|
|
@@ -57,6 +54,7 @@ module Metanorma
|
|
|
57
54
|
skip_dataset: false)
|
|
58
55
|
liquid_doc = Document.new
|
|
59
56
|
liquid_doc.file_system = @config[:file_system]
|
|
57
|
+
liquid_doc.registry = @registry
|
|
60
58
|
|
|
61
59
|
loop do
|
|
62
60
|
current_line = input_lines.next
|
|
@@ -75,6 +73,8 @@ module Metanorma
|
|
|
75
73
|
process_dataset_tag(document, input_lines, liquid_doc, match)
|
|
76
74
|
elsif (match = current_line.match(RENDER_REGEX))
|
|
77
75
|
process_render_tag(liquid_doc, match)
|
|
76
|
+
elsif (match = current_line.match(IMPORT_SECTIONS_REGEX))
|
|
77
|
+
process_import_sections_tag(document, liquid_doc, match)
|
|
78
78
|
elsif (match = current_line.match(IMPORT_REGEX))
|
|
79
79
|
process_import_tag(liquid_doc, match)
|
|
80
80
|
elsif (match = current_line.match(BIBLIOGRAPHY_REGEX))
|
|
@@ -88,14 +88,16 @@ module Metanorma
|
|
|
88
88
|
@title_depth = current_line.sub(/ .*$/,
|
|
89
89
|
"").size
|
|
90
90
|
end
|
|
91
|
+
if (match = current_line.match(BIB_ANCHOR_REGEX))
|
|
92
|
+
@existing_bib_anchors << match[1]
|
|
93
|
+
end
|
|
91
94
|
liquid_doc.add_content(current_line)
|
|
92
95
|
end
|
|
93
96
|
end
|
|
94
97
|
|
|
95
98
|
def process_dataset_tag(document, input_lines, liquid_doc, match)
|
|
96
99
|
@seen_glossarist = true
|
|
97
|
-
@
|
|
98
|
-
@context_names.flatten!
|
|
100
|
+
@registry.register(document, match[1])
|
|
99
101
|
liquid_doc.add_content(prepare_document(document, input_lines).to_s,
|
|
100
102
|
render: false)
|
|
101
103
|
end
|
|
@@ -138,16 +140,7 @@ module Metanorma
|
|
|
138
140
|
end
|
|
139
141
|
|
|
140
142
|
def get_context_path(document, key)
|
|
141
|
-
|
|
142
|
-
context_names = @context_names.map(&:strip)
|
|
143
|
-
found = context_names.find do |context|
|
|
144
|
-
context_name, = context.split("=")
|
|
145
|
-
context_name == key
|
|
146
|
-
end
|
|
147
|
-
return found.split("=").last.strip if found
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
relative_file_path(document, key)
|
|
143
|
+
@registry.context_path(key) || relative_file_path(document, key)
|
|
151
144
|
end
|
|
152
145
|
|
|
153
146
|
def process_render_tag(liquid_doc, match)
|
|
@@ -157,92 +150,115 @@ module Metanorma
|
|
|
157
150
|
concept_name = matches[1]
|
|
158
151
|
options = parse_options(matches[2..])
|
|
159
152
|
|
|
160
|
-
concept = find_concept(context_name, concept_name)
|
|
153
|
+
concept = @registry.find_concept(context_name, concept_name)
|
|
161
154
|
return unless concept
|
|
162
155
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
156
|
+
@rendered_concepts << concept
|
|
157
|
+
renderer = @renderer
|
|
158
|
+
rendered = renderer.render_concept(concept,
|
|
159
|
+
depth: @title_depth,
|
|
160
|
+
anchor_prefix: options["anchor-prefix"])
|
|
161
|
+
liquid_doc.add_content("\n#{rendered}")
|
|
167
162
|
end
|
|
168
163
|
|
|
164
|
+
RENDER_OPTIONS = %w[anchor-prefix].freeze
|
|
165
|
+
|
|
169
166
|
def process_import_tag(liquid_doc, match)
|
|
170
167
|
@seen_glossarist = true
|
|
171
168
|
matches = match[1].split(",").map(&:strip)
|
|
172
169
|
context_name = matches[0]
|
|
173
170
|
options = parse_options(matches[1..])
|
|
174
|
-
dataset = @
|
|
171
|
+
dataset = @registry.resolve_dataset(nil, context_name)
|
|
175
172
|
return unless dataset
|
|
176
173
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
174
|
+
filter_options = options.except(*RENDER_OPTIONS)
|
|
175
|
+
concepts = ConceptFilter.new(filter_options).apply(dataset)
|
|
176
|
+
concepts = concepts.select(&:default_designation)
|
|
177
|
+
@rendered_concepts.concat(concepts)
|
|
178
|
+
renderer = @renderer
|
|
179
|
+
rendered = renderer.render_concepts(concepts,
|
|
180
|
+
depth: @title_depth,
|
|
181
|
+
anchor_prefix: options["anchor-prefix"])
|
|
182
|
+
liquid_doc.add_content("\n#{rendered}")
|
|
183
|
+
end
|
|
180
184
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
185
|
+
def process_import_sections_tag(_document, liquid_doc, match)
|
|
186
|
+
@seen_glossarist = true
|
|
187
|
+
matches = match[1].split(",").map(&:strip)
|
|
188
|
+
context_name = matches[0]
|
|
189
|
+
options = parse_options(matches[1..])
|
|
186
190
|
|
|
187
|
-
|
|
191
|
+
sections = @registry.register_sections(context_name)
|
|
192
|
+
return unless sections && !sections.empty?
|
|
193
|
+
|
|
194
|
+
dataset = @registry.resolve_dataset(nil, context_name)
|
|
195
|
+
return unless dataset
|
|
196
|
+
|
|
197
|
+
renderer = @renderer
|
|
198
|
+
sort_by = options["sort_by"] || "term"
|
|
199
|
+
section_filter = SectionFilter.new(
|
|
200
|
+
exclude: (options["section_exclude"] || "").split("|"),
|
|
201
|
+
include: (options["section_include"] || "").split("|"),
|
|
202
|
+
)
|
|
203
|
+
filtered = section_filter.apply(sections)
|
|
204
|
+
parts = render_section_concepts(filtered, dataset, renderer,
|
|
205
|
+
sort_by, options)
|
|
206
|
+
liquid_doc.add_content("\n#{parts.join("\n\n")}")
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def render_section_concepts(sections, dataset, renderer, sort_by,
|
|
210
|
+
options)
|
|
211
|
+
sections.filter_map do |section|
|
|
212
|
+
filter_options = { "section" => section.id, "sort_by" => sort_by }
|
|
213
|
+
concepts = ConceptFilter.new(filter_options).apply(dataset)
|
|
214
|
+
concepts = concepts.select(&:default_designation)
|
|
215
|
+
next if concepts.empty?
|
|
216
|
+
|
|
217
|
+
@rendered_concepts.concat(concepts)
|
|
218
|
+
heading = "#{'=' * (@title_depth + 1)} #{section.name || section.id}"
|
|
219
|
+
rendered = renderer.render_concepts(concepts,
|
|
220
|
+
depth: @title_depth + 1,
|
|
221
|
+
anchor_prefix: options["anchor-prefix"])
|
|
222
|
+
"#{heading}\n\n#{rendered}"
|
|
223
|
+
end
|
|
188
224
|
end
|
|
189
225
|
|
|
190
226
|
def process_bibliography(document, liquid_doc, match)
|
|
191
227
|
@seen_glossarist = true
|
|
192
228
|
dataset_name = match[1].strip
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
229
|
+
concepts = if @rendered_concepts.empty?
|
|
230
|
+
@registry.resolve_dataset(
|
|
231
|
+
document, dataset_name
|
|
232
|
+
)
|
|
233
|
+
else
|
|
234
|
+
@rendered_concepts
|
|
235
|
+
end
|
|
236
|
+
return unless concepts && !concepts.empty?
|
|
237
|
+
|
|
238
|
+
renderer = BibliographyRenderer.new(
|
|
239
|
+
existing_anchors: @existing_bib_anchors,
|
|
240
|
+
bibliography_data: @registry.bibliography_data,
|
|
241
|
+
)
|
|
242
|
+
liquid_doc.add_content(renderer.render_all(concepts))
|
|
197
243
|
end
|
|
198
244
|
|
|
199
245
|
def process_bibliography_entry(document, liquid_doc, match)
|
|
200
246
|
@seen_glossarist = true
|
|
201
247
|
dataset_name, concept_name = match[1].split(",").map(&:strip)
|
|
202
|
-
concept = find_concept(dataset_name, concept_name, document)
|
|
248
|
+
concept = @registry.find_concept(dataset_name, concept_name, document)
|
|
203
249
|
return unless concept
|
|
204
250
|
|
|
205
|
-
|
|
251
|
+
renderer = BibliographyRenderer.new(
|
|
252
|
+
existing_anchors: @existing_bib_anchors,
|
|
253
|
+
bibliography_data: @registry.bibliography_data,
|
|
254
|
+
)
|
|
255
|
+
entry = renderer.render_entry(concept)
|
|
206
256
|
liquid_doc.add_content(entry) if entry
|
|
207
257
|
end
|
|
208
258
|
|
|
209
|
-
def prepare_dataset_contexts(document, contexts)
|
|
210
|
-
contexts.split(";").map do |context|
|
|
211
|
-
context_name, file_path = context.split(":").map(&:strip)
|
|
212
|
-
path = relative_file_path(document, file_path)
|
|
213
|
-
dataset = load_dataset(path)
|
|
214
|
-
@datasets[context_name] = dataset.to_a
|
|
215
|
-
"#{context_name}=#{path}"
|
|
216
|
-
end
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
def load_dataset(path)
|
|
220
|
-
collection = ::Glossarist::ManagedConceptCollection.new
|
|
221
|
-
collection.load_from_files(path)
|
|
222
|
-
collection
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
def find_concept(dataset_name, concept_name, document = nil)
|
|
226
|
-
dataset = resolve_dataset(document, dataset_name)
|
|
227
|
-
return unless dataset
|
|
228
|
-
|
|
229
|
-
dataset.find do |concept|
|
|
230
|
-
concept.default_designation == concept_name
|
|
231
|
-
end
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
def resolve_dataset(document, dataset_name)
|
|
235
|
-
dataset = @datasets[dataset_name]
|
|
236
|
-
return dataset if dataset
|
|
237
|
-
|
|
238
|
-
return unless document
|
|
239
|
-
|
|
240
|
-
path = relative_file_path(document, dataset_name)
|
|
241
|
-
collection = load_dataset(path)
|
|
242
|
-
@datasets[dataset_name] = collection.to_a
|
|
243
|
-
end
|
|
244
|
-
|
|
245
259
|
def relative_file_path(document, file_path)
|
|
260
|
+
return file_path if File.absolute_path?(file_path)
|
|
261
|
+
|
|
246
262
|
docfile_directory = File.dirname(
|
|
247
263
|
document.attributes["docfile"] || ".",
|
|
248
264
|
)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "glossarist"
|
|
4
|
+
|
|
5
|
+
module Metanorma
|
|
6
|
+
module Plugin
|
|
7
|
+
module Glossarist
|
|
8
|
+
class DatasetRegistry
|
|
9
|
+
def initialize
|
|
10
|
+
@datasets = {}
|
|
11
|
+
@path_cache = {}
|
|
12
|
+
@bibliography_data = {}
|
|
13
|
+
@context_names = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def register(document, contexts)
|
|
17
|
+
paths = contexts.split(";").map do |context|
|
|
18
|
+
context_name, file_path = context.split(":", 2).map(&:strip)
|
|
19
|
+
path = relative_file_path(document, file_path)
|
|
20
|
+
@datasets[context_name] = load_dataset(path).to_a
|
|
21
|
+
"#{context_name}=#{path}"
|
|
22
|
+
end
|
|
23
|
+
@context_names.concat(paths)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def load_cached(path)
|
|
27
|
+
@path_cache[path] ||= load_dataset(path)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def resolve_dataset(document, dataset_name)
|
|
31
|
+
dataset = @datasets[dataset_name]
|
|
32
|
+
return dataset if dataset
|
|
33
|
+
|
|
34
|
+
return unless document
|
|
35
|
+
|
|
36
|
+
path = relative_file_path(document, dataset_name)
|
|
37
|
+
@datasets[dataset_name] = load_dataset(path).to_a
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def find_concept(dataset_name, concept_name, document = nil)
|
|
41
|
+
dataset = resolve_dataset(document, dataset_name)
|
|
42
|
+
return unless dataset
|
|
43
|
+
|
|
44
|
+
dataset.find do |concept|
|
|
45
|
+
concept.default_designation == concept_name
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def context_path(key)
|
|
50
|
+
return nil if @context_names.empty?
|
|
51
|
+
|
|
52
|
+
found = @context_names.find do |context|
|
|
53
|
+
context_name, = context.split("=")
|
|
54
|
+
context_name.strip == key
|
|
55
|
+
end
|
|
56
|
+
found&.split("=")&.last&.strip
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def register_sections(context_name)
|
|
60
|
+
dataset_path = context_path(context_name)
|
|
61
|
+
return nil unless dataset_path
|
|
62
|
+
|
|
63
|
+
@register_cache ||= {}
|
|
64
|
+
@register_cache[dataset_path] ||=
|
|
65
|
+
::Glossarist::DatasetRegister.from_directory(dataset_path)
|
|
66
|
+
@register_cache[dataset_path]&.sections
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def bibliography_data
|
|
70
|
+
@bibliography_data
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def load_dataset(path)
|
|
76
|
+
@path_cache[path] ||= begin
|
|
77
|
+
collection = ::Glossarist::ManagedConceptCollection.new
|
|
78
|
+
collection.load_from_files(path)
|
|
79
|
+
load_bibliography_data(path)
|
|
80
|
+
collection
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def load_bibliography_data(dataset_path)
|
|
85
|
+
bib_path = File.join(dataset_path, "bibliography.yaml")
|
|
86
|
+
return unless File.exist?(bib_path)
|
|
87
|
+
|
|
88
|
+
entries = YAML.safe_load_file(bib_path,
|
|
89
|
+
permitted_classes: [Symbol, Date])
|
|
90
|
+
return unless entries.is_a?(Array)
|
|
91
|
+
|
|
92
|
+
@bibliography_data = entries.each_with_object({}) do |entry, hash|
|
|
93
|
+
hash[entry["id"]] = entry if entry["id"]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def relative_file_path(document, file_path)
|
|
98
|
+
return file_path if File.absolute_path?(file_path)
|
|
99
|
+
|
|
100
|
+
docfile_directory = File.dirname(
|
|
101
|
+
document.attributes["docfile"] || ".",
|
|
102
|
+
)
|
|
103
|
+
document.path_resolver.system_path(file_path, docfile_directory)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "liquid"
|
|
4
|
-
require_relative "liquid/multiply_local_file_system"
|
|
5
|
-
|
|
6
3
|
module Metanorma
|
|
7
4
|
module Plugin
|
|
8
5
|
module Glossarist
|
|
9
6
|
class Document
|
|
10
|
-
attr_accessor :
|
|
7
|
+
attr_accessor :file_system, :registry
|
|
11
8
|
|
|
12
9
|
def initialize
|
|
13
10
|
@content = []
|
|
@@ -15,7 +12,14 @@ module Metanorma
|
|
|
15
12
|
|
|
16
13
|
def add_content(content, options = {})
|
|
17
14
|
@content << if options[:render]
|
|
18
|
-
|
|
15
|
+
LiquidRendering.render(
|
|
16
|
+
content,
|
|
17
|
+
include_paths: [file_system,
|
|
18
|
+
TEMPLATES_DIR,
|
|
19
|
+
options[:template]].compact,
|
|
20
|
+
patterns: LiquidRendering::DOCUMENT_PATTERNS,
|
|
21
|
+
registry: registry,
|
|
22
|
+
)
|
|
19
23
|
else
|
|
20
24
|
content
|
|
21
25
|
end
|
|
@@ -24,21 +28,6 @@ module Metanorma
|
|
|
24
28
|
def to_s
|
|
25
29
|
@content.compact.join("\n")
|
|
26
30
|
end
|
|
27
|
-
|
|
28
|
-
private
|
|
29
|
-
|
|
30
|
-
def render_liquid(file_content, options = {})
|
|
31
|
-
include_paths = [file_system, options[:template]].compact
|
|
32
|
-
template = ::Liquid::Template.parse(file_content)
|
|
33
|
-
template.registers[:file_system] = ::Metanorma::Plugin::Glossarist::Liquid::LocalFileSystem.new(
|
|
34
|
-
include_paths, ["%s.liquid", "_%s.liquid", "_%s.adoc"]
|
|
35
|
-
)
|
|
36
|
-
rendered = template.render
|
|
37
|
-
|
|
38
|
-
return rendered unless template.errors.any?
|
|
39
|
-
|
|
40
|
-
raise template.errors.first.cause
|
|
41
|
-
end
|
|
42
31
|
end
|
|
43
32
|
end
|
|
44
33
|
end
|