lib/iron/import/importer.rb in iron-import-0.5.0 vs lib/iron/import/importer.rb in iron-import-0.6.0
- old
+ new
@@ -31,12 +31,13 @@
# end
#
class Importer
# Array of error message or nil for each non-header row
- attr_accessor :errors, :warnings, :data
+ attr_accessor :errors, :warnings
attr_accessor :sheets
+ attr_reader :data, :custom_reader
# Source file/stream encoding, assumes UTF-8 if none specified
dsl_accessor :encoding
def self.build(options = {}, &block)
importer = Importer.new(options)
@@ -49,20 +50,38 @@
@sheets = {}
reset
end
+ # Takes a block, and sets self to be importer instance, so you can
+ # just call #column, #sheet, etc. directly.
def build(&block)
DslProxy.exec(self, &block) if block
self
end
- def default_sheet
- sheet(1)
+ # For the common case where there is only one "sheet", e.g. CSV files.
+ def default_sheet(&block)
+ sheet(1, true, &block)
end
- # Access a Sheet definition by id (either number (1-N) or sheet name)
+ # Access a Sheet definition by id (either number (1-N) or sheet name).
+ # Used during #build calls to define a sheet with a passed block, like so:
+ #
+ # Importer.build do
+ # sheet(1) do
+ # column :store_name
+ # column :store_address
+ # end
+ # sheet('Orders') do
+ # column :id
+ # column :price
+ # filter do |row|
+ # row[:price].prensent?
+ # end
+ # end
+ # end
def sheet(id, create=true, &block)
# Find the sheet, creating it if needed (and requested!)
if @sheets[id].nil?
if create
@sheets[id] = Sheet.new(self, id)
@@ -76,22 +95,44 @@
sheet.build(&block) if block
# Return the sheet
sheet
end
+
+ # Define a custom file reader to implement your own sheet parsing.
+ def on_file(&block)
+ @custom_reader = CustomReader.new(self) unless @custom_reader
+ @custom_reader.set_reader(:file, block)
+ end
+ def on_stream(&block)
+ @custom_reader = CustomReader.new(self) unless @custom_reader
+ @custom_reader.set_reader(:stream, block)
+ end
+
# Very, very commonly we only want to deal with the default sheet. In this case,
# let folks skip the sheet(n) do ... end block wrapper and just define columns
- # against the main importer. Internally, proxy those calls to the first sheet
+ # against the main importer. Internally, proxy those calls to the first sheet.
def column(*args, &block)
default_sheet.column(*args, &block)
end
+ # Ditto for filters
def filter(*args, &block)
default_sheet.filter(*args, &block)
end
+ # Ditto for start row too
+ def start_row(row_num)
+ default_sheet.start_row(row_num)
+ end
+
+ # More facading
+ def headerless!
+ default_sheet.headerless!
+ end
+
# First call to a freshly #build'd importer, this will read the file/stream/path supplied,
# validate the required values, run custom validations... basically pre-parse and
# massage the supplied data. It will return true on success, or false if one
# or more errors were encountered and the import failed.
#
@@ -111,30 +152,26 @@
def import(path_or_stream, options = {})
# Clear all our load-time state, including all rows, header locations... you name it
reset
# Get the reader for this format
- format = options.delete(:format)
- if format && format != :auto
+ default = @custom_reader ? :custom : :auto
+ format = options.delete(:format) { default }
+ if format == :custom
+ # Custom format selected, use our internal custom reader
+ @data = @custom_reader
+
+ elsif format && format != :auto
+ # Explicit format requested
@data = DataReader::for_format(self, format)
- unless reader
+ unless @data
add_error("Unable to find format handler for format #{format} - aborting")
return
end
+
else
- if path_or_stream.respond_to?(:read)
- @data = DataReader::for_stream(self, path_or_stream)
- unless @data
- add_error("Unable to find format handler for stream - aborting")
- return
- end
- else
- @data = DataReader::for_path(self, path_or_stream)
- unless @data
- add_error("Unable to find format handler for file #{path_or_stream} - aborting")
- return
- end
- end
+ # Auto select
+ @data = DataReader::for_source(self, path_or_stream)
end
# Read in the data!
@data.load(path_or_stream)
end