roo 2.7.1 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/issue_template.md +16 -0
- data/.github/pull_request_template.md +14 -0
- data/.rubocop.yml +186 -0
- data/.travis.yml +12 -7
- data/CHANGELOG.md +31 -2
- data/LICENSE +2 -0
- data/README.md +25 -12
- data/lib/roo.rb +4 -1
- data/lib/roo/base.rb +65 -56
- data/lib/roo/constants.rb +5 -3
- data/lib/roo/csv.rb +20 -12
- data/lib/roo/excelx.rb +42 -16
- data/lib/roo/excelx/cell.rb +10 -6
- data/lib/roo/excelx/cell/base.rb +26 -12
- data/lib/roo/excelx/cell/boolean.rb +9 -6
- data/lib/roo/excelx/cell/date.rb +7 -7
- data/lib/roo/excelx/cell/datetime.rb +14 -18
- data/lib/roo/excelx/cell/empty.rb +3 -2
- data/lib/roo/excelx/cell/number.rb +35 -34
- data/lib/roo/excelx/cell/string.rb +3 -3
- data/lib/roo/excelx/cell/time.rb +4 -3
- data/lib/roo/excelx/comments.rb +3 -3
- data/lib/roo/excelx/coordinate.rb +11 -4
- data/lib/roo/excelx/extractor.rb +21 -3
- data/lib/roo/excelx/format.rb +38 -31
- data/lib/roo/excelx/images.rb +26 -0
- data/lib/roo/excelx/relationships.rb +3 -3
- data/lib/roo/excelx/shared.rb +10 -3
- data/lib/roo/excelx/shared_strings.rb +9 -15
- data/lib/roo/excelx/sheet.rb +49 -10
- data/lib/roo/excelx/sheet_doc.rb +86 -48
- data/lib/roo/excelx/styles.rb +3 -3
- data/lib/roo/excelx/workbook.rb +7 -3
- data/lib/roo/helpers/default_attr_reader.rb +20 -0
- data/lib/roo/helpers/weak_instance_cache.rb +41 -0
- data/lib/roo/open_office.rb +8 -6
- data/lib/roo/spreadsheet.rb +1 -1
- data/lib/roo/utils.rb +48 -19
- data/lib/roo/version.rb +1 -1
- data/roo.gemspec +13 -11
- data/spec/lib/roo/base_spec.rb +45 -3
- data/spec/lib/roo/excelx_spec.rb +125 -31
- data/spec/lib/roo/strict_spec.rb +43 -0
- data/spec/lib/roo/utils_spec.rb +12 -3
- data/spec/lib/roo/weak_instance_cache_spec.rb +92 -0
- data/spec/lib/roo_spec.rb +0 -0
- data/test/excelx/cell/test_attr_reader_default.rb +72 -0
- data/test/excelx/cell/test_base.rb +5 -0
- data/test/excelx/cell/test_datetime.rb +6 -6
- data/test/excelx/cell/test_empty.rb +11 -0
- data/test/excelx/cell/test_number.rb +9 -0
- data/test/excelx/cell/test_string.rb +20 -0
- data/test/excelx/cell/test_time.rb +4 -4
- data/test/excelx/test_coordinate.rb +51 -0
- data/test/formatters/test_csv.rb +17 -0
- data/test/formatters/test_xml.rb +4 -4
- data/test/roo/test_base.rb +2 -2
- data/test/roo/test_csv.rb +28 -0
- data/test/test_helper.rb +13 -0
- data/test/test_roo.rb +7 -7
- metadata +21 -11
- data/.github/ISSUE_TEMPLATE +0 -10
- data/Gemfile_ruby2 +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b99744522c3a62e004d42498388c4e99cdb2d45157fc26b87eb54023a957452f
|
4
|
+
data.tar.gz: ab4445f118a1144c71142a2c28724686da05d28ce04a04c0746c49628834d132
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9bc47d73a1556e7b82b652379bcba4940f9982814286feb626097e9ddea0f3a021c7ca7e9d8399293248df5272ac8db0b39c896abd940a759683ce09260e77e2
|
7
|
+
data.tar.gz: 69baf682a05fa581756a92a270e6bd44431f36683d9f1104d0466f8452201d32a34cf6a626b8168d6ef022bb2ed6b0d932e2ff5a3b1c2367c02fd4f224ce8eeb
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Thanks for filing an issue. Following these instructions will help us solve your problem sooner.
|
2
|
+
|
3
|
+
### Steps to reproduce
|
4
|
+
|
5
|
+
1. Create an executable test case for this issue ([sample test case](https://gist.github.com/tgturner/e4b7f491639b8a6dd883fe2ace408652))
|
6
|
+
2. You can share your executable test case as a [gist](https://gist.github.com), or simply paste the content into the issue description.
|
7
|
+
- You can execute the test case by running `ruby the_file.rb` in your terminal. If all goes well, you should see your test case failing.
|
8
|
+
3. Please provide a stripped down version of the offending spreadsheet.
|
9
|
+
|
10
|
+
### Issue
|
11
|
+
Describe the issue
|
12
|
+
|
13
|
+
### System configuration
|
14
|
+
**Roo version**:
|
15
|
+
|
16
|
+
**Ruby version**:
|
@@ -0,0 +1,14 @@
|
|
1
|
+
### Summary
|
2
|
+
|
3
|
+
Provide a general description of the code changes in your pull
|
4
|
+
request... were there any bugs you had fixed? If so, mention them. If
|
5
|
+
these bugs have open GitHub issues, be sure to tag them here as well,
|
6
|
+
to keep the conversation linked together.
|
7
|
+
|
8
|
+
### Other Information
|
9
|
+
|
10
|
+
If there's anything else that's important and relevant to your pull
|
11
|
+
request, mention that information here. This could include
|
12
|
+
benchmarks, or other information.
|
13
|
+
|
14
|
+
Thanks for contributing to Roo!
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.4
|
3
|
+
# RuboCop has a bunch of cops enabled by default. This setting tells RuboCop
|
4
|
+
# to ignore them, so only the ones explicitly set in this file are enabled.
|
5
|
+
DisabledByDefault: true
|
6
|
+
|
7
|
+
Performance:
|
8
|
+
Exclude:
|
9
|
+
- '**/test/**/*'
|
10
|
+
- '**/spec/**/*'
|
11
|
+
|
12
|
+
# Prefer &&/|| over and/or.
|
13
|
+
Style/AndOr:
|
14
|
+
Enabled: true
|
15
|
+
|
16
|
+
# Do not use braces for hash literals when they are the last argument of a
|
17
|
+
# method call.
|
18
|
+
Style/BracesAroundHashParameters:
|
19
|
+
Enabled: true
|
20
|
+
EnforcedStyle: context_dependent
|
21
|
+
|
22
|
+
# Align `when` with `case`.
|
23
|
+
Layout/CaseIndentation:
|
24
|
+
Enabled: true
|
25
|
+
|
26
|
+
# Align comments with method definitions.
|
27
|
+
Layout/CommentIndentation:
|
28
|
+
Enabled: true
|
29
|
+
|
30
|
+
Layout/ElseAlignment:
|
31
|
+
Enabled: true
|
32
|
+
|
33
|
+
# Align `end` with the matching keyword or starting expression except for
|
34
|
+
# assignments, where it should be aligned with the LHS.
|
35
|
+
Layout/EndAlignment:
|
36
|
+
Enabled: true
|
37
|
+
EnforcedStyleAlignWith: variable
|
38
|
+
AutoCorrect: true
|
39
|
+
|
40
|
+
Layout/EmptyLineAfterMagicComment:
|
41
|
+
Enabled: true
|
42
|
+
|
43
|
+
Layout/EmptyLinesAroundBlockBody:
|
44
|
+
Enabled: true
|
45
|
+
|
46
|
+
# In a regular class definition, no empty lines around the body.
|
47
|
+
Layout/EmptyLinesAroundClassBody:
|
48
|
+
Enabled: true
|
49
|
+
|
50
|
+
# In a regular method definition, no empty lines around the body.
|
51
|
+
Layout/EmptyLinesAroundMethodBody:
|
52
|
+
Enabled: true
|
53
|
+
|
54
|
+
# In a regular module definition, no empty lines around the body.
|
55
|
+
Layout/EmptyLinesAroundModuleBody:
|
56
|
+
Enabled: true
|
57
|
+
|
58
|
+
Layout/FirstParameterIndentation:
|
59
|
+
Enabled: true
|
60
|
+
|
61
|
+
# Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }.
|
62
|
+
Style/HashSyntax:
|
63
|
+
Enabled: true
|
64
|
+
|
65
|
+
# Method definitions after `private` or `protected` isolated calls need one
|
66
|
+
# extra level of indentation.
|
67
|
+
Layout/IndentationConsistency:
|
68
|
+
Enabled: true
|
69
|
+
|
70
|
+
# Two spaces, no tabs (for indentation).
|
71
|
+
Layout/IndentationWidth:
|
72
|
+
Enabled: true
|
73
|
+
|
74
|
+
Layout/LeadingCommentSpace:
|
75
|
+
Enabled: true
|
76
|
+
|
77
|
+
Layout/SpaceAfterColon:
|
78
|
+
Enabled: true
|
79
|
+
|
80
|
+
Layout/SpaceAfterComma:
|
81
|
+
Enabled: true
|
82
|
+
|
83
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
84
|
+
Enabled: true
|
85
|
+
|
86
|
+
Layout/SpaceAroundKeyword:
|
87
|
+
Enabled: true
|
88
|
+
|
89
|
+
Layout/SpaceAroundOperators:
|
90
|
+
Enabled: true
|
91
|
+
|
92
|
+
Layout/SpaceBeforeComma:
|
93
|
+
Enabled: true
|
94
|
+
|
95
|
+
Layout/SpaceBeforeFirstArg:
|
96
|
+
Enabled: true
|
97
|
+
|
98
|
+
Style/DefWithParentheses:
|
99
|
+
Enabled: true
|
100
|
+
|
101
|
+
# Defining a method with parameters needs parentheses.
|
102
|
+
Style/MethodDefParentheses:
|
103
|
+
Enabled: true
|
104
|
+
|
105
|
+
Style/FrozenStringLiteralComment:
|
106
|
+
Enabled: true
|
107
|
+
EnforcedStyle: always
|
108
|
+
|
109
|
+
# Use `foo {}` not `foo{}`.
|
110
|
+
Layout/SpaceBeforeBlockBraces:
|
111
|
+
Enabled: true
|
112
|
+
|
113
|
+
# Use `foo { bar }` not `foo {bar}`.
|
114
|
+
Layout/SpaceInsideBlockBraces:
|
115
|
+
Enabled: true
|
116
|
+
|
117
|
+
# Use `{ a: 1 }` not `{a:1}`.
|
118
|
+
Layout/SpaceInsideHashLiteralBraces:
|
119
|
+
Enabled: true
|
120
|
+
|
121
|
+
Layout/SpaceInsideParens:
|
122
|
+
Enabled: true
|
123
|
+
|
124
|
+
# Check quotes usage according to lint rule below.
|
125
|
+
Style/StringLiterals:
|
126
|
+
Enabled: true
|
127
|
+
EnforcedStyle: double_quotes
|
128
|
+
|
129
|
+
# Detect hard tabs, no hard tabs.
|
130
|
+
Layout/Tab:
|
131
|
+
Enabled: true
|
132
|
+
|
133
|
+
# Blank lines should not have any spaces.
|
134
|
+
Layout/TrailingBlankLines:
|
135
|
+
Enabled: true
|
136
|
+
|
137
|
+
# No trailing whitespace.
|
138
|
+
Layout/TrailingWhitespace:
|
139
|
+
Enabled: true
|
140
|
+
|
141
|
+
# Use quotes for string literals when they are enough.
|
142
|
+
Style/UnneededPercentQ:
|
143
|
+
Enabled: true
|
144
|
+
|
145
|
+
# Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
|
146
|
+
Lint/RequireParentheses:
|
147
|
+
Enabled: true
|
148
|
+
|
149
|
+
Lint/StringConversionInInterpolation:
|
150
|
+
Enabled: true
|
151
|
+
|
152
|
+
Lint/UriEscapeUnescape:
|
153
|
+
Enabled: true
|
154
|
+
|
155
|
+
Style/ParenthesesAroundCondition:
|
156
|
+
Enabled: true
|
157
|
+
|
158
|
+
Style/RedundantReturn:
|
159
|
+
Enabled: true
|
160
|
+
AllowMultipleReturnValues: true
|
161
|
+
|
162
|
+
Style/Semicolon:
|
163
|
+
Enabled: true
|
164
|
+
AllowAsExpressionSeparator: true
|
165
|
+
|
166
|
+
# Prefer Foo.method over Foo::method
|
167
|
+
Style/ColonMethodCall:
|
168
|
+
Enabled: true
|
169
|
+
|
170
|
+
Style/TrivialAccessors:
|
171
|
+
Enabled: true
|
172
|
+
|
173
|
+
Performance/FlatMap:
|
174
|
+
Enabled: true
|
175
|
+
|
176
|
+
Performance/RedundantMerge:
|
177
|
+
Enabled: true
|
178
|
+
|
179
|
+
Performance/StartWith:
|
180
|
+
Enabled: true
|
181
|
+
|
182
|
+
Performance/EndWith:
|
183
|
+
Enabled: true
|
184
|
+
|
185
|
+
Performance/RegexpMatch:
|
186
|
+
Enabled: true
|
data/.travis.yml
CHANGED
@@ -1,17 +1,22 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 2.
|
4
|
-
- 2.
|
5
|
-
- 2.
|
3
|
+
- 2.3
|
4
|
+
- 2.4
|
5
|
+
- 2.5
|
6
|
+
- 2.6
|
6
7
|
- ruby-head
|
7
8
|
- jruby-9.1.6.0
|
9
|
+
env:
|
10
|
+
- LONG_RUN=true
|
8
11
|
matrix:
|
9
12
|
include:
|
10
|
-
- rvm: 2.
|
11
|
-
|
12
|
-
- rvm:
|
13
|
-
|
13
|
+
- rvm: 2.6
|
14
|
+
env: RUBYOPT=--jit LONG_RUN=true
|
15
|
+
- rvm: ruby-head
|
16
|
+
env: RUBYOPT=--jit LONG_RUN=true
|
14
17
|
allow_failures:
|
15
18
|
- rvm: ruby-head
|
19
|
+
- rvm: ruby-head
|
20
|
+
env: RUBYOPT=--jit LONG_RUN=true
|
16
21
|
- rvm: jruby-9.1.6.0
|
17
22
|
bundler_args: --without local_development
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,33 @@
|
|
1
|
-
##
|
1
|
+
## [2.8.0] 2019-01-18
|
2
|
+
### Fixed
|
3
|
+
- Fixed inconsistent column length for CSV [375](https://github.com/roo-rb/roo/pull/375)
|
4
|
+
- Fixed formatted_value with `%` for Excelx [416](https://github.com/roo-rb/roo/pull/416)
|
5
|
+
- Improved Memory consumption and performance [434](https://github.com/roo-rb/roo/pull/434) [449](https://github.com/roo-rb/roo/pull/449) [454](https://github.com/roo-rb/roo/pull/454) [456](https://github.com/roo-rb/roo/pull/456) [458](https://github.com/roo-rb/roo/pull/458) [462](https://github.com/roo-rb/roo/pull/462) [466](https://github.com/roo-rb/roo/pull/466)
|
6
|
+
- Accept both Transitional and Strict Type for Excelx's worksheets [441](https://github.com/roo-rb/roo/pull/441)
|
7
|
+
- Fixed ruby warnings [442](https://github.com/roo-rb/roo/pull/442) [476](https://github.com/roo-rb/roo/pull/476)
|
8
|
+
- Restore support for URL as file identifier for CSV [462](https://github.com/roo-rb/roo/pull/462)
|
9
|
+
- Fixed missing location for Excelx's links [482](https://github.com/roo-rb/roo/pull/482)
|
10
|
+
|
11
|
+
### Changed / Added
|
12
|
+
- Drop support for ruby 2.2.x and lower
|
13
|
+
- Updated rubyzip version for fixing security issue. Now minimal version is 1.2.1
|
14
|
+
- Roo::Excelx::Coordinate now inherits Array [458](https://github.com/roo-rb/roo/pull/458)
|
15
|
+
- Improved Roo::HeaderRowNotFoundError exception's message [461](https://github.com/roo-rb/roo/pull/461)
|
16
|
+
- Added `empty_cell` option which by default disable allocation for Roo::Excelx::Cell::Empty [464](https://github.com/roo-rb/roo/pull/464)
|
17
|
+
- Added support for variable number of decimals for Excelx's formatted_value [387](https://github.com/roo-rb/roo/pull/387)
|
18
|
+
- Added `disable_html_injection` option to disable html injection for shared string in `Roo::Excelx` [392](https://github.com/roo-rb/roo/pull/392)
|
19
|
+
- Added image extraction for Excelx [414](https://github.com/roo-rb/roo/pull/414) [397](https://github.com/roo-rb/roo/pull/397)
|
20
|
+
- Added support for `1e6` as scientific notation for Excelx [433](https://github.com/roo-rb/roo/pull/433)
|
21
|
+
- Added support for Integer as 0 based index for Excelx's `sheet_for` [455](https://github.com/roo-rb/roo/pull/455)
|
22
|
+
- Extended `no_hyperlinks` option for non streaming Excelx methods [459](https://github.com/roo-rb/roo/pull/459)
|
23
|
+
- Added `empty_cell` option to disable Roo::Excelx::Cell::Empty allocation for Excelx [464](https://github.com/roo-rb/roo/pull/464)
|
24
|
+
- Added support for Integer with leading zero for Roo:Excelx [479](https://github.com/roo-rb/roo/pull/479)
|
25
|
+
- Refactored Excelx code [453](https://github.com/roo-rb/roo/pull/453) [477](https://github.com/roo-rb/roo/pull/477) [483](https://github.com/roo-rb/roo/pull/483) [484](https://github.com/roo-rb/roo/pull/484)
|
26
|
+
|
27
|
+
### Deprecations
|
28
|
+
- Roo::Excelx::Sheet#present_cells is deprecated [454](https://github.com/roo-rb/roo/pull/454)
|
29
|
+
- Roo::Utils.split_coordinate is deprecated [458](https://github.com/roo-rb/roo/pull/458)
|
30
|
+
- Roo::Excelx::Cell::Base#link is deprecated [457](https://github.com/roo-rb/roo/pull/457)
|
2
31
|
|
3
32
|
## [2.7.1] 2017-01-03
|
4
33
|
### Fixed
|
@@ -48,7 +77,7 @@
|
|
48
77
|
- Discard hyperlinks lookups to allow streaming parsing without loading whole files
|
49
78
|
|
50
79
|
## [2.4.0] 2016-05-14
|
51
|
-
### Fixed
|
80
|
+
### Fixed
|
52
81
|
- Fixed opening spreadsheets with charts [315](https://github.com/roo-rb/roo/pull/315)
|
53
82
|
- Fixed memory issues for Roo::Utils.number_to_letter [308](https://github.com/roo-rb/roo/pull/308)
|
54
83
|
- Fixed Roo::Excelx::Cell::Number to recognize floating point numbers [306](https://github.com/roo-rb/roo/pull/306)
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Roo
|
2
2
|
|
3
|
-
[![Build Status](https://img.shields.io/travis/roo-rb/roo.svg?style=flat-square)](https://travis-ci.org/roo-rb/roo) [![
|
3
|
+
[![Build Status](https://img.shields.io/travis/roo-rb/roo.svg?style=flat-square)](https://travis-ci.org/roo-rb/roo) [![Maintainability](https://api.codeclimate.com/v1/badges/be8d7bf34e2aeaf67c62/maintainability)](https://codeclimate.com/github/roo-rb/roo/maintainability) [![Coverage Status](https://img.shields.io/coveralls/roo-rb/roo.svg?style=flat-square)](https://coveralls.io/r/roo-rb/roo) [![Gem Version](https://img.shields.io/gem/v/roo.svg?style=flat-square)](https://rubygems.org/gems/roo)
|
4
4
|
|
5
5
|
Roo implements read access for all common spreadsheet types. It can handle:
|
6
6
|
* Excel 2007 - 2013 formats (xlsx, xlsm)
|
@@ -89,13 +89,13 @@ sheet.last_column
|
|
89
89
|
You can access the top-left cell in the following ways
|
90
90
|
|
91
91
|
```ruby
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
92
|
+
sheet.cell(1,1)
|
93
|
+
sheet.cell('A',1)
|
94
|
+
sheet.cell(1,'A')
|
95
|
+
sheet.a1
|
96
96
|
|
97
97
|
# Access the second sheet's top-left cell.
|
98
|
-
|
98
|
+
sheet.cell(1,'A',sheet.sheets[1])
|
99
99
|
```
|
100
100
|
|
101
101
|
#### Querying a spreadsheet
|
@@ -117,6 +117,12 @@ sheet.parse(id: /UPC|SKU/, qty: /ATS*\sATP\s*QTY\z/)
|
|
117
117
|
# => [{:id => 727880013358, :qty => 12}, ...]
|
118
118
|
```
|
119
119
|
|
120
|
+
Use the ``:headers`` option to include the header row in the parsed content.
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
sheet.parse(headers: true)
|
124
|
+
```
|
125
|
+
|
120
126
|
Use the ``:header_search`` option to locate the header row and assign the header names.
|
121
127
|
|
122
128
|
```ruby
|
@@ -129,6 +135,16 @@ Use the ``:clean`` option to strip out control characters and surrounding white
|
|
129
135
|
sheet.parse(clean: true)
|
130
136
|
```
|
131
137
|
|
138
|
+
#### Options
|
139
|
+
|
140
|
+
When opening the file you can add a hash of options.
|
141
|
+
|
142
|
+
##### expand_merged_ranges
|
143
|
+
If you open a document with merged cells and do not want to end up with nil values for the rows after the first one.
|
144
|
+
```ruby
|
145
|
+
xlsx = Roo::Excelx.new('./roo_error.xlsx', {:expand_merged_ranges => true})
|
146
|
+
```
|
147
|
+
|
132
148
|
### Exporting spreadsheets
|
133
149
|
Roo has the ability to export sheets using the following formats. It
|
134
150
|
will only export the ``default_sheet``.
|
@@ -230,7 +246,7 @@ ods.formula('A', 2)
|
|
230
246
|
|
231
247
|
```ruby
|
232
248
|
# Load a CSV file
|
233
|
-
|
249
|
+
csv = Roo::CSV.new("mycsv.csv")
|
234
250
|
```
|
235
251
|
|
236
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.
|
@@ -240,10 +256,10 @@ For instance, you can load tab-delimited files (``.tsv``), and you can use a par
|
|
240
256
|
|
241
257
|
```ruby
|
242
258
|
# Load a tab-delimited csv
|
243
|
-
|
259
|
+
csv = Roo::CSV.new("mytsv.tsv", csv_options: {col_sep: "\t"})
|
244
260
|
|
245
261
|
# Load a csv with an explicit encoding
|
246
|
-
|
262
|
+
csv = Roo::CSV.new("mycsv.csv", csv_options: {encoding: Encoding::ISO_8859_1})
|
247
263
|
```
|
248
264
|
|
249
265
|
## Upgrading from Roo 1.13.x
|
@@ -272,9 +288,6 @@ You can run the tests/examples with Rspec like reporters by running
|
|
272
288
|
Roo also has a few tests that take a long time (5+ seconds). To run these, use
|
273
289
|
`LONG_RUN=true bundle exec rake`
|
274
290
|
|
275
|
-
When testing using Ruby 2.0 or 2.1, use this command:
|
276
|
-
`BUNDLE_GEMFILE=Gemfile_ruby2 bundle exec rake`
|
277
|
-
|
278
291
|
### Issues
|
279
292
|
|
280
293
|
If you find an issue, please create a gist and refer to it in an issue ([sample gist](https://gist.github.com/stevendaniels/98a05849036e99bb8b3c)). Here are some instructions for creating such a gist.
|
data/lib/roo.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'roo/version'
|
1
4
|
require 'roo/constants'
|
2
5
|
require 'roo/errors'
|
3
6
|
require 'roo/spreadsheet'
|
@@ -9,7 +12,7 @@ module Roo
|
|
9
12
|
autoload :Excelx, 'roo/excelx'
|
10
13
|
autoload :CSV, 'roo/csv'
|
11
14
|
|
12
|
-
TEMP_PREFIX = 'roo_'
|
15
|
+
TEMP_PREFIX = 'roo_'
|
13
16
|
|
14
17
|
CLASS_FOR_EXTENSION = {
|
15
18
|
ods: Roo::OpenOffice,
|
data/lib/roo/base.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
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
|
23
|
-
MIN_ROW_COL = 0
|
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
|
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 (
|
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
|
-
%
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
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 <<
|
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 <<
|
217
|
+
result << " - empty -"
|
216
218
|
end
|
217
219
|
result << "\n" if sheet != sheets.last
|
218
220
|
n += 1
|
@@ -286,12 +288,12 @@ class Roo::Base
|
|
286
288
|
clean_sheet_if_need(options)
|
287
289
|
search_or_set_header(options)
|
288
290
|
headers = @headers ||
|
289
|
-
|
290
|
-
[cell(@header_line, 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(
|
296
|
+
yield(headers.each_with_object({}) { |(k, v), hash| hash[k] = cell(line, v) })
|
295
297
|
end
|
296
298
|
end
|
297
299
|
end
|
@@ -306,18 +308,22 @@ class Roo::Base
|
|
306
308
|
|
307
309
|
def row_with(query, return_headers = false)
|
308
310
|
line_no = 0
|
311
|
+
closest_mismatched_headers = []
|
309
312
|
each do |row|
|
310
313
|
line_no += 1
|
311
314
|
headers = query.map { |q| row.grep(q)[0] }.compact
|
312
|
-
|
313
315
|
if headers.length == query.length
|
314
316
|
@header_line = line_no
|
315
317
|
return return_headers ? headers : line_no
|
316
|
-
|
317
|
-
|
318
|
+
else
|
319
|
+
closest_mismatched_headers = headers if headers.length > closest_mismatched_headers.length
|
320
|
+
if line_no > 100
|
321
|
+
break
|
322
|
+
end
|
318
323
|
end
|
319
324
|
end
|
320
|
-
|
325
|
+
missing_headers = query.select { |q| closest_mismatched_headers.grep(q).empty? }
|
326
|
+
raise Roo::HeaderRowNotFoundError, missing_headers
|
321
327
|
end
|
322
328
|
|
323
329
|
protected
|
@@ -330,7 +336,7 @@ class Roo::Base
|
|
330
336
|
filename = File.basename(filename, File.extname(filename))
|
331
337
|
end
|
332
338
|
|
333
|
-
if uri?(filename) && (qs_begin = filename.rindex(
|
339
|
+
if uri?(filename) && (qs_begin = filename.rindex("?"))
|
334
340
|
filename = filename[0..qs_begin - 1]
|
335
341
|
end
|
336
342
|
exts = Array(exts)
|
@@ -356,7 +362,7 @@ class Roo::Base
|
|
356
362
|
# Diese Methode ist eine temp. Loesung, um zu erforschen, ob der
|
357
363
|
# Zugriff mit numerischen Keys schneller ist.
|
358
364
|
def key_to_num(str)
|
359
|
-
r, c = str.split(
|
365
|
+
r, c = str.split(",")
|
360
366
|
[r.to_i, c.to_i]
|
361
367
|
end
|
362
368
|
|
@@ -418,9 +424,9 @@ class Roo::Base
|
|
418
424
|
|
419
425
|
def find_by_conditions(options)
|
420
426
|
rows = first_row.upto(last_row)
|
421
|
-
header_for =
|
422
|
-
[col
|
423
|
-
end
|
427
|
+
header_for = 1.upto(last_column).each_with_object({}) do |col, hash|
|
428
|
+
hash[col] = cell(@header_line, col)
|
429
|
+
end
|
424
430
|
|
425
431
|
# are all conditions met?
|
426
432
|
conditions = options[:conditions]
|
@@ -435,9 +441,9 @@ class Roo::Base
|
|
435
441
|
rows.map { |i| row(i) }
|
436
442
|
else
|
437
443
|
rows.map do |i|
|
438
|
-
|
439
|
-
[header_for.fetch(j)
|
440
|
-
end
|
444
|
+
1.upto(row(i).size).each_with_object({}) do |j, hash|
|
445
|
+
hash[header_for.fetch(j)] = cell(i, j)
|
446
|
+
end
|
441
447
|
end
|
442
448
|
end
|
443
449
|
end
|
@@ -455,7 +461,7 @@ class Roo::Base
|
|
455
461
|
|
456
462
|
def find_basename(filename)
|
457
463
|
if uri?(filename)
|
458
|
-
require
|
464
|
+
require "uri"
|
459
465
|
uri = URI.parse filename
|
460
466
|
File.basename(uri.path)
|
461
467
|
elsif !is_stream?(filename)
|
@@ -464,9 +470,9 @@ class Roo::Base
|
|
464
470
|
end
|
465
471
|
|
466
472
|
def make_tmpdir(prefix = nil, root = nil, &block)
|
467
|
-
warn
|
473
|
+
warn "[DEPRECATION] extend Roo::Tempdir and use its .make_tempdir instead"
|
468
474
|
prefix = "#{Roo::TEMP_PREFIX}#{prefix}"
|
469
|
-
root ||= ENV[
|
475
|
+
root ||= ENV["ROO_TMP"]
|
470
476
|
|
471
477
|
if block_given?
|
472
478
|
# folder is deleted at end of block
|
@@ -485,14 +491,17 @@ class Roo::Base
|
|
485
491
|
end
|
486
492
|
|
487
493
|
def sanitize_value(v)
|
488
|
-
v.gsub(/[[:cntrl:]]|^[\p{Space}]+|[\p{Space}]+$/,
|
494
|
+
v.gsub(/[[:cntrl:]]|^[\p{Space}]+|[\p{Space}]+$/, "")
|
489
495
|
end
|
490
496
|
|
491
497
|
def set_headers(hash = {})
|
492
498
|
# try to find header row with all values or give an error
|
493
499
|
# then create new hash by indexing strings and keeping integers for header array
|
494
|
-
|
495
|
-
@headers =
|
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
|
496
505
|
end
|
497
506
|
|
498
507
|
def header_index(query)
|
@@ -525,17 +534,17 @@ class Roo::Base
|
|
525
534
|
end
|
526
535
|
|
527
536
|
def uri?(filename)
|
528
|
-
filename.start_with?(
|
537
|
+
filename.start_with?("http://", "https://", "ftp://")
|
529
538
|
rescue
|
530
539
|
false
|
531
540
|
end
|
532
541
|
|
533
542
|
def download_uri(uri, tmpdir)
|
534
|
-
require
|
543
|
+
require "open-uri"
|
535
544
|
tempfilename = File.join(tmpdir, find_basename(uri))
|
536
545
|
begin
|
537
|
-
File.open(tempfilename,
|
538
|
-
open(uri,
|
546
|
+
File.open(tempfilename, "wb") do |file|
|
547
|
+
open(uri, "User-Agent" => "Ruby/#{RUBY_VERSION}") do |net|
|
539
548
|
file.write(net.read)
|
540
549
|
end
|
541
550
|
end
|
@@ -546,15 +555,15 @@ class Roo::Base
|
|
546
555
|
end
|
547
556
|
|
548
557
|
def open_from_stream(stream, tmpdir)
|
549
|
-
tempfilename = File.join(tmpdir,
|
550
|
-
File.open(tempfilename,
|
558
|
+
tempfilename = File.join(tmpdir, "spreadsheet")
|
559
|
+
File.open(tempfilename, "wb") do |file|
|
551
560
|
file.write(stream[7..-1])
|
552
561
|
end
|
553
|
-
File.join(tmpdir,
|
562
|
+
File.join(tmpdir, "spreadsheet")
|
554
563
|
end
|
555
564
|
|
556
565
|
def unzip(filename, tmpdir)
|
557
|
-
require
|
566
|
+
require "zip/filesystem"
|
558
567
|
|
559
568
|
Zip::File.open(filename) do |zip|
|
560
569
|
process_zipfile_packed(zip, tmpdir)
|
@@ -567,7 +576,7 @@ class Roo::Base
|
|
567
576
|
when nil
|
568
577
|
fail ArgumentError, "Error: sheet 'nil' not valid"
|
569
578
|
when Integer
|
570
|
-
sheets.fetch(sheet
|
579
|
+
sheets.fetch(sheet) do
|
571
580
|
fail RangeError, "sheet index #{sheet} not found"
|
572
581
|
end
|
573
582
|
when String
|
@@ -579,16 +588,16 @@ class Roo::Base
|
|
579
588
|
end
|
580
589
|
end
|
581
590
|
|
582
|
-
def process_zipfile_packed(zip, tmpdir, path =
|
591
|
+
def process_zipfile_packed(zip, tmpdir, path = "")
|
583
592
|
if zip.file.file? path
|
584
593
|
# extract and return filename
|
585
|
-
File.open(File.join(tmpdir, path),
|
594
|
+
File.open(File.join(tmpdir, path), "wb") do |file|
|
586
595
|
file.write(zip.read(path))
|
587
596
|
end
|
588
597
|
File.join(tmpdir, path)
|
589
598
|
else
|
590
599
|
ret = nil
|
591
|
-
path +=
|
600
|
+
path += "/" unless path.empty?
|
592
601
|
zip.dir.foreach(path) do |filename|
|
593
602
|
ret = process_zipfile_packed(zip, tmpdir, path + filename)
|
594
603
|
end
|