Class: SmarterCSV::Writer

Inherits:
Object
  • Object
show all
Defined in:
lib/smarter_csv/writer.rb

Instance Method Summary collapse

Constructor Details

#initialize(file_path_or_io, options = {}) ⇒ Writer

Returns a new instance of Writer.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/smarter_csv/writer.rb', line 54

def initialize(file_path_or_io, options = {})
  @options = options

  @row_sep = options[:row_sep] || $/
  @col_sep = options[:col_sep] || ','
  @quote_char = options[:quote_char] || '"'
  @escaped_quote_char = @quote_char * 2
  @force_quotes = options[:force_quotes] == true
  @quote_headers = options[:quote_headers] == true
  @disable_auto_quoting = options[:disable_auto_quoting] == true
  @value_converters = options[:value_converters] || {}
  @encoding = options[:encoding]
  @write_nil_value = options.fetch(:write_nil_value, '')
  @write_empty_value = options.fetch(:write_empty_value, '')
  @write_bom = options[:write_bom] == true
  @map_all_keys = @value_converters.has_key?(:_all)
  @mapped_keys = Set.new(@value_converters.keys - [:_all])
  @header_converter = options[:header_converter]

  @discover_headers = true
  if options.has_key?(:discover_headers)
    @discover_headers = options[:discover_headers] == true
  else
    @discover_headers = !(options.has_key?(:map_headers) || options.has_key?(:headers))
  end

  @headers = []
  @headers = options[:headers] if options.has_key?(:headers)
  @headers = options[:map_headers].keys if options.has_key?(:map_headers) && !options.has_key?(:headers)
  @map_headers = options[:map_headers] || {}

  # Accept an IO-like object (StringIO, IO, etc.) or any path-like object (String, Pathname, etc.)
  if file_path_or_io.respond_to?(:write)
    # External IO handed in — we should not close it ourselves.
    @output_file = file_path_or_io
    @file_opened_by_us = false
  else
    path =
      if file_path_or_io.respond_to?(:to_path)
        file_path_or_io.to_path
      elsif file_path_or_io.is_a?(String)
        file_path_or_io
      else
        raise ArgumentError,
              "SmarterCSV::Writer expects an IO-like object (responding to #write) " \
              "or a path-like object (responding to #to_path or being a String), " \
              "but got #{file_path_or_io.class}"
      end
    mode = @encoding ? "w+:#{@encoding}" : 'w+'
    @output_file = File.open(path, mode)
    @file_opened_by_us = true
  end
  @quote_regex = Regexp.union(@col_sep, @row_sep, @quote_char)

  if !@discover_headers && !@headers.empty?
    # Headers are fully known at construction time — write the header line immediately
    # and stream data rows directly to @output_file, bypassing the temp file entirely.
    @temp_file = nil
    @output_file.write("\xEF\xBB\xBF") if @write_bom
    write_header_line
  else
    @temp_file = Tempfile.new('smarter_csv')
  end
end

Instance Method Details

#<<(data) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/smarter_csv/writer.rb', line 119

def <<(data)
  case data
  when Hash
    process_hash(data)
  when Array
    data.each { |item| self << item }
  when NilClass
    # ignore
  else
    raise InvalidInputData, "Invalid data type: #{data.class}. Must be a Hash or an Array."
  end
end

#finalizeObject



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/smarter_csv/writer.rb', line 132

def finalize
  if @temp_file
    # Header-discovery mode: headers were accumulated while writing rows;
    # now prepend the header line and copy the buffered rows to the output.
    @output_file.write("\xEF\xBB\xBF") if @write_bom
    write_header_line
    @temp_file.rewind
    @output_file.write(@temp_file.read)
    @temp_file.close!
  end
  # In direct-write mode (@temp_file == nil) the header line and all data rows
  # were already written to @output_file — nothing left to do but flush and close.
  @output_file.flush
  @output_file.close if @file_opened_by_us # only close files we opened; caller owns external IO objects
end