Skip to content

Commit

Permalink
Merge branch master into proper-cell-types-for-excelx
Browse files Browse the repository at this point in the history
  • Loading branch information
stevendaniels committed Aug 20, 2015
2 parents f0b6428 + 9b4e67c commit b7890dd
Show file tree
Hide file tree
Showing 19 changed files with 418 additions and 348 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## [2.1.1] - 2015-08-02
### Fixed invalid new lines with _x000D_ character[#231](https://github.com/roo-rb/roo/pull/231)
### Fixed missing URI issue. [#245](https://github.com/roo-rb/roo/pull/245)

## [2.1.0] - 2015-07-18
### Added
- Added support for Excel 2007 `xlsm` files. [#232](https://github.com/roo-rb/roo/pull/232)
Expand Down
11 changes: 4 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@
[![Build Status](https://img.shields.io/travis/roo-rb/roo.svg?style=flat-square)](https://travis-ci.org/roo-rb/roo) [![Code Climate](https://img.shields.io/codeclimate/github/roo-rb/roo.svg?style=flat-square)](https://codeclimate.com/github/roo-rb/roo) [![Coverage Status](https://img.shields.io/coveralls/roo-rb/roo.svg?style=flat-square)](https://coveralls.io/r/roo-rb/roo) [![Gem Version](https://img.shields.io/gem/v/roo.svg?style=flat-square)](https://rubygems.org/gems/roo)

Roo implements read access for all common spreadsheet types. It can handle:

* Excelx
* OpenOffice / LibreOffice
* Excel 2007 - 2013 formats (xlsx, xlsm)
* LibreOffice / OpenOffice.org formats (ods)
* CSV

## Additional Libraries

In addition, the [roo-xls](https://github.com/roo-rb/roo-xls) and [roo-google](https://github.com/roo-rb/roo-google) gems exist to extend Roo to support reading classic Excel formats (i.e. `.xls` and ``Excel2003XML``) and read/write access for Google spreadsheets.
* Excel 97, Excel 2002 XML, and Excel 2003 XML formats when using the [roo-xls](https://github.com/roo-rb/roo-xls) gem (xls, xml)
* Google spreadsheets with read/write access when using [roo-google](https://github.com/roo-rb/roo-google)

## Installation

Expand Down
1 change: 1 addition & 0 deletions lib/roo.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'roo/constants'
require 'roo/errors'
require 'roo/spreadsheet'
require 'roo/base'

Expand Down
155 changes: 76 additions & 79 deletions lib/roo/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def collect_last_row_col_for_sheet(sheet)
first_column = [first_column, key.last.to_i].min
last_column = [last_column, key.last.to_i].max
end if @cell[sheet]
{first_row: first_row, first_column: first_column, last_row: last_row, last_column: last_column}
{ first_row: first_row, first_column: first_column, last_row: last_row, last_column: last_column }
end

%w(first_row last_row first_column last_column).each do |key|
Expand All @@ -117,22 +117,23 @@ def to_yaml(prefix = {}, from_row = nil, from_column = nil, to_row = nil, to_col
result = "--- \n"
from_row.upto(to_row) do |row|
from_column.upto(to_column) do |col|
unless empty?(row, col, sheet)
result << "cell_#{row}_#{col}: \n"
prefix.each do|k, v|
result << " #{k}: #{v} \n"
end
result << " row: #{row} \n"
result << " col: #{col} \n"
result << " celltype: #{celltype(row, col, sheet)} \n"
value = cell(row, col, sheet)
if celltype(row, col, sheet) == :time
value = integer_to_timestring(value)
end
result << " value: #{value} \n"
next if empty?(row, col, sheet)

result << "cell_#{row}_#{col}: \n"
prefix.each do|k, v|
result << " #{k}: #{v} \n"
end
result << " row: #{row} \n"
result << " col: #{col} \n"
result << " celltype: #{celltype(row, col, sheet)} \n"
value = cell(row, col, sheet)
if celltype(row, col, sheet) == :time
value = integer_to_timestring(value)
end
result << " value: #{value} \n"
end
end

result
end

Expand Down Expand Up @@ -170,7 +171,7 @@ def to_matrix(from_row = nil, from_column = nil, to_row = nil, to_column = nil,
end

def inspect
"<##{ self.class }:#{ self.object_id.to_s(8) } #{ self.instance_variables.join(' ') }>"
"<##{self.class}:#{object_id.to_s(8)} #{instance_variables.join(' ')}>"
end

# find a row either by row number or a condition
Expand Down Expand Up @@ -217,15 +218,15 @@ def set(row, col, value, sheet = default_sheet) #:nodoc:
row, col = normalize(row, col)
cell_type = cell_type_by_value(value)
set_value(row, col, value, sheet)
set_type(row, col, cell_type , sheet)
set_type(row, col, cell_type, sheet)
end

def cell_type_by_value(value)
case value
when Fixnum then :float
when String, Float then :string
else
raise ArgumentError, "Type for #{value} not set"
fail ArgumentError, "Type for #{value} not set"
end
end

Expand Down Expand Up @@ -256,13 +257,13 @@ def info
sheets.each do|sheet|
self.default_sheet = sheet
result << 'Sheet ' + n.to_s + ":\n"
unless first_row
result << ' - empty -'
else
if first_row
result << " First row: #{first_row}\n"
result << " Last row: #{last_row}\n"
result << " First column: #{::Roo::Utils.number_to_letter(first_column)}\n"
result << " Last column: #{::Roo::Utils.number_to_letter(last_column)}"
else
result << ' - empty -'
end
result << "\n" if sheet != sheets.last
n += 1
Expand All @@ -282,12 +283,12 @@ def to_xml
# sonst gibt es Fehler bei leeren Blaettern
first_row.upto(last_row) do |row|
first_column.upto(last_column) do |col|
unless empty?(row, col)
x.cell(cell(row, col),
next if empty?(row, col)

x.cell(cell(row, col),
row: row,
column: col,
type: celltype(row, col))
end
end
end
end
Expand Down Expand Up @@ -318,7 +319,7 @@ def method_missing(m, *args)
# access different worksheets by calling spreadsheet.sheet(1)
# or spreadsheet.sheet('SHEETNAME')
def sheet(index, name = false)
self.default_sheet = String === index ? index : sheets[index]
self.default_sheet = index.is_a?(::String) ? index : sheets[index]
name ? [default_sheet, self] : self
end

Expand Down Expand Up @@ -352,25 +353,23 @@ def each_with_pagename
# control characters and white spaces around columns

def each(options = {})
if block_given?
if options.empty?
1.upto(last_row) do |line|
yield row(line)
end
else
clean_sheet_if_need(options)
search_or_set_header(options)
headers = @headers ||
Hash[(first_column..last_column).map do |col|
[cell(@header_line, col), col]
end]

@header_line.upto(last_row) do |line|
yield(Hash[headers.map { |k, v| [k, cell(line, v)] }])
end
return to_enum(:each, options) unless block_given?

if options.empty?
1.upto(last_row) do |line|
yield row(line)
end
else
to_enum(:each, options)
clean_sheet_if_need(options)
search_or_set_header(options)
headers = @headers ||
Hash[(first_column..last_column).map do |col|
[cell(@header_line, col), col]
end]

@header_line.upto(last_row) do |line|
yield(Hash[headers.map { |k, v| [k, cell(line, v)] }])
end
end
end

Expand All @@ -393,10 +392,10 @@ def row_with(query, return_headers = false)
@header_line = line_no
return return_headers ? headers : line_no
elsif line_no > 100
fail "Couldn't find header row."
raise Roo::HeaderRowNotFoundError
end
end
fail "Couldn't find header row."
raise Roo::HeaderRowNotFoundError
end

protected
Expand All @@ -409,23 +408,24 @@ def file_type_check(filename, exts, name, warning_level, packed = nil)
filename = File.basename(filename, File.extname(filename))
end

if uri?(filename) && qs_begin = filename.rindex('?')
if uri?(filename) && (qs_begin = filename.rindex('?'))
filename = filename[0..qs_begin - 1]
end
exts = Array(exts)
if !exts.include?(File.extname(filename).downcase)
case warning_level
when :error
warn file_type_warning_message(filename, exts)
fail TypeError, "#{filename} is not #{name} file"
when :warning
warn "are you sure, this is #{name} spreadsheet file?"
warn file_type_warning_message(filename, exts)
when :ignore
# ignore
else
fail "#{warning_level} illegal state of file_warning"
end

return if exts.include?(File.extname(filename).downcase)

case warning_level
when :error
warn file_type_warning_message(filename, exts)
fail TypeError, "#{filename} is not #{name} file"
when :warning
warn "are you sure, this is #{name} spreadsheet file?"
warn file_type_warning_message(filename, exts)
when :ignore
# ignore
else
fail "#{warning_level} illegal state of file_warning"
end
end

Expand Down Expand Up @@ -476,9 +476,9 @@ def local_filename(filename, tmpdir, packed)
return if is_stream?(filename)
filename = download_uri(filename, tmpdir) if uri?(filename)
filename = unzip(filename, tmpdir) if packed == :zip
unless File.file?(filename)
fail IOError, "file #{filename} does not exist"
end

fail IOError, "file #{filename} does not exist" unless File.file?(filename)

filename
end

Expand Down Expand Up @@ -536,11 +536,8 @@ def reinitialize
end

def make_tmpdir(prefix = nil, root = nil, &block)
prefix = if prefix
TEMP_PREFIX + prefix
else
TEMP_PREFIX
end
prefix = "#{TEMP_PREFIX}#{prefix}"

::Dir.mktmpdir(prefix, root || ENV['ROO_TMP'], &block).tap do |result|
block_given? || track_tmpdir!(result)
end
Expand Down Expand Up @@ -588,9 +585,9 @@ def normalize(row, col)
fail ArgumentError
end
end
if col.is_a?(::String)
col = ::Roo::Utils.letter_to_number(col)
end

col = ::Roo::Utils.letter_to_number(col) if col.is_a?(::String)

[row, col]
end

Expand Down Expand Up @@ -641,7 +638,7 @@ def validate_sheet!(sheet)
fail RangeError, "sheet index #{sheet} not found"
end
when String
unless sheets.include? sheet
unless sheets.include?(sheet)
fail RangeError, "sheet '#{sheet}' not found"
end
else
Expand Down Expand Up @@ -670,14 +667,14 @@ def process_zipfile_packed(zip, tmpdir, path = '')
# parameter is nil the output goes to STDOUT
def write_csv_content(file = nil, sheet = nil, separator = ',')
file ||= STDOUT
if first_row(sheet) # sheet is not empty
1.upto(last_row(sheet)) do |row|
1.upto(last_column(sheet)) do |col|
file.print(separator) if col > 1
file.print cell_to_csv(row, col, sheet)
end
file.print("\n")
end # sheet not empty
return unless first_row(sheet) # The sheet is empty

1.upto(last_row(sheet)) do |row|
1.upto(last_column(sheet)) do |col|
file.print(separator) if col > 1
file.print cell_to_csv(row, col, sheet)
end
file.print("\n")
end
end

Expand Down Expand Up @@ -729,9 +726,9 @@ def cell_to_csv(row, col, sheet)
# converts an integer value to a time string like '02:05:06'
def integer_to_timestring(content)
h = (content / 3600.0).floor
content = content - h * 3600
content -= h * 3600
m = (content / 60.0).floor
content = content - m * 60
content -= m * 60
s = content
sprintf('%02d:%02d:%02d', h, m, s)
end
Expand Down
9 changes: 9 additions & 0 deletions lib/roo/errors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Roo
# A base error class for Roo. Most errors thrown by Roo should inherit from
# this class.
class Error < StandardError; end

# Raised when Roo cannot find a header row that matches the given column
# name(s).
class HeaderRowNotFoundError < Error; end
end
Loading

0 comments on commit b7890dd

Please sign in to comment.