roo 2.9.0 → 2.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ae372e5e7ed6c045de6911c323a805fa4f60c68c2e8cd96358db03a01ff3beef
4
- data.tar.gz: 6a2f09854519bd5d1b233aafcd1cf58d29343dc48e3cc735498ea2696785fd24
3
+ metadata.gz: cd6f8267a04fcec20134f5170360fdd0259369b0c8b319100d0304c95964a6f5
4
+ data.tar.gz: 6e33716242265c9c02a7cc42b22690f03114cf12bcbbe846055040fede3cd790
5
5
  SHA512:
6
- metadata.gz: 5dae710a3f90d9d7038b0c4813ac0b400e6c587c2cf00be9239e27294f60046b2748b090207fce2ebaa3139a3adc681d0063bec9c1e6ee7c91cde51de1ae5861
7
- data.tar.gz: 34691a95ee37cf9af6640c2090d2542db2a84558829a42b4ecab13870e2d9e73ee1d0c243cb01d536d5709158cb746bf69b1e0d0a0888226f5906bf41566e542
6
+ metadata.gz: 65dd59afe1dfda800c7e88547f305dbd1eb295c5715fd23dc5123618307205ae1a0c0740511739d2880b4d91cabca10fad152d522cca96a2694f8e3c1cccbd39
7
+ data.tar.gz: 9d239b22ee226fc3466d2445127dc2c4fc50c488249171b0b86dc17a4a41779e543e83ed3def1fe9fae26ddc67608a81733483e3757b32156d822abdbf2bc974
@@ -0,0 +1,15 @@
1
+ name: Changelog
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled]
6
+
7
+ jobs:
8
+ changelog:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v2
12
+ - uses: amoniacou/changelog-enforcer@v1.4.0
13
+ with:
14
+ changeLogPath: 'CHANGELOG.md'
15
+ skipLabel: 'Skip-Changelog'
data/CHANGELOG.md CHANGED
@@ -1,4 +1,15 @@
1
- ## Unreleased
1
+ ## [2.10.1] 2024-01-17
2
+
3
+ ### Changed/Added
4
+ - Prevent warnings on Ruby 3.1 if finalizer is called twice [586](https://github.com/roo-rb/roo/pull/586)
5
+ - Fix Roo::Base#each_with_pagename degraded at [576](https://github.com/roo-rb/roo/pull/576) [583](https://github.com/roo-rb/roo/pull/583)
6
+
7
+ ## [2.10.0] 2023-02-07
8
+
9
+ ### Changed/Added
10
+ - Fix gsub! usage for open office documents on a frozen string [581](https://github.com/roo-rb/roo/pull/581)
11
+ - Add support for boolean values in open office files that were generated via Google Sheets [580](https://github.com/roo-rb/roo/pull/580)
12
+ - Roo::Base#each_with_pagename returns Enumerator Object [576](https://github.com/roo-rb/roo/pull/576)
2
13
 
3
14
  ## [2.9.0] 2022-03-19
4
15
 
data/README.md CHANGED
@@ -18,26 +18,41 @@ Install as a gem
18
18
  Or add it to your Gemfile
19
19
 
20
20
  ```ruby
21
- gem "roo", "~> 2.8.0"
21
+ gem "roo", "~> 2.10.0"
22
22
  ```
23
23
  ## Usage
24
24
 
25
- Opening a spreadsheet
25
+ ### Opening a spreadsheet
26
26
 
27
+ You can use the `Roo::Spreadsheet` class so `roo` automatically detects which [parser class](https://github.com/roo-rb/roo/blob/master/lib/roo.rb#L17) to use for you.
27
28
  ```ruby
28
29
  require 'roo'
29
30
 
30
- xlsx = Roo::Spreadsheet.open('./new_prices.xlsx')
31
- xlsx = Roo::Excelx.new("./new_prices.xlsx")
31
+ file_name = './new_prices.xlsx'
32
+ xlsx = Roo::Spreadsheet.open(file_name)
33
+ xlsx.info
34
+ # => Returns basic info about the spreadsheet file
35
+ ```
32
36
 
33
- # Use the extension option if the extension is ambiguous.
34
- xlsx = Roo::Spreadsheet.open('./rails_temp_upload', extension: :xlsx)
37
+ ``Roo::Spreadsheet.open`` can accept both string paths and ``File`` instances. Also, you can provide the extension of the file as an option:
38
+
39
+ ```ruby
40
+ require 'roo'
35
41
 
42
+ file_name = './rails_temp_upload'
43
+ xlsx = Roo::Spreadsheet.open(file_name, extension: :xlsx)
36
44
  xlsx.info
37
45
  # => Returns basic info about the spreadsheet file
38
46
  ```
39
47
 
40
- ``Roo::Spreadsheet.open`` can accept both paths and ``File`` instances.
48
+ On the other hand, if you know what the file extension is, you can use the specific parser class instead:
49
+ ```ruby
50
+ require 'roo'
51
+
52
+ xlsx = Roo::Excelx.new("./new_prices.xlsx")
53
+ xlsx.info
54
+ # => Returns basic info about the spreadsheet file
55
+ ```
41
56
 
42
57
  ### Working with sheets
43
58
 
data/lib/roo/base.rb CHANGED
@@ -250,6 +250,8 @@ class Roo::Base
250
250
 
251
251
  # iterate through all worksheets of a document
252
252
  def each_with_pagename
253
+ return to_enum(:each_with_pagename) { sheets.size } unless block_given?
254
+
253
255
  sheets.each do |s|
254
256
  yield sheet(s, true)
255
257
  end
@@ -51,7 +51,7 @@ module Roo
51
51
  when /^#,##0.(0+)$/ then number_format("%.#{$1.size}f")
52
52
  when '0%'
53
53
  proc do |number|
54
- Kernel.format('%d%%', number.to_f * 100)
54
+ Kernel.format('%.0f%%', number.to_f * 100)
55
55
  end
56
56
  when '0.00%'
57
57
  proc do |number|
@@ -66,6 +66,16 @@ module Roo
66
66
  when '##0.0E+0' then '%.1E'
67
67
  when "_-* #,##0.00\\ _€_-;\\-* #,##0.00\\ _€_-;_-* \"-\"??\\ _€_-;_-@_-" then number_format('%.2f', '-%.2f')
68
68
  when '@' then proc { |number| number }
69
+ when /^(?:_\()?"([^"]*)"(?:\* )?([^_]+)/
70
+ proc do |number|
71
+ formatted_number = generate_formatter($2).call(number)
72
+ "#{$1}#{formatted_number}"
73
+ end
74
+ when /^_[- \(]\[\$([^-]*)[^#@]+([^_]+)/
75
+ proc do |number|
76
+ formatted_number = generate_formatter($2).call(number)
77
+ "#{$1}#{formatted_number}"
78
+ end
69
79
  else
70
80
  raise "Unknown format: #{format.inspect}"
71
81
  end
@@ -211,10 +211,19 @@ module Roo
211
211
  extracted_cells = {}
212
212
  empty_cell = @options[:empty_cell]
213
213
 
214
- doc.xpath('/worksheet/sheetData/row/c').each do |cell_xml|
215
- coordinate = ::Roo::Utils.extract_coordinate(cell_xml["r"])
216
- cell = cell_from_xml(cell_xml, hyperlinks(relationships)[coordinate], coordinate, empty_cell)
217
- extracted_cells[coordinate] = cell if cell
214
+ doc.xpath('/worksheet/sheetData/row').each.with_index(1) do |row_xml, ycoord|
215
+ row_xml.xpath('c').each.with_index(1) do |cell_xml, xcoord|
216
+ r = cell_xml['r']
217
+ coordinate =
218
+ if r.nil?
219
+ ::Roo::Excelx::Coordinate.new(ycoord, xcoord)
220
+ else
221
+ ::Roo::Utils.extract_coordinate(r)
222
+ end
223
+
224
+ cell = cell_from_xml(cell_xml, hyperlinks(relationships)[coordinate], coordinate, empty_cell)
225
+ extracted_cells[coordinate] = cell if cell
226
+ end
218
227
  end
219
228
 
220
229
  expand_merged_ranges(extracted_cells) if @options[:expand_merged_ranges]
@@ -32,6 +32,7 @@ module Roo
32
32
  doc.xpath('//definedName').each_with_object({}) do |defined_name, hash|
33
33
  # "Sheet1!$C$5"
34
34
  sheet, coordinates = defined_name.text.split('!$', 2)
35
+ next unless coordinates
35
36
  col, row = coordinates.split('$')
36
37
  name = defined_name['name']
37
38
  hash[name] = Label.new(name, sheet, row, col)
@@ -423,7 +423,10 @@ module Roo
423
423
  @style[sheet][key] = style_name
424
424
  case @cell_type[sheet][key]
425
425
  when :float
426
- @cell[sheet][key] = (table_cell.attributes['value'].to_s.include?(".") || table_cell.children.first.text.include?(".")) ? v.to_f : v.to_i
426
+ value = (table_cell.attributes['value'].to_s.include?(".") || table_cell.children.first.text.include?(".")) ? v.to_f : v.to_i
427
+ value = 'true' if formula == '=TRUE()'
428
+ value = 'false' if formula == '=FALSE()'
429
+ @cell[sheet][key] = value
427
430
  when :percentage
428
431
  @cell[sheet][key] = v.to_f
429
432
  when :string
@@ -517,7 +520,7 @@ module Roo
517
520
  str_v += child.content #.text
518
521
  end
519
522
  end
520
- str_v.gsub!(/'/, "'") # special case not supported by unescapeHTML
523
+ str_v = str_v.gsub(/'/, "'") # special case not supported by unescapeHTML
521
524
  str_v = CGI.unescapeHTML(str_v)
522
525
  end # == 'p'
523
526
  end
data/lib/roo/tempdir.rb CHANGED
@@ -4,7 +4,10 @@ module Roo
4
4
  if @tempdirs && (dirs_to_remove = @tempdirs[object_id])
5
5
  @tempdirs.delete(object_id)
6
6
  dirs_to_remove.each do |dir|
7
- ::FileUtils.remove_entry(dir)
7
+ # Pass force=true to avoid an exception (and thus warnings in Ruby 3.1) if dir has
8
+ # already been removed. This can occur when the finalizer is called both in a forked
9
+ # child process and in the parent.
10
+ ::FileUtils.remove_entry(dir, true)
8
11
  end
9
12
  end
10
13
  end
data/lib/roo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Roo
2
- VERSION = "2.9.0"
2
+ VERSION = "2.10.1"
3
3
  end
@@ -182,6 +182,26 @@ describe Roo::Base do
182
182
  end
183
183
  end
184
184
 
185
+ describe '#each_with_pagename' do
186
+ context 'when block given' do
187
+ it 'iterate with sheet and sheet_name' do
188
+ sheet_names = []
189
+ spreadsheet.each_with_pagename do |sheet_name, sheet|
190
+ sheet_names << sheet_name
191
+ end
192
+ expect(sheet_names).to eq ['my_sheet', 'blank sheet']
193
+ end
194
+ end
195
+
196
+ context 'when called without block' do
197
+ it 'should return an enumerator with all the rows' do
198
+ each_with_pagename = spreadsheet.each_with_pagename
199
+ expect(each_with_pagename).to be_a(Enumerator)
200
+ expect(each_with_pagename.to_a.last).to eq([spreadsheet.default_sheet, spreadsheet])
201
+ end
202
+ end
203
+ end
204
+
185
205
  describe '#each' do
186
206
  it 'should return an enumerator with all the rows' do
187
207
  each = spreadsheet.each
@@ -326,6 +326,30 @@ describe Roo::Excelx do
326
326
  expect(subject.formatted_value(4, 1)).to eq '05010'
327
327
  end
328
328
  end
329
+
330
+ context 'contains US currency' do
331
+ let(:path) { 'test/files/currency-us.xlsx' }
332
+
333
+ it 'returns a zero-padded number' do
334
+ expect(subject.formatted_value(4, 1)).to eq '$20.51'
335
+ end
336
+ end
337
+
338
+ context 'contains euro currency' do
339
+ let(:path) { 'test/files/currency-euro.xlsx' }
340
+
341
+ it 'returns a zero-padded number' do
342
+ expect(subject.formatted_value(4, 1)).to eq '€20.51'
343
+ end
344
+ end
345
+
346
+ context 'contains uk currency' do
347
+ let(:path) { 'test/files/currency-uk.xlsx' }
348
+
349
+ it 'returns a zero-padded number' do
350
+ expect(subject.formatted_value(4, 1)).to eq '£20.51'
351
+ end
352
+ end
329
353
  end
330
354
 
331
355
  describe '#row' do
@@ -609,6 +633,17 @@ describe Roo::Excelx do
609
633
  end
610
634
  end
611
635
 
636
+ describe 'opening a file with filters' do
637
+ let(:path) { 'test/files/wrong_coordinates.xlsx' }
638
+ subject(:xlsx) do
639
+ Roo::Spreadsheet.open(path)
640
+ end
641
+
642
+ it 'should properly extract defined_names' do
643
+ expect(subject.sheet(0).workbook.defined_names.length).to eq(1)
644
+ end
645
+ end
646
+
612
647
  describe 'images' do
613
648
  let(:path) { 'test/files/images.xlsx' }
614
649
 
@@ -35,6 +35,11 @@ class TestRooExcelxCellNumber < Minitest::Test
35
35
  assert_kind_of(Float, cell.value)
36
36
  end
37
37
 
38
+ def test_rounded_percent_formatted_value
39
+ cell = Roo::Excelx::Cell::Number.new '0.569999999995', nil, ['0%'], nil, nil, nil
40
+ assert_equal('57%', cell.formatted_value)
41
+ end
42
+
38
43
  def test_formats_with_negative_numbers
39
44
  [
40
45
  ['#,##0 ;(#,##0)', '(1,042)'],
@@ -36,6 +36,27 @@ module TestAccesingFiles
36
36
  end
37
37
  end
38
38
 
39
+ def test_finalize_twice
40
+ skip if defined? JRUBY_VERSION
41
+
42
+ instance = Class.new { include Roo::Tempdir }.new
43
+
44
+ tempdir = instance.make_tempdir(instance, "my_temp_prefix", nil)
45
+ assert File.exist?(tempdir), "Expected #{tempdir} to initially exist"
46
+
47
+ pid = Process.fork do
48
+ # Inside the forked process finalize does not affect the parent process's state, but does
49
+ # delete the tempfile on disk
50
+ instance.finalize_tempdirs(instance.object_id)
51
+ end
52
+
53
+ Process.wait(pid)
54
+ refute File.exist?(tempdir), "Expected #{tempdir} to have been cleaned up by child process"
55
+
56
+ instance.finalize_tempdirs(instance.object_id)
57
+ refute File.exist?(tempdir), "Expected #{tempdir} to still have been cleaned up"
58
+ end
59
+
39
60
  def test_cleanup_on_error
40
61
  # NOTE: This test was occasionally failing because when it started running
41
62
  # other tests would have already added folders to the temp directory,
@@ -341,6 +341,15 @@ class TestRworkbookExcelx < Minitest::Test
341
341
  assert_equal "Example richtext", xlsx.cell("b", 1)
342
342
  end
343
343
 
344
+ def test_implicit_coordinates
345
+ xlsx = roo_class.new(File.join(TESTDIR, 'implicit_coordinates.xlsx'))
346
+
347
+ assert_equal 'Test', xlsx.cell('a', 1)
348
+ assert_equal 'A2', xlsx.cell('a', 2)
349
+ assert_equal 'B2', xlsx.cell(2, 2)
350
+ assert_equal 'C2', xlsx.cell('c', 2)
351
+ end
352
+
344
353
  def roo_class
345
354
  Roo::Excelx
346
355
  end
data/test/test_roo.rb CHANGED
@@ -274,6 +274,24 @@ class TestRoo < Minitest::Test
274
274
  end
275
275
  end
276
276
 
277
+ def test_cell_boolean_from_google_sheets
278
+ with_each_spreadsheet(:name=>'boolean-from-google-sheets', :format=>[:openoffice, :excelx]) do |oo|
279
+ if oo.class == Roo::Excelx
280
+ assert_equal true, oo.cell(1, 1), "failure in #{oo.class}"
281
+ assert_equal false, oo.cell(2, 1), "failure in #{oo.class}"
282
+
283
+ cell = oo.sheet_for(oo.default_sheet).cells[[1, 1,]]
284
+ assert_equal 'TRUE', cell.formatted_value
285
+
286
+ cell = oo.sheet_for(oo.default_sheet).cells[[2, 1,]]
287
+ assert_equal 'FALSE', cell.formatted_value
288
+ else
289
+ assert_equal "true", oo.cell(1,1), "failure in #{oo.class}"
290
+ assert_equal "false", oo.cell(2,1), "failure in #{oo.class}"
291
+ end
292
+ end
293
+ end
294
+
277
295
  def test_cell_multiline
278
296
  with_each_spreadsheet(:name=>'paragraph', :format=>[:openoffice, :excelx]) do |oo|
279
297
  assert_equal "This is a test\nof a multiline\nCell", oo.cell(1,1)
@@ -282,6 +300,18 @@ class TestRoo < Minitest::Test
282
300
  end
283
301
  end
284
302
 
303
+ def test_apostrophe_replacement
304
+ with_each_spreadsheet(:name=>'apostrophe', :format=>[:openoffice]) do |oo|
305
+ assert_equal "'", oo.cell(1,1)
306
+ end
307
+ end
308
+
309
+ def test_frozen_string_usage
310
+ with_each_spreadsheet(:name=>'frozen_string', :format=>[:openoffice]) do |oo|
311
+ assert_equal "", oo.cell(1,1)
312
+ end
313
+ end
314
+
285
315
  def test_row_whitespace
286
316
  # auf dieses Dokument habe ich keinen Zugriff TODO:
287
317
  # TODO: No access to document whitespace?
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.9.0
4
+ version: 2.10.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Preymesser
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2022-03-19 00:00:00.000000000 Z
16
+ date: 2024-01-18 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: nokogiri
@@ -133,6 +133,7 @@ files:
133
133
  - ".codeclimate.yml"
134
134
  - ".github/issue_template.md"
135
135
  - ".github/pull_request_template.md"
136
+ - ".github/workflows/pull-request.yml"
136
137
  - ".github/workflows/ruby.yml"
137
138
  - ".gitignore"
138
139
  - ".rubocop.yml"
@@ -255,7 +256,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
255
256
  - !ruby/object:Gem::Version
256
257
  version: '0'
257
258
  requirements: []
258
- rubygems_version: 3.3.3
259
+ rubygems_version: 3.3.26
259
260
  signing_key:
260
261
  specification_version: 4
261
262
  summary: Roo can access the contents of various spreadsheet files.