roo 2.7.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.
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