culturecode-roo 2.0.1 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/.gitignore +1 -0
  2. data/CHANGELOG.md +513 -0
  3. data/README.md +206 -73
  4. data/lib/roo.rb +3 -3
  5. data/lib/roo/base.rb +49 -33
  6. data/lib/roo/csv.rb +10 -0
  7. data/lib/roo/excelx.rb +187 -60
  8. data/lib/roo/excelx/comments.rb +2 -1
  9. data/lib/roo/excelx/sheet_doc.rb +30 -3
  10. data/lib/roo/open_office.rb +250 -221
  11. data/lib/roo/utils.rb +28 -31
  12. data/lib/roo/version.rb +1 -1
  13. data/roo.gemspec +10 -12
  14. data/spec/lib/roo/csv_spec.rb +14 -0
  15. data/spec/lib/roo/excelx_spec.rb +90 -2
  16. data/spec/lib/roo/libreoffice_spec.rb +16 -0
  17. data/spec/lib/roo/openoffice_spec.rb +11 -0
  18. data/spec/lib/roo/utils_spec.rb +5 -4
  19. data/test/test_roo.rb +113 -2
  20. metadata +29 -180
  21. data/CHANGELOG +0 -438
  22. data/scripts/txt2html +0 -67
  23. data/test/files/1900_base.xlsx +0 -0
  24. data/test/files/1904_base.xlsx +0 -0
  25. data/test/files/Bibelbund.csv +0 -3741
  26. data/test/files/Bibelbund.ods +0 -0
  27. data/test/files/Bibelbund.xlsx +0 -0
  28. data/test/files/Bibelbund1.ods +0 -0
  29. data/test/files/Pfand_from_windows_phone.xlsx +0 -0
  30. data/test/files/advanced_header.ods +0 -0
  31. data/test/files/bbu.ods +0 -0
  32. data/test/files/bbu.xlsx +0 -0
  33. data/test/files/bode-v1.ods.zip +0 -0
  34. data/test/files/bode-v1.xls.zip +0 -0
  35. data/test/files/boolean.csv +0 -2
  36. data/test/files/boolean.ods +0 -0
  37. data/test/files/boolean.xlsx +0 -0
  38. data/test/files/borders.ods +0 -0
  39. data/test/files/borders.xlsx +0 -0
  40. data/test/files/bug-numbered-sheet-names.xlsx +0 -0
  41. data/test/files/comments.ods +0 -0
  42. data/test/files/comments.xlsx +0 -0
  43. data/test/files/csvtypes.csv +0 -1
  44. data/test/files/datetime.ods +0 -0
  45. data/test/files/datetime.xlsx +0 -0
  46. data/test/files/dreimalvier.ods +0 -0
  47. data/test/files/emptysheets.ods +0 -0
  48. data/test/files/emptysheets.xlsx +0 -0
  49. data/test/files/encrypted-letmein.ods +0 -0
  50. data/test/files/file_item_error.xlsx +0 -0
  51. data/test/files/formula.ods +0 -0
  52. data/test/files/formula.xlsx +0 -0
  53. data/test/files/formula_string_error.xlsx +0 -0
  54. data/test/files/html-escape.ods +0 -0
  55. data/test/files/link.csv +0 -1
  56. data/test/files/link.xlsx +0 -0
  57. data/test/files/matrix.ods +0 -0
  58. data/test/files/named_cells.ods +0 -0
  59. data/test/files/named_cells.xlsx +0 -0
  60. data/test/files/no_spreadsheet_file.txt +0 -1
  61. data/test/files/numbers-export.xlsx +0 -0
  62. data/test/files/numbers1.csv +0 -18
  63. data/test/files/numbers1.ods +0 -0
  64. data/test/files/numbers1.xlsx +0 -0
  65. data/test/files/numbers1withnull.xlsx +0 -0
  66. data/test/files/numeric-link.xlsx +0 -0
  67. data/test/files/only_one_sheet.ods +0 -0
  68. data/test/files/only_one_sheet.xlsx +0 -0
  69. data/test/files/paragraph.ods +0 -0
  70. data/test/files/paragraph.xlsx +0 -0
  71. data/test/files/ric.ods +0 -0
  72. data/test/files/sheet1.xml +0 -109
  73. data/test/files/simple_spreadsheet.ods +0 -0
  74. data/test/files/simple_spreadsheet.xlsx +0 -0
  75. data/test/files/simple_spreadsheet_from_italo.ods +0 -0
  76. data/test/files/so_datetime.csv +0 -8
  77. data/test/files/style.ods +0 -0
  78. data/test/files/style.xlsx +0 -0
  79. data/test/files/time-test.csv +0 -2
  80. data/test/files/time-test.ods +0 -0
  81. data/test/files/time-test.xlsx +0 -0
  82. data/test/files/type_excel.ods +0 -0
  83. data/test/files/type_excel.xlsx +0 -0
  84. data/test/files/type_excelx.ods +0 -0
  85. data/test/files/type_openoffice.xlsx +0 -0
  86. data/test/files/whitespace.ods +0 -0
  87. data/test/files/whitespace.xlsx +0 -0
data/README.md CHANGED
@@ -1,121 +1,254 @@
1
- # Roo [![Build Status](https://travis-ci.org/roo-rb/roo.svg)](https://travis-ci.org/roo-rb/roo)[![Code Climate](https://codeclimate.com/github/roo-rb/roo/badges/gpa.svg)](https://codeclimate.com/github/roo-rb/roo)[![Coverage Status](https://coveralls.io/repos/roo-rb/roo/badge.png)](https://coveralls.io/r/roo-rb/roo)
1
+ # Roo
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)
4
+
5
+ Roo implements read access for all common spreadsheet types. It can handle:
2
6
 
3
- Roo implements read access for all spreadsheet types and read/write access for
4
- Google spreadsheets. It can handle
5
7
  * Excelx
6
8
  * OpenOffice / LibreOffice
7
9
  * CSV
8
10
 
9
- ## Additional libraries
11
+ ## Additional Libraries
12
+
13
+ In addition, the [roo-xls](https://github.com/roo-rb/roo-xls) and [roo-google](https://github.com/roo-rb/roo-google) gems exist to extend Roo to support reading classic Excel formats (i.e. `.xls` and ``Excel2003XML``) and read/write access for Google spreadsheets.
14
+
15
+ # #Installation
16
+
17
+ Install as a gem
10
18
 
11
- In addition, the roo-xls and roo-google gems exist to Google Spreadsheet add classic Excel
12
- handling capabilities to roo.
19
+ $ gem install roo
13
20
 
14
- ## Usage:
21
+ Or add it to your Gemfile
22
+
23
+ ```ruby
24
+ gem 'roo', '~> 2.0.0'
25
+ ```
26
+ ## Usage
27
+
28
+ Opening a spreadsheet
15
29
 
16
30
  ```ruby
17
31
  require 'roo'
18
32
 
19
- s = Roo::OpenOffice.new("myspreadsheet.ods") # loads an OpenOffice Spreadsheet
20
- s = Roo::OpenOffice.new("myspreadsheet.ods", :password => "password") # loads an encrypted OpenOffice Spreadsheet
21
- s = Roo::Excelx.new("myspreadsheet.xlsx") # loads an Excel Spreadsheet for Excel .xlsx files
22
- s = Roo::CSV.new("mycsv.csv") # loads a CSV file
33
+ xlsx = Roo::Spreadsheet.open('./new_prices.xlsx')
34
+ xlsx = Roo::Excelx.new("./new_prices.xlsx")
23
35
 
24
- # You can use CSV to load TSV files, or files of a certain encoding by passing
25
- # in options under the :csv_options key
26
- s = Roo::CSV.new("mytsv.tsv", csv_options: {col_sep: "\t"}) # TSV
27
- s = Roo::CSV.new("mycsv.csv", csv_options: {encoding: Encoding::ISO_8859_1}) # csv with explicit encoding
36
+ # Use the extension option if the extension is ambiguous.
37
+ xlsx = Roo::Spreadsheet.open('./rails_temp_upload', extension: :xlsx)
28
38
 
29
- s.default_sheet = s.sheets.first # first sheet in the spreadsheet file will be used
39
+ xlsx.info
40
+ # => Returns basic info about the spreadsheet file
41
+ ```
30
42
 
31
- # s.sheets is an array which holds the names of the sheets within
32
- # a spreadsheet.
33
- # you can also write
34
- # s.default_sheet = s.sheets[3] or
35
- # s.default_sheet = 'Sheet 3'
43
+ ``Roo::Spreadsheet.open`` can accept both paths and ``File`` instances.
36
44
 
37
- s.cell(1,1) # returns the content of the first row/first cell in the sheet
38
- s.cell('A',1) # same cell
39
- s.cell(1,'A') # same cell
40
- s.cell(1,'A',s.sheets[0]) # same cell
45
+ ### Working with sheets
41
46
 
42
- # almost all methods have an optional argument 'sheet'.
43
- # If this parameter is omitted, the default_sheet will be used.
47
+ ```ruby
48
+ ods.sheets
49
+ # => ['Info', 'Sheet 2', 'Sheet 3'] # an Array of sheet names in the workbook
44
50
 
45
- s.info # Returns basic info about the spreadsheet file
51
+ ods.sheet('Info').row(1)
52
+ ods.sheet(0).row(1)
46
53
 
47
- s.first_row # the number of the first row
48
- s.last_row # the number of the last row
49
- s.first_column # the number of the first column
50
- s.last_column # the number of the last column
54
+ # Set the last sheet as the default sheet.
55
+ ods.default_sheet = ods.sheets.last
56
+ ods.default_sheet = s.sheets[3]
57
+ ods.default_sheet = 'Sheet 3'
51
58
 
52
- # limited font information is available
59
+ # Iterate through each sheet
60
+ ods.each_with_pagename do |name, sheet|
61
+ p sheet.row(1)
62
+ end
63
+ ```
53
64
 
54
- s.font(1,1).bold?
55
- s.font(1,1).italic?
56
- s.font(1,1).underline?
65
+ ### Accessing rows and columns
57
66
 
67
+ Roo uses Excel's numbering for rows, columns and cells, so `1` is the first index, not `0` as it is in an ``Array``
58
68
 
59
- # Roo::Spreadsheet requires spreadsheet gem
60
- require 'spreadsheet'
69
+ ```ruby
70
+ sheet.row(1)
71
+ # returns the first row of the spreadsheet.
61
72
 
62
- # Spreadsheet.open can accept both files and paths
73
+ sheet.column(1)
74
+ # returns the first column of the spreadsheet.
75
+ ```
63
76
 
64
- xls = Roo::Spreadsheet.open('./new_prices.xls')
77
+ Almost all methods have an optional argument `sheet`. If this parameter is omitted, the default_sheet will be used.
65
78
 
66
- # If the File.path or provided path string does not have an extension, you can optionally
67
- # provide one as a string or symbol
79
+ ```ruby
80
+ sheet.first_row(sheet.sheets[0])
81
+ # => 1 # the number of the first row
82
+ sheet.last_row
83
+ # => 42 # the number of the last row
84
+ sheet.first_column
85
+ # => 1 # the number of the first column
86
+ sheet.last_column
87
+ # => 10 # the number of the last column
88
+ ```
68
89
 
69
- xls = Roo::Spreadsheet.open('./rails_temp_upload', extension: :xls)
90
+ #### Accessing cells
70
91
 
71
- # no more setting xls.default_sheet, just use this
92
+ You can access the top-left cell in the following ways
72
93
 
73
- xls.sheet('Info').row(1)
74
- xls.sheet(0).row(1)
94
+ ```ruby
95
+ s.cell(1,1)
96
+ s.cell('A',1)
97
+ s.cell(1,'A')
98
+ s.a1
75
99
 
76
- # excel likes to create random "Data01" sheets for macros
77
- # use this to find the sheet with the most data to parse
100
+ # Access the second sheet's top-left cell.
101
+ s.cell(1,'A',s.sheets[1])
102
+ ```
78
103
 
79
- xls.longest_sheet
104
+ #### Querying a spreadsheet
105
+ Use ``each`` with a ``block`` to iterate over each row.
80
106
 
81
- # this excel file has multiple worksheets, let's iterate through each of them and process
107
+ If each is given a hash with the names of some columns, then each will generate a hash with the columns supplied for each row.
82
108
 
83
- xls.each_with_pagename do |name, sheet|
84
- p sheet.row(1)
109
+ ```ruby
110
+ sheet.each(id: 'ID', name: 'FULL_NAME') do |hash|
111
+ puts hash.inspect
112
+ # => { id: 1, name: 'John Smith' }
85
113
  end
114
+ ```
86
115
 
87
- # pull out a hash of exclusive column data (get rid of useless columns and save memory)
116
+ Use ``sheet.parse`` to return an array of rows. Column names can be a ``String`` or a ``Regexp``.
88
117
 
89
- xls.each(:id => 'UPC',:qty => 'ATS') {|hash| arr << hash}
90
- #=> hash will appear like {:upc=>727880013358, :qty => 12}
118
+ ```ruby
119
+ sheet.parse(:id => /UPC|SKU/,:qty => /ATS*\sATP\s*QTY\z/)
120
+ # => [{:upc => 727880013358, :qty => 12}, ...]
121
+ ```
91
122
 
92
- # NOTE: .parse does the same as .each, except it returns an array (similar to each vs. map)
123
+ Use the ``:header_search`` option to locate the header row and assign the header names.
93
124
 
94
- # not sure exactly what a column will be named? try a wildcard search with a regex
125
+ ```ruby
126
+ sheet.parse(header_search: [/UPC*SKU/,/ATS*\sATP\s*QTY\z/])
127
+ ```
95
128
 
96
- xls.parse(:id => /UPC|SKU/,:qty => /ATS*\sATP\s*QTY\z/)
129
+ Use the ``:clean`` option to strip out control characters and surrounding white space.
97
130
 
98
- # if you need to locate the header row and assign the header names themselves,
99
- # use the :header_search option
131
+ ```ruby
132
+ sheet.parse(:clean => true)
133
+ ```
100
134
 
101
- xls.parse(:header_search => [/UPC*SKU/,/ATS*\sATP\s*QTY\z/])
102
- #=> each element will appear in this fashion:
103
- #=> {"UPC" => 123456789012, "STYLE" => "987B0", "COLOR" => "blue", "QTY" => 78}
135
+ ### Exporting spreadsheets
136
+ Roo has the ability to export sheets using the following formats. It
137
+ will only export the ``default_sheet``.
104
138
 
105
- # want to strip out annoying unicode characters and surrounding white space?
139
+ ```ruby
140
+ sheet.to_csv
141
+ sheet.to_matrix
142
+ sheet.to_xml
143
+ sheet.to_yaml
144
+ ```
145
+
146
+ ### Excel (xlsx) Support
106
147
 
107
- xls.parse(:clean => true)
148
+ Stream rows from an Excelx spreadsheet.
108
149
 
109
- # another bonus feature is a patch to prevent the Spreadsheet gem from parsing
110
- # thousands and thousands of blank lines. i got fed up after watching my computer
111
- # nearly catch fire for 4 hours for a spreadsheet with only 200 ACTUAL lines
112
- # - located in lib/roo/worksheet.rb
150
+ ```ruby
151
+ xlsx = Roo::Excelx.new("./test_data/test_small.xlsx")
152
+ xlsx.each_row_streaming do |row|
153
+ puts row.inspect # Array of Excelx::Cell objects
154
+ end
155
+ ```
113
156
 
114
- # if you want to load and stream .xlsx rows
157
+ Iterate over each row
115
158
 
116
- s = Roo::Excelx.new("./test_data/test_small.xlsx")
117
- s.each_row_streaming do |row|
118
- puts row.inspect # Array of Excelx::Cell objects
159
+ ```ruby
160
+ xlsx.each_row do |row|
161
+ ...
119
162
  end
163
+ ```
164
+
165
+ ``Roo::Excelx`` also provides these helpful methods.
166
+
167
+ ```ruby
168
+ xlsx.excelx_type(3, 'C')
169
+ # => :numeric_or_formula
170
+
171
+ xlsx.cell(3, 'C')
172
+ # => 600000383.0
173
+
174
+ xlsx.excelx_value(row,col)
175
+ # => '0600000383'
176
+ ```
177
+
178
+ ``Roo::Excelx`` can access celltype, comments, font information, formulas, hyperlinks and labels.
179
+
180
+ ```ruby
181
+ xlsx.comment(1,1, ods.sheets[-1])
182
+ xlsx.font(1,1).bold?
183
+ xlsx.formula('A', 2)
184
+ ```
185
+
186
+ ### OpenOffice / LibreOffice Support
187
+
188
+ Roo::OpenOffice supports for encrypted OpenOffice spreadsheets.
120
189
 
190
+ ```ruby
191
+ # Load an encrypted OpenOffice Spreadsheet
192
+ ods = Roo::OpenOffice.new("myspreadsheet.ods", :password => "password")
193
+ ```
194
+
195
+ ``Roo::OpenOffice`` can access celltype, comments, font information, formulas and labels.
196
+
197
+ ```ruby
198
+ ods.celltype
199
+ # => :percentage
200
+
201
+ ods.comment(1,1, ods.sheets[-1])
202
+
203
+ ods.font(1,1).italic?
204
+ # => false
205
+
206
+ ods.formula('A', 2)
121
207
  ```
208
+
209
+ ### CSV Support
210
+
211
+ ```ruby
212
+ # Load a CSV file
213
+ s = Roo::CSV.new("mycsv.csv")
214
+ ```
215
+
216
+ Because Roo uses the [standard CSV library](), and you can use options available to that library to parse csv files. You can pass options using the ``csv_options`` key.
217
+
218
+ For instance, you can load tab-delimited files (``.tsv``), and you can use a particular encoding when opening the file.
219
+
220
+
221
+ ```ruby
222
+ # Load a tab-delimited csv
223
+ s = Roo::CSV.new("mytsv.tsv", csv_options: {col_sep: "\t"})
224
+
225
+ # Load a csv with an explicit encoding
226
+ s = Roo::CSV.new("mycsv.csv", csv_options: {encoding: Encoding::ISO_8859_1})
227
+ ```
228
+
229
+ ## Upgrading from Roo 1.13.x
230
+ If you use ``.xls`` or Google spreadsheets, you will need to install ``roo-xls`` or ``roo-google`` to continue using that functionality.
231
+
232
+ Roo's public methods have stayed relatively consistent between 1.13.x and 2.0.0, but please check the [Changelog](https://github.com/roo-rb/roo/blob/master/CHANGELOG.md) to better understand the changes made since 1.13.x.
233
+
234
+
235
+
236
+ ## Contributing
237
+ ### Features
238
+ 1. Fork it ( https://github.com/[my-github-username]/roo/fork )
239
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
240
+ 3. Commit your changes (`git commit -am 'My new feature'`)
241
+ 4. Push to the branch (`git push origin my-new-feature`)
242
+ 5. Create a new Pull Request
243
+
244
+ ### Issues
245
+
246
+ 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.
247
+
248
+ 1. [Create a gist](https://gist.github.com) with code that creates the error.
249
+ 2. Clone the gist repo locally, add a stripped down version of the offending spreadsheet to the gist repo, and push the gist's changes master.
250
+ 3. Paste the gist url here.
251
+
252
+
253
+ ## License
254
+ [Roo uses an MIT License](https://github.com/roo-rb/roo/blob/master/LICENSE)
data/lib/roo.rb CHANGED
@@ -1,7 +1,7 @@
1
- module Roo
2
- autoload :Spreadsheet, 'roo/spreadsheet'
3
- autoload :Base, 'roo/base'
1
+ require 'roo/spreadsheet'
2
+ require 'roo/base'
4
3
 
4
+ module Roo
5
5
  autoload :OpenOffice, 'roo/open_office'
6
6
  autoload :LibreOffice, 'roo/libre_office'
7
7
  autoload :Excelx, 'roo/excelx'
@@ -9,7 +9,7 @@ require 'roo/utils'
9
9
  class Roo::Base
10
10
  include Enumerable
11
11
 
12
- TEMP_PREFIX = 'roo_'
12
+ TEMP_PREFIX = 'roo_'.freeze
13
13
  MAX_ROW_COL = 999_999.freeze
14
14
  MIN_ROW_COL = 0.freeze
15
15
 
@@ -32,6 +32,15 @@ class Roo::Base
32
32
  @last_column = {}
33
33
 
34
34
  @header_line = 1
35
+ rescue => e # clean up any temp files, but only if an error was raised
36
+ close
37
+ raise e
38
+ end
39
+
40
+ def close
41
+ return nil unless @tmpdirs
42
+ @tmpdirs.each { |dir| ::FileUtils.remove_entry(dir) }
43
+ nil
35
44
  end
36
45
 
37
46
  def default_sheet
@@ -160,9 +169,8 @@ class Roo::Base
160
169
  end)
161
170
  end
162
171
 
163
- # call to_s method defined on subclasses
164
172
  def inspect
165
- to_s
173
+ "<##{ self.class }:#{ self.object_id.to_s(8) } #{ self.instance_variables.join(' ') }>"
166
174
  end
167
175
 
168
176
  # find a row either by row number or a condition
@@ -321,26 +329,6 @@ class Roo::Base
321
329
  end
322
330
  end
323
331
 
324
-
325
- def clean_sheet_if_need(options)
326
- return unless options[:clean]
327
- options.delete(:clean)
328
- @cleaned ||= {}
329
- clean_sheet(default_sheet) unless @cleaned[default_sheet]
330
- end
331
-
332
- def search_or_set_header(options)
333
- if options[:header_search]
334
- @headers = nil
335
- @header_line = row_with(options[:header_search])
336
- elsif [:first_row, true].include?(options[:headers])
337
- @headers = []
338
- row(first_row).each_with_index { |x, i| @headers << [x, i + 1] }
339
- else
340
- set_headers(options)
341
- end
342
- end
343
-
344
332
  # by passing in headers as options, this method returns
345
333
  # specific columns from your header assignment
346
334
  # for example:
@@ -361,11 +349,13 @@ class Roo::Base
361
349
  # * is the wildcard character
362
350
 
363
351
  # you can also pass in a :clean => true option to strip the sheet of
364
- # odd unicode characters and white spaces around columns
352
+ # control characters and white spaces around columns
365
353
 
366
354
  def each(options = {})
367
- if options.empty?
368
- 1.upto(last_row) do |line|
355
+ if first_row.nil?
356
+ # Sheet is empty
357
+ elsif options.empty?
358
+ first_row.upto(last_row) do |line|
369
359
  yield row(line)
370
360
  end
371
361
  else
@@ -450,9 +440,37 @@ class Roo::Base
450
440
  "#{arr[0]},#{arr[1]}"
451
441
  end
452
442
 
443
+ def is_stream?(filename_or_stream)
444
+ filename_or_stream.respond_to?(:seek)
445
+ end
446
+
453
447
  private
454
448
 
449
+ def track_tmpdir!(tmpdir)
450
+ (@tmpdirs ||= []) << tmpdir
451
+ end
452
+
453
+ def clean_sheet_if_need(options)
454
+ return unless options[:clean]
455
+ options.delete(:clean)
456
+ @cleaned ||= {}
457
+ clean_sheet(default_sheet) unless @cleaned[default_sheet]
458
+ end
459
+
460
+ def search_or_set_header(options)
461
+ if options[:header_search]
462
+ @headers = nil
463
+ @header_line = row_with(options[:header_search])
464
+ elsif [:first_row, true].include?(options[:headers])
465
+ @headers = []
466
+ row(first_row).each_with_index { |x, i| @headers << [x, i + 1] }
467
+ else
468
+ set_headers(options)
469
+ end
470
+ end
471
+
455
472
  def local_filename(filename, tmpdir, packed)
473
+ return if is_stream?(filename)
456
474
  filename = download_uri(filename, tmpdir) if uri?(filename)
457
475
  filename = unzip(filename, tmpdir) if packed == :zip
458
476
  unless File.file?(filename)
@@ -518,21 +536,21 @@ class Roo::Base
518
536
  else
519
537
  TEMP_PREFIX
520
538
  end
521
- Dir.mktmpdir(prefix, root || ENV['ROO_TMP'], &block)
539
+ ::Dir.mktmpdir(prefix, root || ENV['ROO_TMP'], &block).tap do |result|
540
+ block_given? || track_tmpdir!(result)
541
+ end
522
542
  end
523
543
 
524
544
  def clean_sheet(sheet)
525
545
  read_cells(sheet)
526
546
  @cell[sheet].each_pair do |coord, value|
527
- if value.is_a?(::String)
528
- @cell[sheet][coord] = sanitize_value(value)
529
- end
547
+ @cell[sheet][coord] = sanitize_value(value) if value.is_a?(::String)
530
548
  end
531
549
  @cleaned[sheet] = true
532
550
  end
533
551
 
534
552
  def sanitize_value(v)
535
- v.unpack('U*').select { |b| b < 127 }.pack('U*').strip
553
+ v.gsub(/[[:cntrl:]]|^[\p{Space}]+|[\p{Space}]+$/, '')
536
554
  end
537
555
 
538
556
  def set_headers(hash = {})
@@ -703,8 +721,6 @@ class Roo::Base
703
721
  end
704
722
  end
705
723
 
706
- private
707
-
708
724
  # converts an integer value to a time string like '02:05:06'
709
725
  def integer_to_timestring(content)
710
726
  h = (content / 3600.0).floor