roo 2.6.0 → 2.8.3
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 +5 -5
- data/.codeclimate.yml +17 -0
- data/.github/issue_template.md +16 -0
- data/.github/pull_request_template.md +14 -0
- data/.rubocop.yml +186 -0
- data/.travis.yml +14 -11
- data/CHANGELOG.md +64 -2
- data/Gemfile +2 -4
- data/LICENSE +2 -0
- data/README.md +36 -10
- data/lib/roo/base.rb +82 -225
- data/lib/roo/constants.rb +5 -3
- data/lib/roo/csv.rb +100 -97
- data/lib/roo/excelx/cell/base.rb +26 -12
- data/lib/roo/excelx/cell/boolean.rb +9 -6
- data/lib/roo/excelx/cell/date.rb +7 -7
- data/lib/roo/excelx/cell/datetime.rb +50 -44
- data/lib/roo/excelx/cell/empty.rb +3 -2
- data/lib/roo/excelx/cell/number.rb +44 -47
- data/lib/roo/excelx/cell/string.rb +3 -3
- data/lib/roo/excelx/cell/time.rb +17 -16
- data/lib/roo/excelx/cell.rb +10 -6
- data/lib/roo/excelx/comments.rb +3 -3
- data/lib/roo/excelx/coordinate.rb +11 -4
- data/lib/roo/excelx/extractor.rb +21 -3
- data/lib/roo/excelx/format.rb +38 -31
- data/lib/roo/excelx/images.rb +26 -0
- data/lib/roo/excelx/relationships.rb +12 -4
- data/lib/roo/excelx/shared.rb +10 -3
- data/lib/roo/excelx/shared_strings.rb +9 -15
- data/lib/roo/excelx/sheet.rb +49 -10
- data/lib/roo/excelx/sheet_doc.rb +89 -48
- data/lib/roo/excelx/styles.rb +3 -3
- data/lib/roo/excelx/workbook.rb +7 -3
- data/lib/roo/excelx.rb +50 -19
- data/lib/roo/formatters/base.rb +15 -0
- data/lib/roo/formatters/csv.rb +84 -0
- data/lib/roo/formatters/matrix.rb +23 -0
- data/lib/roo/formatters/xml.rb +31 -0
- data/lib/roo/formatters/yaml.rb +40 -0
- data/lib/roo/helpers/default_attr_reader.rb +20 -0
- data/lib/roo/helpers/weak_instance_cache.rb +41 -0
- data/lib/roo/open_office.rb +17 -9
- data/lib/roo/spreadsheet.rb +1 -1
- data/lib/roo/tempdir.rb +5 -10
- data/lib/roo/utils.rb +70 -20
- data/lib/roo/version.rb +1 -1
- data/lib/roo.rb +4 -1
- data/roo.gemspec +14 -11
- data/spec/lib/roo/base_spec.rb +45 -3
- data/spec/lib/roo/excelx/relationships_spec.rb +43 -0
- data/spec/lib/roo/excelx/sheet_doc_spec.rb +11 -0
- data/spec/lib/roo/excelx_spec.rb +150 -31
- data/spec/lib/roo/strict_spec.rb +43 -0
- data/spec/lib/roo/utils_spec.rb +25 -3
- data/spec/lib/roo/weak_instance_cache_spec.rb +92 -0
- data/spec/lib/roo_spec.rb +0 -0
- data/spec/spec_helper.rb +2 -6
- data/test/excelx/cell/test_attr_reader_default.rb +72 -0
- data/test/excelx/cell/test_base.rb +5 -0
- data/test/excelx/cell/test_datetime.rb +6 -6
- data/test/excelx/cell/test_empty.rb +11 -0
- data/test/excelx/cell/test_number.rb +9 -0
- data/test/excelx/cell/test_string.rb +20 -0
- data/test/excelx/cell/test_time.rb +5 -5
- data/test/excelx/test_coordinate.rb +51 -0
- data/test/formatters/test_csv.rb +136 -0
- data/test/formatters/test_matrix.rb +76 -0
- data/test/formatters/test_xml.rb +78 -0
- data/test/formatters/test_yaml.rb +20 -0
- data/test/helpers/test_accessing_files.rb +60 -0
- data/test/helpers/test_comments.rb +43 -0
- data/test/helpers/test_formulas.rb +9 -0
- data/test/helpers/test_labels.rb +103 -0
- data/test/helpers/test_sheets.rb +55 -0
- data/test/helpers/test_styles.rb +62 -0
- data/test/roo/test_base.rb +182 -0
- data/test/roo/test_csv.rb +88 -0
- data/test/roo/test_excelx.rb +330 -0
- data/test/roo/test_libre_office.rb +9 -0
- data/test/roo/test_open_office.rb +289 -0
- data/test/test_helper.rb +129 -14
- data/test/test_roo.rb +32 -1787
- metadata +81 -29
- data/.github/ISSUE_TEMPLATE +0 -10
- data/Gemfile_ruby2 +0 -29
data/lib/roo/base.rb
CHANGED
@@ -1,16 +1,24 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
1
|
+
require "tmpdir"
|
2
|
+
require "stringio"
|
3
|
+
require "nokogiri"
|
4
|
+
require "roo/utils"
|
5
|
+
require "roo/formatters/base"
|
6
|
+
require "roo/formatters/csv"
|
7
|
+
require "roo/formatters/matrix"
|
8
|
+
require "roo/formatters/xml"
|
9
|
+
require "roo/formatters/yaml"
|
7
10
|
|
8
11
|
# Base class for all other types of spreadsheets
|
9
12
|
class Roo::Base
|
10
13
|
include Enumerable
|
14
|
+
include Roo::Formatters::Base
|
15
|
+
include Roo::Formatters::CSV
|
16
|
+
include Roo::Formatters::Matrix
|
17
|
+
include Roo::Formatters::XML
|
18
|
+
include Roo::Formatters::YAML
|
11
19
|
|
12
|
-
MAX_ROW_COL = 999_999
|
13
|
-
MIN_ROW_COL = 0
|
20
|
+
MAX_ROW_COL = 999_999
|
21
|
+
MIN_ROW_COL = 0
|
14
22
|
|
15
23
|
attr_reader :headers
|
16
24
|
|
@@ -18,10 +26,14 @@ class Roo::Base
|
|
18
26
|
attr_accessor :header_line
|
19
27
|
|
20
28
|
def self.TEMP_PREFIX
|
21
|
-
warn
|
29
|
+
warn "[DEPRECATION] please access TEMP_PREFIX via Roo::TEMP_PREFIX"
|
22
30
|
Roo::TEMP_PREFIX
|
23
31
|
end
|
24
32
|
|
33
|
+
def self.finalize(object_id)
|
34
|
+
proc { finalize_tempdirs(object_id) }
|
35
|
+
end
|
36
|
+
|
25
37
|
def initialize(filename, options = {}, _file_warning = :error, _tmpdir = nil)
|
26
38
|
@filename = filename
|
27
39
|
@options = options
|
@@ -42,6 +54,11 @@ class Roo::Base
|
|
42
54
|
if self.class.respond_to?(:finalize_tempdirs)
|
43
55
|
self.class.finalize_tempdirs(object_id)
|
44
56
|
end
|
57
|
+
|
58
|
+
instance_variables.each do |instance_variable|
|
59
|
+
instance_variable_set(instance_variable, nil)
|
60
|
+
end
|
61
|
+
|
45
62
|
nil
|
46
63
|
end
|
47
64
|
|
@@ -50,10 +67,10 @@ class Roo::Base
|
|
50
67
|
end
|
51
68
|
|
52
69
|
# sets the working sheet in the document
|
53
|
-
# 'sheet' can be a number (
|
70
|
+
# 'sheet' can be a number (0 = first sheet) or the name of a sheet.
|
54
71
|
def default_sheet=(sheet)
|
55
72
|
validate_sheet!(sheet)
|
56
|
-
@default_sheet = sheet
|
73
|
+
@default_sheet = sheet.is_a?(String) ? sheet : sheets[sheet]
|
57
74
|
@first_row[sheet] = @last_row[sheet] = @first_column[sheet] = @last_column[sheet] = nil
|
58
75
|
@cells_read[sheet] = false
|
59
76
|
end
|
@@ -86,7 +103,7 @@ class Roo::Base
|
|
86
103
|
def collect_last_row_col_for_sheet(sheet)
|
87
104
|
first_row = first_column = MAX_ROW_COL
|
88
105
|
last_row = last_column = MIN_ROW_COL
|
89
|
-
@cell[sheet].each_pair do|key, value|
|
106
|
+
@cell[sheet].each_pair do |key, value|
|
90
107
|
next unless value
|
91
108
|
first_row = [first_row, key.first.to_i].min
|
92
109
|
last_row = [last_row, key.first.to_i].max
|
@@ -96,82 +113,14 @@ class Roo::Base
|
|
96
113
|
{ first_row: first_row, first_column: first_column, last_row: last_row, last_column: last_column }
|
97
114
|
end
|
98
115
|
|
99
|
-
%
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
end # end
|
105
|
-
EOS
|
106
|
-
end
|
107
|
-
|
108
|
-
# returns a rectangular area (default: all cells) as yaml-output
|
109
|
-
# you can add additional attributes with the prefix parameter like:
|
110
|
-
# oo.to_yaml({"file"=>"flightdata_2007-06-26", "sheet" => "1"})
|
111
|
-
def to_yaml(prefix = {}, from_row = nil, from_column = nil, to_row = nil, to_column = nil, sheet = default_sheet)
|
112
|
-
return '' unless first_row # empty result if there is no first_row in a sheet
|
113
|
-
|
114
|
-
from_row ||= first_row(sheet)
|
115
|
-
to_row ||= last_row(sheet)
|
116
|
-
from_column ||= first_column(sheet)
|
117
|
-
to_column ||= last_column(sheet)
|
118
|
-
|
119
|
-
result = "--- \n"
|
120
|
-
from_row.upto(to_row) do |row|
|
121
|
-
from_column.upto(to_column) do |col|
|
122
|
-
next if empty?(row, col, sheet)
|
123
|
-
|
124
|
-
result << "cell_#{row}_#{col}: \n"
|
125
|
-
prefix.each do|k, v|
|
126
|
-
result << " #{k}: #{v} \n"
|
127
|
-
end
|
128
|
-
result << " row: #{row} \n"
|
129
|
-
result << " col: #{col} \n"
|
130
|
-
result << " celltype: #{celltype(row, col, sheet)} \n"
|
131
|
-
value = cell(row, col, sheet)
|
132
|
-
if celltype(row, col, sheet) == :time
|
133
|
-
value = integer_to_timestring(value)
|
134
|
-
end
|
135
|
-
result << " value: #{value} \n"
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
result
|
140
|
-
end
|
141
|
-
|
142
|
-
# write the current spreadsheet to stdout or into a file
|
143
|
-
def to_csv(filename = nil, separator = ',', sheet = default_sheet)
|
144
|
-
if filename
|
145
|
-
File.open(filename, 'w') do |file|
|
146
|
-
write_csv_content(file, sheet, separator)
|
147
|
-
end
|
148
|
-
true
|
149
|
-
else
|
150
|
-
sio = ::StringIO.new
|
151
|
-
write_csv_content(sio, sheet, separator)
|
152
|
-
sio.rewind
|
153
|
-
sio.read
|
116
|
+
%i(first_row last_row first_column last_column).each do |key|
|
117
|
+
ivar = "@#{key}".to_sym
|
118
|
+
define_method(key) do |sheet = default_sheet|
|
119
|
+
read_cells(sheet)
|
120
|
+
instance_variable_get(ivar)[sheet] ||= first_last_row_col_for_sheet(sheet)[key]
|
154
121
|
end
|
155
122
|
end
|
156
123
|
|
157
|
-
# returns a matrix object from the whole sheet or a rectangular area of a sheet
|
158
|
-
def to_matrix(from_row = nil, from_column = nil, to_row = nil, to_column = nil, sheet = default_sheet)
|
159
|
-
require 'matrix'
|
160
|
-
|
161
|
-
return Matrix.empty unless first_row
|
162
|
-
|
163
|
-
from_row ||= first_row(sheet)
|
164
|
-
to_row ||= last_row(sheet)
|
165
|
-
from_column ||= first_column(sheet)
|
166
|
-
to_column ||= last_column(sheet)
|
167
|
-
|
168
|
-
Matrix.rows(from_row.upto(to_row).map do |row|
|
169
|
-
from_column.upto(to_column).map do |col|
|
170
|
-
cell(row, col, sheet)
|
171
|
-
end
|
172
|
-
end)
|
173
|
-
end
|
174
|
-
|
175
124
|
def inspect
|
176
125
|
"<##{self.class}:#{object_id.to_s(8)} #{instance_variables.join(' ')}>"
|
177
126
|
end
|
@@ -256,16 +205,16 @@ class Roo::Base
|
|
256
205
|
"Number of sheets: #{sheets.size}\n"\
|
257
206
|
"Sheets: #{sheets.join(', ')}\n"
|
258
207
|
n = 1
|
259
|
-
sheets.each do|sheet|
|
208
|
+
sheets.each do |sheet|
|
260
209
|
self.default_sheet = sheet
|
261
|
-
result <<
|
210
|
+
result << "Sheet " + n.to_s + ":\n"
|
262
211
|
if first_row
|
263
212
|
result << " First row: #{first_row}\n"
|
264
213
|
result << " Last row: #{last_row}\n"
|
265
214
|
result << " First column: #{::Roo::Utils.number_to_letter(first_column)}\n"
|
266
215
|
result << " Last column: #{::Roo::Utils.number_to_letter(last_column)}"
|
267
216
|
else
|
268
|
-
result <<
|
217
|
+
result << " - empty -"
|
269
218
|
end
|
270
219
|
result << "\n" if sheet != sheets.last
|
271
220
|
n += 1
|
@@ -274,32 +223,6 @@ class Roo::Base
|
|
274
223
|
end
|
275
224
|
end
|
276
225
|
|
277
|
-
# returns an XML representation of all sheets of a spreadsheet file
|
278
|
-
def to_xml
|
279
|
-
Nokogiri::XML::Builder.new do |xml|
|
280
|
-
xml.spreadsheet do
|
281
|
-
sheets.each do |sheet|
|
282
|
-
self.default_sheet = sheet
|
283
|
-
xml.sheet(name: sheet) do |x|
|
284
|
-
if first_row && last_row && first_column && last_column
|
285
|
-
# sonst gibt es Fehler bei leeren Blaettern
|
286
|
-
first_row.upto(last_row) do |row|
|
287
|
-
first_column.upto(last_column) do |col|
|
288
|
-
next if empty?(row, col)
|
289
|
-
|
290
|
-
x.cell(cell(row, col),
|
291
|
-
row: row,
|
292
|
-
column: col,
|
293
|
-
type: celltype(row, col))
|
294
|
-
end
|
295
|
-
end
|
296
|
-
end
|
297
|
-
end
|
298
|
-
end
|
299
|
-
end
|
300
|
-
end.to_xml
|
301
|
-
end
|
302
|
-
|
303
226
|
# when a method like spreadsheet.a42 is called
|
304
227
|
# convert it to a call of spreadsheet.cell('a',42)
|
305
228
|
def method_missing(m, *args)
|
@@ -365,39 +288,42 @@ class Roo::Base
|
|
365
288
|
clean_sheet_if_need(options)
|
366
289
|
search_or_set_header(options)
|
367
290
|
headers = @headers ||
|
368
|
-
|
369
|
-
[cell(@header_line, col)
|
370
|
-
end
|
291
|
+
(first_column..last_column).each_with_object({}) do |col, hash|
|
292
|
+
hash[cell(@header_line, col)] = col
|
293
|
+
end
|
371
294
|
|
372
295
|
@header_line.upto(last_row) do |line|
|
373
|
-
yield(
|
296
|
+
yield(headers.each_with_object({}) { |(k, v), hash| hash[k] = cell(line, v) })
|
374
297
|
end
|
375
298
|
end
|
376
299
|
end
|
377
300
|
|
378
301
|
def parse(options = {})
|
379
|
-
|
380
|
-
|
381
|
-
yield(row) if block_given?
|
382
|
-
ary << row
|
302
|
+
results = each(options).map do |row|
|
303
|
+
block_given? ? yield(row) : row
|
383
304
|
end
|
384
|
-
|
305
|
+
|
306
|
+
options[:headers] == true ? results : results.drop(1)
|
385
307
|
end
|
386
308
|
|
387
309
|
def row_with(query, return_headers = false)
|
388
310
|
line_no = 0
|
311
|
+
closest_mismatched_headers = []
|
389
312
|
each do |row|
|
390
313
|
line_no += 1
|
391
314
|
headers = query.map { |q| row.grep(q)[0] }.compact
|
392
|
-
|
393
315
|
if headers.length == query.length
|
394
316
|
@header_line = line_no
|
395
317
|
return return_headers ? headers : line_no
|
396
|
-
|
397
|
-
|
318
|
+
else
|
319
|
+
closest_mismatched_headers = headers if headers.length > closest_mismatched_headers.length
|
320
|
+
if line_no > 100
|
321
|
+
break
|
322
|
+
end
|
398
323
|
end
|
399
324
|
end
|
400
|
-
|
325
|
+
missing_headers = query.select { |q| closest_mismatched_headers.grep(q).empty? }
|
326
|
+
raise Roo::HeaderRowNotFoundError, missing_headers
|
401
327
|
end
|
402
328
|
|
403
329
|
protected
|
@@ -410,7 +336,7 @@ class Roo::Base
|
|
410
336
|
filename = File.basename(filename, File.extname(filename))
|
411
337
|
end
|
412
338
|
|
413
|
-
if uri?(filename) && (qs_begin = filename.rindex(
|
339
|
+
if uri?(filename) && (qs_begin = filename.rindex("?"))
|
414
340
|
filename = filename[0..qs_begin - 1]
|
415
341
|
end
|
416
342
|
exts = Array(exts)
|
@@ -436,7 +362,7 @@ class Roo::Base
|
|
436
362
|
# Diese Methode ist eine temp. Loesung, um zu erforschen, ob der
|
437
363
|
# Zugriff mit numerischen Keys schneller ist.
|
438
364
|
def key_to_num(str)
|
439
|
-
r, c = str.split(
|
365
|
+
r, c = str.split(",")
|
440
366
|
[r.to_i, c.to_i]
|
441
367
|
end
|
442
368
|
|
@@ -498,9 +424,9 @@ class Roo::Base
|
|
498
424
|
|
499
425
|
def find_by_conditions(options)
|
500
426
|
rows = first_row.upto(last_row)
|
501
|
-
header_for =
|
502
|
-
[col
|
503
|
-
end
|
427
|
+
header_for = 1.upto(last_column).each_with_object({}) do |col, hash|
|
428
|
+
hash[col] = cell(@header_line, col)
|
429
|
+
end
|
504
430
|
|
505
431
|
# are all conditions met?
|
506
432
|
conditions = options[:conditions]
|
@@ -515,9 +441,9 @@ class Roo::Base
|
|
515
441
|
rows.map { |i| row(i) }
|
516
442
|
else
|
517
443
|
rows.map do |i|
|
518
|
-
|
519
|
-
[header_for.fetch(j)
|
520
|
-
end
|
444
|
+
1.upto(row(i).size).each_with_object({}) do |j, hash|
|
445
|
+
hash[header_for.fetch(j)] = cell(i, j)
|
446
|
+
end
|
521
447
|
end
|
522
448
|
end
|
523
449
|
end
|
@@ -535,7 +461,7 @@ class Roo::Base
|
|
535
461
|
|
536
462
|
def find_basename(filename)
|
537
463
|
if uri?(filename)
|
538
|
-
require
|
464
|
+
require "uri"
|
539
465
|
uri = URI.parse filename
|
540
466
|
File.basename(uri.path)
|
541
467
|
elsif !is_stream?(filename)
|
@@ -544,9 +470,9 @@ class Roo::Base
|
|
544
470
|
end
|
545
471
|
|
546
472
|
def make_tmpdir(prefix = nil, root = nil, &block)
|
547
|
-
warn
|
473
|
+
warn "[DEPRECATION] extend Roo::Tempdir and use its .make_tempdir instead"
|
548
474
|
prefix = "#{Roo::TEMP_PREFIX}#{prefix}"
|
549
|
-
root ||= ENV[
|
475
|
+
root ||= ENV["ROO_TMP"]
|
550
476
|
|
551
477
|
if block_given?
|
552
478
|
# folder is deleted at end of block
|
@@ -565,14 +491,17 @@ class Roo::Base
|
|
565
491
|
end
|
566
492
|
|
567
493
|
def sanitize_value(v)
|
568
|
-
v.gsub(/[[:cntrl:]]|^[\p{Space}]+|[\p{Space}]+$/,
|
494
|
+
v.gsub(/[[:cntrl:]]|^[\p{Space}]+|[\p{Space}]+$/, "")
|
569
495
|
end
|
570
496
|
|
571
497
|
def set_headers(hash = {})
|
572
498
|
# try to find header row with all values or give an error
|
573
499
|
# then create new hash by indexing strings and keeping integers for header array
|
574
|
-
|
575
|
-
@headers =
|
500
|
+
header_row = row_with(hash.values, true)
|
501
|
+
@headers = {}
|
502
|
+
hash.each_with_index do |(key, _), index|
|
503
|
+
@headers[key] = header_index(header_row[index])
|
504
|
+
end
|
576
505
|
end
|
577
506
|
|
578
507
|
def header_index(query)
|
@@ -605,17 +534,17 @@ class Roo::Base
|
|
605
534
|
end
|
606
535
|
|
607
536
|
def uri?(filename)
|
608
|
-
filename.start_with?(
|
537
|
+
filename.start_with?("http://", "https://", "ftp://")
|
609
538
|
rescue
|
610
539
|
false
|
611
540
|
end
|
612
541
|
|
613
542
|
def download_uri(uri, tmpdir)
|
614
|
-
require
|
543
|
+
require "open-uri"
|
615
544
|
tempfilename = File.join(tmpdir, find_basename(uri))
|
616
545
|
begin
|
617
|
-
File.open(tempfilename,
|
618
|
-
open(uri,
|
546
|
+
File.open(tempfilename, "wb") do |file|
|
547
|
+
open(uri, "User-Agent" => "Ruby/#{RUBY_VERSION}") do |net|
|
619
548
|
file.write(net.read)
|
620
549
|
end
|
621
550
|
end
|
@@ -626,15 +555,15 @@ class Roo::Base
|
|
626
555
|
end
|
627
556
|
|
628
557
|
def open_from_stream(stream, tmpdir)
|
629
|
-
tempfilename = File.join(tmpdir,
|
630
|
-
File.open(tempfilename,
|
558
|
+
tempfilename = File.join(tmpdir, "spreadsheet")
|
559
|
+
File.open(tempfilename, "wb") do |file|
|
631
560
|
file.write(stream[7..-1])
|
632
561
|
end
|
633
|
-
File.join(tmpdir,
|
562
|
+
File.join(tmpdir, "spreadsheet")
|
634
563
|
end
|
635
564
|
|
636
565
|
def unzip(filename, tmpdir)
|
637
|
-
require
|
566
|
+
require "zip/filesystem"
|
638
567
|
|
639
568
|
Zip::File.open(filename) do |zip|
|
640
569
|
process_zipfile_packed(zip, tmpdir)
|
@@ -647,7 +576,7 @@ class Roo::Base
|
|
647
576
|
when nil
|
648
577
|
fail ArgumentError, "Error: sheet 'nil' not valid"
|
649
578
|
when Integer
|
650
|
-
sheets.fetch(sheet
|
579
|
+
sheets.fetch(sheet) do
|
651
580
|
fail RangeError, "sheet index #{sheet} not found"
|
652
581
|
end
|
653
582
|
when String
|
@@ -659,92 +588,20 @@ class Roo::Base
|
|
659
588
|
end
|
660
589
|
end
|
661
590
|
|
662
|
-
def process_zipfile_packed(zip, tmpdir, path =
|
591
|
+
def process_zipfile_packed(zip, tmpdir, path = "")
|
663
592
|
if zip.file.file? path
|
664
593
|
# extract and return filename
|
665
|
-
File.open(File.join(tmpdir, path),
|
594
|
+
File.open(File.join(tmpdir, path), "wb") do |file|
|
666
595
|
file.write(zip.read(path))
|
667
596
|
end
|
668
597
|
File.join(tmpdir, path)
|
669
598
|
else
|
670
599
|
ret = nil
|
671
|
-
path +=
|
600
|
+
path += "/" unless path.empty?
|
672
601
|
zip.dir.foreach(path) do |filename|
|
673
602
|
ret = process_zipfile_packed(zip, tmpdir, path + filename)
|
674
603
|
end
|
675
604
|
ret
|
676
605
|
end
|
677
606
|
end
|
678
|
-
|
679
|
-
# Write all cells to the csv file. File can be a filename or nil. If the this
|
680
|
-
# parameter is nil the output goes to STDOUT
|
681
|
-
def write_csv_content(file = nil, sheet = nil, separator = ',')
|
682
|
-
file ||= STDOUT
|
683
|
-
return unless first_row(sheet) # The sheet is empty
|
684
|
-
|
685
|
-
1.upto(last_row(sheet)) do |row|
|
686
|
-
1.upto(last_column(sheet)) do |col|
|
687
|
-
file.print(separator) if col > 1
|
688
|
-
file.print cell_to_csv(row, col, sheet)
|
689
|
-
end
|
690
|
-
file.print("\n")
|
691
|
-
end
|
692
|
-
end
|
693
|
-
|
694
|
-
# The content of a cell in the csv output
|
695
|
-
def cell_to_csv(row, col, sheet)
|
696
|
-
return '' if empty?(row, col, sheet)
|
697
|
-
|
698
|
-
onecell = cell(row, col, sheet)
|
699
|
-
|
700
|
-
case celltype(row, col, sheet)
|
701
|
-
when :string
|
702
|
-
%("#{onecell.gsub('"', '""')}") unless onecell.empty?
|
703
|
-
when :boolean
|
704
|
-
# TODO: this only works for excelx
|
705
|
-
onecell = self.sheet_for(sheet).cells[[row, col]].formatted_value
|
706
|
-
%("#{onecell.gsub('"', '""').downcase}")
|
707
|
-
when :float, :percentage
|
708
|
-
if onecell == onecell.to_i
|
709
|
-
onecell.to_i.to_s
|
710
|
-
else
|
711
|
-
onecell.to_s
|
712
|
-
end
|
713
|
-
when :formula
|
714
|
-
case onecell
|
715
|
-
when String
|
716
|
-
%("#{onecell.gsub('"', '""')}") unless onecell.empty?
|
717
|
-
when Integer
|
718
|
-
onecell.to_s
|
719
|
-
when Float
|
720
|
-
if onecell == onecell.to_i
|
721
|
-
onecell.to_i.to_s
|
722
|
-
else
|
723
|
-
onecell.to_s
|
724
|
-
end
|
725
|
-
when DateTime
|
726
|
-
onecell.to_s
|
727
|
-
else
|
728
|
-
fail "unhandled onecell-class #{onecell.class}"
|
729
|
-
end
|
730
|
-
when :date, :datetime
|
731
|
-
onecell.to_s
|
732
|
-
when :time
|
733
|
-
integer_to_timestring(onecell)
|
734
|
-
when :link
|
735
|
-
%("#{onecell.url.gsub('"', '""')}")
|
736
|
-
else
|
737
|
-
fail "unhandled celltype #{celltype(row, col, sheet)}"
|
738
|
-
end || ''
|
739
|
-
end
|
740
|
-
|
741
|
-
# converts an integer value to a time string like '02:05:06'
|
742
|
-
def integer_to_timestring(content)
|
743
|
-
h = (content / 3600.0).floor
|
744
|
-
content -= h * 3600
|
745
|
-
m = (content / 60.0).floor
|
746
|
-
content -= m * 60
|
747
|
-
s = content
|
748
|
-
sprintf('%02d:%02d:%02d', h, m, s)
|
749
|
-
end
|
750
607
|
end
|
data/lib/roo/constants.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Roo
|
2
|
-
ROO_EXCEL_NOTICE = "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."
|
3
|
-
ROO_EXCELML_NOTICE = "Excel SpreadsheetML support has been extracted to roo-xls. Install roo-xls to use Roo::Excel2003XML."
|
4
|
-
ROO_GOOGLE_NOTICE = "Google support has been extracted to roo-google. Install roo-google to use Roo::Google."
|
4
|
+
ROO_EXCEL_NOTICE = "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."
|
5
|
+
ROO_EXCELML_NOTICE = "Excel SpreadsheetML support has been extracted to roo-xls. Install roo-xls to use Roo::Excel2003XML."
|
6
|
+
ROO_GOOGLE_NOTICE = "Google support has been extracted to roo-google. Install roo-google to use Roo::Google."
|
5
7
|
end
|