metanorma-standoc 1.9.0 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +3 -13
  3. data/.hound.yml +3 -1
  4. data/.rubocop.yml +4 -8
  5. data/lib/asciidoctor/standoc/base.rb +31 -35
  6. data/lib/asciidoctor/standoc/biblio.rng +1 -0
  7. data/lib/asciidoctor/standoc/blocks.rb +25 -9
  8. data/lib/asciidoctor/standoc/blocks_notes.rb +41 -24
  9. data/lib/asciidoctor/standoc/cleanup.rb +59 -84
  10. data/lib/asciidoctor/standoc/cleanup_block.rb +63 -85
  11. data/lib/asciidoctor/standoc/cleanup_boilerplate.rb +28 -15
  12. data/lib/asciidoctor/standoc/cleanup_footnotes.rb +1 -0
  13. data/lib/asciidoctor/standoc/cleanup_image.rb +71 -0
  14. data/lib/asciidoctor/standoc/cleanup_inline.rb +117 -77
  15. data/lib/asciidoctor/standoc/cleanup_maths.rb +36 -27
  16. data/lib/asciidoctor/standoc/cleanup_ref.rb +31 -15
  17. data/lib/asciidoctor/standoc/cleanup_ref_dl.rb +1 -1
  18. data/lib/asciidoctor/standoc/cleanup_reqt.rb +47 -0
  19. data/lib/asciidoctor/standoc/cleanup_section.rb +77 -135
  20. data/lib/asciidoctor/standoc/cleanup_section_names.rb +75 -0
  21. data/lib/asciidoctor/standoc/cleanup_terms.rb +19 -18
  22. data/lib/asciidoctor/standoc/converter.rb +7 -2
  23. data/lib/asciidoctor/standoc/datamodel/plantuml_renderer.rb +67 -66
  24. data/lib/asciidoctor/standoc/front.rb +35 -18
  25. data/lib/asciidoctor/standoc/front_contributor.rb +70 -45
  26. data/lib/asciidoctor/standoc/inline.rb +45 -34
  27. data/lib/asciidoctor/standoc/isodoc.rng +209 -4
  28. data/lib/asciidoctor/standoc/lists.rb +4 -2
  29. data/lib/asciidoctor/standoc/macros.rb +11 -11
  30. data/lib/asciidoctor/standoc/macros_form.rb +63 -0
  31. data/lib/asciidoctor/standoc/macros_plantuml.rb +19 -21
  32. data/lib/asciidoctor/standoc/macros_terms.rb +33 -23
  33. data/lib/asciidoctor/standoc/ref.rb +87 -112
  34. data/lib/asciidoctor/standoc/ref_date_id.rb +62 -0
  35. data/lib/asciidoctor/standoc/ref_sect.rb +20 -17
  36. data/lib/asciidoctor/standoc/section.rb +3 -1
  37. data/lib/asciidoctor/standoc/term_lookup_cleanup.rb +40 -27
  38. data/lib/asciidoctor/standoc/terms.rb +25 -18
  39. data/lib/asciidoctor/standoc/utils.rb +35 -9
  40. data/lib/asciidoctor/standoc/validate.rb +48 -33
  41. data/lib/metanorma-standoc.rb +0 -1
  42. data/lib/metanorma/standoc/version.rb +1 -1
  43. data/metanorma-standoc.gemspec +4 -4
  44. data/spec/asciidoctor/base_spec.rb +701 -508
  45. data/spec/asciidoctor/blocks_spec.rb +831 -738
  46. data/spec/asciidoctor/cleanup_sections_spec.rb +51 -14
  47. data/spec/asciidoctor/cleanup_spec.rb +889 -682
  48. data/spec/asciidoctor/inline_spec.rb +62 -14
  49. data/spec/asciidoctor/isobib_cache_spec.rb +404 -358
  50. data/spec/asciidoctor/lists_spec.rb +149 -137
  51. data/spec/asciidoctor/macros_plantuml_spec.rb +8 -8
  52. data/spec/asciidoctor/macros_spec.rb +923 -503
  53. data/spec/asciidoctor/macros_yaml2text_spec.rb +1 -1
  54. data/spec/asciidoctor/refs_dl_spec.rb +4 -4
  55. data/spec/asciidoctor/refs_spec.rb +1528 -1533
  56. data/spec/asciidoctor/section_spec.rb +405 -299
  57. data/spec/asciidoctor/table_spec.rb +6 -6
  58. data/spec/asciidoctor/validate_spec.rb +342 -304
  59. data/spec/spec_helper.rb +13 -9
  60. data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec.yml +54 -54
  61. data/spec/vcr_cassettes/isobib_get_123.yml +13 -13
  62. data/spec/vcr_cassettes/isobib_get_123_1.yml +25 -25
  63. data/spec/vcr_cassettes/isobib_get_123_1_fr.yml +37 -37
  64. data/spec/vcr_cassettes/isobib_get_123_2001.yml +12 -12
  65. data/spec/vcr_cassettes/isobib_get_124.yml +13 -13
  66. data/spec/vcr_cassettes/rfcbib_get_rfc8341.yml +14 -14
  67. data/spec/vcr_cassettes/separates_iev_citations_by_top_level_clause.yml +46 -46
  68. metadata +16 -15
  69. data/lib/liquid/custom_blocks/key_iterator.rb +0 -21
  70. data/lib/liquid/custom_blocks/with_json_nested_context.rb +0 -18
  71. data/lib/liquid/custom_blocks/with_yaml_nested_context.rb +0 -19
  72. data/lib/liquid/custom_filters/values.rb +0 -7
@@ -45,6 +45,11 @@
45
45
  <optional>
46
46
  <attribute name="alt"/>
47
47
  </optional>
48
+ <optional>
49
+ <attribute name="updatetype">
50
+ <data type="boolean"/>
51
+ </attribute>
52
+ </optional>
48
53
  <text/>
49
54
  </element>
50
55
  </define>
@@ -543,6 +548,9 @@
543
548
  </define>
544
549
  <define name="BibDataExtensionType">
545
550
  <ref name="doctype"/>
551
+ <optional>
552
+ <ref name="docsubtype"/>
553
+ </optional>
546
554
  <optional>
547
555
  <ref name="editorialgroup"/>
548
556
  </optional>
@@ -876,8 +884,28 @@
876
884
  </zeroOrMore>
877
885
  </element>
878
886
  </define>
887
+ <define name="pagebreak">
888
+ <element name="pagebreak">
889
+ <optional>
890
+ <attribute name="orientation">
891
+ <choice>
892
+ <value>landscape</value>
893
+ <value>portrait</value>
894
+ </choice>
895
+ </attribute>
896
+ </optional>
897
+ </element>
898
+ </define>
879
899
  </include>
880
900
  <!-- end overrides -->
901
+ <define name="docsubtype">
902
+ <element name="docsubtype">
903
+ <ref name="DocumentSubtype"/>
904
+ </element>
905
+ </define>
906
+ <define name="DocumentSubtype">
907
+ <text/>
908
+ </define>
881
909
  <define name="colgroup">
882
910
  <element name="colgroup">
883
911
  <oneOrMore>
@@ -927,7 +955,24 @@
927
955
  <define name="concept">
928
956
  <element name="concept">
929
957
  <optional>
930
- <attribute name="term"/>
958
+ <element name="refterm">
959
+ <zeroOrMore>
960
+ <choice>
961
+ <ref name="PureTextElement"/>
962
+ <ref name="stem"/>
963
+ </choice>
964
+ </zeroOrMore>
965
+ </element>
966
+ </optional>
967
+ <optional>
968
+ <element name="renderterm">
969
+ <zeroOrMore>
970
+ <choice>
971
+ <ref name="PureTextElement"/>
972
+ <ref name="stem"/>
973
+ </choice>
974
+ </zeroOrMore>
975
+ </element>
931
976
  </optional>
932
977
  <choice>
933
978
  <ref name="eref"/>
@@ -943,8 +988,170 @@
943
988
  <ref name="permission"/>
944
989
  <ref name="imagemap"/>
945
990
  <ref name="svgmap"/>
991
+ <ref name="inputform"/>
992
+ </choice>
993
+ </define>
994
+ <define name="inputform">
995
+ <element name="form">
996
+ <attribute name="id">
997
+ <data type="ID"/>
998
+ </attribute>
999
+ <attribute name="name"/>
1000
+ <attribute name="action"/>
1001
+ <zeroOrMore>
1002
+ <choice>
1003
+ <ref name="TextElement"/>
1004
+ <ref name="FormInput"/>
1005
+ </choice>
1006
+ </zeroOrMore>
1007
+ </element>
1008
+ </define>
1009
+ <define name="FormInput">
1010
+ <choice>
1011
+ <ref name="input"/>
1012
+ <ref name="formlabel"/>
1013
+ <ref name="select"/>
1014
+ <ref name="textarea"/>
946
1015
  </choice>
947
1016
  </define>
1017
+ <define name="InputType">
1018
+ <choice>
1019
+ <value>button</value>
1020
+ <value>checkbox</value>
1021
+ <value>date</value>
1022
+ <value>file</value>
1023
+ <value>password</value>
1024
+ <value>radio</value>
1025
+ <value>submit</value>
1026
+ <value>text</value>
1027
+ </choice>
1028
+ </define>
1029
+ <define name="input">
1030
+ <element name="input">
1031
+ <attribute name="type">
1032
+ <ref name="InputType"/>
1033
+ </attribute>
1034
+ <optional>
1035
+ <attribute name="checked">
1036
+ <data type="boolean"/>
1037
+ </attribute>
1038
+ </optional>
1039
+ <optional>
1040
+ <attribute name="disabled">
1041
+ <data type="boolean"/>
1042
+ </attribute>
1043
+ </optional>
1044
+ <optional>
1045
+ <attribute name="readonly">
1046
+ <data type="boolean"/>
1047
+ </attribute>
1048
+ </optional>
1049
+ <optional>
1050
+ <attribute name="maxlength">
1051
+ <data type="int"/>
1052
+ </attribute>
1053
+ </optional>
1054
+ <optional>
1055
+ <attribute name="minlength">
1056
+ <data type="int"/>
1057
+ </attribute>
1058
+ </optional>
1059
+ <optional>
1060
+ <attribute name="name"/>
1061
+ </optional>
1062
+ <optional>
1063
+ <attribute name="value"/>
1064
+ </optional>
1065
+ <optional>
1066
+ <attribute name="id">
1067
+ <data type="ID"/>
1068
+ </attribute>
1069
+ </optional>
1070
+ </element>
1071
+ </define>
1072
+ <define name="formlabel">
1073
+ <element name="label">
1074
+ <attribute name="for">
1075
+ <data type="IDREF"/>
1076
+ </attribute>
1077
+ <zeroOrMore>
1078
+ <ref name="PureTextElement"/>
1079
+ </zeroOrMore>
1080
+ </element>
1081
+ </define>
1082
+ <define name="select">
1083
+ <element name="select">
1084
+ <optional>
1085
+ <attribute name="name"/>
1086
+ </optional>
1087
+ <optional>
1088
+ <attribute name="value"/>
1089
+ </optional>
1090
+ <optional>
1091
+ <attribute name="id">
1092
+ <data type="ID"/>
1093
+ </attribute>
1094
+ </optional>
1095
+ <optional>
1096
+ <attribute name="disabled">
1097
+ <data type="boolean"/>
1098
+ </attribute>
1099
+ </optional>
1100
+ <optional>
1101
+ <attribute name="multiple">
1102
+ <data type="boolean"/>
1103
+ </attribute>
1104
+ </optional>
1105
+ <optional>
1106
+ <attribute name="size">
1107
+ <data type="int"/>
1108
+ </attribute>
1109
+ </optional>
1110
+ <oneOrMore>
1111
+ <ref name="option"/>
1112
+ </oneOrMore>
1113
+ </element>
1114
+ </define>
1115
+ <define name="option">
1116
+ <element name="option">
1117
+ <optional>
1118
+ <attribute name="disabled">
1119
+ <data type="boolean"/>
1120
+ </attribute>
1121
+ </optional>
1122
+ <optional>
1123
+ <attribute name="value"/>
1124
+ </optional>
1125
+ <zeroOrMore>
1126
+ <ref name="PureTextElement"/>
1127
+ </zeroOrMore>
1128
+ </element>
1129
+ </define>
1130
+ <define name="textarea">
1131
+ <element name="textarea">
1132
+ <optional>
1133
+ <attribute name="name"/>
1134
+ </optional>
1135
+ <optional>
1136
+ <attribute name="value"/>
1137
+ </optional>
1138
+ <optional>
1139
+ <attribute name="id">
1140
+ <data type="ID"/>
1141
+ </attribute>
1142
+ </optional>
1143
+ <optional>
1144
+ <attribute name="rows">
1145
+ <data type="int"/>
1146
+ </attribute>
1147
+ </optional>
1148
+ <optional>
1149
+ <attribute name="cols">
1150
+ <data type="int"/>
1151
+ </attribute>
1152
+ </optional>
1153
+ </element>
1154
+ </define>
948
1155
  <define name="bibliography">
949
1156
  <element name="bibliography">
950
1157
  <oneOrMore>
@@ -1017,9 +1224,7 @@
1017
1224
  </define>
1018
1225
  <define name="IsoWorkgroup">
1019
1226
  <optional>
1020
- <attribute name="number">
1021
- <data type="int"/>
1022
- </attribute>
1227
+ <attribute name="number"/>
1023
1228
  </optional>
1024
1229
  <optional>
1025
1230
  <attribute name="type"/>
@@ -28,14 +28,16 @@ module Asciidoctor
28
28
  end
29
29
 
30
30
  def ul_li_attrs(node)
31
+ c = node.attr?("checked")
31
32
  attr_code(
32
- uncheckedcheckbox: node.attr?("checkbox") ? !node.attr?("checked") : nil,
33
- checkedcheckbox: node.attr?("checkbox") ? node.attr?("checked") : nil,
33
+ uncheckedcheckbox: node.attr?("checkbox") ? !c : nil,
34
+ checkedcheckbox: node.attr?("checkbox") ? c : nil
34
35
  )
35
36
  end
36
37
 
37
38
  def ulist(node)
38
39
  return reference(node) if in_norm_ref? || in_biblio?
40
+
39
41
  noko do |xml|
40
42
  xml.ul **ul_attrs(node) do |xml_ul|
41
43
  node.items.each do |item|
@@ -4,6 +4,7 @@ require "uuidtools"
4
4
  require "yaml"
5
5
  require_relative "./macros_plantuml"
6
6
  require_relative "./macros_terms"
7
+ require_relative "./macros_form"
7
8
  require_relative "./datamodel/attributes_table_preprocessor"
8
9
  require_relative "./datamodel/diagram_preprocessor"
9
10
  require "metanorma-plugin-datastruct"
@@ -74,12 +75,11 @@ module Asciidoctor
74
75
  def supply_br(lines)
75
76
  ignore = false
76
77
  lines.each_with_index do |l, i|
77
- /^(--+|====+|\|===|\.\.\.\.+|\*\*\*\*+|\+\+\+\++|\`\`\`\`+|____\+)$/.match(l) &&
78
- (ignore = !ignore)
79
- next if l.empty? || l.match(/ \+$/)
80
- next if /^\[.*\]$/.match?(l)
81
- next if ignore
82
- next if i == lines.size - 1 || i < lines.size - 1 && lines[i + 1].empty?
78
+ /^(--+|====+|\|===|\.\.\.\.+|\*\*\*\*+|\+\+\+\++|\`\`\`\`+|____\+)$/
79
+ .match(l) && (ignore = !ignore)
80
+ next if l.empty? || l.match(/ \+$/) || /^\[.*\]$/.match?(l) || ignore
81
+ next if i == lines.size - 1 ||
82
+ i < lines.size - 1 && lines[i + 1].empty?
83
83
 
84
84
  lines[i] += " +"
85
85
  end
@@ -128,22 +128,22 @@ module Asciidoctor
128
128
  def process(parent, reader, attrs)
129
129
  attrs["name"] = "todo"
130
130
  attrs["caption"] = "TODO"
131
- create_block parent, :admonition, reader.lines, attrs,
132
- content_model: :compound
131
+ create_block(parent, :admonition, reader.lines, attrs,
132
+ content_model: :compound)
133
133
  end
134
134
  end
135
135
 
136
136
  class ToDoInlineAdmonitionBlock < Extensions::Treeprocessor
137
137
  def process(document)
138
138
  (document.find_by context: :paragraph).each do |para|
139
- next unless /^TODO: /.match para.lines[0]
139
+ next unless /^TODO: /.match? para.lines[0]
140
140
 
141
141
  parent = para.parent
142
142
  para.set_attr("name", "todo")
143
143
  para.set_attr("caption", "TODO")
144
144
  para.lines[0].sub!(/^TODO: /, "")
145
- todo = Block.new parent, :admonition, attributes: para.attributes,
146
- source: para.lines, content_model: :compound
145
+ todo = Block.new(parent, :admonition, attributes: para.attributes,
146
+ source: para.lines, content_model: :compound)
147
147
  parent.blocks[parent.blocks.index(para)] = todo
148
148
  end
149
149
  end
@@ -0,0 +1,63 @@
1
+ module Asciidoctor
2
+ module Standoc
3
+ class FormInputMacro < Asciidoctor::Extensions::InlineMacroProcessor
4
+ use_dsl
5
+ named :input
6
+
7
+ def process(_parent, target, attr)
8
+ m = %w(id name value disabled readonly checked maxlength minlength)
9
+ .map { |a| attr[a] ? " #{a}='#{attr[a]}'" : nil }.compact
10
+ %{<input type='#{target}' #{m.join}/>}
11
+ end
12
+ end
13
+
14
+ class FormLabelMacro < Asciidoctor::Extensions::InlineMacroProcessor
15
+ use_dsl
16
+ named :label
17
+ parse_content_as :text
18
+
19
+ def process(parent, target, attr)
20
+ out = Asciidoctor::Inline.new(parent, :quoted, attr["text"]).convert
21
+ %{<label for="#{target}">#{out}</label>}
22
+ end
23
+ end
24
+
25
+ class FormTextareaMacro < Asciidoctor::Extensions::InlineMacroProcessor
26
+ use_dsl
27
+ named :textarea
28
+ using_format :short
29
+
30
+ def process(_parent, _target, attr)
31
+ m = %w(id name rows cols value)
32
+ .map { |a| attr[a] ? " #{a}='#{attr[a]}'" : nil }.compact
33
+ %{<textarea #{m.join}/>}
34
+ end
35
+ end
36
+
37
+ class FormSelectMacro < Asciidoctor::Extensions::InlineMacroProcessor
38
+ use_dsl
39
+ named :select
40
+ using_format :short
41
+
42
+ def process(parent, _target, attr)
43
+ m = %w(id name size disabled multiple value)
44
+ .map { |a| attr[a] ? " #{a}='#{attr[a]}'" : nil }.compact
45
+ out = Asciidoctor::Inline.new(parent, :quoted, attr["text"]).convert
46
+ %{<select #{m.join}>#{out}</select>}
47
+ end
48
+ end
49
+
50
+ class FormOptionMacro < Asciidoctor::Extensions::InlineMacroProcessor
51
+ use_dsl
52
+ named :option
53
+ using_format :short
54
+
55
+ def process(parent, _target, attr)
56
+ m = %w(disabled value)
57
+ .map { |a| attr[a] ? " #{a}='#{attr[a]}'" : nil }.compact
58
+ out = Asciidoctor::Inline.new(parent, :quoted, attr["text"]).convert
59
+ %{<option #{m.join}">#{out}</option>}
60
+ end
61
+ end
62
+ end
63
+ end
@@ -4,8 +4,8 @@ module Asciidoctor
4
4
  # https://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby
5
5
  def self.plantuml_installed?
6
6
  cmd = "plantuml"
7
- exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
8
- ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
7
+ exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
8
+ ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
9
9
  exts.each do |ext|
10
10
  exe = File.join(path, "#{cmd}#{ext}")
11
11
  return exe if File.executable?(exe) && !File.directory?(exe)
@@ -15,7 +15,7 @@ module Asciidoctor
15
15
  nil
16
16
  end
17
17
 
18
- def self.run umlfile, outfile
18
+ def self.run(umlfile, outfile)
19
19
  system "plantuml #{umlfile.path}" or (warn $? and return false)
20
20
  i = 0
21
21
  until !Gem.win_platform? || File.exist?(outfile) || i == 15
@@ -29,9 +29,9 @@ module Asciidoctor
29
29
  # sleep need for windows because dot works in separate process and
30
30
  # plantuml process may finish earlier then dot, as result png file
31
31
  # maybe not created yet after plantuml finish
32
- def self.generate_file parent, reader
32
+ def self.generate_file(parent, reader)
33
33
  localdir = Metanorma::Utils::localdir(parent.document)
34
- imagesdir = parent.document.attr('imagesdir')
34
+ imagesdir = parent.document.attr("imagesdir")
35
35
  umlfile, outfile = save_plantuml parent, reader, localdir
36
36
  run(umlfile, outfile) or raise "No image output from PlantUML (#{umlfile}, #{outfile})!"
37
37
  umlfile.unlink
@@ -40,7 +40,7 @@ module Asciidoctor
40
40
  File.writable?(localdir) or raise "Destination path #{path} not writable for PlantUML!"
41
41
  path.mkpath
42
42
  File.writable?(path) or raise "Destination path #{path} not writable for PlantUML!"
43
- #File.exist?(path) or raise "Destination path #{path} already exists for PlantUML!"
43
+ # File.exist?(path) or raise "Destination path #{path} already exists for PlantUML!"
44
44
 
45
45
  # Warning: metanorma/metanorma-standoc#187
46
46
  # Windows Ruby 2.4 will crash if a Tempfile is "mv"ed.
@@ -51,21 +51,21 @@ module Asciidoctor
51
51
  imagesdir ? filename : File.join(path, filename)
52
52
  end
53
53
 
54
- def self.save_plantuml parent, reader, localdir
54
+ def self.save_plantuml(_parent, reader, _localdir)
55
55
  src = reader.source
56
56
  reader.lines.first.sub(/\s+$/, "").match /^@startuml($| )/ or
57
57
  src = "@startuml\n#{src}\n@enduml\n"
58
58
  /^@startuml (?<fn>[^\n]+)\n/ =~ src
59
- Tempfile.open(["plantuml", ".pml"], :encoding => "utf-8") do |f|
59
+ Tempfile.open(["plantuml", ".pml"], encoding: "utf-8") do |f|
60
60
  f.write(src)
61
61
  [f, File.join(File.dirname(f.path),
62
62
  (fn || File.basename(f.path, ".pml")) + ".png")]
63
63
  end
64
64
  end
65
65
 
66
- def self.generate_attrs attrs
67
- through_attrs = %w(id align float title role width height alt).
68
- inject({}) do |memo, key|
66
+ def self.generate_attrs(attrs)
67
+ %w(id align float title role width height alt)
68
+ .inject({}) do |memo, key|
69
69
  memo[key] = attrs[key] if attrs.has_key? key
70
70
  memo
71
71
  end
@@ -81,19 +81,17 @@ module Asciidoctor
81
81
  def abort(parent, reader, attrs, msg)
82
82
  warn msg
83
83
  attrs["language"] = "plantuml"
84
- create_listing_block parent, reader.source, attrs.reject { |k, v| k == 1 }
84
+ create_listing_block parent, reader.source, attrs.reject { |k, _v| k == 1 }
85
85
  end
86
86
 
87
87
  def process(parent, reader, attrs)
88
- begin
89
- PlantUMLBlockMacroBackend.plantuml_installed?
90
- filename = PlantUMLBlockMacroBackend.generate_file(parent, reader)
91
- through_attrs = PlantUMLBlockMacroBackend.generate_attrs attrs
92
- through_attrs["target"] = filename
93
- create_image_block parent, through_attrs
94
- rescue StandardError => e
95
- abort(parent, reader, attrs, e.message)
96
- end
88
+ PlantUMLBlockMacroBackend.plantuml_installed?
89
+ filename = PlantUMLBlockMacroBackend.generate_file(parent, reader)
90
+ through_attrs = PlantUMLBlockMacroBackend.generate_attrs attrs
91
+ through_attrs["target"] = filename
92
+ create_image_block parent, through_attrs
93
+ rescue StandardError => e
94
+ abort(parent, reader, attrs, e.message)
97
95
  end
98
96
  end
99
97
  end