roo 2.8.1 → 2.9.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 62db88823f6fadaa7e33bbc16a68f3ed67f85d7676cbc51340d48c6867622187
4
- data.tar.gz: ada9c23708c44a4c97e145918022170fd425f0a651e56b1748be2c258f41fe26
3
+ metadata.gz: ae372e5e7ed6c045de6911c323a805fa4f60c68c2e8cd96358db03a01ff3beef
4
+ data.tar.gz: 6a2f09854519bd5d1b233aafcd1cf58d29343dc48e3cc735498ea2696785fd24
5
5
  SHA512:
6
- metadata.gz: ebb7cc89869d46e4bbf36166c00d86462878e1c54676a1439537f1499ed4c388254e4c1b89197487a96e3905f89cf601e6e3b8f7c0db977844b478de8938d58a
7
- data.tar.gz: 885465f444f35ffd2ce68e0963d87ab1457234df91c16012d60d58f38f0eda6ad0822867fe44623921f8c0184465c1477f4484c80183b692f0106ecc235cd8e1
6
+ metadata.gz: 5dae710a3f90d9d7038b0c4813ac0b400e6c587c2cf00be9239e27294f60046b2748b090207fce2ebaa3139a3adc681d0063bec9c1e6ee7c91cde51de1ae5861
7
+ data.tar.gz: 34691a95ee37cf9af6640c2090d2542db2a84558829a42b4ecab13870e2d9e73ee1d0c243cb01d536d5709158cb746bf69b1e0d0a0888226f5906bf41566e542
@@ -0,0 +1,34 @@
1
+ name: Ruby
2
+ on:
3
+ push:
4
+ branches:
5
+ - master
6
+ pull_request:
7
+ branches:
8
+ - master
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ ruby:
16
+ - '2.7'
17
+ - '3.0'
18
+ - '3.1'
19
+ - ruby-head
20
+ - jruby-9.3.3.0
21
+ include:
22
+ - ruby: ruby-head
23
+ env:
24
+ RUBYOPT: '--jit'
25
+ steps:
26
+ - uses: actions/checkout@v2
27
+ - uses: ruby/setup-ruby@v1
28
+ with:
29
+ ruby-version: ${{ matrix.ruby }}
30
+ bundler-cache: true
31
+ - run: bundle exec rake
32
+ env:
33
+ LONG_RUN: true
34
+
data/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  ## Unreleased
2
2
 
3
+ ## [2.9.0] 2022-03-19
4
+
5
+ ### Changed/Added
6
+ - Ruby 3.x Support [555](https://github.com/roo-rb/roo/pull/555)
7
+ - Ignore all richdata at 'xl/richData' of XSLX [552](https://github.com/roo-rb/roo/pull/552)
8
+ - Only copy if cell is present when `expand_merged_ranges: true` [557](https://github.com/roo-rb/roo/pull/557)
9
+ - Fixes issue where the contents of hidden sheet was returned when parsing visible sheets only. [536](https://github.com/roo-rb/roo/pull/536)
10
+ - Add formats [525](https://github.com/roo-rb/roo/pull/525)
11
+ - Fix warnings caused by Ruby 2.7 update [530](https://github.com/roo-rb/roo/pull/530)
12
+ - Add formats [525](https://github.com/roo-rb/roo/pull/525)
13
+
14
+ ### Removed
15
+ - Support for ruby 2.4, 2.5, 2.6(excluded jRuby)
16
+
17
+ ## [2.8.3] 2020-02-03
18
+ ### Changed/Added
19
+ - Updated rubyzip version. Now minimal version is 1.3.0 [515](https://github.com/roo-rb/roo/pull/515) - [CVE-2019-16892](https://github.com/rubyzip/rubyzip/pull/403)
20
+
21
+ ## [2.8.2] 2019-02-01
22
+ ### Changed/Added
23
+ - Support range cell for Excelx's links [490](https://github.com/roo-rb/roo/pull/490)
24
+ - Skip `extract_hyperlinks` if not required [488](https://github.com/roo-rb/roo/pull/488)
25
+
26
+ ### Fixed
27
+ - Fixed error for invalid link [492](https://github.com/roo-rb/roo/pull/492)
28
+
3
29
  ## [2.8.1] 2019-01-21
4
30
  ### Fixed
5
31
  - Fixed error if excelx's cell have empty children [487](https://github.com/roo-rb/roo/pull/487)
data/Gemfile CHANGED
@@ -10,6 +10,7 @@ group :test do
10
10
  gem 'simplecov', '>= 0.9.0', require: false
11
11
  gem 'coveralls', require: false
12
12
  gem "minitest-reporters"
13
+ gem 'webrick' if RUBY_VERSION >= '3.0.0'
13
14
  end
14
15
 
15
16
  group :local_development do
data/README.md CHANGED
@@ -18,7 +18,7 @@ Install as a gem
18
18
  Or add it to your Gemfile
19
19
 
20
20
  ```ruby
21
- gem "roo", "~> 2.7.0"
21
+ gem "roo", "~> 2.8.0"
22
22
  ```
23
23
  ## Usage
24
24
 
@@ -249,7 +249,7 @@ ods.formula('A', 2)
249
249
  csv = Roo::CSV.new("mycsv.csv")
250
250
  ```
251
251
 
252
- Because Roo uses the [standard CSV library](), you can use options available to that library to parse csv files. You can pass options using the ``csv_options`` key.
252
+ Because Roo uses the standard CSV library, you can use options available to that library to parse csv files. You can pass options using the ``csv_options`` key.
253
253
 
254
254
  For instance, you can load tab-delimited files (``.tsv``), and you can use a particular encoding when opening the file.
255
255
 
@@ -262,6 +262,18 @@ csv = Roo::CSV.new("mytsv.tsv", csv_options: {col_sep: "\t"})
262
262
  csv = Roo::CSV.new("mycsv.csv", csv_options: {encoding: Encoding::ISO_8859_1})
263
263
  ```
264
264
 
265
+ You can also open csv files through the Roo::Spreadsheet class (useful if you accept both CSV and Excel types from a user file upload, for example).
266
+
267
+ ```ruby
268
+ # Load a spreadsheet from a file path
269
+ # Roo figures out the right parser based on file extension
270
+ spreadsheet = Roo::Spreadsheet.open(csv_or_xlsx_file)
271
+
272
+ # Load a csv and auto-strip the BOM (byte order mark)
273
+ # csv files saved from MS Excel typically have the BOM marker at the beginning of the file
274
+ spreadsheet = Roo::Spreadsheet.open("mycsv.csv", { csv_options: { encoding: 'bom|utf-8' } })
275
+ ```
276
+
265
277
  ## Upgrading from Roo 1.13.x
266
278
  If you use ``.xls`` or Google spreadsheets, you will need to install ``roo-xls`` or ``roo-google`` to continue using that functionality.
267
279
 
@@ -271,7 +283,7 @@ Roo's public methods have stayed relatively consistent between 1.13.x and 2.0.0,
271
283
 
272
284
  ## Contributing
273
285
  ### Features
274
- 1. Fork it ( https://github.com/[my-github-username]/roo/fork )
286
+ 1. Fork it ( https://github.com/roo-rb/roo/fork )
275
287
  2. Install it (`bundle install --with local_development`)
276
288
  3. Create your feature branch (`git checkout -b my-new-feature`)
277
289
  4. Commit your changes (`git commit -am 'My new feature'`)
data/lib/roo/base.rb CHANGED
@@ -544,7 +544,7 @@ class Roo::Base
544
544
  tempfilename = File.join(tmpdir, find_basename(uri))
545
545
  begin
546
546
  File.open(tempfilename, "wb") do |file|
547
- open(uri, "User-Agent" => "Ruby/#{RUBY_VERSION}") do |net|
547
+ URI.open(uri, "User-Agent" => "Ruby/#{RUBY_VERSION}") do |net|
548
548
  file.write(net.read)
549
549
  end
550
550
  end
data/lib/roo/csv.rb CHANGED
@@ -90,17 +90,23 @@ module Roo
90
90
  def each_row(options, &block)
91
91
  if uri?(filename)
92
92
  each_row_using_tempdir(options, &block)
93
- elsif is_stream?(filename_or_stream)
94
- ::CSV.new(filename_or_stream, options).each(&block)
95
93
  else
96
- ::CSV.foreach(filename, options, &block)
94
+ csv_foreach(filename_or_stream, options, &block)
97
95
  end
98
96
  end
99
97
 
100
98
  def each_row_using_tempdir(options, &block)
101
99
  ::Dir.mktmpdir(Roo::TEMP_PREFIX, ENV["ROO_TMP"]) do |tmpdir|
102
100
  tmp_filename = download_uri(filename, tmpdir)
103
- ::CSV.foreach(tmp_filename, options, &block)
101
+ csv_foreach(tmp_filename, options, &block)
102
+ end
103
+ end
104
+
105
+ def csv_foreach(path_or_io, options, &block)
106
+ if is_stream?(path_or_io)
107
+ ::CSV.new(path_or_io, **options).each(&block)
108
+ else
109
+ ::CSV.foreach(path_or_io, **options, &block)
104
110
  end
105
111
  end
106
112
 
@@ -48,7 +48,7 @@ module Roo
48
48
  when /^(0+)$/ then "%0#{$1.size}d"
49
49
  when /^0\.(0+)$/ then "%.#{$1.size}f"
50
50
  when '#,##0' then number_format('%.0f')
51
- when '#,##0.00' then number_format('%.2f')
51
+ when /^#,##0.(0+)$/ then number_format("%.#{$1.size}f")
52
52
  when '0%'
53
53
  proc do |number|
54
54
  Kernel.format('%d%%', number.to_f * 100)
@@ -64,6 +64,7 @@ module Roo
64
64
  when '#,##0.00;[Red](#,##0.00)' then number_format('%.2f', '[Red](%.2f)')
65
65
  # FIXME: not quite sure what the format should look like in this case.
66
66
  when '##0.0E+0' then '%.1E'
67
+ when "_-* #,##0.00\\ _€_-;\\-* #,##0.00\\ _€_-;_-* \"-\"??\\ _€_-;_-@_-" then number_format('%.2f', '-%.2f')
67
68
  when '@' then proc { |number| number }
68
69
  else
69
70
  raise "Unknown format: #{format.inspect}"
@@ -13,7 +13,7 @@ module Roo
13
13
  super
14
14
  @format = excelx_type.last
15
15
  @datetime = create_datetime(base_date, value)
16
- @value = link ? Roo::Link.new(link, value) : (value.to_f * 86_400).to_i
16
+ @value = link ? Roo::Link.new(link, value) : (value.to_f * 86_400).round.to_i
17
17
  end
18
18
 
19
19
  def formatted_value
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'roo/excelx/extractor'
2
4
 
3
5
  module Roo
@@ -11,10 +13,16 @@ module Roo
11
13
  @relationships ||= extract_relationships
12
14
  end
13
15
 
16
+ def include_type?(type)
17
+ to_a.any? do |_, rel|
18
+ rel["Type"]&.include? type
19
+ end
20
+ end
21
+
14
22
  private
15
23
 
16
24
  def extract_relationships
17
- return [] unless doc_exists?
25
+ return {} unless doc_exists?
18
26
 
19
27
  doc.xpath('/Relationships/Relationship').each_with_object({}) do |rel, hash|
20
28
  hash[rel['Id']] = rel
@@ -22,7 +22,7 @@ module Roo
22
22
 
23
23
  def hyperlinks(relationships)
24
24
  # If you're sure you're not going to need this hyperlinks you can discard it
25
- @hyperlinks ||= if @options[:no_hyperlinks]
25
+ @hyperlinks ||= if @options[:no_hyperlinks] || !relationships.include_type?("hyperlink")
26
26
  {}
27
27
  else
28
28
  extract_hyperlinks(relationships)
@@ -101,12 +101,7 @@ module Roo
101
101
  cell_xml_children.each do |cell|
102
102
  case cell.name
103
103
  when 'is'
104
- content = +""
105
- cell.children.each do |inline_str|
106
- if inline_str.name == 't'
107
- content << inline_str.content
108
- end
109
- end
104
+ content = cell.search('t').map(&:content).join
110
105
  unless content.empty?
111
106
  return Excelx::Cell.cell_class(:string).new(content, formula, style, hyperlink, coordinate)
112
107
  end
@@ -185,7 +180,10 @@ module Roo
185
180
  if relationship = relationships[hyperlink['id']]
186
181
  target_link = relationship['Target']
187
182
  target_link += "##{hyperlink['location']}" if hyperlink['location']
188
- hash[::Roo::Utils.ref_to_key(hyperlink["ref"].to_s)] = target_link
183
+
184
+ Roo::Utils.coordinates_in_range(hyperlink["ref"].to_s) do |coord|
185
+ hash[coord] = target_link
186
+ end
189
187
  end
190
188
  end
191
189
  end
@@ -194,11 +192,12 @@ module Roo
194
192
  # Extract merged ranges from xml
195
193
  merges = {}
196
194
  doc.xpath('/worksheet/mergeCells/mergeCell').each do |mergecell_xml|
197
- tl, br = mergecell_xml["ref"].split(/:/).map { |ref| ::Roo::Utils.ref_to_key(ref) }
198
- for row in tl[0]..br[0] do
199
- for col in tl[1]..br[1] do
200
- next if row == tl[0] && col == tl[1]
201
- merges[[row, col]] = tl
195
+ src, dst = mergecell_xml["ref"].split(/:/).map { |ref| ::Roo::Utils.ref_to_key(ref) }
196
+ next unless cells[src]
197
+ for row in src[0]..dst[0] do
198
+ for col in src[1]..dst[1] do
199
+ next if row == src[0] && col == src[1]
200
+ merges[[row, col]] = src
202
201
  end
203
202
  end
204
203
  end
data/lib/roo/excelx.rb CHANGED
@@ -60,15 +60,16 @@ module Roo
60
60
  @filename = local_filename(filename_or_stream, @tmpdir, packed)
61
61
  process_zipfile(@filename || filename_or_stream)
62
62
 
63
- @sheet_names = workbook.sheets.map do |sheet|
64
- unless options[:only_visible_sheets] && sheet['state'] == 'hidden'
65
- sheet['name']
66
- end
67
- end.compact
63
+ @sheet_names = []
68
64
  @sheets = []
69
65
  @sheets_by_name = {}
70
- @sheet_names.each_with_index do |sheet_name, n|
71
- @sheets_by_name[sheet_name] = @sheets[n] = Sheet.new(sheet_name, @shared, n, sheet_options)
66
+
67
+ workbook.sheets.each_with_index do |sheet, index|
68
+ next if options[:only_visible_sheets] && sheet['state'] == 'hidden'
69
+
70
+ sheet_name = sheet['name']
71
+ @sheet_names << sheet_name
72
+ @sheets_by_name[sheet_name] = @sheets[index] = Sheet.new(sheet_name, @shared, index, sheet_options)
72
73
  end
73
74
 
74
75
  if cell_max
@@ -428,6 +429,11 @@ module Roo
428
429
  entries.each do |entry|
429
430
  path =
430
431
  case entry.name.downcase
432
+ when /richdata/
433
+ # FIXME: Ignore richData as parsing is not implemented yet and can cause
434
+ # Zip::DestinationFileExistsError when including a second "styles.xml" entry
435
+ # see http://schemas.microsoft.com/office/spreadsheetml/2017/richdata2
436
+ nil
431
437
  when /sharedstrings.xml$/
432
438
  "#{@tmpdir}/roo_sharedStrings.xml"
433
439
  when /styles.xml$/
@@ -24,8 +24,14 @@ module Roo
24
24
  options[:file_warning] = :ignore
25
25
  extension.tr('.', '').downcase.to_sym
26
26
  else
27
- res = ::File.extname((path =~ /\A#{::URI::DEFAULT_PARSER.make_regexp}\z/) ? ::URI.parse(::URI.encode(path)).path : path)
28
- res.tr('.', '').downcase.to_sym
27
+ parsed_path =
28
+ if path =~ /\A#{::URI::DEFAULT_PARSER.make_regexp}\z/
29
+ # path is 7th match
30
+ Regexp.last_match[7]
31
+ else
32
+ path
33
+ end
34
+ ::File.extname(parsed_path).tr('.', '').downcase.to_sym
29
35
  end
30
36
  end
31
37
  end
data/lib/roo/utils.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Roo
2
4
  module Utils
3
5
  extend self
@@ -41,7 +43,7 @@ module Roo
41
43
 
42
44
  # convert a number to something like 'AB' (1 => 'A', 2 => 'B', ...)
43
45
  def number_to_letter(num)
44
- result = ""
46
+ result = +""
45
47
 
46
48
  until num.zero?
47
49
  num, index = (num - 1).divmod(26)
@@ -73,6 +75,25 @@ module Roo
73
75
  (x2 - (x1 - 1)) * (y2 - (y1 - 1))
74
76
  end
75
77
 
78
+ def coordinates_in_range(str)
79
+ return to_enum(:coordinates_in_range, str) unless block_given?
80
+ coordinates = str.split(":", 2).map! { |s| extract_coordinate s }
81
+
82
+ case coordinates.size
83
+ when 1
84
+ yield coordinates[0]
85
+ when 2
86
+ tl, br = coordinates
87
+ rows = tl.row..br.row
88
+ cols = tl.column..br.column
89
+ rows.each do |row|
90
+ cols.each do |column|
91
+ yield Excelx::Coordinate.new(row, column)
92
+ end
93
+ end
94
+ end
95
+ end
96
+
76
97
  def load_xml(path)
77
98
  ::File.open(path, 'rb') do |file|
78
99
  ::Nokogiri::XML(file)
data/lib/roo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Roo
2
- VERSION = "2.8.1"
2
+ VERSION = "2.9.0"
3
3
  end
data/roo.gemspec CHANGED
@@ -17,12 +17,19 @@ Gem::Specification.new do |spec|
17
17
  spec.files.reject! { |fn| fn.include?('test/files') }
18
18
  spec.require_paths = ['lib']
19
19
 
20
- spec.required_ruby_version = ">= 2.3.0"
20
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
21
+ spec.required_ruby_version = ">= 2.6.0"
22
+ else
23
+ spec.required_ruby_version = ">= 2.7.0"
24
+ end
21
25
 
22
26
  spec.add_dependency 'nokogiri', '~> 1'
23
- spec.add_dependency 'rubyzip', '>= 1.2.1', '< 2.0.0'
27
+ spec.add_dependency 'rubyzip', '>= 1.3.0', '< 3.0.0'
24
28
 
25
- spec.add_development_dependency 'rake', '~> 10.1'
29
+ spec.add_development_dependency 'rake'
26
30
  spec.add_development_dependency 'minitest', '~> 5.4', '>= 5.4.3'
27
31
  spec.add_development_dependency 'rack', '~> 1.6', '< 2.0.0'
32
+ if RUBY_VERSION >= '3.0.0'
33
+ spec.add_development_dependency 'matrix'
34
+ end
28
35
  end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Roo::Excelx::Cell::Time do
4
+ it "should set the cell value to the correct number of seconds" do
5
+ value = 0.05513888888888888 # '1:19:24'
6
+ excelx_type = [:numeric_or_formula, "h:mm:ss"]
7
+ base_timestamp = Date.new(1899, 12, 30).to_time.to_i
8
+ time_cell = Roo::Excelx::Cell::Time.new(value, nil, excelx_type, 1, nil, base_timestamp, nil)
9
+ expect(time_cell.value).to eq(1*60*60 + 19*60 + 24) # '1:19:24' in seconds
10
+ # use case from https://github.com/roo-rb/roo/issues/310
11
+ value = 0.523761574074074 # '12:34:13' in seconds
12
+ time_cell = Roo::Excelx::Cell::Time.new(value, nil, excelx_type, 1, nil, base_timestamp, nil)
13
+ expect(time_cell.value).to eq(12*60*60 + 34*60 + 13) # 12:34:13 in seconds
14
+ end
15
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Roo::Excelx::Relationships do
6
+ subject(:relationships) { Roo::Excelx::Relationships.new Roo::Excelx.new(path).rels_files[0] }
7
+
8
+ describe "#include_type?" do
9
+ [
10
+ ["with hyperlink type", "test/files/link.xlsx", true, false],
11
+ ["with nil path", "test/files/Bibelbund.xlsx", false, false],
12
+ ["with comments type", "test/files/comments-google.xlsx", false, true],
13
+ ].each do |context_desc, file_path, hyperlink_value, comments_value|
14
+ context context_desc do
15
+ let(:path) { file_path }
16
+
17
+ it "should return #{hyperlink_value} for hyperlink" do
18
+ expect(subject.include_type?("hyperlink")).to be hyperlink_value
19
+ end
20
+
21
+ it "should return #{hyperlink_value} for link" do
22
+ expect(subject.include_type?("link")).to be hyperlink_value
23
+ end
24
+
25
+ it "should return false for hypelink" do
26
+ expect(subject.include_type?("hypelink")).to be false
27
+ end
28
+
29
+ it "should return false for coment" do
30
+ expect(subject.include_type?("coment")).to be false
31
+ end
32
+
33
+ it "should return #{comments_value} for comments" do
34
+ expect(subject.include_type?("comments")).to be comments_value
35
+ end
36
+
37
+ it "should return #{comments_value} for comment" do
38
+ expect(subject.include_type?("comment")).to be comments_value
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -86,6 +86,14 @@ describe Roo::Excelx do
86
86
  end
87
87
  end
88
88
 
89
+ describe 'for a workbook with hidden sheets' do
90
+ let(:path) { 'test/files/hidden_sheets.xlsx' }
91
+
92
+ it 'returns the cell contents from the visible sheet' do
93
+ expect(Roo::Excelx.new(path, only_visible_sheets: true).cell('A', 1)).to eq "visible sheet 1"
94
+ end
95
+ end
96
+
89
97
  describe '#parse' do
90
98
  let(:path) { 'test/files/numeric-link.xlsx' }
91
99
 
@@ -379,9 +387,34 @@ describe Roo::Excelx do
379
387
  expect(subject.hyperlink?(1, 1)).to eq true
380
388
  expect(subject.hyperlink?(1, 2)).to eq false
381
389
  end
390
+
391
+ context 'defined on cell range' do
392
+ let(:path) { 'test/files/cell-range-link.xlsx' }
393
+
394
+ it 'returns the expected result' do
395
+ [[false]*3, *[[true, true, false]]*4, [false]*3].each.with_index(1) do |row, row_index|
396
+ row.each.with_index(1) do |value, col_index|
397
+ expect(subject.hyperlink?(row_index, col_index)).to eq(value)
398
+ end
399
+ end
400
+ end
401
+ end
382
402
  end
383
403
 
384
404
  describe '#hyperlink' do
405
+ context 'defined on cell range' do
406
+ let(:path) { 'test/files/cell-range-link.xlsx' }
407
+
408
+ it 'returns the expected result' do
409
+ link = "http://www.google.com"
410
+ [[nil]*3, *[[link, link, nil]]*4, [nil]*3].each.with_index(1) do |row, row_index|
411
+ row.each.with_index(1) do |value, col_index|
412
+ expect(subject.hyperlink(row_index, col_index)).to eq(value)
413
+ end
414
+ end
415
+ end
416
+ end
417
+
385
418
  context 'without location' do
386
419
  let(:path) { 'test/files/link.xlsx' }
387
420
 
@@ -628,4 +661,4 @@ describe 'Roo::Excelx with options set' do
628
661
  end
629
662
  end
630
663
  end
631
- end
664
+ end
@@ -25,7 +25,7 @@ describe Roo::Spreadsheet do
25
25
  let(:filename) { tempfile.path }
26
26
 
27
27
  it 'loads the proper type' do
28
- expect(Roo::CSV).to receive(:new).with(filename, file_warning: :ignore).and_call_original
28
+ expect(Roo::CSV).to receive(:new).with(filename, {file_warning: :ignore}).and_call_original
29
29
  expect(Roo::Spreadsheet.open(tempfile, extension: :csv)).to be_a(Roo::CSV)
30
30
  end
31
31
  end
@@ -43,15 +43,6 @@ RSpec.describe ::Roo::Utils do
43
43
  end
44
44
  end
45
45
 
46
- context '.split_coordinate' do
47
- it "returns the expected result" do
48
- expect(described_class.split_coordinate('A1')).to eq [1, 1]
49
- expect(described_class.split_coordinate('B2')).to eq [2, 2]
50
- expect(described_class.split_coordinate('R2')).to eq [2, 18]
51
- expect(described_class.split_coordinate('AR31')).to eq [31, 18 + 26]
52
- end
53
- end
54
-
55
46
  context '.extract_coordinate' do
56
47
  it "returns the expected result" do
57
48
  expect(described_class.extract_coordinate('A1')).to eq [1, 1]
@@ -90,6 +81,19 @@ RSpec.describe ::Roo::Utils do
90
81
  end
91
82
  end
92
83
 
84
+ context '.coordinates_in_range' do
85
+ it "returns the expected result" do
86
+ expect(described_class.coordinates_in_range('').to_a).to eq []
87
+ expect(described_class.coordinates_in_range('B2').to_a).to eq [[2, 2]]
88
+ expect(described_class.coordinates_in_range('D2:G3').to_a).to eq [[2, 4], [2, 5], [2, 6], [2, 7], [3, 4], [3, 5], [3, 6], [3, 7]]
89
+ expect(described_class.coordinates_in_range('G3:D2').to_a).to eq []
90
+ end
91
+
92
+ it "raises an error when appropriate" do
93
+ expect { described_class.coordinates_in_range('D2:G3:I5').to_a }.to raise_error(ArgumentError)
94
+ end
95
+ end
96
+
93
97
  context '.load_xml' do
94
98
  it 'returns the expected result' do
95
99
  expect(described_class.load_xml('test/files/sheet1.xml')).to be_a(Nokogiri::XML::Document)
@@ -60,7 +60,7 @@ class TestAttrReaderDefault < Minitest::Test
60
60
  object.instance_variable_defined?(attr_name) ? object.instance_variable_get(attr_name) : nil
61
61
  else
62
62
  object.public_send(attr_name)
63
- end
63
+ end
64
64
 
65
65
  if expected_value
66
66
  assert_equal expected_value, value
@@ -66,6 +66,7 @@ class TestRooExcelxCellNumber < Minitest::Test
66
66
  ['0.000000000', '1042.000000000'],
67
67
  ['#,##0', '1,042'],
68
68
  ['#,##0.00', '1,042.00'],
69
+ ['#,##0.000', '1,042.000'],
69
70
  ['0%', '104200%'],
70
71
  ['0.00%', '104200.00%'],
71
72
  ['0.00E+00', '1.04E+03'],
@@ -74,6 +75,7 @@ class TestRooExcelxCellNumber < Minitest::Test
74
75
  ['#,##0.00;(#,##0.00)', '1,042.00'],
75
76
  ['#,##0.00;[Red](#,##0.00)', '1,042.00'],
76
77
  ['##0.0E+0', '1.0E+03'],
78
+ ["_-* #,##0.00\\ _€_-;\\-* #,##0.00\\ _€_-;_-* \"-\"??\\ _€_-;_-@_-", '1,042.00'],
77
79
  ['@', '1042']
78
80
  ].each do |style_format, result|
79
81
  cell = Roo::Excelx::Cell::Number.new '1042', nil, [style_format], nil, nil, nil
@@ -3,7 +3,7 @@ require "test_helper"
3
3
  class TestRworkbookExcelx < Minitest::Test
4
4
  def test_download_uri_with_invalid_host
5
5
  assert_raises(RuntimeError) do
6
- Roo::Excelx.new("http://example.com/file.xlsx")
6
+ Roo::Excelx.new("http://examples.com/file.xlsx")
7
7
  end
8
8
  end
9
9
 
@@ -133,6 +133,20 @@ class TestRworkbookExcelx < Minitest::Test
133
133
  end
134
134
  end
135
135
 
136
+ def test_expand_merged_range_doesnt_insert_nil_values
137
+ options = { expand_merged_ranges: true }
138
+ xlsx = roo_class.new(File.join(TESTDIR, "merged_ranges.xlsx"), options)
139
+
140
+ refute_includes xlsx.sheet_for(0).cells.values, nil, "`nil` was copied into the cells hash from an empty merged range"
141
+ end
142
+
143
+ def test_expand_merged_range_doesnt_raise_issue_506
144
+ # Issue 506 sent an example test.xlsx file that would raise an error upon parsing.
145
+ xl = Roo::Spreadsheet.open(File.join(TESTDIR, "expand_merged_ranges_issue_506.xlsx"), expand_merged_ranges: true)
146
+ data = xl.parse(one: /one/i, two: /two/i, clean: true)
147
+ assert_equal [{:one=>"John", :two=>"Johnson"}, {:one=>"Sam", :two=>nil}, {:one=>"Dave", :two=>nil}], data
148
+ end
149
+
136
150
  def test_noexpand_merged_range
137
151
  xlsx = roo_class.new(File.join(TESTDIR, "merged_ranges.xlsx"))
138
152
 
@@ -302,6 +316,11 @@ class TestRworkbookExcelx < Minitest::Test
302
316
  end
303
317
  end
304
318
 
319
+ def test_handles_link_without_hyperlink
320
+ workbook = Roo::Spreadsheet.open(File.join(TESTDIR, "bad_link.xlsx"))
321
+ assert_equal "Test", workbook.cell(1, 1)
322
+ end
323
+
305
324
  # Excel has two base date formats one from 1900 and the other from 1904.
306
325
  # see #test_base_dates_in_excel
307
326
  def test_base_dates_in_excelx
@@ -315,6 +334,13 @@ class TestRworkbookExcelx < Minitest::Test
315
334
  end
316
335
  end
317
336
 
337
+ def test_parsing_xlsx_with_richtext
338
+ xlsx = roo_class.new(File.join(TESTDIR, "richtext_example.xlsx"))
339
+
340
+ assert_equal "Example richtext", xlsx.cell("a", 1)
341
+ assert_equal "Example richtext", xlsx.cell("b", 1)
342
+ end
343
+
318
344
  def roo_class
319
345
  Roo::Excelx
320
346
  end
@@ -15,7 +15,7 @@ class TestRooOpenOffice < Minitest::Test
15
15
 
16
16
  def test_download_uri_with_invalid_host
17
17
  assert_raises(RuntimeError) do
18
- roo_class.new("http://example.com/file.ods")
18
+ roo_class.new("http://examples.com/file.ods")
19
19
  end
20
20
  end
21
21
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roo
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.1
4
+ version: 2.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Preymesser
@@ -10,10 +10,10 @@ authors:
10
10
  - Oleksandr Simonov
11
11
  - Steven Daniels
12
12
  - Anmol Chopra
13
- autorequire:
13
+ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2019-01-21 00:00:00.000000000 Z
16
+ date: 2022-03-19 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: nokogiri
@@ -35,34 +35,34 @@ dependencies:
35
35
  requirements:
36
36
  - - ">="
37
37
  - !ruby/object:Gem::Version
38
- version: 1.2.1
38
+ version: 1.3.0
39
39
  - - "<"
40
40
  - !ruby/object:Gem::Version
41
- version: 2.0.0
41
+ version: 3.0.0
42
42
  type: :runtime
43
43
  prerelease: false
44
44
  version_requirements: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - ">="
47
47
  - !ruby/object:Gem::Version
48
- version: 1.2.1
48
+ version: 1.3.0
49
49
  - - "<"
50
50
  - !ruby/object:Gem::Version
51
- version: 2.0.0
51
+ version: 3.0.0
52
52
  - !ruby/object:Gem::Dependency
53
53
  name: rake
54
54
  requirement: !ruby/object:Gem::Requirement
55
55
  requirements:
56
- - - "~>"
56
+ - - ">="
57
57
  - !ruby/object:Gem::Version
58
- version: '10.1'
58
+ version: '0'
59
59
  type: :development
60
60
  prerelease: false
61
61
  version_requirements: !ruby/object:Gem::Requirement
62
62
  requirements:
63
- - - "~>"
63
+ - - ">="
64
64
  - !ruby/object:Gem::Version
65
- version: '10.1'
65
+ version: '0'
66
66
  - !ruby/object:Gem::Dependency
67
67
  name: minitest
68
68
  requirement: !ruby/object:Gem::Requirement
@@ -103,6 +103,20 @@ dependencies:
103
103
  - - "<"
104
104
  - !ruby/object:Gem::Version
105
105
  version: 2.0.0
106
+ - !ruby/object:Gem::Dependency
107
+ name: matrix
108
+ requirement: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ type: :development
114
+ prerelease: false
115
+ version_requirements: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
106
120
  description: |-
107
121
  Roo can access the contents of various spreadsheet files. It can handle
108
122
  * OpenOffice
@@ -119,10 +133,10 @@ files:
119
133
  - ".codeclimate.yml"
120
134
  - ".github/issue_template.md"
121
135
  - ".github/pull_request_template.md"
136
+ - ".github/workflows/ruby.yml"
122
137
  - ".gitignore"
123
138
  - ".rubocop.yml"
124
139
  - ".simplecov"
125
- - ".travis.yml"
126
140
  - CHANGELOG.md
127
141
  - Gemfile
128
142
  - Guardfile
@@ -181,7 +195,9 @@ files:
181
195
  - spec/helpers.rb
182
196
  - spec/lib/roo/base_spec.rb
183
197
  - spec/lib/roo/csv_spec.rb
198
+ - spec/lib/roo/excelx/cell/time_spec.rb
184
199
  - spec/lib/roo/excelx/format_spec.rb
200
+ - spec/lib/roo/excelx/relationships_spec.rb
185
201
  - spec/lib/roo/excelx/sheet_doc_spec.rb
186
202
  - spec/lib/roo/excelx_spec.rb
187
203
  - spec/lib/roo/libreoffice_spec.rb
@@ -224,7 +240,7 @@ homepage: https://github.com/roo-rb/roo
224
240
  licenses:
225
241
  - MIT
226
242
  metadata: {}
227
- post_install_message:
243
+ post_install_message:
228
244
  rdoc_options: []
229
245
  require_paths:
230
246
  - lib
@@ -232,16 +248,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
232
248
  requirements:
233
249
  - - ">="
234
250
  - !ruby/object:Gem::Version
235
- version: 2.3.0
251
+ version: 2.7.0
236
252
  required_rubygems_version: !ruby/object:Gem::Requirement
237
253
  requirements:
238
254
  - - ">="
239
255
  - !ruby/object:Gem::Version
240
256
  version: '0'
241
257
  requirements: []
242
- rubyforge_project:
243
- rubygems_version: 2.7.7
244
- signing_key:
258
+ rubygems_version: 3.3.3
259
+ signing_key:
245
260
  specification_version: 4
246
261
  summary: Roo can access the contents of various spreadsheet files.
247
262
  test_files: []
data/.travis.yml DELETED
@@ -1,22 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.3
4
- - 2.4
5
- - 2.5
6
- - 2.6
7
- - ruby-head
8
- - jruby-9.1.6.0
9
- env:
10
- - LONG_RUN=true
11
- matrix:
12
- include:
13
- - rvm: 2.6
14
- env: RUBYOPT=--jit LONG_RUN=true
15
- - rvm: ruby-head
16
- env: RUBYOPT=--jit LONG_RUN=true
17
- allow_failures:
18
- - rvm: ruby-head
19
- - rvm: ruby-head
20
- env: RUBYOPT=--jit LONG_RUN=true
21
- - rvm: jruby-9.1.6.0
22
- bundler_args: --without local_development