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