Module: Sentiero::Web::CsvWriter
- Defined in:
- lib/sentiero/web/csv_writer.rb
Overview
Minimal RFC 4180 CSV serializer with spreadsheet-formula-injection guarding: cells starting with a formula trigger are prefixed with a single quote so the spreadsheet treats them as text rather than executing them.
Constant Summary collapse
- FORMULA_TRIGGERS =
Tab/CR included: some spreadsheets strip them before re-evaluating the cell.
["=", "+", "-", "@", "\t", "\r"].freeze
Class Method Summary collapse
- .format_cell(cell) ⇒ Object
- .format_row(row) ⇒ Object
- .generate(headers, rows) ⇒ Object
- .guard_injection(value) ⇒ Object
- .quote(value) ⇒ Object
- .stringify(cell) ⇒ Object
Class Method Details
.format_cell(cell) ⇒ Object
22 23 24 |
# File 'lib/sentiero/web/csv_writer.rb', line 22 def format_cell(cell) quote(guard_injection(stringify(cell))) end |
.format_row(row) ⇒ Object
18 19 20 |
# File 'lib/sentiero/web/csv_writer.rb', line 18 def format_row(row) row.map { |cell| format_cell(cell) }.join(",") end |
.generate(headers, rows) ⇒ Object
14 15 16 |
# File 'lib/sentiero/web/csv_writer.rb', line 14 def generate(headers, rows) ([headers] + rows).map { |row| format_row(row) }.join("\r\n") + "\r\n" end |
.guard_injection(value) ⇒ Object
35 36 37 |
# File 'lib/sentiero/web/csv_writer.rb', line 35 def guard_injection(value) value.start_with?(*FORMULA_TRIGGERS) ? "'#{value}" : value end |
.quote(value) ⇒ Object
39 40 41 42 |
# File 'lib/sentiero/web/csv_writer.rb', line 39 def quote(value) return value unless value.match?(/[",\r\n]/) %("#{value.gsub('"', '""')}") end |
.stringify(cell) ⇒ Object
26 27 28 29 30 31 32 33 |
# File 'lib/sentiero/web/csv_writer.rb', line 26 def stringify(cell) case cell when nil then "" when true then "true" when false then "false" else cell.to_s end end |