Skip to content

Commit

Permalink
Sane coercion of numeric types
Browse files Browse the repository at this point in the history
  • Loading branch information
rlburkes committed Jan 8, 2015
1 parent 39f313a commit f695408
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 42 deletions.
7 changes: 4 additions & 3 deletions lib/roo/excelx/sheet_doc.rb
Expand Up @@ -104,9 +104,10 @@ def cell_from_xml(cell_xml, hyperlink)
when :string
excelx_type = :string
cell.content
else
value_type = cell.content.index('.') ? :float : :string
cell.content
else
val = Integer(cell.content) rescue Float(cell.content) rescue cell.content
value_type = val && val.is_a?(Float) ? :float : :string
val
end
return Excelx::Cell.new(value,value_type,formula,excelx_type,cell.content,style, hyperlink, @workbook.base_date, Excelx::Cell::Coordinate.new(row, column))
end
Expand Down
32 changes: 10 additions & 22 deletions lib/roo/utils.rb
@@ -1,7 +1,10 @@
module Roo
module Utils
extend self

LETTERS = ('A'..'Z').to_a
CODEPOINT_OFFSET = 'A'.ord - 1

def split_coordinate(str)
letter, number = split_coord(str)
x = letter_to_number(letter)
Expand All @@ -24,38 +27,23 @@ def split_coord(s)
# convert a number to something like 'AB' (1 => 'A', 2 => 'B', ...)
def number_to_letter(n)
letters = ''
if n > 26
while n % 26 == 0 && n != 0
letters << 'Z'
n = ((n - 26) / 26).to_i
end
while n > 0
num = n % 26
letters = LETTERS[num - 1] + letters
n = (n / 26).to_i
end
else
letters = LETTERS[n - 1]
while n > 0
index = (n - 1) % 26
letters.prepend(LETTERS[index])
n = (n - index - 1) / 26
end
letters
end

# convert letters like 'AB' to a number ('A' => 1, 'B' => 2, ...)
def letter_to_number(letters)
result = 0
while letters && letters.length > 0
character = letters[0, 1].upcase
num = LETTERS.index(character)
fail ArgumentError, "invalid column character '#{letters[0, 1]}'" if num.nil?
num += 1
result = result * 26 + num
letters = letters[1..-1]
letters.upcase.each_codepoint.reduce(0) do |result, codepoint|
26 * result + codepoint - CODEPOINT_OFFSET
end
result
end

# Compute upper bound for cells in a given cell range.
def self.num_cells_in_range(str)
def num_cells_in_range(str)
cells = str.split(':')
return 1 if cells.count == 1
raise ArgumentError.new("invalid range string: #{str}. Supported range format 'A1:B2'") if cells.count != 2
Expand Down
28 changes: 14 additions & 14 deletions spec/lib/roo/excelx_spec.rb
Expand Up @@ -48,7 +48,7 @@

it 'returns a link with the number as a string value' do
expect(subject).to be_a(Roo::Link)
expect(subject).to eq('8675309.0')
expect(subject).to eq('8675309')
end
end
end
Expand Down Expand Up @@ -99,15 +99,15 @@
let(:path) { 'test/files/numbers1.xlsx' }

it 'returns the expected result' do
expect(subject.row(1, "Sheet5")).to eq [1.0, 5.0, 5.0, nil, nil]
expect(subject.row(1, "Sheet5")).to eq [1, 5, 5, nil, nil]
end
end

describe '#column' do
let(:path) { 'test/files/numbers1.xlsx' }

it 'returns the expected result' do
expect(subject.column(1, "Sheet5")).to eq [1.0, 2.0, 3.0, Date.new(2007,11,21), 42.0, "ABC"]
expect(subject.column(1, "Sheet5")).to eq [1, 2, 3, Date.new(2007,11,21), 42, "ABC"]
end
end

Expand Down Expand Up @@ -205,7 +205,7 @@

it 'returns the expected result' do
expect(subject.celltype(1, 1, "Sheet4")).to eq :date
expect(subject.celltype(1, 2, "Sheet4")).to eq :float
expect(subject.celltype(1, 2, "Sheet4")).to eq :string
expect(subject.celltype(6, 2, "Sheet5")).to eq :string
expect(subject.celltype(1000, 2000, "Sheet5")).to eq nil
end
Expand Down Expand Up @@ -339,16 +339,16 @@
[nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil],
["Date", "Start time", "End time", "Pause", "Sum", "Comment", nil, nil],
[Date.new(2007, 5, 7), 9.25, 10.25, 0.0, 1.0, "Task 1"],
[Date.new(2007, 5, 7), 10.75, 12.50, 0.0, 1.75, "Task 1"],
[Date.new(2007, 5, 7), 18.0, 19.0, 0.0, 1.0, "Task 2"],
[Date.new(2007, 5, 8), 9.25, 10.25, 0.0, 1.0, "Task 2"],
[Date.new(2007, 5, 8), 14.5, 15.5, 0.0, 1.0, "Task 3"],
[Date.new(2007, 5, 8), 8.75, 9.25, 0.0, 0.5, "Task 3"],
[Date.new(2007, 5, 14), 21.75, 22.25, 0.0, 0.5, "Task 3"],
[Date.new(2007, 5, 14), 22.5, 23.0, 0.0, 0.5, "Task 3"],
[Date.new(2007, 5, 15), 11.75, 12.75, 0.0, 1.0, "Task 3"],
[Date.new(2007, 5, 7), 10.75, 10.75, 0.0, 0.0, "Task 1"],
[Date.new(2007, 5, 7), 9.25, 10.25, 0, 1, "Task 1"],
[Date.new(2007, 5, 7), 10.75, 12.50, 0, 1.75, "Task 1"],
[Date.new(2007, 5, 7), 18.0, 19.0, 0, 1, "Task 2"],
[Date.new(2007, 5, 8), 9.25, 10.25, 0, 1, "Task 2"],
[Date.new(2007, 5, 8), 14.5, 15.5, 0, 1, "Task 3"],
[Date.new(2007, 5, 8), 8.75, 9.25, 0, 0.5, "Task 3"],
[Date.new(2007, 5, 14), 21.75, 22.25, 0, 0.5, "Task 3"],
[Date.new(2007, 5, 14), 22.5, 23.0, 0, 0.5, "Task 3"],
[Date.new(2007, 5, 15), 11.75, 12.75, 0, 1, "Task 3"],
[Date.new(2007, 5, 7), 10.75, 10.75, 0, 0, "Task 1"],
[nil]
]
end
Expand Down
7 changes: 4 additions & 3 deletions spec/lib/roo/utils_spec.rb
Expand Up @@ -2,10 +2,11 @@

RSpec.describe ::Roo::Utils do
subject { described_class }

context '#number_to_letter' do
::Roo::Utils::LETTERS.each_with_index do |l, i|
it "should return '#{l}' when passed #{i+1}" do
expect(described_class.number_to_letter(i+1)).to eq(l)
::Roo::Utils::LETTERS.each_with_index do |letter, index|
it "should return '#{letter}' when passed #{index+1}" do
expect(described_class.number_to_letter(index+1)).to eq(letter)
end
end

Expand Down

0 comments on commit f695408

Please sign in to comment.