class CSV::Writer

Note: Don’t use this class directly. This is an internal class.

Attributes

headers[R]
lineno[R]

A CSV::Writer receives an output, prepares the header, format and output. It allows us to write new rows in the object and rewind it.

Public Class Methods

new(output, options) click to toggle source
# File csv/writer.rb, line 16
def initialize(output, options)
  @output = output
  @options = options
  @lineno = 0
  @fields_converter = nil
  prepare
  if @options[:write_headers] and @headers
    self << @headers
  end
  @fields_converter = @options[:fields_converter]
end

Public Instance Methods

<<(row) click to toggle source

Adds a new row

# File csv/writer.rb, line 31
def <<(row)
  case row
  when Row
    row = row.fields
  when Hash
    row = @headers.collect {|header| row[header]}
  end

  @headers ||= row if @use_headers
  @lineno += 1

  if @fields_converter
    quoted_fields = [false] * row.size
    row = @fields_converter.convert(row, nil, lineno, quoted_fields)
  end

  i = -1
  converted_row = row.collect do |field|
    i += 1
    quote(field, i)
  end
  line = converted_row.join(@column_separator) + @row_separator
  if @output_encoding
    line = line.encode(@output_encoding)
  end
  @output << line

  self
end
rewind() click to toggle source

Winds back to the beginning

# File csv/writer.rb, line 64
def rewind
  @lineno = 0
  @headers = nil if @options[:headers].nil?
end

Private Instance Methods

prepare() click to toggle source
# File csv/writer.rb, line 70
def prepare
  @encoding = @options[:encoding]

  prepare_header
  prepare_format
  prepare_output
end
prepare_force_quotes_fields(force_quotes) click to toggle source
# File csv/writer.rb, line 106
def prepare_force_quotes_fields(force_quotes)
  @force_quotes_fields = {}
  force_quotes.each do |name_or_index|
    case name_or_index
    when Integer
      index = name_or_index
      @force_quotes_fields[index] = true
    when String, Symbol
      name = name_or_index.to_s
      if @headers.nil?
        message = ":headers is required when you use field name " +
                  "in :force_quotes: " +
                  "#{name_or_index.inspect}: #{force_quotes.inspect}"
        raise ArgumentError, message
      end
      index = @headers.index(name)
      next if index.nil?
      @force_quotes_fields[index] = true
    else
      message = ":force_quotes element must be " +
                "field index or field name: " +
                "#{name_or_index.inspect}: #{force_quotes.inspect}"
      raise ArgumentError, message
    end
  end
end
prepare_format() click to toggle source
# File csv/writer.rb, line 133
def prepare_format
  @column_separator = @options[:column_separator].to_s.encode(@encoding)
  row_separator = @options[:row_separator]
  if row_separator == :auto
    @row_separator = InputRecordSeparator.value.encode(@encoding)
  else
    @row_separator = row_separator.to_s.encode(@encoding)
  end
  @quote_character = @options[:quote_character]
  force_quotes = @options[:force_quotes]
  if force_quotes.is_a?(Array)
    prepare_force_quotes_fields(force_quotes)
    @force_quotes = false
  elsif force_quotes
    @force_quotes_fields = nil
    @force_quotes = true
  else
    @force_quotes_fields = nil
    @force_quotes = false
  end
  unless @force_quotes
    @quotable_pattern =
      Regexp.new("[\r\n".encode(@encoding) +
                 Regexp.escape(@column_separator) +
                 Regexp.escape(@quote_character.encode(@encoding)) +
                 "]".encode(@encoding))
  end
  @quote_empty = @options.fetch(:quote_empty, true)
end
prepare_header() click to toggle source
# File csv/writer.rb, line 78
def prepare_header
  headers = @options[:headers]
  case headers
  when Array
    @headers = headers
    @use_headers = true
  when String
    @headers = CSV.parse_line(headers,
                              col_sep: @options[:column_separator],
                              row_sep: @options[:row_separator],
                              quote_char: @options[:quote_character])
    @use_headers = true
  when true
    @headers = nil
    @use_headers = true
  else
    @headers = nil
    @use_headers = false
  end
  return unless @headers

  converter = @options[:header_fields_converter]
  @headers = converter.convert(@headers, nil, 0, [])
  @headers.each do |header|
    header.freeze if header.is_a?(String)
  end
end
prepare_output() click to toggle source
# File csv/writer.rb, line 163
def prepare_output
  @output_encoding = nil
  return unless @output.is_a?(StringIO)

  output_encoding = @output.internal_encoding || @output.external_encoding
  if @encoding != output_encoding
    if @options[:force_encoding]
      @output_encoding = output_encoding
    else
      compatible_encoding = Encoding.compatible?(@encoding, output_encoding)
      if compatible_encoding
        @output.set_encoding(compatible_encoding)
        @output.seek(0, IO::SEEK_END)
      end
    end
  end
end
quote(field, i) click to toggle source
# File csv/writer.rb, line 190
def quote(field, i)
  if @force_quotes
    quote_field(field)
  elsif @force_quotes_fields and @force_quotes_fields[i]
    quote_field(field)
  else
    if field.nil?  # represent +nil+ fields as empty unquoted fields
      ""
    else
      field = String(field)  # Stringify fields
      # represent empty fields as empty quoted fields
      if (@quote_empty and field.empty?) or (field.valid_encoding? and @quotable_pattern.match?(field))
        quote_field(field)
      else
        field  # unquoted field
      end
    end
  end
end
quote_field(field) click to toggle source
# File csv/writer.rb, line 181
def quote_field(field)
  field = String(field)
  encoded_quote_character = @quote_character.encode(field.encoding)
  encoded_quote_character +
    field.gsub(encoded_quote_character,
               encoded_quote_character * 2) +
    encoded_quote_character
end