roo 2.0.1 → 2.7.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|