roo 2.7.1 → 2.8.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 +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
|
-
[](https://travis-ci.org/roo-rb/roo) [](https://travis-ci.org/roo-rb/roo) [](https://codeclimate.com/github/roo-rb/roo/maintainability) [](https://coveralls.io/r/roo-rb/roo) [](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
|