roo 2.7.0 → 2.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +5 -5
  2. data/.github/issue_template.md +16 -0
  3. data/.github/pull_request_template.md +14 -0
  4. data/.rubocop.yml +186 -0
  5. data/.travis.yml +12 -7
  6. data/CHANGELOG.md +53 -2
  7. data/LICENSE +2 -0
  8. data/README.md +29 -13
  9. data/lib/roo/base.rb +69 -61
  10. data/lib/roo/constants.rb +5 -3
  11. data/lib/roo/csv.rb +20 -12
  12. data/lib/roo/excelx/cell/base.rb +26 -12
  13. data/lib/roo/excelx/cell/boolean.rb +9 -6
  14. data/lib/roo/excelx/cell/date.rb +7 -7
  15. data/lib/roo/excelx/cell/datetime.rb +14 -18
  16. data/lib/roo/excelx/cell/empty.rb +3 -2
  17. data/lib/roo/excelx/cell/number.rb +35 -34
  18. data/lib/roo/excelx/cell/string.rb +3 -3
  19. data/lib/roo/excelx/cell/time.rb +4 -3
  20. data/lib/roo/excelx/cell.rb +10 -6
  21. data/lib/roo/excelx/comments.rb +3 -3
  22. data/lib/roo/excelx/coordinate.rb +11 -4
  23. data/lib/roo/excelx/extractor.rb +21 -3
  24. data/lib/roo/excelx/format.rb +38 -31
  25. data/lib/roo/excelx/images.rb +26 -0
  26. data/lib/roo/excelx/relationships.rb +12 -4
  27. data/lib/roo/excelx/shared.rb +10 -3
  28. data/lib/roo/excelx/shared_strings.rb +9 -15
  29. data/lib/roo/excelx/sheet.rb +49 -10
  30. data/lib/roo/excelx/sheet_doc.rb +89 -48
  31. data/lib/roo/excelx/styles.rb +3 -3
  32. data/lib/roo/excelx/workbook.rb +7 -3
  33. data/lib/roo/excelx.rb +42 -16
  34. data/lib/roo/helpers/default_attr_reader.rb +20 -0
  35. data/lib/roo/helpers/weak_instance_cache.rb +41 -0
  36. data/lib/roo/open_office.rb +8 -6
  37. data/lib/roo/spreadsheet.rb +1 -1
  38. data/lib/roo/utils.rb +70 -20
  39. data/lib/roo/version.rb +1 -1
  40. data/lib/roo.rb +4 -1
  41. data/roo.gemspec +13 -11
  42. data/spec/lib/roo/base_spec.rb +45 -3
  43. data/spec/lib/roo/excelx/relationships_spec.rb +43 -0
  44. data/spec/lib/roo/excelx/sheet_doc_spec.rb +11 -0
  45. data/spec/lib/roo/excelx_spec.rb +150 -31
  46. data/spec/lib/roo/strict_spec.rb +43 -0
  47. data/spec/lib/roo/utils_spec.rb +25 -3
  48. data/spec/lib/roo/weak_instance_cache_spec.rb +92 -0
  49. data/spec/lib/roo_spec.rb +0 -0
  50. data/spec/spec_helper.rb +1 -1
  51. data/test/excelx/cell/test_attr_reader_default.rb +72 -0
  52. data/test/excelx/cell/test_base.rb +5 -0
  53. data/test/excelx/cell/test_datetime.rb +6 -6
  54. data/test/excelx/cell/test_empty.rb +11 -0
  55. data/test/excelx/cell/test_number.rb +9 -0
  56. data/test/excelx/cell/test_string.rb +20 -0
  57. data/test/excelx/cell/test_time.rb +4 -4
  58. data/test/excelx/test_coordinate.rb +51 -0
  59. data/test/formatters/test_csv.rb +19 -2
  60. data/test/formatters/test_xml.rb +13 -9
  61. data/test/helpers/test_accessing_files.rb +60 -0
  62. data/test/helpers/test_comments.rb +43 -0
  63. data/test/helpers/test_formulas.rb +9 -0
  64. data/test/helpers/test_labels.rb +103 -0
  65. data/test/helpers/test_sheets.rb +55 -0
  66. data/test/helpers/test_styles.rb +62 -0
  67. data/test/roo/test_base.rb +182 -0
  68. data/test/roo/test_csv.rb +37 -1
  69. data/test/roo/test_excelx.rb +157 -13
  70. data/test/roo/test_open_office.rb +196 -33
  71. data/test/test_helper.rb +66 -22
  72. data/test/test_roo.rb +32 -881
  73. metadata +32 -14
  74. data/.github/ISSUE_TEMPLATE +0 -10
  75. data/Gemfile_ruby2 +0 -30
data/lib/roo/base.rb CHANGED
@@ -1,9 +1,7 @@
1
- # encoding: utf-8
2
-
3
- require 'tmpdir'
4
- require 'stringio'
5
- require 'nokogiri'
6
- require 'roo/utils'
1
+ require "tmpdir"
2
+ require "stringio"
3
+ require "nokogiri"
4
+ require "roo/utils"
7
5
  require "roo/formatters/base"
8
6
  require "roo/formatters/csv"
9
7
  require "roo/formatters/matrix"
@@ -19,8 +17,8 @@ class Roo::Base
19
17
  include Roo::Formatters::XML
20
18
  include Roo::Formatters::YAML
21
19
 
22
- MAX_ROW_COL = 999_999.freeze
23
- MIN_ROW_COL = 0.freeze
20
+ MAX_ROW_COL = 999_999
21
+ MIN_ROW_COL = 0
24
22
 
25
23
  attr_reader :headers
26
24
 
@@ -28,7 +26,7 @@ class Roo::Base
28
26
  attr_accessor :header_line
29
27
 
30
28
  def self.TEMP_PREFIX
31
- warn '[DEPRECATION] please access TEMP_PREFIX via Roo::TEMP_PREFIX'
29
+ warn "[DEPRECATION] please access TEMP_PREFIX via Roo::TEMP_PREFIX"
32
30
  Roo::TEMP_PREFIX
33
31
  end
34
32
 
@@ -56,6 +54,11 @@ class Roo::Base
56
54
  if self.class.respond_to?(:finalize_tempdirs)
57
55
  self.class.finalize_tempdirs(object_id)
58
56
  end
57
+
58
+ instance_variables.each do |instance_variable|
59
+ instance_variable_set(instance_variable, nil)
60
+ end
61
+
59
62
  nil
60
63
  end
61
64
 
@@ -64,10 +67,10 @@ class Roo::Base
64
67
  end
65
68
 
66
69
  # sets the working sheet in the document
67
- # 'sheet' can be a number (1 = first sheet) or the name of a sheet.
70
+ # 'sheet' can be a number (0 = first sheet) or the name of a sheet.
68
71
  def default_sheet=(sheet)
69
72
  validate_sheet!(sheet)
70
- @default_sheet = sheet
73
+ @default_sheet = sheet.is_a?(String) ? sheet : sheets[sheet]
71
74
  @first_row[sheet] = @last_row[sheet] = @first_column[sheet] = @last_column[sheet] = nil
72
75
  @cells_read[sheet] = false
73
76
  end
@@ -100,7 +103,7 @@ class Roo::Base
100
103
  def collect_last_row_col_for_sheet(sheet)
101
104
  first_row = first_column = MAX_ROW_COL
102
105
  last_row = last_column = MIN_ROW_COL
103
- @cell[sheet].each_pair do|key, value|
106
+ @cell[sheet].each_pair do |key, value|
104
107
  next unless value
105
108
  first_row = [first_row, key.first.to_i].min
106
109
  last_row = [last_row, key.first.to_i].max
@@ -110,13 +113,12 @@ class Roo::Base
110
113
  { first_row: first_row, first_column: first_column, last_row: last_row, last_column: last_column }
111
114
  end
112
115
 
113
- %w(first_row last_row first_column last_column).each do |key|
114
- class_eval <<-EOS, __FILE__, __LINE__ + 1
115
- def #{key}(sheet = default_sheet) # def first_row(sheet = default_sheet)
116
- read_cells(sheet) # read_cells(sheet)
117
- @#{key}[sheet] ||= first_last_row_col_for_sheet(sheet)[:#{key}] # @first_row[sheet] ||= first_last_row_col_for_sheet(sheet)[:first_row]
118
- end # end
119
- EOS
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]
121
+ end
120
122
  end
121
123
 
122
124
  def inspect
@@ -203,16 +205,16 @@ class Roo::Base
203
205
  "Number of sheets: #{sheets.size}\n"\
204
206
  "Sheets: #{sheets.join(', ')}\n"
205
207
  n = 1
206
- sheets.each do|sheet|
208
+ sheets.each do |sheet|
207
209
  self.default_sheet = sheet
208
- result << 'Sheet ' + n.to_s + ":\n"
210
+ result << "Sheet " + n.to_s + ":\n"
209
211
  if first_row
210
212
  result << " First row: #{first_row}\n"
211
213
  result << " Last row: #{last_row}\n"
212
214
  result << " First column: #{::Roo::Utils.number_to_letter(first_column)}\n"
213
215
  result << " Last column: #{::Roo::Utils.number_to_letter(last_column)}"
214
216
  else
215
- result << ' - empty -'
217
+ result << " - empty -"
216
218
  end
217
219
  result << "\n" if sheet != sheets.last
218
220
  n += 1
@@ -286,39 +288,42 @@ class Roo::Base
286
288
  clean_sheet_if_need(options)
287
289
  search_or_set_header(options)
288
290
  headers = @headers ||
289
- Hash[(first_column..last_column).map do |col|
290
- [cell(@header_line, col), col]
291
- end]
291
+ (first_column..last_column).each_with_object({}) do |col, hash|
292
+ hash[cell(@header_line, col)] = col
293
+ end
292
294
 
293
295
  @header_line.upto(last_row) do |line|
294
- yield(Hash[headers.map { |k, v| [k, cell(line, v)] }])
296
+ yield(headers.each_with_object({}) { |(k, v), hash| hash[k] = cell(line, v) })
295
297
  end
296
298
  end
297
299
  end
298
300
 
299
301
  def parse(options = {})
300
- ary = []
301
- each(options) do |row|
302
- yield(row) if block_given?
303
- ary << row
302
+ results = each(options).map do |row|
303
+ block_given? ? yield(row) : row
304
304
  end
305
- ary
305
+
306
+ options[:headers] == true ? results : results.drop(1)
306
307
  end
307
308
 
308
309
  def row_with(query, return_headers = false)
309
310
  line_no = 0
311
+ closest_mismatched_headers = []
310
312
  each do |row|
311
313
  line_no += 1
312
314
  headers = query.map { |q| row.grep(q)[0] }.compact
313
-
314
315
  if headers.length == query.length
315
316
  @header_line = line_no
316
317
  return return_headers ? headers : line_no
317
- elsif line_no > 100
318
- raise Roo::HeaderRowNotFoundError
318
+ else
319
+ closest_mismatched_headers = headers if headers.length > closest_mismatched_headers.length
320
+ if line_no > 100
321
+ break
322
+ end
319
323
  end
320
324
  end
321
- raise Roo::HeaderRowNotFoundError
325
+ missing_headers = query.select { |q| closest_mismatched_headers.grep(q).empty? }
326
+ raise Roo::HeaderRowNotFoundError, missing_headers
322
327
  end
323
328
 
324
329
  protected
@@ -331,7 +336,7 @@ class Roo::Base
331
336
  filename = File.basename(filename, File.extname(filename))
332
337
  end
333
338
 
334
- if uri?(filename) && (qs_begin = filename.rindex('?'))
339
+ if uri?(filename) && (qs_begin = filename.rindex("?"))
335
340
  filename = filename[0..qs_begin - 1]
336
341
  end
337
342
  exts = Array(exts)
@@ -357,7 +362,7 @@ class Roo::Base
357
362
  # Diese Methode ist eine temp. Loesung, um zu erforschen, ob der
358
363
  # Zugriff mit numerischen Keys schneller ist.
359
364
  def key_to_num(str)
360
- r, c = str.split(',')
365
+ r, c = str.split(",")
361
366
  [r.to_i, c.to_i]
362
367
  end
363
368
 
@@ -419,9 +424,9 @@ class Roo::Base
419
424
 
420
425
  def find_by_conditions(options)
421
426
  rows = first_row.upto(last_row)
422
- header_for = Hash[1.upto(last_column).map do |col|
423
- [col, cell(@header_line, col)]
424
- end]
427
+ header_for = 1.upto(last_column).each_with_object({}) do |col, hash|
428
+ hash[col] = cell(@header_line, col)
429
+ end
425
430
 
426
431
  # are all conditions met?
427
432
  conditions = options[:conditions]
@@ -436,9 +441,9 @@ class Roo::Base
436
441
  rows.map { |i| row(i) }
437
442
  else
438
443
  rows.map do |i|
439
- Hash[1.upto(row(i).size).map do |j|
440
- [header_for.fetch(j), cell(i, j)]
441
- end]
444
+ 1.upto(row(i).size).each_with_object({}) do |j, hash|
445
+ hash[header_for.fetch(j)] = cell(i, j)
446
+ end
442
447
  end
443
448
  end
444
449
  end
@@ -456,7 +461,7 @@ class Roo::Base
456
461
 
457
462
  def find_basename(filename)
458
463
  if uri?(filename)
459
- require 'uri'
464
+ require "uri"
460
465
  uri = URI.parse filename
461
466
  File.basename(uri.path)
462
467
  elsif !is_stream?(filename)
@@ -465,9 +470,9 @@ class Roo::Base
465
470
  end
466
471
 
467
472
  def make_tmpdir(prefix = nil, root = nil, &block)
468
- warn '[DEPRECATION] extend Roo::Tempdir and use its .make_tempdir instead'
473
+ warn "[DEPRECATION] extend Roo::Tempdir and use its .make_tempdir instead"
469
474
  prefix = "#{Roo::TEMP_PREFIX}#{prefix}"
470
- root ||= ENV['ROO_TMP']
475
+ root ||= ENV["ROO_TMP"]
471
476
 
472
477
  if block_given?
473
478
  # folder is deleted at end of block
@@ -486,14 +491,17 @@ class Roo::Base
486
491
  end
487
492
 
488
493
  def sanitize_value(v)
489
- v.gsub(/[[:cntrl:]]|^[\p{Space}]+|[\p{Space}]+$/, '')
494
+ v.gsub(/[[:cntrl:]]|^[\p{Space}]+|[\p{Space}]+$/, "")
490
495
  end
491
496
 
492
497
  def set_headers(hash = {})
493
498
  # try to find header row with all values or give an error
494
499
  # then create new hash by indexing strings and keeping integers for header array
495
- @headers = row_with(hash.values, true)
496
- @headers = Hash[hash.keys.zip(@headers.map { |x| header_index(x) })]
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
497
505
  end
498
506
 
499
507
  def header_index(query)
@@ -526,17 +534,17 @@ class Roo::Base
526
534
  end
527
535
 
528
536
  def uri?(filename)
529
- filename.start_with?('http://', 'https://', 'ftp://')
537
+ filename.start_with?("http://", "https://", "ftp://")
530
538
  rescue
531
539
  false
532
540
  end
533
541
 
534
542
  def download_uri(uri, tmpdir)
535
- require 'open-uri'
543
+ require "open-uri"
536
544
  tempfilename = File.join(tmpdir, find_basename(uri))
537
545
  begin
538
- File.open(tempfilename, 'wb') do |file|
539
- open(uri, 'User-Agent' => "Ruby/#{RUBY_VERSION}") do |net|
546
+ File.open(tempfilename, "wb") do |file|
547
+ open(uri, "User-Agent" => "Ruby/#{RUBY_VERSION}") do |net|
540
548
  file.write(net.read)
541
549
  end
542
550
  end
@@ -547,15 +555,15 @@ class Roo::Base
547
555
  end
548
556
 
549
557
  def open_from_stream(stream, tmpdir)
550
- tempfilename = File.join(tmpdir, 'spreadsheet')
551
- File.open(tempfilename, 'wb') do |file|
558
+ tempfilename = File.join(tmpdir, "spreadsheet")
559
+ File.open(tempfilename, "wb") do |file|
552
560
  file.write(stream[7..-1])
553
561
  end
554
- File.join(tmpdir, 'spreadsheet')
562
+ File.join(tmpdir, "spreadsheet")
555
563
  end
556
564
 
557
565
  def unzip(filename, tmpdir)
558
- require 'zip/filesystem'
566
+ require "zip/filesystem"
559
567
 
560
568
  Zip::File.open(filename) do |zip|
561
569
  process_zipfile_packed(zip, tmpdir)
@@ -568,7 +576,7 @@ class Roo::Base
568
576
  when nil
569
577
  fail ArgumentError, "Error: sheet 'nil' not valid"
570
578
  when Integer
571
- sheets.fetch(sheet - 1) do
579
+ sheets.fetch(sheet) do
572
580
  fail RangeError, "sheet index #{sheet} not found"
573
581
  end
574
582
  when String
@@ -580,16 +588,16 @@ class Roo::Base
580
588
  end
581
589
  end
582
590
 
583
- def process_zipfile_packed(zip, tmpdir, path = '')
591
+ def process_zipfile_packed(zip, tmpdir, path = "")
584
592
  if zip.file.file? path
585
593
  # extract and return filename
586
- File.open(File.join(tmpdir, path), 'wb') do |file|
594
+ File.open(File.join(tmpdir, path), "wb") do |file|
587
595
  file.write(zip.read(path))
588
596
  end
589
597
  File.join(tmpdir, path)
590
598
  else
591
599
  ret = nil
592
- path += '/' unless path.empty?
600
+ path += "/" unless path.empty?
593
601
  zip.dir.foreach(path) do |filename|
594
602
  ret = process_zipfile_packed(zip, tmpdir, path + filename)
595
603
  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.".freeze
3
- ROO_EXCELML_NOTICE = "Excel SpreadsheetML support has been extracted to roo-xls. Install roo-xls to use Roo::Excel2003XML.".freeze
4
- ROO_GOOGLE_NOTICE = "Google support has been extracted to roo-google. Install roo-google to use Roo::Google.".freeze
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
data/lib/roo/csv.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "csv"
2
4
  require "time"
3
5
 
@@ -63,25 +65,31 @@ module Roo
63
65
  def read_cells(sheet = default_sheet)
64
66
  sheet ||= default_sheet
65
67
  return if @cells_read[sheet]
66
- set_row_count(sheet)
67
- set_column_count(sheet)
68
- row_num = 1
68
+ row_num = 0
69
+ max_col_num = 0
69
70
 
70
71
  each_row csv_options do |row|
71
- row.each_with_index do |elem, col_num|
72
- coordinate = [row_num, col_num + 1]
72
+ row_num += 1
73
+ col_num = 0
74
+
75
+ row.each do |elem|
76
+ col_num += 1
77
+ coordinate = [row_num, col_num]
73
78
  @cell[coordinate] = elem
74
79
  @cell_type[coordinate] = celltype_class(elem)
75
80
  end
76
- row_num += 1
81
+
82
+ max_col_num = col_num if col_num > max_col_num
77
83
  end
78
84
 
85
+ set_row_count(sheet, row_num)
86
+ set_column_count(sheet, max_col_num)
79
87
  @cells_read[sheet] = true
80
88
  end
81
89
 
82
90
  def each_row(options, &block)
83
91
  if uri?(filename)
84
- each_row_using_temp_dir(filename)
92
+ each_row_using_tempdir(options, &block)
85
93
  elsif is_stream?(filename_or_stream)
86
94
  ::CSV.new(filename_or_stream, options).each(&block)
87
95
  else
@@ -89,24 +97,24 @@ module Roo
89
97
  end
90
98
  end
91
99
 
92
- def each_row_using_tempdir
100
+ def each_row_using_tempdir(options, &block)
93
101
  ::Dir.mktmpdir(Roo::TEMP_PREFIX, ENV["ROO_TMP"]) do |tmpdir|
94
102
  tmp_filename = download_uri(filename, tmpdir)
95
103
  ::CSV.foreach(tmp_filename, options, &block)
96
104
  end
97
105
  end
98
106
 
99
- def set_row_count(sheet)
107
+ def set_row_count(sheet, last_row)
100
108
  @first_row[sheet] = 1
101
- @last_row[sheet] = ::CSV.readlines(@filename).size
109
+ @last_row[sheet] = last_row
102
110
  @last_row[sheet] = @first_row[sheet] if @last_row[sheet].zero?
103
111
 
104
112
  nil
105
113
  end
106
114
 
107
- def set_column_count(sheet)
115
+ def set_column_count(sheet, last_col)
108
116
  @first_column[sheet] = 1
109
- @last_column[sheet] = (::CSV.readlines(@filename).first || []).size
117
+ @last_column[sheet] = last_col
110
118
  @last_column[sheet] = @first_column[sheet] if @last_column[sheet].zero?
111
119
 
112
120
  nil
@@ -1,13 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "roo/helpers/default_attr_reader"
4
+
1
5
  module Roo
2
6
  class Excelx
3
7
  class Cell
4
8
  class Base
9
+ extend Roo::Helpers::DefaultAttrReader
5
10
  attr_reader :cell_type, :cell_value, :value
6
11
 
7
12
  # FIXME: I think style should be deprecated. Having a style attribute
8
13
  # for a cell doesn't really accomplish much. It seems to be used
9
14
  # when you want to export to excelx.
10
- attr_reader :style
15
+ attr_reader_with_default default_type: :base, style: 1
11
16
 
12
17
 
13
18
  # FIXME: Updating a cell's value should be able tochange the cell's type,
@@ -34,14 +39,12 @@ module Roo
34
39
  attr_writer :value
35
40
 
36
41
  def initialize(value, formula, excelx_type, style, link, coordinate)
37
- @link = !!link
38
42
  @cell_value = value
39
- @cell_type = excelx_type
40
- @formula = formula
41
- @style = style
43
+ @cell_type = excelx_type if excelx_type
44
+ @formula = formula if formula
45
+ @style = style unless style == 1
42
46
  @coordinate = coordinate
43
- @type = :base
44
- @value = link? ? Roo::Link.new(link, value) : value
47
+ @value = link ? Roo::Link.new(link, value) : value
45
48
  end
46
49
 
47
50
  def type
@@ -50,16 +53,16 @@ module Roo
50
53
  elsif link?
51
54
  :link
52
55
  else
53
- @type
56
+ default_type
54
57
  end
55
58
  end
56
59
 
57
60
  def formula?
58
- !!@formula
61
+ !!(defined?(@formula) && @formula)
59
62
  end
60
63
 
61
64
  def link?
62
- !!@link
65
+ Roo::Link === @value
63
66
  end
64
67
 
65
68
  alias_method :formatted_value, :value
@@ -68,9 +71,16 @@ module Roo
68
71
  formatted_value
69
72
  end
70
73
 
71
- # DEPRECATED: Please use link instead.
74
+ # DEPRECATED: Please use link? instead.
72
75
  def hyperlink
73
- warn '[DEPRECATION] `hyperlink` is deprecated. Please use `link` instead.'
76
+ warn '[DEPRECATION] `hyperlink` is deprecated. Please use `link?` instead.'
77
+ link?
78
+ end
79
+
80
+ # DEPRECATED: Please use link? instead.
81
+ def link
82
+ warn '[DEPRECATION] `link` is deprecated. Please use `link?` instead.'
83
+ link?
74
84
  end
75
85
 
76
86
  # DEPRECATED: Please use cell_value instead.
@@ -88,6 +98,10 @@ module Roo
88
98
  def empty?
89
99
  false
90
100
  end
101
+
102
+ def presence
103
+ empty? ? nil : self
104
+ end
91
105
  end
92
106
  end
93
107
  end
@@ -1,17 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Roo
2
4
  class Excelx
3
5
  class Cell
4
6
  class Boolean < Cell::Base
5
- attr_reader :value, :formula, :format, :cell_type, :cell_value, :link, :coordinate
7
+ attr_reader :value, :formula, :format, :cell_value, :coordinate
8
+
9
+ attr_reader_with_default default_type: :boolean, cell_type: :boolean
6
10
 
7
11
  def initialize(value, formula, style, link, coordinate)
8
- super(value, formula, nil, style, link, coordinate)
9
- @type = @cell_type = :boolean
10
- @value = link? ? Roo::Link.new(link, value) : create_boolean(value)
12
+ super(value, formula, nil, style, nil, coordinate)
13
+ @value = link ? Roo::Link.new(link, value) : create_boolean(value)
11
14
  end
12
15
 
13
16
  def formatted_value
14
- value ? 'TRUE'.freeze : 'FALSE'.freeze
17
+ value ? 'TRUE' : 'FALSE'
15
18
  end
16
19
 
17
20
  private
@@ -19,7 +22,7 @@ module Roo
19
22
  def create_boolean(value)
20
23
  # FIXME: Using a boolean will cause methods like Base#to_csv to fail.
21
24
  # Roo is using some method to ignore false/nil values.
22
- value.to_i == 1 ? true : false
25
+ value.to_i == 1
23
26
  end
24
27
  end
25
28
  end
@@ -4,23 +4,23 @@ module Roo
4
4
  class Excelx
5
5
  class Cell
6
6
  class Date < Roo::Excelx::Cell::DateTime
7
- attr_reader :value, :formula, :format, :cell_type, :cell_value, :link, :coordinate
7
+ attr_reader :value, :formula, :format, :cell_type, :cell_value, :coordinate
8
+
9
+ attr_reader_with_default default_type: :date
8
10
 
9
11
  def initialize(value, formula, excelx_type, style, link, base_date, coordinate)
10
12
  # NOTE: Pass all arguments to the parent class, DateTime.
11
13
  super
12
- @type = :date
13
14
  @format = excelx_type.last
14
- @value = link? ? Roo::Link.new(link, value) : create_date(base_date, value)
15
+ @value = link ? Roo::Link.new(link, value) : create_date(base_date, value)
15
16
  end
16
17
 
17
18
  private
18
19
 
19
- def create_date(base_date, value)
20
- date = base_date + value.to_i
21
- yyyy, mm, dd = date.strftime('%Y-%m-%d').split('-')
20
+ def create_datetime(_,_); end
22
21
 
23
- ::Date.new(yyyy.to_i, mm.to_i, dd.to_i)
22
+ def create_date(base_date, value)
23
+ base_date + value.to_i
24
24
  end
25
25
  end
26
26
  end
@@ -1,16 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'date'
2
4
 
3
5
  module Roo
4
6
  class Excelx
5
7
  class Cell
6
8
  class DateTime < Cell::Base
7
- attr_reader :value, :formula, :format, :cell_value, :link, :coordinate
9
+ SECONDS_IN_DAY = 60 * 60 * 24
10
+
11
+ attr_reader :value, :formula, :format, :cell_value, :coordinate
8
12
 
9
- def initialize(value, formula, excelx_type, style, link, base_date, coordinate)
10
- super(value, formula, excelx_type, style, link, coordinate)
11
- @type = :datetime
13
+ attr_reader_with_default default_type: :datetime
14
+
15
+ def initialize(value, formula, excelx_type, style, link, base_timestamp, coordinate)
16
+ super(value, formula, excelx_type, style, nil, coordinate)
12
17
  @format = excelx_type.last
13
- @value = link? ? Roo::Link.new(link, value) : create_datetime(base_date, value)
18
+ @value = link ? Roo::Link.new(link, value) : create_datetime(base_timestamp, value)
14
19
  end
15
20
 
16
21
  # Public: Returns formatted value for a datetime. Format's can be an
@@ -78,7 +83,7 @@ module Roo
78
83
 
79
84
  TIME_FORMATS = {
80
85
  'hh' => '%H', # Hour (24): 01
81
- 'h' => '%-k'.freeze, # Hour (24): 1
86
+ 'h' => '%-k', # Hour (24): 1
82
87
  # 'hh'.freeze => '%I'.freeze, # Hour (12): 08
83
88
  # 'h'.freeze => '%-l'.freeze, # Hour (12): 8
84
89
  'mm' => '%M', # Minute: 01
@@ -92,18 +97,9 @@ module Roo
92
97
  '0' => '%1N' # Fractional Seconds: tenths.
93
98
  }
94
99
 
95
- def create_datetime(base_date, value)
96
- date = base_date + value.to_f.round(6)
97
- datetime_string = date.strftime('%Y-%m-%d %H:%M:%S.%N')
98
- t = round_datetime(datetime_string)
99
-
100
- ::DateTime.civil(t.year, t.month, t.day, t.hour, t.min, t.sec)
101
- end
102
-
103
- def round_datetime(datetime_string)
104
- /(?<yyyy>\d+)-(?<mm>\d+)-(?<dd>\d+) (?<hh>\d+):(?<mi>\d+):(?<ss>\d+.\d+)/ =~ datetime_string
105
-
106
- ::Time.new(yyyy, mm, dd, hh, mi, ss.to_r).round(0)
100
+ def create_datetime(base_timestamp, value)
101
+ timestamp = (base_timestamp + (value.to_f.round(6) * SECONDS_IN_DAY)).round(0)
102
+ ::Time.at(timestamp).utc.to_datetime
107
103
  end
108
104
  end
109
105
  end
@@ -3,10 +3,11 @@ module Roo
3
3
  class Excelx
4
4
  class Cell
5
5
  class Empty < Cell::Base
6
- attr_reader :value, :formula, :format, :cell_type, :cell_value, :hyperlink, :coordinate
6
+ attr_reader :value, :formula, :format, :cell_type, :cell_value, :coordinate
7
+
8
+ attr_reader_with_default default_type: nil, style: nil
7
9
 
8
10
  def initialize(coordinate)
9
- @value = @formula = @format = @cell_type = @cell_value = @hyperlink = nil
10
11
  @coordinate = coordinate
11
12
  end
12
13