roo 2.1.1 → 2.2.0

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: 53d37d09a786f9536fd5bb00c7cacf9888c8b032
4
- data.tar.gz: 284974b328b26668c58e3b952a12b8777439037f
3
+ metadata.gz: 0a61e2a1f190d2a30554f2bed45557c31573761e
4
+ data.tar.gz: 3bebd2370044ab8c20911368291932337201bb73
5
5
  SHA512:
6
- metadata.gz: 4b0e6d58d52600710fc5feb49e7483fc81e4bca65fa9013a3fa1004f5526107392b02f982ae981676544e301056a0246b4b85e38d3959565174c247ce49695a9
7
- data.tar.gz: 5c3beb6580bece8aa730d1f29b6316889ebb7ce5a7003d8f654edcb1c9800a9f939021fd0d88749489ffdee6ebf496739664e5fede43aeb0067a2a16ad46f2f5
6
+ metadata.gz: e57f90c7aad4b738399b3b3ff0e00b06cbe5fd9fd7ae68c8540f6086521d925db17a8219cacceb656012c3e2f10d814218d95a7ac2c188958c83c4c9425e5f4b
7
+ data.tar.gz: f1fe2348405fcd5097465d761ba60b321cfd52d45957eef61fecf8d6a43a7afd59afeb11e873318f6188dd0b117219b93b87e9ec3d99e9421a2f9defbb195cb2
data/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ## [2.2.0] - 2015-10-31
2
+ ### Added
3
+ - Added support for returning Integers values to Roo::OpenOffice [#258](https://github.com/roo-rb/roo/pull/258)
4
+ - A missing Header Raises `Roo::HeaderRowNotFoundError` [#247](https://github.com/roo-rb/roo/pull/247)
5
+ - Roo::Excelx::Shared class to pass shared data to Roo::Excelx sheets [#220](https://github.com/roo-rb/roo/pull/220)
6
+ - Proper Type support to Roo::Excelx [#240](https://github.com/roo-rb/roo/pull/240)
7
+ - Added Roo::HeaderRowNotFoundError [#247](https://github.com/roo-rb/roo/pull/247)
8
+
9
+ ### Changed
10
+ - Made spelling/grammar corrections in the README[260](https://github.com/roo-rb/roo/pull/260)
11
+ - Moved Roo::Excelx::Format module [#259](https://github.com/roo-rb/roo/pull/259)
12
+ - Updated README with details about `Roo::Excelx#each_with_streaming` method [#250](https://github.com/roo-rb/roo/pull/250)
13
+
14
+ ### Fixed
15
+ - Fixed Base64 not found issue in Open Office [#267](https://github.com/roo-rb/roo/pull/267)
16
+ - Fixed Regexp to allow method access to cells with multiple digits [#255](https://github.com/roo-rb/roo/pull/255), [#268](https://github.com/roo-rb/roo/pull/268)
17
+
1
18
  ## [2.1.1] - 2015-08-02
2
19
  ### Fixed invalid new lines with _x000D_ character[#231](https://github.com/roo-rb/roo/pull/231)
3
20
  ### Fixed missing URI issue. [#245](https://github.com/roo-rb/roo/pull/245)
data/Guardfile CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  guard :minitest, test_folders: ['test'] do
5
5
  watch(%r{^test/(.*)\/?test_(.*)\.rb$})
6
- watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
6
+ watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1].to_s.sub('roo/', '')}test_#{m[2]}.rb" }
7
7
  watch(%r{^test/test_helper\.rb$}) { 'test' }
8
8
  end
9
9
 
@@ -21,4 +21,3 @@ guard :rspec, cmd: 'bundle exec rspec' do
21
21
  watch('spec/spec_helper.rb') { "spec" }
22
22
  watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
23
23
  end
24
-
data/README.md CHANGED
@@ -151,6 +151,26 @@ xlsx.each_row_streaming do |row|
151
151
  end
152
152
  ```
153
153
 
154
+ By default blank cells will be excluded from the array. To keep them, use the option pad_cells = true. (They will be set to nil in the array)
155
+ ```ruby
156
+ xlsx.each_row_streaming(pad_cells: true) do |row|
157
+ puts row.inspect # Array of Excelx::Cell objects
158
+ end
159
+ ```
160
+
161
+ To stream only some of the rows, you can use the ```max_rows``` and ```offset```options.
162
+ ```ruby
163
+ xlsx.each_row_streaming(offset: 1) do |row| # Will exclude first (inevitably header) row
164
+ puts row.inspect # Array of Excelx::Cell objects
165
+ end
166
+ ```
167
+
168
+ ```ruby
169
+ xlsx.each_row_streaming(max_rows: 3) do |row| # Will yield 4 rows (it's automatically incremented by 1) after the supplied offset.
170
+ puts row.inspect # Array of Excelx::Cell objects
171
+ end
172
+ ```
173
+
154
174
  Iterate over each row
155
175
 
156
176
  ```ruby
@@ -182,7 +202,7 @@ xlsx.formula('A', 2)
182
202
 
183
203
  ### OpenOffice / LibreOffice Support
184
204
 
185
- Roo::OpenOffice supports for encrypted OpenOffice spreadsheets.
205
+ Roo::OpenOffice has support for encrypted OpenOffice spreadsheets.
186
206
 
187
207
  ```ruby
188
208
  # Load an encrypted OpenOffice Spreadsheet
@@ -210,7 +230,7 @@ ods.formula('A', 2)
210
230
  s = Roo::CSV.new("mycsv.csv")
211
231
  ```
212
232
 
213
- 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.
233
+ 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.
214
234
 
215
235
  For instance, you can load tab-delimited files (``.tsv``), and you can use a particular encoding when opening the file.
216
236
 
data/lib/roo.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'roo/constants'
2
+ require 'roo/errors'
2
3
  require 'roo/spreadsheet'
3
4
  require 'roo/base'
4
5
 
data/lib/roo/base.rb CHANGED
@@ -303,7 +303,7 @@ class Roo::Base
303
303
  def method_missing(m, *args)
304
304
  # #aa42 => #cell('aa',42)
305
305
  # #aa42('Sheet1') => #cell('aa',42,'Sheet1')
306
- if m =~ /^([a-z]+)(\d)$/
306
+ if m =~ /^([a-z]+)(\d+)$/
307
307
  col = ::Roo::Utils.letter_to_number(Regexp.last_match[1])
308
308
  row = Regexp.last_match[2].to_i
309
309
  if args.empty?
@@ -392,10 +392,10 @@ class Roo::Base
392
392
  @header_line = line_no
393
393
  return return_headers ? headers : line_no
394
394
  elsif line_no > 100
395
- fail "Couldn't find header row."
395
+ raise Roo::HeaderRowNotFoundError
396
396
  end
397
397
  end
398
- fail "Couldn't find header row."
398
+ raise Roo::HeaderRowNotFoundError
399
399
  end
400
400
 
401
401
  protected
@@ -680,47 +680,47 @@ class Roo::Base
680
680
 
681
681
  # The content of a cell in the csv output
682
682
  def cell_to_csv(row, col, sheet)
683
- if empty?(row, col, sheet)
684
- ''
685
- else
686
- onecell = cell(row, col, sheet)
687
-
688
- case celltype(row, col, sheet)
689
- when :string
683
+ return '' if empty?(row, col, sheet)
684
+
685
+ onecell = cell(row, col, sheet)
686
+
687
+ case celltype(row, col, sheet)
688
+ when :string
689
+ %("#{onecell.gsub('"', '""')}") unless onecell.empty?
690
+ when :boolean
691
+ # TODO: this only works for excelx
692
+ onecell = self.sheet_for(sheet).cells[[row, col]].formatted_value
693
+ %("#{onecell.gsub('"', '""').downcase}")
694
+ when :float, :percentage
695
+ if onecell == onecell.to_i
696
+ onecell.to_i.to_s
697
+ else
698
+ onecell.to_s
699
+ end
700
+ when :formula
701
+ case onecell
702
+ when String
690
703
  %("#{onecell.gsub('"', '""')}") unless onecell.empty?
691
- when :boolean
692
- %("#{onecell.gsub('"', '""').downcase}")
693
- when :float, :percentage
704
+ when Float
694
705
  if onecell == onecell.to_i
695
706
  onecell.to_i.to_s
696
707
  else
697
708
  onecell.to_s
698
709
  end
699
- when :formula
700
- case onecell
701
- when String
702
- %("#{onecell.gsub('"', '""')}") unless onecell.empty?
703
- when Float
704
- if onecell == onecell.to_i
705
- onecell.to_i.to_s
706
- else
707
- onecell.to_s
708
- end
709
- when DateTime
710
- onecell.to_s
711
- else
712
- fail "unhandled onecell-class #{onecell.class}"
713
- end
714
- when :date, :datetime
710
+ when DateTime
715
711
  onecell.to_s
716
- when :time
717
- integer_to_timestring(onecell)
718
- when :link
719
- %("#{onecell.url.gsub('"', '""')}")
720
712
  else
721
- fail "unhandled celltype #{celltype(row, col, sheet)}"
722
- end || ''
723
- end
713
+ fail "unhandled onecell-class #{onecell.class}"
714
+ end
715
+ when :date, :datetime
716
+ onecell.to_s
717
+ when :time
718
+ integer_to_timestring(onecell)
719
+ when :link
720
+ %("#{onecell.url.gsub('"', '""')}")
721
+ else
722
+ fail "unhandled celltype #{celltype(row, col, sheet)}"
723
+ end || ''
724
724
  end
725
725
 
726
726
  # converts an integer value to a time string like '02:05:06'
data/lib/roo/errors.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Roo
2
+ # A base error class for Roo. Most errors thrown by Roo should inherit from
3
+ # this class.
4
+ class Error < StandardError; end
5
+
6
+ # Raised when Roo cannot find a header row that matches the given column
7
+ # name(s).
8
+ class HeaderRowNotFoundError < Error; end
9
+ end
data/lib/roo/excelx.rb CHANGED
@@ -2,9 +2,13 @@ require 'nokogiri'
2
2
  require 'zip/filesystem'
3
3
  require 'roo/link'
4
4
  require 'roo/utils'
5
+ require 'forwardable'
5
6
 
6
7
  module Roo
7
8
  class Excelx < Roo::Base
9
+ extend Forwardable
10
+
11
+ require 'roo/excelx/shared'
8
12
  require 'roo/excelx/workbook'
9
13
  require 'roo/excelx/shared_strings'
10
14
  require 'roo/excelx/styles'
@@ -13,68 +17,10 @@ module Roo
13
17
  require 'roo/excelx/relationships'
14
18
  require 'roo/excelx/comments'
15
19
  require 'roo/excelx/sheet_doc'
16
-
17
- module Format
18
- EXCEPTIONAL_FORMATS = {
19
- 'h:mm am/pm' => :date,
20
- 'h:mm:ss am/pm' => :date
21
- }
22
-
23
- STANDARD_FORMATS = {
24
- 0 => 'General'.freeze,
25
- 1 => '0'.freeze,
26
- 2 => '0.00'.freeze,
27
- 3 => '#,##0'.freeze,
28
- 4 => '#,##0.00'.freeze,
29
- 9 => '0%'.freeze,
30
- 10 => '0.00%'.freeze,
31
- 11 => '0.00E+00'.freeze,
32
- 12 => '# ?/?'.freeze,
33
- 13 => '# ??/??'.freeze,
34
- 14 => 'mm-dd-yy'.freeze,
35
- 15 => 'd-mmm-yy'.freeze,
36
- 16 => 'd-mmm'.freeze,
37
- 17 => 'mmm-yy'.freeze,
38
- 18 => 'h:mm AM/PM'.freeze,
39
- 19 => 'h:mm:ss AM/PM'.freeze,
40
- 20 => 'h:mm'.freeze,
41
- 21 => 'h:mm:ss'.freeze,
42
- 22 => 'm/d/yy h:mm'.freeze,
43
- 37 => '#,##0 ;(#,##0)'.freeze,
44
- 38 => '#,##0 ;[Red](#,##0)'.freeze,
45
- 39 => '#,##0.00;(#,##0.00)'.freeze,
46
- 40 => '#,##0.00;[Red](#,##0.00)'.freeze,
47
- 45 => 'mm:ss'.freeze,
48
- 46 => '[h]:mm:ss'.freeze,
49
- 47 => 'mmss.0'.freeze,
50
- 48 => '##0.0E+0'.freeze,
51
- 49 => '@'.freeze
52
- }
53
-
54
- def to_type(format)
55
- format = format.to_s.downcase
56
- if (type = EXCEPTIONAL_FORMATS[format])
57
- type
58
- elsif format.include?('#')
59
- :float
60
- elsif !format.match(/d+(?![\]])/).nil? || format.include?('y')
61
- if format.include?('h') || format.include?('s')
62
- :datetime
63
- else
64
- :date
65
- end
66
- elsif format.include?('h') || format.include?('s')
67
- :time
68
- elsif format.include?('%')
69
- :percentage
70
- else
71
- :float
72
- end
73
- end
74
-
75
- module_function :to_type
76
- end
20
+ require 'roo/excelx/coordinate'
21
+ require 'roo/excelx/format'
77
22
 
23
+ delegate [:styles, :workbook, :shared_strings, :rels_files, :sheet_files, :comments_files] => :@shared
78
24
  ExceedsMaxError = Class.new(StandardError)
79
25
 
80
26
  # initialization and opening of a spreadsheet file
@@ -94,9 +40,8 @@ module Roo
94
40
  end
95
41
 
96
42
  @tmpdir = make_tmpdir(basename, options[:tmpdir_root])
43
+ @shared = Shared.new(@tmpdir)
97
44
  @filename = local_filename(filename_or_stream, @tmpdir, packed)
98
- @comments_files = []
99
- @rels_files = []
100
45
  process_zipfile(@filename || filename_or_stream)
101
46
 
102
47
  @sheet_names = workbook.sheets.map do |sheet|
@@ -106,7 +51,7 @@ module Roo
106
51
  end.compact
107
52
  @sheets = []
108
53
  @sheets_by_name = Hash[@sheet_names.map.with_index do |sheet_name, n|
109
- @sheets[n] = Sheet.new(sheet_name, @rels_files[n], @sheet_files[n], @comments_files[n], styles, shared_strings, workbook, sheet_options)
54
+ @sheets[n] = Sheet.new(sheet_name, @shared, n, sheet_options)
110
55
  [sheet_name, @sheets[n]]
111
56
  end]
112
57
 
@@ -187,7 +132,7 @@ module Roo
187
132
  def set(row, col, value, sheet = nil) #:nodoc:
188
133
  key = normalize(row, col)
189
134
  cell_type = cell_type_by_value(value)
190
- sheet_for(sheet).cells[key] = Cell.new(value, cell_type, nil, cell_type, value, nil, nil, nil, Cell::Coordinate.new(row, col))
135
+ sheet_for(sheet).cells[key] = Cell.new(value, cell_type, nil, cell_type, value, nil, nil, nil, Coordinate.new(row, col))
191
136
  end
192
137
 
193
138
  # Returns the formula at (row,col).
@@ -239,14 +184,14 @@ module Roo
239
184
  # Note: this is only available within the Excelx class
240
185
  def excelx_type(row, col, sheet = nil)
241
186
  key = normalize(row, col)
242
- safe_send(sheet_for(sheet).cells[key], :excelx_type)
187
+ safe_send(sheet_for(sheet).cells[key], :cell_type)
243
188
  end
244
189
 
245
190
  # returns the internal value of an excelx cell
246
191
  # Note: this is only available within the Excelx class
247
192
  def excelx_value(row, col, sheet = nil)
248
193
  key = normalize(row, col)
249
- safe_send(sheet_for(sheet).cells[key], :excelx_value)
194
+ safe_send(sheet_for(sheet).cells[key], :cell_value)
250
195
  end
251
196
 
252
197
  # returns the internal format of an excel cell
@@ -259,8 +204,8 @@ module Roo
259
204
  sheet = sheet_for(sheet)
260
205
  key = normalize(row, col)
261
206
  cell = sheet.cells[key]
262
- !cell || !cell.value || (cell.type == :string && cell.value.empty?) \
263
- || (row < sheet.first_row || row > sheet.last_row || col < sheet.first_column || col > sheet.last_column)
207
+ !cell || cell.empty? || (cell.type == :string && cell.value.empty?) ||
208
+ (row < sheet.first_row || row > sheet.last_row || col < sheet.first_column || col > sheet.last_column)
264
209
  end
265
210
 
266
211
  # shows the internal representation of all cells
@@ -404,6 +349,7 @@ module Roo
404
349
  name = sheets[id]
405
350
  entry = entries.find { |e| e.name =~ /#{name}$/ }
406
351
  path = "#{tmpdir}/roo_sheet#{i + 1}"
352
+ sheet_files << path
407
353
  @sheet_files << path
408
354
  entry.extract(path)
409
355
  end
@@ -457,19 +403,21 @@ module Roo
457
403
  # sheet's comment file is in the sheet1.xml.rels file. SEE
458
404
  # ECMA-376 12.3.3 in "Ecma Office Open XML Part 1".
459
405
  nr = Regexp.last_match[1].to_i
460
- @comments_files[nr - 1] = "#{@tmpdir}/roo_comments#{nr}"
406
+ comments_files[nr - 1] = "#{@tmpdir}/roo_comments#{nr}"
461
407
  when /sheet([0-9]+).xml.rels$/
462
408
  # FIXME: Roo seems to use sheet[\d].xml.rels for hyperlinks only, but
463
409
  # it also stores the location for sharedStrings, comments,
464
410
  # drawings, etc.
465
411
  nr = Regexp.last_match[1].to_i
466
- @rels_files[nr - 1] = "#{@tmpdir}/roo_rels#{nr}"
412
+ rels_files[nr - 1] = "#{@tmpdir}/roo_rels#{nr}"
467
413
  end
468
414
 
469
415
  entry.extract(path) if path
470
416
  end
471
417
  end
472
418
 
419
+ # NOTE: To reduce memory, styles, shared_strings, workbook can be class
420
+ # variables in a Shared module.
473
421
  def styles
474
422
  @styles ||= Styles.new(File.join(@tmpdir, 'roo_styles.xml'))
475
423
  end
@@ -1,4 +1,12 @@
1
1
  require 'date'
2
+ require 'roo/excelx/cell/base'
3
+ require 'roo/excelx/cell/boolean'
4
+ require 'roo/excelx/cell/datetime'
5
+ require 'roo/excelx/cell/date'
6
+ require 'roo/excelx/cell/empty'
7
+ require 'roo/excelx/cell/number'
8
+ require 'roo/excelx/cell/string'
9
+ require 'roo/excelx/cell/time'
2
10
 
3
11
  module Roo
4
12
  class Excelx
@@ -6,7 +14,9 @@ module Roo
6
14
  attr_reader :type, :formula, :value, :excelx_type, :excelx_value, :style, :hyperlink, :coordinate
7
15
  attr_writer :value
8
16
 
17
+ # DEPRECATED: Please use Cell.create_cell instead.
9
18
  def initialize(value, type, formula, excelx_type, excelx_value, style, hyperlink, base_date, coordinate)
19
+ warn '[DEPRECATION] `Cell.new` is deprecated. Please use `Cell.create_cell` instead.'
10
20
  @type = type
11
21
  @formula = formula
12
22
  @base_date = base_date if [:date, :datetime].include?(@type)
@@ -29,10 +39,29 @@ module Roo
29
39
  end
30
40
  end
31
41
 
42
+ def self.create_cell(type, *values)
43
+ case type
44
+ when :string
45
+ Cell::String.new(*values)
46
+ when :boolean
47
+ Cell::Boolean.new(*values)
48
+ when :number
49
+ Cell::Number.new(*values)
50
+ when :date
51
+ Cell::Date.new(*values)
52
+ when :datetime
53
+ Cell::DateTime.new(*values)
54
+ when :time
55
+ Cell::Time.new(*values)
56
+ end
57
+ end
58
+
59
+ # Deprecated: use Roo::Excelx::Coordinate instead.
32
60
  class Coordinate
33
61
  attr_accessor :row, :column
34
62
 
35
63
  def initialize(row, column)
64
+ warn '[DEPRECATION] `Roo::Excel::Cell::Coordinate` is deprecated. Please use `Roo::Excelx::Coordinate` instead.'
36
65
  @row, @column = row, column
37
66
  end
38
67
  end
@@ -57,20 +86,20 @@ module Roo
57
86
  def create_date(date)
58
87
  yyyy, mm, dd = date.strftime('%Y-%m-%d').split('-')
59
88
 
60
- Date.new(yyyy.to_i, mm.to_i, dd.to_i)
89
+ ::Date.new(yyyy.to_i, mm.to_i, dd.to_i)
61
90
  end
62
91
 
63
92
  def create_datetime(date)
64
93
  datetime_string = date.strftime('%Y-%m-%d %H:%M:%S.%N')
65
94
  t = round_datetime(datetime_string)
66
95
 
67
- DateTime.civil(t.year, t.month, t.day, t.hour, t.min, t.sec)
96
+ ::DateTime.civil(t.year, t.month, t.day, t.hour, t.min, t.sec)
68
97
  end
69
98
 
70
99
  def round_datetime(datetime_string)
71
100
  /(?<yyyy>\d+)-(?<mm>\d+)-(?<dd>\d+) (?<hh>\d+):(?<mi>\d+):(?<ss>\d+.\d+)/ =~ datetime_string
72
101
 
73
- Time.new(yyyy.to_i, mm.to_i, dd.to_i, hh.to_i, mi.to_i, ss.to_r).round(0)
102
+ ::Time.new(yyyy.to_i, mm.to_i, dd.to_i, hh.to_i, mi.to_i, ss.to_r).round(0)
74
103
  end
75
104
  end
76
105
  end