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
|
@@ -1,76 +1,63 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "liquid"
|
|
4
|
-
require_relative "../../concept_filter"
|
|
5
|
-
require_relative "../../concept_serializer"
|
|
6
|
-
|
|
7
3
|
module Metanorma
|
|
8
4
|
module Plugin
|
|
9
5
|
module Glossarist
|
|
10
6
|
module Liquid
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
collection = ::Glossarist::ManagedConceptCollection.new
|
|
17
|
-
collection.load_from_files(folder_path)
|
|
18
|
-
collection
|
|
19
|
-
end
|
|
7
|
+
class WithGlossaristContext < ::Liquid::Block
|
|
8
|
+
def self.register!
|
|
9
|
+
::Liquid::Environment.default.register_tag(
|
|
10
|
+
"with_glossarist_context", self
|
|
11
|
+
)
|
|
20
12
|
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
13
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@contexts = []
|
|
32
|
-
@raw_filters = {}
|
|
14
|
+
def initialize(tag_name, markup, tokens)
|
|
15
|
+
super
|
|
16
|
+
@contexts = []
|
|
17
|
+
@raw_filters = {}
|
|
33
18
|
|
|
34
|
-
|
|
35
|
-
";", 2
|
|
36
|
-
)
|
|
19
|
+
contexts_part, filters_part = markup.strip.split(";", 2)
|
|
37
20
|
|
|
38
|
-
|
|
21
|
+
parse_filters(filters_part.strip) if filters_part && !filters_part.strip.empty?
|
|
39
22
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
end
|
|
23
|
+
contexts_part.split(",").each do |context|
|
|
24
|
+
context_name, file_path = context.split("=", 2).map(&:strip)
|
|
25
|
+
@contexts << { name: context_name, file_path: file_path }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
46
28
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
collection = load_collection(local_context[:file_path].strip)
|
|
50
|
-
filtered = Metanorma::Plugin::Glossarist::ConceptFilter.new(@raw_filters).apply(collection)
|
|
51
|
-
context[local_context[:name]] = filtered.map do |c|
|
|
52
|
-
Metanorma::Plugin::Glossarist::ConceptSerializer.new(c).to_h
|
|
53
|
-
end
|
|
54
|
-
end
|
|
29
|
+
def render(context)
|
|
30
|
+
registry = context.registers[:dataset_registry]
|
|
55
31
|
|
|
56
|
-
|
|
57
|
-
|
|
32
|
+
@contexts.each do |local_context|
|
|
33
|
+
path = local_context[:file_path].strip
|
|
34
|
+
collection = registry ? registry.load_cached(path) : load_collection(path)
|
|
35
|
+
filtered = ConceptFilter.new(@raw_filters).apply(collection)
|
|
36
|
+
context[local_context[:name]] = filtered.map do |c|
|
|
37
|
+
ManagedConceptDrop.new(c)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
58
40
|
|
|
59
|
-
|
|
41
|
+
super
|
|
42
|
+
end
|
|
60
43
|
|
|
61
|
-
|
|
62
|
-
stripped = filters_str.gsub(/\A['"]|['"]\z/,
|
|
63
|
-
"")
|
|
64
|
-
stripped.split(";").each do |filter|
|
|
65
|
-
property, value = filter.split("=", 2)
|
|
66
|
-
if property
|
|
67
|
-
@raw_filters[property.strip] =
|
|
68
|
-
value&.strip
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
44
|
+
private
|
|
72
45
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
46
|
+
def parse_filters(filters_str)
|
|
47
|
+
stripped = filters_str.gsub(/\A['"]|['"]\z/, "")
|
|
48
|
+
stripped.split(";").each do |filter|
|
|
49
|
+
property, value = filter.split("=", 2)
|
|
50
|
+
@raw_filters[property.strip] = value&.strip if property
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def load_collection(folder_path)
|
|
55
|
+
collection = ::Glossarist::ManagedConceptCollection.new
|
|
56
|
+
collection.load_from_files(folder_path)
|
|
57
|
+
collection
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "liquid"
|
|
4
|
-
|
|
5
3
|
module Metanorma
|
|
6
4
|
module Plugin
|
|
7
5
|
module Glossarist
|
|
8
6
|
module Liquid
|
|
9
7
|
module CustomFilters
|
|
10
8
|
module Filters
|
|
9
|
+
def self.register!
|
|
10
|
+
::Liquid::Environment.default.register_filter(self)
|
|
11
|
+
end
|
|
12
|
+
|
|
11
13
|
def values(list)
|
|
12
14
|
list.values
|
|
13
15
|
end
|
|
@@ -15,11 +17,15 @@ module Metanorma
|
|
|
15
17
|
def sanitize_references(str)
|
|
16
18
|
Sanitize.references(str)
|
|
17
19
|
end
|
|
20
|
+
|
|
21
|
+
def format_ref(label)
|
|
22
|
+
return "" if label.nil? || label.strip.empty?
|
|
23
|
+
|
|
24
|
+
label.gsub(%r{[ /:]}, "_")
|
|
25
|
+
end
|
|
18
26
|
end
|
|
19
27
|
end
|
|
20
28
|
end
|
|
21
29
|
end
|
|
22
30
|
end
|
|
23
31
|
end
|
|
24
|
-
|
|
25
|
-
Liquid::Template.register_filter(Metanorma::Plugin::Glossarist::Liquid::CustomFilters::Filters)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Includes Lutaml::Model::Liquid::IndexedAccess into Glossarist collection
|
|
4
|
+
# classes so that their auto-generated Liquid drops support bracket access
|
|
5
|
+
# (e.g., +localizations['eng']+, +definition[0]+).
|
|
6
|
+
#
|
|
7
|
+
# Glossarist collections support +self[key]+ but do not yet include
|
|
8
|
+
# IndexedAccess in their published gem. Once they do, this file can be
|
|
9
|
+
# removed entirely.
|
|
10
|
+
#
|
|
11
|
+
# @see Lutaml::Model::Liquid::IndexedAccess (lutaml-model >= 0.8.15)
|
|
12
|
+
# @see https://github.com/lutaml/lutaml-model/pull/705
|
|
13
|
+
module Metanorma
|
|
14
|
+
module Plugin
|
|
15
|
+
module Glossarist
|
|
16
|
+
module Liquid
|
|
17
|
+
module PolyfillIndexedAccess
|
|
18
|
+
COLLECTION_CLASSES = %w[
|
|
19
|
+
Glossarist::Collections::LocalizationCollection
|
|
20
|
+
Glossarist::Collections::DetailedDefinitionCollection
|
|
21
|
+
Glossarist::Collections::ConceptSourceCollection
|
|
22
|
+
].freeze
|
|
23
|
+
|
|
24
|
+
def self.apply!
|
|
25
|
+
COLLECTION_CLASSES.each do |class_name|
|
|
26
|
+
klass = class_name.split("::").reduce(Object) do |mod, name|
|
|
27
|
+
mod.const_get(name)
|
|
28
|
+
end
|
|
29
|
+
klass.include(Lutaml::Model::Liquid::IndexedAccess) unless klass.include?(Lutaml::Model::Liquid::IndexedAccess)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Plugin
|
|
5
|
+
module Glossarist
|
|
6
|
+
module Liquid
|
|
7
|
+
class LocalizationCollectionDrop < ::Liquid::Drop
|
|
8
|
+
include Enumerable
|
|
9
|
+
|
|
10
|
+
def initialize(collection)
|
|
11
|
+
super()
|
|
12
|
+
@collection = collection
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def liquid_method_missing(method)
|
|
16
|
+
l10n = @collection.find_by(:language_code, method.to_s)
|
|
17
|
+
l10n ? l10n.to_liquid : super
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def [](key)
|
|
21
|
+
liquid_method_missing(key.to_s)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def key?(key)
|
|
25
|
+
!@collection.find_by(:language_code, key.to_s).nil?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def size
|
|
29
|
+
@collection.size
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def each(&block)
|
|
33
|
+
@collection.each(&block)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def first
|
|
37
|
+
@collection.first
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def last
|
|
41
|
+
@collection.last
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Plugin
|
|
5
|
+
module Glossarist
|
|
6
|
+
module Liquid
|
|
7
|
+
class ManagedConceptDataDrop < ::Liquid::Drop
|
|
8
|
+
def initialize(concept_data)
|
|
9
|
+
super()
|
|
10
|
+
@concept_data = concept_data
|
|
11
|
+
@auto_drop = concept_data.to_liquid
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def localizations
|
|
15
|
+
@localizations ||= LocalizationCollectionDrop.new(@concept_data.localizations)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def identifier
|
|
19
|
+
@concept_data.id
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def liquid_method_missing(method)
|
|
23
|
+
@auto_drop.invoke_drop(method)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Plugin
|
|
5
|
+
module Glossarist
|
|
6
|
+
module Liquid
|
|
7
|
+
class ManagedConceptDrop < ::Liquid::Drop
|
|
8
|
+
def initialize(concept)
|
|
9
|
+
super()
|
|
10
|
+
@concept = concept
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def data
|
|
14
|
+
@data ||= ManagedConceptDataDrop.new(@concept.data)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def schema_version
|
|
18
|
+
@concept.schema_version
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def uuid
|
|
22
|
+
@concept.uuid
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def identifier
|
|
26
|
+
@concept.identifier
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def default_designation
|
|
30
|
+
@concept.default_designation
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def tags
|
|
34
|
+
@concept.data.tags
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def liquid_method_missing(method)
|
|
38
|
+
l10n = @concept.localization(method.to_s)
|
|
39
|
+
l10n ? l10n.to_liquid : super
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "liquid"
|
|
4
|
+
|
|
5
|
+
module Metanorma
|
|
6
|
+
module Plugin
|
|
7
|
+
module Glossarist
|
|
8
|
+
module Liquid
|
|
9
|
+
autoload :LocalFileSystem,
|
|
10
|
+
"metanorma/plugin/glossarist/liquid/multiply_local_file_system"
|
|
11
|
+
autoload :ManagedConceptDataDrop,
|
|
12
|
+
"metanorma/plugin/glossarist/liquid/drops/managed_concept_data_drop"
|
|
13
|
+
autoload :ManagedConceptDrop,
|
|
14
|
+
"metanorma/plugin/glossarist/liquid/drops/managed_concept_drop"
|
|
15
|
+
autoload :LocalizationCollectionDrop,
|
|
16
|
+
"metanorma/plugin/glossarist/liquid/drops/localization_collection_drop"
|
|
17
|
+
autoload :PolyfillIndexedAccess,
|
|
18
|
+
"metanorma/plugin/glossarist/liquid/drop_bracket_access"
|
|
19
|
+
autoload :WithGlossaristContext,
|
|
20
|
+
"metanorma/plugin/glossarist/liquid/custom_blocks/with_glossarist_context"
|
|
21
|
+
autoload :CustomFilters,
|
|
22
|
+
"metanorma/plugin/glossarist/liquid/custom_filters"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "liquid"
|
|
4
|
+
|
|
5
|
+
module Metanorma
|
|
6
|
+
module Plugin
|
|
7
|
+
module Glossarist
|
|
8
|
+
module LiquidRendering
|
|
9
|
+
DEFAULT_PATTERNS = ["%s.liquid", "_%s.liquid"].freeze
|
|
10
|
+
DOCUMENT_PATTERNS = ["%s.liquid", "_%s.liquid", "_%s.adoc"].freeze
|
|
11
|
+
|
|
12
|
+
def self.render(content, include_paths:, patterns: DEFAULT_PATTERNS,
|
|
13
|
+
assigns: {}, registry: nil)
|
|
14
|
+
template = ::Liquid::Template.parse(content)
|
|
15
|
+
template.registers[:file_system] =
|
|
16
|
+
Liquid::LocalFileSystem.new(include_paths, patterns)
|
|
17
|
+
template.registers[:dataset_registry] = registry if registry
|
|
18
|
+
rendered = template.render(assigns)
|
|
19
|
+
raise template.errors.first.cause if template.errors.any?
|
|
20
|
+
|
|
21
|
+
rendered
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{% if anchor %}[[{{ anchor }}]]
|
|
2
|
+
{% endif %}{{ depth_marker }} {{ l10n.data.terms[0].designation }}
|
|
3
|
+
{%- for term in l10n.data.terms offset:1 %}
|
|
4
|
+
{%- if term.normative_status == "preferred" %}
|
|
5
|
+
preferred:[{{ term.designation }}]
|
|
6
|
+
{%- elsif term.normative_status == "admitted" %}
|
|
7
|
+
admitted:[{{ term.designation }}]
|
|
8
|
+
{%- elsif term.normative_status == "deprecated" %}
|
|
9
|
+
deprecated:[{{ term.designation }}]
|
|
10
|
+
{%- else %}
|
|
11
|
+
alt:[{{ term.designation }}]
|
|
12
|
+
{%- endif %}
|
|
13
|
+
{%- endfor %}
|
|
14
|
+
{% if l10n.data.domain %}
|
|
15
|
+
domain:[{{ l10n.data.domain }}]
|
|
16
|
+
|
|
17
|
+
{% endif %}
|
|
18
|
+
{% if l10n.data.definition.definitions[0] %}
|
|
19
|
+
{{ l10n.data.definition.definitions[0].content | sanitize_references }}
|
|
20
|
+
|
|
21
|
+
{% endif %}
|
|
22
|
+
{% for example in l10n.data.examples.definitions -%}
|
|
23
|
+
[example]
|
|
24
|
+
====
|
|
25
|
+
{{ example.content | sanitize_references }}
|
|
26
|
+
====
|
|
27
|
+
|
|
28
|
+
{% endfor %}
|
|
29
|
+
{% for note in l10n.data.notes.definitions -%}
|
|
30
|
+
[NOTE]
|
|
31
|
+
====
|
|
32
|
+
{{ note.content | sanitize_references }}
|
|
33
|
+
====
|
|
34
|
+
|
|
35
|
+
{% endfor %}
|
|
36
|
+
{% if l10n.data.annotations -%}
|
|
37
|
+
{% for annotation in l10n.data.annotations.definitions -%}
|
|
38
|
+
[NOTE]
|
|
39
|
+
====
|
|
40
|
+
{{ annotation.content | sanitize_references }}
|
|
41
|
+
====
|
|
42
|
+
|
|
43
|
+
{% endfor %}
|
|
44
|
+
{% endif %}
|
|
45
|
+
{% for source in l10n.data.sources.sources -%}
|
|
46
|
+
{% assign ref_text = source.origin.text | format_ref -%}
|
|
47
|
+
{% if ref_text != "" -%}
|
|
48
|
+
[.source]
|
|
49
|
+
<<{{ ref_text }}{% if source.origin.locality %},{{ source.origin.locality.type }}="{{ source.origin.locality.reference_from }}"{% elsif source.origin.original %},{{ source.origin.original }}{% endif %}>>{% if source.modification %}, {{ source.modification }}{% endif %}
|
|
50
|
+
|
|
51
|
+
{% endif %}
|
|
52
|
+
{% endfor %}
|
|
@@ -4,15 +4,17 @@ module Metanorma
|
|
|
4
4
|
module Plugin
|
|
5
5
|
module Glossarist
|
|
6
6
|
module Sanitize
|
|
7
|
-
REF_REGEX = /{{([^,{}]+),([^}]+?)}}(.*)$/
|
|
7
|
+
REF_REGEX = /{{(urn:[^,{}]+),([^}]+?)}}(.*)$/m
|
|
8
8
|
XREF_REGEX = /<<((?>[^,>\n]+))(?:,[^>\n]*)?>>/
|
|
9
9
|
|
|
10
10
|
def self.references(str)
|
|
11
11
|
return str unless str&.match?(REF_REGEX)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
str.gsub(REF_REGEX) do
|
|
14
|
+
m = Regexp.last_match
|
|
15
|
+
urn = Metanorma::Utils.to_ncname(m[1]).gsub(":", "_")
|
|
16
|
+
"{{#{urn},#{m[2]}}}#{m[3]}"
|
|
17
|
+
end
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
def self.extract_xrefs(text)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Plugin
|
|
5
|
+
module Glossarist
|
|
6
|
+
# Filters an array of Glossarist::Section objects by include/exclude patterns.
|
|
7
|
+
#
|
|
8
|
+
# Pattern matching uses *substring* matching against section IDs
|
|
9
|
+
# (e.g., pattern "3" matches sections with IDs "3", "3.1", "34").
|
|
10
|
+
# For the common _arm/_mim filtering, use patterns like "_arm" and "_mim"
|
|
11
|
+
# which are specific enough to avoid false positives.
|
|
12
|
+
#
|
|
13
|
+
# @example Exclude _arm and _mim sections
|
|
14
|
+
# SectionFilter.new(exclude: ["_arm", "_mim"]).apply(sections)
|
|
15
|
+
#
|
|
16
|
+
# @example Include only _arm sections
|
|
17
|
+
# SectionFilter.new(include: ["_arm"]).apply(sections)
|
|
18
|
+
class SectionFilter
|
|
19
|
+
# @param exclude [Array<String>] substring patterns; sections whose ID
|
|
20
|
+
# contains any of these are removed
|
|
21
|
+
# @param include [Array<String>] substring patterns; only sections whose
|
|
22
|
+
# ID contains at least one of these are kept (empty = include all)
|
|
23
|
+
def initialize(exclude: [], include: [])
|
|
24
|
+
@exclude = exclude.reject(&:empty?)
|
|
25
|
+
@include = include.reject(&:empty?)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @param sections [Array<Glossarist::Section>]
|
|
29
|
+
# @return [Array<Glossarist::Section>]
|
|
30
|
+
def apply(sections)
|
|
31
|
+
sections.select { |s| matches?(s) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def matches?(section)
|
|
37
|
+
!excluded?(section) && included?(section)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def excluded?(section)
|
|
41
|
+
@exclude.any? { |p| section.id.include?(p) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def included?(section)
|
|
45
|
+
@include.empty? || @include.any? { |p| section.id.include?(p) }
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Plugin
|
|
5
|
+
module Glossarist
|
|
6
|
+
class TemplateRenderer
|
|
7
|
+
DEFAULT_TEMPLATE = File.join(TEMPLATES_DIR, "_concept.liquid")
|
|
8
|
+
|
|
9
|
+
def initialize(file_system:, lang: "eng")
|
|
10
|
+
@file_system = file_system
|
|
11
|
+
@lang = lang
|
|
12
|
+
@template_cache = {}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def render_concepts(concepts, depth:, anchor_prefix: nil)
|
|
16
|
+
tree = build_concept_tree(concepts)
|
|
17
|
+
parts = tree.map { |c| render_tree_node(c, depth, anchor_prefix) }
|
|
18
|
+
normalize_whitespace(parts.join("\n\n"))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def render_concept(concept, depth:, anchor_prefix: nil)
|
|
22
|
+
l10n = concept.localization(@lang)
|
|
23
|
+
context = {
|
|
24
|
+
"concept" => concept.to_liquid,
|
|
25
|
+
"l10n" => l10n&.to_liquid,
|
|
26
|
+
"depth_marker" => "=" * (depth + 1),
|
|
27
|
+
"anchor" => build_anchor(concept.data.id.to_s, anchor_prefix),
|
|
28
|
+
}
|
|
29
|
+
template_content = cached_template(concept)
|
|
30
|
+
rendered = render_template(template_content, context)
|
|
31
|
+
normalize_whitespace(rendered)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def cached_template(concept)
|
|
37
|
+
version = concept.schema_version
|
|
38
|
+
@template_cache[version] ||= begin
|
|
39
|
+
path = File.join(TEMPLATES_DIR, "_concept_#{version}.liquid")
|
|
40
|
+
File.exist?(path) ? File.read(path) : File.read(DEFAULT_TEMPLATE)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def render_tree_node((concept, children), depth, anchor_prefix)
|
|
45
|
+
result = render_concept(concept, depth: depth,
|
|
46
|
+
anchor_prefix: anchor_prefix)
|
|
47
|
+
children.each do |child_node|
|
|
48
|
+
result += "\n" + render_tree_node(child_node, depth + 1,
|
|
49
|
+
anchor_prefix)
|
|
50
|
+
end
|
|
51
|
+
result
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def build_concept_tree(concepts)
|
|
55
|
+
concept_map = concepts.to_h { |c| [concept_id(c), c] }
|
|
56
|
+
children_of = Hash.new { |h, k| h[k] = [] }
|
|
57
|
+
roots = []
|
|
58
|
+
|
|
59
|
+
concepts.each do |c|
|
|
60
|
+
parent_id = find_parent_id(c)
|
|
61
|
+
if parent_id && concept_map[parent_id]
|
|
62
|
+
children_of[parent_id] << c
|
|
63
|
+
else
|
|
64
|
+
roots << c
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
build_tree_nodes(roots, children_of)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def build_tree_nodes(concepts, children_of)
|
|
72
|
+
concepts.map do |c|
|
|
73
|
+
[c, build_tree_nodes(children_of[concept_id(c)], children_of)]
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def concept_id(concept)
|
|
78
|
+
concept.data.id.to_s
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def find_parent_id(concept)
|
|
82
|
+
concept.data.related&.find { |r| r.type == "broader" }&.ref&.id&.to_s
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def build_anchor(id, prefix)
|
|
86
|
+
anchor = prefix ? "#{prefix}#{id}" : id
|
|
87
|
+
if anchor.match?(/\A\d/)
|
|
88
|
+
anchor
|
|
89
|
+
else
|
|
90
|
+
Metanorma::Utils.to_ncname(anchor.gsub(
|
|
91
|
+
":", "_"
|
|
92
|
+
))
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def render_template(content, assigns)
|
|
97
|
+
LiquidRendering.render(
|
|
98
|
+
content,
|
|
99
|
+
include_paths: [TEMPLATES_DIR, @file_system].compact,
|
|
100
|
+
assigns: assigns,
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def normalize_whitespace(text)
|
|
105
|
+
text.gsub(/\n{3,}/, "\n\n")
|
|
106
|
+
.gsub(/([^\]\n])\n(={2,6} )/, "\\1\n\n\\2")
|
|
107
|
+
.strip
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|