brakeman 6.2.2 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (202) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +17 -0
  3. data/bundle/load.rb +7 -6
  4. data/bundle/ruby/3.1.0/gems/csv-3.3.2/LICENSE.txt +33 -0
  5. data/bundle/ruby/3.1.0/gems/csv-3.3.2/NEWS.md +965 -0
  6. data/bundle/ruby/3.1.0/gems/csv-3.3.2/README.md +55 -0
  7. data/bundle/ruby/3.1.0/gems/csv-3.3.2/lib/csv/core_ext/array.rb +9 -0
  8. data/bundle/ruby/3.1.0/gems/csv-3.3.2/lib/csv/core_ext/string.rb +9 -0
  9. data/bundle/ruby/3.1.0/gems/csv-3.3.2/lib/csv/fields_converter.rb +96 -0
  10. data/bundle/ruby/3.1.0/gems/csv-3.3.2/lib/csv/input_record_separator.rb +18 -0
  11. data/bundle/ruby/3.1.0/gems/csv-3.3.2/lib/csv/parser.rb +1292 -0
  12. data/bundle/ruby/3.1.0/gems/csv-3.3.2/lib/csv/row.rb +757 -0
  13. data/bundle/ruby/3.1.0/gems/csv-3.3.2/lib/csv/table.rb +1055 -0
  14. data/bundle/ruby/3.1.0/gems/csv-3.3.2/lib/csv/version.rb +6 -0
  15. data/bundle/ruby/3.1.0/gems/csv-3.3.2/lib/csv/writer.rb +209 -0
  16. data/bundle/ruby/3.1.0/gems/csv-3.3.2/lib/csv.rb +3017 -0
  17. data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/config.rb +22 -26
  18. data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/history.rb +3 -3
  19. data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/io/ansi.rb +64 -111
  20. data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/io/dumb.rb +16 -2
  21. data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/io/windows.rb +77 -60
  22. data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/io.rb +14 -0
  23. data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/key_actor/base.rb +10 -4
  24. data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/key_actor/emacs.rb +96 -96
  25. data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/key_actor/vi_command.rb +182 -182
  26. data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/key_actor/vi_insert.rb +137 -137
  27. data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/key_stroke.rb +26 -16
  28. data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/line_editor.rb +238 -404
  29. data/bundle/ruby/3.1.0/gems/reline-0.6.0/lib/reline/unicode.rb +415 -0
  30. data/bundle/ruby/3.1.0/gems/reline-0.6.0/lib/reline/version.rb +3 -0
  31. data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline.rb +18 -18
  32. data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/NEWS.md +50 -0
  33. data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/parsers/baseparser.rb +54 -45
  34. data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/parsers/pullparser.rb +4 -0
  35. data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/rexml.rb +1 -1
  36. data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/source.rb +45 -4
  37. data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/text.rb +15 -40
  38. data/bundle/ruby/3.1.0/gems/{sexp_processor-4.17.2 → sexp_processor-4.17.3}/History.rdoc +6 -0
  39. data/bundle/ruby/3.1.0/gems/{sexp_processor-4.17.2 → sexp_processor-4.17.3}/lib/sexp.rb +1 -1
  40. data/bundle/ruby/3.1.0/gems/{sexp_processor-4.17.2 → sexp_processor-4.17.3}/lib/sexp_processor.rb +1 -1
  41. data/bundle/ruby/3.1.0/gems/terminal-table-3.0.2/History.rdoc +142 -0
  42. data/bundle/ruby/3.1.0/gems/terminal-table-3.0.2/README.md +417 -0
  43. data/bundle/ruby/3.1.0/gems/{terminal-table-1.8.0 → terminal-table-3.0.2}/lib/terminal-table/cell.rb +8 -8
  44. data/bundle/ruby/3.1.0/gems/{terminal-table-1.8.0 → terminal-table-3.0.2}/lib/terminal-table/row.rb +18 -4
  45. data/bundle/ruby/3.1.0/gems/terminal-table-3.0.2/lib/terminal-table/separator.rb +66 -0
  46. data/bundle/ruby/3.1.0/gems/terminal-table-3.0.2/lib/terminal-table/style.rb +284 -0
  47. data/bundle/ruby/3.1.0/gems/{terminal-table-1.8.0 → terminal-table-3.0.2}/lib/terminal-table/table.rb +47 -18
  48. data/bundle/ruby/3.1.0/gems/terminal-table-3.0.2/lib/terminal-table/util.rb +13 -0
  49. data/bundle/ruby/3.1.0/gems/{terminal-table-1.8.0 → terminal-table-3.0.2}/lib/terminal-table/version.rb +1 -1
  50. data/bundle/ruby/3.1.0/gems/{terminal-table-1.8.0 → terminal-table-3.0.2}/lib/terminal-table.rb +2 -2
  51. data/bundle/ruby/3.1.0/gems/{terminal-table-1.8.0 → terminal-table-3.0.2}/terminal-table.gemspec +3 -3
  52. data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/creole.rb +2 -0
  53. data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/liquid.rb +0 -3
  54. data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/mapping.rb +3 -3
  55. data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/rdoc.rb +0 -8
  56. data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/template.rb +27 -3
  57. data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt.rb +1 -4
  58. data/bundle/ruby/3.1.0/gems/{unicode-display_width-1.8.0 → unicode-display_width-2.6.0}/CHANGELOG.md +65 -2
  59. data/bundle/ruby/3.1.0/gems/{unicode-display_width-1.8.0 → unicode-display_width-2.6.0}/MIT-LICENSE.txt +1 -1
  60. data/bundle/ruby/3.1.0/gems/{unicode-display_width-1.8.0 → unicode-display_width-2.6.0}/README.md +67 -20
  61. data/bundle/ruby/3.1.0/gems/unicode-display_width-2.6.0/data/display_width.marshal.gz +0 -0
  62. data/bundle/ruby/3.1.0/gems/unicode-display_width-2.6.0/lib/unicode/display_width/constants.rb +10 -0
  63. data/bundle/ruby/3.1.0/gems/unicode-display_width-2.6.0/lib/unicode/display_width/index.rb +34 -0
  64. data/bundle/ruby/3.1.0/gems/unicode-display_width-2.6.0/lib/unicode/display_width/no_string_ext.rb +8 -0
  65. data/bundle/ruby/3.1.0/gems/unicode-display_width-2.6.0/lib/unicode/display_width/string_ext.rb +9 -0
  66. data/bundle/ruby/3.1.0/gems/unicode-display_width-2.6.0/lib/unicode/display_width.rb +123 -0
  67. data/lib/brakeman/app_tree.rb +23 -18
  68. data/lib/brakeman/checks/check_deserialize.rb +4 -1
  69. data/lib/brakeman/checks/check_evaluation.rb +20 -2
  70. data/lib/brakeman/checks/check_model_attr_accessible.rb +1 -0
  71. data/lib/brakeman/file_parser.rb +2 -1
  72. data/lib/brakeman/options.rb +8 -5
  73. data/lib/brakeman/processors/alias_processor.rb +6 -2
  74. data/lib/brakeman/processors/lib/file_type_detector.rb +9 -7
  75. data/lib/brakeman/report/ignore/config.rb +0 -1
  76. data/lib/brakeman/report/report_sarif.rb +122 -2
  77. data/lib/brakeman/rescanner.rb +40 -390
  78. data/lib/brakeman/scanner.rb +62 -38
  79. data/lib/brakeman/tracker/file_cache.rb +83 -0
  80. data/lib/brakeman/tracker.rb +19 -2
  81. data/lib/brakeman/version.rb +1 -1
  82. data/lib/brakeman.rb +12 -2
  83. metadata +170 -160
  84. data/bundle/ruby/3.1.0/gems/reline-0.5.10/lib/reline/terminfo.rb +0 -158
  85. data/bundle/ruby/3.1.0/gems/reline-0.5.10/lib/reline/unicode.rb +0 -671
  86. data/bundle/ruby/3.1.0/gems/reline-0.5.10/lib/reline/version.rb +0 -3
  87. data/bundle/ruby/3.1.0/gems/terminal-table-1.8.0/History.rdoc +0 -85
  88. data/bundle/ruby/3.1.0/gems/terminal-table-1.8.0/README.rdoc +0 -247
  89. data/bundle/ruby/3.1.0/gems/terminal-table-1.8.0/lib/terminal-table/separator.rb +0 -14
  90. data/bundle/ruby/3.1.0/gems/terminal-table-1.8.0/lib/terminal-table/style.rb +0 -79
  91. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/erubis.rb +0 -51
  92. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/maruku.rb +0 -10
  93. data/bundle/ruby/3.1.0/gems/tilt-2.4.0/lib/tilt/wikicloth.rb +0 -12
  94. data/bundle/ruby/3.1.0/gems/unicode-display_width-1.8.0/data/display_width.marshal.gz +0 -0
  95. data/bundle/ruby/3.1.0/gems/unicode-display_width-1.8.0/lib/unicode/display_width/constants.rb +0 -8
  96. data/bundle/ruby/3.1.0/gems/unicode-display_width-1.8.0/lib/unicode/display_width/index.rb +0 -12
  97. data/bundle/ruby/3.1.0/gems/unicode-display_width-1.8.0/lib/unicode/display_width/no_string_ext.rb +0 -7
  98. data/bundle/ruby/3.1.0/gems/unicode-display_width-1.8.0/lib/unicode/display_width/string_ext.rb +0 -17
  99. data/bundle/ruby/3.1.0/gems/unicode-display_width-1.8.0/lib/unicode/display_width.rb +0 -51
  100. /data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/BSDL +0 -0
  101. /data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/COPYING +0 -0
  102. /data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/README.md +0 -0
  103. /data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/face.rb +0 -0
  104. /data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/key_actor/composite.rb +0 -0
  105. /data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/key_actor.rb +0 -0
  106. /data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/kill_ring.rb +0 -0
  107. /data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/lib/reline/unicode/east_asian_width.rb +0 -0
  108. /data/bundle/ruby/3.1.0/gems/{reline-0.5.10 → reline-0.6.0}/license_of_rb-readline +0 -0
  109. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/LICENSE.txt +0 -0
  110. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/README.md +0 -0
  111. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/attlistdecl.rb +0 -0
  112. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/attribute.rb +0 -0
  113. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/cdata.rb +0 -0
  114. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/child.rb +0 -0
  115. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/comment.rb +0 -0
  116. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/doctype.rb +0 -0
  117. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/document.rb +0 -0
  118. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/dtd/attlistdecl.rb +0 -0
  119. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/dtd/dtd.rb +0 -0
  120. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/dtd/elementdecl.rb +0 -0
  121. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/dtd/entitydecl.rb +0 -0
  122. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/dtd/notationdecl.rb +0 -0
  123. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/element.rb +0 -0
  124. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/encoding.rb +0 -0
  125. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/entity.rb +0 -0
  126. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/formatters/default.rb +0 -0
  127. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/formatters/pretty.rb +0 -0
  128. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/formatters/transitive.rb +0 -0
  129. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/functions.rb +0 -0
  130. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/instruction.rb +0 -0
  131. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/light/node.rb +0 -0
  132. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/namespace.rb +0 -0
  133. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/node.rb +0 -0
  134. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/output.rb +0 -0
  135. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/parent.rb +0 -0
  136. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/parseexception.rb +0 -0
  137. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/parsers/lightparser.rb +0 -0
  138. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/parsers/sax2parser.rb +0 -0
  139. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/parsers/streamparser.rb +0 -0
  140. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/parsers/treeparser.rb +0 -0
  141. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/parsers/ultralightparser.rb +0 -0
  142. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/parsers/xpathparser.rb +0 -0
  143. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/quickpath.rb +0 -0
  144. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/sax2listener.rb +0 -0
  145. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/security.rb +0 -0
  146. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/streamlistener.rb +0 -0
  147. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/undefinednamespaceexception.rb +0 -0
  148. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/validation/relaxng.rb +0 -0
  149. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/validation/validation.rb +0 -0
  150. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/validation/validationexception.rb +0 -0
  151. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/xmldecl.rb +0 -0
  152. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/xmltokens.rb +0 -0
  153. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/xpath.rb +0 -0
  154. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml/xpath_parser.rb +0 -0
  155. /data/bundle/ruby/3.1.0/gems/{rexml-3.3.8 → rexml-3.4.0}/lib/rexml.rb +0 -0
  156. /data/bundle/ruby/3.1.0/gems/{sexp_processor-4.17.2 → sexp_processor-4.17.3}/Manifest.txt +0 -0
  157. /data/bundle/ruby/3.1.0/gems/{sexp_processor-4.17.2 → sexp_processor-4.17.3}/README.rdoc +0 -0
  158. /data/bundle/ruby/3.1.0/gems/{sexp_processor-4.17.2 → sexp_processor-4.17.3}/lib/composite_sexp_processor.rb +0 -0
  159. /data/bundle/ruby/3.1.0/gems/{sexp_processor-4.17.2 → sexp_processor-4.17.3}/lib/pt_testcase.rb +0 -0
  160. /data/bundle/ruby/3.1.0/gems/{sexp_processor-4.17.2 → sexp_processor-4.17.3}/lib/sexp_matcher.rb +0 -0
  161. /data/bundle/ruby/3.1.0/gems/{sexp_processor-4.17.2 → sexp_processor-4.17.3}/lib/strict_sexp.rb +0 -0
  162. /data/bundle/ruby/3.1.0/gems/{sexp_processor-4.17.2 → sexp_processor-4.17.3}/lib/unique.rb +0 -0
  163. /data/bundle/ruby/3.1.0/gems/{terminal-table-1.8.0 → terminal-table-3.0.2}/Gemfile +0 -0
  164. /data/bundle/ruby/3.1.0/gems/{terminal-table-1.8.0 → terminal-table-3.0.2}/LICENSE.txt +0 -0
  165. /data/bundle/ruby/3.1.0/gems/{terminal-table-1.8.0 → terminal-table-3.0.2}/Manifest +0 -0
  166. /data/bundle/ruby/3.1.0/gems/{terminal-table-1.8.0 → terminal-table-3.0.2}/Todo.rdoc +0 -0
  167. /data/bundle/ruby/3.1.0/gems/{terminal-table-1.8.0 → terminal-table-3.0.2}/lib/terminal-table/import.rb +0 -0
  168. /data/bundle/ruby/3.1.0/gems/{terminal-table-1.8.0 → terminal-table-3.0.2}/lib/terminal-table/table_helper.rb +0 -0
  169. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/COPYING +0 -0
  170. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/_emacs_org.rb +0 -0
  171. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/_handlebars.rb +0 -0
  172. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/_jbuilder.rb +0 -0
  173. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/_org.rb +0 -0
  174. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/asciidoc.rb +0 -0
  175. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/babel.rb +0 -0
  176. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/builder.rb +0 -0
  177. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/cli.rb +0 -0
  178. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/coffee.rb +0 -0
  179. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/commonmarker.rb +0 -0
  180. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/csv.rb +0 -0
  181. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/erb.rb +0 -0
  182. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/erubi.rb +0 -0
  183. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/etanni.rb +0 -0
  184. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/haml.rb +0 -0
  185. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/kramdown.rb +0 -0
  186. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/livescript.rb +0 -0
  187. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/markaby.rb +0 -0
  188. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/nokogiri.rb +0 -0
  189. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/pandoc.rb +0 -0
  190. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/pipeline.rb +0 -0
  191. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/plain.rb +0 -0
  192. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/prawn.rb +0 -0
  193. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/radius.rb +0 -0
  194. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/rdiscount.rb +0 -0
  195. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/redcarpet.rb +0 -0
  196. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/redcloth.rb +0 -0
  197. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/rst-pandoc.rb +0 -0
  198. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/sass.rb +0 -0
  199. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/slim.rb +0 -0
  200. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/string.rb +0 -0
  201. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/typescript.rb +0 -0
  202. /data/bundle/ruby/3.1.0/gems/{tilt-2.4.0 → tilt-2.5.0}/lib/tilt/yajl.rb +0 -0
@@ -0,0 +1,1055 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ class CSV
6
+ # = \CSV::Table
7
+ # A \CSV::Table instance represents \CSV data.
8
+ # (see {class CSV}[../CSV.html]).
9
+ #
10
+ # The instance may have:
11
+ # - Rows: each is a Table::Row object.
12
+ # - Headers: names for the columns.
13
+ #
14
+ # === Instance Methods
15
+ #
16
+ # \CSV::Table has three groups of instance methods:
17
+ # - Its own internally defined instance methods.
18
+ # - Methods included by module Enumerable.
19
+ # - Methods delegated to class Array.:
20
+ # * Array#empty?
21
+ # * Array#length
22
+ # * Array#size
23
+ #
24
+ # == Creating a \CSV::Table Instance
25
+ #
26
+ # Commonly, a new \CSV::Table instance is created by parsing \CSV source
27
+ # using headers:
28
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
29
+ # table = CSV.parse(source, headers: true)
30
+ # table.class # => CSV::Table
31
+ #
32
+ # You can also create an instance directly. See ::new.
33
+ #
34
+ # == Headers
35
+ #
36
+ # If a table has headers, the headers serve as labels for the columns of data.
37
+ # Each header serves as the label for its column.
38
+ #
39
+ # The headers for a \CSV::Table object are stored as an \Array of Strings.
40
+ #
41
+ # Commonly, headers are defined in the first row of \CSV source:
42
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
43
+ # table = CSV.parse(source, headers: true)
44
+ # table.headers # => ["Name", "Value"]
45
+ #
46
+ # If no headers are defined, the \Array is empty:
47
+ # table = CSV::Table.new([])
48
+ # table.headers # => []
49
+ #
50
+ # == Access Modes
51
+ #
52
+ # \CSV::Table provides three modes for accessing table data:
53
+ # - \Row mode.
54
+ # - Column mode.
55
+ # - Mixed mode (the default for a new table).
56
+ #
57
+ # The access mode for a\CSV::Table instance affects the behavior
58
+ # of some of its instance methods:
59
+ # - #[]
60
+ # - #[]=
61
+ # - #delete
62
+ # - #delete_if
63
+ # - #each
64
+ # - #values_at
65
+ #
66
+ # === \Row Mode
67
+ #
68
+ # Set a table to row mode with method #by_row!:
69
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
70
+ # table = CSV.parse(source, headers: true)
71
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
72
+ #
73
+ # Specify a single row by an \Integer index:
74
+ # # Get a row.
75
+ # table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
76
+ # # Set a row, then get it.
77
+ # table[1] = CSV::Row.new(['Name', 'Value'], ['bam', 3])
78
+ # table[1] # => #<CSV::Row "Name":"bam" "Value":3>
79
+ #
80
+ # Specify a sequence of rows by a \Range:
81
+ # # Get rows.
82
+ # table[1..2] # => [#<CSV::Row "Name":"bam" "Value":3>, #<CSV::Row "Name":"baz" "Value":"2">]
83
+ # # Set rows, then get them.
84
+ # table[1..2] = [
85
+ # CSV::Row.new(['Name', 'Value'], ['bat', 4]),
86
+ # CSV::Row.new(['Name', 'Value'], ['bad', 5]),
87
+ # ]
88
+ # table[1..2] # => [["Name", #<CSV::Row "Name":"bat" "Value":4>], ["Value", #<CSV::Row "Name":"bad" "Value":5>]]
89
+ #
90
+ # === Column Mode
91
+ #
92
+ # Set a table to column mode with method #by_col!:
93
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
94
+ # table = CSV.parse(source, headers: true)
95
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
96
+ #
97
+ # Specify a column by an \Integer index:
98
+ # # Get a column.
99
+ # table[0]
100
+ # # Set a column, then get it.
101
+ # table[0] = ['FOO', 'BAR', 'BAZ']
102
+ # table[0] # => ["FOO", "BAR", "BAZ"]
103
+ #
104
+ # Specify a column by its \String header:
105
+ # # Get a column.
106
+ # table['Name'] # => ["FOO", "BAR", "BAZ"]
107
+ # # Set a column, then get it.
108
+ # table['Name'] = ['Foo', 'Bar', 'Baz']
109
+ # table['Name'] # => ["Foo", "Bar", "Baz"]
110
+ #
111
+ # === Mixed Mode
112
+ #
113
+ # In mixed mode, you can refer to either rows or columns:
114
+ # - An \Integer index refers to a row.
115
+ # - A \Range index refers to multiple rows.
116
+ # - A \String index refers to a column.
117
+ #
118
+ # Set a table to mixed mode with method #by_col_or_row!:
119
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
120
+ # table = CSV.parse(source, headers: true)
121
+ # table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
122
+ #
123
+ # Specify a single row by an \Integer index:
124
+ # # Get a row.
125
+ # table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
126
+ # # Set a row, then get it.
127
+ # table[1] = CSV::Row.new(['Name', 'Value'], ['bam', 3])
128
+ # table[1] # => #<CSV::Row "Name":"bam" "Value":3>
129
+ #
130
+ # Specify a sequence of rows by a \Range:
131
+ # # Get rows.
132
+ # table[1..2] # => [#<CSV::Row "Name":"bam" "Value":3>, #<CSV::Row "Name":"baz" "Value":"2">]
133
+ # # Set rows, then get them.
134
+ # table[1] = CSV::Row.new(['Name', 'Value'], ['bat', 4])
135
+ # table[2] = CSV::Row.new(['Name', 'Value'], ['bad', 5])
136
+ # table[1..2] # => [["Name", #<CSV::Row "Name":"bat" "Value":4>], ["Value", #<CSV::Row "Name":"bad" "Value":5>]]
137
+ #
138
+ # Specify a column by its \String header:
139
+ # # Get a column.
140
+ # table['Name'] # => ["foo", "bat", "bad"]
141
+ # # Set a column, then get it.
142
+ # table['Name'] = ['Foo', 'Bar', 'Baz']
143
+ # table['Name'] # => ["Foo", "Bar", "Baz"]
144
+ class Table
145
+ # :call-seq:
146
+ # CSV::Table.new(array_of_rows, headers = nil) -> csv_table
147
+ #
148
+ # Returns a new \CSV::Table object.
149
+ #
150
+ # - Argument +array_of_rows+ must be an \Array of CSV::Row objects.
151
+ # - Argument +headers+, if given, may be an \Array of Strings.
152
+ #
153
+ # ---
154
+ #
155
+ # Create an empty \CSV::Table object:
156
+ # table = CSV::Table.new([])
157
+ # table # => #<CSV::Table mode:col_or_row row_count:1>
158
+ #
159
+ # Create a non-empty \CSV::Table object:
160
+ # rows = [
161
+ # CSV::Row.new([], []),
162
+ # CSV::Row.new([], []),
163
+ # CSV::Row.new([], []),
164
+ # ]
165
+ # table = CSV::Table.new(rows)
166
+ # table # => #<CSV::Table mode:col_or_row row_count:4>
167
+ #
168
+ # ---
169
+ #
170
+ # If argument +headers+ is an \Array of Strings,
171
+ # those Strings become the table's headers:
172
+ # table = CSV::Table.new([], headers: ['Name', 'Age'])
173
+ # table.headers # => ["Name", "Age"]
174
+ #
175
+ # If argument +headers+ is not given and the table has rows,
176
+ # the headers are taken from the first row:
177
+ # rows = [
178
+ # CSV::Row.new(['Foo', 'Bar'], []),
179
+ # CSV::Row.new(['foo', 'bar'], []),
180
+ # CSV::Row.new(['FOO', 'BAR'], []),
181
+ # ]
182
+ # table = CSV::Table.new(rows)
183
+ # table.headers # => ["Foo", "Bar"]
184
+ #
185
+ # If argument +headers+ is not given and the table is empty (has no rows),
186
+ # the headers are also empty:
187
+ # table = CSV::Table.new([])
188
+ # table.headers # => []
189
+ #
190
+ # ---
191
+ #
192
+ # Raises an exception if argument +array_of_rows+ is not an \Array object:
193
+ # # Raises NoMethodError (undefined method `first' for :foo:Symbol):
194
+ # CSV::Table.new(:foo)
195
+ #
196
+ # Raises an exception if an element of +array_of_rows+ is not a \CSV::Table object:
197
+ # # Raises NoMethodError (undefined method `headers' for :foo:Symbol):
198
+ # CSV::Table.new([:foo])
199
+ def initialize(array_of_rows, headers: nil)
200
+ @table = array_of_rows
201
+ @headers = headers
202
+ unless @headers
203
+ if @table.empty?
204
+ @headers = []
205
+ else
206
+ @headers = @table.first.headers
207
+ end
208
+ end
209
+
210
+ @mode = :col_or_row
211
+ end
212
+
213
+ # The current access mode for indexing and iteration.
214
+ attr_reader :mode
215
+
216
+ # Internal data format used to compare equality.
217
+ attr_reader :table
218
+ protected :table
219
+
220
+ ### Array Delegation ###
221
+
222
+ extend Forwardable
223
+ def_delegators :@table, :empty?, :length, :size
224
+
225
+ # :call-seq:
226
+ # table.by_col -> table_dup
227
+ #
228
+ # Returns a duplicate of +self+, in column mode
229
+ # (see {Column Mode}[#class-CSV::Table-label-Column+Mode]):
230
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
231
+ # table = CSV.parse(source, headers: true)
232
+ # table.mode # => :col_or_row
233
+ # dup_table = table.by_col
234
+ # dup_table.mode # => :col
235
+ # dup_table.equal?(table) # => false # It's a dup
236
+ #
237
+ # This may be used to chain method calls without changing the mode
238
+ # (but also will affect performance and memory usage):
239
+ # dup_table.by_col['Name']
240
+ #
241
+ # Also note that changes to the duplicate table will not affect the original.
242
+ def by_col
243
+ self.class.new(@table.dup).by_col!
244
+ end
245
+
246
+ # :call-seq:
247
+ # table.by_col! -> self
248
+ #
249
+ # Sets the mode for +self+ to column mode
250
+ # (see {Column Mode}[#class-CSV::Table-label-Column+Mode]); returns +self+:
251
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
252
+ # table = CSV.parse(source, headers: true)
253
+ # table.mode # => :col_or_row
254
+ # table1 = table.by_col!
255
+ # table.mode # => :col
256
+ # table1.equal?(table) # => true # Returned self
257
+ def by_col!
258
+ @mode = :col
259
+
260
+ self
261
+ end
262
+
263
+ # :call-seq:
264
+ # table.by_col_or_row -> table_dup
265
+ #
266
+ # Returns a duplicate of +self+, in mixed mode
267
+ # (see {Mixed Mode}[#class-CSV::Table-label-Mixed+Mode]):
268
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
269
+ # table = CSV.parse(source, headers: true).by_col!
270
+ # table.mode # => :col
271
+ # dup_table = table.by_col_or_row
272
+ # dup_table.mode # => :col_or_row
273
+ # dup_table.equal?(table) # => false # It's a dup
274
+ #
275
+ # This may be used to chain method calls without changing the mode
276
+ # (but also will affect performance and memory usage):
277
+ # dup_table.by_col_or_row['Name']
278
+ #
279
+ # Also note that changes to the duplicate table will not affect the original.
280
+ def by_col_or_row
281
+ self.class.new(@table.dup).by_col_or_row!
282
+ end
283
+
284
+ # :call-seq:
285
+ # table.by_col_or_row! -> self
286
+ #
287
+ # Sets the mode for +self+ to mixed mode
288
+ # (see {Mixed Mode}[#class-CSV::Table-label-Mixed+Mode]); returns +self+:
289
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
290
+ # table = CSV.parse(source, headers: true).by_col!
291
+ # table.mode # => :col
292
+ # table1 = table.by_col_or_row!
293
+ # table.mode # => :col_or_row
294
+ # table1.equal?(table) # => true # Returned self
295
+ def by_col_or_row!
296
+ @mode = :col_or_row
297
+
298
+ self
299
+ end
300
+
301
+ # :call-seq:
302
+ # table.by_row -> table_dup
303
+ #
304
+ # Returns a duplicate of +self+, in row mode
305
+ # (see {Row Mode}[#class-CSV::Table-label-Row+Mode]):
306
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
307
+ # table = CSV.parse(source, headers: true)
308
+ # table.mode # => :col_or_row
309
+ # dup_table = table.by_row
310
+ # dup_table.mode # => :row
311
+ # dup_table.equal?(table) # => false # It's a dup
312
+ #
313
+ # This may be used to chain method calls without changing the mode
314
+ # (but also will affect performance and memory usage):
315
+ # dup_table.by_row[1]
316
+ #
317
+ # Also note that changes to the duplicate table will not affect the original.
318
+ def by_row
319
+ self.class.new(@table.dup).by_row!
320
+ end
321
+
322
+ # :call-seq:
323
+ # table.by_row! -> self
324
+ #
325
+ # Sets the mode for +self+ to row mode
326
+ # (see {Row Mode}[#class-CSV::Table-label-Row+Mode]); returns +self+:
327
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
328
+ # table = CSV.parse(source, headers: true)
329
+ # table.mode # => :col_or_row
330
+ # table1 = table.by_row!
331
+ # table.mode # => :row
332
+ # table1.equal?(table) # => true # Returned self
333
+ def by_row!
334
+ @mode = :row
335
+
336
+ self
337
+ end
338
+
339
+ # :call-seq:
340
+ # table.headers -> array_of_headers
341
+ #
342
+ # Returns a new \Array containing the \String headers for the table.
343
+ #
344
+ # If the table is not empty, returns the headers from the first row:
345
+ # rows = [
346
+ # CSV::Row.new(['Foo', 'Bar'], []),
347
+ # CSV::Row.new(['FOO', 'BAR'], []),
348
+ # CSV::Row.new(['foo', 'bar'], []),
349
+ # ]
350
+ # table = CSV::Table.new(rows)
351
+ # table.headers # => ["Foo", "Bar"]
352
+ # table.delete(0)
353
+ # table.headers # => ["FOO", "BAR"]
354
+ # table.delete(0)
355
+ # table.headers # => ["foo", "bar"]
356
+ #
357
+ # If the table is empty, returns a copy of the headers in the table itself:
358
+ # table.delete(0)
359
+ # table.headers # => ["Foo", "Bar"]
360
+ def headers
361
+ if @table.empty?
362
+ @headers.dup
363
+ else
364
+ @table.first.headers
365
+ end
366
+ end
367
+
368
+ # :call-seq:
369
+ # table[n] -> row or column_data
370
+ # table[range] -> array_of_rows or array_of_column_data
371
+ # table[header] -> array_of_column_data
372
+ #
373
+ # Returns data from the table; does not modify the table.
374
+ #
375
+ # ---
376
+ #
377
+ # Fetch a \Row by Its \Integer Index::
378
+ # - Form: <tt>table[n]</tt>, +n+ an integer.
379
+ # - Access mode: <tt>:row</tt> or <tt>:col_or_row</tt>.
380
+ # - Return value: _nth_ row of the table, if that row exists;
381
+ # otherwise +nil+.
382
+ #
383
+ # Returns the _nth_ row of the table if that row exists:
384
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
385
+ # table = CSV.parse(source, headers: true)
386
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
387
+ # table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
388
+ # table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
389
+ # table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
390
+ #
391
+ # Counts backward from the last row if +n+ is negative:
392
+ # table[-1] # => #<CSV::Row "Name":"baz" "Value":"2">
393
+ #
394
+ # Returns +nil+ if +n+ is too large or too small:
395
+ # table[4] # => nil
396
+ # table[-4] # => nil
397
+ #
398
+ # Raises an exception if the access mode is <tt>:row</tt>
399
+ # and +n+ is not an \Integer:
400
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
401
+ # # Raises TypeError (no implicit conversion of String into Integer):
402
+ # table['Name']
403
+ #
404
+ # ---
405
+ #
406
+ # Fetch a Column by Its \Integer Index::
407
+ # - Form: <tt>table[n]</tt>, +n+ an \Integer.
408
+ # - Access mode: <tt>:col</tt>.
409
+ # - Return value: _nth_ column of the table, if that column exists;
410
+ # otherwise an \Array of +nil+ fields of length <tt>self.size</tt>.
411
+ #
412
+ # Returns the _nth_ column of the table if that column exists:
413
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
414
+ # table = CSV.parse(source, headers: true)
415
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
416
+ # table[1] # => ["0", "1", "2"]
417
+ #
418
+ # Counts backward from the last column if +n+ is negative:
419
+ # table[-2] # => ["foo", "bar", "baz"]
420
+ #
421
+ # Returns an \Array of +nil+ fields if +n+ is too large or too small:
422
+ # table[4] # => [nil, nil, nil]
423
+ # table[-4] # => [nil, nil, nil]
424
+ #
425
+ # ---
426
+ #
427
+ # Fetch Rows by \Range::
428
+ # - Form: <tt>table[range]</tt>, +range+ a \Range object.
429
+ # - Access mode: <tt>:row</tt> or <tt>:col_or_row</tt>.
430
+ # - Return value: rows from the table, beginning at row <tt>range.start</tt>,
431
+ # if those rows exists.
432
+ #
433
+ # Returns rows from the table, beginning at row <tt>range.first</tt>,
434
+ # if those rows exist:
435
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
436
+ # table = CSV.parse(source, headers: true)
437
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
438
+ # rows = table[1..2] # => #<CSV::Row "Name":"bar" "Value":"1">
439
+ # rows # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
440
+ # table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
441
+ # rows = table[1..2] # => #<CSV::Row "Name":"bar" "Value":"1">
442
+ # rows # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
443
+ #
444
+ # If there are too few rows, returns all from <tt>range.start</tt> to the end:
445
+ # rows = table[1..50] # => #<CSV::Row "Name":"bar" "Value":"1">
446
+ # rows # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
447
+ #
448
+ # Special case: if <tt>range.start == table.size</tt>, returns an empty \Array:
449
+ # table[table.size..50] # => []
450
+ #
451
+ # If <tt>range.end</tt> is negative, calculates the ending index from the end:
452
+ # rows = table[0..-1]
453
+ # rows # => [#<CSV::Row "Name":"foo" "Value":"0">, #<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
454
+ #
455
+ # If <tt>range.start</tt> is negative, calculates the starting index from the end:
456
+ # rows = table[-1..2]
457
+ # rows # => [#<CSV::Row "Name":"baz" "Value":"2">]
458
+ #
459
+ # If <tt>range.start</tt> is larger than <tt>table.size</tt>, returns +nil+:
460
+ # table[4..4] # => nil
461
+ #
462
+ # ---
463
+ #
464
+ # Fetch Columns by \Range::
465
+ # - Form: <tt>table[range]</tt>, +range+ a \Range object.
466
+ # - Access mode: <tt>:col</tt>.
467
+ # - Return value: column data from the table, beginning at column <tt>range.start</tt>,
468
+ # if those columns exist.
469
+ #
470
+ # Returns column values from the table, if the column exists;
471
+ # the values are arranged by row:
472
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
473
+ # table = CSV.parse(source, headers: true)
474
+ # table.by_col!
475
+ # table[0..1] # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
476
+ #
477
+ # Special case: if <tt>range.start == headers.size</tt>,
478
+ # returns an \Array (size: <tt>table.size</tt>) of empty \Arrays:
479
+ # table[table.headers.size..50] # => [[], [], []]
480
+ #
481
+ # If <tt>range.end</tt> is negative, calculates the ending index from the end:
482
+ # table[0..-1] # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
483
+ #
484
+ # If <tt>range.start</tt> is negative, calculates the starting index from the end:
485
+ # table[-2..2] # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
486
+ #
487
+ # If <tt>range.start</tt> is larger than <tt>table.size</tt>,
488
+ # returns an \Array of +nil+ values:
489
+ # table[4..4] # => [nil, nil, nil]
490
+ #
491
+ # ---
492
+ #
493
+ # Fetch a Column by Its \String Header::
494
+ # - Form: <tt>table[header]</tt>, +header+ a \String header.
495
+ # - Access mode: <tt>:col</tt> or <tt>:col_or_row</tt>
496
+ # - Return value: column data from the table, if that +header+ exists.
497
+ #
498
+ # Returns column values from the table, if the column exists:
499
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
500
+ # table = CSV.parse(source, headers: true)
501
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
502
+ # table['Name'] # => ["foo", "bar", "baz"]
503
+ # table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
504
+ # col = table['Name']
505
+ # col # => ["foo", "bar", "baz"]
506
+ #
507
+ # Modifying the returned column values does not modify the table:
508
+ # col[0] = 'bat'
509
+ # col # => ["bat", "bar", "baz"]
510
+ # table['Name'] # => ["foo", "bar", "baz"]
511
+ #
512
+ # Returns an \Array of +nil+ values if there is no such column:
513
+ # table['Nosuch'] # => [nil, nil, nil]
514
+ def [](index_or_header)
515
+ if @mode == :row or # by index
516
+ (@mode == :col_or_row and (index_or_header.is_a?(Integer) or index_or_header.is_a?(Range)))
517
+ @table[index_or_header]
518
+ else # by header
519
+ @table.map { |row| row[index_or_header] }
520
+ end
521
+ end
522
+
523
+ # :call-seq:
524
+ # table[n] = row -> row
525
+ # table[n] = field_or_array_of_fields -> field_or_array_of_fields
526
+ # table[header] = field_or_array_of_fields -> field_or_array_of_fields
527
+ #
528
+ # Puts data onto the table.
529
+ #
530
+ # ---
531
+ #
532
+ # Set a \Row by Its \Integer Index::
533
+ # - Form: <tt>table[n] = row</tt>, +n+ an \Integer,
534
+ # +row+ a \CSV::Row instance or an \Array of fields.
535
+ # - Access mode: <tt>:row</tt> or <tt>:col_or_row</tt>.
536
+ # - Return value: +row+.
537
+ #
538
+ # If the row exists, it is replaced:
539
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
540
+ # table = CSV.parse(source, headers: true)
541
+ # new_row = CSV::Row.new(['Name', 'Value'], ['bat', 3])
542
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
543
+ # return_value = table[0] = new_row
544
+ # return_value.equal?(new_row) # => true # Returned the row
545
+ # table[0].to_h # => {"Name"=>"bat", "Value"=>3}
546
+ #
547
+ # With access mode <tt>:col_or_row</tt>:
548
+ # table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
549
+ # table[0] = CSV::Row.new(['Name', 'Value'], ['bam', 4])
550
+ # table[0].to_h # => {"Name"=>"bam", "Value"=>4}
551
+ #
552
+ # With an \Array instead of a \CSV::Row, inherits headers from the table:
553
+ # array = ['bad', 5]
554
+ # return_value = table[0] = array
555
+ # return_value.equal?(array) # => true # Returned the array
556
+ # table[0].to_h # => {"Name"=>"bad", "Value"=>5}
557
+ #
558
+ # If the row does not exist, extends the table by adding rows:
559
+ # assigns rows with +nil+ as needed:
560
+ # table.size # => 3
561
+ # table[5] = ['bag', 6]
562
+ # table.size # => 6
563
+ # table[3] # => nil
564
+ # table[4]# => nil
565
+ # table[5].to_h # => {"Name"=>"bag", "Value"=>6}
566
+ #
567
+ # Note that the +nil+ rows are actually +nil+, not a row of +nil+ fields.
568
+ #
569
+ # ---
570
+ #
571
+ # Set a Column by Its \Integer Index::
572
+ # - Form: <tt>table[n] = array_of_fields</tt>, +n+ an \Integer,
573
+ # +array_of_fields+ an \Array of \String fields.
574
+ # - Access mode: <tt>:col</tt>.
575
+ # - Return value: +array_of_fields+.
576
+ #
577
+ # If the column exists, it is replaced:
578
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
579
+ # table = CSV.parse(source, headers: true)
580
+ # new_col = [3, 4, 5]
581
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
582
+ # return_value = table[1] = new_col
583
+ # return_value.equal?(new_col) # => true # Returned the column
584
+ # table[1] # => [3, 4, 5]
585
+ # # The rows, as revised:
586
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
587
+ # table[0].to_h # => {"Name"=>"foo", "Value"=>3}
588
+ # table[1].to_h # => {"Name"=>"bar", "Value"=>4}
589
+ # table[2].to_h # => {"Name"=>"baz", "Value"=>5}
590
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
591
+ #
592
+ # If there are too few values, fills with +nil+ values:
593
+ # table[1] = [0]
594
+ # table[1] # => [0, nil, nil]
595
+ #
596
+ # If there are too many values, ignores the extra values:
597
+ # table[1] = [0, 1, 2, 3, 4]
598
+ # table[1] # => [0, 1, 2]
599
+ #
600
+ # If a single value is given, replaces all fields in the column with that value:
601
+ # table[1] = 'bat'
602
+ # table[1] # => ["bat", "bat", "bat"]
603
+ #
604
+ # ---
605
+ #
606
+ # Set a Column by Its \String Header::
607
+ # - Form: <tt>table[header] = field_or_array_of_fields</tt>,
608
+ # +header+ a \String header, +field_or_array_of_fields+ a field value
609
+ # or an \Array of \String fields.
610
+ # - Access mode: <tt>:col</tt> or <tt>:col_or_row</tt>.
611
+ # - Return value: +field_or_array_of_fields+.
612
+ #
613
+ # If the column exists, it is replaced:
614
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
615
+ # table = CSV.parse(source, headers: true)
616
+ # new_col = [3, 4, 5]
617
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
618
+ # return_value = table['Value'] = new_col
619
+ # return_value.equal?(new_col) # => true # Returned the column
620
+ # table['Value'] # => [3, 4, 5]
621
+ # # The rows, as revised:
622
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
623
+ # table[0].to_h # => {"Name"=>"foo", "Value"=>3}
624
+ # table[1].to_h # => {"Name"=>"bar", "Value"=>4}
625
+ # table[2].to_h # => {"Name"=>"baz", "Value"=>5}
626
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
627
+ #
628
+ # If there are too few values, fills with +nil+ values:
629
+ # table['Value'] = [0]
630
+ # table['Value'] # => [0, nil, nil]
631
+ #
632
+ # If there are too many values, ignores the extra values:
633
+ # table['Value'] = [0, 1, 2, 3, 4]
634
+ # table['Value'] # => [0, 1, 2]
635
+ #
636
+ # If the column does not exist, extends the table by adding columns:
637
+ # table['Note'] = ['x', 'y', 'z']
638
+ # table['Note'] # => ["x", "y", "z"]
639
+ # # The rows, as revised:
640
+ # table.by_row!
641
+ # table[0].to_h # => {"Name"=>"foo", "Value"=>0, "Note"=>"x"}
642
+ # table[1].to_h # => {"Name"=>"bar", "Value"=>1, "Note"=>"y"}
643
+ # table[2].to_h # => {"Name"=>"baz", "Value"=>2, "Note"=>"z"}
644
+ # table.by_col!
645
+ #
646
+ # If a single value is given, replaces all fields in the column with that value:
647
+ # table['Value'] = 'bat'
648
+ # table['Value'] # => ["bat", "bat", "bat"]
649
+ def []=(index_or_header, value)
650
+ if @mode == :row or # by index
651
+ (@mode == :col_or_row and index_or_header.is_a? Integer)
652
+ if value.is_a? Array
653
+ @table[index_or_header] = Row.new(headers, value)
654
+ else
655
+ @table[index_or_header] = value
656
+ end
657
+ else # set column
658
+ unless index_or_header.is_a? Integer
659
+ index = @headers.index(index_or_header) || @headers.size
660
+ @headers[index] = index_or_header
661
+ end
662
+ if value.is_a? Array # multiple values
663
+ @table.each_with_index do |row, i|
664
+ if row.header_row?
665
+ row[index_or_header] = index_or_header
666
+ else
667
+ row[index_or_header] = value[i]
668
+ end
669
+ end
670
+ else # repeated value
671
+ @table.each do |row|
672
+ if row.header_row?
673
+ row[index_or_header] = index_or_header
674
+ else
675
+ row[index_or_header] = value
676
+ end
677
+ end
678
+ end
679
+ end
680
+ end
681
+
682
+ # :call-seq:
683
+ # table.values_at(*indexes) -> array_of_rows
684
+ # table.values_at(*headers) -> array_of_columns_data
685
+ #
686
+ # If the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>,
687
+ # and each argument is either an \Integer or a \Range,
688
+ # returns rows.
689
+ # Otherwise, returns columns data.
690
+ #
691
+ # In either case, the returned values are in the order
692
+ # specified by the arguments. Arguments may be repeated.
693
+ #
694
+ # ---
695
+ #
696
+ # Returns rows as an \Array of \CSV::Row objects.
697
+ #
698
+ # No argument:
699
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
700
+ # table = CSV.parse(source, headers: true)
701
+ # table.values_at # => []
702
+ #
703
+ # One index:
704
+ # values = table.values_at(0)
705
+ # values # => [#<CSV::Row "Name":"foo" "Value":"0">]
706
+ #
707
+ # Two indexes:
708
+ # values = table.values_at(2, 0)
709
+ # values # => [#<CSV::Row "Name":"baz" "Value":"2">, #<CSV::Row "Name":"foo" "Value":"0">]
710
+ #
711
+ # One \Range:
712
+ # values = table.values_at(1..2)
713
+ # values # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
714
+ #
715
+ # \Ranges and indexes:
716
+ # values = table.values_at(0..1, 1..2, 0, 2)
717
+ # pp values
718
+ # Output:
719
+ # [#<CSV::Row "Name":"foo" "Value":"0">,
720
+ # #<CSV::Row "Name":"bar" "Value":"1">,
721
+ # #<CSV::Row "Name":"bar" "Value":"1">,
722
+ # #<CSV::Row "Name":"baz" "Value":"2">,
723
+ # #<CSV::Row "Name":"foo" "Value":"0">,
724
+ # #<CSV::Row "Name":"baz" "Value":"2">]
725
+ #
726
+ # ---
727
+ #
728
+ # Returns columns data as row Arrays,
729
+ # each consisting of the specified columns data for that row:
730
+ # values = table.values_at('Name')
731
+ # values # => [["foo"], ["bar"], ["baz"]]
732
+ # values = table.values_at('Value', 'Name')
733
+ # values # => [["0", "foo"], ["1", "bar"], ["2", "baz"]]
734
+ def values_at(*indices_or_headers)
735
+ if @mode == :row or # by indices
736
+ ( @mode == :col_or_row and indices_or_headers.all? do |index|
737
+ index.is_a?(Integer) or
738
+ ( index.is_a?(Range) and
739
+ index.first.is_a?(Integer) and
740
+ index.last.is_a?(Integer) )
741
+ end )
742
+ @table.values_at(*indices_or_headers)
743
+ else # by headers
744
+ @table.map { |row| row.values_at(*indices_or_headers) }
745
+ end
746
+ end
747
+
748
+ # :call-seq:
749
+ # table << row_or_array -> self
750
+ #
751
+ # If +row_or_array+ is a \CSV::Row object,
752
+ # it is appended to the table:
753
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
754
+ # table = CSV.parse(source, headers: true)
755
+ # table << CSV::Row.new(table.headers, ['bat', 3])
756
+ # table[3] # => #<CSV::Row "Name":"bat" "Value":3>
757
+ #
758
+ # If +row_or_array+ is an \Array, it is used to create a new
759
+ # \CSV::Row object which is then appended to the table:
760
+ # table << ['bam', 4]
761
+ # table[4] # => #<CSV::Row "Name":"bam" "Value":4>
762
+ def <<(row_or_array)
763
+ if row_or_array.is_a? Array # append Array
764
+ @table << Row.new(headers, row_or_array)
765
+ else # append Row
766
+ @table << row_or_array
767
+ end
768
+
769
+ self # for chaining
770
+ end
771
+
772
+ #
773
+ # :call-seq:
774
+ # table.push(*rows_or_arrays) -> self
775
+ #
776
+ # A shortcut for appending multiple rows. Equivalent to:
777
+ # rows.each {|row| self << row }
778
+ #
779
+ # Each argument may be either a \CSV::Row object or an \Array:
780
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
781
+ # table = CSV.parse(source, headers: true)
782
+ # rows = [
783
+ # CSV::Row.new(table.headers, ['bat', 3]),
784
+ # ['bam', 4]
785
+ # ]
786
+ # table.push(*rows)
787
+ # table[3..4] # => [#<CSV::Row "Name":"bat" "Value":3>, #<CSV::Row "Name":"bam" "Value":4>]
788
+ def push(*rows)
789
+ rows.each { |row| self << row }
790
+
791
+ self # for chaining
792
+ end
793
+
794
+ # :call-seq:
795
+ # table.delete(*indexes) -> deleted_values
796
+ # table.delete(*headers) -> deleted_values
797
+ #
798
+ # If the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>,
799
+ # and each argument is either an \Integer or a \Range,
800
+ # returns deleted rows.
801
+ # Otherwise, returns deleted columns data.
802
+ #
803
+ # In either case, the returned values are in the order
804
+ # specified by the arguments. Arguments may be repeated.
805
+ #
806
+ # ---
807
+ #
808
+ # Returns rows as an \Array of \CSV::Row objects.
809
+ #
810
+ # One index:
811
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
812
+ # table = CSV.parse(source, headers: true)
813
+ # deleted_values = table.delete(0)
814
+ # deleted_values # => [#<CSV::Row "Name":"foo" "Value":"0">]
815
+ #
816
+ # Two indexes:
817
+ # table = CSV.parse(source, headers: true)
818
+ # deleted_values = table.delete(2, 0)
819
+ # deleted_values # => [#<CSV::Row "Name":"baz" "Value":"2">, #<CSV::Row "Name":"foo" "Value":"0">]
820
+ #
821
+ # ---
822
+ #
823
+ # Returns columns data as column Arrays.
824
+ #
825
+ # One header:
826
+ # table = CSV.parse(source, headers: true)
827
+ # deleted_values = table.delete('Name')
828
+ # deleted_values # => ["foo", "bar", "baz"]
829
+ #
830
+ # Two headers:
831
+ # table = CSV.parse(source, headers: true)
832
+ # deleted_values = table.delete('Value', 'Name')
833
+ # deleted_values # => [["0", "1", "2"], ["foo", "bar", "baz"]]
834
+ def delete(*indexes_or_headers)
835
+ if indexes_or_headers.empty?
836
+ raise ArgumentError, "wrong number of arguments (given 0, expected 1+)"
837
+ end
838
+ deleted_values = indexes_or_headers.map do |index_or_header|
839
+ if @mode == :row or # by index
840
+ (@mode == :col_or_row and index_or_header.is_a? Integer)
841
+ @table.delete_at(index_or_header)
842
+ else # by header
843
+ if index_or_header.is_a? Integer
844
+ @headers.delete_at(index_or_header)
845
+ else
846
+ @headers.delete(index_or_header)
847
+ end
848
+ @table.map { |row| row.delete(index_or_header).last }
849
+ end
850
+ end
851
+ if indexes_or_headers.size == 1
852
+ deleted_values[0]
853
+ else
854
+ deleted_values
855
+ end
856
+ end
857
+
858
+ # :call-seq:
859
+ # table.delete_if {|row_or_column| ... } -> self
860
+ #
861
+ # Removes rows or columns for which the block returns a truthy value;
862
+ # returns +self+.
863
+ #
864
+ # Removes rows when the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>;
865
+ # calls the block with each \CSV::Row object:
866
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
867
+ # table = CSV.parse(source, headers: true)
868
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
869
+ # table.size # => 3
870
+ # table.delete_if {|row| row['Name'].start_with?('b') }
871
+ # table.size # => 1
872
+ #
873
+ # Removes columns when the access mode is <tt>:col</tt>;
874
+ # calls the block with each column as a 2-element array
875
+ # containing the header and an \Array of column fields:
876
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
877
+ # table = CSV.parse(source, headers: true)
878
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
879
+ # table.headers.size # => 2
880
+ # table.delete_if {|column_data| column_data[1].include?('2') }
881
+ # table.headers.size # => 1
882
+ #
883
+ # Returns a new \Enumerator if no block is given:
884
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
885
+ # table = CSV.parse(source, headers: true)
886
+ # table.delete_if # => #<Enumerator: #<CSV::Table mode:col_or_row row_count:4>:delete_if>
887
+ def delete_if(&block)
888
+ return enum_for(__method__) { @mode == :row or @mode == :col_or_row ? size : headers.size } unless block_given?
889
+
890
+ if @mode == :row or @mode == :col_or_row # by index
891
+ @table.delete_if(&block)
892
+ else # by header
893
+ headers.each do |header|
894
+ delete(header) if yield([header, self[header]])
895
+ end
896
+ end
897
+
898
+ self # for chaining
899
+ end
900
+
901
+ include Enumerable
902
+
903
+ # :call-seq:
904
+ # table.each {|row_or_column| ... ) -> self
905
+ #
906
+ # Calls the block with each row or column; returns +self+.
907
+ #
908
+ # When the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>,
909
+ # calls the block with each \CSV::Row object:
910
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
911
+ # table = CSV.parse(source, headers: true)
912
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
913
+ # table.each {|row| p row }
914
+ # Output:
915
+ # #<CSV::Row "Name":"foo" "Value":"0">
916
+ # #<CSV::Row "Name":"bar" "Value":"1">
917
+ # #<CSV::Row "Name":"baz" "Value":"2">
918
+ #
919
+ # When the access mode is <tt>:col</tt>,
920
+ # calls the block with each column as a 2-element array
921
+ # containing the header and an \Array of column fields:
922
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
923
+ # table.each {|column_data| p column_data }
924
+ # Output:
925
+ # ["Name", ["foo", "bar", "baz"]]
926
+ # ["Value", ["0", "1", "2"]]
927
+ #
928
+ # Returns a new \Enumerator if no block is given:
929
+ # table.each # => #<Enumerator: #<CSV::Table mode:col row_count:4>:each>
930
+ def each(&block)
931
+ return enum_for(__method__) { @mode == :col ? headers.size : size } unless block_given?
932
+
933
+ if @mode == :col
934
+ headers.each.with_index do |header, i|
935
+ yield([header, @table.map {|row| row[header, i]}])
936
+ end
937
+ else
938
+ @table.each(&block)
939
+ end
940
+
941
+ self # for chaining
942
+ end
943
+
944
+ # :call-seq:
945
+ # table == other_table -> true or false
946
+ #
947
+ # Returns +true+ if all each row of +self+ <tt>==</tt>
948
+ # the corresponding row of +other_table+, otherwise, +false+.
949
+ #
950
+ # The access mode does no affect the result.
951
+ #
952
+ # Equal tables:
953
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
954
+ # table = CSV.parse(source, headers: true)
955
+ # other_table = CSV.parse(source, headers: true)
956
+ # table == other_table # => true
957
+ #
958
+ # Different row count:
959
+ # other_table.delete(2)
960
+ # table == other_table # => false
961
+ #
962
+ # Different last row:
963
+ # other_table << ['bat', 3]
964
+ # table == other_table # => false
965
+ def ==(other)
966
+ return @table == other.table if other.is_a? CSV::Table
967
+ @table == other
968
+ end
969
+
970
+ # :call-seq:
971
+ # table.to_a -> array_of_arrays
972
+ #
973
+ # Returns the table as an \Array of \Arrays;
974
+ # the headers are in the first row:
975
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
976
+ # table = CSV.parse(source, headers: true)
977
+ # table.to_a # => [["Name", "Value"], ["foo", "0"], ["bar", "1"], ["baz", "2"]]
978
+ def to_a
979
+ array = [headers]
980
+ @table.each do |row|
981
+ array.push(row.fields) unless row.header_row?
982
+ end
983
+
984
+ array
985
+ end
986
+
987
+ # :call-seq:
988
+ # table.to_csv(**options) -> csv_string
989
+ #
990
+ # Returns the table as \CSV string.
991
+ # See {Options for Generating}[../CSV.html#class-CSV-label-Options+for+Generating].
992
+ #
993
+ # Defaults option +write_headers+ to +true+:
994
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
995
+ # table = CSV.parse(source, headers: true)
996
+ # table.to_csv # => "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
997
+ #
998
+ # Omits the headers if option +write_headers+ is given as +false+
999
+ # (see {Option +write_headers+}[../CSV.html#class-CSV-label-Option+write_headers]):
1000
+ # table.to_csv(write_headers: false) # => "foo,0\nbar,1\nbaz,2\n"
1001
+ #
1002
+ # Limit rows if option +limit+ is given like +2+:
1003
+ # table.to_csv(limit: 2) # => "Name,Value\nfoo,0\nbar,1\n"
1004
+ def to_csv(write_headers: true, limit: nil, **options)
1005
+ array = write_headers ? [headers.to_csv(**options)] : []
1006
+ limit ||= @table.size
1007
+ limit = @table.size + 1 + limit if limit < 0
1008
+ limit = 0 if limit < 0
1009
+ @table.first(limit).each do |row|
1010
+ array.push(row.fields.to_csv(**options)) unless row.header_row?
1011
+ end
1012
+
1013
+ array.join("")
1014
+ end
1015
+ alias_method :to_s, :to_csv
1016
+
1017
+ #
1018
+ # Extracts the nested value specified by the sequence of +index+ or +header+ objects by calling dig at each step,
1019
+ # returning nil if any intermediate step is nil.
1020
+ #
1021
+ def dig(index_or_header, *index_or_headers)
1022
+ value = self[index_or_header]
1023
+ if value.nil?
1024
+ nil
1025
+ elsif index_or_headers.empty?
1026
+ value
1027
+ else
1028
+ unless value.respond_to?(:dig)
1029
+ raise TypeError, "#{value.class} does not have \#dig method"
1030
+ end
1031
+ value.dig(*index_or_headers)
1032
+ end
1033
+ end
1034
+
1035
+ # :call-seq:
1036
+ # table.inspect => string
1037
+ #
1038
+ # Returns a <tt>US-ASCII</tt>-encoded \String showing table:
1039
+ # - Class: <tt>CSV::Table</tt>.
1040
+ # - Access mode: <tt>:row</tt>, <tt>:col</tt>, or <tt>:col_or_row</tt>.
1041
+ # - Size: Row count, including the header row.
1042
+ #
1043
+ # Example:
1044
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
1045
+ # table = CSV.parse(source, headers: true)
1046
+ # table.inspect # => "#<CSV::Table mode:col_or_row row_count:4>\nName,Value\nfoo,0\nbar,1\nbaz,2\n"
1047
+ #
1048
+ def inspect
1049
+ inspected = +"#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>"
1050
+ summary = to_csv(limit: 5)
1051
+ inspected << "\n" << summary if summary.encoding.ascii_compatible?
1052
+ inspected
1053
+ end
1054
+ end
1055
+ end