brakeman 6.2.2 → 8.0.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.
Files changed (626) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +110 -0
  3. data/README.md +4 -4
  4. data/bundle/load.rb +16 -14
  5. data/bundle/ruby/3.2.0/gems/csv-3.3.6/LICENSE.txt +33 -0
  6. data/bundle/ruby/3.2.0/gems/csv-3.3.6/NEWS.md +1047 -0
  7. data/bundle/ruby/3.2.0/gems/csv-3.3.6/README.md +55 -0
  8. data/bundle/ruby/3.2.0/gems/csv-3.3.6/lib/csv/core_ext/array.rb +9 -0
  9. data/bundle/ruby/3.2.0/gems/csv-3.3.6/lib/csv/core_ext/string.rb +9 -0
  10. data/bundle/ruby/3.2.0/gems/csv-3.3.6/lib/csv/fields_converter.rb +96 -0
  11. data/bundle/ruby/3.2.0/gems/csv-3.3.6/lib/csv/input_record_separator.rb +18 -0
  12. data/bundle/ruby/3.2.0/gems/csv-3.3.6/lib/csv/parser.rb +1307 -0
  13. data/bundle/ruby/3.2.0/gems/csv-3.3.6/lib/csv/row.rb +781 -0
  14. data/bundle/ruby/3.2.0/gems/csv-3.3.6/lib/csv/table.rb +1055 -0
  15. data/bundle/ruby/3.2.0/gems/csv-3.3.6/lib/csv/version.rb +6 -0
  16. data/bundle/ruby/3.2.0/gems/csv-3.3.6/lib/csv/writer.rb +209 -0
  17. data/bundle/ruby/3.2.0/gems/csv-3.3.6/lib/csv.rb +2989 -0
  18. data/bundle/ruby/3.2.0/gems/erubi-1.13.1/CHANGELOG +111 -0
  19. data/bundle/ruby/{3.1.0/gems/erubis-2.7.0 → 3.2.0/gems/erubi-1.13.1}/MIT-LICENSE +1 -0
  20. data/bundle/ruby/3.2.0/gems/erubi-1.13.1/README.rdoc +151 -0
  21. data/bundle/ruby/3.2.0/gems/erubi-1.13.1/lib/erubi/capture_block.rb +91 -0
  22. data/bundle/ruby/3.2.0/gems/erubi-1.13.1/lib/erubi/capture_end.rb +58 -0
  23. data/bundle/ruby/3.2.0/gems/erubi-1.13.1/lib/erubi.rb +299 -0
  24. data/bundle/ruby/{3.1.0/gems/haml-5.2.2 → 3.2.0/gems/haml-6.4.0}/CHANGELOG.md +180 -4
  25. data/bundle/ruby/{3.1.0/gems/haml-5.2.2 → 3.2.0/gems/haml-6.4.0}/FAQ.md +1 -1
  26. data/bundle/ruby/3.2.0/gems/haml-6.4.0/Gemfile +34 -0
  27. data/bundle/ruby/{3.1.0/gems/haml-5.2.2 → 3.2.0/gems/haml-6.4.0}/MIT-LICENSE +1 -1
  28. data/bundle/ruby/{3.1.0/gems/haml-5.2.2 → 3.2.0/gems/haml-6.4.0}/README.md +23 -34
  29. data/bundle/ruby/{3.1.0/gems/haml-5.2.2 → 3.2.0/gems/haml-6.4.0}/REFERENCE.md +100 -171
  30. data/bundle/ruby/3.2.0/gems/haml-6.4.0/exe/haml +6 -0
  31. data/bundle/ruby/3.2.0/gems/haml-6.4.0/haml.gemspec +45 -0
  32. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/ambles.rb +20 -0
  33. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/attribute_builder.rb +162 -0
  34. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/attribute_compiler.rb +133 -0
  35. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/attribute_parser.rb +116 -0
  36. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/cli.rb +154 -0
  37. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/compiler/children_compiler.rb +155 -0
  38. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/compiler/comment_compiler.rb +51 -0
  39. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/compiler/doctype_compiler.rb +52 -0
  40. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/compiler/script_compiler.rb +114 -0
  41. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/compiler/silent_script_compiler.rb +24 -0
  42. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/compiler/tag_compiler.rb +76 -0
  43. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/compiler.rb +97 -0
  44. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/dynamic_merger.rb +67 -0
  45. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/engine.rb +59 -0
  46. data/bundle/ruby/{3.1.0/gems/haml-5.2.2 → 3.2.0/gems/haml-6.4.0}/lib/haml/error.rb +5 -4
  47. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/escape.rb +13 -0
  48. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/escape_any.rb +21 -0
  49. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters/base.rb +12 -0
  50. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters/cdata.rb +20 -0
  51. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters/coffee.rb +17 -0
  52. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters/css.rb +33 -0
  53. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters/erb.rb +10 -0
  54. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters/escaped.rb +22 -0
  55. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters/javascript.rb +33 -0
  56. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters/less.rb +20 -0
  57. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters/markdown.rb +11 -0
  58. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters/plain.rb +29 -0
  59. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters/preserve.rb +22 -0
  60. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters/ruby.rb +10 -0
  61. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters/sass.rb +15 -0
  62. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters/scss.rb +15 -0
  63. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters/text_base.rb +25 -0
  64. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters/tilt_base.rb +59 -0
  65. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/filters.rb +75 -0
  66. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/force_escape.rb +29 -0
  67. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/helpers.rb +15 -0
  68. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/html.rb +22 -0
  69. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/identity.rb +13 -0
  70. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/object_ref.rb +35 -0
  71. data/bundle/ruby/{3.1.0/gems/haml-5.2.2 → 3.2.0/gems/haml-6.4.0}/lib/haml/parser.rb +159 -24
  72. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/rails_helpers.rb +53 -0
  73. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/rails_template.rb +62 -0
  74. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/railtie.rb +10 -0
  75. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/ruby_expression.rb +32 -0
  76. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/string_splitter.rb +140 -0
  77. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/template.rb +20 -0
  78. data/bundle/ruby/{3.1.0/gems/haml-5.2.2 → 3.2.0/gems/haml-6.4.0}/lib/haml/temple_line_counter.rb +2 -1
  79. data/bundle/ruby/{3.1.0/gems/haml-5.2.2 → 3.2.0/gems/haml-6.4.0}/lib/haml/util.rb +19 -15
  80. data/bundle/ruby/{3.1.0/gems/haml-5.2.2 → 3.2.0/gems/haml-6.4.0}/lib/haml/version.rb +1 -2
  81. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml/whitespace.rb +8 -0
  82. data/bundle/ruby/3.2.0/gems/haml-6.4.0/lib/haml.rb +13 -0
  83. data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/Changelog.md +4 -0
  84. data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/Gemfile +1 -0
  85. data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/list_renderer.rb +2 -2
  86. data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/menu.rb +7 -5
  87. data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/version.rb +1 -1
  88. data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline.rb +17 -12
  89. data/bundle/ruby/3.2.0/gems/parallel-1.28.0/lib/parallel/version.rb +4 -0
  90. data/bundle/ruby/{3.1.0/gems/parallel-1.26.3 → 3.2.0/gems/parallel-1.28.0}/lib/parallel.rb +12 -1
  91. data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline/config.rb +27 -26
  92. data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline/history.rb +3 -3
  93. data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline/io/ansi.rb +82 -142
  94. data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline/io/dumb.rb +16 -2
  95. data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline/io/windows.rb +120 -77
  96. data/bundle/ruby/3.2.0/gems/reline-0.7.0/lib/reline/io.rb +61 -0
  97. data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline/key_actor/base.rb +10 -4
  98. data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline/key_actor/emacs.rb +97 -97
  99. data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline/key_actor/vi_command.rb +182 -182
  100. data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline/key_actor/vi_insert.rb +137 -137
  101. data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline/key_stroke.rb +26 -16
  102. data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline/line_editor.rb +373 -537
  103. data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline/unicode/east_asian_width.rb +48 -23
  104. data/bundle/ruby/3.2.0/gems/reline-0.7.0/lib/reline/unicode.rb +421 -0
  105. data/bundle/ruby/3.2.0/gems/reline-0.7.0/lib/reline/version.rb +3 -0
  106. data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline.rb +49 -40
  107. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/NEWS.md +219 -1
  108. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/attribute.rb +7 -8
  109. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/cdata.rb +1 -1
  110. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/child.rb +2 -3
  111. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/comment.rb +1 -1
  112. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/doctype.rb +3 -8
  113. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/document.rb +21 -5
  114. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/element.rb +53 -59
  115. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/encoding.rb +3 -6
  116. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/functions.rb +3 -3
  117. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/instruction.rb +1 -1
  118. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/namespace.rb +4 -4
  119. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/node.rb +2 -2
  120. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/parsers/baseparser.rb +208 -116
  121. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/parsers/pullparser.rb +4 -0
  122. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/parsers/xpathparser.rb +4 -4
  123. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/quickpath.rb +19 -18
  124. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/rexml.rb +1 -1
  125. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/security.rb +2 -2
  126. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/source.rb +68 -8
  127. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/text.rb +29 -57
  128. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/validation/relaxng.rb +27 -26
  129. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/validation/validation.rb +8 -8
  130. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/xpath.rb +2 -13
  131. data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/xpath_parser.rb +44 -42
  132. data/bundle/ruby/{3.1.0/gems/ruby2ruby-2.5.1 → 3.2.0/gems/ruby2ruby-2.5.2}/History.rdoc +6 -0
  133. data/bundle/ruby/{3.1.0/gems/ruby2ruby-2.5.1 → 3.2.0/gems/ruby2ruby-2.5.2}/lib/ruby2ruby.rb +7 -4
  134. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3 → 3.2.0/gems/ruby_parser-3.22.0}/History.rdoc +67 -0
  135. data/bundle/ruby/3.2.0/gems/ruby_parser-3.22.0/Manifest.txt +39 -0
  136. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3 → 3.2.0/gems/ruby_parser-3.22.0}/README.rdoc +15 -3
  137. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3 → 3.2.0/gems/ruby_parser-3.22.0}/compare/normalize.rb +19 -3
  138. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3 → 3.2.0/gems/ruby_parser-3.22.0}/gauntlet.md +35 -5
  139. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3 → 3.2.0/gems/ruby_parser-3.22.0}/lib/ruby_lexer.rb +9 -18
  140. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3 → 3.2.0/gems/ruby_parser-3.22.0}/lib/ruby_lexer.rex.rb +1 -2
  141. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3 → 3.2.0/gems/ruby_parser-3.22.0}/lib/ruby_parser.rb +18 -14
  142. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3/lib/ruby_parser.yy → 3.2.0/gems/ruby_parser-3.22.0/lib/ruby_parser2.yy} +15 -22
  143. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3/lib/ruby20_parser.rb → 3.2.0/gems/ruby_parser-3.22.0/lib/ruby_parser20.rb} +19 -22
  144. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3/lib/ruby21_parser.rb → 3.2.0/gems/ruby_parser-3.22.0/lib/ruby_parser21.rb} +19 -22
  145. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3/lib/ruby22_parser.rb → 3.2.0/gems/ruby_parser-3.22.0/lib/ruby_parser22.rb} +19 -22
  146. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3/lib/ruby23_parser.rb → 3.2.0/gems/ruby_parser-3.22.0/lib/ruby_parser23.rb} +19 -22
  147. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3/lib/ruby24_parser.rb → 3.2.0/gems/ruby_parser-3.22.0/lib/ruby_parser24.rb} +19 -22
  148. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3/lib/ruby25_parser.rb → 3.2.0/gems/ruby_parser-3.22.0/lib/ruby_parser25.rb} +19 -22
  149. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3/lib/ruby26_parser.rb → 3.2.0/gems/ruby_parser-3.22.0/lib/ruby_parser26.rb} +19 -22
  150. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3/lib/ruby27_parser.rb → 3.2.0/gems/ruby_parser-3.22.0/lib/ruby_parser27.rb} +21 -24
  151. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3/lib/ruby3_parser.yy → 3.2.0/gems/ruby_parser-3.22.0/lib/ruby_parser3.yy} +214 -79
  152. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3/lib/ruby30_parser.rb → 3.2.0/gems/ruby_parser-3.22.0/lib/ruby_parser30.rb} +3339 -3351
  153. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3/lib/ruby31_parser.rb → 3.2.0/gems/ruby_parser-3.22.0/lib/ruby_parser31.rb} +3772 -3808
  154. data/bundle/ruby/3.2.0/gems/ruby_parser-3.22.0/lib/ruby_parser32.rb +13565 -0
  155. data/bundle/ruby/3.2.0/gems/ruby_parser-3.22.0/lib/ruby_parser33.rb +13653 -0
  156. data/bundle/ruby/3.2.0/gems/ruby_parser-3.22.0/lib/ruby_parser34.rb +13653 -0
  157. data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3 → 3.2.0/gems/ruby_parser-3.22.0}/lib/ruby_parser_extras.rb +109 -60
  158. data/bundle/ruby/{3.1.0/gems/sexp_processor-4.17.2 → 3.2.0/gems/sexp_processor-4.17.5}/History.rdoc +20 -0
  159. data/bundle/ruby/{3.1.0/gems/sexp_processor-4.17.2 → 3.2.0/gems/sexp_processor-4.17.5}/lib/pt_testcase.rb +1 -1
  160. data/bundle/ruby/{3.1.0/gems/sexp_processor-4.17.2 → 3.2.0/gems/sexp_processor-4.17.5}/lib/sexp.rb +1 -1
  161. data/bundle/ruby/{3.1.0/gems/sexp_processor-4.17.2 → 3.2.0/gems/sexp_processor-4.17.5}/lib/sexp_processor.rb +1 -1
  162. data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/CHANGES +8 -2
  163. data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/Gemfile +5 -2
  164. data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/LICENSE +1 -1
  165. data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/README.jp.md +10 -12
  166. data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/README.md +21 -26
  167. data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/embedded.rb +1 -1
  168. data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/logic_less/context.rb +4 -3
  169. data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/splat/builder.rb +18 -19
  170. data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/version.rb +1 -1
  171. data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/CHANGES +12 -0
  172. data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/README.md +1 -1
  173. data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/filters/encoding.rb +1 -1
  174. data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/filters/static_analyzer.rb +1 -1
  175. data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/filters/string_splitter.rb +61 -5
  176. data/bundle/ruby/3.2.0/gems/temple-0.10.6/lib/temple/parser_engine.rb +34 -0
  177. data/bundle/ruby/3.2.0/gems/temple-0.10.6/lib/temple/static_analyzer.rb +148 -0
  178. data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/version.rb +1 -1
  179. data/bundle/ruby/3.2.0/gems/terminal-table-4.0.0/History.rdoc +149 -0
  180. data/bundle/ruby/3.2.0/gems/terminal-table-4.0.0/README.md +417 -0
  181. data/bundle/ruby/{3.1.0/gems/terminal-table-1.8.0 → 3.2.0/gems/terminal-table-4.0.0}/lib/terminal-table/cell.rb +9 -9
  182. data/bundle/ruby/{3.1.0/gems/terminal-table-1.8.0 → 3.2.0/gems/terminal-table-4.0.0}/lib/terminal-table/row.rb +18 -4
  183. data/bundle/ruby/3.2.0/gems/terminal-table-4.0.0/lib/terminal-table/separator.rb +66 -0
  184. data/bundle/ruby/3.2.0/gems/terminal-table-4.0.0/lib/terminal-table/style.rb +284 -0
  185. data/bundle/ruby/{3.1.0/gems/terminal-table-1.8.0 → 3.2.0/gems/terminal-table-4.0.0}/lib/terminal-table/table.rb +49 -18
  186. data/bundle/ruby/3.2.0/gems/terminal-table-4.0.0/lib/terminal-table/util.rb +13 -0
  187. data/bundle/ruby/{3.1.0/gems/terminal-table-1.8.0 → 3.2.0/gems/terminal-table-4.0.0}/lib/terminal-table/version.rb +1 -1
  188. data/bundle/ruby/{3.1.0/gems/terminal-table-1.8.0 → 3.2.0/gems/terminal-table-4.0.0}/lib/terminal-table.rb +2 -2
  189. data/bundle/ruby/{3.1.0/gems/terminal-table-1.8.0 → 3.2.0/gems/terminal-table-4.0.0}/terminal-table.gemspec +3 -4
  190. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/COPYING +1 -1
  191. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/asciidoc.rb +12 -5
  192. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/babel.rb +5 -0
  193. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/builder.rb +4 -0
  194. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/coffee.rb +14 -4
  195. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/commonmarker.rb +65 -0
  196. data/bundle/ruby/3.2.0/gems/tilt-2.8.0/lib/tilt/csv.rb +63 -0
  197. data/bundle/ruby/3.2.0/gems/tilt-2.8.0/lib/tilt/erb.rb +130 -0
  198. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/erubi.rb +41 -8
  199. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/etanni.rb +7 -0
  200. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/haml.rb +67 -1
  201. data/bundle/ruby/3.2.0/gems/tilt-2.8.0/lib/tilt/kramdown.rb +59 -0
  202. data/bundle/ruby/3.2.0/gems/tilt-2.8.0/lib/tilt/liquid.rb +93 -0
  203. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/livescript.rb +11 -4
  204. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/mapping.rb +9 -9
  205. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/markaby.rb +11 -2
  206. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/nokogiri.rb +13 -2
  207. data/bundle/ruby/3.2.0/gems/tilt-2.8.0/lib/tilt/pandoc.rb +81 -0
  208. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/pipeline.rb +6 -1
  209. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/plain.rb +5 -1
  210. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/prawn.rb +17 -6
  211. data/bundle/ruby/3.2.0/gems/tilt-2.8.0/lib/tilt/radius.rb +96 -0
  212. data/bundle/ruby/3.2.0/gems/tilt-2.8.0/lib/tilt/rdiscount.rb +82 -0
  213. data/bundle/ruby/3.2.0/gems/tilt-2.8.0/lib/tilt/rdoc.rb +41 -0
  214. data/bundle/ruby/3.2.0/gems/tilt-2.8.0/lib/tilt/redcarpet.rb +73 -0
  215. data/bundle/ruby/3.2.0/gems/tilt-2.8.0/lib/tilt/redcloth.rb +40 -0
  216. data/bundle/ruby/3.2.0/gems/tilt-2.8.0/lib/tilt/rst-pandoc.rb +33 -0
  217. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/sass.rb +16 -3
  218. data/bundle/ruby/3.2.0/gems/tilt-2.8.0/lib/tilt/slim.rb +18 -0
  219. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/string.rb +10 -2
  220. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/template.rb +189 -33
  221. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/typescript.rb +5 -0
  222. data/bundle/ruby/3.2.0/gems/tilt-2.8.0/lib/tilt/yajl.rb +91 -0
  223. data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt.rb +9 -5
  224. data/bundle/ruby/3.2.0/gems/unicode-display_width-3.2.0/CHANGELOG.md +299 -0
  225. data/bundle/ruby/{3.1.0/gems/unicode-display_width-1.8.0 → 3.2.0/gems/unicode-display_width-3.2.0}/MIT-LICENSE.txt +1 -1
  226. data/bundle/ruby/3.2.0/gems/unicode-display_width-3.2.0/README.md +194 -0
  227. data/bundle/ruby/3.2.0/gems/unicode-display_width-3.2.0/data/display_width.marshal.gz +0 -0
  228. data/bundle/ruby/3.2.0/gems/unicode-display_width-3.2.0/lib/unicode/display_width/constants.rb +10 -0
  229. data/bundle/ruby/3.2.0/gems/unicode-display_width-3.2.0/lib/unicode/display_width/emoji_support.rb +55 -0
  230. data/bundle/ruby/3.2.0/gems/unicode-display_width-3.2.0/lib/unicode/display_width/index.rb +34 -0
  231. data/bundle/ruby/3.2.0/gems/unicode-display_width-3.2.0/lib/unicode/display_width/no_string_ext.rb +8 -0
  232. data/bundle/ruby/3.2.0/gems/unicode-display_width-3.2.0/lib/unicode/display_width/reline_ext.rb +14 -0
  233. data/bundle/ruby/3.2.0/gems/unicode-display_width-3.2.0/lib/unicode/display_width/string_ext.rb +9 -0
  234. data/bundle/ruby/3.2.0/gems/unicode-display_width-3.2.0/lib/unicode/display_width.rb +247 -0
  235. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/CHANGELOG.md +202 -0
  236. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/CODE_OF_CONDUCT.md +74 -0
  237. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/Gemfile +9 -0
  238. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/Gemfile.lock +37 -0
  239. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/MIT-LICENSE.txt +20 -0
  240. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/README.md +205 -0
  241. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/data/emoji.marshal.gz +0 -0
  242. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/data/generate_constants.rb +344 -0
  243. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/constants.rb +50 -0
  244. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex.rb +8 -0
  245. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_basic.rb +8 -0
  246. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_emoji_keycap.rb +8 -0
  247. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_include_mqe.rb +8 -0
  248. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_include_mqe_uqe.rb +8 -0
  249. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_include_text.rb +8 -0
  250. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_picto.rb +8 -0
  251. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_picto_no_emoji.rb +8 -0
  252. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_possible.rb +8 -0
  253. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_prop_component.rb +8 -0
  254. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_prop_emoji.rb +8 -0
  255. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_prop_modifier.rb +8 -0
  256. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_prop_modifier_base.rb +8 -0
  257. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_prop_presentation.rb +8 -0
  258. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_text.rb +8 -0
  259. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_text_presentation.rb +8 -0
  260. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_valid.rb +8 -0
  261. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_valid_include_text.rb +8 -0
  262. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_well_formed.rb +8 -0
  263. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated/regex_well_formed_include_text.rb +8 -0
  264. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex.rb +8 -0
  265. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_basic.rb +8 -0
  266. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_emoji_keycap.rb +8 -0
  267. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_include_mqe.rb +8 -0
  268. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_include_mqe_uqe.rb +8 -0
  269. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_include_text.rb +8 -0
  270. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_picto.rb +8 -0
  271. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_picto_no_emoji.rb +8 -0
  272. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_possible.rb +8 -0
  273. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_prop_component.rb +8 -0
  274. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_prop_emoji.rb +8 -0
  275. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_prop_modifier.rb +8 -0
  276. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_prop_modifier_base.rb +8 -0
  277. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_prop_presentation.rb +8 -0
  278. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_text.rb +8 -0
  279. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_text_presentation.rb +8 -0
  280. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_valid.rb +8 -0
  281. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_valid_include_text.rb +8 -0
  282. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_well_formed.rb +8 -0
  283. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/generated_native/regex_well_formed_include_text.rb +8 -0
  284. data/bundle/ruby/{3.1.0/gems/unicode-display_width-1.8.0/lib/unicode/display_width → 3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji}/index.rb +5 -3
  285. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/lazy_constants.rb +56 -0
  286. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji/list.rb +13 -0
  287. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/lib/unicode/emoji.rb +111 -0
  288. data/bundle/ruby/3.2.0/gems/unicode-emoji-4.2.0/unicode-emoji.gemspec +22 -0
  289. data/lib/brakeman/app_tree.rb +87 -25
  290. data/lib/brakeman/checks/base_check.rb +5 -2
  291. data/lib/brakeman/checks/check_deserialize.rb +4 -1
  292. data/lib/brakeman/checks/check_eol_rails.rb +2 -0
  293. data/lib/brakeman/checks/check_eol_ruby.rb +2 -0
  294. data/lib/brakeman/checks/check_evaluation.rb +45 -8
  295. data/lib/brakeman/checks/check_execute.rb +23 -19
  296. data/lib/brakeman/checks/check_model_attr_accessible.rb +1 -0
  297. data/lib/brakeman/checks/check_model_attributes.rb +1 -1
  298. data/lib/brakeman/checks/check_render.rb +6 -27
  299. data/lib/brakeman/checks/check_render_rce.rb +43 -0
  300. data/lib/brakeman/checks/check_session_settings.rb +1 -1
  301. data/lib/brakeman/checks/check_sql.rb +17 -7
  302. data/lib/brakeman/checks/check_weak_rsa_key.rb +1 -1
  303. data/lib/brakeman/checks.rb +31 -25
  304. data/lib/brakeman/commandline.rb +22 -5
  305. data/lib/brakeman/file_parser.rb +2 -0
  306. data/lib/brakeman/file_path.rb +4 -0
  307. data/lib/brakeman/logger.rb +265 -0
  308. data/lib/brakeman/messages.rb +1 -1
  309. data/lib/brakeman/options.rb +22 -16
  310. data/lib/brakeman/parsers/haml6_embedded.rb +23 -0
  311. data/lib/brakeman/parsers/rails_erubi.rb +82 -0
  312. data/lib/brakeman/parsers/template_parser.rb +39 -22
  313. data/lib/brakeman/processor.rb +4 -2
  314. data/lib/brakeman/processors/alias_processor.rb +18 -4
  315. data/lib/brakeman/processors/base_processor.rb +15 -6
  316. data/lib/brakeman/processors/controller_alias_processor.rb +1 -1
  317. data/lib/brakeman/processors/controller_processor.rb +3 -3
  318. data/lib/brakeman/processors/{erubis_template_processor.rb → erubi_template_procesor.rb} +3 -3
  319. data/lib/brakeman/processors/gem_processor.rb +1 -1
  320. data/lib/brakeman/processors/haml6_template_processor.rb +92 -0
  321. data/lib/brakeman/processors/haml_template_processor.rb +20 -3
  322. data/lib/brakeman/processors/lib/file_type_detector.rb +9 -7
  323. data/lib/brakeman/processors/lib/rails2_config_processor.rb +5 -4
  324. data/lib/brakeman/processors/lib/rails2_route_processor.rb +1 -1
  325. data/lib/brakeman/processors/lib/rails3_config_processor.rb +11 -2
  326. data/lib/brakeman/processors/lib/render_helper.rb +42 -3
  327. data/lib/brakeman/processors/lib/render_path.rb +1 -1
  328. data/lib/brakeman/processors/model_processor.rb +1 -1
  329. data/lib/brakeman/processors/template_processor.rb +1 -1
  330. data/lib/brakeman/report/ignore/config.rb +1 -2
  331. data/lib/brakeman/report/pager.rb +1 -1
  332. data/lib/brakeman/report/report_html.rb +1 -1
  333. data/lib/brakeman/report/report_junit.rb +4 -57
  334. data/lib/brakeman/report/report_sarif.rb +122 -2
  335. data/lib/brakeman/report/templates/header.html.erb +8 -3
  336. data/lib/brakeman/report/templates/ignored_warnings.html.erb +3 -3
  337. data/lib/brakeman/rescanner.rb +40 -390
  338. data/lib/brakeman/scanner.rb +113 -98
  339. data/lib/brakeman/tracker/collection.rb +12 -2
  340. data/lib/brakeman/tracker/config.rb +17 -13
  341. data/lib/brakeman/tracker/constants.rb +17 -2
  342. data/lib/brakeman/tracker/controller.rb +1 -1
  343. data/lib/brakeman/tracker/file_cache.rb +83 -0
  344. data/lib/brakeman/tracker.rb +32 -17
  345. data/lib/brakeman/version.rb +1 -1
  346. data/lib/brakeman.rb +130 -55
  347. data/lib/ruby_parser/bm_sexp.rb +14 -0
  348. metadata +460 -410
  349. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/CHANGES.txt +0 -828
  350. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/README.txt +0 -102
  351. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/contrib/erubis +0 -3468
  352. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/contrib/erubis-run.rb +0 -132
  353. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/contrib/inline-require +0 -179
  354. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/context.rb +0 -83
  355. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/converter.rb +0 -357
  356. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/engine/ec.rb +0 -117
  357. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/engine/ecpp.rb +0 -113
  358. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/engine/ejava.rb +0 -110
  359. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/engine/ejavascript.rb +0 -119
  360. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/engine/enhanced.rb +0 -126
  361. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/engine/eperl.rb +0 -95
  362. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/engine/ephp.rb +0 -99
  363. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/engine/eruby.rb +0 -125
  364. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/engine/escheme.rb +0 -114
  365. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/engine/optimized.rb +0 -127
  366. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/engine.rb +0 -120
  367. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/enhancer.rb +0 -723
  368. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/error.rb +0 -23
  369. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/evaluator.rb +0 -88
  370. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/generator.rb +0 -85
  371. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/helper.rb +0 -47
  372. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/helpers/rails_form_helper.rb +0 -197
  373. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/helpers/rails_helper.rb +0 -353
  374. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/local-setting.rb +0 -9
  375. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/main.rb +0 -516
  376. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/preprocessing.rb +0 -58
  377. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/tiny.rb +0 -144
  378. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis/util.rb +0 -22
  379. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/lib/erubis.rb +0 -73
  380. data/bundle/ruby/3.1.0/gems/erubis-2.7.0/setup.rb +0 -1331
  381. data/bundle/ruby/3.1.0/gems/haml-5.2.2/Gemfile +0 -16
  382. data/bundle/ruby/3.1.0/gems/haml-5.2.2/TODO +0 -24
  383. data/bundle/ruby/3.1.0/gems/haml-5.2.2/haml.gemspec +0 -45
  384. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/attribute_builder.rb +0 -219
  385. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/attribute_compiler.rb +0 -237
  386. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/attribute_parser.rb +0 -150
  387. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/buffer.rb +0 -182
  388. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/compiler.rb +0 -330
  389. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/engine.rb +0 -238
  390. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/escapable.rb +0 -77
  391. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/exec.rb +0 -347
  392. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/filters.rb +0 -399
  393. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/generator.rb +0 -42
  394. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/helpers/action_view_extensions.rb +0 -60
  395. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/helpers/action_view_mods.rb +0 -132
  396. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/helpers/action_view_xss_mods.rb +0 -60
  397. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/helpers/safe_erubi_template.rb +0 -20
  398. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/helpers/safe_erubis_template.rb +0 -33
  399. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/helpers/xss_mods.rb +0 -114
  400. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/helpers.rb +0 -709
  401. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/options.rb +0 -273
  402. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/plugin.rb +0 -54
  403. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/railtie.rb +0 -53
  404. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/sass_rails_filter.rb +0 -47
  405. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/template/options.rb +0 -27
  406. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/template.rb +0 -39
  407. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml/temple_engine.rb +0 -124
  408. data/bundle/ruby/3.1.0/gems/haml-5.2.2/lib/haml.rb +0 -25
  409. data/bundle/ruby/3.1.0/gems/haml-5.2.2/yard/default/fulldoc/html/css/common.sass +0 -15
  410. data/bundle/ruby/3.1.0/gems/haml-5.2.2/yard/default/layout/html/footer.erb +0 -12
  411. data/bundle/ruby/3.1.0/gems/parallel-1.26.3/lib/parallel/version.rb +0 -4
  412. data/bundle/ruby/3.1.0/gems/reline-0.5.10/lib/reline/io.rb +0 -41
  413. data/bundle/ruby/3.1.0/gems/reline-0.5.10/lib/reline/terminfo.rb +0 -158
  414. data/bundle/ruby/3.1.0/gems/reline-0.5.10/lib/reline/unicode.rb +0 -671
  415. data/bundle/ruby/3.1.0/gems/reline-0.5.10/lib/reline/version.rb +0 -3
  416. data/bundle/ruby/3.1.0/gems/ruby_parser-3.20.3/Manifest.txt +0 -48
  417. data/bundle/ruby/3.1.0/gems/ruby_parser-3.20.3/lib/ruby20_parser.y +0 -2707
  418. data/bundle/ruby/3.1.0/gems/ruby_parser-3.20.3/lib/ruby21_parser.y +0 -2724
  419. data/bundle/ruby/3.1.0/gems/ruby_parser-3.20.3/lib/ruby22_parser.y +0 -2735
  420. data/bundle/ruby/3.1.0/gems/ruby_parser-3.20.3/lib/ruby23_parser.y +0 -2737
  421. data/bundle/ruby/3.1.0/gems/ruby_parser-3.20.3/lib/ruby24_parser.y +0 -2745
  422. data/bundle/ruby/3.1.0/gems/ruby_parser-3.20.3/lib/ruby25_parser.y +0 -2745
  423. data/bundle/ruby/3.1.0/gems/ruby_parser-3.20.3/lib/ruby26_parser.y +0 -2760
  424. data/bundle/ruby/3.1.0/gems/ruby_parser-3.20.3/lib/ruby27_parser.y +0 -3360
  425. data/bundle/ruby/3.1.0/gems/ruby_parser-3.20.3/lib/ruby30_parser.y +0 -3508
  426. data/bundle/ruby/3.1.0/gems/ruby_parser-3.20.3/lib/ruby31_parser.y +0 -3542
  427. data/bundle/ruby/3.1.0/gems/ruby_parser-3.20.3/lib/ruby32_parser.rb +0 -13601
  428. data/bundle/ruby/3.1.0/gems/ruby_parser-3.20.3/lib/ruby32_parser.y +0 -3543
  429. data/bundle/ruby/3.1.0/gems/temple-0.10.3/lib/temple/static_analyzer.rb +0 -78
  430. data/bundle/ruby/3.1.0/gems/terminal-table-1.8.0/History.rdoc +0 -85
  431. data/bundle/ruby/3.1.0/gems/terminal-table-1.8.0/README.rdoc +0 -247
  432. data/bundle/ruby/3.1.0/gems/terminal-table-1.8.0/lib/terminal-table/separator.rb +0 -14
  433. data/bundle/ruby/3.1.0/gems/terminal-table-1.8.0/lib/terminal-table/style.rb +0 -79
  434. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/creole.rb +0 -14
  435. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/csv.rb +0 -53
  436. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/erb.rb +0 -65
  437. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/erubis.rb +0 -51
  438. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/kramdown.rb +0 -13
  439. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/liquid.rb +0 -40
  440. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/maruku.rb +0 -10
  441. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/pandoc.rb +0 -39
  442. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/radius.rb +0 -41
  443. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/rdiscount.rb +0 -23
  444. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/rdoc.rb +0 -19
  445. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/redcarpet.rb +0 -31
  446. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/redcloth.rb +0 -13
  447. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/rst-pandoc.rb +0 -10
  448. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/slim.rb +0 -5
  449. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/wikicloth.rb +0 -12
  450. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/yajl.rb +0 -81
  451. data/bundle/ruby/3.1.0/gems/unicode-display_width-1.8.0/CHANGELOG.md +0 -137
  452. data/bundle/ruby/3.1.0/gems/unicode-display_width-1.8.0/README.md +0 -124
  453. data/bundle/ruby/3.1.0/gems/unicode-display_width-1.8.0/data/display_width.marshal.gz +0 -0
  454. data/bundle/ruby/3.1.0/gems/unicode-display_width-1.8.0/lib/unicode/display_width/constants.rb +0 -8
  455. data/bundle/ruby/3.1.0/gems/unicode-display_width-1.8.0/lib/unicode/display_width/no_string_ext.rb +0 -7
  456. data/bundle/ruby/3.1.0/gems/unicode-display_width-1.8.0/lib/unicode/display_width/string_ext.rb +0 -17
  457. data/bundle/ruby/3.1.0/gems/unicode-display_width-1.8.0/lib/unicode/display_width.rb +0 -51
  458. data/lib/brakeman/parsers/erubis_patch.rb +0 -11
  459. data/lib/brakeman/parsers/rails2_erubis.rb +0 -9
  460. data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +0 -52
  461. data/lib/brakeman/parsers/rails3_erubis.rb +0 -85
  462. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/AUTHORS +0 -0
  463. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/COPYING +0 -0
  464. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/LICENSE +0 -0
  465. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/README.md +0 -0
  466. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/TODO +0 -0
  467. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/highline.gemspec +0 -0
  468. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/builtin_styles.rb +0 -0
  469. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/color_scheme.rb +0 -0
  470. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/compatibility.rb +0 -0
  471. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/custom_errors.rb +0 -0
  472. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/import.rb +0 -0
  473. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/io_console_compatible.rb +0 -0
  474. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/list.rb +0 -0
  475. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/menu/item.rb +0 -0
  476. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/paginator.rb +0 -0
  477. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/question/answer_converter.rb +0 -0
  478. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/question.rb +0 -0
  479. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/question_asker.rb +0 -0
  480. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/simulate.rb +0 -0
  481. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/statement.rb +0 -0
  482. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/string.rb +0 -0
  483. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/string_extensions.rb +0 -0
  484. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/style.rb +0 -0
  485. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/template_renderer.rb +0 -0
  486. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/terminal/io_console.rb +0 -0
  487. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/terminal/ncurses.rb +0 -0
  488. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/terminal/unix_stty.rb +0 -0
  489. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/terminal.rb +0 -0
  490. /data/bundle/ruby/{3.1.0/gems/highline-3.1.1 → 3.2.0/gems/highline-3.1.2}/lib/highline/wrapper.rb +0 -0
  491. /data/bundle/ruby/{3.1.0/gems/parallel-1.26.3 → 3.2.0/gems/parallel-1.28.0}/MIT-LICENSE.txt +0 -0
  492. /data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/BSDL +0 -0
  493. /data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/COPYING +0 -0
  494. /data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/README.md +0 -0
  495. /data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline/face.rb +0 -0
  496. /data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline/key_actor/composite.rb +0 -0
  497. /data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline/key_actor.rb +0 -0
  498. /data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/lib/reline/kill_ring.rb +0 -0
  499. /data/bundle/ruby/{3.1.0/gems/reline-0.5.10 → 3.2.0/gems/reline-0.7.0}/license_of_rb-readline +0 -0
  500. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/LICENSE.txt +0 -0
  501. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/README.md +0 -0
  502. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/attlistdecl.rb +0 -0
  503. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/dtd/attlistdecl.rb +0 -0
  504. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/dtd/dtd.rb +0 -0
  505. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/dtd/elementdecl.rb +0 -0
  506. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/dtd/entitydecl.rb +0 -0
  507. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/dtd/notationdecl.rb +0 -0
  508. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/entity.rb +0 -0
  509. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/formatters/default.rb +0 -0
  510. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/formatters/pretty.rb +0 -0
  511. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/formatters/transitive.rb +0 -0
  512. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/light/node.rb +0 -0
  513. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/output.rb +0 -0
  514. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/parent.rb +0 -0
  515. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/parseexception.rb +0 -0
  516. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/parsers/lightparser.rb +0 -0
  517. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/parsers/sax2parser.rb +0 -0
  518. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/parsers/streamparser.rb +0 -0
  519. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/parsers/treeparser.rb +0 -0
  520. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/parsers/ultralightparser.rb +0 -0
  521. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/sax2listener.rb +0 -0
  522. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/streamlistener.rb +0 -0
  523. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/undefinednamespaceexception.rb +0 -0
  524. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/validation/validationexception.rb +0 -0
  525. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/xmldecl.rb +0 -0
  526. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml/xmltokens.rb +0 -0
  527. /data/bundle/ruby/{3.1.0/gems/rexml-3.3.8 → 3.2.0/gems/rexml-3.4.4}/lib/rexml.rb +0 -0
  528. /data/bundle/ruby/{3.1.0/gems/ruby2ruby-2.5.1 → 3.2.0/gems/ruby2ruby-2.5.2}/Manifest.txt +0 -0
  529. /data/bundle/ruby/{3.1.0/gems/ruby2ruby-2.5.1 → 3.2.0/gems/ruby2ruby-2.5.2}/README.rdoc +0 -0
  530. /data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3 → 3.2.0/gems/ruby_parser-3.22.0}/debugging.md +0 -0
  531. /data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3 → 3.2.0/gems/ruby_parser-3.22.0}/lib/rp_extensions.rb +0 -0
  532. /data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3 → 3.2.0/gems/ruby_parser-3.22.0}/lib/rp_stringscanner.rb +0 -0
  533. /data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3 → 3.2.0/gems/ruby_parser-3.22.0}/lib/ruby_lexer.rex +0 -0
  534. /data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3 → 3.2.0/gems/ruby_parser-3.22.0}/lib/ruby_lexer_strings.rb +0 -0
  535. /data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3 → 3.2.0/gems/ruby_parser-3.22.0}/tools/munge.rb +0 -0
  536. /data/bundle/ruby/{3.1.0/gems/ruby_parser-3.20.3 → 3.2.0/gems/ruby_parser-3.22.0}/tools/ripper.rb +0 -0
  537. /data/bundle/ruby/{3.1.0/gems/sexp_processor-4.17.2 → 3.2.0/gems/sexp_processor-4.17.5}/Manifest.txt +0 -0
  538. /data/bundle/ruby/{3.1.0/gems/sexp_processor-4.17.2 → 3.2.0/gems/sexp_processor-4.17.5}/README.rdoc +0 -0
  539. /data/bundle/ruby/{3.1.0/gems/sexp_processor-4.17.2 → 3.2.0/gems/sexp_processor-4.17.5}/lib/composite_sexp_processor.rb +0 -0
  540. /data/bundle/ruby/{3.1.0/gems/sexp_processor-4.17.2 → 3.2.0/gems/sexp_processor-4.17.5}/lib/sexp_matcher.rb +0 -0
  541. /data/bundle/ruby/{3.1.0/gems/sexp_processor-4.17.2 → 3.2.0/gems/sexp_processor-4.17.5}/lib/strict_sexp.rb +0 -0
  542. /data/bundle/ruby/{3.1.0/gems/sexp_processor-4.17.2 → 3.2.0/gems/sexp_processor-4.17.5}/lib/unique.rb +0 -0
  543. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/code_attributes.rb +0 -0
  544. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/command.rb +0 -0
  545. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/controls.rb +0 -0
  546. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/do_inserter.rb +0 -0
  547. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/end_inserter.rb +0 -0
  548. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/engine.rb +0 -0
  549. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/erb_converter.rb +0 -0
  550. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/filter.rb +0 -0
  551. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/grammar.rb +0 -0
  552. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/include.rb +0 -0
  553. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/interpolation.rb +0 -0
  554. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/logic_less/filter.rb +0 -0
  555. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/logic_less.rb +0 -0
  556. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/parser.rb +0 -0
  557. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/railtie.rb +0 -0
  558. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/smart/escaper.rb +0 -0
  559. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/smart/filter.rb +0 -0
  560. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/smart/parser.rb +0 -0
  561. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/smart.rb +0 -0
  562. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/splat/filter.rb +0 -0
  563. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/template.rb +0 -0
  564. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim/translator.rb +0 -0
  565. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/lib/slim.rb +0 -0
  566. /data/bundle/ruby/{3.1.0/gems/slim-5.2.1 → 3.2.0/gems/slim-5.2.2}/slim.gemspec +0 -0
  567. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/EXPRESSIONS.md +0 -0
  568. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/Gemfile +0 -0
  569. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/LICENSE +0 -0
  570. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/engine.rb +0 -0
  571. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/erb/engine.rb +0 -0
  572. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/erb/parser.rb +0 -0
  573. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/erb/template.rb +0 -0
  574. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/erb/trimming.rb +0 -0
  575. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/exceptions.rb +0 -0
  576. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/filter.rb +0 -0
  577. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/filters/ambles.rb +0 -0
  578. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/filters/code_merger.rb +0 -0
  579. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/filters/control_flow.rb +0 -0
  580. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/filters/dynamic_inliner.rb +0 -0
  581. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/filters/dynamic_merger.rb +0 -0
  582. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/filters/eraser.rb +0 -0
  583. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/filters/escapable.rb +0 -0
  584. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/filters/multi_flattener.rb +0 -0
  585. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/filters/remove_bom.rb +0 -0
  586. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/filters/static_merger.rb +0 -0
  587. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/filters/validator.rb +0 -0
  588. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/generator.rb +0 -0
  589. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/generators/array.rb +0 -0
  590. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/generators/array_buffer.rb +0 -0
  591. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/generators/erb.rb +0 -0
  592. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/generators/rails_output_buffer.rb +0 -0
  593. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/generators/string_buffer.rb +0 -0
  594. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/grammar.rb +0 -0
  595. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/html/attribute_merger.rb +0 -0
  596. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/html/attribute_remover.rb +0 -0
  597. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/html/attribute_sorter.rb +0 -0
  598. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/html/dispatcher.rb +0 -0
  599. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/html/fast.rb +0 -0
  600. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/html/filter.rb +0 -0
  601. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/html/pretty.rb +0 -0
  602. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/html/safe.rb +0 -0
  603. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/map.rb +0 -0
  604. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/mixins/dispatcher.rb +0 -0
  605. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/mixins/engine_dsl.rb +0 -0
  606. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/mixins/grammar_dsl.rb +0 -0
  607. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/mixins/options.rb +0 -0
  608. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/mixins/template.rb +0 -0
  609. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/parser.rb +0 -0
  610. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/templates/rails.rb +0 -0
  611. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/templates/tilt.rb +0 -0
  612. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/templates.rb +0 -0
  613. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple/utils.rb +0 -0
  614. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/lib/temple.rb +0 -0
  615. /data/bundle/ruby/{3.1.0/gems/temple-0.10.3 → 3.2.0/gems/temple-0.10.6}/temple.gemspec +0 -0
  616. /data/bundle/ruby/{3.1.0/gems/terminal-table-1.8.0 → 3.2.0/gems/terminal-table-4.0.0}/Gemfile +0 -0
  617. /data/bundle/ruby/{3.1.0/gems/terminal-table-1.8.0 → 3.2.0/gems/terminal-table-4.0.0}/LICENSE.txt +0 -0
  618. /data/bundle/ruby/{3.1.0/gems/terminal-table-1.8.0 → 3.2.0/gems/terminal-table-4.0.0}/Manifest +0 -0
  619. /data/bundle/ruby/{3.1.0/gems/terminal-table-1.8.0 → 3.2.0/gems/terminal-table-4.0.0}/Todo.rdoc +0 -0
  620. /data/bundle/ruby/{3.1.0/gems/terminal-table-1.8.0 → 3.2.0/gems/terminal-table-4.0.0}/lib/terminal-table/import.rb +0 -0
  621. /data/bundle/ruby/{3.1.0/gems/terminal-table-1.8.0 → 3.2.0/gems/terminal-table-4.0.0}/lib/terminal-table/table_helper.rb +0 -0
  622. /data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/_emacs_org.rb +0 -0
  623. /data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/_handlebars.rb +0 -0
  624. /data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/_jbuilder.rb +0 -0
  625. /data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/_org.rb +0 -0
  626. /data/bundle/ruby/{3.1.0/gems/tilt-2.4.0 → 3.2.0/gems/tilt-2.8.0}/lib/tilt/cli.rb +0 -0
@@ -0,0 +1,2989 @@
1
+ # encoding: US-ASCII
2
+ # frozen_string_literal: true
3
+ # = csv.rb -- CSV Reading and Writing
4
+ #
5
+ # Created by James Edward Gray II on 2005-10-31.
6
+ #
7
+ # See CSV for documentation.
8
+ #
9
+ # == Description
10
+ #
11
+ # Welcome to the new and improved CSV.
12
+ #
13
+ # This version of the CSV library began its life as FasterCSV. FasterCSV was
14
+ # intended as a replacement to Ruby's then standard CSV library. It was
15
+ # designed to address concerns users of that library had and it had three
16
+ # primary goals:
17
+ #
18
+ # 1. Be significantly faster than CSV while remaining a pure Ruby library.
19
+ # 2. Use a smaller and easier to maintain code base. (FasterCSV eventually
20
+ # grew larger, was also but considerably richer in features. The parsing
21
+ # core remains quite small.)
22
+ # 3. Improve on the CSV interface.
23
+ #
24
+ # Obviously, the last one is subjective. I did try to defer to the original
25
+ # interface whenever I didn't have a compelling reason to change it though, so
26
+ # hopefully this won't be too radically different.
27
+ #
28
+ # We must have met our goals because FasterCSV was renamed to CSV and replaced
29
+ # the original library as of Ruby 1.9. If you are migrating code from 1.8 or
30
+ # earlier, you may have to change your code to comply with the new interface.
31
+ #
32
+ # == What's the Different From the Old CSV?
33
+ #
34
+ # I'm sure I'll miss something, but I'll try to mention most of the major
35
+ # differences I am aware of, to help others quickly get up to speed:
36
+ #
37
+ # === \CSV Parsing
38
+ #
39
+ # * This parser is m17n aware. See CSV for full details.
40
+ # * This library has a stricter parser and will throw MalformedCSVErrors on
41
+ # problematic data.
42
+ # * This library has a less liberal idea of a line ending than CSV. What you
43
+ # set as the <tt>:row_sep</tt> is law. It can auto-detect your line endings
44
+ # though.
45
+ # * The old library returned empty lines as <tt>[nil]</tt>. This library calls
46
+ # them <tt>[]</tt>.
47
+ # * This library has a much faster parser.
48
+ #
49
+ # === Interface
50
+ #
51
+ # * CSV now uses keyword parameters to set options.
52
+ # * CSV no longer has generate_row() or parse_row().
53
+ # * The old CSV's Reader and Writer classes have been dropped.
54
+ # * CSV::open() is now more like Ruby's open().
55
+ # * CSV objects now support most standard IO methods.
56
+ # * CSV now has a new() method used to wrap objects like String and IO for
57
+ # reading and writing.
58
+ # * CSV::generate() is different from the old method.
59
+ # * CSV no longer supports partial reads. It works line-by-line.
60
+ # * CSV no longer allows the instance methods to override the separators for
61
+ # performance reasons. They must be set in the constructor.
62
+ #
63
+ # If you use this library and find yourself missing any functionality I have
64
+ # trimmed, please {let me know}[mailto:james@grayproductions.net].
65
+ #
66
+ # == Documentation
67
+ #
68
+ # See CSV for documentation.
69
+ #
70
+ # == What is CSV, really?
71
+ #
72
+ # CSV maintains a pretty strict definition of CSV taken directly from
73
+ # {the RFC}[https://www.ietf.org/rfc/rfc4180.txt]. I relax the rules in only one
74
+ # place and that is to make using this library easier. CSV will parse all valid
75
+ # CSV.
76
+ #
77
+ # What you don't want to do is to feed CSV invalid data. Because of the way the
78
+ # CSV format works, it's common for a parser to need to read until the end of
79
+ # the file to be sure a field is invalid. This consumes a lot of time and memory.
80
+ #
81
+ # Luckily, when working with invalid CSV, Ruby's built-in methods will almost
82
+ # always be superior in every way. For example, parsing non-quoted fields is as
83
+ # easy as:
84
+ #
85
+ # data.split(",")
86
+ #
87
+ # == Questions and/or Comments
88
+ #
89
+ # Feel free to email {James Edward Gray II}[mailto:james@grayproductions.net]
90
+ # with any questions.
91
+
92
+ require "forwardable"
93
+ require "date"
94
+ require "time"
95
+ require "stringio"
96
+
97
+ require_relative "csv/fields_converter"
98
+ require_relative "csv/input_record_separator"
99
+ require_relative "csv/parser"
100
+ require_relative "csv/row"
101
+ require_relative "csv/table"
102
+ require_relative "csv/writer"
103
+
104
+ # == \CSV
105
+ #
106
+ # === \CSV Data
107
+ #
108
+ # \CSV (comma-separated values) data is a text representation of a table:
109
+ # - A _row_ _separator_ delimits table rows.
110
+ # A common row separator is the newline character <tt>"\n"</tt>.
111
+ # - A _column_ _separator_ delimits fields in a row.
112
+ # A common column separator is the comma character <tt>","</tt>.
113
+ #
114
+ # This \CSV \String, with row separator <tt>"\n"</tt>
115
+ # and column separator <tt>","</tt>,
116
+ # has three rows and two columns:
117
+ # "foo,0\nbar,1\nbaz,2\n"
118
+ #
119
+ # Despite the name \CSV, a \CSV representation can use different separators.
120
+ #
121
+ # For more about tables, see the Wikipedia article
122
+ # "{Table (information)}[https://en.wikipedia.org/wiki/Table_(information)]",
123
+ # especially its section
124
+ # "{Simple table}[https://en.wikipedia.org/wiki/Table_(information)#Simple_table]"
125
+ #
126
+ # == \Class \CSV
127
+ #
128
+ # Class \CSV provides methods for:
129
+ # - Parsing \CSV data from a \String object, a \File (via its file path), or an \IO object.
130
+ # - Generating \CSV data to a \String object.
131
+ #
132
+ # To make \CSV available:
133
+ # require 'csv'
134
+ #
135
+ # All examples here assume that this has been done.
136
+ #
137
+ # == Keeping It Simple
138
+ #
139
+ # A \CSV object has dozens of instance methods that offer fine-grained control
140
+ # of parsing and generating \CSV data.
141
+ # For many needs, though, simpler approaches will do.
142
+ #
143
+ # This section summarizes the singleton methods in \CSV
144
+ # that allow you to parse and generate without explicitly
145
+ # creating \CSV objects.
146
+ # For details, follow the links.
147
+ #
148
+ # === Simple Parsing
149
+ #
150
+ # Parsing methods commonly return either of:
151
+ # - An \Array of Arrays of Strings:
152
+ # - The outer \Array is the entire "table".
153
+ # - Each inner \Array is a row.
154
+ # - Each \String is a field.
155
+ # - A CSV::Table object. For details, see
156
+ # {\CSV with Headers}[#class-CSV-label-CSV+with+Headers].
157
+ #
158
+ # ==== Parsing a \String
159
+ #
160
+ # The input to be parsed can be a string:
161
+ # string = "foo,0\nbar,1\nbaz,2\n"
162
+ #
163
+ # \Method CSV.parse returns the entire \CSV data:
164
+ # CSV.parse(string) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
165
+ #
166
+ # \Method CSV.parse_line returns only the first row:
167
+ # CSV.parse_line(string) # => ["foo", "0"]
168
+ #
169
+ # \CSV extends class \String with instance method String#parse_csv,
170
+ # which also returns only the first row:
171
+ # string.parse_csv # => ["foo", "0"]
172
+ #
173
+ # ==== Parsing Via a \File Path
174
+ #
175
+ # The input to be parsed can be in a file:
176
+ # string = "foo,0\nbar,1\nbaz,2\n"
177
+ # path = 't.csv'
178
+ # File.write(path, string)
179
+ #
180
+ # \Method CSV.read returns the entire \CSV data:
181
+ # CSV.read(path) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
182
+ #
183
+ # \Method CSV.foreach iterates, passing each row to the given block:
184
+ # CSV.foreach(path) do |row|
185
+ # p row
186
+ # end
187
+ # Output:
188
+ # ["foo", "0"]
189
+ # ["bar", "1"]
190
+ # ["baz", "2"]
191
+ #
192
+ # \Method CSV.table returns the entire \CSV data as a CSV::Table object:
193
+ # CSV.table(path) # => #<CSV::Table mode:col_or_row row_count:3>
194
+ #
195
+ # ==== Parsing from an Open \IO Stream
196
+ #
197
+ # The input to be parsed can be in an open \IO stream:
198
+ #
199
+ # \Method CSV.read returns the entire \CSV data:
200
+ # File.open(path) do |file|
201
+ # CSV.read(file)
202
+ # end # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
203
+ #
204
+ # As does method CSV.parse:
205
+ # File.open(path) do |file|
206
+ # CSV.parse(file)
207
+ # end # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
208
+ #
209
+ # \Method CSV.parse_line returns only the first row:
210
+ # File.open(path) do |file|
211
+ # CSV.parse_line(file)
212
+ # end # => ["foo", "0"]
213
+ #
214
+ # \Method CSV.foreach iterates, passing each row to the given block:
215
+ # File.open(path) do |file|
216
+ # CSV.foreach(file) do |row|
217
+ # p row
218
+ # end
219
+ # end
220
+ # Output:
221
+ # ["foo", "0"]
222
+ # ["bar", "1"]
223
+ # ["baz", "2"]
224
+ #
225
+ # \Method CSV.table returns the entire \CSV data as a CSV::Table object:
226
+ # File.open(path) do |file|
227
+ # CSV.table(file)
228
+ # end # => #<CSV::Table mode:col_or_row row_count:3>
229
+ #
230
+ # === Simple Generating
231
+ #
232
+ # \Method CSV.generate returns a \String;
233
+ # this example uses method CSV#<< to append the rows
234
+ # that are to be generated:
235
+ # output_string = CSV.generate do |csv|
236
+ # csv << ['foo', 0]
237
+ # csv << ['bar', 1]
238
+ # csv << ['baz', 2]
239
+ # end
240
+ # output_string # => "foo,0\nbar,1\nbaz,2\n"
241
+ #
242
+ # \Method CSV.generate_line returns a \String containing the single row
243
+ # constructed from an \Array:
244
+ # CSV.generate_line(['foo', '0']) # => "foo,0\n"
245
+ #
246
+ # \CSV extends class \Array with instance method <tt>Array#to_csv</tt>,
247
+ # which forms an \Array into a \String:
248
+ # ['foo', '0'].to_csv # => "foo,0\n"
249
+ #
250
+ # === "Filtering" \CSV
251
+ #
252
+ # \Method CSV.filter provides a Unix-style filter for \CSV data.
253
+ # The input data is processed to form the output data:
254
+ # in_string = "foo,0\nbar,1\nbaz,2\n"
255
+ # out_string = ''
256
+ # CSV.filter(in_string, out_string) do |row|
257
+ # row[0] = row[0].upcase
258
+ # row[1] *= 4
259
+ # end
260
+ # out_string # => "FOO,0000\nBAR,1111\nBAZ,2222\n"
261
+ #
262
+ # == \CSV Objects
263
+ #
264
+ # There are three ways to create a \CSV object:
265
+ # - \Method CSV.new returns a new \CSV object.
266
+ # - \Method CSV.instance returns a new or cached \CSV object.
267
+ # - \Method \CSV() also returns a new or cached \CSV object.
268
+ #
269
+ # === Instance Methods
270
+ #
271
+ # \CSV has three groups of instance methods:
272
+ # - Its own internally defined instance methods.
273
+ # - Methods included by module Enumerable.
274
+ # - Methods delegated to class IO. See below.
275
+ #
276
+ # ==== Delegated Methods
277
+ #
278
+ # For convenience, a CSV object will delegate to many methods in class IO.
279
+ # (A few have wrapper "guard code" in \CSV.) You may call:
280
+ # * IO#binmode
281
+ # * #binmode?
282
+ # * IO#close
283
+ # * IO#close_read
284
+ # * IO#close_write
285
+ # * IO#closed?
286
+ # * #eof
287
+ # * #eof?
288
+ # * IO#external_encoding
289
+ # * IO#fcntl
290
+ # * IO#fileno
291
+ # * #flock
292
+ # * IO#flush
293
+ # * IO#fsync
294
+ # * IO#internal_encoding
295
+ # * #ioctl
296
+ # * IO#isatty
297
+ # * #path
298
+ # * IO#pid
299
+ # * IO#pos
300
+ # * IO#pos=
301
+ # * IO#reopen
302
+ # * #rewind
303
+ # * IO#seek
304
+ # * #stat
305
+ # * IO#string
306
+ # * IO#sync
307
+ # * IO#sync=
308
+ # * IO#tell
309
+ # * #to_i
310
+ # * #to_io
311
+ # * IO#truncate
312
+ # * IO#tty?
313
+ #
314
+ # === Options
315
+ #
316
+ # The default values for options are:
317
+ # DEFAULT_OPTIONS = {
318
+ # # For both parsing and generating.
319
+ # col_sep: ",",
320
+ # row_sep: :auto,
321
+ # quote_char: '"',
322
+ # # For parsing.
323
+ # field_size_limit: nil,
324
+ # converters: nil,
325
+ # unconverted_fields: nil,
326
+ # headers: false,
327
+ # return_headers: false,
328
+ # header_converters: nil,
329
+ # skip_blanks: false,
330
+ # skip_lines: nil,
331
+ # liberal_parsing: false,
332
+ # nil_value: nil,
333
+ # empty_value: "",
334
+ # strip: false,
335
+ # # For generating.
336
+ # write_headers: nil,
337
+ # quote_empty: true,
338
+ # force_quotes: false,
339
+ # write_converters: nil,
340
+ # write_nil_value: nil,
341
+ # write_empty_value: "",
342
+ # }
343
+ #
344
+ # ==== Options for Parsing
345
+ #
346
+ # Options for parsing, described in detail below, include:
347
+ # - +row_sep+: Specifies the row separator; used to delimit rows.
348
+ # - +col_sep+: Specifies the column separator; used to delimit fields.
349
+ # - +quote_char+: Specifies the quote character; used to quote fields.
350
+ # - +field_size_limit+: Specifies the maximum field size + 1 allowed.
351
+ # Deprecated since 3.2.3. Use +max_field_size+ instead.
352
+ # - +max_field_size+: Specifies the maximum field size allowed.
353
+ # - +converters+: Specifies the field converters to be used.
354
+ # - +unconverted_fields+: Specifies whether unconverted fields are to be available.
355
+ # - +headers+: Specifies whether data contains headers,
356
+ # or specifies the headers themselves.
357
+ # - +return_headers+: Specifies whether headers are to be returned.
358
+ # - +header_converters+: Specifies the header converters to be used.
359
+ # - +skip_blanks+: Specifies whether blanks lines are to be ignored.
360
+ # - +skip_lines+: Specifies how comments lines are to be recognized.
361
+ # - +strip+: Specifies whether leading and trailing whitespace are to be
362
+ # stripped from fields. This must be compatible with +col_sep+; if it is not,
363
+ # then an +ArgumentError+ exception will be raised.
364
+ # - +liberal_parsing+: Specifies whether \CSV should attempt to parse
365
+ # non-compliant data.
366
+ # - +nil_value+: Specifies the object that is to be substituted for each null (no-text) field.
367
+ # - +empty_value+: Specifies the object that is to be substituted for each empty field.
368
+ #
369
+ # :include: ../doc/csv/options/common/row_sep.rdoc
370
+ #
371
+ # :include: ../doc/csv/options/common/col_sep.rdoc
372
+ #
373
+ # :include: ../doc/csv/options/common/quote_char.rdoc
374
+ #
375
+ # :include: ../doc/csv/options/parsing/field_size_limit.rdoc
376
+ #
377
+ # :include: ../doc/csv/options/parsing/converters.rdoc
378
+ #
379
+ # :include: ../doc/csv/options/parsing/unconverted_fields.rdoc
380
+ #
381
+ # :include: ../doc/csv/options/parsing/headers.rdoc
382
+ #
383
+ # :include: ../doc/csv/options/parsing/return_headers.rdoc
384
+ #
385
+ # :include: ../doc/csv/options/parsing/header_converters.rdoc
386
+ #
387
+ # :include: ../doc/csv/options/parsing/skip_blanks.rdoc
388
+ #
389
+ # :include: ../doc/csv/options/parsing/skip_lines.rdoc
390
+ #
391
+ # :include: ../doc/csv/options/parsing/strip.rdoc
392
+ #
393
+ # :include: ../doc/csv/options/parsing/liberal_parsing.rdoc
394
+ #
395
+ # :include: ../doc/csv/options/parsing/nil_value.rdoc
396
+ #
397
+ # :include: ../doc/csv/options/parsing/empty_value.rdoc
398
+ #
399
+ # ==== Options for Generating
400
+ #
401
+ # Options for generating, described in detail below, include:
402
+ # - +row_sep+: Specifies the row separator; used to delimit rows.
403
+ # - +col_sep+: Specifies the column separator; used to delimit fields.
404
+ # - +quote_char+: Specifies the quote character; used to quote fields.
405
+ # - +write_headers+: Specifies whether headers are to be written.
406
+ # - +force_quotes+: Specifies whether each output field is to be quoted.
407
+ # - +quote_empty+: Specifies whether each empty output field is to be quoted.
408
+ # - +write_converters+: Specifies the field converters to be used in writing.
409
+ # - +write_nil_value+: Specifies the object that is to be substituted for each +nil+-valued field.
410
+ # - +write_empty_value+: Specifies the object that is to be substituted for each empty field.
411
+ #
412
+ # :include: ../doc/csv/options/common/row_sep.rdoc
413
+ #
414
+ # :include: ../doc/csv/options/common/col_sep.rdoc
415
+ #
416
+ # :include: ../doc/csv/options/common/quote_char.rdoc
417
+ #
418
+ # :include: ../doc/csv/options/generating/write_headers.rdoc
419
+ #
420
+ # :include: ../doc/csv/options/generating/force_quotes.rdoc
421
+ #
422
+ # :include: ../doc/csv/options/generating/quote_empty.rdoc
423
+ #
424
+ # :include: ../doc/csv/options/generating/write_converters.rdoc
425
+ #
426
+ # :include: ../doc/csv/options/generating/write_nil_value.rdoc
427
+ #
428
+ # :include: ../doc/csv/options/generating/write_empty_value.rdoc
429
+ #
430
+ # === \CSV with Headers
431
+ #
432
+ # CSV allows to specify column names of CSV file, whether they are in data, or
433
+ # provided separately. If headers are specified, reading methods return an instance
434
+ # of CSV::Table, consisting of CSV::Row.
435
+ #
436
+ # # Headers are part of data
437
+ # data = CSV.parse(<<~ROWS, headers: true)
438
+ # Name,Department,Salary
439
+ # Bob,Engineering,1000
440
+ # Jane,Sales,2000
441
+ # John,Management,5000
442
+ # ROWS
443
+ #
444
+ # data.class #=> CSV::Table
445
+ # data.first #=> #<CSV::Row "Name":"Bob" "Department":"Engineering" "Salary":"1000">
446
+ # data.first.to_h #=> {"Name"=>"Bob", "Department"=>"Engineering", "Salary"=>"1000"}
447
+ #
448
+ # # Headers provided by developer
449
+ # data = CSV.parse('Bob,Engineering,1000', headers: %i[name department salary])
450
+ # data.first #=> #<CSV::Row name:"Bob" department:"Engineering" salary:"1000">
451
+ #
452
+ # === \Converters
453
+ #
454
+ # By default, each value (field or header) parsed by \CSV is formed into a \String.
455
+ # You can use a _field_ _converter_ or _header_ _converter_
456
+ # to intercept and modify the parsed values:
457
+ # - See {Field Converters}[#class-CSV-label-Field+Converters].
458
+ # - See {Header Converters}[#class-CSV-label-Header+Converters].
459
+ #
460
+ # Also by default, each value to be written during generation is written 'as-is'.
461
+ # You can use a _write_ _converter_ to modify values before writing.
462
+ # - See {Write Converters}[#class-CSV-label-Write+Converters].
463
+ #
464
+ # ==== Specifying \Converters
465
+ #
466
+ # You can specify converters for parsing or generating in the +options+
467
+ # argument to various \CSV methods:
468
+ # - Option +converters+ for converting parsed field values.
469
+ # - Option +header_converters+ for converting parsed header values.
470
+ # - Option +write_converters+ for converting values to be written (generated).
471
+ #
472
+ # There are three forms for specifying converters:
473
+ # - A converter proc: executable code to be used for conversion.
474
+ # - A converter name: the name of a stored converter.
475
+ # - A converter list: an array of converter procs, converter names, and converter lists.
476
+ #
477
+ # ===== Converter Procs
478
+ #
479
+ # This converter proc, +strip_converter+, accepts a value +field+
480
+ # and returns <tt>field.strip</tt>:
481
+ # strip_converter = proc {|field| field.strip }
482
+ # In this call to <tt>CSV.parse</tt>,
483
+ # the keyword argument <tt>converters: string_converter</tt>
484
+ # specifies that:
485
+ # - \Proc +string_converter+ is to be called for each parsed field.
486
+ # - The converter's return value is to replace the +field+ value.
487
+ # Example:
488
+ # string = " foo , 0 \n bar , 1 \n baz , 2 \n"
489
+ # array = CSV.parse(string, converters: strip_converter)
490
+ # array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
491
+ #
492
+ # A converter proc can receive a second argument, +field_info+,
493
+ # that contains details about the field.
494
+ # This modified +strip_converter+ displays its arguments:
495
+ # strip_converter = proc do |field, field_info|
496
+ # p [field, field_info]
497
+ # field.strip
498
+ # end
499
+ # string = " foo , 0 \n bar , 1 \n baz , 2 \n"
500
+ # array = CSV.parse(string, converters: strip_converter)
501
+ # array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
502
+ # Output:
503
+ # [" foo ", #<struct CSV::FieldInfo index=0, line=1, header=nil>]
504
+ # [" 0 ", #<struct CSV::FieldInfo index=1, line=1, header=nil>]
505
+ # [" bar ", #<struct CSV::FieldInfo index=0, line=2, header=nil>]
506
+ # [" 1 ", #<struct CSV::FieldInfo index=1, line=2, header=nil>]
507
+ # [" baz ", #<struct CSV::FieldInfo index=0, line=3, header=nil>]
508
+ # [" 2 ", #<struct CSV::FieldInfo index=1, line=3, header=nil>]
509
+ # Each CSV::FieldInfo object shows:
510
+ # - The 0-based field index.
511
+ # - The 1-based line index.
512
+ # - The field header, if any.
513
+ #
514
+ # ===== Stored \Converters
515
+ #
516
+ # A converter may be given a name and stored in a structure where
517
+ # the parsing methods can find it by name.
518
+ #
519
+ # The storage structure for field converters is the \Hash CSV::Converters.
520
+ # It has several built-in converter procs:
521
+ # - <tt>:integer</tt>: converts each \String-embedded integer into a true \Integer.
522
+ # - <tt>:float</tt>: converts each \String-embedded float into a true \Float.
523
+ # - <tt>:date</tt>: converts each \String-embedded date into a true \Date.
524
+ # - <tt>:date_time</tt>: converts each \String-embedded date-time into a true \DateTime
525
+ # - <tt>:time</tt>: converts each \String-embedded time into a true \Time
526
+ # .
527
+ # This example creates a converter proc, then stores it:
528
+ # strip_converter = proc {|field| field.strip }
529
+ # CSV::Converters[:strip] = strip_converter
530
+ # Then the parsing method call can refer to the converter
531
+ # by its name, <tt>:strip</tt>:
532
+ # string = " foo , 0 \n bar , 1 \n baz , 2 \n"
533
+ # array = CSV.parse(string, converters: :strip)
534
+ # array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
535
+ #
536
+ # The storage structure for header converters is the \Hash CSV::HeaderConverters,
537
+ # which works in the same way.
538
+ # It also has built-in converter procs:
539
+ # - <tt>:downcase</tt>: Downcases each header.
540
+ # - <tt>:symbol</tt>: Converts each header to a \Symbol.
541
+ #
542
+ # There is no such storage structure for write headers.
543
+ #
544
+ # In order for the parsing methods to access stored converters in non-main-Ractors, the
545
+ # storage structure must be made shareable first.
546
+ # Therefore, <tt>Ractor.make_shareable(CSV::Converters)</tt> and
547
+ # <tt>Ractor.make_shareable(CSV::HeaderConverters)</tt> must be called before the creation
548
+ # of Ractors that use the converters stored in these structures. (Since making the storage
549
+ # structures shareable involves freezing them, any custom converters that are to be used
550
+ # must be added first.)
551
+ #
552
+ # ===== Converter Lists
553
+ #
554
+ # A _converter_ _list_ is an \Array that may include any assortment of:
555
+ # - Converter procs.
556
+ # - Names of stored converters.
557
+ # - Nested converter lists.
558
+ #
559
+ # Examples:
560
+ # numeric_converters = [:integer, :float]
561
+ # date_converters = [:date, :date_time]
562
+ # [numeric_converters, strip_converter]
563
+ # [strip_converter, date_converters, :float]
564
+ #
565
+ # Like a converter proc, a converter list may be named and stored in either
566
+ # \CSV::Converters or CSV::HeaderConverters:
567
+ # CSV::Converters[:custom] = [strip_converter, date_converters, :float]
568
+ # CSV::HeaderConverters[:custom] = [:downcase, :symbol]
569
+ #
570
+ # There are two built-in converter lists:
571
+ # CSV::Converters[:numeric] # => [:integer, :float]
572
+ # CSV::Converters[:all] # => [:date_time, :numeric]
573
+ #
574
+ # ==== Field \Converters
575
+ #
576
+ # With no conversion, all parsed fields in all rows become Strings:
577
+ # string = "foo,0\nbar,1\nbaz,2\n"
578
+ # ary = CSV.parse(string)
579
+ # ary # => # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
580
+ #
581
+ # When you specify a field converter, each parsed field is passed to the converter;
582
+ # its return value becomes the stored value for the field.
583
+ # A converter might, for example, convert an integer embedded in a \String
584
+ # into a true \Integer.
585
+ # (In fact, that's what built-in field converter +:integer+ does.)
586
+ #
587
+ # There are three ways to use field \converters.
588
+ #
589
+ # - Using option {converters}[#class-CSV-label-Option+converters] with a parsing method:
590
+ # ary = CSV.parse(string, converters: :integer)
591
+ # ary # => [0, 1, 2] # => [["foo", 0], ["bar", 1], ["baz", 2]]
592
+ # - Using option {converters}[#class-CSV-label-Option+converters] with a new \CSV instance:
593
+ # csv = CSV.new(string, converters: :integer)
594
+ # # Field converters in effect:
595
+ # csv.converters # => [:integer]
596
+ # csv.read # => [["foo", 0], ["bar", 1], ["baz", 2]]
597
+ # - Using method #convert to add a field converter to a \CSV instance:
598
+ # csv = CSV.new(string)
599
+ # # Add a converter.
600
+ # csv.convert(:integer)
601
+ # csv.converters # => [:integer]
602
+ # csv.read # => [["foo", 0], ["bar", 1], ["baz", 2]]
603
+ #
604
+ # Installing a field converter does not affect already-read rows:
605
+ # csv = CSV.new(string)
606
+ # csv.shift # => ["foo", "0"]
607
+ # # Add a converter.
608
+ # csv.convert(:integer)
609
+ # csv.converters # => [:integer]
610
+ # csv.read # => [["bar", 1], ["baz", 2]]
611
+ #
612
+ # There are additional built-in \converters, and custom \converters are also supported.
613
+ #
614
+ # ===== Built-In Field \Converters
615
+ #
616
+ # The built-in field converters are in \Hash CSV::Converters:
617
+ # - Each key is a field converter name.
618
+ # - Each value is one of:
619
+ # - A \Proc field converter.
620
+ # - An \Array of field converter names.
621
+ #
622
+ # Display:
623
+ # CSV::Converters.each_pair do |name, value|
624
+ # if value.kind_of?(Proc)
625
+ # p [name, value.class]
626
+ # else
627
+ # p [name, value]
628
+ # end
629
+ # end
630
+ # Output:
631
+ # [:integer, Proc]
632
+ # [:float, Proc]
633
+ # [:numeric, [:integer, :float]]
634
+ # [:date, Proc]
635
+ # [:date_time, Proc]
636
+ # [:time, Proc]
637
+ # [:all, [:date_time, :numeric]]
638
+ #
639
+ # Each of these converters transcodes values to UTF-8 before attempting conversion.
640
+ # If a value cannot be transcoded to UTF-8 the conversion will
641
+ # fail and the value will remain unconverted.
642
+ #
643
+ # Converter +:integer+ converts each field that Integer() accepts:
644
+ # data = '0,1,2,x'
645
+ # # Without the converter
646
+ # csv = CSV.parse_line(data)
647
+ # csv # => ["0", "1", "2", "x"]
648
+ # # With the converter
649
+ # csv = CSV.parse_line(data, converters: :integer)
650
+ # csv # => [0, 1, 2, "x"]
651
+ #
652
+ # Converter +:float+ converts each field that Float() accepts:
653
+ # data = '1.0,3.14159,x'
654
+ # # Without the converter
655
+ # csv = CSV.parse_line(data)
656
+ # csv # => ["1.0", "3.14159", "x"]
657
+ # # With the converter
658
+ # csv = CSV.parse_line(data, converters: :float)
659
+ # csv # => [1.0, 3.14159, "x"]
660
+ #
661
+ # Converter +:numeric+ converts with both +:integer+ and +:float+..
662
+ #
663
+ # Converter +:date+ converts each field that Date::parse accepts:
664
+ # data = '2001-02-03,x'
665
+ # # Without the converter
666
+ # csv = CSV.parse_line(data)
667
+ # csv # => ["2001-02-03", "x"]
668
+ # # With the converter
669
+ # csv = CSV.parse_line(data, converters: :date)
670
+ # csv # => [#<Date: 2001-02-03 ((2451944j,0s,0n),+0s,2299161j)>, "x"]
671
+ #
672
+ # Converter +:date_time+ converts each field that DateTime::parse accepts:
673
+ # data = '2020-05-07T14:59:00-05:00,x'
674
+ # # Without the converter
675
+ # csv = CSV.parse_line(data)
676
+ # csv # => ["2020-05-07T14:59:00-05:00", "x"]
677
+ # # With the converter
678
+ # csv = CSV.parse_line(data, converters: :date_time)
679
+ # csv # => [#<DateTime: 2020-05-07T14:59:00-05:00 ((2458977j,71940s,0n),-18000s,2299161j)>, "x"]
680
+ #
681
+ # Converter +time+ converts each field that Time::parse accepts:
682
+ # data = '2020-05-07T14:59:00-05:00,x'
683
+ # # Without the converter
684
+ # csv = CSV.parse_line(data)
685
+ # csv # => ["2020-05-07T14:59:00-05:00", "x"]
686
+ # # With the converter
687
+ # csv = CSV.parse_line(data, converters: :time)
688
+ # csv # => [2020-05-07 14:59:00 -0500, "x"]
689
+ #
690
+ # Converter +:numeric+ converts with both +:date_time+ and +:numeric+..
691
+ #
692
+ # As seen above, method #convert adds \converters to a \CSV instance,
693
+ # and method #converters returns an \Array of the \converters in effect:
694
+ # csv = CSV.new('0,1,2')
695
+ # csv.converters # => []
696
+ # csv.convert(:integer)
697
+ # csv.converters # => [:integer]
698
+ # csv.convert(:date)
699
+ # csv.converters # => [:integer, :date]
700
+ #
701
+ # ===== Custom Field \Converters
702
+ #
703
+ # You can define a custom field converter:
704
+ # strip_converter = proc {|field| field.strip }
705
+ # string = " foo , 0 \n bar , 1 \n baz , 2 \n"
706
+ # array = CSV.parse(string, converters: strip_converter)
707
+ # array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
708
+ # You can register the converter in \Converters \Hash,
709
+ # which allows you to refer to it by name:
710
+ # CSV::Converters[:strip] = strip_converter
711
+ # string = " foo , 0 \n bar , 1 \n baz , 2 \n"
712
+ # array = CSV.parse(string, converters: :strip)
713
+ # array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
714
+ #
715
+ # ==== Header \Converters
716
+ #
717
+ # Header converters operate only on headers (and not on other rows).
718
+ #
719
+ # There are three ways to use header \converters;
720
+ # these examples use built-in header converter +:downcase+,
721
+ # which downcases each parsed header.
722
+ #
723
+ # - Option +header_converters+ with a singleton parsing method:
724
+ # string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2"
725
+ # tbl = CSV.parse(string, headers: true, header_converters: :downcase)
726
+ # tbl.class # => CSV::Table
727
+ # tbl.headers # => ["name", "count"]
728
+ #
729
+ # - Option +header_converters+ with a new \CSV instance:
730
+ # csv = CSV.new(string, header_converters: :downcase)
731
+ # # Header converters in effect:
732
+ # csv.header_converters # => [:downcase]
733
+ # tbl = CSV.parse(string, headers: true)
734
+ # tbl.headers # => ["Name", "Count"]
735
+ #
736
+ # - Method #header_convert adds a header converter to a \CSV instance:
737
+ # csv = CSV.new(string)
738
+ # # Add a header converter.
739
+ # csv.header_convert(:downcase)
740
+ # csv.header_converters # => [:downcase]
741
+ # tbl = CSV.parse(string, headers: true)
742
+ # tbl.headers # => ["Name", "Count"]
743
+ #
744
+ # ===== Built-In Header \Converters
745
+ #
746
+ # The built-in header \converters are in \Hash CSV::HeaderConverters.
747
+ # The keys there are the names of the \converters:
748
+ # CSV::HeaderConverters.keys # => [:downcase, :symbol]
749
+ #
750
+ # Converter +:downcase+ converts each header by downcasing it:
751
+ # string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2"
752
+ # tbl = CSV.parse(string, headers: true, header_converters: :downcase)
753
+ # tbl.class # => CSV::Table
754
+ # tbl.headers # => ["name", "count"]
755
+ #
756
+ # Converter +:symbol+ converts each header by making it into a \Symbol:
757
+ # string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2"
758
+ # tbl = CSV.parse(string, headers: true, header_converters: :symbol)
759
+ # tbl.headers # => [:name, :count]
760
+ # Details:
761
+ # - Strips leading and trailing whitespace.
762
+ # - Downcases the header.
763
+ # - Replaces embedded spaces with underscores.
764
+ # - Removes non-word characters.
765
+ # - Makes the string into a \Symbol.
766
+ #
767
+ # ===== Custom Header \Converters
768
+ #
769
+ # You can define a custom header converter:
770
+ # upcase_converter = proc {|header| header.upcase }
771
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
772
+ # table = CSV.parse(string, headers: true, header_converters: upcase_converter)
773
+ # table # => #<CSV::Table mode:col_or_row row_count:4>
774
+ # table.headers # => ["NAME", "VALUE"]
775
+ # You can register the converter in \HeaderConverters \Hash,
776
+ # which allows you to refer to it by name:
777
+ # CSV::HeaderConverters[:upcase] = upcase_converter
778
+ # table = CSV.parse(string, headers: true, header_converters: :upcase)
779
+ # table # => #<CSV::Table mode:col_or_row row_count:4>
780
+ # table.headers # => ["NAME", "VALUE"]
781
+ #
782
+ # ===== Write \Converters
783
+ #
784
+ # When you specify a write converter for generating \CSV,
785
+ # each field to be written is passed to the converter;
786
+ # its return value becomes the new value for the field.
787
+ # A converter might, for example, strip whitespace from a field.
788
+ #
789
+ # Using no write converter (all fields unmodified):
790
+ # output_string = CSV.generate do |csv|
791
+ # csv << [' foo ', 0]
792
+ # csv << [' bar ', 1]
793
+ # csv << [' baz ', 2]
794
+ # end
795
+ # output_string # => " foo ,0\n bar ,1\n baz ,2\n"
796
+ # Using option +write_converters+ with two custom write converters:
797
+ # strip_converter = proc {|field| field.respond_to?(:strip) ? field.strip : field }
798
+ # upcase_converter = proc {|field| field.respond_to?(:upcase) ? field.upcase : field }
799
+ # write_converters = [strip_converter, upcase_converter]
800
+ # output_string = CSV.generate(write_converters: write_converters) do |csv|
801
+ # csv << [' foo ', 0]
802
+ # csv << [' bar ', 1]
803
+ # csv << [' baz ', 2]
804
+ # end
805
+ # output_string # => "FOO,0\nBAR,1\nBAZ,2\n"
806
+ #
807
+ # === Character Encodings (M17n or Multilingualization)
808
+ #
809
+ # This new CSV parser is m17n savvy. The parser works in the Encoding of the IO
810
+ # or String object being read from or written to. Your data is never transcoded
811
+ # (unless you ask Ruby to transcode it for you) and will literally be parsed in
812
+ # the Encoding it is in. Thus CSV will return Arrays or Rows of Strings in the
813
+ # Encoding of your data. This is accomplished by transcoding the parser itself
814
+ # into your Encoding.
815
+ #
816
+ # Some transcoding must take place, of course, to accomplish this multiencoding
817
+ # support. For example, <tt>:col_sep</tt>, <tt>:row_sep</tt>, and
818
+ # <tt>:quote_char</tt> must be transcoded to match your data. Hopefully this
819
+ # makes the entire process feel transparent, since CSV's defaults should just
820
+ # magically work for your data. However, you can set these values manually in
821
+ # the target Encoding to avoid the translation.
822
+ #
823
+ # It's also important to note that while all of CSV's core parser is now
824
+ # Encoding agnostic, some features are not. For example, the built-in
825
+ # converters will try to transcode data to UTF-8 before making conversions.
826
+ # Again, you can provide custom converters that are aware of your Encodings to
827
+ # avoid this translation. It's just too hard for me to support native
828
+ # conversions in all of Ruby's Encodings.
829
+ #
830
+ # Anyway, the practical side of this is simple: make sure IO and String objects
831
+ # passed into CSV have the proper Encoding set and everything should just work.
832
+ # CSV methods that allow you to open IO objects (CSV::foreach(), CSV::open(),
833
+ # CSV::read(), and CSV::readlines()) do allow you to specify the Encoding.
834
+ #
835
+ # One minor exception comes when generating CSV into a String with an Encoding
836
+ # that is not ASCII compatible. There's no existing data for CSV to use to
837
+ # prepare itself and thus you will probably need to manually specify the desired
838
+ # Encoding for most of those cases. It will try to guess using the fields in a
839
+ # row of output though, when using CSV::generate_line() or Array#to_csv().
840
+ #
841
+ # I try to point out any other Encoding issues in the documentation of methods
842
+ # as they come up.
843
+ #
844
+ # This has been tested to the best of my ability with all non-"dummy" Encodings
845
+ # Ruby ships with. However, it is brave new code and may have some bugs.
846
+ # Please feel free to {report}[mailto:james@grayproductions.net] any issues you
847
+ # find with it.
848
+ #
849
+ class CSV
850
+
851
+ # The error thrown when the parser encounters illegal CSV formatting.
852
+ class MalformedCSVError < RuntimeError
853
+ attr_reader :line_number
854
+ alias_method :lineno, :line_number
855
+ def initialize(message, line_number)
856
+ @line_number = line_number
857
+ super("#{message} in line #{line_number}.")
858
+ end
859
+ end
860
+
861
+ # The error thrown when the parser encounters invalid encoding in CSV.
862
+ class InvalidEncodingError < MalformedCSVError
863
+ attr_reader :encoding
864
+ def initialize(encoding, line_number)
865
+ @encoding = encoding
866
+ super("Invalid byte sequence in #{encoding}", line_number)
867
+ end
868
+ end
869
+
870
+ #
871
+ # A FieldInfo Struct contains details about a field's position in the data
872
+ # source it was read from. CSV will pass this Struct to some blocks that make
873
+ # decisions based on field structure. See CSV.convert_fields() for an
874
+ # example.
875
+ #
876
+ # <b><tt>index</tt></b>:: The zero-based index of the field in its row.
877
+ # <b><tt>line</tt></b>:: The line of the data source this row is from.
878
+ # <b><tt>header</tt></b>:: The header for the column, when available.
879
+ # <b><tt>quoted?</tt></b>:: True or false, whether the original value is quoted or not.
880
+ #
881
+ FieldInfo = Struct.new(:index, :line, :header, :quoted?)
882
+
883
+ # A Regexp used to find and convert some common Date formats.
884
+ DateMatcher = / \A(?: (\w+,?\s+)?\w+\s+\d{1,2},?\s+\d{2,4} |
885
+ \d{4}-\d{2}-\d{2} )\z /x
886
+ # A Regexp used to find and convert some common (Date)Time formats.
887
+ DateTimeMatcher =
888
+ / \A(?: (\w+,?\s+)?\w+\s+\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2},?\s+\d{2,4} |
889
+ # ISO-8601 and RFC-3339 (space instead of T) recognized by (Date)Time.parse
890
+ \d{4}-\d{2}-\d{2}
891
+ (?:[T\s]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?(?:[+-]\d{2}(?::\d{2})|Z)?)?)?
892
+ )\z /x
893
+
894
+ # The encoding used by all converters.
895
+ ConverterEncoding = Encoding.find("UTF-8")
896
+
897
+ # A \Hash containing the names and \Procs for the built-in field converters.
898
+ # See {Built-In Field Converters}[#class-CSV-label-Built-In+Field+Converters].
899
+ #
900
+ # This \Hash is intentionally left unfrozen, and may be extended with
901
+ # custom field converters.
902
+ # See {Custom Field Converters}[#class-CSV-label-Custom+Field+Converters].
903
+ Converters = {
904
+ integer: lambda { |f|
905
+ Integer(f.encode(ConverterEncoding)) rescue f
906
+ },
907
+ float: lambda { |f|
908
+ Float(f.encode(ConverterEncoding)) rescue f
909
+ },
910
+ numeric: [:integer, :float],
911
+ date: lambda { |f|
912
+ begin
913
+ e = f.encode(ConverterEncoding)
914
+ e.match?(DateMatcher) ? Date.parse(e) : f
915
+ rescue # encoding conversion or date parse errors
916
+ f
917
+ end
918
+ },
919
+ date_time: lambda { |f|
920
+ begin
921
+ e = f.encode(ConverterEncoding)
922
+ e.match?(DateTimeMatcher) ? DateTime.parse(e) : f
923
+ rescue # encoding conversion or date parse errors
924
+ f
925
+ end
926
+ },
927
+ time: lambda { |f|
928
+ begin
929
+ e = f.encode(ConverterEncoding)
930
+ e.match?(DateTimeMatcher) ? Time.parse(e) : f
931
+ rescue # encoding conversion or parse errors
932
+ f
933
+ end
934
+ },
935
+ all: [:date_time, :numeric],
936
+ }
937
+
938
+ # A \Hash containing the names and \Procs for the built-in header converters.
939
+ # See {Built-In Header Converters}[#class-CSV-label-Built-In+Header+Converters].
940
+ #
941
+ # This \Hash is intentionally left unfrozen, and may be extended with
942
+ # custom field converters.
943
+ # See {Custom Header Converters}[#class-CSV-label-Custom+Header+Converters].
944
+ HeaderConverters = {
945
+ downcase: lambda { |h| h.encode(ConverterEncoding).downcase },
946
+ symbol: lambda { |h|
947
+ h.encode(ConverterEncoding).downcase.gsub(/[^\s\w]+/, "").strip.
948
+ gsub(/\s+/, "_").to_sym
949
+ },
950
+ symbol_raw: lambda { |h| h.encode(ConverterEncoding).to_sym }
951
+ }
952
+
953
+ # Default values for method options.
954
+ DEFAULT_OPTIONS = {
955
+ # For both parsing and generating.
956
+ col_sep: ",",
957
+ row_sep: :auto,
958
+ quote_char: '"',
959
+ # For parsing.
960
+ field_size_limit: nil,
961
+ max_field_size: nil,
962
+ converters: nil,
963
+ unconverted_fields: nil,
964
+ headers: false,
965
+ return_headers: false,
966
+ header_converters: nil,
967
+ skip_blanks: false,
968
+ skip_lines: nil,
969
+ liberal_parsing: false,
970
+ nil_value: nil,
971
+ empty_value: "",
972
+ strip: false,
973
+ # For generating.
974
+ write_headers: nil,
975
+ quote_empty: true,
976
+ force_quotes: false,
977
+ write_converters: nil,
978
+ write_nil_value: nil,
979
+ write_empty_value: "",
980
+ }.freeze
981
+
982
+ class << self
983
+ # :call-seq:
984
+ # instance(string, **options)
985
+ # instance(io = $stdout, **options)
986
+ # instance(string, **options) {|csv| ... }
987
+ # instance(io = $stdout, **options) {|csv| ... }
988
+ #
989
+ # Creates or retrieves cached \CSV objects.
990
+ # For arguments and options, see CSV.new.
991
+ #
992
+ # This API is not Ractor-safe.
993
+ #
994
+ # ---
995
+ #
996
+ # With no block given, returns a \CSV object.
997
+ #
998
+ # The first call to +instance+ creates and caches a \CSV object:
999
+ # s0 = 's0'
1000
+ # csv0 = CSV.instance(s0)
1001
+ # csv0.class # => CSV
1002
+ #
1003
+ # Subsequent calls to +instance+ with that _same_ +string+ or +io+
1004
+ # retrieve that same cached object:
1005
+ # csv1 = CSV.instance(s0)
1006
+ # csv1.class # => CSV
1007
+ # csv1.equal?(csv0) # => true # Same CSV object
1008
+ #
1009
+ # A subsequent call to +instance+ with a _different_ +string+ or +io+
1010
+ # creates and caches a _different_ \CSV object.
1011
+ # s1 = 's1'
1012
+ # csv2 = CSV.instance(s1)
1013
+ # csv2.equal?(csv0) # => false # Different CSV object
1014
+ #
1015
+ # All the cached objects remains available:
1016
+ # csv3 = CSV.instance(s0)
1017
+ # csv3.equal?(csv0) # true # Same CSV object
1018
+ # csv4 = CSV.instance(s1)
1019
+ # csv4.equal?(csv2) # true # Same CSV object
1020
+ #
1021
+ # ---
1022
+ #
1023
+ # When a block is given, calls the block with the created or retrieved
1024
+ # \CSV object; returns the block's return value:
1025
+ # CSV.instance(s0) {|csv| :foo } # => :foo
1026
+ def instance(data = $stdout, **options)
1027
+ # create a _signature_ for this method call, data object and options
1028
+ sig = [data.object_id] +
1029
+ options.values_at(*DEFAULT_OPTIONS.keys)
1030
+
1031
+ # fetch or create the instance for this signature
1032
+ @@instances ||= Hash.new
1033
+ instance = (@@instances[sig] ||= new(data, **options))
1034
+
1035
+ if block_given?
1036
+ yield instance # run block, if given, returning result
1037
+ else
1038
+ instance # or return the instance
1039
+ end
1040
+ end
1041
+
1042
+ # :call-seq:
1043
+ # filter(in_string_or_io, **options) {|row| ... } -> array_of_arrays or csv_table
1044
+ # filter(in_string_or_io, out_string_or_io, **options) {|row| ... } -> array_of_arrays or csv_table
1045
+ # filter(**options) {|row| ... } -> array_of_arrays or csv_table
1046
+ #
1047
+ # - Parses \CSV from a source (\String, \IO stream, or ARGF).
1048
+ # - Calls the given block with each parsed row:
1049
+ # - Without headers, each row is an \Array.
1050
+ # - With headers, each row is a CSV::Row.
1051
+ # - Generates \CSV to an output (\String, \IO stream, or STDOUT).
1052
+ # - Returns the parsed source:
1053
+ # - Without headers, an \Array of \Arrays.
1054
+ # - With headers, a CSV::Table.
1055
+ #
1056
+ # When +in_string_or_io+ is given, but not +out_string_or_io+,
1057
+ # parses from the given +in_string_or_io+
1058
+ # and generates to STDOUT.
1059
+ #
1060
+ # \String input without headers:
1061
+ #
1062
+ # in_string = "foo,0\nbar,1\nbaz,2"
1063
+ # CSV.filter(in_string) do |row|
1064
+ # row[0].upcase!
1065
+ # row[1] = - row[1].to_i
1066
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
1067
+ #
1068
+ # Output (to STDOUT):
1069
+ #
1070
+ # FOO,0
1071
+ # BAR,-1
1072
+ # BAZ,-2
1073
+ #
1074
+ # \String input with headers:
1075
+ #
1076
+ # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
1077
+ # CSV.filter(in_string, headers: true) do |row|
1078
+ # row[0].upcase!
1079
+ # row[1] = - row[1].to_i
1080
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
1081
+ #
1082
+ # Output (to STDOUT):
1083
+ #
1084
+ # Name,Value
1085
+ # FOO,0
1086
+ # BAR,-1
1087
+ # BAZ,-2
1088
+ #
1089
+ # \IO stream input without headers:
1090
+ #
1091
+ # File.write('t.csv', "foo,0\nbar,1\nbaz,2")
1092
+ # File.open('t.csv') do |in_io|
1093
+ # CSV.filter(in_io) do |row|
1094
+ # row[0].upcase!
1095
+ # row[1] = - row[1].to_i
1096
+ # end
1097
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
1098
+ #
1099
+ # Output (to STDOUT):
1100
+ #
1101
+ # FOO,0
1102
+ # BAR,-1
1103
+ # BAZ,-2
1104
+ #
1105
+ # \IO stream input with headers:
1106
+ #
1107
+ # File.write('t.csv', "Name,Value\nfoo,0\nbar,1\nbaz,2")
1108
+ # File.open('t.csv') do |in_io|
1109
+ # CSV.filter(in_io, headers: true) do |row|
1110
+ # row[0].upcase!
1111
+ # row[1] = - row[1].to_i
1112
+ # end
1113
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
1114
+ #
1115
+ # Output (to STDOUT):
1116
+ #
1117
+ # Name,Value
1118
+ # FOO,0
1119
+ # BAR,-1
1120
+ # BAZ,-2
1121
+ #
1122
+ # When both +in_string_or_io+ and +out_string_or_io+ are given,
1123
+ # parses from +in_string_or_io+ and generates to +out_string_or_io+.
1124
+ #
1125
+ # \String output without headers:
1126
+ #
1127
+ # in_string = "foo,0\nbar,1\nbaz,2"
1128
+ # out_string = ''
1129
+ # CSV.filter(in_string, out_string) do |row|
1130
+ # row[0].upcase!
1131
+ # row[1] = - row[1].to_i
1132
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
1133
+ # out_string # => "FOO,0\nBAR,-1\nBAZ,-2\n"
1134
+ #
1135
+ # \String output with headers:
1136
+ #
1137
+ # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
1138
+ # out_string = ''
1139
+ # CSV.filter(in_string, out_string, headers: true) do |row|
1140
+ # row[0].upcase!
1141
+ # row[1] = - row[1].to_i
1142
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
1143
+ # out_string # => "Name,Value\nFOO,0\nBAR,-1\nBAZ,-2\n"
1144
+ #
1145
+ # \IO stream output without headers:
1146
+ #
1147
+ # in_string = "foo,0\nbar,1\nbaz,2"
1148
+ # File.open('t.csv', 'w') do |out_io|
1149
+ # CSV.filter(in_string, out_io) do |row|
1150
+ # row[0].upcase!
1151
+ # row[1] = - row[1].to_i
1152
+ # end
1153
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
1154
+ # File.read('t.csv') # => "FOO,0\nBAR,-1\nBAZ,-2\n"
1155
+ #
1156
+ # \IO stream output with headers:
1157
+ #
1158
+ # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
1159
+ # File.open('t.csv', 'w') do |out_io|
1160
+ # CSV.filter(in_string, out_io, headers: true) do |row|
1161
+ # row[0].upcase!
1162
+ # row[1] = - row[1].to_i
1163
+ # end
1164
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
1165
+ # File.read('t.csv') # => "Name,Value\nFOO,0\nBAR,-1\nBAZ,-2\n"
1166
+ #
1167
+ # When neither +in_string_or_io+ nor +out_string_or_io+ given,
1168
+ # parses from {ARGF}[rdoc-ref:ARGF]
1169
+ # and generates to STDOUT.
1170
+ #
1171
+ # Without headers:
1172
+ #
1173
+ # # Put Ruby code into a file.
1174
+ # ruby = <<-EOT
1175
+ # require 'csv'
1176
+ # CSV.filter do |row|
1177
+ # row[0].upcase!
1178
+ # row[1] = - row[1].to_i
1179
+ # end
1180
+ # EOT
1181
+ # File.write('t.rb', ruby)
1182
+ # # Put some CSV into a file.
1183
+ # File.write('t.csv', "foo,0\nbar,1\nbaz,2")
1184
+ # # Run the Ruby code with CSV filename as argument.
1185
+ # system(Gem.ruby, "t.rb", "t.csv")
1186
+ #
1187
+ # Output (to STDOUT):
1188
+ #
1189
+ # FOO,0
1190
+ # BAR,-1
1191
+ # BAZ,-2
1192
+ #
1193
+ # With headers:
1194
+ #
1195
+ # # Put Ruby code into a file.
1196
+ # ruby = <<-EOT
1197
+ # require 'csv'
1198
+ # CSV.filter(headers: true) do |row|
1199
+ # row[0].upcase!
1200
+ # row[1] = - row[1].to_i
1201
+ # end
1202
+ # EOT
1203
+ # File.write('t.rb', ruby)
1204
+ # # Put some CSV into a file.
1205
+ # File.write('t.csv', "Name,Value\nfoo,0\nbar,1\nbaz,2")
1206
+ # # Run the Ruby code with CSV filename as argument.
1207
+ # system(Gem.ruby, "t.rb", "t.csv")
1208
+ #
1209
+ # Output (to STDOUT):
1210
+ #
1211
+ # Name,Value
1212
+ # FOO,0
1213
+ # BAR,-1
1214
+ # BAZ,-2
1215
+ #
1216
+ # Arguments:
1217
+ #
1218
+ # * Argument +in_string_or_io+ must be a \String or an \IO stream.
1219
+ # * Argument +out_string_or_io+ must be a \String or an \IO stream.
1220
+ # * Arguments <tt>**options</tt> must be keyword options.
1221
+ #
1222
+ # - Each option defined as an {option for parsing}[#class-CSV-label-Options+for+Parsing]
1223
+ # is used for parsing the filter input.
1224
+ # - Each option defined as an {option for generating}[#class-CSV-label-Options+for+Generating]
1225
+ # is used for generator the filter input.
1226
+ #
1227
+ # However, there are three options that may be used for both parsing and generating:
1228
+ # +col_sep+, +quote_char+, and +row_sep+.
1229
+ #
1230
+ # Therefore for method +filter+ (and method +filter+ only),
1231
+ # there are special options that allow these parsing and generating options
1232
+ # to be specified separately:
1233
+ #
1234
+ # - Options +input_col_sep+ and +output_col_sep+
1235
+ # (and their aliases +in_col_sep+ and +out_col_sep+)
1236
+ # specify the column separators for parsing and generating.
1237
+ # - Options +input_quote_char+ and +output_quote_char+
1238
+ # (and their aliases +in_quote_char+ and +out_quote_char+)
1239
+ # specify the quote characters for parsing and generting.
1240
+ # - Options +input_row_sep+ and +output_row_sep+
1241
+ # (and their aliases +in_row_sep+ and +out_row_sep+)
1242
+ # specify the row separators for parsing and generating.
1243
+ #
1244
+ # Example options (for column separators):
1245
+ #
1246
+ # CSV.filter # Default for both parsing and generating.
1247
+ # CSV.filter(in_col_sep: ';') # ';' for parsing, default for generating.
1248
+ # CSV.filter(out_col_sep: '|') # Default for parsing, '|' for generating.
1249
+ # CSV.filter(in_col_sep: ';', out_col_sep: '|') # ';' for parsing, '|' for generating.
1250
+ #
1251
+ # Note that for a special option (e.g., +input_col_sep+)
1252
+ # and its corresponding "regular" option (e.g., +col_sep+),
1253
+ # the two are mutually overriding.
1254
+ #
1255
+ # Another example (possibly surprising):
1256
+ #
1257
+ # CSV.filter(in_col_sep: ';', col_sep: '|') # '|' for both parsing(!) and generating.
1258
+ #
1259
+ def filter(input=nil, output=nil, **options)
1260
+ # parse options for input, output, or both
1261
+ in_options, out_options = Hash.new, {row_sep: InputRecordSeparator.value}
1262
+ options.each do |key, value|
1263
+ case key
1264
+ when /\Ain(?:put)?_(.+)\Z/
1265
+ in_options[$1.to_sym] = value
1266
+ when /\Aout(?:put)?_(.+)\Z/
1267
+ out_options[$1.to_sym] = value
1268
+ else
1269
+ in_options[key] = value
1270
+ out_options[key] = value
1271
+ end
1272
+ end
1273
+
1274
+ # build input and output wrappers
1275
+ input = new(input || ARGF, **in_options)
1276
+ output = new(output || $stdout, **out_options)
1277
+
1278
+ # process headers
1279
+ need_manual_header_output =
1280
+ (in_options[:headers] and
1281
+ out_options[:headers] == true and
1282
+ out_options[:write_headers])
1283
+ if need_manual_header_output
1284
+ first_row = input.shift
1285
+ if first_row
1286
+ if first_row.is_a?(Row)
1287
+ headers = first_row.headers
1288
+ yield headers
1289
+ output << headers
1290
+ end
1291
+ yield first_row
1292
+ output << first_row
1293
+ end
1294
+ end
1295
+
1296
+ # read, yield, write
1297
+ input.each do |row|
1298
+ yield row
1299
+ output << row
1300
+ end
1301
+ end
1302
+
1303
+ #
1304
+ # :call-seq:
1305
+ # foreach(path_or_io, mode='r', **options) {|row| ... )
1306
+ # foreach(path_or_io, mode='r', **options) -> new_enumerator
1307
+ #
1308
+ # Calls the block with each row read from source +path_or_io+.
1309
+ #
1310
+ # \Path input without headers:
1311
+ #
1312
+ # string = "foo,0\nbar,1\nbaz,2\n"
1313
+ # in_path = 't.csv'
1314
+ # File.write(in_path, string)
1315
+ # CSV.foreach(in_path) {|row| p row }
1316
+ #
1317
+ # Output:
1318
+ #
1319
+ # ["foo", "0"]
1320
+ # ["bar", "1"]
1321
+ # ["baz", "2"]
1322
+ #
1323
+ # \Path input with headers:
1324
+ #
1325
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
1326
+ # in_path = 't.csv'
1327
+ # File.write(in_path, string)
1328
+ # CSV.foreach(in_path, headers: true) {|row| p row }
1329
+ #
1330
+ # Output:
1331
+ #
1332
+ # <CSV::Row "Name":"foo" "Value":"0">
1333
+ # <CSV::Row "Name":"bar" "Value":"1">
1334
+ # <CSV::Row "Name":"baz" "Value":"2">
1335
+ #
1336
+ # \IO stream input without headers:
1337
+ #
1338
+ # string = "foo,0\nbar,1\nbaz,2\n"
1339
+ # path = 't.csv'
1340
+ # File.write(path, string)
1341
+ # File.open('t.csv') do |in_io|
1342
+ # CSV.foreach(in_io) {|row| p row }
1343
+ # end
1344
+ #
1345
+ # Output:
1346
+ #
1347
+ # ["foo", "0"]
1348
+ # ["bar", "1"]
1349
+ # ["baz", "2"]
1350
+ #
1351
+ # \IO stream input with headers:
1352
+ #
1353
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
1354
+ # path = 't.csv'
1355
+ # File.write(path, string)
1356
+ # File.open('t.csv') do |in_io|
1357
+ # CSV.foreach(in_io, headers: true) {|row| p row }
1358
+ # end
1359
+ #
1360
+ # Output:
1361
+ #
1362
+ # <CSV::Row "Name":"foo" "Value":"0">
1363
+ # <CSV::Row "Name":"bar" "Value":"1">
1364
+ # <CSV::Row "Name":"baz" "Value":"2">
1365
+ #
1366
+ # With no block given, returns an \Enumerator:
1367
+ #
1368
+ # string = "foo,0\nbar,1\nbaz,2\n"
1369
+ # path = 't.csv'
1370
+ # File.write(path, string)
1371
+ # CSV.foreach(path) # => #<Enumerator: CSV:foreach("t.csv", "r")>
1372
+ #
1373
+ # Arguments:
1374
+ # * Argument +path_or_io+ must be a file path or an \IO stream.
1375
+ # * Argument +mode+, if given, must be a \File mode.
1376
+ # See {Access Modes}[https://docs.ruby-lang.org/en/master/File.html#class-File-label-Access+Modes].
1377
+ # * Arguments <tt>**options</tt> must be keyword options.
1378
+ # See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
1379
+ # * This method optionally accepts an additional <tt>:encoding</tt> option
1380
+ # that you can use to specify the Encoding of the data read from +path_or_io+.
1381
+ # You must provide this unless your data is in the encoding
1382
+ # given by <tt>Encoding::default_external</tt>.
1383
+ # Parsing will use this to determine how to parse the data.
1384
+ # You may provide a second Encoding to
1385
+ # have the data transcoded as it is read. For example,
1386
+ # encoding: 'UTF-32BE:UTF-8'
1387
+ # would read +UTF-32BE+ data from the file
1388
+ # but transcode it to +UTF-8+ before parsing.
1389
+ def foreach(path_or_io, mode="r", **options, &block)
1390
+ return to_enum(__method__, path_or_io, mode, **options) unless block_given?
1391
+ open(path_or_io, mode, **options) do |csv|
1392
+ csv.each(&block)
1393
+ end
1394
+ end
1395
+
1396
+ #
1397
+ # :call-seq:
1398
+ # generate(csv_string, **options) {|csv| ... }
1399
+ # generate(**options) {|csv| ... }
1400
+ #
1401
+ # * Argument +csv_string+, if given, must be a \String object;
1402
+ # defaults to a new empty \String.
1403
+ # * Arguments +options+, if given, should be generating options.
1404
+ # See {Options for Generating}[#class-CSV-label-Options+for+Generating].
1405
+ #
1406
+ # ---
1407
+ #
1408
+ # Creates a new \CSV object via <tt>CSV.new(csv_string, **options)</tt>;
1409
+ # calls the block with the \CSV object, which the block may modify;
1410
+ # returns the \String generated from the \CSV object.
1411
+ #
1412
+ # Note that a passed \String *is* modified by this method.
1413
+ # Pass <tt>csv_string</tt>.dup if the \String must be preserved.
1414
+ #
1415
+ # This method has one additional option: <tt>:encoding</tt>,
1416
+ # which sets the base Encoding for the output if no no +str+ is specified.
1417
+ # CSV needs this hint if you plan to output non-ASCII compatible data.
1418
+ #
1419
+ # ---
1420
+ #
1421
+ # Add lines:
1422
+ # input_string = "foo,0\nbar,1\nbaz,2\n"
1423
+ # output_string = CSV.generate(input_string) do |csv|
1424
+ # csv << ['bat', 3]
1425
+ # csv << ['bam', 4]
1426
+ # end
1427
+ # output_string # => "foo,0\nbar,1\nbaz,2\nbat,3\nbam,4\n"
1428
+ # input_string # => "foo,0\nbar,1\nbaz,2\nbat,3\nbam,4\n"
1429
+ # output_string.equal?(input_string) # => true # Same string, modified
1430
+ #
1431
+ # Add lines into new string, preserving old string:
1432
+ # input_string = "foo,0\nbar,1\nbaz,2\n"
1433
+ # output_string = CSV.generate(input_string.dup) do |csv|
1434
+ # csv << ['bat', 3]
1435
+ # csv << ['bam', 4]
1436
+ # end
1437
+ # output_string # => "foo,0\nbar,1\nbaz,2\nbat,3\nbam,4\n"
1438
+ # input_string # => "foo,0\nbar,1\nbaz,2\n"
1439
+ # output_string.equal?(input_string) # => false # Different strings
1440
+ #
1441
+ # Create lines from nothing:
1442
+ # output_string = CSV.generate do |csv|
1443
+ # csv << ['foo', 0]
1444
+ # csv << ['bar', 1]
1445
+ # csv << ['baz', 2]
1446
+ # end
1447
+ # output_string # => "foo,0\nbar,1\nbaz,2\n"
1448
+ #
1449
+ # ---
1450
+ #
1451
+ # Raises an exception if +csv_string+ is not a \String object:
1452
+ # # Raises TypeError (no implicit conversion of Integer into String)
1453
+ # CSV.generate(0)
1454
+ #
1455
+ def generate(str=nil, **options)
1456
+ encoding = options[:encoding]
1457
+ # add a default empty String, if none was given
1458
+ if str
1459
+ str = StringIO.new(str)
1460
+ str.seek(0, IO::SEEK_END)
1461
+ str.set_encoding(encoding) if encoding
1462
+ else
1463
+ str = +""
1464
+ str.force_encoding(encoding) if encoding
1465
+ end
1466
+ csv = new(str, **options) # wrap
1467
+ yield csv # yield for appending
1468
+ csv.string # return final String
1469
+ end
1470
+
1471
+ # :call-seq:
1472
+ # CSV.generate_line(ary)
1473
+ # CSV.generate_line(ary, **options)
1474
+ #
1475
+ # Returns the \String created by generating \CSV from +ary+
1476
+ # using the specified +options+.
1477
+ #
1478
+ # Argument +ary+ must be an \Array.
1479
+ #
1480
+ # Special options:
1481
+ # * Option <tt>:row_sep</tt> defaults to <tt>"\n"> on Ruby 3.0 or later
1482
+ # and <tt>$INPUT_RECORD_SEPARATOR</tt> (<tt>$/</tt>) otherwise.:
1483
+ # $INPUT_RECORD_SEPARATOR # => "\n"
1484
+ # * This method accepts an additional option, <tt>:encoding</tt>, which sets the base
1485
+ # Encoding for the output. This method will try to guess your Encoding from
1486
+ # the first non-+nil+ field in +row+, if possible, but you may need to use
1487
+ # this parameter as a backup plan.
1488
+ #
1489
+ # For other +options+,
1490
+ # see {Options for Generating}[#class-CSV-label-Options+for+Generating].
1491
+ #
1492
+ # ---
1493
+ #
1494
+ # Returns the \String generated from an \Array:
1495
+ # CSV.generate_line(['foo', '0']) # => "foo,0\n"
1496
+ #
1497
+ # ---
1498
+ #
1499
+ # Raises an exception if +ary+ is not an \Array:
1500
+ # # Raises NoMethodError (undefined method `find' for :foo:Symbol)
1501
+ # CSV.generate_line(:foo)
1502
+ #
1503
+ def generate_line(row, **options)
1504
+ options = {row_sep: InputRecordSeparator.value}.merge(options)
1505
+ str = +""
1506
+ if options[:encoding]
1507
+ str.force_encoding(options[:encoding])
1508
+ else
1509
+ fallback_encoding = nil
1510
+ output_encoding = nil
1511
+ row.each do |field|
1512
+ next unless field.is_a?(String)
1513
+ fallback_encoding ||= field.encoding
1514
+ next if field.ascii_only?
1515
+ output_encoding = field.encoding
1516
+ break
1517
+ end
1518
+ output_encoding ||= fallback_encoding
1519
+ if output_encoding
1520
+ str.force_encoding(output_encoding)
1521
+ end
1522
+ end
1523
+ (new(str, **options) << row).string
1524
+ end
1525
+
1526
+ # :call-seq:
1527
+ # CSV.generate_lines(rows)
1528
+ # CSV.generate_lines(rows, **options)
1529
+ #
1530
+ # Returns the \String created by generating \CSV from
1531
+ # using the specified +options+.
1532
+ #
1533
+ # Argument +rows+ must be an \Array of row. Row is \Array of \String or \CSV::Row.
1534
+ #
1535
+ # Special options:
1536
+ # * Option <tt>:row_sep</tt> defaults to <tt>"\n"</tt> on Ruby 3.0 or later
1537
+ # and <tt>$INPUT_RECORD_SEPARATOR</tt> (<tt>$/</tt>) otherwise.:
1538
+ # $INPUT_RECORD_SEPARATOR # => "\n"
1539
+ # * This method accepts an additional option, <tt>:encoding</tt>, which sets the base
1540
+ # Encoding for the output. This method will try to guess your Encoding from
1541
+ # the first non-+nil+ field in +row+, if possible, but you may need to use
1542
+ # this parameter as a backup plan.
1543
+ #
1544
+ # For other +options+,
1545
+ # see {Options for Generating}[#class-CSV-label-Options+for+Generating].
1546
+ #
1547
+ # ---
1548
+ #
1549
+ # Returns the \String generated from an
1550
+ # CSV.generate_lines([['foo', '0'], ['bar', '1'], ['baz', '2']]) # => "foo,0\nbar,1\nbaz,2\n"
1551
+ #
1552
+ # ---
1553
+ #
1554
+ # Raises an exception
1555
+ # # Raises NoMethodError (undefined method `each' for :foo:Symbol)
1556
+ # CSV.generate_lines(:foo)
1557
+ #
1558
+ def generate_lines(rows, **options)
1559
+ self.generate(**options) do |csv|
1560
+ rows.each do |row|
1561
+ csv << row
1562
+ end
1563
+ end
1564
+ end
1565
+
1566
+ #
1567
+ # :call-seq:
1568
+ # open(path_or_io, mode = "rb", **options ) -> new_csv
1569
+ # open(path_or_io, mode = "rb", **options ) { |csv| ... } -> object
1570
+ #
1571
+ # possible options elements:
1572
+ # keyword form:
1573
+ # :invalid => nil # raise error on invalid byte sequence (default)
1574
+ # :invalid => :replace # replace invalid byte sequence
1575
+ # :undef => :replace # replace undefined conversion
1576
+ # :replace => string # replacement string ("?" or "\uFFFD" if not specified)
1577
+ #
1578
+ # * Argument +path_or_io+, must be a file path or an \IO stream.
1579
+ # :include: ../doc/csv/arguments/io.rdoc
1580
+ # * Argument +mode+, if given, must be a \File mode.
1581
+ # See {Access Modes}[https://docs.ruby-lang.org/en/master/File.html#class-File-label-Access+Modes].
1582
+ # * Arguments <tt>**options</tt> must be keyword options.
1583
+ # See {Options for Generating}[#class-CSV-label-Options+for+Generating].
1584
+ # * This method optionally accepts an additional <tt>:encoding</tt> option
1585
+ # that you can use to specify the Encoding of the data read from +path_or_io+.
1586
+ # You must provide this unless your data is in the encoding
1587
+ # given by <tt>Encoding::default_external</tt>.
1588
+ # Parsing will use this to determine how to parse the data.
1589
+ # You may provide a second Encoding to
1590
+ # have the data transcoded as it is read. For example,
1591
+ # encoding: 'UTF-32BE:UTF-8'
1592
+ # would read +UTF-32BE+ data from the file
1593
+ # but transcode it to +UTF-8+ before parsing.
1594
+ #
1595
+ # ---
1596
+ #
1597
+ # These examples assume prior execution of:
1598
+ # string = "foo,0\nbar,1\nbaz,2\n"
1599
+ # path = 't.csv'
1600
+ # File.write(path, string)
1601
+ #
1602
+ # string_io = StringIO.new
1603
+ # string_io << "foo,0\nbar,1\nbaz,2\n"
1604
+ #
1605
+ # ---
1606
+ #
1607
+ # With no block given, returns a new \CSV object.
1608
+ #
1609
+ # Create a \CSV object using a file path:
1610
+ # csv = CSV.open(path)
1611
+ # csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
1612
+ #
1613
+ # Create a \CSV object using an open \File:
1614
+ # csv = CSV.open(File.open(path))
1615
+ # csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
1616
+ #
1617
+ # Create a \CSV object using a \StringIO:
1618
+ # csv = CSV.open(string_io)
1619
+ # csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
1620
+ # ---
1621
+ #
1622
+ # With a block given, calls the block with the created \CSV object;
1623
+ # returns the block's return value:
1624
+ #
1625
+ # Using a file path:
1626
+ # csv = CSV.open(path) {|csv| p csv}
1627
+ # csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
1628
+ # Output:
1629
+ # #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
1630
+ #
1631
+ # Using an open \File:
1632
+ # csv = CSV.open(File.open(path)) {|csv| p csv}
1633
+ # csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
1634
+ # Output:
1635
+ # #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
1636
+ #
1637
+ # Using a \StringIO:
1638
+ # csv = CSV.open(string_io) {|csv| p csv}
1639
+ # csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
1640
+ # Output:
1641
+ # #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
1642
+ # ---
1643
+ #
1644
+ # Raises an exception if the argument is not a \String object or \IO object:
1645
+ # # Raises TypeError (no implicit conversion of Symbol into String)
1646
+ # CSV.open(:foo)
1647
+ def open(path_or_io, mode="r", **options)
1648
+ # wrap a File opened with the remaining +args+ with no newline
1649
+ # decorator
1650
+ file_opts = {}
1651
+ may_enable_bom_detection_automatically(path_or_io,
1652
+ mode,
1653
+ options,
1654
+ file_opts)
1655
+ file_opts.merge!(options)
1656
+ unless file_opts.key?(:newline)
1657
+ file_opts[:universal_newline] ||= false
1658
+ end
1659
+ options.delete(:invalid)
1660
+ options.delete(:undef)
1661
+ options.delete(:replace)
1662
+ options.delete_if {|k, _| /newline\z/.match?(k)}
1663
+
1664
+ if path_or_io.is_a?(StringIO)
1665
+ f = create_stringio(path_or_io.string, mode, **file_opts)
1666
+ else
1667
+ begin
1668
+ f = File.open(path_or_io, mode, **file_opts)
1669
+ rescue ArgumentError => e
1670
+ raise unless /needs binmode/.match?(e.message) and mode == "r"
1671
+ mode = "rb"
1672
+ file_opts = {encoding: Encoding.default_external}.merge(file_opts)
1673
+ retry
1674
+ end
1675
+ end
1676
+
1677
+ begin
1678
+ csv = new(f, **options)
1679
+ rescue Exception
1680
+ f.close
1681
+ raise
1682
+ end
1683
+
1684
+ # handle blocks like Ruby's open(), not like the CSV library
1685
+ if block_given?
1686
+ begin
1687
+ yield csv
1688
+ ensure
1689
+ csv.close
1690
+ end
1691
+ else
1692
+ csv
1693
+ end
1694
+ end
1695
+
1696
+ #
1697
+ # :call-seq:
1698
+ # parse(string) -> array_of_arrays
1699
+ # parse(io) -> array_of_arrays
1700
+ # parse(string, headers: ..., **options) -> csv_table
1701
+ # parse(io, headers: ..., **options) -> csv_table
1702
+ # parse(string, **options) {|row| ... }
1703
+ # parse(io, **options) {|row| ... }
1704
+ #
1705
+ # Parses +string+ or +io+ using the specified +options+.
1706
+ #
1707
+ # - Argument +string+ should be a \String object;
1708
+ # it will be put into a new StringIO object positioned at the beginning.
1709
+ # :include: ../doc/csv/arguments/io.rdoc
1710
+ # - Argument +options+: see {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
1711
+ #
1712
+ # ====== Without Option +headers+
1713
+ #
1714
+ # Without {option +headers+}[#class-CSV-label-Option+headers] case.
1715
+ #
1716
+ # These examples assume prior execution of:
1717
+ # string = "foo,0\nbar,1\nbaz,2\n"
1718
+ # path = 't.csv'
1719
+ # File.write(path, string)
1720
+ #
1721
+ # ---
1722
+ #
1723
+ # With no block given, returns an \Array of Arrays formed from the source.
1724
+ #
1725
+ # Parse a \String:
1726
+ # a_of_a = CSV.parse(string)
1727
+ # a_of_a # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
1728
+ #
1729
+ # Parse an open \File:
1730
+ # a_of_a = File.open(path) do |file|
1731
+ # CSV.parse(file)
1732
+ # end
1733
+ # a_of_a # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
1734
+ #
1735
+ # ---
1736
+ #
1737
+ # With a block given, calls the block with each parsed row:
1738
+ #
1739
+ # Parse a \String:
1740
+ # CSV.parse(string) {|row| p row }
1741
+ #
1742
+ # Output:
1743
+ # ["foo", "0"]
1744
+ # ["bar", "1"]
1745
+ # ["baz", "2"]
1746
+ #
1747
+ # Parse an open \File:
1748
+ # File.open(path) do |file|
1749
+ # CSV.parse(file) {|row| p row }
1750
+ # end
1751
+ #
1752
+ # Output:
1753
+ # ["foo", "0"]
1754
+ # ["bar", "1"]
1755
+ # ["baz", "2"]
1756
+ #
1757
+ # ====== With Option +headers+
1758
+ #
1759
+ # With {option +headers+}[#class-CSV-label-Option+headers] case.
1760
+ #
1761
+ # These examples assume prior execution of:
1762
+ # string = "Name,Count\nfoo,0\nbar,1\nbaz,2\n"
1763
+ # path = 't.csv'
1764
+ # File.write(path, string)
1765
+ #
1766
+ # ---
1767
+ #
1768
+ # With no block given, returns a CSV::Table object formed from the source.
1769
+ #
1770
+ # Parse a \String:
1771
+ # csv_table = CSV.parse(string, headers: ['Name', 'Count'])
1772
+ # csv_table # => #<CSV::Table mode:col_or_row row_count:5>
1773
+ #
1774
+ # Parse an open \File:
1775
+ # csv_table = File.open(path) do |file|
1776
+ # CSV.parse(file, headers: ['Name', 'Count'])
1777
+ # end
1778
+ # csv_table # => #<CSV::Table mode:col_or_row row_count:4>
1779
+ #
1780
+ # ---
1781
+ #
1782
+ # With a block given, calls the block with each parsed row,
1783
+ # which has been formed into a CSV::Row object:
1784
+ #
1785
+ # Parse a \String:
1786
+ # CSV.parse(string, headers: ['Name', 'Count']) {|row| p row }
1787
+ #
1788
+ # Output:
1789
+ # # <CSV::Row "Name":"foo" "Count":"0">
1790
+ # # <CSV::Row "Name":"bar" "Count":"1">
1791
+ # # <CSV::Row "Name":"baz" "Count":"2">
1792
+ #
1793
+ # Parse an open \File:
1794
+ # File.open(path) do |file|
1795
+ # CSV.parse(file, headers: ['Name', 'Count']) {|row| p row }
1796
+ # end
1797
+ #
1798
+ # Output:
1799
+ # # <CSV::Row "Name":"foo" "Count":"0">
1800
+ # # <CSV::Row "Name":"bar" "Count":"1">
1801
+ # # <CSV::Row "Name":"baz" "Count":"2">
1802
+ #
1803
+ # ---
1804
+ #
1805
+ # Raises an exception if the argument is not a \String object or \IO object:
1806
+ # # Raises NoMethodError (undefined method `close' for :foo:Symbol)
1807
+ # CSV.parse(:foo)
1808
+ #
1809
+ # ---
1810
+ #
1811
+ # Please make sure if your text contains \BOM or not. CSV.parse will not remove
1812
+ # \BOM automatically. You might want to remove \BOM before calling CSV.parse :
1813
+ # # remove BOM on calling File.open
1814
+ # File.open(path, encoding: 'bom|utf-8') do |file|
1815
+ # CSV.parse(file, headers: true) do |row|
1816
+ # # you can get value by column name because BOM is removed
1817
+ # p row['Name']
1818
+ # end
1819
+ # end
1820
+ #
1821
+ # Output:
1822
+ # # "foo"
1823
+ # # "bar"
1824
+ # # "baz"
1825
+ def parse(str, **options, &block)
1826
+ csv = new(str, **options)
1827
+
1828
+ return csv.each(&block) if block_given?
1829
+
1830
+ # slurp contents, if no block is given
1831
+ begin
1832
+ csv.read
1833
+ ensure
1834
+ csv.close
1835
+ end
1836
+ end
1837
+
1838
+ # :call-seq:
1839
+ # CSV.parse_line(string) -> new_array or nil
1840
+ # CSV.parse_line(io) -> new_array or nil
1841
+ # CSV.parse_line(string, **options) -> new_array or nil
1842
+ # CSV.parse_line(io, **options) -> new_array or nil
1843
+ # CSV.parse_line(string, headers: true, **options) -> csv_row or nil
1844
+ # CSV.parse_line(io, headers: true, **options) -> csv_row or nil
1845
+ #
1846
+ # Returns the data created by parsing the first line of +string+ or +io+
1847
+ # using the specified +options+.
1848
+ #
1849
+ # - Argument +string+ should be a \String object;
1850
+ # it will be put into a new StringIO object positioned at the beginning.
1851
+ # :include: ../doc/csv/arguments/io.rdoc
1852
+ # - Argument +options+: see {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
1853
+ #
1854
+ # ====== Without Option +headers+
1855
+ #
1856
+ # Without option +headers+, returns the first row as a new \Array.
1857
+ #
1858
+ # These examples assume prior execution of:
1859
+ # string = "foo,0\nbar,1\nbaz,2\n"
1860
+ # path = 't.csv'
1861
+ # File.write(path, string)
1862
+ #
1863
+ # Parse the first line from a \String object:
1864
+ # CSV.parse_line(string) # => ["foo", "0"]
1865
+ #
1866
+ # Parse the first line from a File object:
1867
+ # File.open(path) do |file|
1868
+ # CSV.parse_line(file) # => ["foo", "0"]
1869
+ # end # => ["foo", "0"]
1870
+ #
1871
+ # Returns +nil+ if the argument is an empty \String:
1872
+ # CSV.parse_line('') # => nil
1873
+ #
1874
+ # ====== With Option +headers+
1875
+ #
1876
+ # With {option +headers+}[#class-CSV-label-Option+headers],
1877
+ # returns the first row as a CSV::Row object.
1878
+ #
1879
+ # These examples assume prior execution of:
1880
+ # string = "Name,Count\nfoo,0\nbar,1\nbaz,2\n"
1881
+ # path = 't.csv'
1882
+ # File.write(path, string)
1883
+ #
1884
+ # Parse the first line from a \String object:
1885
+ # CSV.parse_line(string, headers: true) # => #<CSV::Row "Name":"foo" "Count":"0">
1886
+ #
1887
+ # Parse the first line from a File object:
1888
+ # File.open(path) do |file|
1889
+ # CSV.parse_line(file, headers: true)
1890
+ # end # => #<CSV::Row "Name":"foo" "Count":"0">
1891
+ #
1892
+ # ---
1893
+ #
1894
+ # Raises an exception if the argument is +nil+:
1895
+ # # Raises ArgumentError (Cannot parse nil as CSV):
1896
+ # CSV.parse_line(nil)
1897
+ #
1898
+ def parse_line(line, **options)
1899
+ new(line, **options).each.first
1900
+ end
1901
+
1902
+ #
1903
+ # :call-seq:
1904
+ # read(path_or_io, **options) -> array_of_arrays
1905
+ # read(path_or_io, headers: true, **options) -> csv_table
1906
+ #
1907
+ # Opens the given +path_or_io+ with the given +options+ (see CSV.open),
1908
+ # reads the source (see CSV#read), and returns the result,
1909
+ # which will be either an \Array of Arrays or a CSV::Table.
1910
+ #
1911
+ # Without headers:
1912
+ # string = "foo,0\nbar,1\nbaz,2\n"
1913
+ # path = 't.csv'
1914
+ # File.write(path, string)
1915
+ # CSV.read(path) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
1916
+ #
1917
+ # With headers:
1918
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
1919
+ # path = 't.csv'
1920
+ # File.write(path, string)
1921
+ # CSV.read(path, headers: true) # => #<CSV::Table mode:col_or_row row_count:4>
1922
+ def read(path_or_io, **options)
1923
+ open(path_or_io, **options) { |csv| csv.read }
1924
+ end
1925
+
1926
+ # :call-seq:
1927
+ # CSV.readlines(path_or_io, **options)
1928
+ #
1929
+ # Alias for CSV.read.
1930
+ def readlines(path_or_io, **options)
1931
+ read(path_or_io, **options)
1932
+ end
1933
+
1934
+ # :call-seq:
1935
+ # CSV.table(path_or_io, **options)
1936
+ #
1937
+ # Calls CSV.read with +path_or_io+, +options+, and certain default options:
1938
+ # - +headers+: +true+
1939
+ # - +converters+: +:numeric+
1940
+ # - +header_converters+: +:symbol+
1941
+ #
1942
+ # Returns a CSV::Table object.
1943
+ #
1944
+ # Example:
1945
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
1946
+ # path = 't.csv'
1947
+ # File.write(path, string)
1948
+ # CSV.table(path) # => #<CSV::Table mode:col_or_row row_count:4>
1949
+ def table(path_or_io, **options)
1950
+ default_options = {
1951
+ headers: true,
1952
+ converters: :numeric,
1953
+ header_converters: :symbol,
1954
+ }
1955
+ options = default_options.merge(options)
1956
+ read(path_or_io, **options)
1957
+ end
1958
+
1959
+ ON_WINDOWS = /mingw|mswin/.match?(RUBY_PLATFORM)
1960
+ private_constant :ON_WINDOWS
1961
+
1962
+ private
1963
+ def may_enable_bom_detection_automatically(path_or_io,
1964
+ mode,
1965
+ options,
1966
+ file_opts)
1967
+ if path_or_io.is_a?(StringIO)
1968
+ # Support to StringIO was dropped for Ruby 2.6 and earlier without BOM support:
1969
+ # https://github.com/ruby/stringio/pull/47
1970
+ return if RUBY_VERSION < "2.7"
1971
+ else
1972
+ # "bom|utf-8" may be buggy on Windows:
1973
+ # https://bugs.ruby-lang.org/issues/20526
1974
+ return if ON_WINDOWS
1975
+ end
1976
+ return unless Encoding.default_external == Encoding::UTF_8
1977
+ return if options.key?(:encoding)
1978
+ return if options.key?(:external_encoding)
1979
+ return if mode.is_a?(String) and mode.include?(":")
1980
+ file_opts[:encoding] = "bom|utf-8"
1981
+ end
1982
+
1983
+ if RUBY_VERSION < "2.7"
1984
+ def create_stringio(str, mode, opts)
1985
+ opts.delete_if {|k, _| k == :universal_newline or DEFAULT_OPTIONS.key?(k)}
1986
+ raise ArgumentError, "Unsupported options parsing StringIO: #{opts.keys}" unless opts.empty?
1987
+ StringIO.new(str, mode)
1988
+ end
1989
+ else
1990
+ def create_stringio(str, mode, opts)
1991
+ StringIO.new(str, mode, **opts)
1992
+ end
1993
+ end
1994
+ end
1995
+
1996
+ # :call-seq:
1997
+ # CSV.new(string)
1998
+ # CSV.new(io)
1999
+ # CSV.new(string, **options)
2000
+ # CSV.new(io, **options)
2001
+ #
2002
+ # Returns the new \CSV object created using +string+ or +io+
2003
+ # and the specified +options+.
2004
+ #
2005
+ # - Argument +string+ should be a \String object;
2006
+ # it will be put into a new StringIO object positioned at the beginning.
2007
+ # :include: ../doc/csv/arguments/io.rdoc
2008
+ # - Argument +options+: See:
2009
+ # * {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
2010
+ # * {Options for Generating}[#class-CSV-label-Options+for+Generating]
2011
+ # For performance reasons, the options cannot be overridden
2012
+ # in a \CSV object, so those specified here will endure.
2013
+ #
2014
+ # In addition to the \CSV instance methods, several \IO methods are delegated.
2015
+ # See {Delegated Methods}[#class-CSV-label-Delegated+Methods].
2016
+ #
2017
+ # ---
2018
+ #
2019
+ # Create a \CSV object from a \String object:
2020
+ # csv = CSV.new('foo,0')
2021
+ # csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
2022
+ #
2023
+ # Create a \CSV object from a \File object:
2024
+ # File.write('t.csv', 'foo,0')
2025
+ # csv = CSV.new(File.open('t.csv'))
2026
+ # csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
2027
+ #
2028
+ # ---
2029
+ #
2030
+ # Raises an exception if the argument is +nil+:
2031
+ # # Raises ArgumentError (Cannot parse nil as CSV):
2032
+ # CSV.new(nil)
2033
+ #
2034
+ def initialize(data,
2035
+ col_sep: ",",
2036
+ row_sep: :auto,
2037
+ quote_char: '"',
2038
+ field_size_limit: nil,
2039
+ max_field_size: nil,
2040
+ converters: nil,
2041
+ unconverted_fields: nil,
2042
+ headers: false,
2043
+ return_headers: false,
2044
+ write_headers: nil,
2045
+ header_converters: nil,
2046
+ skip_blanks: false,
2047
+ force_quotes: false,
2048
+ skip_lines: nil,
2049
+ liberal_parsing: false,
2050
+ internal_encoding: nil,
2051
+ external_encoding: nil,
2052
+ encoding: nil,
2053
+ nil_value: nil,
2054
+ empty_value: "",
2055
+ strip: false,
2056
+ quote_empty: true,
2057
+ write_converters: nil,
2058
+ write_nil_value: nil,
2059
+ write_empty_value: "")
2060
+ raise ArgumentError.new("Cannot parse nil as CSV") if data.nil?
2061
+
2062
+ if data.is_a?(String)
2063
+ if encoding
2064
+ if encoding.is_a?(String)
2065
+ data_external_encoding, data_internal_encoding = encoding.split(":", 2)
2066
+ if data_internal_encoding
2067
+ data = data.encode(data_internal_encoding, data_external_encoding)
2068
+ else
2069
+ data = data.dup.force_encoding(data_external_encoding)
2070
+ end
2071
+ else
2072
+ data = data.dup.force_encoding(encoding)
2073
+ end
2074
+ end
2075
+ @io = StringIO.new(data)
2076
+ else
2077
+ @io = data
2078
+ end
2079
+ @encoding = determine_encoding(encoding, internal_encoding)
2080
+
2081
+ @base_fields_converter_options = {
2082
+ nil_value: nil_value,
2083
+ empty_value: empty_value,
2084
+ }
2085
+ @write_fields_converter_options = {
2086
+ nil_value: write_nil_value,
2087
+ empty_value: write_empty_value,
2088
+ }
2089
+ @initial_converters = converters
2090
+ @initial_header_converters = header_converters
2091
+ @initial_write_converters = write_converters
2092
+
2093
+ if max_field_size.nil? and field_size_limit
2094
+ max_field_size = field_size_limit - 1
2095
+ end
2096
+ @parser_options = {
2097
+ column_separator: col_sep,
2098
+ row_separator: row_sep,
2099
+ quote_character: quote_char,
2100
+ max_field_size: max_field_size,
2101
+ unconverted_fields: unconverted_fields,
2102
+ headers: headers,
2103
+ return_headers: return_headers,
2104
+ skip_blanks: skip_blanks,
2105
+ skip_lines: skip_lines,
2106
+ liberal_parsing: liberal_parsing,
2107
+ encoding: @encoding,
2108
+ nil_value: nil_value,
2109
+ empty_value: empty_value,
2110
+ strip: strip,
2111
+ }
2112
+ @parser = nil
2113
+
2114
+ @writer_options = {
2115
+ encoding: @encoding,
2116
+ force_encoding: (not encoding.nil?),
2117
+ force_quotes: force_quotes,
2118
+ headers: headers,
2119
+ write_headers: write_headers,
2120
+ column_separator: col_sep,
2121
+ row_separator: row_sep,
2122
+ quote_character: quote_char,
2123
+ quote_empty: quote_empty,
2124
+ }
2125
+
2126
+ @writer = nil
2127
+ writer if @writer_options[:write_headers]
2128
+ end
2129
+
2130
+ class TSV < CSV
2131
+ def initialize(data, **options)
2132
+ super(data, **({col_sep: "\t"}.merge(options)))
2133
+ end
2134
+ end
2135
+
2136
+ # :call-seq:
2137
+ # csv.col_sep -> string
2138
+ #
2139
+ # Returns the encoded column separator; used for parsing and writing;
2140
+ # see {Option +col_sep+}[#class-CSV-label-Option+col_sep]:
2141
+ # CSV.new('').col_sep # => ","
2142
+ def col_sep
2143
+ parser.column_separator
2144
+ end
2145
+
2146
+ # :call-seq:
2147
+ # csv.row_sep -> string
2148
+ #
2149
+ # Returns the encoded row separator; used for parsing and writing;
2150
+ # see {Option +row_sep+}[#class-CSV-label-Option+row_sep]:
2151
+ # CSV.new('').row_sep # => "\n"
2152
+ def row_sep
2153
+ parser.row_separator
2154
+ end
2155
+
2156
+ # :call-seq:
2157
+ # csv.quote_char -> character
2158
+ #
2159
+ # Returns the encoded quote character; used for parsing and writing;
2160
+ # see {Option +quote_char+}[#class-CSV-label-Option+quote_char]:
2161
+ # CSV.new('').quote_char # => "\""
2162
+ def quote_char
2163
+ parser.quote_character
2164
+ end
2165
+
2166
+ # :call-seq:
2167
+ # csv.field_size_limit -> integer or nil
2168
+ #
2169
+ # Returns the limit for field size; used for parsing;
2170
+ # see {Option +field_size_limit+}[#class-CSV-label-Option+field_size_limit]:
2171
+ # CSV.new('').field_size_limit # => nil
2172
+ #
2173
+ # Deprecated since 3.2.3. Use +max_field_size+ instead.
2174
+ def field_size_limit
2175
+ parser.field_size_limit
2176
+ end
2177
+
2178
+ # :call-seq:
2179
+ # csv.max_field_size -> integer or nil
2180
+ #
2181
+ # Returns the limit for field size; used for parsing;
2182
+ # see {Option +max_field_size+}[#class-CSV-label-Option+max_field_size]:
2183
+ # CSV.new('').max_field_size # => nil
2184
+ #
2185
+ # Since 3.2.3.
2186
+ def max_field_size
2187
+ parser.max_field_size
2188
+ end
2189
+
2190
+ # :call-seq:
2191
+ # csv.skip_lines -> regexp or nil
2192
+ #
2193
+ # Returns the \Regexp used to identify comment lines; used for parsing;
2194
+ # see {Option +skip_lines+}[#class-CSV-label-Option+skip_lines]:
2195
+ # CSV.new('').skip_lines # => nil
2196
+ def skip_lines
2197
+ parser.skip_lines
2198
+ end
2199
+
2200
+ # :call-seq:
2201
+ # csv.converters -> array
2202
+ #
2203
+ # Returns an \Array containing field converters;
2204
+ # see {Field Converters}[#class-CSV-label-Field+Converters]:
2205
+ # csv = CSV.new('')
2206
+ # csv.converters # => []
2207
+ # csv.convert(:integer)
2208
+ # csv.converters # => [:integer]
2209
+ # csv.convert(proc {|x| x.to_s })
2210
+ # csv.converters
2211
+ #
2212
+ # Notes that you need to call
2213
+ # +Ractor.make_shareable(CSV::Converters)+ on the main Ractor to use
2214
+ # this method.
2215
+ def converters
2216
+ parser_fields_converter.map do |converter|
2217
+ name = Converters.rassoc(converter)
2218
+ name ? name.first : converter
2219
+ end
2220
+ end
2221
+
2222
+ # :call-seq:
2223
+ # csv.unconverted_fields? -> object
2224
+ #
2225
+ # Returns the value that determines whether unconverted fields are to be
2226
+ # available; used for parsing;
2227
+ # see {Option +unconverted_fields+}[#class-CSV-label-Option+unconverted_fields]:
2228
+ # CSV.new('').unconverted_fields? # => nil
2229
+ def unconverted_fields?
2230
+ parser.unconverted_fields?
2231
+ end
2232
+
2233
+ # :call-seq:
2234
+ # csv.headers -> object
2235
+ #
2236
+ # Returns the value that determines whether headers are used; used for parsing;
2237
+ # see {Option +headers+}[#class-CSV-label-Option+headers]:
2238
+ # CSV.new('').headers # => nil
2239
+ def headers
2240
+ if @writer
2241
+ @writer.headers
2242
+ else
2243
+ parsed_headers = parser.headers
2244
+ return parsed_headers if parsed_headers
2245
+ raw_headers = @parser_options[:headers]
2246
+ raw_headers = nil if raw_headers == false
2247
+ raw_headers
2248
+ end
2249
+ end
2250
+
2251
+ # :call-seq:
2252
+ # csv.return_headers? -> true or false
2253
+ #
2254
+ # Returns the value that determines whether headers are to be returned; used for parsing;
2255
+ # see {Option +return_headers+}[#class-CSV-label-Option+return_headers]:
2256
+ # CSV.new('').return_headers? # => false
2257
+ def return_headers?
2258
+ parser.return_headers?
2259
+ end
2260
+
2261
+ # :call-seq:
2262
+ # csv.write_headers? -> true or false
2263
+ #
2264
+ # Returns the value that determines whether headers are to be written; used for generating;
2265
+ # see {Option +write_headers+}[#class-CSV-label-Option+write_headers]:
2266
+ # CSV.new('').write_headers? # => nil
2267
+ def write_headers?
2268
+ @writer_options[:write_headers]
2269
+ end
2270
+
2271
+ # :call-seq:
2272
+ # csv.header_converters -> array
2273
+ #
2274
+ # Returns an \Array containing header converters; used for parsing;
2275
+ # see {Header Converters}[#class-CSV-label-Header+Converters]:
2276
+ # CSV.new('').header_converters # => []
2277
+ #
2278
+ # Notes that you need to call
2279
+ # +Ractor.make_shareable(CSV::HeaderConverters)+ on the main Ractor
2280
+ # to use this method.
2281
+ def header_converters
2282
+ header_fields_converter.map do |converter|
2283
+ name = HeaderConverters.rassoc(converter)
2284
+ name ? name.first : converter
2285
+ end
2286
+ end
2287
+
2288
+ # :call-seq:
2289
+ # csv.skip_blanks? -> true or false
2290
+ #
2291
+ # Returns the value that determines whether blank lines are to be ignored; used for parsing;
2292
+ # see {Option +skip_blanks+}[#class-CSV-label-Option+skip_blanks]:
2293
+ # CSV.new('').skip_blanks? # => false
2294
+ def skip_blanks?
2295
+ parser.skip_blanks?
2296
+ end
2297
+
2298
+ # :call-seq:
2299
+ # csv.force_quotes? -> true or false
2300
+ #
2301
+ # Returns the value that determines whether all output fields are to be quoted;
2302
+ # used for generating;
2303
+ # see {Option +force_quotes+}[#class-CSV-label-Option+force_quotes]:
2304
+ # CSV.new('').force_quotes? # => false
2305
+ def force_quotes?
2306
+ @writer_options[:force_quotes]
2307
+ end
2308
+
2309
+ # :call-seq:
2310
+ # csv.liberal_parsing? -> true or false
2311
+ #
2312
+ # Returns the value that determines whether illegal input is to be handled; used for parsing;
2313
+ # see {Option +liberal_parsing+}[#class-CSV-label-Option+liberal_parsing]:
2314
+ # CSV.new('').liberal_parsing? # => false
2315
+ def liberal_parsing?
2316
+ parser.liberal_parsing?
2317
+ end
2318
+
2319
+ # :call-seq:
2320
+ # csv.encoding -> encoding
2321
+ #
2322
+ # Returns the encoding used for parsing and generating;
2323
+ # see {Character Encodings (M17n or Multilingualization)}[#class-CSV-label-Character+Encodings+-28M17n+or+Multilingualization-29]:
2324
+ # CSV.new('').encoding # => #<Encoding:UTF-8>
2325
+ attr_reader :encoding
2326
+
2327
+ # :call-seq:
2328
+ # csv.lineno -> integer
2329
+ #
2330
+ # Returns the count of the rows parsed or generated.
2331
+ #
2332
+ # Parsing:
2333
+ # string = "foo,0\nbar,1\nbaz,2\n"
2334
+ # path = 't.csv'
2335
+ # File.write(path, string)
2336
+ # CSV.open(path) do |csv|
2337
+ # csv.each do |row|
2338
+ # p [csv.lineno, row]
2339
+ # end
2340
+ # end
2341
+ # Output:
2342
+ # [1, ["foo", "0"]]
2343
+ # [2, ["bar", "1"]]
2344
+ # [3, ["baz", "2"]]
2345
+ #
2346
+ # Generating:
2347
+ # CSV.generate do |csv|
2348
+ # p csv.lineno; csv << ['foo', 0]
2349
+ # p csv.lineno; csv << ['bar', 1]
2350
+ # p csv.lineno; csv << ['baz', 2]
2351
+ # end
2352
+ # Output:
2353
+ # 0
2354
+ # 1
2355
+ # 2
2356
+ def lineno
2357
+ if @writer
2358
+ @writer.lineno
2359
+ else
2360
+ parser.lineno
2361
+ end
2362
+ end
2363
+
2364
+ # :call-seq:
2365
+ # csv.line -> array
2366
+ #
2367
+ # Returns the line most recently read:
2368
+ # string = "foo,0\nbar,1\nbaz,2\n"
2369
+ # path = 't.csv'
2370
+ # File.write(path, string)
2371
+ # CSV.open(path) do |csv|
2372
+ # csv.each do |row|
2373
+ # p [csv.lineno, csv.line]
2374
+ # end
2375
+ # end
2376
+ # Output:
2377
+ # [1, "foo,0\n"]
2378
+ # [2, "bar,1\n"]
2379
+ # [3, "baz,2\n"]
2380
+ def line
2381
+ parser.line
2382
+ end
2383
+
2384
+ ### IO and StringIO Delegation ###
2385
+
2386
+ extend Forwardable
2387
+ def_delegators :@io, :binmode, :close, :close_read, :close_write,
2388
+ :closed?, :external_encoding, :fcntl,
2389
+ :fileno, :flush, :fsync, :internal_encoding,
2390
+ :isatty, :pid, :pos, :pos=, :reopen,
2391
+ :seek, :string, :sync, :sync=, :tell,
2392
+ :truncate, :tty?
2393
+
2394
+ def binmode?
2395
+ if @io.respond_to?(:binmode?)
2396
+ @io.binmode?
2397
+ else
2398
+ false
2399
+ end
2400
+ end
2401
+
2402
+ def flock(*args)
2403
+ raise NotImplementedError unless @io.respond_to?(:flock)
2404
+ @io.flock(*args)
2405
+ end
2406
+
2407
+ def ioctl(*args)
2408
+ raise NotImplementedError unless @io.respond_to?(:ioctl)
2409
+ @io.ioctl(*args)
2410
+ end
2411
+
2412
+ def path
2413
+ @io.path if @io.respond_to?(:path)
2414
+ end
2415
+
2416
+ def stat(*args)
2417
+ raise NotImplementedError unless @io.respond_to?(:stat)
2418
+ @io.stat(*args)
2419
+ end
2420
+
2421
+ def to_i
2422
+ raise NotImplementedError unless @io.respond_to?(:to_i)
2423
+ @io.to_i
2424
+ end
2425
+
2426
+ def to_io
2427
+ @io.respond_to?(:to_io) ? @io.to_io : @io
2428
+ end
2429
+
2430
+ def eof?
2431
+ parser.eof?
2432
+ end
2433
+ alias_method :eof, :eof?
2434
+
2435
+ # Rewinds the underlying IO object and resets CSV's lineno() counter.
2436
+ def rewind
2437
+ @parser = nil
2438
+ @writer.rewind if @writer
2439
+ @io.rewind
2440
+ end
2441
+
2442
+ ### End Delegation ###
2443
+
2444
+ # :call-seq:
2445
+ # csv << row -> self
2446
+ #
2447
+ # Appends a row to +self+.
2448
+ #
2449
+ # - Argument +row+ must be an \Array object or a CSV::Row object.
2450
+ # - The output stream must be open for writing.
2451
+ #
2452
+ # ---
2453
+ #
2454
+ # Append Arrays:
2455
+ # CSV.generate do |csv|
2456
+ # csv << ['foo', 0]
2457
+ # csv << ['bar', 1]
2458
+ # csv << ['baz', 2]
2459
+ # end # => "foo,0\nbar,1\nbaz,2\n"
2460
+ #
2461
+ # Append CSV::Rows:
2462
+ # headers = []
2463
+ # CSV.generate do |csv|
2464
+ # csv << CSV::Row.new(headers, ['foo', 0])
2465
+ # csv << CSV::Row.new(headers, ['bar', 1])
2466
+ # csv << CSV::Row.new(headers, ['baz', 2])
2467
+ # end # => "foo,0\nbar,1\nbaz,2\n"
2468
+ #
2469
+ # Headers in CSV::Row objects are not appended:
2470
+ # headers = ['Name', 'Count']
2471
+ # CSV.generate do |csv|
2472
+ # csv << CSV::Row.new(headers, ['foo', 0])
2473
+ # csv << CSV::Row.new(headers, ['bar', 1])
2474
+ # csv << CSV::Row.new(headers, ['baz', 2])
2475
+ # end # => "foo,0\nbar,1\nbaz,2\n"
2476
+ #
2477
+ # ---
2478
+ #
2479
+ # Raises an exception if +row+ is not an \Array or \CSV::Row:
2480
+ # CSV.generate do |csv|
2481
+ # # Raises NoMethodError (undefined method `collect' for :foo:Symbol)
2482
+ # csv << :foo
2483
+ # end
2484
+ #
2485
+ # Raises an exception if the output stream is not opened for writing:
2486
+ # path = 't.csv'
2487
+ # File.write(path, '')
2488
+ # File.open(path) do |file|
2489
+ # CSV.open(file) do |csv|
2490
+ # # Raises IOError (not opened for writing)
2491
+ # csv << ['foo', 0]
2492
+ # end
2493
+ # end
2494
+ def <<(row)
2495
+ writer << row
2496
+ self
2497
+ end
2498
+ alias_method :add_row, :<<
2499
+ alias_method :puts, :<<
2500
+
2501
+ # :call-seq:
2502
+ # convert(converter_name) -> array_of_procs
2503
+ # convert {|field, field_info| ... } -> array_of_procs
2504
+ #
2505
+ # - With no block, installs a field converter (a \Proc).
2506
+ # - With a block, defines and installs a custom field converter.
2507
+ # - Returns the \Array of installed field converters.
2508
+ #
2509
+ # - Argument +converter_name+, if given, should be the name
2510
+ # of an existing field converter.
2511
+ #
2512
+ # See {Field Converters}[#class-CSV-label-Field+Converters].
2513
+ # ---
2514
+ #
2515
+ # With no block, installs a field converter:
2516
+ # csv = CSV.new('')
2517
+ # csv.convert(:integer)
2518
+ # csv.convert(:float)
2519
+ # csv.convert(:date)
2520
+ # csv.converters # => [:integer, :float, :date]
2521
+ #
2522
+ # ---
2523
+ #
2524
+ # The block, if given, is called for each field:
2525
+ # - Argument +field+ is the field value.
2526
+ # - Argument +field_info+ is a CSV::FieldInfo object
2527
+ # containing details about the field.
2528
+ #
2529
+ # The examples here assume the prior execution of:
2530
+ # string = "foo,0\nbar,1\nbaz,2\n"
2531
+ # path = 't.csv'
2532
+ # File.write(path, string)
2533
+ #
2534
+ # Example giving a block:
2535
+ # csv = CSV.open(path)
2536
+ # csv.convert {|field, field_info| p [field, field_info]; field.upcase }
2537
+ # csv.read # => [["FOO", "0"], ["BAR", "1"], ["BAZ", "2"]]
2538
+ #
2539
+ # Output:
2540
+ # ["foo", #<struct CSV::FieldInfo index=0, line=1, header=nil>]
2541
+ # ["0", #<struct CSV::FieldInfo index=1, line=1, header=nil>]
2542
+ # ["bar", #<struct CSV::FieldInfo index=0, line=2, header=nil>]
2543
+ # ["1", #<struct CSV::FieldInfo index=1, line=2, header=nil>]
2544
+ # ["baz", #<struct CSV::FieldInfo index=0, line=3, header=nil>]
2545
+ # ["2", #<struct CSV::FieldInfo index=1, line=3, header=nil>]
2546
+ #
2547
+ # The block need not return a \String object:
2548
+ # csv = CSV.open(path)
2549
+ # csv.convert {|field, field_info| field.to_sym }
2550
+ # csv.read # => [[:foo, :"0"], [:bar, :"1"], [:baz, :"2"]]
2551
+ #
2552
+ # If +converter_name+ is given, the block is not called:
2553
+ # csv = CSV.open(path)
2554
+ # csv.convert(:integer) {|field, field_info| fail 'Cannot happen' }
2555
+ # csv.read # => [["foo", 0], ["bar", 1], ["baz", 2]]
2556
+ #
2557
+ # ---
2558
+ #
2559
+ # Raises a parse-time exception if +converter_name+ is not the name of a built-in
2560
+ # field converter:
2561
+ # csv = CSV.open(path)
2562
+ # csv.convert(:nosuch) => [nil]
2563
+ # # Raises NoMethodError (undefined method `arity' for nil:NilClass)
2564
+ # csv.read
2565
+ def convert(name = nil, &converter)
2566
+ parser_fields_converter.add_converter(name, &converter)
2567
+ end
2568
+
2569
+ # :call-seq:
2570
+ # header_convert(converter_name) -> array_of_procs
2571
+ # header_convert {|header, field_info| ... } -> array_of_procs
2572
+ #
2573
+ # - With no block, installs a header converter (a \Proc).
2574
+ # - With a block, defines and installs a custom header converter.
2575
+ # - Returns the \Array of installed header converters.
2576
+ #
2577
+ # - Argument +converter_name+, if given, should be the name
2578
+ # of an existing header converter.
2579
+ #
2580
+ # See {Header Converters}[#class-CSV-label-Header+Converters].
2581
+ # ---
2582
+ #
2583
+ # With no block, installs a header converter:
2584
+ # csv = CSV.new('')
2585
+ # csv.header_convert(:symbol)
2586
+ # csv.header_convert(:downcase)
2587
+ # csv.header_converters # => [:symbol, :downcase]
2588
+ #
2589
+ # ---
2590
+ #
2591
+ # The block, if given, is called for each header:
2592
+ # - Argument +header+ is the header value.
2593
+ # - Argument +field_info+ is a CSV::FieldInfo object
2594
+ # containing details about the header.
2595
+ #
2596
+ # The examples here assume the prior execution of:
2597
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
2598
+ # path = 't.csv'
2599
+ # File.write(path, string)
2600
+ #
2601
+ # Example giving a block:
2602
+ # csv = CSV.open(path, headers: true)
2603
+ # csv.header_convert {|header, field_info| p [header, field_info]; header.upcase }
2604
+ # table = csv.read
2605
+ # table # => #<CSV::Table mode:col_or_row row_count:4>
2606
+ # table.headers # => ["NAME", "VALUE"]
2607
+ #
2608
+ # Output:
2609
+ # ["Name", #<struct CSV::FieldInfo index=0, line=1, header=nil>]
2610
+ # ["Value", #<struct CSV::FieldInfo index=1, line=1, header=nil>]
2611
+
2612
+ # The block need not return a \String object:
2613
+ # csv = CSV.open(path, headers: true)
2614
+ # csv.header_convert {|header, field_info| header.to_sym }
2615
+ # table = csv.read
2616
+ # table.headers # => [:Name, :Value]
2617
+ #
2618
+ # If +converter_name+ is given, the block is not called:
2619
+ # csv = CSV.open(path, headers: true)
2620
+ # csv.header_convert(:downcase) {|header, field_info| fail 'Cannot happen' }
2621
+ # table = csv.read
2622
+ # table.headers # => ["name", "value"]
2623
+ # ---
2624
+ #
2625
+ # Raises a parse-time exception if +converter_name+ is not the name of a built-in
2626
+ # field converter:
2627
+ # csv = CSV.open(path, headers: true)
2628
+ # csv.header_convert(:nosuch)
2629
+ # # Raises NoMethodError (undefined method `arity' for nil:NilClass)
2630
+ # csv.read
2631
+ def header_convert(name = nil, &converter)
2632
+ header_fields_converter.add_converter(name, &converter)
2633
+ end
2634
+
2635
+ include Enumerable
2636
+
2637
+ # :call-seq:
2638
+ # csv.each -> enumerator
2639
+ # csv.each {|row| ...}
2640
+ #
2641
+ # Calls the block with each successive row.
2642
+ # The data source must be opened for reading.
2643
+ #
2644
+ # Without headers:
2645
+ # string = "foo,0\nbar,1\nbaz,2\n"
2646
+ # csv = CSV.new(string)
2647
+ # csv.each do |row|
2648
+ # p row
2649
+ # end
2650
+ # Output:
2651
+ # ["foo", "0"]
2652
+ # ["bar", "1"]
2653
+ # ["baz", "2"]
2654
+ #
2655
+ # With headers:
2656
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
2657
+ # csv = CSV.new(string, headers: true)
2658
+ # csv.each do |row|
2659
+ # p row
2660
+ # end
2661
+ # Output:
2662
+ # <CSV::Row "Name":"foo" "Value":"0">
2663
+ # <CSV::Row "Name":"bar" "Value":"1">
2664
+ # <CSV::Row "Name":"baz" "Value":"2">
2665
+ #
2666
+ # ---
2667
+ #
2668
+ # Raises an exception if the source is not opened for reading:
2669
+ # string = "foo,0\nbar,1\nbaz,2\n"
2670
+ # csv = CSV.new(string)
2671
+ # csv.close
2672
+ # # Raises IOError (not opened for reading)
2673
+ # csv.each do |row|
2674
+ # p row
2675
+ # end
2676
+ def each(&block)
2677
+ parser.parse(&block)
2678
+ end
2679
+
2680
+ # :call-seq:
2681
+ # csv.read -> array or csv_table
2682
+ #
2683
+ # Forms the remaining rows from +self+ into:
2684
+ # - A CSV::Table object, if headers are in use.
2685
+ # - An \Array of Arrays, otherwise.
2686
+ #
2687
+ # The data source must be opened for reading.
2688
+ #
2689
+ # Without headers:
2690
+ # string = "foo,0\nbar,1\nbaz,2\n"
2691
+ # path = 't.csv'
2692
+ # File.write(path, string)
2693
+ # csv = CSV.open(path)
2694
+ # csv.read # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
2695
+ #
2696
+ # With headers:
2697
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
2698
+ # path = 't.csv'
2699
+ # File.write(path, string)
2700
+ # csv = CSV.open(path, headers: true)
2701
+ # csv.read # => #<CSV::Table mode:col_or_row row_count:4>
2702
+ #
2703
+ # ---
2704
+ #
2705
+ # Raises an exception if the source is not opened for reading:
2706
+ # string = "foo,0\nbar,1\nbaz,2\n"
2707
+ # csv = CSV.new(string)
2708
+ # csv.close
2709
+ # # Raises IOError (not opened for reading)
2710
+ # csv.read
2711
+ def read
2712
+ rows = to_a
2713
+ if parser.use_headers?
2714
+ Table.new(rows, headers: parser.headers)
2715
+ else
2716
+ rows
2717
+ end
2718
+ end
2719
+ alias_method :readlines, :read
2720
+
2721
+ # :call-seq:
2722
+ # csv.header_row? -> true or false
2723
+ #
2724
+ # Returns +true+ if the next row to be read is a header row\;
2725
+ # +false+ otherwise.
2726
+ #
2727
+ # Without headers:
2728
+ # string = "foo,0\nbar,1\nbaz,2\n"
2729
+ # csv = CSV.new(string)
2730
+ # csv.header_row? # => false
2731
+ #
2732
+ # With headers:
2733
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
2734
+ # csv = CSV.new(string, headers: true)
2735
+ # csv.header_row? # => true
2736
+ # csv.shift # => #<CSV::Row "Name":"foo" "Value":"0">
2737
+ # csv.header_row? # => false
2738
+ #
2739
+ # ---
2740
+ #
2741
+ # Raises an exception if the source is not opened for reading:
2742
+ # string = "foo,0\nbar,1\nbaz,2\n"
2743
+ # csv = CSV.new(string)
2744
+ # csv.close
2745
+ # # Raises IOError (not opened for reading)
2746
+ # csv.header_row?
2747
+ def header_row?
2748
+ parser.header_row?
2749
+ end
2750
+
2751
+ # :call-seq:
2752
+ # csv.shift -> array, csv_row, or nil
2753
+ #
2754
+ # Returns the next row of data as:
2755
+ # - An \Array if no headers are used.
2756
+ # - A CSV::Row object if headers are used.
2757
+ #
2758
+ # The data source must be opened for reading.
2759
+ #
2760
+ # Without headers:
2761
+ # string = "foo,0\nbar,1\nbaz,2\n"
2762
+ # csv = CSV.new(string)
2763
+ # csv.shift # => ["foo", "0"]
2764
+ # csv.shift # => ["bar", "1"]
2765
+ # csv.shift # => ["baz", "2"]
2766
+ # csv.shift # => nil
2767
+ #
2768
+ # With headers:
2769
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
2770
+ # csv = CSV.new(string, headers: true)
2771
+ # csv.shift # => #<CSV::Row "Name":"foo" "Value":"0">
2772
+ # csv.shift # => #<CSV::Row "Name":"bar" "Value":"1">
2773
+ # csv.shift # => #<CSV::Row "Name":"baz" "Value":"2">
2774
+ # csv.shift # => nil
2775
+ #
2776
+ # ---
2777
+ #
2778
+ # Raises an exception if the source is not opened for reading:
2779
+ # string = "foo,0\nbar,1\nbaz,2\n"
2780
+ # csv = CSV.new(string)
2781
+ # csv.close
2782
+ # # Raises IOError (not opened for reading)
2783
+ # csv.shift
2784
+ def shift
2785
+ parser.parse do |row|
2786
+ return row
2787
+ end
2788
+ nil
2789
+ end
2790
+ alias_method :gets, :shift
2791
+ alias_method :readline, :shift
2792
+
2793
+ # :call-seq:
2794
+ # csv.inspect -> string
2795
+ #
2796
+ # Returns a \String showing certain properties of +self+:
2797
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
2798
+ # csv = CSV.new(string, headers: true)
2799
+ # s = csv.inspect
2800
+ # s # => "#<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:\",\" row_sep:\"\\n\" quote_char:\"\\\"\" headers:true>"
2801
+ def inspect
2802
+ str = ["#<", self.class.to_s, " io_type:"]
2803
+ # show type of wrapped IO
2804
+ if @io == $stdout then str << "$stdout"
2805
+ elsif @io == $stdin then str << "$stdin"
2806
+ elsif @io == $stderr then str << "$stderr"
2807
+ else str << @io.class.to_s
2808
+ end
2809
+ # show IO.path(), if available
2810
+ if @io.respond_to?(:path) and (p = @io.path)
2811
+ str << " io_path:" << p.inspect
2812
+ end
2813
+ # show encoding
2814
+ str << " encoding:" << @encoding.name
2815
+ # show other attributes
2816
+ ["lineno", "col_sep", "row_sep", "quote_char"].each do |attr_name|
2817
+ if a = __send__(attr_name)
2818
+ str << " " << attr_name << ":" << a.inspect
2819
+ end
2820
+ end
2821
+ ["skip_blanks", "liberal_parsing"].each do |attr_name|
2822
+ if a = __send__("#{attr_name}?")
2823
+ str << " " << attr_name << ":" << a.inspect
2824
+ end
2825
+ end
2826
+ _headers = headers
2827
+ str << " headers:" << _headers.inspect if _headers
2828
+ str << ">"
2829
+ begin
2830
+ str.join('')
2831
+ rescue # any encoding error
2832
+ str.map do |s|
2833
+ e = Encoding::Converter.asciicompat_encoding(s.encoding)
2834
+ e ? s.encode(e) : s.force_encoding("ASCII-8BIT")
2835
+ end.join('')
2836
+ end
2837
+ end
2838
+
2839
+ private
2840
+
2841
+ def determine_encoding(encoding, internal_encoding)
2842
+ # honor the IO encoding if we can, otherwise default to ASCII-8BIT
2843
+ io_encoding = raw_encoding
2844
+ return io_encoding if io_encoding
2845
+
2846
+ return Encoding.find(internal_encoding) if internal_encoding
2847
+
2848
+ if encoding
2849
+ encoding, = encoding.split(":", 2) if encoding.is_a?(String)
2850
+ return Encoding.find(encoding)
2851
+ end
2852
+
2853
+ Encoding.default_internal || Encoding.default_external
2854
+ end
2855
+
2856
+ def normalize_converters(converters)
2857
+ converters ||= []
2858
+ unless converters.is_a?(Array)
2859
+ converters = [converters]
2860
+ end
2861
+ converters.collect do |converter|
2862
+ case converter
2863
+ when Proc # custom code block
2864
+ [nil, converter]
2865
+ else # by name
2866
+ [converter, nil]
2867
+ end
2868
+ end
2869
+ end
2870
+
2871
+ #
2872
+ # Processes +fields+ with <tt>@converters</tt>, or <tt>@header_converters</tt>
2873
+ # if +headers+ is passed as +true+, returning the converted field set. Any
2874
+ # converter that changes the field into something other than a String halts
2875
+ # the pipeline of conversion for that field. This is primarily an efficiency
2876
+ # shortcut.
2877
+ #
2878
+ def convert_fields(fields, headers = false)
2879
+ if headers
2880
+ header_fields_converter.convert(fields, nil, 0)
2881
+ else
2882
+ parser_fields_converter.convert(fields, @headers, lineno)
2883
+ end
2884
+ end
2885
+
2886
+ #
2887
+ # Returns the encoding of the internal IO object.
2888
+ #
2889
+ def raw_encoding
2890
+ if @io.respond_to? :internal_encoding
2891
+ @io.internal_encoding || @io.external_encoding
2892
+ elsif @io.respond_to? :encoding
2893
+ @io.encoding
2894
+ else
2895
+ nil
2896
+ end
2897
+ end
2898
+
2899
+ def parser_fields_converter
2900
+ @parser_fields_converter ||= build_parser_fields_converter
2901
+ end
2902
+
2903
+ def build_parser_fields_converter
2904
+ specific_options = {
2905
+ builtin_converters_name: :Converters,
2906
+ }
2907
+ options = @base_fields_converter_options.merge(specific_options)
2908
+ build_fields_converter(@initial_converters, options)
2909
+ end
2910
+
2911
+ def header_fields_converter
2912
+ @header_fields_converter ||= build_header_fields_converter
2913
+ end
2914
+
2915
+ def build_header_fields_converter
2916
+ specific_options = {
2917
+ builtin_converters_name: :HeaderConverters,
2918
+ accept_nil: true,
2919
+ }
2920
+ options = @base_fields_converter_options.merge(specific_options)
2921
+ build_fields_converter(@initial_header_converters, options)
2922
+ end
2923
+
2924
+ def writer_fields_converter
2925
+ @writer_fields_converter ||= build_writer_fields_converter
2926
+ end
2927
+
2928
+ def build_writer_fields_converter
2929
+ build_fields_converter(@initial_write_converters,
2930
+ @write_fields_converter_options)
2931
+ end
2932
+
2933
+ def build_fields_converter(initial_converters, options)
2934
+ fields_converter = FieldsConverter.new(options)
2935
+ normalize_converters(initial_converters).each do |name, converter|
2936
+ fields_converter.add_converter(name, &converter)
2937
+ end
2938
+ fields_converter
2939
+ end
2940
+
2941
+ def parser
2942
+ @parser ||= Parser.new(@io, parser_options)
2943
+ end
2944
+
2945
+ def parser_options
2946
+ @parser_options.merge(header_fields_converter: header_fields_converter,
2947
+ fields_converter: parser_fields_converter)
2948
+ end
2949
+
2950
+ def writer
2951
+ @writer ||= Writer.new(@io, writer_options)
2952
+ end
2953
+
2954
+ def writer_options
2955
+ @writer_options.merge(header_fields_converter: header_fields_converter,
2956
+ fields_converter: writer_fields_converter)
2957
+ end
2958
+ end
2959
+
2960
+ # Passes +args+ to CSV::instance.
2961
+ #
2962
+ # CSV("CSV,data").read
2963
+ # #=> [["CSV", "data"]]
2964
+ #
2965
+ # If a block is given, the instance is passed the block and the return value
2966
+ # becomes the return value of the block.
2967
+ #
2968
+ # CSV("CSV,data") { |c|
2969
+ # c.read.any? { |a| a.include?("data") }
2970
+ # } #=> true
2971
+ #
2972
+ # CSV("CSV,data") { |c|
2973
+ # c.read.any? { |a| a.include?("zombies") }
2974
+ # } #=> false
2975
+ #
2976
+ # CSV options may also be given.
2977
+ #
2978
+ # io = StringIO.new
2979
+ # CSV(io, col_sep: ";") { |csv| csv << ["a", "b", "c"] }
2980
+ #
2981
+ # This API is not Ractor-safe.
2982
+ #
2983
+ def CSV(*args, **options, &block)
2984
+ CSV.instance(*args, **options, &block)
2985
+ end
2986
+
2987
+ require_relative "csv/version"
2988
+ require_relative "csv/core_ext/array"
2989
+ require_relative "csv/core_ext/string"