roo 2.0.1 → 2.7.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/.codeclimate.yml +17 -0
- data/.github/ISSUE_TEMPLATE +10 -0
- data/.gitignore +4 -0
- data/.travis.yml +10 -6
- data/CHANGELOG.md +116 -1
- data/Gemfile +3 -4
- data/Gemfile_ruby2 +30 -0
- data/Guardfile +1 -2
- data/README.md +56 -22
- data/Rakefile +1 -1
- data/lib/roo/base.rb +108 -245
- data/lib/roo/constants.rb +5 -0
- data/lib/roo/csv.rb +94 -87
- data/lib/roo/errors.rb +11 -0
- data/lib/roo/excelx/cell/base.rb +94 -0
- data/lib/roo/excelx/cell/boolean.rb +27 -0
- data/lib/roo/excelx/cell/date.rb +28 -0
- data/lib/roo/excelx/cell/datetime.rb +111 -0
- data/lib/roo/excelx/cell/empty.rb +19 -0
- data/lib/roo/excelx/cell/number.rb +87 -0
- data/lib/roo/excelx/cell/string.rb +19 -0
- data/lib/roo/excelx/cell/time.rb +43 -0
- data/lib/roo/excelx/cell.rb +33 -4
- data/lib/roo/excelx/comments.rb +33 -0
- data/lib/roo/excelx/coordinate.rb +12 -0
- data/lib/roo/excelx/extractor.rb +3 -4
- data/lib/roo/excelx/format.rb +64 -0
- data/lib/roo/excelx/shared.rb +32 -0
- data/lib/roo/excelx/shared_strings.rb +124 -4
- data/lib/roo/excelx/sheet.rb +12 -7
- data/lib/roo/excelx/sheet_doc.rb +108 -97
- data/lib/roo/excelx/styles.rb +1 -1
- data/lib/roo/excelx.rb +61 -103
- 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/libre_office.rb +1 -2
- data/lib/roo/link.rb +21 -2
- data/lib/roo/open_office.rb +468 -523
- data/lib/roo/spreadsheet.rb +3 -1
- data/lib/roo/tempdir.rb +21 -0
- data/lib/roo/utils.rb +7 -7
- data/lib/roo/version.rb +1 -1
- data/lib/roo.rb +8 -3
- data/roo.gemspec +2 -1
- data/spec/helpers.rb +5 -0
- data/spec/lib/roo/base_spec.rb +229 -0
- data/spec/lib/roo/csv_spec.rb +19 -0
- data/spec/lib/roo/excelx_spec.rb +97 -11
- data/spec/lib/roo/openoffice_spec.rb +18 -1
- data/spec/lib/roo/spreadsheet_spec.rb +20 -0
- data/spec/lib/roo/utils_spec.rb +1 -1
- data/spec/spec_helper.rb +5 -5
- data/test/all_ss.rb +12 -11
- data/test/excelx/cell/test_base.rb +63 -0
- data/test/excelx/cell/test_boolean.rb +36 -0
- data/test/excelx/cell/test_date.rb +38 -0
- data/test/excelx/cell/test_datetime.rb +45 -0
- data/test/excelx/cell/test_empty.rb +7 -0
- data/test/excelx/cell/test_number.rb +74 -0
- data/test/excelx/cell/test_string.rb +28 -0
- data/test/excelx/cell/test_time.rb +30 -0
- data/test/formatters/test_csv.rb +119 -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 +60 -0
- data/test/roo/test_excelx.rb +325 -0
- data/test/roo/test_libre_office.rb +9 -0
- data/test/roo/test_open_office.rb +289 -0
- data/test/test_helper.rb +116 -18
- data/test/test_roo.rb +362 -2088
- metadata +70 -4
- data/test/test_generic_spreadsheet.rb +0 -237
data/lib/roo/excelx/styles.rb
CHANGED
data/lib/roo/excelx.rb
CHANGED
@@ -1,10 +1,19 @@
|
|
1
1
|
require 'nokogiri'
|
2
2
|
require 'zip/filesystem'
|
3
3
|
require 'roo/link'
|
4
|
+
require 'roo/tempdir'
|
4
5
|
require 'roo/utils'
|
6
|
+
require 'forwardable'
|
7
|
+
require 'set'
|
5
8
|
|
6
9
|
module Roo
|
7
10
|
class Excelx < Roo::Base
|
11
|
+
extend Roo::Tempdir
|
12
|
+
extend Forwardable
|
13
|
+
|
14
|
+
ERROR_VALUES = %w(#N/A #REF! #NAME? #DIV/0! #NULL! #VALUE! #NUM!).to_set
|
15
|
+
|
16
|
+
require 'roo/excelx/shared'
|
8
17
|
require 'roo/excelx/workbook'
|
9
18
|
require 'roo/excelx/shared_strings'
|
10
19
|
require 'roo/excelx/styles'
|
@@ -13,68 +22,10 @@ module Roo
|
|
13
22
|
require 'roo/excelx/relationships'
|
14
23
|
require 'roo/excelx/comments'
|
15
24
|
require 'roo/excelx/sheet_doc'
|
16
|
-
|
17
|
-
|
18
|
-
EXCEPTIONAL_FORMATS = {
|
19
|
-
'h:mm am/pm' => :date,
|
20
|
-
'h:mm:ss am/pm' => :date
|
21
|
-
}
|
22
|
-
|
23
|
-
STANDARD_FORMATS = {
|
24
|
-
0 => 'General'.freeze,
|
25
|
-
1 => '0'.freeze,
|
26
|
-
2 => '0.00'.freeze,
|
27
|
-
3 => '#,##0'.freeze,
|
28
|
-
4 => '#,##0.00'.freeze,
|
29
|
-
9 => '0%'.freeze,
|
30
|
-
10 => '0.00%'.freeze,
|
31
|
-
11 => '0.00E+00'.freeze,
|
32
|
-
12 => '# ?/?'.freeze,
|
33
|
-
13 => '# ??/??'.freeze,
|
34
|
-
14 => 'mm-dd-yy'.freeze,
|
35
|
-
15 => 'd-mmm-yy'.freeze,
|
36
|
-
16 => 'd-mmm'.freeze,
|
37
|
-
17 => 'mmm-yy'.freeze,
|
38
|
-
18 => 'h:mm AM/PM'.freeze,
|
39
|
-
19 => 'h:mm:ss AM/PM'.freeze,
|
40
|
-
20 => 'h:mm'.freeze,
|
41
|
-
21 => 'h:mm:ss'.freeze,
|
42
|
-
22 => 'm/d/yy h:mm'.freeze,
|
43
|
-
37 => '#,##0 ;(#,##0)'.freeze,
|
44
|
-
38 => '#,##0 ;[Red](#,##0)'.freeze,
|
45
|
-
39 => '#,##0.00;(#,##0.00)'.freeze,
|
46
|
-
40 => '#,##0.00;[Red](#,##0.00)'.freeze,
|
47
|
-
45 => 'mm:ss'.freeze,
|
48
|
-
46 => '[h]:mm:ss'.freeze,
|
49
|
-
47 => 'mmss.0'.freeze,
|
50
|
-
48 => '##0.0E+0'.freeze,
|
51
|
-
49 => '@'.freeze
|
52
|
-
}
|
53
|
-
|
54
|
-
def to_type(format)
|
55
|
-
format = format.to_s.downcase
|
56
|
-
if (type = EXCEPTIONAL_FORMATS[format])
|
57
|
-
type
|
58
|
-
elsif format.include?('#')
|
59
|
-
:float
|
60
|
-
elsif !format.match(/d+(?![\]])/).nil? || format.include?('y')
|
61
|
-
if format.include?('h') || format.include?('s')
|
62
|
-
:datetime
|
63
|
-
else
|
64
|
-
:date
|
65
|
-
end
|
66
|
-
elsif format.include?('h') || format.include?('s')
|
67
|
-
:time
|
68
|
-
elsif format.include?('%')
|
69
|
-
:percentage
|
70
|
-
else
|
71
|
-
:float
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
module_function :to_type
|
76
|
-
end
|
25
|
+
require 'roo/excelx/coordinate'
|
26
|
+
require 'roo/excelx/format'
|
77
27
|
|
28
|
+
delegate [:styles, :workbook, :shared_strings, :rels_files, :sheet_files, :comments_files] => :@shared
|
78
29
|
ExceedsMaxError = Class.new(StandardError)
|
79
30
|
|
80
31
|
# initialization and opening of a spreadsheet file
|
@@ -87,16 +38,22 @@ module Roo
|
|
87
38
|
cell_max = options.delete(:cell_max)
|
88
39
|
sheet_options = {}
|
89
40
|
sheet_options[:expand_merged_ranges] = (options[:expand_merged_ranges] || false)
|
41
|
+
sheet_options[:no_hyperlinks] = (options[:no_hyperlinks] || false)
|
90
42
|
|
91
43
|
unless is_stream?(filename_or_stream)
|
92
|
-
file_type_check(filename_or_stream,
|
93
|
-
basename =
|
44
|
+
file_type_check(filename_or_stream, %w[.xlsx .xlsm], 'an Excel 2007', file_warning, packed)
|
45
|
+
basename = find_basename(filename_or_stream)
|
94
46
|
end
|
95
47
|
|
96
|
-
|
48
|
+
# NOTE: Create temp directory and allow Ruby to cleanup the temp directory
|
49
|
+
# when the object is garbage collected. Initially, the finalizer was
|
50
|
+
# created in the Roo::Tempdir module, but that led to a segfault
|
51
|
+
# when testing in Ruby 2.4.0.
|
52
|
+
@tmpdir = self.class.make_tempdir(self, basename, options[:tmpdir_root])
|
53
|
+
ObjectSpace.define_finalizer(self, self.class.finalize(object_id))
|
54
|
+
|
55
|
+
@shared = Shared.new(@tmpdir)
|
97
56
|
@filename = local_filename(filename_or_stream, @tmpdir, packed)
|
98
|
-
@comments_files = []
|
99
|
-
@rels_files = []
|
100
57
|
process_zipfile(@filename || filename_or_stream)
|
101
58
|
|
102
59
|
@sheet_names = workbook.sheets.map do |sheet|
|
@@ -106,7 +63,7 @@ module Roo
|
|
106
63
|
end.compact
|
107
64
|
@sheets = []
|
108
65
|
@sheets_by_name = Hash[@sheet_names.map.with_index do |sheet_name, n|
|
109
|
-
@sheets[n] = Sheet.new(sheet_name, @
|
66
|
+
@sheets[n] = Sheet.new(sheet_name, @shared, n, sheet_options)
|
110
67
|
[sheet_name, @sheets[n]]
|
111
68
|
end]
|
112
69
|
|
@@ -116,9 +73,9 @@ module Roo
|
|
116
73
|
end
|
117
74
|
|
118
75
|
super
|
119
|
-
rescue
|
120
|
-
|
121
|
-
raise
|
76
|
+
rescue
|
77
|
+
self.class.finalize_tempdirs(object_id)
|
78
|
+
raise
|
122
79
|
end
|
123
80
|
|
124
81
|
def method_missing(method, *args)
|
@@ -187,7 +144,7 @@ module Roo
|
|
187
144
|
def set(row, col, value, sheet = nil) #:nodoc:
|
188
145
|
key = normalize(row, col)
|
189
146
|
cell_type = cell_type_by_value(value)
|
190
|
-
sheet_for(sheet).cells[key] = Cell.new(value, cell_type, nil, cell_type, value, nil, nil, nil,
|
147
|
+
sheet_for(sheet).cells[key] = Cell.new(value, cell_type, nil, cell_type, value, nil, nil, nil, Coordinate.new(row, col))
|
191
148
|
end
|
192
149
|
|
193
150
|
# Returns the formula at (row,col).
|
@@ -239,14 +196,21 @@ module Roo
|
|
239
196
|
# Note: this is only available within the Excelx class
|
240
197
|
def excelx_type(row, col, sheet = nil)
|
241
198
|
key = normalize(row, col)
|
242
|
-
safe_send(sheet_for(sheet).cells[key], :
|
199
|
+
safe_send(sheet_for(sheet).cells[key], :cell_type)
|
243
200
|
end
|
244
201
|
|
245
202
|
# returns the internal value of an excelx cell
|
246
203
|
# Note: this is only available within the Excelx class
|
247
204
|
def excelx_value(row, col, sheet = nil)
|
248
205
|
key = normalize(row, col)
|
249
|
-
safe_send(sheet_for(sheet).cells[key], :
|
206
|
+
safe_send(sheet_for(sheet).cells[key], :cell_value)
|
207
|
+
end
|
208
|
+
|
209
|
+
# returns the internal value of an excelx cell
|
210
|
+
# Note: this is only available within the Excelx class
|
211
|
+
def formatted_value(row, col, sheet = nil)
|
212
|
+
key = normalize(row, col)
|
213
|
+
safe_send(sheet_for(sheet).cells[key], :formatted_value)
|
250
214
|
end
|
251
215
|
|
252
216
|
# returns the internal format of an excel cell
|
@@ -259,8 +223,8 @@ module Roo
|
|
259
223
|
sheet = sheet_for(sheet)
|
260
224
|
key = normalize(row, col)
|
261
225
|
cell = sheet.cells[key]
|
262
|
-
!cell ||
|
263
|
-
|
226
|
+
!cell || cell.empty? ||
|
227
|
+
(row < sheet.first_row || row > sheet.last_row || col < sheet.first_column || col > sheet.last_column)
|
264
228
|
end
|
265
229
|
|
266
230
|
# shows the internal representation of all cells
|
@@ -321,7 +285,12 @@ module Roo
|
|
321
285
|
# Yield an array of Excelx::Cell
|
322
286
|
# Takes options for sheet, pad_cells, and max_rows
|
323
287
|
def each_row_streaming(options = {})
|
324
|
-
sheet_for(options.delete(:sheet))
|
288
|
+
sheet = sheet_for(options.delete(:sheet))
|
289
|
+
if block_given?
|
290
|
+
sheet.each_row(options) { |row| yield row }
|
291
|
+
else
|
292
|
+
sheet.to_enum(:each_row, options)
|
293
|
+
end
|
325
294
|
end
|
326
295
|
|
327
296
|
private
|
@@ -394,11 +363,14 @@ module Roo
|
|
394
363
|
end
|
395
364
|
end
|
396
365
|
|
366
|
+
# Extracts the sheets in order, but it will ignore sheets that are not
|
367
|
+
# worksheets.
|
397
368
|
def extract_sheets_in_order(entries, sheet_ids, sheets, tmpdir)
|
398
|
-
sheet_ids.each_with_index do |id, i|
|
369
|
+
(sheet_ids & sheets.keys).each_with_index do |id, i|
|
399
370
|
name = sheets[id]
|
400
|
-
entry = entries.find { |e| e.name =~ /#{name}$/ }
|
371
|
+
entry = entries.find { |e| "/#{e.name}" =~ /#{name}$/ }
|
401
372
|
path = "#{tmpdir}/roo_sheet#{i + 1}"
|
373
|
+
sheet_files << path
|
402
374
|
@sheet_files << path
|
403
375
|
entry.extract(path)
|
404
376
|
end
|
@@ -409,19 +381,13 @@ module Roo
|
|
409
381
|
@sheet_files = []
|
410
382
|
|
411
383
|
unless is_stream?(zipfilename_or_stream)
|
412
|
-
|
384
|
+
zip_file = Zip::File.open(zipfilename_or_stream)
|
413
385
|
else
|
414
|
-
|
415
|
-
|
416
|
-
entries = []
|
417
|
-
while (entry = stream.get_next_entry)
|
418
|
-
entries << entry
|
419
|
-
end
|
420
|
-
process_zipfile_entries entries
|
421
|
-
ensure
|
422
|
-
stream.close
|
423
|
-
end
|
386
|
+
zip_file = Zip::CentralDirectory.new
|
387
|
+
zip_file.read_from_stream zipfilename_or_stream
|
424
388
|
end
|
389
|
+
|
390
|
+
process_zipfile_entries zip_file.to_a.sort_by(&:name)
|
425
391
|
end
|
426
392
|
|
427
393
|
def process_zipfile_entries(entries)
|
@@ -458,31 +424,23 @@ module Roo
|
|
458
424
|
# sheet's comment file is in the sheet1.xml.rels file. SEE
|
459
425
|
# ECMA-376 12.3.3 in "Ecma Office Open XML Part 1".
|
460
426
|
nr = Regexp.last_match[1].to_i
|
461
|
-
|
427
|
+
comments_files[nr - 1] = "#{@tmpdir}/roo_comments#{nr}"
|
428
|
+
when %r{chartsheets/_rels/sheet([0-9]+).xml.rels$}
|
429
|
+
# NOTE: Chart sheet relationship files were interfering with
|
430
|
+
# worksheets.
|
431
|
+
nil
|
462
432
|
when /sheet([0-9]+).xml.rels$/
|
463
433
|
# FIXME: Roo seems to use sheet[\d].xml.rels for hyperlinks only, but
|
464
434
|
# it also stores the location for sharedStrings, comments,
|
465
435
|
# drawings, etc.
|
466
436
|
nr = Regexp.last_match[1].to_i
|
467
|
-
|
437
|
+
rels_files[nr - 1] = "#{@tmpdir}/roo_rels#{nr}"
|
468
438
|
end
|
469
439
|
|
470
440
|
entry.extract(path) if path
|
471
441
|
end
|
472
442
|
end
|
473
443
|
|
474
|
-
def styles
|
475
|
-
@styles ||= Styles.new(File.join(@tmpdir, 'roo_styles.xml'))
|
476
|
-
end
|
477
|
-
|
478
|
-
def shared_strings
|
479
|
-
@shared_strings ||= SharedStrings.new(File.join(@tmpdir, 'roo_sharedStrings.xml'))
|
480
|
-
end
|
481
|
-
|
482
|
-
def workbook
|
483
|
-
@workbook ||= Workbook.new(File.join(@tmpdir, 'roo_workbook.xml'))
|
484
|
-
end
|
485
|
-
|
486
444
|
def safe_send(object, method, *args)
|
487
445
|
object.send(method, *args) if object && object.respond_to?(method)
|
488
446
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Roo
|
2
|
+
module Formatters
|
3
|
+
module Base
|
4
|
+
# converts an integer value to a time string like '02:05:06'
|
5
|
+
def integer_to_timestring(content)
|
6
|
+
h = (content / 3600.0).floor
|
7
|
+
content -= h * 3600
|
8
|
+
m = (content / 60.0).floor
|
9
|
+
content -= m * 60
|
10
|
+
s = content
|
11
|
+
Kernel.format("%02d:%02d:%02d", h, m, s)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Roo
|
2
|
+
module Formatters
|
3
|
+
module CSV
|
4
|
+
def to_csv(filename = nil, separator = ",", sheet = default_sheet)
|
5
|
+
if filename
|
6
|
+
File.open(filename, "w") do |file|
|
7
|
+
write_csv_content(file, sheet, separator)
|
8
|
+
end
|
9
|
+
true
|
10
|
+
else
|
11
|
+
sio = ::StringIO.new
|
12
|
+
write_csv_content(sio, sheet, separator)
|
13
|
+
sio.rewind
|
14
|
+
sio.read
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# Write all cells to the csv file. File can be a filename or nil. If the
|
21
|
+
# file argument is nil the output goes to STDOUT
|
22
|
+
def write_csv_content(file = nil, sheet = nil, separator = ",")
|
23
|
+
file ||= STDOUT
|
24
|
+
return unless first_row(sheet) # The sheet is empty
|
25
|
+
|
26
|
+
1.upto(last_row(sheet)) do |row|
|
27
|
+
1.upto(last_column(sheet)) do |col|
|
28
|
+
# TODO: use CSV.generate_line
|
29
|
+
file.print(separator) if col > 1
|
30
|
+
file.print cell_to_csv(row, col, sheet)
|
31
|
+
end
|
32
|
+
file.print("\n")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# The content of a cell in the csv output
|
37
|
+
def cell_to_csv(row, col, sheet)
|
38
|
+
return "" if empty?(row, col, sheet)
|
39
|
+
|
40
|
+
onecell = cell(row, col, sheet)
|
41
|
+
|
42
|
+
case celltype(row, col, sheet)
|
43
|
+
when :string
|
44
|
+
%("#{onecell.gsub('"', '""')}") unless onecell.empty?
|
45
|
+
when :boolean
|
46
|
+
# TODO: this only works for excelx
|
47
|
+
onecell = self.sheet_for(sheet).cells[[row, col]].formatted_value
|
48
|
+
%("#{onecell.gsub('"', '""').downcase}")
|
49
|
+
when :float, :percentage
|
50
|
+
if onecell == onecell.to_i
|
51
|
+
onecell.to_i.to_s
|
52
|
+
else
|
53
|
+
onecell.to_s
|
54
|
+
end
|
55
|
+
when :formula
|
56
|
+
case onecell
|
57
|
+
when String
|
58
|
+
%("#{onecell.gsub('"', '""')}") unless onecell.empty?
|
59
|
+
when Integer
|
60
|
+
onecell.to_s
|
61
|
+
when Float
|
62
|
+
if onecell == onecell.to_i
|
63
|
+
onecell.to_i.to_s
|
64
|
+
else
|
65
|
+
onecell.to_s
|
66
|
+
end
|
67
|
+
when Date, DateTime, TrueClass, FalseClass
|
68
|
+
onecell.to_s
|
69
|
+
else
|
70
|
+
fail "unhandled onecell-class #{onecell.class}"
|
71
|
+
end
|
72
|
+
when :date, :datetime
|
73
|
+
onecell.to_s
|
74
|
+
when :time
|
75
|
+
integer_to_timestring(onecell)
|
76
|
+
when :link
|
77
|
+
%("#{onecell.url.gsub('"', '""')}")
|
78
|
+
else
|
79
|
+
fail "unhandled celltype #{celltype(row, col, sheet)}"
|
80
|
+
end || ""
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Roo
|
2
|
+
module Formatters
|
3
|
+
module Matrix
|
4
|
+
# returns a matrix object from the whole sheet or a rectangular area of a sheet
|
5
|
+
def to_matrix(from_row = nil, from_column = nil, to_row = nil, to_column = nil, sheet = default_sheet)
|
6
|
+
require 'matrix'
|
7
|
+
|
8
|
+
return ::Matrix.empty unless first_row
|
9
|
+
|
10
|
+
from_row ||= first_row(sheet)
|
11
|
+
to_row ||= last_row(sheet)
|
12
|
+
from_column ||= first_column(sheet)
|
13
|
+
to_column ||= last_column(sheet)
|
14
|
+
|
15
|
+
::Matrix.rows(from_row.upto(to_row).map do |row|
|
16
|
+
from_column.upto(to_column).map do |col|
|
17
|
+
cell(row, col, sheet)
|
18
|
+
end
|
19
|
+
end)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# returns an XML representation of all sheets of a spreadsheet file
|
2
|
+
module Roo
|
3
|
+
module Formatters
|
4
|
+
module XML
|
5
|
+
def to_xml
|
6
|
+
Nokogiri::XML::Builder.new do |xml|
|
7
|
+
xml.spreadsheet do
|
8
|
+
sheets.each do |sheet|
|
9
|
+
self.default_sheet = sheet
|
10
|
+
xml.sheet(name: sheet) do |x|
|
11
|
+
if first_row && last_row && first_column && last_column
|
12
|
+
# sonst gibt es Fehler bei leeren Blaettern
|
13
|
+
first_row.upto(last_row) do |row|
|
14
|
+
first_column.upto(last_column) do |col|
|
15
|
+
next if empty?(row, col)
|
16
|
+
|
17
|
+
x.cell(cell(row, col),
|
18
|
+
row: row,
|
19
|
+
column: col,
|
20
|
+
type: celltype(row, col))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end.to_xml
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Roo
|
2
|
+
module Formatters
|
3
|
+
module YAML
|
4
|
+
# returns a rectangular area (default: all cells) as yaml-output
|
5
|
+
# you can add additional attributes with the prefix parameter like:
|
6
|
+
# oo.to_yaml({"file"=>"flightdata_2007-06-26", "sheet" => "1"})
|
7
|
+
def to_yaml(prefix = {}, from_row = nil, from_column = nil, to_row = nil, to_column = nil, sheet = default_sheet)
|
8
|
+
# return an empty string if there is no first_row, i.e. the sheet is empty
|
9
|
+
return "" unless first_row
|
10
|
+
|
11
|
+
from_row ||= first_row(sheet)
|
12
|
+
to_row ||= last_row(sheet)
|
13
|
+
from_column ||= first_column(sheet)
|
14
|
+
to_column ||= last_column(sheet)
|
15
|
+
|
16
|
+
result = "--- \n"
|
17
|
+
from_row.upto(to_row) do |row|
|
18
|
+
from_column.upto(to_column) do |col|
|
19
|
+
next if empty?(row, col, sheet)
|
20
|
+
|
21
|
+
result << "cell_#{row}_#{col}: \n"
|
22
|
+
prefix.each do|k, v|
|
23
|
+
result << " #{k}: #{v} \n"
|
24
|
+
end
|
25
|
+
result << " row: #{row} \n"
|
26
|
+
result << " col: #{col} \n"
|
27
|
+
result << " celltype: #{celltype(row, col, sheet)} \n"
|
28
|
+
value = cell(row, col, sheet)
|
29
|
+
if celltype(row, col, sheet) == :time
|
30
|
+
value = integer_to_timestring(value)
|
31
|
+
end
|
32
|
+
result << " value: #{value} \n"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
result
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/roo/libre_office.rb
CHANGED
data/lib/roo/link.rb
CHANGED
@@ -1,9 +1,28 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
1
3
|
module Roo
|
2
4
|
class Link < String
|
5
|
+
# FIXME: Roo::Link inherits from String. A link cell is_a?(Roo::Link). **It is
|
6
|
+
# the only situation where a cells `value` is always a String**. Link
|
7
|
+
# cells have a nifty `to_uri` method, but this method isn't easily
|
8
|
+
# reached. (e.g. `sheet.sheet_for(nil).cells[[row,column]]).value.to_uri`;
|
9
|
+
# `sheet.hyperlink(row, column)` doesn't use `to_uri`).
|
10
|
+
#
|
11
|
+
# 1. Add different types of links (String, Numeric, Date, DateTime, etc.)
|
12
|
+
# 2. Remove Roo::Link.
|
13
|
+
# 3. Don't inherit the string and pass the cell's value.
|
14
|
+
#
|
15
|
+
# I don't know the historical reasons for the Roo::Link, but right now
|
16
|
+
# it seems uneccessary. I'm in favor of keeping it just in case.
|
17
|
+
#
|
18
|
+
# I'm also in favor of passing the cell's value to Roo::Link. The
|
19
|
+
# cell.value's class would still be Roo::Link, but the value itself
|
20
|
+
# would depend on what type of cell it is (Numeric, Date, etc.).
|
21
|
+
#
|
3
22
|
attr_reader :href
|
4
|
-
|
23
|
+
alias_method :url, :href
|
5
24
|
|
6
|
-
def initialize(href='', text=href)
|
25
|
+
def initialize(href = '', text = href)
|
7
26
|
super(text)
|
8
27
|
@href = href
|
9
28
|
end
|