culturecode-roo 2.0.1 → 2.0.2

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.
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