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