roo 2.8.3 → 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 +4 -4
- data/.github/workflows/pull-request.yml +15 -0
- data/.github/workflows/ruby.yml +34 -0
- data/CHANGELOG.md +26 -1
- data/Gemfile +1 -0
- data/README.md +36 -9
- data/lib/roo/base.rb +3 -1
- data/lib/roo/csv.rb +10 -4
- data/lib/roo/excelx/cell/number.rb +13 -2
- data/lib/roo/excelx/cell/time.rb +1 -1
- data/lib/roo/excelx/sheet_doc.rb +20 -15
- data/lib/roo/excelx/workbook.rb +1 -0
- data/lib/roo/excelx.rb +13 -7
- data/lib/roo/open_office.rb +5 -2
- data/lib/roo/spreadsheet.rb +8 -2
- data/lib/roo/tempdir.rb +4 -1
- data/lib/roo/version.rb +1 -1
- data/roo.gemspec +9 -2
- data/spec/lib/roo/base_spec.rb +20 -0
- data/spec/lib/roo/excelx/cell/time_spec.rb +15 -0
- data/spec/lib/roo/excelx_spec.rb +44 -1
- data/spec/lib/roo/spreadsheet_spec.rb +1 -1
- data/spec/lib/roo/utils_spec.rb +0 -9
- data/test/excelx/cell/test_attr_reader_default.rb +1 -1
- data/test/excelx/cell/test_number.rb +7 -0
- data/test/helpers/test_accessing_files.rb +21 -0
- data/test/roo/test_excelx.rb +30 -0
- data/test/test_roo.rb +30 -0
- metadata +28 -12
- data/.travis.yml +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd6f8267a04fcec20134f5170360fdd0259369b0c8b319100d0304c95964a6f5
|
4
|
+
data.tar.gz: 6e33716242265c9c02a7cc42b22690f03114cf12bcbbe846055040fede3cd790
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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'
|
@@ -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,4 +1,29 @@
|
|
1
|
-
##
|
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)
|
13
|
+
|
14
|
+
## [2.9.0] 2022-03-19
|
15
|
+
|
16
|
+
### Changed/Added
|
17
|
+
- Ruby 3.x Support [555](https://github.com/roo-rb/roo/pull/555)
|
18
|
+
- Ignore all richdata at 'xl/richData' of XSLX [552](https://github.com/roo-rb/roo/pull/552)
|
19
|
+
- Only copy if cell is present when `expand_merged_ranges: true` [557](https://github.com/roo-rb/roo/pull/557)
|
20
|
+
- Fixes issue where the contents of hidden sheet was returned when parsing visible sheets only. [536](https://github.com/roo-rb/roo/pull/536)
|
21
|
+
- Add formats [525](https://github.com/roo-rb/roo/pull/525)
|
22
|
+
- Fix warnings caused by Ruby 2.7 update [530](https://github.com/roo-rb/roo/pull/530)
|
23
|
+
- Add formats [525](https://github.com/roo-rb/roo/pull/525)
|
24
|
+
|
25
|
+
### Removed
|
26
|
+
- Support for ruby 2.4, 2.5, 2.6(excluded jRuby)
|
2
27
|
|
3
28
|
## [2.8.3] 2020-02-03
|
4
29
|
### Changed/Added
|
data/Gemfile
CHANGED
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.
|
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
|
-
|
31
|
-
xlsx = Roo::
|
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
|
+
```
|
36
|
+
|
37
|
+
``Roo::Spreadsheet.open`` can accept both string paths and ``File`` instances. Also, you can provide the extension of the file as an option:
|
32
38
|
|
33
|
-
|
34
|
-
|
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
|
-
|
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
|
|
@@ -249,7 +264,7 @@ ods.formula('A', 2)
|
|
249
264
|
csv = Roo::CSV.new("mycsv.csv")
|
250
265
|
```
|
251
266
|
|
252
|
-
Because Roo uses the
|
267
|
+
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
268
|
|
254
269
|
For instance, you can load tab-delimited files (``.tsv``), and you can use a particular encoding when opening the file.
|
255
270
|
|
@@ -262,6 +277,18 @@ csv = Roo::CSV.new("mytsv.tsv", csv_options: {col_sep: "\t"})
|
|
262
277
|
csv = Roo::CSV.new("mycsv.csv", csv_options: {encoding: Encoding::ISO_8859_1})
|
263
278
|
```
|
264
279
|
|
280
|
+
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).
|
281
|
+
|
282
|
+
```ruby
|
283
|
+
# Load a spreadsheet from a file path
|
284
|
+
# Roo figures out the right parser based on file extension
|
285
|
+
spreadsheet = Roo::Spreadsheet.open(csv_or_xlsx_file)
|
286
|
+
|
287
|
+
# Load a csv and auto-strip the BOM (byte order mark)
|
288
|
+
# csv files saved from MS Excel typically have the BOM marker at the beginning of the file
|
289
|
+
spreadsheet = Roo::Spreadsheet.open("mycsv.csv", { csv_options: { encoding: 'bom|utf-8' } })
|
290
|
+
```
|
291
|
+
|
265
292
|
## Upgrading from Roo 1.13.x
|
266
293
|
If you use ``.xls`` or Google spreadsheets, you will need to install ``roo-xls`` or ``roo-google`` to continue using that functionality.
|
267
294
|
|
@@ -271,7 +298,7 @@ Roo's public methods have stayed relatively consistent between 1.13.x and 2.0.0,
|
|
271
298
|
|
272
299
|
## Contributing
|
273
300
|
### Features
|
274
|
-
1. Fork it ( https://github.com/
|
301
|
+
1. Fork it ( https://github.com/roo-rb/roo/fork )
|
275
302
|
2. Install it (`bundle install --with local_development`)
|
276
303
|
3. Create your feature branch (`git checkout -b my-new-feature`)
|
277
304
|
4. Commit your changes (`git commit -am 'My new feature'`)
|
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
|
@@ -544,7 +546,7 @@ class Roo::Base
|
|
544
546
|
tempfilename = File.join(tmpdir, find_basename(uri))
|
545
547
|
begin
|
546
548
|
File.open(tempfilename, "wb") do |file|
|
547
|
-
open(uri, "User-Agent" => "Ruby/#{RUBY_VERSION}") do |net|
|
549
|
+
URI.open(uri, "User-Agent" => "Ruby/#{RUBY_VERSION}") do |net|
|
548
550
|
file.write(net.read)
|
549
551
|
end
|
550
552
|
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
|
-
|
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
|
-
|
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,10 +48,10 @@ 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
|
51
|
+
when /^#,##0.(0+)$/ then number_format("%.#{$1.size}f")
|
52
52
|
when '0%'
|
53
53
|
proc do |number|
|
54
|
-
Kernel.format('
|
54
|
+
Kernel.format('%.0f%%', number.to_f * 100)
|
55
55
|
end
|
56
56
|
when '0.00%'
|
57
57
|
proc do |number|
|
@@ -64,7 +64,18 @@ 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 }
|
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
|
68
79
|
else
|
69
80
|
raise "Unknown format: #{format.inspect}"
|
70
81
|
end
|
data/lib/roo/excelx/cell/time.rb
CHANGED
@@ -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
|
data/lib/roo/excelx/sheet_doc.rb
CHANGED
@@ -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
|
@@ -197,11 +192,12 @@ module Roo
|
|
197
192
|
# Extract merged ranges from xml
|
198
193
|
merges = {}
|
199
194
|
doc.xpath('/worksheet/mergeCells/mergeCell').each do |mergecell_xml|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
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
|
205
201
|
end
|
206
202
|
end
|
207
203
|
end
|
@@ -215,10 +211,19 @@ module Roo
|
|
215
211
|
extracted_cells = {}
|
216
212
|
empty_cell = @options[:empty_cell]
|
217
213
|
|
218
|
-
doc.xpath('/worksheet/sheetData/row
|
219
|
-
|
220
|
-
|
221
|
-
|
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
|
222
227
|
end
|
223
228
|
|
224
229
|
expand_merged_ranges(extracted_cells) if @options[:expand_merged_ranges]
|
data/lib/roo/excelx/workbook.rb
CHANGED
@@ -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)
|
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 =
|
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
|
-
|
71
|
-
|
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$/
|
data/lib/roo/open_office.rb
CHANGED
@@ -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
|
-
|
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
|
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/spreadsheet.rb
CHANGED
@@ -24,8 +24,14 @@ module Roo
|
|
24
24
|
options[:file_warning] = :ignore
|
25
25
|
extension.tr('.', '').downcase.to_sym
|
26
26
|
else
|
27
|
-
|
28
|
-
|
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/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
|
-
|
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
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
|
-
|
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
27
|
spec.add_dependency 'rubyzip', '>= 1.3.0', '< 3.0.0'
|
24
28
|
|
25
|
-
spec.add_development_dependency 'rake'
|
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
|
data/spec/lib/roo/base_spec.rb
CHANGED
@@ -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
|
@@ -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
|
data/spec/lib/roo/excelx_spec.rb
CHANGED
@@ -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
|
|
@@ -318,6 +326,30 @@ describe Roo::Excelx do
|
|
318
326
|
expect(subject.formatted_value(4, 1)).to eq '05010'
|
319
327
|
end
|
320
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
|
321
353
|
end
|
322
354
|
|
323
355
|
describe '#row' do
|
@@ -601,6 +633,17 @@ describe Roo::Excelx do
|
|
601
633
|
end
|
602
634
|
end
|
603
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
|
+
|
604
647
|
describe 'images' do
|
605
648
|
let(:path) { 'test/files/images.xlsx' }
|
606
649
|
|
@@ -653,4 +696,4 @@ describe 'Roo::Excelx with options set' do
|
|
653
696
|
end
|
654
697
|
end
|
655
698
|
end
|
656
|
-
end
|
699
|
+
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
|
data/spec/lib/roo/utils_spec.rb
CHANGED
@@ -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]
|
@@ -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)'],
|
@@ -66,6 +71,7 @@ class TestRooExcelxCellNumber < Minitest::Test
|
|
66
71
|
['0.000000000', '1042.000000000'],
|
67
72
|
['#,##0', '1,042'],
|
68
73
|
['#,##0.00', '1,042.00'],
|
74
|
+
['#,##0.000', '1,042.000'],
|
69
75
|
['0%', '104200%'],
|
70
76
|
['0.00%', '104200.00%'],
|
71
77
|
['0.00E+00', '1.04E+03'],
|
@@ -74,6 +80,7 @@ class TestRooExcelxCellNumber < Minitest::Test
|
|
74
80
|
['#,##0.00;(#,##0.00)', '1,042.00'],
|
75
81
|
['#,##0.00;[Red](#,##0.00)', '1,042.00'],
|
76
82
|
['##0.0E+0', '1.0E+03'],
|
83
|
+
["_-* #,##0.00\\ _€_-;\\-* #,##0.00\\ _€_-;_-* \"-\"??\\ _€_-;_-@_-", '1,042.00'],
|
77
84
|
['@', '1042']
|
78
85
|
].each do |style_format, result|
|
79
86
|
cell = Roo::Excelx::Cell::Number.new '1042', nil, [style_format], nil, nil, nil
|
@@ -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,
|
data/test/roo/test_excelx.rb
CHANGED
@@ -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
|
|
@@ -320,6 +334,22 @@ class TestRworkbookExcelx < Minitest::Test
|
|
320
334
|
end
|
321
335
|
end
|
322
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
|
+
|
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
|
+
|
323
353
|
def roo_class
|
324
354
|
Roo::Excelx
|
325
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.
|
4
|
+
version: 2.10.1
|
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:
|
16
|
+
date: 2024-01-18 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: nokogiri
|
@@ -53,16 +53,16 @@ dependencies:
|
|
53
53
|
name: rake
|
54
54
|
requirement: !ruby/object:Gem::Requirement
|
55
55
|
requirements:
|
56
|
-
- - "
|
56
|
+
- - ">="
|
57
57
|
- !ruby/object:Gem::Version
|
58
|
-
version: '
|
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: '
|
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,11 @@ files:
|
|
119
133
|
- ".codeclimate.yml"
|
120
134
|
- ".github/issue_template.md"
|
121
135
|
- ".github/pull_request_template.md"
|
136
|
+
- ".github/workflows/pull-request.yml"
|
137
|
+
- ".github/workflows/ruby.yml"
|
122
138
|
- ".gitignore"
|
123
139
|
- ".rubocop.yml"
|
124
140
|
- ".simplecov"
|
125
|
-
- ".travis.yml"
|
126
141
|
- CHANGELOG.md
|
127
142
|
- Gemfile
|
128
143
|
- Guardfile
|
@@ -181,6 +196,7 @@ files:
|
|
181
196
|
- spec/helpers.rb
|
182
197
|
- spec/lib/roo/base_spec.rb
|
183
198
|
- spec/lib/roo/csv_spec.rb
|
199
|
+
- spec/lib/roo/excelx/cell/time_spec.rb
|
184
200
|
- spec/lib/roo/excelx/format_spec.rb
|
185
201
|
- spec/lib/roo/excelx/relationships_spec.rb
|
186
202
|
- spec/lib/roo/excelx/sheet_doc_spec.rb
|
@@ -225,7 +241,7 @@ homepage: https://github.com/roo-rb/roo
|
|
225
241
|
licenses:
|
226
242
|
- MIT
|
227
243
|
metadata: {}
|
228
|
-
post_install_message:
|
244
|
+
post_install_message:
|
229
245
|
rdoc_options: []
|
230
246
|
require_paths:
|
231
247
|
- lib
|
@@ -233,15 +249,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
233
249
|
requirements:
|
234
250
|
- - ">="
|
235
251
|
- !ruby/object:Gem::Version
|
236
|
-
version: 2.
|
252
|
+
version: 2.7.0
|
237
253
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
238
254
|
requirements:
|
239
255
|
- - ">="
|
240
256
|
- !ruby/object:Gem::Version
|
241
257
|
version: '0'
|
242
258
|
requirements: []
|
243
|
-
rubygems_version: 3.
|
244
|
-
signing_key:
|
259
|
+
rubygems_version: 3.3.26
|
260
|
+
signing_key:
|
245
261
|
specification_version: 4
|
246
262
|
summary: Roo can access the contents of various spreadsheet files.
|
247
263
|
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
|