Class: Synthra::Export::Csv

Inherits:
Base
  • Object
show all
Defined in:
lib/synthra/export/csv.rb

Overview

Exports generated data as CSV

Examples:

Export schema data as CSV

exporter = Csv.new(schema, registry: registry, count: 100)
csv = exporter.export

Instance Method Summary collapse

Constructor Details

This class inherits a constructor from Synthra::Export::Base

Instance Method Details

#escape_csv_formula(str) ⇒ Object (private)

SECURITY (CSV injection): a string value beginning with = + - @ tab or CR is treated as a formula by spreadsheet apps that open the export. Prefix it with a single quote so the cell is rendered as literal text. The gem ships a hostile_payloads type that emits exactly these.



95
96
97
# File 'lib/synthra/export/csv.rb', line 95

def escape_csv_formula(str)
  str.match?(/\A[=+\-@\t\r]/) ? "'#{str}" : str
end

#exportObject



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/synthra/export/csv.rb', line 15

def export
  count = options[:count] || 10
  seed = options[:seed]
  mode = options[:mode] || :random
  
  records = schema.generate_many(count, seed: seed, mode: mode, registry: registry)
  
  return "" if records.empty?

  # Flatten nested objects for CSV
  flat_records = records.map { |r| flatten_record(r) }
  
  # Get all possible headers
  headers = flat_records.flat_map(&:keys).uniq.sort

  CSV.generate do |csv|
    csv << headers
    flat_records.each do |record|
      csv << headers.map { |h| format_value(record[h]) }
    end
  end
end

#export_headersString

Export with headers only (no data)

Returns:

  • (String)

    CSV header row



42
43
44
45
# File 'lib/synthra/export/csv.rb', line 42

def export_headers
  headers = schema.fields.map(&:name)
  CSV.generate { |csv| csv << headers }
end

#flatten_record(record, prefix = "") ⇒ Object (private)



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/synthra/export/csv.rb', line 49

def flatten_record(record, prefix = "")
  result = {}

  record.each do |key, value|
    full_key = prefix.empty? ? key : "#{prefix}.#{key}"

    case value
    when Hash
      result.merge!(flatten_record(value, full_key))
    when Array
      if value.first.is_a?(Hash)
        # Array of objects - just count them
        result[full_key] = "[#{value.length} items]"
      else
        result[full_key] = value.join("; ")
      end
    else
      result[full_key] = value
    end
  end

  result
end

#format_value(value) ⇒ Object (private)



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/synthra/export/csv.rb', line 73

def format_value(value)
  case value
  when nil
    ""
  when true
    "true"
  when false
    "false"
  when Array
    escape_csv_formula(value.join("; "))
  when Hash
    escape_csv_formula(JSON.generate(value))
  when String
    escape_csv_formula(value)
  else
    value.to_s # numerics/dates are safe — never prefix them (would break -5, =NA values stay strings)
  end
end