roo 2.0.1 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: db86b4586034783940303cfd17a7ee5c60277a51
4
- data.tar.gz: 2cf9222f2f2759f164164f745c72abe6b901df98
3
+ metadata.gz: 53d37d09a786f9536fd5bb00c7cacf9888c8b032
4
+ data.tar.gz: 284974b328b26668c58e3b952a12b8777439037f
5
5
  SHA512:
6
- metadata.gz: 442e05a8bffdc839c45a8684e8882f4327098387617b76027b6b5c0543c2a7b4be284c511c35f8333c9bc1147e31350d1f6f67174fc62e5b7fc45520ab0ad307
7
- data.tar.gz: 4ba67602e5772cae52f41ea0357ddd6835d25c35be57797307a6194a46ad977d7922b884b4fa826c91efd8533c6f3e56995e8cb0a13146a32e4a3ee7d5e42ae9
6
+ metadata.gz: 4b0e6d58d52600710fc5feb49e7483fc81e4bca65fa9013a3fa1004f5526107392b02f982ae981676544e301056a0246b4b85e38d3959565174c247ce49695a9
7
+ data.tar.gz: 5c3beb6580bece8aa730d1f29b6316889ebb7ce5a7003d8f654edcb1c9800a9f939021fd0d88749489ffdee6ebf496739664e5fede43aeb0067a2a16ad46f2f5
data/.travis.yml CHANGED
@@ -10,4 +10,5 @@ matrix:
10
10
  allow_failures:
11
11
  - rvm: ruby-head
12
12
  - rvm: jruby-19mode
13
+ - rvm: rbx-2
13
14
  bundler_args: --without local_development
data/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## [2.1.1] - 2015-08-02
2
+ ### Fixed invalid new lines with _x000D_ character[#231](https://github.com/roo-rb/roo/pull/231)
3
+ ### Fixed missing URI issue. [#245](https://github.com/roo-rb/roo/pull/245)
4
+
5
+ ## [2.1.0] - 2015-07-18
6
+ ### Added
7
+ - Added support for Excel 2007 `xlsm` files. [#232](https://github.com/roo-rb/roo/pull/232)
8
+ - Roo::Excelx returns an enumerator when calling each_row_streaming without a block. [#224](https://github.com/roo-rb/roo/pull/224)
9
+ - Returns an enumerator when calling `each` without a block. [#219](https://github.com/roo-rb/roo/pull/219)
10
+
11
+ ### Fixed
12
+ - Removed tabs and windows CRLF. [#235](https://github.com/roo-rb/roo/pull/235), [#234](https://github.com/roo-rb/roo/pull/234)
13
+ - Fixed Regexp to only check for valid URI's when opening a spreadsheet. [#229](https://github.com/roo-rb/roo/pull/228)
14
+ - Open streams in Roo:Excelx correctly. [#222](https://github.com/roo-rb/roo/pull/222)
15
+
1
16
  ## [2.0.1] - 2015-06-01
2
17
  ### Added
3
18
  - Return an enumerator when calling '#each' without a block [#219](https://github.com/roo-rb/roo/pull/219)
@@ -464,7 +479,7 @@
464
479
 
465
480
  ## [0.2.4] - 2007-06-16
466
481
  ### Fixed
467
- - ID 11605 Two cols with same value: crash roo (openoffice version only)
482
+ - ID 11605 Two cols with same value: crash roo (openoffice version only)
468
483
 
469
484
  ## [0.2.3] - 2007-06-02
470
485
  ### Changed / Added
data/README.md CHANGED
@@ -3,16 +3,13 @@
3
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
4
 
5
5
  Roo implements read access for all common spreadsheet types. It can handle:
6
-
7
- * Excelx
8
- * OpenOffice / LibreOffice
6
+ * Excel 2007 - 2013 formats (xlsx, xlsm)
7
+ * LibreOffice / OpenOffice.org formats (ods)
9
8
  * CSV
9
+ * Excel 97, Excel 2002 XML, and Excel 2003 XML formats when using the [roo-xls](https://github.com/roo-rb/roo-xls) gem (xls, xml)
10
+ * Google spreadsheets with read/write access when using [roo-google](https://github.com/roo-rb/roo-google)
10
11
 
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
12
+ ## Installation
16
13
 
17
14
  Install as a gem
18
15
 
@@ -21,7 +18,7 @@ Install as a gem
21
18
  Or add it to your Gemfile
22
19
 
23
20
  ```ruby
24
- gem 'roo', '~> 2.0.0'
21
+ gem 'roo', '~> 2.1.0'
25
22
  ```
26
23
  ## Usage
27
24
 
@@ -102,7 +99,7 @@ s.cell(1,'A',s.sheets[1])
102
99
  ```
103
100
 
104
101
  #### Querying a spreadsheet
105
- Use ``each`` with a ``block`` to iterate over each row.
102
+ Use ``each`` to iterate over each row.
106
103
 
107
104
  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.
108
105
 
@@ -143,7 +140,7 @@ sheet.to_xml
143
140
  sheet.to_yaml
144
141
  ```
145
142
 
146
- ### Excel (xlsx) Support
143
+ ### Excel (xlsx and xlsm) Support
147
144
 
148
145
  Stream rows from an Excelx spreadsheet.
149
146
 
data/lib/roo/base.rb CHANGED
@@ -91,7 +91,7 @@ class Roo::Base
91
91
  first_column = [first_column, key.last.to_i].min
92
92
  last_column = [last_column, key.last.to_i].max
93
93
  end if @cell[sheet]
94
- {first_row: first_row, first_column: first_column, last_row: last_row, last_column: last_column}
94
+ { first_row: first_row, first_column: first_column, last_row: last_row, last_column: last_column }
95
95
  end
96
96
 
97
97
  %w(first_row last_row first_column last_column).each do |key|
@@ -117,22 +117,23 @@ class Roo::Base
117
117
  result = "--- \n"
118
118
  from_row.upto(to_row) do |row|
119
119
  from_column.upto(to_column) do |col|
120
- unless empty?(row, col, sheet)
121
- result << "cell_#{row}_#{col}: \n"
122
- prefix.each do|k, v|
123
- result << " #{k}: #{v} \n"
124
- end
125
- result << " row: #{row} \n"
126
- result << " col: #{col} \n"
127
- result << " celltype: #{celltype(row, col, sheet)} \n"
128
- value = cell(row, col, sheet)
129
- if celltype(row, col, sheet) == :time
130
- value = integer_to_timestring(value)
131
- end
132
- result << " value: #{value} \n"
120
+ next if empty?(row, col, sheet)
121
+
122
+ result << "cell_#{row}_#{col}: \n"
123
+ prefix.each do|k, v|
124
+ result << " #{k}: #{v} \n"
125
+ end
126
+ result << " row: #{row} \n"
127
+ result << " col: #{col} \n"
128
+ result << " celltype: #{celltype(row, col, sheet)} \n"
129
+ value = cell(row, col, sheet)
130
+ if celltype(row, col, sheet) == :time
131
+ value = integer_to_timestring(value)
133
132
  end
133
+ result << " value: #{value} \n"
134
134
  end
135
135
  end
136
+
136
137
  result
137
138
  end
138
139
 
@@ -170,7 +171,7 @@ class Roo::Base
170
171
  end
171
172
 
172
173
  def inspect
173
- "<##{ self.class }:#{ self.object_id.to_s(8) } #{ self.instance_variables.join(' ') }>"
174
+ "<##{self.class}:#{object_id.to_s(8)} #{instance_variables.join(' ')}>"
174
175
  end
175
176
 
176
177
  # find a row either by row number or a condition
@@ -217,7 +218,7 @@ class Roo::Base
217
218
  row, col = normalize(row, col)
218
219
  cell_type = cell_type_by_value(value)
219
220
  set_value(row, col, value, sheet)
220
- set_type(row, col, cell_type , sheet)
221
+ set_type(row, col, cell_type, sheet)
221
222
  end
222
223
 
223
224
  def cell_type_by_value(value)
@@ -225,7 +226,7 @@ class Roo::Base
225
226
  when Fixnum then :float
226
227
  when String, Float then :string
227
228
  else
228
- raise ArgumentError, "Type for #{value} not set"
229
+ fail ArgumentError, "Type for #{value} not set"
229
230
  end
230
231
  end
231
232
 
@@ -256,13 +257,13 @@ class Roo::Base
256
257
  sheets.each do|sheet|
257
258
  self.default_sheet = sheet
258
259
  result << 'Sheet ' + n.to_s + ":\n"
259
- unless first_row
260
- result << ' - empty -'
261
- else
260
+ if first_row
262
261
  result << " First row: #{first_row}\n"
263
262
  result << " Last row: #{last_row}\n"
264
263
  result << " First column: #{::Roo::Utils.number_to_letter(first_column)}\n"
265
264
  result << " Last column: #{::Roo::Utils.number_to_letter(last_column)}"
265
+ else
266
+ result << ' - empty -'
266
267
  end
267
268
  result << "\n" if sheet != sheets.last
268
269
  n += 1
@@ -282,12 +283,12 @@ class Roo::Base
282
283
  # sonst gibt es Fehler bei leeren Blaettern
283
284
  first_row.upto(last_row) do |row|
284
285
  first_column.upto(last_column) do |col|
285
- unless empty?(row, col)
286
- x.cell(cell(row, col),
286
+ next if empty?(row, col)
287
+
288
+ x.cell(cell(row, col),
287
289
  row: row,
288
290
  column: col,
289
291
  type: celltype(row, col))
290
- end
291
292
  end
292
293
  end
293
294
  end
@@ -318,7 +319,7 @@ class Roo::Base
318
319
  # access different worksheets by calling spreadsheet.sheet(1)
319
320
  # or spreadsheet.sheet('SHEETNAME')
320
321
  def sheet(index, name = false)
321
- self.default_sheet = String === index ? index : sheets[index]
322
+ self.default_sheet = index.is_a?(::String) ? index : sheets[index]
322
323
  name ? [default_sheet, self] : self
323
324
  end
324
325
 
@@ -352,25 +353,23 @@ class Roo::Base
352
353
  # control characters and white spaces around columns
353
354
 
354
355
  def each(options = {})
355
- if block_given?
356
- if options.empty?
357
- 1.upto(last_row) do |line|
358
- yield row(line)
359
- end
360
- else
361
- clean_sheet_if_need(options)
362
- search_or_set_header(options)
363
- headers = @headers ||
364
- Hash[(first_column..last_column).map do |col|
365
- [cell(@header_line, col), col]
366
- end]
367
-
368
- @header_line.upto(last_row) do |line|
369
- yield(Hash[headers.map { |k, v| [k, cell(line, v)] }])
370
- end
356
+ return to_enum(:each, options) unless block_given?
357
+
358
+ if options.empty?
359
+ 1.upto(last_row) do |line|
360
+ yield row(line)
371
361
  end
372
362
  else
373
- to_enum(:each, options)
363
+ clean_sheet_if_need(options)
364
+ search_or_set_header(options)
365
+ headers = @headers ||
366
+ Hash[(first_column..last_column).map do |col|
367
+ [cell(@header_line, col), col]
368
+ end]
369
+
370
+ @header_line.upto(last_row) do |line|
371
+ yield(Hash[headers.map { |k, v| [k, cell(line, v)] }])
372
+ end
374
373
  end
375
374
  end
376
375
 
@@ -401,30 +400,32 @@ class Roo::Base
401
400
 
402
401
  protected
403
402
 
404
- def file_type_check(filename, ext, name, warning_level, packed = nil)
403
+ def file_type_check(filename, exts, name, warning_level, packed = nil)
405
404
  if packed == :zip
406
- # lalala.ods.zip => lalala.ods
407
- # hier wird KEIN unzip gemacht, sondern nur der Name der Datei
408
- # getestet, falls es eine gepackte Datei ist.
405
+ # spreadsheet.ods.zip => spreadsheet.ods
406
+ # Decompression is not performed here, only the 'zip' extension
407
+ # is removed from the file.
409
408
  filename = File.basename(filename, File.extname(filename))
410
409
  end
411
410
 
412
- if uri?(filename) && qs_begin = filename.rindex('?')
411
+ if uri?(filename) && (qs_begin = filename.rindex('?'))
413
412
  filename = filename[0..qs_begin - 1]
414
413
  end
415
- if File.extname(filename).downcase != ext
416
- case warning_level
417
- when :error
418
- warn file_type_warning_message(filename, ext)
419
- fail TypeError, "#{filename} is not #{name} file"
420
- when :warning
421
- warn "are you sure, this is #{name} spreadsheet file?"
422
- warn file_type_warning_message(filename, ext)
423
- when :ignore
424
- # ignore
425
- else
426
- fail "#{warning_level} illegal state of file_warning"
427
- end
414
+ exts = Array(exts)
415
+
416
+ return if exts.include?(File.extname(filename).downcase)
417
+
418
+ case warning_level
419
+ when :error
420
+ warn file_type_warning_message(filename, exts)
421
+ fail TypeError, "#{filename} is not #{name} file"
422
+ when :warning
423
+ warn "are you sure, this is #{name} spreadsheet file?"
424
+ warn file_type_warning_message(filename, exts)
425
+ when :ignore
426
+ # ignore
427
+ else
428
+ fail "#{warning_level} illegal state of file_warning"
428
429
  end
429
430
  end
430
431
 
@@ -475,16 +476,18 @@ class Roo::Base
475
476
  return if is_stream?(filename)
476
477
  filename = download_uri(filename, tmpdir) if uri?(filename)
477
478
  filename = unzip(filename, tmpdir) if packed == :zip
478
- unless File.file?(filename)
479
- fail IOError, "file #{filename} does not exist"
480
- end
479
+
480
+ fail IOError, "file #{filename} does not exist" unless File.file?(filename)
481
+
481
482
  filename
482
483
  end
483
484
 
484
- def file_type_warning_message(filename, ext)
485
- "use #{Roo::CLASS_FOR_EXTENSION.fetch(ext.sub('.', '').to_sym)}.new to handle #{ext} spreadsheet files. This has #{File.extname(filename).downcase}"
485
+ def file_type_warning_message(filename, exts)
486
+ *rest, last_ext = exts
487
+ ext_list = rest.any? ? "#{rest.join(', ')} or #{last_ext}" : last_ext
488
+ "use #{Roo::CLASS_FOR_EXTENSION.fetch(last_ext.sub('.', '').to_sym)}.new to handle #{ext_list} spreadsheet files. This has #{File.extname(filename).downcase}"
486
489
  rescue KeyError
487
- raise "unknown file type: #{ext}"
490
+ raise "unknown file types: #{ext_list}"
488
491
  end
489
492
 
490
493
  def find_by_row(row_index)
@@ -533,11 +536,8 @@ class Roo::Base
533
536
  end
534
537
 
535
538
  def make_tmpdir(prefix = nil, root = nil, &block)
536
- prefix = if prefix
537
- TEMP_PREFIX + prefix
538
- else
539
- TEMP_PREFIX
540
- end
539
+ prefix = "#{TEMP_PREFIX}#{prefix}"
540
+
541
541
  ::Dir.mktmpdir(prefix, root || ENV['ROO_TMP'], &block).tap do |result|
542
542
  block_given? || track_tmpdir!(result)
543
543
  end
@@ -585,9 +585,9 @@ class Roo::Base
585
585
  fail ArgumentError
586
586
  end
587
587
  end
588
- if col.is_a?(::String)
589
- col = ::Roo::Utils.letter_to_number(col)
590
- end
588
+
589
+ col = ::Roo::Utils.letter_to_number(col) if col.is_a?(::String)
590
+
591
591
  [row, col]
592
592
  end
593
593
 
@@ -638,7 +638,7 @@ class Roo::Base
638
638
  fail RangeError, "sheet index #{sheet} not found"
639
639
  end
640
640
  when String
641
- unless sheets.include? sheet
641
+ unless sheets.include?(sheet)
642
642
  fail RangeError, "sheet '#{sheet}' not found"
643
643
  end
644
644
  else
@@ -667,14 +667,14 @@ class Roo::Base
667
667
  # parameter is nil the output goes to STDOUT
668
668
  def write_csv_content(file = nil, sheet = nil, separator = ',')
669
669
  file ||= STDOUT
670
- if first_row(sheet) # sheet is not empty
671
- 1.upto(last_row(sheet)) do |row|
672
- 1.upto(last_column(sheet)) do |col|
673
- file.print(separator) if col > 1
674
- file.print cell_to_csv(row, col, sheet)
675
- end
676
- file.print("\n")
677
- end # sheet not empty
670
+ return unless first_row(sheet) # The sheet is empty
671
+
672
+ 1.upto(last_row(sheet)) do |row|
673
+ 1.upto(last_column(sheet)) do |col|
674
+ file.print(separator) if col > 1
675
+ file.print cell_to_csv(row, col, sheet)
676
+ end
677
+ file.print("\n")
678
678
  end
679
679
  end
680
680
 
@@ -726,9 +726,9 @@ class Roo::Base
726
726
  # converts an integer value to a time string like '02:05:06'
727
727
  def integer_to_timestring(content)
728
728
  h = (content / 3600.0).floor
729
- content = content - h * 3600
729
+ content -= h * 3600
730
730
  m = (content / 60.0).floor
731
- content = content - m * 60
731
+ content -= m * 60
732
732
  s = content
733
733
  sprintf('%02d:%02d:%02d', h, m, s)
734
734
  end
@@ -0,0 +1,5 @@
1
+ module Roo
2
+ ROO_EXCEL_NOTICE = "Excel support has been extracted to roo-xls due to its dependency on the GPL'd spreadsheet gem. Install roo-xls to use Roo::Excel.".freeze
3
+ ROO_EXCELML_NOTICE = "Excel SpreadsheetML support has been extracted to roo-xls. Install roo-xls to use Roo::Excel2003XML.".freeze
4
+ ROO_GOOGLE_NOTICE = "Google support has been extracted to roo-google. Install roo-google to use Roo::Google.".freeze
5
+ end
@@ -13,9 +13,19 @@ module Roo
13
13
 
14
14
  private
15
15
 
16
+ def fix_invalid_shared_strings(doc)
17
+ invalid = { '_x000D_' => "\n" }
18
+ xml = doc.to_s
19
+
20
+ if xml[/#{invalid.keys.join('|')}/]
21
+ @doc = ::Nokogiri::XML(xml.gsub(/#{invalid.keys.join('|')}/, invalid))
22
+ end
23
+ end
24
+
16
25
  def extract_shared_strings
17
26
  return [] unless doc_exists?
18
27
 
28
+ fix_invalid_shared_strings(doc)
19
29
  # read the shared strings xml document
20
30
  doc.xpath('/sst/si').map do |si|
21
31
  shared_string = ''
data/lib/roo/excelx.rb CHANGED
@@ -89,7 +89,7 @@ module Roo
89
89
  sheet_options[:expand_merged_ranges] = (options[:expand_merged_ranges] || false)
90
90
 
91
91
  unless is_stream?(filename_or_stream)
92
- file_type_check(filename_or_stream, '.xlsx', 'an Excel-xlsx', file_warning, packed)
92
+ file_type_check(filename_or_stream, %w[.xlsx .xlsm], 'an Excel 2007', file_warning, packed)
93
93
  basename = File.basename(filename_or_stream)
94
94
  end
95
95
 
@@ -321,7 +321,12 @@ module Roo
321
321
  # Yield an array of Excelx::Cell
322
322
  # Takes options for sheet, pad_cells, and max_rows
323
323
  def each_row_streaming(options = {})
324
- sheet_for(options.delete(:sheet)).each_row(options) { |row| yield row }
324
+ sheet = sheet_for(options.delete(:sheet))
325
+ if block_given?
326
+ sheet.each_row(options) { |row| yield row }
327
+ else
328
+ sheet.to_enum(:each_row, options)
329
+ end
325
330
  end
326
331
 
327
332
  private
@@ -409,19 +414,13 @@ module Roo
409
414
  @sheet_files = []
410
415
 
411
416
  unless is_stream?(zipfilename_or_stream)
412
- process_zipfile_entries Zip::File.open(zipfilename_or_stream).to_a.sort_by(&:name)
417
+ zip_file = Zip::File.open(zipfilename_or_stream)
413
418
  else
414
- stream = Zip::InputStream.open zipfilename_or_stream
415
- begin
416
- entries = []
417
- while (entry = stream.get_next_entry)
418
- entries << entry
419
- end
420
- process_zipfile_entries entries
421
- ensure
422
- stream.close
423
- end
419
+ zip_file = Zip::CentralDirectory.new
420
+ zip_file.read_from_stream zipfilename_or_stream
424
421
  end
422
+
423
+ process_zipfile_entries zip_file.to_a.sort_by(&:name)
425
424
  end
426
425
 
427
426
  def process_zipfile_entries(entries)
@@ -1,5 +1,4 @@
1
1
  require 'roo/open_office'
2
2
 
3
3
  # LibreOffice is just an alias for Roo::OpenOffice class
4
- class Roo::LibreOffice < Roo::OpenOffice
5
- end
4
+ Roo::LibreOffice = Roo::OpenOffice