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.
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Roo
2
- VERSION = "2.0.1"
2
+ VERSION = "2.1.1"
3
3
  end
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 "Excel support has been extracted to roo-xls due to its dependency on the GPL'd spreadsheet gem. Install roo-xls to use Roo::Excel."
21
+ raise ROO_EXCEL_NOTICE
20
22
  when :Excel2003XML
21
- raise "Excel SpreadsheetML support has been extracted to roo-xls. Install roo-xls to use Roo::Excel2003XML."
23
+ raise ROO_EXCELML_NOTICE
22
24
  when :Google
23
- raise "Google support has been extracted to roo-google. Install roo-google to use Roo::Google."
25
+ raise ROO_GOOGLE_NOTICE
24
26
  else
25
27
  super
26
28
  end
data/spec/helpers.rb ADDED
@@ -0,0 +1,5 @@
1
+ module Helpers
2
+ def yaml_entry(row,col,type,value)
3
+ "cell_#{row}_#{col}: \n row: #{row} \n col: #{col} \n celltype: #{type} \n value: #{value} \n"
4
+ end
5
+ end
@@ -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
@@ -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
@@ -1,8 +1,13 @@
1
1
  require 'simplecov'
2
2
  require 'roo'
3
-
4
3
  require 'vcr'
5
4
 
5
+ require 'helpers'
6
+
7
+ RSpec.configure do |c|
8
+ c.include Helpers
9
+ end
10
+
6
11
  VCR.configure do |c|
7
12
  c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
8
13
  c.hook_into :webmock # or :fakeweb
data/test/all_ss.rb CHANGED
@@ -1,11 +1,12 @@
1
- require 'roo'
2
- Dir.glob("test/files/*.ods").each do |fn|
3
- begin
4
- oo = Roo::OpenOffice.new fn
5
- print File.basename(fn) + " "
6
- puts oo.officeversion
7
- rescue Zip::ZipError, Errno::ENOENT => e
8
- # file is not a real .ods spreadsheet file
9
- puts e.message
10
- end
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)