asciidoctor-iso 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile.lock +12 -10
  5. data/README.adoc +113 -16
  6. data/bin/rspec +18 -0
  7. data/lib/asciidoctor/iso/base.rb +30 -28
  8. data/lib/asciidoctor/iso/blocks.rb +33 -33
  9. data/lib/asciidoctor/iso/cleanup.rb +79 -33
  10. data/lib/asciidoctor/iso/cleanup_block.rb +71 -18
  11. data/lib/asciidoctor/iso/cleanup_ref.rb +35 -30
  12. data/lib/asciidoctor/iso/converter.rb +0 -3
  13. data/lib/asciidoctor/iso/front.rb +29 -16
  14. data/lib/asciidoctor/iso/html/isodoc.css +34 -0
  15. data/lib/asciidoctor/iso/html/wordstyle.css +138 -6
  16. data/lib/asciidoctor/iso/inline.rb +10 -22
  17. data/lib/asciidoctor/iso/isodoc.rng +66 -16
  18. data/lib/asciidoctor/iso/isostandard.rng +129 -15
  19. data/lib/asciidoctor/iso/lists.rb +49 -42
  20. data/lib/asciidoctor/iso/macros.rb +12 -8
  21. data/lib/asciidoctor/iso/section.rb +53 -37
  22. data/lib/asciidoctor/iso/table.rb +9 -1
  23. data/lib/asciidoctor/iso/utils.rb +18 -13
  24. data/lib/asciidoctor/iso/validate.rb +100 -24
  25. data/lib/asciidoctor/iso/validate_requirements.rb +106 -0
  26. data/lib/asciidoctor/iso/validate_section.rb +85 -65
  27. data/lib/asciidoctor/iso/validate_style.rb +68 -115
  28. data/lib/asciidoctor/iso/version.rb +1 -1
  29. data/spec/asciidoctor-iso/base_spec.rb +193 -0
  30. data/spec/asciidoctor-iso/blocks_spec.rb +426 -0
  31. data/spec/asciidoctor-iso/cleanup_spec.rb +687 -0
  32. data/spec/asciidoctor-iso/inline_spec.rb +159 -0
  33. data/spec/asciidoctor-iso/lists_spec.rb +189 -0
  34. data/spec/asciidoctor-iso/macros_spec.rb +20 -0
  35. data/spec/asciidoctor-iso/refs_spec.rb +194 -0
  36. data/spec/asciidoctor-iso/section_spec.rb +301 -0
  37. data/spec/asciidoctor-iso/table_spec.rb +307 -0
  38. data/spec/asciidoctor-iso/validate_spec.rb +749 -0
  39. data/spec/examples/english.yaml +69 -0
  40. data/spec/examples/rice.adoc +30 -28
  41. data/spec/examples/rice.doc +3035 -2865
  42. data/spec/examples/rice.html +281 -234
  43. data/spec/examples/rice.preview.html +30 -20
  44. data/spec/examples/rice.xml +250 -282
  45. data/spec/spec_helper.rb +87 -0
  46. metadata +17 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 81628e6445230282af27ae6ee08a5b1662850618
4
- data.tar.gz: 5ec3c5c6573556c7cf6cfb5ebcaa827d728db794
3
+ metadata.gz: 1214d438d3c226629a8fff96a4c26c21f20d5bba
4
+ data.tar.gz: b57913fafdf74be666b2bc5c4a865fb6ac0fc9bc
5
5
  SHA512:
6
- metadata.gz: e79a67ec52c08f26beeda9eca31e38556832ed8eaaa000b42e05fbafc24a59cc96166b81d32b43264432a11e1087862a14fe30dc3ae241a60012bb9ad6f9137e
7
- data.tar.gz: 7ee22285f177d6c466cbf7870a4f9e001eda32485a517625afb3df7c37d5f83e51cef712b77569f1bca21323e60a8017a7c02101c68080368956af4f2f7c644f
6
+ metadata.gz: c6fe48101588ebe48a5f8f5d5b6b9486d3cbc8f1616b49259773d17bee1179a9cb0362a7ca4920803f0c22dc9065955e964fc24a6e93e622a64acb064a6074e7
7
+ data.tar.gz: ef8a83afdfa714b47047e0b12b1aa6590308a6ca30c7d723960cb002708c33382e9dd53e37ecddf4ab0f52eb633e3626497fce6a8b6dd168db4ef682fe4a8ef3
@@ -1,2 +1,4 @@
1
1
  rfc2629*.* linguist-vendored
2
2
  mathml2omml*.* linguist-vendored
3
+ *.html linguist-vendored
4
+ *.css linguist-vendored
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.3
5
+ before_install: gem install bundler -v 1.16.1
@@ -1,8 +1,8 @@
1
1
  GIT
2
2
  remote: https://github.com/riboseinc/html2doc.git
3
- revision: 8a131b82eeecd0c27e4bde873f2ba16d425754d6
3
+ revision: 3eb26a6807a55abc4b2e35570601618568f12e07
4
4
  specs:
5
- html2doc (0.6.0)
5
+ html2doc (0.6.2)
6
6
  asciimath
7
7
  htmlentities (~> 4.3.4)
8
8
  image_size
@@ -14,13 +14,14 @@ GIT
14
14
 
15
15
  GIT
16
16
  remote: https://github.com/riboseinc/isodoc.git
17
- revision: 0818963410de3aeb880f263678c47dcadcfe9b50
17
+ revision: 4fe5f001024f5c1c659c68b6c61a6e54bcc8e444
18
18
  specs:
19
- isodoc (0.4.0)
19
+ isodoc (0.5.0)
20
20
  asciimath
21
21
  html2doc
22
22
  htmlentities (~> 4.3.4)
23
23
  image_size
24
+ liquid
24
25
  mime-types
25
26
  nokogiri (= 1.8.1)
26
27
  ruby-xslt
@@ -52,7 +53,7 @@ GEM
52
53
  docile (1.1.5)
53
54
  equivalent-xml (0.6.0)
54
55
  nokogiri (>= 1.4.3)
55
- ffi (1.9.21)
56
+ ffi (1.9.23)
56
57
  formatador (0.2.5)
57
58
  guard (2.14.2)
58
59
  formatador (>= 0.2.4)
@@ -71,6 +72,7 @@ GEM
71
72
  htmlentities (4.3.4)
72
73
  image_size (1.5.0)
73
74
  json (2.1.0)
75
+ liquid (4.0.0)
74
76
  listen (3.1.5)
75
77
  rb-fsevent (~> 0.9, >= 0.9.4)
76
78
  rb-inotify (~> 0.9, >= 0.9.7)
@@ -89,15 +91,15 @@ GEM
89
91
  shellany (~> 0.0)
90
92
  optout (0.0.2)
91
93
  parallel (1.12.1)
92
- parser (2.4.0.2)
93
- ast (~> 2.3)
94
+ parser (2.5.0.3)
95
+ ast (~> 2.4.0)
94
96
  powerpack (0.1.1)
95
97
  pry (0.11.3)
96
98
  coderay (~> 1.1.0)
97
99
  method_source (~> 0.9.0)
98
100
  rainbow (3.0.0)
99
101
  rake (12.3.0)
100
- rb-fsevent (0.10.2)
102
+ rb-fsevent (0.10.3)
101
103
  rb-inotify (0.9.10)
102
104
  ffi (>= 0.5.0, < 2)
103
105
  rspec (3.7.0)
@@ -113,9 +115,9 @@ GEM
113
115
  diff-lcs (>= 1.2.0, < 2.0)
114
116
  rspec-support (~> 3.7.0)
115
117
  rspec-support (3.7.1)
116
- rubocop (0.52.1)
118
+ rubocop (0.53.0)
117
119
  parallel (~> 1.10)
118
- parser (>= 2.4.0.2, < 3.0)
120
+ parser (>= 2.5)
119
121
  powerpack (~> 0.1)
120
122
  rainbow (>= 2.2.2, < 4.0)
121
123
  ruby-progressbar (~> 1.7)
@@ -98,13 +98,17 @@ The document model ("IsoDoc") used in document generation
98
98
  intends to introduce rigour into the ISO
99
99
  standards authoring process; the existing
100
100
  https://www.iso.org/iso-templates.html[Microsoft Word template from ISO]
101
- do not support such rigour down to the element level.
101
+ do not support such rigour down to the element level. It also introduces
102
+ flexibility by decoupling the document structure from its presentation.
102
103
 
103
104
  The ISO International Standard format is prescribed in
104
105
  http://www.iec.ch/members_experts/refdocs/iec/isoiecdir-2%7Bed7.0%7Den.pdf[ISO/IEC DIR 2 "Principles and rules for the structure and drafting of ISO and IEC documents"],
105
106
  to a level amenable to an explicit document model. A formal document
106
107
  model would allow checking for consistency in format and content, and expedite
107
- authoring and quality control of ISO standards. Authoring standards through a more abstract formal model also permit enhanced functionality such as cross reference link checking and auto numbering of sections, figures, tables and formulas.
108
+ authoring and quality control of ISO standards. Authoring standards through a
109
+ more abstract formal model also permit enhanced functionality such as
110
+ cross-reference link checking and auto-numbering of sections, figures, tables and formulas.
111
+ Outputting a document in different languages also becomes straightforward.
108
112
 
109
113
  The document model for ISO Standards specifically is derived from a more general
110
114
  https://github.com/riboseinc/isodoc-models[StandardDocument model]. Other
@@ -112,10 +116,7 @@ ISO-like standards can also be derived from this more general model;
112
116
  CSD (https://github.com/riboseinc/csd, https://github.com/riboseinc/asciidoctor-csd)
113
117
  is one such instance.
114
118
 
115
- The document model for ISO Standards is still under development, but it already contains
116
- all the markup needed to render the
117
- https://www.iso.org/publication/PUB100407.html["Rice document"], the ISO's
118
- model document of an international standard, and all the structures described
119
+ The document model for ISO Standards contains all the structures described
119
120
  in ISO/IEC DIR 2. It is expressed as a
120
121
  link:lib/asciidoctor/iso/isostandard.rnc[Relax NG Compact schema]; actual
121
122
  validation occurs against its link:lib/asciidoctor/iso/isostandard.rng[full Relax
@@ -153,7 +154,31 @@ http://discuss.asciidoctor.org/footnotes-with-paragraph-breaks-td4130.html[by de
153
154
  XML to break up paragraphs within a footnote.)
154
155
 
155
156
  [[model_additions]]
156
- === Asciidoctor model additions
157
+ == Asciidoctor model additions
158
+
159
+ === Section titles
160
+ ISO has special section types: "Scope", "Normative References", "Terms and Definitions", "Symbols and Abbreviated Terms", "Bibliography". By default, these are identified in Asciidoc by using those titles. The gem allows you to override the title by using a `heading` attribute on the node, so that the actual title in your Asciidoc can be something different; that is useful, for example, if you are translating the document into different languages. So:
161
+
162
+ [source,asciidoctor]
163
+ --
164
+ [heading=scope]
165
+ == 范围
166
+ --
167
+
168
+ Note that both the XML population, and the isodoc gem will overwrite any supplied title. If you are translating ISO documents into other languages, you will still need access to versions of the asciidoctor-iso and isodoc gems in those languages.
169
+
170
+ === Obligation
171
+ The obligation of sections (whether they are normative or informative) is indicated
172
+ with the attribute "obligation". For most sections, this is fixed; for annexes and clauses, the
173
+ default value of the obligation is "normative", and users need to set the obligation
174
+ to "informative" as a section attribute.
175
+
176
+ [source,asciidoctor]
177
+ --
178
+ [[AnnexA]]
179
+ [appendix,obligation=informative]
180
+ == Determination of defects
181
+ --
157
182
 
158
183
  === Term markup
159
184
 
@@ -176,7 +201,30 @@ domain:[rice]
176
201
  _paddy_ (<<paddy>>) from which the husk only has been removed
177
202
  --
178
203
 
179
- ==== Paragraph alignment
204
+ === Terms and Definitions markup
205
+
206
+ If the Terms and Definitions of a standard are partly or fully sourced from
207
+ another standard, that standard is cited in a `source` attribute to the section,
208
+ which is set to the reference anchor of the standard (given under the Normative
209
+ Referencecs)..
210
+ The boilerplate of the Terms and Definitions section is adjusted accordingly.
211
+
212
+ [source,asciidoctor]
213
+ --
214
+ [source=ISO712]
215
+ == Terms and Definitions
216
+ --
217
+
218
+ Multiple sources are allowed, and need to be quoted and comma-delimited:
219
+
220
+ [source,asciidoctor]
221
+ --
222
+ [source="ISO712,ISO24333"]
223
+ == Terms and Definitions
224
+ --
225
+
226
+
227
+ === Paragraph alignment
180
228
 
181
229
  Alignment is defined as an attribute for paragraphs:
182
230
 
@@ -195,7 +243,7 @@ This paragraph is aligned center
195
243
  This paragraph is justified, which is the default
196
244
  --
197
245
 
198
- ==== Reviewer notes
246
+ === Reviewer notes
199
247
 
200
248
  Reviewer notes are encoded as sidebars, and can be separated at a distance from the
201
249
  text they are annotating; the text they are annotating is indicated through anchors.
@@ -236,7 +284,7 @@ Profile?!
236
284
  ****
237
285
  --
238
286
 
239
- ==== Strikethrough and Small Caps
287
+ === Strikethrough and Small Caps
240
288
 
241
289
  The following formatting macros are used for strikethrough and small caps text:
242
290
 
@@ -246,7 +294,7 @@ The following formatting macros are used for strikethrough and small caps text:
246
294
  [smallcap]#small caps text#
247
295
  --
248
296
 
249
- ==== Count of table header and footer rows
297
+ === Count of table header and footer rows
250
298
 
251
299
  In Asciidoc, a table can have at most one header row or footer row. In ISO,
252
300
  a nominal single header row is routinely broken up into multiple rows in order
@@ -266,7 +314,7 @@ stem:[w_max]
266
314
  |===
267
315
  --
268
316
 
269
- ==== Inline clause numbers
317
+ === Inline clause numbers
270
318
 
271
319
  For some clauses (notably test methods), the clause heading appears inline with the clause, instead of being separated on a different line. This is indicated in Asciidoc by the option
272
320
  attribute `inline-header`:
@@ -280,7 +328,7 @@ attribute `inline-header`:
280
328
  consisting of a conical sample divider
281
329
  --
282
330
 
283
- ==== Bibliographic details
331
+ === Bibliographic details
284
332
 
285
333
  Citations can include details of where in the document the citation is located; these
286
334
  are entered by suffixing the type of locality, followed by the reference. Multiple
@@ -297,7 +345,40 @@ example:
297
345
  The references cannot contain spaces. Any text following the sequence of localities
298
346
  will be displayed instead of the localities.
299
347
 
300
- ==== Block Quotes
348
+ === Additional warning types
349
+
350
+ Asciidoctor natively supports the ISO admonitions "Caution", "Warning", and "Important"
351
+ through its admonition syntax:
352
+
353
+ [source,asciidoctor]
354
+ --
355
+ CAUTION: This is a single-block caution
356
+
357
+ [WARNING]
358
+ ====
359
+ This is a
360
+
361
+ multiple-block warning
362
+ ====
363
+ --
364
+
365
+ If the admonitions "Danger" and "Safety Precaution" are needed, they should be indicated
366
+ through a `type` attribute, which will override the admonition type appearing in the Asciidoc:
367
+
368
+ [source,asciidoctor]
369
+ --
370
+ [type=Danger]
371
+ CAUTION: This is a single-block caution
372
+
373
+ [WARNING,type=Safety Precaution]
374
+ ====
375
+ This is a
376
+
377
+ multiple-block warning
378
+ ====
379
+ --
380
+
381
+ === Block Quotes
301
382
 
302
383
  As in normal Asciidoctor, block quotes are preceded with an author and a citation;
303
384
  but the citation is expected to be in the same format as all other citations,
@@ -417,11 +498,24 @@ render them.
417
498
  The gem relies on Asciidoctor document attributes to provide necessary
418
499
  metadata about the document. These include:
419
500
 
501
+ `:nodoc:`:: Do not generate Word and HTML output, only generate XML output.
502
+ Can be used as a command-line option (like all other document attributes):
503
+ `asciidoctor -a nodoc -b iso -r "asciidoctor-iso" a.adoc`
504
+
505
+ `:novalid:`:: Suppress validation.
506
+
507
+ `:i18nyaml:`:: Name of YAML file of internationalisation text, to use instead
508
+ of the built-in English, French or Chinese text used to label parts of the document
509
+ (e.g. "Table", "Foreword", boilerplate text for Normative References, etc.)
510
+ Use if you wish to output an ISO standard in a language other than those three.
511
+ A sample YAML file for English, with "Foreword" replaced with "Frontispiece",
512
+ is available at link:spec/examples/english.yaml[].
513
+
420
514
  `:docnumber:`:: The ISO document number (mandatory)
421
515
 
422
516
  `:tc-docnumber:`:: The document number assigned by the Technical committee
423
517
 
424
- `:partnumber:`:: The ISO document part number
518
+ `:partnumber:`:: The ISO document part number. (This can be "part-subpart" if this is an IEC document.)
425
519
 
426
520
  `:edition:`:: The document edition
427
521
 
@@ -492,6 +586,9 @@ not supplied. Example values: `JWG`, `JAG`, `AG` (advisory group), `AHG`, `SWG`,
492
586
 
493
587
  `:language:` :: The language of the document (`en` or `fr`) (mandatory)
494
588
 
589
+ `:script:` :: The script of the document (defaults to `Latn`). Must be supplied as
590
+ `Hans` for Simplified Chinese.
591
+
495
592
  `:publisher:`:: The standards agency publishing the standard; can be multiple
496
593
  (comma-delimited). Defaults to `ISO`.
497
594
 
@@ -542,4 +639,4 @@ model document of an international standard. This repository includes:
542
639
  * the link:spec/examples/rice.doc[Word .doc rendering of the Rice document].
543
640
  * the link:spec/examples/rice.html[HTML rendering of the Rice document].
544
641
 
545
-
642
+ See also `link:spec/asciidoctor-iso[]` for individual features.
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require "pathname"
10
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path(
11
+ "../../Gemfile", Pathname.new(__FILE__).realpath
12
+ )
13
+
14
+ require "rubygems"
15
+ require "bundler/setup"
16
+
17
+ load Gem.bin_path("rspec-core", "rspec")
18
+
@@ -23,7 +23,7 @@ module Asciidoctor
23
23
  def skip(node, name = nil)
24
24
  name = name || node.node_name
25
25
  w = "converter missing for #{name} node in ISO backend"
26
- warning(node, w, nil)
26
+ Utils::warning(node, w, nil)
27
27
  nil
28
28
  end
29
29
 
@@ -31,7 +31,7 @@ module Asciidoctor
31
31
  File.join(File.dirname(__FILE__), File.join("html", file))
32
32
  end
33
33
 
34
- def doc_converter
34
+ def doc_converter(node)
35
35
  IsoDoc::Convert.new(
36
36
  htmlstylesheet: html_doc_path("htmlstyle.css"),
37
37
  wordstylesheet: html_doc_path("wordstyle.css"),
@@ -41,41 +41,45 @@ module Asciidoctor
41
41
  wordcoverpage: html_doc_path("word_iso_titlepage.html"),
42
42
  htmlintropage: html_doc_path("html_iso_intro.html"),
43
43
  wordintropage: html_doc_path("word_iso_intro.html"),
44
+ i18nyaml: node.attr("i18nyaml"),
45
+ ulstyle: "l3",
46
+ olstyle: "l2",
44
47
  )
45
48
  end
46
49
 
47
- def init
50
+ def init(node)
48
51
  @fn_number = 0
49
52
  @draft = false
50
53
  @refids = Set.new
51
54
  @anchors = {}
55
+ @draft = node.attributes.has_key?("draft")
56
+ @novalid = node.attr("novalid")
52
57
  end
53
58
 
54
59
  def document(node)
55
- init
60
+ init(node)
56
61
  ret1 = makexml(node)
57
62
  ret = ret1.to_xml(indent: 2)
58
63
  filename = node.attr("docfile").gsub(/\.adoc/, ".xml").
59
- gsub(%r{^.*/}, '')
60
- File.open("#{filename}", "w") { |f| f.write(ret) }
61
- doc_converter.convert filename
64
+ gsub(%r{^.*/}, "")
65
+ File.open(filename, "w") { |f| f.write(ret) }
66
+ doc_converter(node).convert filename unless node.attr("nodoc")
62
67
  ret
63
68
  end
64
69
 
65
70
  def makexml(node)
66
71
  result = ["<?xml version='1.0' encoding='UTF-8'?>\n<iso-standard>"]
67
- @draft = node.attributes.has_key?("draft")
68
72
  result << noko { |ixml| front node, ixml }
69
73
  result << noko { |ixml| middle node, ixml }
70
74
  result << "</iso-standard>"
71
75
  result = textcleanup(result.flatten * "\n")
72
76
  ret1 = cleanup(Nokogiri::XML(result))
73
77
  ret1.root.add_namespace(nil, "http://riboseinc.com/isoxml")
74
- validate(ret1)
78
+ validate(ret1) unless @novalid
75
79
  ret1
76
80
  end
77
81
 
78
- def is_draft
82
+ def draft?
79
83
  @draft
80
84
  end
81
85
 
@@ -92,42 +96,41 @@ module Asciidoctor
92
96
  end
93
97
  end
94
98
 
99
+ def term_source_attr(seen_xref)
100
+ { bibitemid: seen_xref.children[0]["target"],
101
+ format: seen_xref.children[0]["format"],
102
+ type: "inline" }
103
+ end
104
+
95
105
  def add_term_source(xml_t, seen_xref, m)
96
- attr = { bibitemid: seen_xref.children[0]["target"],
97
- format: seen_xref.children[0]["format"],
98
- type: "inline" }
99
- xml_t.origin seen_xref.children[0].content, **attr_code(attr)
100
- m[:section] && xml_t.isosection do |s|
101
- s.reference m[:section].gsub(/ /, "")
102
- end
103
- m[:text] && xml_t.modification do |mod|
104
- mod.p { |p| p << m[:text].sub(/^\s+/, "") }
106
+ xml_t.origin seen_xref.children[0].content,
107
+ **attr_code(term_source_attr(seen_xref))
108
+ m[:text] && xml_t.modification do |mod|
109
+ mod.p { |p| p << m[:text].sub(/^\s+/, "") }
105
110
  end
106
111
  end
107
112
 
108
113
  TERM_REFERENCE_RE_STR = <<~REGEXP.freeze
109
- ^(?<xref><xref[^>]+>)
110
- (,\s(?<section>[^, ]+))?
114
+ ^(?<xref><xref[^>]+>([^<]*</xref>)?)
111
115
  (,\s(?<text>.*))?
112
- $
116
+ $
113
117
  REGEXP
114
118
  TERM_REFERENCE_RE =
115
119
  Regexp.new(TERM_REFERENCE_RE_STR.gsub(/\s/, "").gsub(/_/, "\\s"),
116
120
  Regexp::IGNORECASE | Regexp::MULTILINE)
117
121
 
118
-
119
- def extract_termsource_refs(text)
122
+ def extract_termsource_refs(text, node)
120
123
  matched = TERM_REFERENCE_RE.match text
121
124
  if matched.nil?
122
- warning(node, "term reference not in expected format", text)
125
+ Utils::warning(node, "term reference not in expected format", text)
123
126
  end
124
127
  matched
125
128
  end
126
129
 
127
130
  def termsource(node)
128
- matched = extract_termsource_refs(node.content) or return
131
+ matched = extract_termsource_refs(node.content, node) || return
129
132
  noko do |xml|
130
- attrs = { status: matched[:text] ? "identical" : "modified" }
133
+ attrs = { status: matched[:text] ? "modified" : "identical" }
131
134
  xml.termsource **attrs do |xml_t|
132
135
  seen_xref = Nokogiri::XML.fragment(matched[:xref])
133
136
  add_term_source(xml_t, seen_xref, matched)
@@ -135,7 +138,6 @@ module Asciidoctor
135
138
  end
136
139
  end.join("\n")
137
140
  end
138
-
139
141
  end
140
142
  end
141
143
  end