metanorma-utils 1.2.9 → 1.3.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d2ac1711efc0b167d21909878face2dd4787e79d9aa5141d58908c1a8904056
4
- data.tar.gz: e632e154726172f7ce1d09b0450d9b0ad70274936070336e01d05baff77e21ad
3
+ metadata.gz: cb766833ae078338bdac8e0954736f83dfc5feee9de6cbc15c03bd1998848529
4
+ data.tar.gz: 93ee70fa9e268f252c5c9c3f5dd58e2859c2b794b13d50e117f374ae8e80efde
5
5
  SHA512:
6
- metadata.gz: cc3c1a6c0482904f4bc9a5b94a1c8ac42e4da45961c7fc6bb4ccbe527ef51cda959707f4f4906ed6042cf30e9c1407eec071c04aa57b551a0c38a86a791a3c46
7
- data.tar.gz: dc015326b8d670cddc54488b452f48c7cf922a3677520d579798c8b56497957e3d934222f3f065a4600ea7bc0dc860ae6a8fd3fd5b443f38f482a5e84a8ce6ed
6
+ metadata.gz: 8572ae07eb6b970d0b00c1289b0652c293690b241b4d39a91b4df235a77ef29809dba122be75795bd7f8bde1fcebd59deeee9cb4f001259675626f73e0cd5869
7
+ data.tar.gz: 63f761a6ada0d87f7663811b89f594eb23dafb9d9da9c130d158bd58671648fc5b6cd8a3ed9db895345b1267e3c02141937bda7141e414971dabafa9ec93bbaa
@@ -2,3 +2,4 @@ require_relative "utils/version"
2
2
  require_relative "utils/main"
3
3
  require_relative "utils/image"
4
4
  require_relative "utils/log"
5
+ require_relative "utils/xml"
@@ -48,4 +48,20 @@ class Hash
48
48
  end
49
49
  result
50
50
  end
51
+
52
+ def deep_merge(second)
53
+ merger = proc { |_, v1, v2|
54
+ if Hash === v1 && Hash === v2
55
+ v1.merge(v2, &merger)
56
+ elsif Array === v1 && Array === v2
57
+ v1 | v2
58
+ elsif [:undefined, nil,
59
+ :nil].include?(v2)
60
+ v1
61
+ else
62
+ v2
63
+ end
64
+ }
65
+ merge(second.to_h, &merger)
66
+ end
51
67
  end
data/lib/utils/main.rb CHANGED
@@ -1,34 +1,35 @@
1
1
  require "asciidoctor"
2
2
  require "tempfile"
3
3
  require "sterile"
4
- require "uuidtools"
5
4
  require "htmlentities"
5
+ require "nokogiri"
6
6
 
7
7
  module Metanorma
8
8
  module Utils
9
- NAMECHAR = "\u0000-\u002c\u002f\u003a-\u0040\\u005b-\u005e"\
10
- "\u0060\u007b-\u00b6\u00b8-\u00bf\u00d7\u00f7\u037e"\
11
- "\u2000-\u200b"\
12
- "\u200e-\u203e\u2041-\u206f\u2190-\u2bff\u2ff0-\u3000".freeze
13
- NAMESTARTCHAR = "\\u002d\u002e\u0030-\u0039\u00b7\u0300-\u036f"\
14
- "\u203f-\u2040".freeze
15
-
16
9
  class << self
17
- def to_ncname(tag, asciionly: true)
18
- asciionly and tag = HTMLEntities.new.encode(tag, :basic, :hexadecimal)
19
- start = tag[0]
20
- ret1 = if %r([#{NAMECHAR}#])o.match?(start)
21
- "_"
22
- else
23
- (%r([#{NAMESTARTCHAR}#])o.match?(start) ? "_#{start}" : start)
24
- end
25
- ret2 = tag[1..-1] || ""
26
- (ret1 || "") + ret2.gsub(%r([#{NAMECHAR}#])o, "_")
10
+ def attr_code(attributes)
11
+ attributes.compact.transform_values do |v|
12
+ v.is_a?(String) ? HTMLEntities.new.decode(v) : v
13
+ end
27
14
  end
28
15
 
29
- def anchor_or_uuid(node = nil)
30
- uuid = UUIDTools::UUID.random_create
31
- node.nil? || node.id.nil? || node.id.empty? ? "_#{uuid}" : node.id
16
+ # , " => ," : CSV definition does not deal with space followed by quote
17
+ # at start of field
18
+ def csv_split(text, delim = ";")
19
+ return if text.nil?
20
+
21
+ CSV.parse_line(text&.gsub(/#{delim} "(?!")/, "#{delim}\""),
22
+ liberal_parsing: true,
23
+ col_sep: delim)&.compact&.map(&:strip)
24
+ end
25
+
26
+ # if the contents of node are blocks, output them to out;
27
+ # else, wrap them in <p>
28
+ def wrap_in_para(node, out)
29
+ if node.blocks? then out << node.content
30
+ else
31
+ out.p { |p| p << node.content }
32
+ end
32
33
  end
33
34
 
34
35
  def asciidoc_sub(text, flavour = :standoc)
@@ -140,35 +141,29 @@ module Metanorma
140
141
  %w(Arab Aran Hebr).include? script
141
142
  end
142
143
 
143
- # not currently used
144
- def flatten_rawtext_lines(node, result)
145
- node.lines.each do |x|
146
- result << if node.respond_to?(:context) &&
147
- (node.context == :literal || node.context == :listing)
148
- x.gsub(/</, "&lt;").gsub(/>/, "&gt;")
149
- else
150
- # strip not only HTML <tag>, and Asciidoc xrefs <<xref>>
151
- x.gsub(/<[^>]*>+/, "")
152
- end
153
- end
154
- result
155
- end
156
-
157
- # not currently used
158
- # if node contains blocks, flatten them into a single line;
159
- # and extract only raw text
160
- def flatten_rawtext(node)
161
- result = []
162
- if node.respond_to?(:blocks) && node.blocks?
163
- node.blocks.each { |b| result << flatten_rawtext(b) }
164
- elsif node.respond_to?(:lines)
165
- result = flatten_rawtext_lines(node, result)
166
- elsif node.respond_to?(:text)
167
- result << node.text.gsub(/<[^>]*>+/, "")
168
- else
169
- result << node.content.gsub(/<[^>]*>+/, "")
144
+ # convert definition list term/value pair into Nokogiri XML attribute
145
+ def dl_to_attrs(elem, dlist, name)
146
+ e = dlist.at("./dt[text()='#{name}']") or return
147
+ val = e.at("./following::dd/p") || e.at("./following::dd") or return
148
+ elem[name] = val.text
149
+ end
150
+
151
+ # convert definition list term/value pairs into Nokogiri XML elements
152
+ def dl_to_elems(ins, elem, dlist, name)
153
+ a = elem.at("./#{name}[last()]")
154
+ ins = a if a
155
+ dlist.xpath("./dt[text()='#{name}']").each do |e|
156
+ ins = dl_to_elems1(e, name, ins)
170
157
  end
171
- result.reject(&:empty?)
158
+ ins
159
+ end
160
+
161
+ def dl_to_elems1(term, name, ins)
162
+ v = term.at("./following::dd")
163
+ e = v.elements and e.size == 1 && e.first.name == "p" and v = e.first
164
+ v.name = name
165
+ ins.next = v
166
+ ins.next
172
167
  end
173
168
  end
174
169
  end
data/lib/utils/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Metanorma
2
2
  module Utils
3
- VERSION = "1.2.9".freeze
3
+ VERSION = "1.3.2".freeze
4
4
  end
5
5
  end
data/lib/utils/xml.rb ADDED
@@ -0,0 +1,66 @@
1
+ require "asciidoctor"
2
+ require "tempfile"
3
+ require "uuidtools"
4
+ require "htmlentities"
5
+ require "nokogiri"
6
+
7
+ module Metanorma
8
+ module Utils
9
+ NAMECHAR = "\u0000-\u002c\u002f\u003a-\u0040\\u005b-\u005e"\
10
+ "\u0060\u007b-\u00b6\u00b8-\u00bf\u00d7\u00f7\u037e"\
11
+ "\u2000-\u200b"\
12
+ "\u200e-\u203e\u2041-\u206f\u2190-\u2bff\u2ff0-\u3000".freeze
13
+ NAMESTARTCHAR = "\\u002d\u002e\u0030-\u0039\u00b7\u0300-\u036f"\
14
+ "\u203f-\u2040".freeze
15
+
16
+ class << self
17
+ def to_ncname(tag, asciionly: true)
18
+ asciionly and tag = HTMLEntities.new.encode(tag, :basic, :hexadecimal)
19
+ start = tag[0]
20
+ ret1 = if %r([#{NAMECHAR}#])o.match?(start)
21
+ "_"
22
+ else
23
+ (%r([#{NAMESTARTCHAR}#])o.match?(start) ? "_#{start}" : start)
24
+ end
25
+ ret2 = tag[1..-1] || ""
26
+ (ret1 || "") + ret2.gsub(%r([#{NAMECHAR}#])o, "_")
27
+ end
28
+
29
+ def anchor_or_uuid(node = nil)
30
+ uuid = UUIDTools::UUID.random_create
31
+ node.nil? || node.id.nil? || node.id.empty? ? "_#{uuid}" : node.id
32
+ end
33
+
34
+ NOKOHEAD = <<~HERE.freeze
35
+ <!DOCTYPE html SYSTEM
36
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
37
+ <html xmlns="http://www.w3.org/1999/xhtml">
38
+ <head> <title></title> <meta charset="UTF-8" /> </head>
39
+ <body> </body> </html>
40
+ HERE
41
+
42
+ # block for processing XML document fragments as XHTML,
43
+ # to allow for HTMLentities
44
+ # Unescape special chars used in Asciidoctor substitution processing
45
+ def noko(&block)
46
+ doc = ::Nokogiri::XML.parse(NOKOHEAD)
47
+ fragment = doc.fragment("")
48
+ ::Nokogiri::XML::Builder.with fragment, &block
49
+ fragment.to_xml(encoding: "US-ASCII", indent: 0,
50
+ save_with: Nokogiri::XML::Node::SaveOptions::AS_XML)
51
+ .lines.map do |l|
52
+ l.gsub(/>\n$/, ">").gsub(/\s*\n$/m, " ").gsub("&#150;", "\u0096")
53
+ .gsub("&#151;", "\u0097").gsub("&#x96;", "\u0096")
54
+ .gsub("&#x97;", "\u0097")
55
+ end
56
+ end
57
+
58
+ def ns(xpath)
59
+ xpath.gsub(%r{/([a-zA-z])}, "/xmlns:\\1")
60
+ .gsub(%r{::([a-zA-z])}, "::xmlns:\\1")
61
+ .gsub(%r{\[([a-zA-z][a-z0-9A-Z@/-]* ?=)}, "[xmlns:\\1")
62
+ .gsub(%r{\[([a-zA-z][a-z0-9A-Z@/-]*[/\[\]])}, "[xmlns:\\1")
63
+ end
64
+ end
65
+ end
66
+ end
@@ -49,4 +49,11 @@ RSpec.describe Metanorma::Utils do
49
49
  expect(result[:test4][0]).to include(:test41)
50
50
  expect(result[:test4][0][:test41]).to eq("test41")
51
51
  end
52
+
53
+ it "deep merges hashes" do
54
+ hash1 = { a: [1, 2], b: "c", c: 4, e: { f: { g: "1" } } }
55
+ hash2 = { a: [3], b: "d", d: 5, e: { f: { h: "2" } } }
56
+ expect(hash1.deep_merge(hash2)).to eq({ a: [1, 2, 3], b: "d", c: 4,
57
+ d: 5, e: { f: { g: "1", h: "2" } } })
58
+ end
52
59
  end