require 'csv_generator/version' class CsvGenerator attr_reader :io class << self def generate(path, options = {}, &block) mode = options[:mode] || 'w' permission = options[:permission] || 0644 File.open(path, mode, permission) do |io| with io, options, &block end end def with(io, options = {}) generator = new(io, options) yield generator if block_given? generator end end def initialize(io, options = {}) @io = io @options = options end def <<(row_values) io.write row_generator.generate(row_values) end private def row_generator @row_generator ||= RowGenerator.new(@options) end class RowGenerator KNOWN_OPTIONS = %i(line_separator field_separator quote_charactor).freeze attr_reader(*KNOWN_OPTIONS) attr_reader :escaped_quote def initialize(options = {}) default_config.merge(filter_options options).each do |k, v| instance_variable_set :"@#{k}", v end @escaped_quote = quote_charactor * 2 end private def filter_options(hash) hash.select { |k, _| KNOWN_OPTIONS.include? k } end def default_config { line_separator: "\r\n", field_separator: ',', quote_charactor: '"', } end def escape_quote_character(c) c * 2 end public def generate(row_values) row_values.map { |value| stringify value }.join(field_separator) + line_separator end private def stringify(value) case value when nil '' when String quote value else value end end def quote(str) escaped_string = str.gsub quote_charactor, escaped_quote %("#{escaped_string}") end end end