lib/roo/xls/excel.rb in roo-xls-1.0.0 vs lib/roo/xls/excel.rb in roo-xls-1.1.0

- old
+ new

@@ -1,13 +1,14 @@ require 'roo/xls/version' require 'roo/base' require 'spreadsheet' +require 'tmpdir' module Roo # Class for handling Excel-Spreadsheets class Excel < Roo::Base - FORMULAS_MESSAGE = 'the spreadsheet gem does not support forumulas, so roo can not.' + FORMULAS_MESSAGE = 'the spreadsheet gem does not support formulas, so roo can not.' CHARGUESS = begin require 'charguess' true rescue LoadError @@ -21,22 +22,29 @@ def initialize(filename, options = {}) packed = options[:packed] file_warning = options[:file_warning] || :error mode = options[:mode] || 'rb+' - file_type_check(filename, '.xls', 'an Excel', file_warning, packed) - make_tmpdir do |tmpdir| - filename = download_uri(filename, tmpdir) if uri?(filename) - filename = open_from_stream(filename[7..-1], tmpdir) if filename.is_a?(::String) && filename[0, 7] == 'stream:' - filename = unzip(filename, tmpdir) if packed == :zip + if is_stream?(filename) + @workbook = ::Spreadsheet.open(filename, mode) + else + file_type_check(filename, '.xls', 'an Excel', file_warning, packed) + Dir.mktmpdir do |tmpdir| + filename = download_uri(filename, tmpdir) if uri?(filename) + if filename.is_a?(::String) && filename[0, 7] == 'stream:' + filename = open_from_stream(filename[7..-1], tmpdir) + end + filename = unzip(filename, tmpdir) if packed == :zip - @filename = filename - unless File.file?(@filename) - fail IOError, "file #{@filename} does not exist" + @filename = filename + unless File.file?(@filename) + raise IOError, "file #{@filename} does not exist" + end + @workbook = ::Spreadsheet.open(filename, mode) end - @workbook = ::Spreadsheet.open(filename, mode) end + super(filename, options) @formula = {} @fonts = {} end @@ -55,34 +63,30 @@ @sheets ||= worksheets.collect { |worksheet| normalize_string(worksheet.name) } end # this method lets you find the worksheet with the most data def longest_sheet - sheet(worksheets.inject do|m, o| + sheet(worksheets.inject do |m, o| o.row_count > m.row_count ? o : m end.name) end # returns the content of a cell. The upper left corner is (1,1) or ('A',1) def cell(row, col, sheet = default_sheet) validate_sheet!(sheet) read_cells(sheet) - fail 'should be read' unless @cells_read[sheet] + raise 'should be read' unless @cells_read[sheet] row, col = normalize(row, col) if celltype(row, col, sheet) == :date yyyy, mm, dd = @cell[sheet][[row, col]].split('-') return Date.new(yyyy.to_i, mm.to_i, dd.to_i) end if celltype(row, col, sheet) == :string - return platform_specific_encoding(@cell[sheet][[row, col]]) + platform_specific_encoding(@cell[sheet][[row, col]]) else - if @cell[sheet] && @cell[sheet][[row, col]] - return @cell[sheet][[row, col]] - else - return nil - end + @cell[sheet] && @cell[sheet][[row, col]] end end # returns the type of a cell: # * :float @@ -107,17 +111,17 @@ end end # returns NO formula in excel spreadsheets def formula(_row, _col, _sheet = nil) - fail NotImplementedError, FORMULAS_MESSAGE + raise NotImplementedError, FORMULAS_MESSAGE end alias_method :formula?, :formula # returns NO formulas in excel spreadsheets def formulas(_sheet = nil) - fail NotImplementedError, FORMULAS_MESSAGE + raise NotImplementedError, FORMULAS_MESSAGE end # Given a cell, return the cell's font def font(row, col, sheet = default_sheet) read_cells(sheet) @@ -134,22 +138,22 @@ private # converts name of a sheet to index (0,1,2,..) def sheet_no(name) - return name - 1 if name.is_a?(Fixnum) + return name - 1 if name.is_a?(Integer) i = 0 worksheets.each do |worksheet| return i if name == normalize_string(worksheet.name) i += 1 end - fail StandardError, "sheet '#{name}' not found" + raise StandardError, "sheet '#{name}' not found" end def normalize_string(value) value = every_second_null?(value) ? remove_every_second_null(value) : value - if CHARGUESS && encoding = CharGuess.guess(value) + if CHARGUESS && (encoding = CharGuess.guess(value)) encoding.encode Encoding::UTF_8 else platform_specific_encoding(value) end end @@ -162,13 +166,11 @@ when /mswin32/ value.encode Encoding::ISO_8859_1 else value end - if every_second_null?(result) - result = remove_every_second_null(result) - end + result = remove_every_second_null(result) if every_second_null?(result) result end def every_second_null?(str) result = true @@ -196,12 +198,12 @@ # key = "#{y},#{x+i}" key = [row, col + i] @cell_type[sheet] = {} unless @cell_type[sheet] @cell_type[sheet][key] = value_type @formula[sheet] = {} unless @formula[sheet] - @formula[sheet][key] = formula if formula - @cell[sheet] = {} unless @cell[sheet] + @formula[sheet][key] = formula if formula + @cell[sheet] = {} unless @cell[sheet] @fonts[sheet] = {} unless @fonts[sheet] @fonts[sheet][key] = font @cell[sheet][key] = case value_type @@ -248,11 +250,11 @@ worksheet = @workbook.worksheet(sheet_no(sheet)) row_index = 1 worksheet.each(0) do |row| (0..row.size).each do |cell_index| cell = row.at(cell_index) - next if cell.nil? # skip empty cells + next if cell.nil? # skip empty cells next if cell.class == ::Spreadsheet::Formula && cell.value.nil? # skip empty formula cells value_type, v = if date_or_time?(row, cell_index) read_cell_date_or_time(row, cell_index) else @@ -297,33 +299,35 @@ if cell < 1.0 value_type = :time f = cell * 24.0 * 60.0 * 60.0 secs = f.round h = (secs / 3600.0).floor - secs = secs - 3600 * h + secs -= 3600 * h m = (secs / 60.0).floor - secs = secs - 60 * m + secs -= 60 * m s = secs value = h * 3600 + m * 60 + s else - if row.at(idx).class == ::Spreadsheet::Formula - datetime = row.send(:_datetime, cell) - else - datetime = row.datetime(idx) - end + datetime = + if row.at(idx).class == ::Spreadsheet::Formula + row.send(:_datetime, cell) + else + row.datetime(idx) + end if datetime.hour != 0 || datetime.min != 0 || datetime.sec != 0 value_type = :datetime value = datetime else value_type = :date - if row.at(idx).class == ::Spreadsheet::Formula - value = row.send(:_date, cell) - else - value = row.date(idx) - end + value = + if row.at(idx).class == ::Spreadsheet::Formula + row.send(:_date, cell) + else + row.date(idx) + end value = sprintf('%04d-%02d-%02d', value.year, value.month, value.day) end end [value_type, value] end @@ -331,10 +335,10 @@ # Read the cell and based on the class, # return the values for Roo def read_cell(row, idx) cell = read_cell_content(row, idx) case cell - when Float, Integer, Fixnum, Bignum + when Float, Integer value_type = :float value = cell.to_f when ::Spreadsheet::Link value_type = :link value = cell