metanorma-standoc 1.3.24 → 1.3.29

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/macos.yml +10 -1
  3. data/.github/workflows/ubuntu.yml +13 -3
  4. data/.github/workflows/windows.yml +8 -1
  5. data/lib/asciidoctor/standoc/base.rb +29 -11
  6. data/lib/asciidoctor/standoc/biblio.rng +75 -28
  7. data/lib/asciidoctor/standoc/blocks.rb +12 -5
  8. data/lib/asciidoctor/standoc/cleanup.rb +17 -8
  9. data/lib/asciidoctor/standoc/cleanup_block.rb +3 -0
  10. data/lib/asciidoctor/standoc/cleanup_boilerplate.rb +1 -3
  11. data/lib/asciidoctor/standoc/cleanup_inline.rb +3 -0
  12. data/lib/asciidoctor/standoc/cleanup_ref.rb +15 -6
  13. data/lib/asciidoctor/standoc/cleanup_section.rb +36 -8
  14. data/lib/asciidoctor/standoc/front.rb +5 -3
  15. data/lib/asciidoctor/standoc/inline.rb +43 -18
  16. data/lib/asciidoctor/standoc/isodoc.rng +31 -1
  17. data/lib/asciidoctor/standoc/macros.rb +2 -1
  18. data/lib/asciidoctor/standoc/macros_yaml2text.rb +142 -0
  19. data/lib/asciidoctor/standoc/ref.rb +6 -4
  20. data/lib/asciidoctor/standoc/section.rb +41 -9
  21. data/lib/asciidoctor/standoc/utils.rb +2 -11
  22. data/lib/asciidoctor/standoc/validate.rb +8 -2
  23. data/lib/asciidoctor/standoc/validate_section.rb +1 -3
  24. data/lib/metanorma/standoc/latexml_requirement.rb +14 -12
  25. data/lib/metanorma/standoc/version.rb +1 -1
  26. data/metanorma-standoc.gemspec +2 -2
  27. data/spec/asciidoctor-standoc/base_spec.rb +8 -0
  28. data/spec/asciidoctor-standoc/blocks_spec.rb +74 -2
  29. data/spec/asciidoctor-standoc/cleanup_spec.rb +75 -28
  30. data/spec/asciidoctor-standoc/inline_spec.rb +4 -3
  31. data/spec/asciidoctor-standoc/macros_spec.rb +9 -8
  32. data/spec/asciidoctor-standoc/macros_yaml2text_spec.rb +564 -0
  33. data/spec/asciidoctor-standoc/refs_dl_spec.rb +91 -3
  34. data/spec/asciidoctor-standoc/refs_spec.rb +272 -166
  35. data/spec/asciidoctor-standoc/section_spec.rb +197 -6
  36. data/spec/asciidoctor-standoc/validate_spec.rb +67 -2
  37. data/spec/assets/codes.yml +695 -0
  38. data/spec/assets/xref_error.adoc +7 -0
  39. data/spec/examples/codes_table.html +3174 -0
  40. data/spec/metanorma/processor_spec.rb +2 -2
  41. data/spec/spec_helper.rb +1 -0
  42. data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec.yml +77 -271
  43. data/spec/vcr_cassettes/isobib_get_123.yml +36 -82
  44. data/spec/vcr_cassettes/isobib_get_123_2001.yml +17 -40
  45. data/spec/vcr_cassettes/isobib_get_124.yml +19 -101
  46. data/spec/vcr_cassettes/rfcbib_get_rfc8341.yml +8 -8
  47. data/spec/vcr_cassettes/separates_iev_citations_by_top_level_clause.yml +34 -34
  48. metadata +11 -6
@@ -131,10 +131,13 @@ module Asciidoctor
131
131
  def note_cleanup(xmldoc)
132
132
  q = "//note[following-sibling::*[not(local-name() = 'note')]]"
133
133
  xmldoc.xpath(q).each do |n|
134
+ next if n["keep-separate"] == "true"
134
135
  next unless n.ancestors("table").empty?
135
136
  prev = n.previous_element || next
136
137
  n.parent = prev if ELEMS_ALLOW_NOTES.include? prev.name
137
138
  end
139
+ xmldoc.xpath("//note[@keep-separate]").each { |n| n.delete("keep-separate") }
140
+ xmldoc.xpath("//termnote[@keep-separate]").each { |n| n.delete("keep-separate") }
138
141
  end
139
142
 
140
143
  def requirement_cleanup(x)
@@ -18,7 +18,6 @@ module Asciidoctor
18
18
  source.each do |s|
19
19
  @anchors[s["bibitemid"]] or
20
20
  @log.add("Crossreferences", nil, "term source #{s['bibitemid']} not referenced")
21
- #warn "term source #{s['bibitemid']} not referenced"
22
21
  end
23
22
  if source.empty? && term.nil?
24
23
  div.next = @no_terms_boilerplate
@@ -49,8 +48,7 @@ module Asciidoctor
49
48
  TERM_CLAUSE = "//sections/terms | "\
50
49
  "//sections/clause[descendant::terms]".freeze
51
50
 
52
- NORM_REF = "//bibliography/references[title = 'Normative References' or "\
53
- "title = 'Normative references']".freeze
51
+ NORM_REF = "//bibliography/references[@normative = 'true']".freeze
54
52
 
55
53
  def boilerplate_isodoc(xmldoc)
56
54
  x = xmldoc.dup
@@ -90,6 +90,9 @@ module Asciidoctor
90
90
  end
91
91
 
92
92
  def origin_cleanup(xmldoc)
93
+ xmldoc.xpath("//origin/concept[termref]").each do |x|
94
+ x.replace(x.children)
95
+ end
93
96
  xmldoc.xpath("//origin").each do |x|
94
97
  x["citeas"] = @anchors&.dig(x["bibitemid"], :xref) ||
95
98
  @log.add("Crossreferences", x,
@@ -5,7 +5,7 @@ module Asciidoctor
5
5
  module Standoc
6
6
  module Cleanup
7
7
  def biblio_reorder(xmldoc)
8
- xmldoc.xpath("//references[title = 'Bibliography']").each do |r|
8
+ xmldoc.xpath("//references[@normative = 'false']").each do |r|
9
9
  biblio_reorder1(r)
10
10
  end
11
11
  end
@@ -49,8 +49,8 @@ module Asciidoctor
49
49
  # consecutively, but that standards codes are preserved as is:
50
50
  # only numeric references are renumbered
51
51
  def biblio_renumber(xmldoc)
52
- r = xmldoc.at("//references[title = 'Bibliography'] | "\
53
- "//clause[title = 'Bibliography'][.//bibitem]") or return
52
+ r = xmldoc.at("//references[@normative = 'false'] | "\
53
+ "//clause[.//references[@normative = 'false']]") or return
54
54
  r.xpath(".//bibitem[not(ancestor::bibitem)]").each_with_index do |b, i|
55
55
  next unless docid = b.at("./docidentifier[@type = 'metanorma']")
56
56
  next unless /^\[\d+\]$/.match(docid.text)
@@ -67,18 +67,23 @@ module Asciidoctor
67
67
  end
68
68
 
69
69
  def normref_cleanup(xmldoc)
70
- r = xmldoc.at(NORM_REF) || return
71
- #return if r.at("./bibitem[1]/preceding-sibling::*[1][local-name()='title']")
72
- preface = r.xpath("./title/following-sibling::*") &
70
+ r = xmldoc.at(self.class::NORM_REF) || return
71
+ preface = r.xpath("./title/following-sibling::*") & # intersection
73
72
  r.xpath("./bibitem[1]/preceding-sibling::*")
74
73
  preface.each { |n| n.remove }
75
74
  end
76
75
 
77
76
  def biblio_cleanup(xmldoc)
78
77
  biblio_reorder(xmldoc)
78
+ biblio_nested(xmldoc)
79
79
  biblio_renumber(xmldoc)
80
+ end
81
+
82
+ def biblio_nested(xmldoc)
80
83
  xmldoc.xpath("//references[references]").each do |t|
81
84
  t.name = "clause"
85
+ t.xpath("./references").each { |r| r["normative"] = t["normative"] }
86
+ t.delete("normative")
82
87
  end
83
88
  end
84
89
 
@@ -187,6 +192,10 @@ module Asciidoctor
187
192
  end
188
193
  bib
189
194
  end
195
+
196
+ def fetch_termbase(termbase, id)
197
+ ""
198
+ end
190
199
  end
191
200
  end
192
201
  end
@@ -11,7 +11,7 @@ module Asciidoctor
11
11
  module Cleanup
12
12
  def make_preface(x, s)
13
13
  if x.at("//foreword | //introduction | //acknowledgements | "\
14
- "//clause[@preface]")
14
+ "//*[@preface]")
15
15
  preface = s.add_previous_sibling("<preface/>").first
16
16
  f = x.at("//foreword") and preface.add_child f.remove
17
17
  f = x.at("//introduction") and preface.add_child f.remove
@@ -22,7 +22,7 @@ module Asciidoctor
22
22
  end
23
23
 
24
24
  def move_clauses_into_preface(x, preface)
25
- x.xpath("//clause[@preface]").each do |c|
25
+ x.xpath("//*[@preface]").each do |c|
26
26
  c.delete("preface")
27
27
  preface.add_child c.remove
28
28
  end
@@ -66,9 +66,22 @@ module Asciidoctor
66
66
  def sections_order_cleanup(x)
67
67
  s = x.at("//sections")
68
68
  make_preface(x, s)
69
+ make_annexes(x)
69
70
  make_bibliography(x, s)
70
71
  x.xpath("//sections/annex").reverse_each { |r| s.next = r.remove }
71
72
  end
73
+
74
+ def make_annexes(x)
75
+ x.xpath("//*[@annex]").each do |y|
76
+ y.delete("annex")
77
+ next if y.name == "annex" || !y.ancestors("annex").empty?
78
+ y.wrap("<annex/>")
79
+ y.parent["id"] = "_#{UUIDTools::UUID.random_create}"
80
+ y.parent["obligation"] = y["obligation"]
81
+ y.parent["language"] = y["language"]
82
+ y.parent["script"] = y["script"]
83
+ end
84
+ end
72
85
 
73
86
  def maxlevel(x)
74
87
  max = 5
@@ -117,7 +130,7 @@ module Asciidoctor
117
130
  end
118
131
 
119
132
  def obligations_cleanup_inherit(x)
120
- x.xpath("//annex | //clause").each do |r|
133
+ x.xpath("//annex | //clause[not(ancestor::boilerplate)]").each do |r|
121
134
  r["obligation"] = "normative" unless r["obligation"]
122
135
  end
123
136
  x.xpath(Utils::SUBCLAUSE_XPATH).each do |r|
@@ -192,7 +205,16 @@ module Asciidoctor
192
205
  end
193
206
  end
194
207
 
208
+ def termdef_from_termbase(xmldoc)
209
+ xmldoc.xpath("//term").each do |x|
210
+ if c = x.at("./origin/termref") and !x.at("./definition")
211
+ x.at("./origin").previous = fetch_termbase(c["base"], c.text)
212
+ end
213
+ end
214
+ end
215
+
195
216
  def termdef_cleanup(xmldoc)
217
+ termdef_from_termbase(xmldoc)
196
218
  termdef_unnest_cleanup(xmldoc)
197
219
  termdef_stem_cleanup(xmldoc)
198
220
  termdomain_cleanup(xmldoc)
@@ -209,15 +231,21 @@ module Asciidoctor
209
231
  # Numbers sort *after* letters; we use thorn to force that sort order.
210
232
  def symbol_key(x)
211
233
  key = x.dup
212
- key.xpath("//*[local-name() = 'math']").each do |m|
213
- m.replace(MathML2AsciiMath.m2a(m.to_xml))
234
+ key.traverse do |n|
235
+ next unless n.name == "math"
236
+ n.replace(grkletters(MathML2AsciiMath.m2a(n.to_xml)))
214
237
  end
215
- ret = Nokogiri::XML(MathML2AsciiMath.m2a(key.to_xml))
216
- HTMLEntities.new.decode(ret.text).strip.
238
+ ret = Nokogiri::XML(key.to_xml)
239
+ HTMLEntities.new.decode(ret.text).
240
+ gsub(/[\[\]\{\}<>\(\)]/, "").strip.
217
241
  gsub(/[[:punct]]|[_^]/, ":\\0").gsub(/`/, "").
218
242
  gsub(/[0-9]+/, "þ\\0")
219
243
  end
220
244
 
245
+ def grkletters(x)
246
+ x.gsub(/\b(alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega)\b/i, "&\\1;")
247
+ end
248
+
221
249
  def extract_symbols_list(dl)
222
250
  dl_out = []
223
251
  dl.xpath("./dt | ./dd").each do |dtd|
@@ -233,7 +261,7 @@ module Asciidoctor
233
261
  def symbols_cleanup(docxml)
234
262
  docxml.xpath("//definitions/dl").each do |dl|
235
263
  dl_out = extract_symbols_list(dl)
236
- dl_out.sort! { |a, b| a[:key] <=> b[:key] }
264
+ dl_out.sort! { |a, b| a[:key] <=> b[:key] || a[:dt] <=> b[:dt] }
237
265
  dl.children = dl_out.map { |d| d[:dt].to_s + d[:dd].to_s }.join("\n")
238
266
  end
239
267
  docxml
@@ -78,7 +78,9 @@ module Asciidoctor
78
78
 
79
79
  def datetypes
80
80
  %w{ published accessed created implemented obsoleted
81
- confirmed updated issued circulated unchanged received }
81
+ confirmed updated issued circulated unchanged received
82
+ vote-started vote-ended
83
+ }
82
84
  end
83
85
 
84
86
  def metadata_date(node, xml)
@@ -190,8 +192,8 @@ module Asciidoctor
190
192
  ["en"].each do |lang|
191
193
  at = { language: lang, format: "text/plain" }
192
194
  xml.title **attr_code(at) do |t|
193
- t << Utils::asciidoc_sub(node.attr("title") || node.attr("title-en") ||
194
- node.title)
195
+ t << (Utils::asciidoc_sub(node.attr("title") || node.attr("title-en")) ||
196
+ node.title)
195
197
  end
196
198
  end
197
199
  end
@@ -3,6 +3,7 @@ require "htmlentities"
3
3
  require "unicode2latex"
4
4
  require "mime/types"
5
5
  require "base64"
6
+ require 'English'
6
7
 
7
8
  module Asciidoctor
8
9
  module Standoc
@@ -106,23 +107,47 @@ module Asciidoctor
106
107
  gsub(/&quot;/, '"').gsub(/&#xa;/, "\n")
107
108
  end
108
109
 
110
+ def latex_run1(lxm_input, cmd)
111
+ IO.popen(cmd, "r+", external_encoding: "UTF-8") do |io|
112
+ io.write(lxm_input)
113
+ io.close_write
114
+ io.read
115
+ end
116
+ end
117
+
118
+ def latex_run(lxm_input)
119
+ results = nil
120
+ Metanorma::Standoc::Requirements[:latexml].cmd.each_with_index do |cmd, i|
121
+ warn "Retrying with #{cmd}" if i > 0
122
+ results = latex_run1(lxm_input, cmd)
123
+ if $CHILD_STATUS.to_i.zero?
124
+ warn "Success!" if i > 0
125
+ break
126
+ end
127
+ end
128
+ $CHILD_STATUS.to_i.zero? ? results : nil
129
+ end
130
+
131
+ def latex_parse(text)
132
+ lxm_input = Unicode2LaTeX.unicode2latex(HTMLEntities.new.decode(text))
133
+ results = latex_run(lxm_input)
134
+ results.nil? and
135
+ @log.add('Math', nil,
136
+ "latexmlmath failed to process equation:\n#{lxm_input}")
137
+ results
138
+ end
139
+
109
140
  def stem_parse(text, xml, style)
110
141
  if /&lt;([^:>&]+:)?math(\s+[^>&]+)?&gt; |
111
142
  <([^:>&]+:)?math(\s+[^>&]+)?>/x.match text
112
143
  math = xml_encode(text)
113
144
  xml.stem math, **{ type: "MathML" }
114
145
  elsif style == :latexmath
115
- latex_cmd = Metanorma::Standoc::Requirements[:latexml].cmd
116
- latexmlmath_input =
117
- Unicode2LaTeX::unicode2latex(HTMLEntities.new.decode(text)).
118
- gsub(/'/, '\\').gsub(/\n/, " ")
119
- latex = IO.popen(latex_cmd, "r+", external_encoding: "UTF-8") do |io|
120
- io.write(latexmlmath_input)
121
- io.close_write
122
- io.read
123
- end
146
+ latex = latex_parse(text) or return xml.stem **{ type: "MathML" }
124
147
  xml.stem **{ type: "MathML" } do |s|
125
- s << latex.sub(/<\?[^>]+>/, "")
148
+ math = Nokogiri::XML.fragment(latex.sub(/<\?[^>]+>/, "")).elements[0]
149
+ math.delete("alttext")
150
+ s.parent.children = math
126
151
  end
127
152
  else
128
153
  xml.stem text, **{ type: "AsciiMath" }
@@ -172,14 +197,14 @@ module Asciidoctor
172
197
  types = /^data:/.match(uri) ? datauri2mime(uri) : MIME::Types.type_for(uri)
173
198
  type = types.first.to_s
174
199
  uri = uri.sub(%r{^data:image/\*;}, "data:#{type};")
175
- attr_code(src: @datauriimage ? datauri(uri) : uri,
176
- id: Utils::anchor_or_uuid,
177
- mimetype: type,
178
- height: node.attr("height") || "auto",
179
- width: node.attr("width") || "auto" ,
180
- filename: node.attr("filename"),
181
- title: node.attr("titleattr"),
182
- alt: node.alt == node.attr("default-alt") ? nil : node.alt)
200
+ attr_code(src: uri, #@datauriimage ? datauri(uri) : uri,
201
+ id: Utils::anchor_or_uuid,
202
+ mimetype: type,
203
+ height: node.attr("height") || "auto",
204
+ width: node.attr("width") || "auto" ,
205
+ filename: node.attr("filename"),
206
+ title: node.attr("titleattr"),
207
+ alt: node.alt == node.attr("default-alt") ? nil : node.alt)
183
208
  end
184
209
 
185
210
  def inline_image(node)
@@ -129,6 +129,9 @@
129
129
  </choice>
130
130
  </attribute>
131
131
  </optional>
132
+ <attribute name="normative">
133
+ <data type="boolean"/>
134
+ </attribute>
132
135
  <optional>
133
136
  <ref name="section-title"/>
134
137
  </optional>
@@ -305,6 +308,21 @@
305
308
  </define>
306
309
  </include>
307
310
  <!-- end overrides -->
311
+ <define name="TextElement" combine="choice">
312
+ <ref name="concept"/>
313
+ </define>
314
+ <define name="concept">
315
+ <element name="concept">
316
+ <optional>
317
+ <attribute name="term"/>
318
+ </optional>
319
+ <choice>
320
+ <ref name="eref"/>
321
+ <ref name="xref"/>
322
+ <ref name="termref"/>
323
+ </choice>
324
+ </element>
325
+ </define>
308
326
  <define name="BasicBlock" combine="choice">
309
327
  <choice>
310
328
  <ref name="requirement"/>
@@ -911,7 +929,10 @@
911
929
  </define>
912
930
  <define name="origin">
913
931
  <element name="origin">
914
- <ref name="erefType"/>
932
+ <choice>
933
+ <ref name="erefType"/>
934
+ <ref name="termref"/>
935
+ </choice>
915
936
  </element>
916
937
  </define>
917
938
  <define name="modification">
@@ -919,6 +940,15 @@
919
940
  <ref name="paragraph"/>
920
941
  </element>
921
942
  </define>
943
+ <define name="termref">
944
+ <element name="termref">
945
+ <attribute name="base"/>
946
+ <attribute name="target"/>
947
+ <optional>
948
+ <text/>
949
+ </optional>
950
+ </element>
951
+ </define>
922
952
  <define name="structuredidentifier">
923
953
  <element name="structuredidentifier">
924
954
  <optional>
@@ -2,6 +2,7 @@ require "asciidoctor/extensions"
2
2
  require "fileutils"
3
3
  require "uuidtools"
4
4
  require_relative "./macros_plantuml.rb"
5
+ require_relative "./macros_yaml2text.rb"
5
6
 
6
7
  module Asciidoctor
7
8
  module Standoc
@@ -58,7 +59,7 @@ module Asciidoctor
58
59
  named :concept
59
60
  name_positional_attributes "id", "word", "term"
60
61
  #match %r{concept:(?<target>[^\[]*)\[(?<content>|.*?[^\\])\]$}
61
- match /\{\{(?<content>|.*?[^\\])\}\}$/
62
+ match /\{\{(?<content>|.*?[^\\])\}\}/
62
63
  using_format :short
63
64
 
64
65
  # deal with locality attrs and their disruption of positional attrs
@@ -0,0 +1,142 @@
1
+ require 'ostruct'
2
+
3
+ module Asciidoctor
4
+ module Standoc
5
+ class YamlBlockStruct < OpenStruct
6
+ def to_a
7
+ @table.to_h.keys
8
+ end
9
+
10
+ def values
11
+ @table.to_h.values
12
+ end
13
+
14
+ def each
15
+ return to_a.each unless block_given?
16
+
17
+ to_a.each do |key|
18
+ yield(key)
19
+ end
20
+ end
21
+ end
22
+
23
+ class YamlContextRenderer
24
+ attr_reader :context_object, :context_name
25
+
26
+ def initialize(context_object:, context_name:)
27
+ @context_object = context_object
28
+ @context_name = context_name
29
+ end
30
+
31
+ def respond_to_missing?(name)
32
+ respond_to?(name)
33
+ end
34
+
35
+ def method_missing(name, *_args)
36
+ return context_object if name.to_s == context_name
37
+
38
+ super
39
+ end
40
+
41
+ def render(template)
42
+ ERB.new(template).result(binding)
43
+ end
44
+ end
45
+
46
+ class Yaml2TextPreprocessor < Asciidoctor::Extensions::Preprocessor
47
+ BLOCK_START_REGEXP = /\{(.+?)\.\*,(.+),(.+)\}/.freeze
48
+ BLOCK_END_REGEXP = /\A\{[A-Z]+\}\z/.freeze
49
+ # search document for block `yaml2text`
50
+ # after that take template from block and read file into this template
51
+ # example:
52
+ # [yaml2text,foobar.yaml]
53
+ # ----
54
+ # === {item.name}
55
+ # {item.desc}
56
+ #
57
+ # {item.symbol}:: {item.symbol_def}
58
+ # ----
59
+ #
60
+ # with content of `foobar.yaml` file equal to:
61
+ # - name: spaghetti
62
+ # desc: wheat noodles of 9mm diameter
63
+ # symbol: SPAG
64
+ # symbol_def: the situation is message like spaghetti at a kid's meal
65
+ #
66
+ # will produce:
67
+ # === spaghetti
68
+ # wheat noodles of 9mm diameter
69
+ #
70
+ # SPAG:: the situation is message like spaghetti at a kid's meal
71
+ def process(document, reader)
72
+ input_lines = reader.readlines.to_enum
73
+ Reader.new(processed_lines(document, input_lines))
74
+ end
75
+
76
+ private
77
+
78
+ def processed_lines(document, input_lines)
79
+ result = []
80
+ loop do
81
+ line = input_lines.next
82
+ if yaml_block_match = line.match(/^\[yaml2text,(.+?),(.+?)\]/)
83
+ mark = input_lines.next
84
+ current_yaml_block = []
85
+ while (yaml_block_line = input_lines.next) != mark
86
+ current_yaml_block.push(yaml_block_line)
87
+ end
88
+ content = nested_open_struct_from_yaml(yaml_block_match[1], document)
89
+ result.push(*
90
+ parse_blocks_recursively(lines: current_yaml_block,
91
+ attributes: content,
92
+ context_name: yaml_block_match[2]))
93
+ else
94
+ result.push(line)
95
+ end
96
+ end
97
+ result
98
+ end
99
+
100
+ def nested_open_struct_from_yaml(file_path, document)
101
+ docfile_directory = File.dirname(document.attributes['docfile'] || '.')
102
+ yaml_file_path = document.path_resolver.system_path(file_path, docfile_directory)
103
+ content = YAML.safe_load(File.read(yaml_file_path))
104
+ # Load content as json, then parse with JSON as nested open_struct
105
+ JSON.parse(content.to_json, object_class: YamlBlockStruct)
106
+ end
107
+
108
+ def parse_blocks_recursively(lines:,
109
+ attributes:,
110
+ context_name:,
111
+ parent_context: nil)
112
+ lines = lines.to_enum
113
+ result = []
114
+ loop do
115
+ line = lines.next
116
+ if line.match(BLOCK_START_REGEXP)
117
+ line.gsub!(BLOCK_START_REGEXP, '<% \1.each.with_index do |\2,index| %>')
118
+ end
119
+
120
+ if line.match(BLOCK_END_REGEXP)
121
+ line.gsub!(BLOCK_END_REGEXP, '<% end %>')
122
+ end
123
+ line = line.gsub(/{(.+?[^}]*)}/, '<%= \1 %>').gsub(/[a-z\.]+\#/, 'index')
124
+ result.push(line)
125
+ end
126
+ result = parse_context_block(context_lines: result,
127
+ context_items: attributes,
128
+ context_name: context_name,
129
+ parent_context: parent_context)
130
+ result
131
+ end
132
+
133
+ def parse_context_block(context_lines:,
134
+ context_items:,
135
+ context_name:,
136
+ parent_context: nil)
137
+ renderer = YamlContextRenderer.new(context_object: context_items, context_name: context_name)
138
+ renderer.render(context_lines.join('\n')).split('\n')
139
+ end
140
+ end
141
+ end
142
+ end