metanorma 2.1.4 → 2.1.6

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.
@@ -1,68 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "writeable"
4
+
1
5
  module Metanorma
2
6
  class Compile
3
- def relaton_export(isodoc, options)
4
- options[:relaton] or return
5
- xml = Nokogiri::XML(isodoc, &:huge)
6
- bibdata = xml.at("//bibdata") || xml.at("//xmlns:bibdata")
7
- File.open(options[:relaton], "w:UTF-8") { |f| f.write bibdata.to_xml }
8
- end
7
+ module Extract
8
+ # @param isodoc [String] the XML document
9
+ # @param dirname [String, nil] the directory to extract to
10
+ # @param extract_types [Array<Symbol>, nil] the types to extract
11
+ # @return [void]
12
+ def self.extract(isodoc, dirname, extract_types)
13
+ dirname or return
14
+ extract_types.nil? || extract_types.empty? and
15
+ extract_types = %i[sourcecode image requirement]
16
+ FileUtils.rm_rf dirname
17
+ FileUtils.mkdir_p dirname
18
+ xml = Nokogiri::XML(isodoc, &:huge)
19
+ extract_types.each do |type|
20
+ case type
21
+ when :sourcecode
22
+ export_sourcecode(xml, dirname)
23
+ when :image
24
+ export_image(xml, dirname)
25
+ when :requirement
26
+ export_requirement(xml, dirname)
27
+ end
28
+ end
29
+ end
9
30
 
10
- def clean_sourcecode(xml)
11
- xml.xpath(".//callout | .//annotation | .//xmlns:callout | "\
12
- ".//xmlns:annotation").each(&:remove)
13
- xml.xpath(".//br | .//xmlns:br").each { |x| x.replace("\n") }
14
- a = xml.at("./body | ./xmlns:body") and xml = a
15
- HTMLEntities.new.decode(xml.children.to_xml)
16
- end
31
+ class << self
32
+ include Writeable
17
33
 
18
- def extract(isodoc, dirname, extract_types)
19
- dirname or return
20
- extract_types.nil? || extract_types.empty? and
21
- extract_types = %i[sourcecode image requirement]
22
- FileUtils.rm_rf dirname
23
- FileUtils.mkdir_p dirname
24
- xml = Nokogiri::XML(isodoc, &:huge)
25
- sourcecode_export(xml, dirname) if extract_types.include? :sourcecode
26
- image_export(xml, dirname) if extract_types.include? :image
27
- extract_types.include?(:requirement) and
28
- requirement_export(xml, dirname)
29
- end
34
+ private
30
35
 
31
- def sourcecode_export(xml, dirname)
32
- xml.at("//sourcecode | //xmlns:sourcecode") or return
33
- FileUtils.mkdir_p "#{dirname}/sourcecode"
34
- xml.xpath("//sourcecode | //xmlns:sourcecode").each_with_index do |s, i|
35
- filename = s["filename"] || sprintf("sourcecode-%04d.txt", i)
36
- export_output("#{dirname}/sourcecode/#{filename}",
37
- clean_sourcecode(s.dup))
38
- end
39
- end
36
+ # @param xml [Nokogiri::XML::Document] the XML document
37
+ # @return [String] the cleaned sourcecode
38
+ def clean_sourcecode(xml)
39
+ xml.xpath(".//callout | .//annotation | .//xmlns:callout | "\
40
+ ".//xmlns:annotation").each(&:remove)
41
+ xml.xpath(".//br | .//xmlns:br").each { |x| x.replace("\n") }
42
+ a = xml.at("./body | ./xmlns:body") and xml = a
43
+ HTMLEntities.new.decode(xml.children.to_xml)
44
+ end
40
45
 
41
- def image_export(xml, dirname)
42
- xml.at("//image | //xmlns:image") or return
43
- FileUtils.mkdir_p "#{dirname}/image"
44
- xml.xpath("//image | //xmlns:image").each_with_index do |s, i|
45
- next unless /^data:image/.match? s["src"]
46
+ def export_sourcecode(xml, dirname)
47
+ xml.at("//sourcecode | //xmlns:sourcecode") or return
48
+ FileUtils.mkdir_p "#{dirname}/sourcecode"
49
+ xml.xpath("//sourcecode | //xmlns:sourcecode").each_with_index do |s, i|
50
+ filename = s["filename"] || sprintf("sourcecode-%04d.txt", i)
51
+ export_output("#{dirname}/sourcecode/#{filename}",
52
+ clean_sourcecode(s.dup))
53
+ end
54
+ end
46
55
 
47
- %r{^data:image/(?<imgtype>[^;]+);base64,(?<imgdata>.+)$} =~ s["src"]
48
- fn = s["filename"] || sprintf("image-%<num>04d.%<name>s",
49
- num: i, name: imgtype)
50
- export_output("#{dirname}/image/#{fn}", Base64.strict_decode64(imgdata),
51
- binary: true)
52
- end
53
- end
56
+ def export_image(xml, dirname)
57
+ xml.at("//image | //xmlns:image") or return
58
+ FileUtils.mkdir_p "#{dirname}/image"
59
+ xml.xpath("//image | //xmlns:image").each_with_index do |s, i|
60
+ next unless /^data:image/.match? s["src"]
61
+
62
+ %r{^data:image/(?<imgtype>[^;]+);base64,(?<imgdata>.+)$} =~ s["src"]
63
+ fn = s["filename"] || sprintf("image-%<num>04d.%<name>s",
64
+ num: i, name: imgtype)
65
+ export_output(
66
+ "#{dirname}/image/#{fn}",
67
+ Base64.strict_decode64(imgdata),
68
+ binary: true,
69
+ )
70
+ end
71
+ end
54
72
 
55
- REQUIREMENT_XPATH =
56
- "//requirement | //xmlns:requirement | //recommendation | "\
57
- "//xmlns:recommendation | //permission | //xmlns:permission".freeze
73
+ REQUIREMENT_XPATH =
74
+ "//requirement | //xmlns:requirement | //recommendation | "\
75
+ "//xmlns:recommendation | //permission | //xmlns:permission"
58
76
 
59
- def requirement_export(xml, dirname)
60
- xml.at(REQUIREMENT_XPATH) or return
61
- FileUtils.mkdir_p "#{dirname}/requirement"
62
- xml.xpath(REQUIREMENT_XPATH).each_with_index do |s, i|
63
- fn = s["filename"] ||
64
- sprintf("%<name>s-%<num>04d.xml", name: s.name, num: i)
65
- export_output("#{dirname}/requirement/#{fn}", s)
77
+ def export_requirement(xml, dirname)
78
+ xml.at(REQUIREMENT_XPATH) or return
79
+ FileUtils.mkdir_p "#{dirname}/requirement"
80
+ xml.xpath(REQUIREMENT_XPATH).each_with_index do |s, i|
81
+ fn = s["filename"] ||
82
+ sprintf("%<name>s-%<num>04d.xml", name: s.name, num: i)
83
+ export_output("#{dirname}/requirement/#{fn}", s)
84
+ end
85
+ end
66
86
  end
67
87
  end
68
88
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ class Compile
5
+ module Flavor
6
+ # Load the flavor gem for the given standard type
7
+ # @param stdtype [Symbol] the standard type
8
+ # @return [void]
9
+ def load_flavor(stdtype)
10
+ stdtype = stdtype.to_sym
11
+ flavor = stdtype2flavor(stdtype)
12
+ @registry.supported_backends.include? stdtype or
13
+ Util.log("[metanorma] Info: Loading `#{flavor}` gem "\
14
+ "for standard type `#{stdtype}`.", :info)
15
+ require_flavor(flavor)
16
+ @registry.supported_backends.include? stdtype or
17
+ Util.log("[metanorma] Error: The `#{flavor}` gem does not "\
18
+ "support the standard type #{stdtype}. Exiting.", :fatal)
19
+ end
20
+
21
+ # Convert the standard type to the flavor gem name
22
+ # @param stdtype [Symbol] the standard type
23
+ # @return [String] the flavor gem name
24
+ def stdtype2flavor(stdtype)
25
+ flavor = STDTYPE2FLAVOR[stdtype] || stdtype
26
+ "metanorma-#{flavor}"
27
+ end
28
+
29
+ private
30
+
31
+ STDTYPE2FLAVOR = {}.freeze
32
+
33
+ def require_flavor(flavor)
34
+ require flavor
35
+ Util.log("[metanorma] Info: gem `#{flavor}` loaded.", :info)
36
+ rescue LoadError => e
37
+ error_log = "#{Date.today}-error.log"
38
+ File.write(error_log, e)
39
+
40
+ msg = <<~MSG
41
+ Error: #{e.message}
42
+ Metanorma has encountered an exception.
43
+
44
+ If this problem persists, please report this issue at the following link:
45
+
46
+ * https://github.com/metanorma/metanorma/issues/new
47
+
48
+ Please attach the #{error_log} file.
49
+ Your valuable feedback is very much appreciated!
50
+
51
+ - The Metanorma team
52
+ MSG
53
+ Util.log(msg, :fatal)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ class Compile
5
+ class OutputFilename
6
+ # Returns an instance of OutputFilename from the source filename
7
+ # @param source_filename [String] the source filename
8
+ # @param output_dir [String, nil] the output directory
9
+ # @param processor [Metanorma::Processor, nil] the processor
10
+ # @return [OutputFilename] the instance of OutputFilename
11
+ def self.from_filename(source_filename, output_dir = nil, processor = nil)
12
+ new(strip_ext(source_filename), output_dir, processor)
13
+ end
14
+
15
+ class << self
16
+ private
17
+
18
+ def strip_ext(filename)
19
+ filename.sub(/\.[^.]*$/, "")
20
+ end
21
+ end
22
+
23
+ # @param noext_filename [String] the path (absolute/relative) of the source file, without extension (e.g., "/a/b/c/test")
24
+ # @param output_dir [String, nil] the output directory
25
+ # @param processor [Metanorma::Processor, nil] the processor
26
+ # @return [OutputFilename] the instance of OutputFilename
27
+ def initialize(noext_filename, output_dir = nil, processor = nil)
28
+ @noext_filename = noext_filename
29
+ @output_dir = output_dir
30
+ @processor = processor
31
+ end
32
+
33
+ # Returns the full file path name with the semantic XML extension
34
+ # @return [String] the full file path name with the semantic XML extension
35
+ def semantic_xml
36
+ with_extension("xml")
37
+ end
38
+
39
+ # Returns the full file path name with the presentation XML extension
40
+ # @return [String] the full file path name with the presentation XML extension
41
+ def presentation_xml
42
+ with_extension("presentation.xml")
43
+ end
44
+
45
+ # Returns the full file path name with the given format extension
46
+ # @param format [Symbol] the format
47
+ # @return [String, nil] the full file path name with the format extension
48
+ def for_format(format)
49
+ ext = @processor&.output_formats&.[](format)
50
+ ext ? with_extension(ext) : nil
51
+ end
52
+
53
+ # Returns the full file path name with the given extension
54
+ # @param ext [String] the extension
55
+ # @return [String] the full file path name with the extension
56
+ def with_extension(ext)
57
+ file = change_output_dir
58
+ "#{file}.#{ext}"
59
+ end
60
+
61
+ private
62
+
63
+ def change_output_dir
64
+ File.expand_path(if !@output_dir.nil?
65
+ File.join(
66
+ @output_dir,
67
+ File.basename(@noext_filename),
68
+ )
69
+ else
70
+ @noext_filename
71
+ end)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ class Compile
5
+ class OutputFilenameConfig
6
+ DEFAULT_TEMPLATE =
7
+ "{{ document.docidentifier | downcase" \
8
+ " | replace: '/' , '-'" \
9
+ " | replace: ' ' , '-' }}"
10
+
11
+ attr_reader :template
12
+
13
+ def initialize(template)
14
+ @template = if template.nil? || template.empty?
15
+ DEFAULT_TEMPLATE
16
+ else
17
+ template
18
+ end
19
+ end
20
+
21
+ def generate_filename(relaton_data)
22
+ template = Liquid::Template.parse(@template)
23
+ template.render("document" => relaton_data)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "liquid"
4
+
5
+ module Metanorma
6
+ class Compile
7
+ class RelatonDrop < Liquid::Drop
8
+ def initialize(relaton_data)
9
+ @relaton = relaton_data
10
+ end
11
+
12
+ def docidentifier
13
+ at("./docidentifier")
14
+ end
15
+
16
+ def title
17
+ at("./title")
18
+ end
19
+
20
+ def date
21
+ at("./date/on")
22
+ end
23
+
24
+ def publisher
25
+ at("./contributor[role/@type = 'publisher']/organization/name")
26
+ end
27
+
28
+ def language
29
+ at("./language")
30
+ end
31
+
32
+ def script
33
+ at("./script")
34
+ end
35
+
36
+ def version
37
+ at("./version")
38
+ end
39
+
40
+ def slugify
41
+ docidentifier&.downcase
42
+ &.gsub(/[^a-z0-9]+/, "-")
43
+ &.gsub(/-+/, "-")
44
+ &.gsub(/^-|-$/, "")
45
+ end
46
+
47
+ private
48
+
49
+ def at(xpath)
50
+ @relaton.at(xpath)&.text
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ class Compile
5
+ module Validator
6
+ def validate_options!(options)
7
+ validate_type!(options)
8
+ validate_format!(options)
9
+ end
10
+
11
+ def validate_type!(options)
12
+ unless options[:type]
13
+ Util.log("[metanorma] Error: Please specify a standard type: "\
14
+ "#{@registry.supported_backends}.", :fatal)
15
+ end
16
+ stdtype = options[:type].to_sym
17
+ load_flavor(stdtype)
18
+ end
19
+
20
+ def validate_format!(options)
21
+ unless options[:format] == :asciidoc
22
+ Util.log("[metanorma] Error: Only source file format currently "\
23
+ "supported is 'asciidoc'.", :fatal)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ class Compile
5
+ module Writeable
6
+ def export_output(fname, content, **options)
7
+ mode = options[:binary] ? "wb" : "w:UTF-8"
8
+ File.open(fname, mode) { |f| f.write content }
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,3 +1,3 @@
1
1
  module Metanorma
2
- VERSION = "2.1.4".freeze
2
+ VERSION = "2.1.6".freeze
3
3
  end
data/metanorma.gemspec CHANGED
@@ -45,7 +45,7 @@ Gem::Specification.new do |spec|
45
45
  spec.add_development_dependency "rspec", "~> 3.0"
46
46
  spec.add_development_dependency "rspec-command", "~> 1.0"
47
47
  spec.add_development_dependency "rubocop", "~> 1"
48
- spec.add_development_dependency "rubocop-performance"
48
+ spec.add_development_dependency "rubocop-performance"
49
49
  spec.add_development_dependency "sassc-embedded", "~> 1"
50
50
  spec.add_development_dependency "simplecov", "~> 0.15"
51
51
  spec.add_development_dependency "xml-c14n"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metanorma
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.4
4
+ version: 2.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-04 00:00:00.000000000 Z
11
+ date: 2025-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: asciidoctor
@@ -340,8 +340,13 @@ files:
340
340
  - lib/metanorma/collection/xrefprocess/xrefprocess.rb
341
341
  - lib/metanorma/compile/compile.rb
342
342
  - lib/metanorma/compile/compile_options.rb
343
- - lib/metanorma/compile/compile_validate.rb
344
343
  - lib/metanorma/compile/extract.rb
344
+ - lib/metanorma/compile/flavor.rb
345
+ - lib/metanorma/compile/output_filename.rb
346
+ - lib/metanorma/compile/output_filename_config.rb
347
+ - lib/metanorma/compile/relaton_drop.rb
348
+ - lib/metanorma/compile/validator.rb
349
+ - lib/metanorma/compile/writeable.rb
345
350
  - lib/metanorma/config/config.rb
346
351
  - lib/metanorma/input.rb
347
352
  - lib/metanorma/input/asciidoc.rb
@@ -1,68 +0,0 @@
1
- module Metanorma
2
- class Compile
3
- def validate_options(options)
4
- validate_type(options)
5
- validate_format(options)
6
- end
7
-
8
- def validate_type(options)
9
- unless options[:type]
10
- Util.log("[metanorma] Error: Please specify a standard type: "\
11
- "#{@registry.supported_backends}.", :fatal)
12
- end
13
- stdtype = options[:type].to_sym
14
- load_flavor(stdtype)
15
- end
16
-
17
- def validate_format(options)
18
- unless options[:format] == :asciidoc
19
- Util.log("[metanorma] Error: Only source file format currently "\
20
- "supported is 'asciidoc'.", :fatal)
21
- end
22
- end
23
-
24
- def load_flavor(stdtype)
25
- stdtype = stdtype.to_sym
26
- flavor = stdtype2flavor(stdtype)
27
- @registry.supported_backends.include? stdtype or
28
- Util.log("[metanorma] Info: Loading `#{flavor}` gem "\
29
- "for standard type `#{stdtype}`.", :info)
30
- require_flavor(flavor)
31
- @registry.supported_backends.include? stdtype or
32
- Util.log("[metanorma] Error: The `#{flavor}` gem does not "\
33
- "support the standard type #{stdtype}. Exiting.", :fatal)
34
- end
35
-
36
- def stdtype2flavor(stdtype)
37
- flavor = STDTYPE2FLAVOR[stdtype] || stdtype
38
- "metanorma-#{flavor}"
39
- end
40
-
41
- private
42
-
43
- STDTYPE2FLAVOR = {}.freeze
44
-
45
- def require_flavor(flavor)
46
- require flavor
47
- Util.log("[metanorma] Info: gem `#{flavor}` loaded.", :info)
48
- rescue LoadError => e
49
- error_log = "#{Date.today}-error.log"
50
- File.write(error_log, e)
51
-
52
- msg = <<~MSG
53
- Error: #{e.message}
54
- Metanorma has encountered an exception.
55
-
56
- If this problem persists, please report this issue at the following link:
57
-
58
- * https://github.com/metanorma/metanorma/issues/new
59
-
60
- Please attach the #{error_log} file.
61
- Your valuable feedback is very much appreciated!
62
-
63
- - The Metanorma team
64
- MSG
65
- Util.log(msg, :fatal)
66
- end
67
- end
68
- end