lib/io_streams/tabular.rb in iostreams-0.15.0 vs lib/io_streams/tabular.rb in iostreams-0.16.0

- old
+ new

@@ -26,11 +26,10 @@ # # => "5,6,9" # # tabular.render({"third"=>"3", "first_field"=>"1" }) # # => "1,,3" class Tabular - autoload :Errors, 'io_streams/tabular/errors' autoload :Header, 'io_streams/tabular/header' module Parser autoload :Array, 'io_streams/tabular/parser/array' autoload :Base, 'io_streams/tabular/parser/base' @@ -52,31 +51,36 @@ # Parameters # format: [Symbol] # :csv, :hash, :array, :json, :psv, :fixed # # For all other parameters, see Tabular::Header.new - def initialize(format: nil, file_name: nil, **args) + def initialize(format: nil, file_name: nil, format_options: nil, **args) @header = Header.new(**args) klass = if file_name && format.nil? self.class.parser_class_for_file_name(file_name) else self.class.parser_class(format) end - @parser = klass.new + @parser = format_options ? klass.new(format_options) : klass.new end - # Returns [true|false] whether a header row needs to be read first. - def requires_header? + # Returns [true|false] whether a header is still required in order to parse or render the current format. + def header? parser.requires_header? && IOStreams.blank?(header.columns) end + # Returns [true|false] whether a header row show be rendered on output. + def requires_header? + parser.requires_header? + end + # Returns [Array] the header row/line after parsing and cleansing. # Returns `nil` if the row/line is blank, or a header is not required for the supplied format (:json, :hash). # # Notes: - # * Call `parse_header?` first to determine if the header should be parsed first. + # * Call `header?` first to determine if the header should be parsed first. # * The header columns are set after parsing the row, but the header is not cleansed. def parse_header(line) return if IOStreams.blank?(line) || !parser.requires_header? header.columns = parser.parse(line) @@ -102,62 +106,78 @@ return if IOStreams.blank?(row) parser.render(row, header) end + # Returns [String] the header rendered for the output format + # Return nil if no header is required. + def render_header + return unless requires_header? + + if IOStreams.blank?(header.columns) + raise(Errors::MissingHeader, "Header columns must be set before attempting to render a header for format: #{format.inspect}") + end + + parser.render(header.columns, header) + end + # Returns [Array<String>] the cleansed columns def cleanse_header! header.cleanse! header.columns end - # Register a file extension and the reader and writer classes to use to format it + # Register a format and the parser class for it. # # Example: - # # MyXls::Reader and MyXls::Writer must implement .open - # register_extension(:xls, MyXls::Reader, MyXls::Writer) - def self.register_extension(extension, parser) - raise(ArgumentError, "Invalid extension #{extension.inspect}") unless extension.nil? || extension.to_s =~ /\A\w+\Z/ - @extensions[extension.nil? ? nil : extension.to_sym] = parser + # register_format(:csv, IOStreams::Tabular::Parser::Csv) + def self.register_format(format, parser) + raise(ArgumentError, "Invalid format #{format.inspect}") unless format.nil? || format.to_s =~ /\A\w+\Z/ + @formats[format.nil? ? nil : format.to_sym] = parser end - # De-Register a file extension + # De-Register a file format # - # Returns [Symbol] the extension removed, or nil if the extension was not registered + # Returns [Symbol] the format removed, or nil if the format was not registered # # Example: # register_extension(:xls) - def self.deregister_extension(extension) - raise(ArgumentError, "Invalid extension #{extension.inspect}") unless extension.to_s =~ /\A\w+\Z/ - @extensions.delete(extension.to_sym) + def self.deregister_format(format) + raise(ArgumentError, "Invalid format #{format.inspect}") unless format.to_s =~ /\A\w+\Z/ + @formats.delete(format.to_sym) end + # Returns [Array<Symbol>] the list of registered formats + def self.registered_formats + @formats.keys + end + private # A registry to hold formats for processing files during upload or download - @extensions = {} + @formats = {} def self.parser_class(format) - @extensions[format.nil? ? nil : format.to_sym] || raise(ArgumentError, "Unknown Tabular Format: #{format.inspect}") + @formats[format.nil? ? nil : format.to_sym] || raise(ArgumentError, "Unknown Tabular Format: #{format.inspect}") end # Returns the parser to use with tabular for the supplied file_name def self.parser_class_for_file_name(file_name) - extension = nil + format = nil file_name.to_s.split('.').reverse_each do |ext| - if @extensions.include?(ext.to_sym) - extension = ext.to_sym + if @formats.include?(ext.to_sym) + format = ext.to_sym break end end - parser_class(extension) + parser_class(format) end - register_extension(nil, IOStreams::Tabular::Parser::Csv) - register_extension(:array, IOStreams::Tabular::Parser::Array) - register_extension(:csv, IOStreams::Tabular::Parser::Csv) - register_extension(:fixed, IOStreams::Tabular::Parser::Fixed) - register_extension(:hash, IOStreams::Tabular::Parser::Hash) - register_extension(:json, IOStreams::Tabular::Parser::Json) - register_extension(:psv, IOStreams::Tabular::Parser::Psv) + register_format(nil, IOStreams::Tabular::Parser::Csv) + register_format(:array, IOStreams::Tabular::Parser::Array) + register_format(:csv, IOStreams::Tabular::Parser::Csv) + register_format(:fixed, IOStreams::Tabular::Parser::Fixed) + register_format(:hash, IOStreams::Tabular::Parser::Hash) + register_format(:json, IOStreams::Tabular::Parser::Json) + register_format(:psv, IOStreams::Tabular::Parser::Psv) end end