roo 2.0.1 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +16 -1
- data/README.md +8 -11
- data/lib/roo/base.rb +84 -84
- data/lib/roo/constants.rb +5 -0
- data/lib/roo/excelx/shared_strings.rb +10 -0
- data/lib/roo/excelx.rb +12 -13
- data/lib/roo/libre_office.rb +1 -2
- data/lib/roo/open_office.rb +454 -521
- data/lib/roo/spreadsheet.rb +3 -1
- data/lib/roo/version.rb +1 -1
- data/lib/roo.rb +5 -3
- data/spec/helpers.rb +5 -0
- data/spec/lib/roo/base_spec.rb +212 -0
- data/spec/lib/roo/excelx_spec.rb +13 -0
- data/spec/lib/roo/spreadsheet_spec.rb +20 -0
- data/spec/spec_helper.rb +6 -1
- data/test/all_ss.rb +12 -11
- data/test/test_helper.rb +0 -4
- data/test/test_roo.rb +2091 -2088
- metadata +5 -3
- data/test/test_generic_spreadsheet.rb +0 -237
data/lib/roo/spreadsheet.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
1
3
|
module Roo
|
2
4
|
class Spreadsheet
|
3
5
|
class << self
|
@@ -22,7 +24,7 @@ module Roo
|
|
22
24
|
options[:file_warning] = :ignore
|
23
25
|
extension.tr('.', '').downcase.to_sym
|
24
26
|
else
|
25
|
-
res = ::File.extname((path =~ ::URI.regexp) ? ::URI.parse(::URI.encode(path)).path : path)
|
27
|
+
res = ::File.extname((path =~ /\A#{::URI.regexp}\z/) ? ::URI.parse(::URI.encode(path)).path : path)
|
26
28
|
res.tr('.', '').downcase.to_sym
|
27
29
|
end
|
28
30
|
end
|
data/lib/roo/version.rb
CHANGED
data/lib/roo.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'roo/constants'
|
1
2
|
require 'roo/spreadsheet'
|
2
3
|
require 'roo/base'
|
3
4
|
|
@@ -10,17 +11,18 @@ module Roo
|
|
10
11
|
CLASS_FOR_EXTENSION = {
|
11
12
|
ods: Roo::OpenOffice,
|
12
13
|
xlsx: Roo::Excelx,
|
14
|
+
xlsm: Roo::Excelx,
|
13
15
|
csv: Roo::CSV
|
14
16
|
}
|
15
17
|
|
16
18
|
def self.const_missing(const_name)
|
17
19
|
case const_name
|
18
20
|
when :Excel
|
19
|
-
raise
|
21
|
+
raise ROO_EXCEL_NOTICE
|
20
22
|
when :Excel2003XML
|
21
|
-
raise
|
23
|
+
raise ROO_EXCELML_NOTICE
|
22
24
|
when :Google
|
23
|
-
raise
|
25
|
+
raise ROO_GOOGLE_NOTICE
|
24
26
|
else
|
25
27
|
super
|
26
28
|
end
|
data/spec/helpers.rb
ADDED
data/spec/lib/roo/base_spec.rb
CHANGED
@@ -1,4 +1,216 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Roo::Base do
|
4
|
+
let(:klass) do
|
5
|
+
Class.new(Roo::Base) do
|
6
|
+
def initialize(filename, data = {})
|
7
|
+
super(filename)
|
8
|
+
@data ||= data
|
9
|
+
end
|
10
|
+
|
11
|
+
def read_cells(sheet = default_sheet)
|
12
|
+
return if @cells_read[sheet]
|
13
|
+
type_map = { String => :string, Date => :date, Numeric => :float }
|
14
|
+
|
15
|
+
@cell[sheet] = @data
|
16
|
+
@cell_type[sheet] = Hash[@data.map { |k, v| [k, type_map.find {|type,_| v.is_a?(type) }.last ] }]
|
17
|
+
@first_row[sheet] = @data.map { |k, _| k[0] }.min
|
18
|
+
@last_row[sheet] = @data.map { |k, _| k[0] }.max
|
19
|
+
@first_column[sheet] = @data.map { |k, _| k[1] }.min
|
20
|
+
@last_column[sheet] = @data.map { |k, _| k[1] }.max
|
21
|
+
@cells_read[sheet] = true
|
22
|
+
end
|
23
|
+
|
24
|
+
def cell(row, col, sheet = nil)
|
25
|
+
sheet ||= default_sheet
|
26
|
+
read_cells(sheet)
|
27
|
+
@cell[sheet][[row, col]]
|
28
|
+
end
|
29
|
+
|
30
|
+
def celltype(row, col, sheet = nil)
|
31
|
+
sheet ||= default_sheet
|
32
|
+
read_cells(sheet)
|
33
|
+
@cell_type[sheet][[row, col]]
|
34
|
+
end
|
35
|
+
|
36
|
+
def sheets
|
37
|
+
['my_sheet', 'blank sheet']
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
let(:spreadsheet_data) do
|
43
|
+
{
|
44
|
+
[5, 1] => Date.civil(1961, 11, 21),
|
45
|
+
|
46
|
+
[8, 3] => 'thisisc8',
|
47
|
+
[8, 7] => 'thisisg8',
|
48
|
+
|
49
|
+
[12, 1] => 41.0,
|
50
|
+
[12, 2] => 42.0,
|
51
|
+
[12, 3] => 43.0,
|
52
|
+
[12, 4] => 44.0,
|
53
|
+
[12, 5] => 45.0,
|
54
|
+
|
55
|
+
[15, 3] => 43.0,
|
56
|
+
[15, 4] => 44.0,
|
57
|
+
[15, 5] => 45.0,
|
58
|
+
|
59
|
+
[16, 2] => '"Hello world!"',
|
60
|
+
[16, 3] => 'forty-three',
|
61
|
+
[16, 4] => 'forty-four',
|
62
|
+
[16, 5] => 'forty-five'
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
let(:spreadsheet) { klass.new('some_file', spreadsheet_data) }
|
67
|
+
|
68
|
+
describe '#uri?' do
|
69
|
+
it 'should return true when passed a filename starting with http(s)://' do
|
70
|
+
expect(spreadsheet.send(:uri?, 'http://example.com/')).to be_truthy
|
71
|
+
expect(spreadsheet.send(:uri?, 'https://example.com/')).to be_truthy
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should return false when passed a filename which does not start with http(s)://' do
|
75
|
+
expect(spreadsheet.send(:uri?, 'example.com')).to be_falsy
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should return false when passed non-String object such as Tempfile' do
|
79
|
+
expect(spreadsheet.send(:uri?, Tempfile.new('test'))).to be_falsy
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '#set' do
|
84
|
+
it 'should not update cell when setting an invalid type' do
|
85
|
+
spreadsheet.set(1, 1, 1)
|
86
|
+
expect { spreadsheet.set(1, 1, :invalid_type) }.to raise_error(ArgumentError)
|
87
|
+
expect(spreadsheet.cell(1, 1)).to eq(1)
|
88
|
+
expect(spreadsheet.celltype(1, 1)).to eq(:float)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#first_row' do
|
93
|
+
it 'should return the first row' do
|
94
|
+
expect(spreadsheet.first_row).to eq(5)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe '#last_row' do
|
99
|
+
it 'should return the last row' do
|
100
|
+
expect(spreadsheet.last_row).to eq(16)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe '#first_column' do
|
105
|
+
it 'should return the first column' do
|
106
|
+
expect(spreadsheet.first_column).to eq(1)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '#first_column_as_letter' do
|
111
|
+
it 'should return the first column as a letter' do
|
112
|
+
expect(spreadsheet.first_column_as_letter).to eq('A')
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe '#last_column' do
|
117
|
+
it 'should return the last column' do
|
118
|
+
expect(spreadsheet.last_column).to eq(7)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe '#last_column_as_letter' do
|
123
|
+
it 'should return the last column as a letter' do
|
124
|
+
expect(spreadsheet.last_column_as_letter).to eq('G')
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe '#row' do
|
129
|
+
it 'should return the specified row' do
|
130
|
+
expect(spreadsheet.row(12)).to eq([41.0, 42.0, 43.0, 44.0, 45.0, nil, nil])
|
131
|
+
expect(spreadsheet.row(16)).to eq([nil, '"Hello world!"', 'forty-three', 'forty-four', 'forty-five', nil, nil])
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe '#empty?' do
|
136
|
+
it 'should return true when empty' do
|
137
|
+
expect(spreadsheet.empty?(1, 1)).to be_truthy
|
138
|
+
expect(spreadsheet.empty?(8, 3)).to be_falsy
|
139
|
+
expect(spreadsheet.empty?('A', 11)).to be_truthy
|
140
|
+
expect(spreadsheet.empty?('A', 12)).to be_falsy
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe '#reload' do
|
145
|
+
it 'should return reinitialize the spreadsheet' do
|
146
|
+
spreadsheet.reload
|
147
|
+
expect(spreadsheet.instance_variable_get(:@cell).empty?).to be_truthy
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe '#each' do
|
152
|
+
it 'should return an enumerator with all the rows' do
|
153
|
+
each = spreadsheet.each
|
154
|
+
expect(each).to be_a(Enumerator)
|
155
|
+
expect(each.to_a.last).to eq([nil, '"Hello world!"', 'forty-three', 'forty-four', 'forty-five', nil, nil])
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe '#to_yaml' do
|
160
|
+
it 'should convert the spreadsheet to yaml' do
|
161
|
+
expect(spreadsheet.to_yaml({}, 5, 1, 5, 1)).to eq("--- \n" + yaml_entry(5, 1, 'date', '1961-11-21'))
|
162
|
+
expect(spreadsheet.to_yaml({}, 8, 3, 8, 3)).to eq("--- \n" + yaml_entry(8, 3, 'string', 'thisisc8'))
|
163
|
+
|
164
|
+
expect(spreadsheet.to_yaml({}, 12, 3, 12, 3)).to eq("--- \n" + yaml_entry(12, 3, 'float', 43.0))
|
165
|
+
|
166
|
+
expect(spreadsheet.to_yaml({}, 12, 3, 12)).to eq(
|
167
|
+
"--- \n" + yaml_entry(12, 3, 'float', 43.0) +
|
168
|
+
yaml_entry(12, 4, 'float', 44.0) +
|
169
|
+
yaml_entry(12, 5, 'float', 45.0))
|
170
|
+
|
171
|
+
expect(spreadsheet.to_yaml({}, 12, 3)).to eq(
|
172
|
+
"--- \n" + yaml_entry(12, 3, 'float', 43.0) +
|
173
|
+
yaml_entry(12, 4, 'float', 44.0) +
|
174
|
+
yaml_entry(12, 5, 'float', 45.0) +
|
175
|
+
yaml_entry(15, 3, 'float', 43.0) +
|
176
|
+
yaml_entry(15, 4, 'float', 44.0) +
|
177
|
+
yaml_entry(15, 5, 'float', 45.0) +
|
178
|
+
yaml_entry(16, 3, 'string', 'forty-three') +
|
179
|
+
yaml_entry(16, 4, 'string', 'forty-four') +
|
180
|
+
yaml_entry(16, 5, 'string', 'forty-five'))
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
let(:expected_csv) do
|
185
|
+
<<EOS
|
186
|
+
,,,,,,
|
187
|
+
,,,,,,
|
188
|
+
,,,,,,
|
189
|
+
,,,,,,
|
190
|
+
1961-11-21,,,,,,
|
191
|
+
,,,,,,
|
192
|
+
,,,,,,
|
193
|
+
,,"thisisc8",,,,"thisisg8"
|
194
|
+
,,,,,,
|
195
|
+
,,,,,,
|
196
|
+
,,,,,,
|
197
|
+
41,42,43,44,45,,
|
198
|
+
,,,,,,
|
199
|
+
,,,,,,
|
200
|
+
,,43,44,45,,
|
201
|
+
,"""Hello world!""","forty-three","forty-four","forty-five",,
|
202
|
+
EOS
|
203
|
+
end
|
204
|
+
|
205
|
+
let(:expected_csv_with_semicolons) { expected_csv.gsub(/\,/, ';') }
|
206
|
+
|
207
|
+
describe '#to_csv' do
|
208
|
+
it 'should convert the spreadsheet to csv' do
|
209
|
+
expect(spreadsheet.to_csv).to eq(expected_csv)
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'should convert the spreadsheet to csv using the separator when is passed on the parameter' do
|
213
|
+
expect(spreadsheet.to_csv(nil, ';')).to eq(expected_csv_with_semicolons)
|
214
|
+
end
|
215
|
+
end
|
4
216
|
end
|
data/spec/lib/roo/excelx_spec.rb
CHANGED
@@ -447,5 +447,18 @@ describe Roo::Excelx do
|
|
447
447
|
expect(index).to eq 4
|
448
448
|
end
|
449
449
|
end
|
450
|
+
|
451
|
+
context 'without block passed' do
|
452
|
+
it 'returns an enumerator' do
|
453
|
+
expect(subject.each_row_streaming).to be_a(Enumerator)
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
describe '_x000D_' do
|
459
|
+
let(:path) { 'test/files/x000D.xlsx' }
|
460
|
+
it 'does not contain _x000D_' do
|
461
|
+
expect(subject.cell(2, 9)).not_to include('_x000D_')
|
462
|
+
end
|
450
463
|
end
|
451
464
|
end
|
@@ -41,6 +41,26 @@ describe Roo::Spreadsheet do
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
+
context 'for a windows path' do
|
45
|
+
context 'that is xlsx' do
|
46
|
+
let(:filename) { 'c:\Users\Joe\Desktop\myfile.xlsx' }
|
47
|
+
|
48
|
+
it 'loads the proper type' do
|
49
|
+
expect(Roo::Excelx).to receive(:new).with(filename, {})
|
50
|
+
Roo::Spreadsheet.open(filename)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'for a xlsm file' do
|
56
|
+
let(:filename) { 'macros spreadsheet.xlsm' }
|
57
|
+
|
58
|
+
it 'loads the proper type' do
|
59
|
+
expect(Roo::Excelx).to receive(:new).with(filename, {})
|
60
|
+
Roo::Spreadsheet.open(filename)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
44
64
|
context 'for a csv file' do
|
45
65
|
let(:filename) { 'file.csv' }
|
46
66
|
let(:options) { { csv_options: { col_sep: '"' } } }
|
data/spec/spec_helper.rb
CHANGED
data/test/all_ss.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
|
-
require 'roo'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
1
|
+
require 'roo'
|
2
|
+
|
3
|
+
Dir.glob('test/files/*.ods').each do |fn|
|
4
|
+
begin
|
5
|
+
oo = Roo::OpenOffice.new fn
|
6
|
+
print "#{File.basename(fn)} "
|
7
|
+
puts oo.officeversion
|
8
|
+
rescue Zip::ZipError, Errno::ENOENT => e
|
9
|
+
# file is not a real .ods spreadsheet file
|
10
|
+
puts e.message
|
11
|
+
end
|
12
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -45,10 +45,6 @@ def file_diff(fn1,fn2)
|
|
45
45
|
result
|
46
46
|
end
|
47
47
|
|
48
|
-
def yaml_entry(row,col,type,value)
|
49
|
-
"cell_#{row}_#{col}: \n row: #{row} \n col: #{col} \n celltype: #{type} \n value: #{value} \n"
|
50
|
-
end
|
51
|
-
|
52
48
|
class File
|
53
49
|
def File.delete_if_exist(filename)
|
54
50
|
if File.exist?(filename)
|