roo 2.7.1 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +5 -5
  2. data/.github/issue_template.md +16 -0
  3. data/.github/pull_request_template.md +14 -0
  4. data/.rubocop.yml +186 -0
  5. data/.travis.yml +12 -7
  6. data/CHANGELOG.md +31 -2
  7. data/LICENSE +2 -0
  8. data/README.md +25 -12
  9. data/lib/roo.rb +4 -1
  10. data/lib/roo/base.rb +65 -56
  11. data/lib/roo/constants.rb +5 -3
  12. data/lib/roo/csv.rb +20 -12
  13. data/lib/roo/excelx.rb +42 -16
  14. data/lib/roo/excelx/cell.rb +10 -6
  15. data/lib/roo/excelx/cell/base.rb +26 -12
  16. data/lib/roo/excelx/cell/boolean.rb +9 -6
  17. data/lib/roo/excelx/cell/date.rb +7 -7
  18. data/lib/roo/excelx/cell/datetime.rb +14 -18
  19. data/lib/roo/excelx/cell/empty.rb +3 -2
  20. data/lib/roo/excelx/cell/number.rb +35 -34
  21. data/lib/roo/excelx/cell/string.rb +3 -3
  22. data/lib/roo/excelx/cell/time.rb +4 -3
  23. data/lib/roo/excelx/comments.rb +3 -3
  24. data/lib/roo/excelx/coordinate.rb +11 -4
  25. data/lib/roo/excelx/extractor.rb +21 -3
  26. data/lib/roo/excelx/format.rb +38 -31
  27. data/lib/roo/excelx/images.rb +26 -0
  28. data/lib/roo/excelx/relationships.rb +3 -3
  29. data/lib/roo/excelx/shared.rb +10 -3
  30. data/lib/roo/excelx/shared_strings.rb +9 -15
  31. data/lib/roo/excelx/sheet.rb +49 -10
  32. data/lib/roo/excelx/sheet_doc.rb +86 -48
  33. data/lib/roo/excelx/styles.rb +3 -3
  34. data/lib/roo/excelx/workbook.rb +7 -3
  35. data/lib/roo/helpers/default_attr_reader.rb +20 -0
  36. data/lib/roo/helpers/weak_instance_cache.rb +41 -0
  37. data/lib/roo/open_office.rb +8 -6
  38. data/lib/roo/spreadsheet.rb +1 -1
  39. data/lib/roo/utils.rb +48 -19
  40. data/lib/roo/version.rb +1 -1
  41. data/roo.gemspec +13 -11
  42. data/spec/lib/roo/base_spec.rb +45 -3
  43. data/spec/lib/roo/excelx_spec.rb +125 -31
  44. data/spec/lib/roo/strict_spec.rb +43 -0
  45. data/spec/lib/roo/utils_spec.rb +12 -3
  46. data/spec/lib/roo/weak_instance_cache_spec.rb +92 -0
  47. data/spec/lib/roo_spec.rb +0 -0
  48. data/test/excelx/cell/test_attr_reader_default.rb +72 -0
  49. data/test/excelx/cell/test_base.rb +5 -0
  50. data/test/excelx/cell/test_datetime.rb +6 -6
  51. data/test/excelx/cell/test_empty.rb +11 -0
  52. data/test/excelx/cell/test_number.rb +9 -0
  53. data/test/excelx/cell/test_string.rb +20 -0
  54. data/test/excelx/cell/test_time.rb +4 -4
  55. data/test/excelx/test_coordinate.rb +51 -0
  56. data/test/formatters/test_csv.rb +17 -0
  57. data/test/formatters/test_xml.rb +4 -4
  58. data/test/roo/test_base.rb +2 -2
  59. data/test/roo/test_csv.rb +28 -0
  60. data/test/test_helper.rb +13 -0
  61. data/test/test_roo.rb +7 -7
  62. metadata +21 -11
  63. data/.github/ISSUE_TEMPLATE +0 -10
  64. data/Gemfile_ruby2 +0 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 711650fb2132509af1a6df5cd4b5a3d83eaac68f
4
- data.tar.gz: ca23282af485cfa63d4b4a761d4081841edd385c
2
+ SHA256:
3
+ metadata.gz: b99744522c3a62e004d42498388c4e99cdb2d45157fc26b87eb54023a957452f
4
+ data.tar.gz: ab4445f118a1144c71142a2c28724686da05d28ce04a04c0746c49628834d132
5
5
  SHA512:
6
- metadata.gz: 7166103bc551a008905e6052369ed5cf714bd949d83021e3596cef8ae083fccead0faac1b28591bf28b09f977202955c4f2e92fda45a5961475eff4ea3caa93e
7
- data.tar.gz: dc030d1d1530114289e8a420283fdf3131759ee107dfaa17bcc35c098e5d0e8b74fe984dc655b72a2701b211e3af3442f3cd79f0ad07e9cf1e7587496cfc7a16
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!
@@ -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
@@ -1,17 +1,22 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2.4
4
- - 2.3.1
5
- - 2.4.0
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.0.0
11
- gemfile: Gemfile_ruby2
12
- - rvm: 2.1.8
13
- gemfile: Gemfile_ruby2
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
@@ -1,4 +1,33 @@
1
- ## Unreleased
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
@@ -1,4 +1,6 @@
1
1
  Copyright (c) 2008-2014 Thomas Preymesser, Ben Woosley
2
+ Copyright (c) 2014-2017 Ben Woosley
3
+ Copyright (c) 2015-2017 Oleksandr Simonov, Steven Daniels
2
4
 
3
5
  MIT License
4
6
 
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) [![Code Climate](https://img.shields.io/codeclimate/github/roo-rb/roo.svg?style=flat-square)](https://codeclimate.com/github/roo-rb/roo) [![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)
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
- s.cell(1,1)
93
- s.cell('A',1)
94
- s.cell(1,'A')
95
- s.a1
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
- s.cell(1,'A',s.sheets[1])
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
- s = Roo::CSV.new("mycsv.csv")
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
- s = Roo::CSV.new("mytsv.tsv", csv_options: {col_sep: "\t"})
259
+ csv = Roo::CSV.new("mytsv.tsv", csv_options: {col_sep: "\t"})
244
260
 
245
261
  # Load a csv with an explicit encoding
246
- s = Roo::CSV.new("mycsv.csv", csv_options: {encoding: Encoding::ISO_8859_1})
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_'.freeze
15
+ TEMP_PREFIX = 'roo_'
13
16
 
14
17
  CLASS_FOR_EXTENSION = {
15
18
  ods: Roo::OpenOffice,
@@ -1,9 +1,7 @@
1
- # encoding: utf-8
2
-
3
- require 'tmpdir'
4
- require 'stringio'
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.freeze
23
- MIN_ROW_COL = 0.freeze
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 '[DEPRECATION] please access TEMP_PREFIX via Roo::TEMP_PREFIX'
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 (1 = first sheet) or the name of a sheet.
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
- %w(first_row last_row first_column last_column).each do |key|
114
- class_eval <<-EOS, __FILE__, __LINE__ + 1
115
- def #{key}(sheet = default_sheet) # def first_row(sheet = default_sheet)
116
- read_cells(sheet) # read_cells(sheet)
117
- @#{key}[sheet] ||= first_last_row_col_for_sheet(sheet)[:#{key}] # @first_row[sheet] ||= first_last_row_col_for_sheet(sheet)[:first_row]
118
- end # end
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 << 'Sheet ' + n.to_s + ":\n"
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 << ' - empty -'
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
- Hash[(first_column..last_column).map do |col|
290
- [cell(@header_line, col), 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(Hash[headers.map { |k, v| [k, cell(line, v)] }])
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
- elsif line_no > 100
317
- raise Roo::HeaderRowNotFoundError
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
- raise Roo::HeaderRowNotFoundError
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 = Hash[1.upto(last_column).map do |col|
422
- [col, cell(@header_line, 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
- Hash[1.upto(row(i).size).map do |j|
439
- [header_for.fetch(j), cell(i, 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 'uri'
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 '[DEPRECATION] extend Roo::Tempdir and use its .make_tempdir instead'
473
+ warn "[DEPRECATION] extend Roo::Tempdir and use its .make_tempdir instead"
468
474
  prefix = "#{Roo::TEMP_PREFIX}#{prefix}"
469
- root ||= ENV['ROO_TMP']
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
- @headers = row_with(hash.values, true)
495
- @headers = Hash[hash.keys.zip(@headers.map { |x| header_index(x) })]
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?('http://', 'https://', 'ftp://')
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 'open-uri'
543
+ require "open-uri"
535
544
  tempfilename = File.join(tmpdir, find_basename(uri))
536
545
  begin
537
- File.open(tempfilename, 'wb') do |file|
538
- open(uri, 'User-Agent' => "Ruby/#{RUBY_VERSION}") do |net|
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, 'spreadsheet')
550
- File.open(tempfilename, 'wb') do |file|
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, 'spreadsheet')
562
+ File.join(tmpdir, "spreadsheet")
554
563
  end
555
564
 
556
565
  def unzip(filename, tmpdir)
557
- require 'zip/filesystem'
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 - 1) do
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), 'wb') do |file|
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 += '/' unless path.empty?
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