roo 1.13.2 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (216) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -0
  3. data/.github/ISSUE_TEMPLATE +10 -0
  4. data/.gitignore +11 -0
  5. data/.simplecov +4 -0
  6. data/.travis.yml +17 -0
  7. data/CHANGELOG.md +626 -0
  8. data/Gemfile +17 -12
  9. data/Gemfile_ruby2 +30 -0
  10. data/Guardfile +23 -0
  11. data/LICENSE +3 -1
  12. data/README.md +285 -0
  13. data/Rakefile +23 -23
  14. data/examples/roo_soap_client.rb +28 -31
  15. data/examples/roo_soap_server.rb +4 -6
  16. data/examples/write_me.rb +9 -10
  17. data/lib/roo/base.rb +298 -495
  18. data/lib/roo/constants.rb +5 -0
  19. data/lib/roo/csv.rb +127 -113
  20. data/lib/roo/errors.rb +11 -0
  21. data/lib/roo/excelx/cell/base.rb +94 -0
  22. data/lib/roo/excelx/cell/boolean.rb +27 -0
  23. data/lib/roo/excelx/cell/date.rb +28 -0
  24. data/lib/roo/excelx/cell/datetime.rb +111 -0
  25. data/lib/roo/excelx/cell/empty.rb +19 -0
  26. data/lib/roo/excelx/cell/number.rb +87 -0
  27. data/lib/roo/excelx/cell/string.rb +19 -0
  28. data/lib/roo/excelx/cell/time.rb +43 -0
  29. data/lib/roo/excelx/cell.rb +106 -0
  30. data/lib/roo/excelx/comments.rb +55 -0
  31. data/lib/roo/excelx/coordinate.rb +12 -0
  32. data/lib/roo/excelx/extractor.rb +21 -0
  33. data/lib/roo/excelx/format.rb +64 -0
  34. data/lib/roo/excelx/relationships.rb +25 -0
  35. data/lib/roo/excelx/shared.rb +32 -0
  36. data/lib/roo/excelx/shared_strings.rb +157 -0
  37. data/lib/roo/excelx/sheet.rb +112 -0
  38. data/lib/roo/excelx/sheet_doc.rb +211 -0
  39. data/lib/roo/excelx/styles.rb +64 -0
  40. data/lib/roo/excelx/workbook.rb +59 -0
  41. data/lib/roo/excelx.rb +376 -602
  42. data/lib/roo/font.rb +17 -0
  43. data/lib/roo/formatters/base.rb +15 -0
  44. data/lib/roo/formatters/csv.rb +84 -0
  45. data/lib/roo/formatters/matrix.rb +23 -0
  46. data/lib/roo/formatters/xml.rb +31 -0
  47. data/lib/roo/formatters/yaml.rb +40 -0
  48. data/lib/roo/libre_office.rb +4 -0
  49. data/lib/roo/link.rb +34 -0
  50. data/lib/roo/open_office.rb +626 -0
  51. data/lib/roo/spreadsheet.rb +22 -23
  52. data/lib/roo/tempdir.rb +21 -0
  53. data/lib/roo/utils.rb +78 -0
  54. data/lib/roo/version.rb +3 -0
  55. data/lib/roo.rb +23 -24
  56. data/roo.gemspec +21 -204
  57. data/spec/helpers.rb +5 -0
  58. data/spec/lib/roo/base_spec.rb +229 -3
  59. data/spec/lib/roo/csv_spec.rb +38 -11
  60. data/spec/lib/roo/excelx/format_spec.rb +7 -6
  61. data/spec/lib/roo/excelx_spec.rb +510 -11
  62. data/spec/lib/roo/libreoffice_spec.rb +16 -6
  63. data/spec/lib/roo/openoffice_spec.rb +30 -8
  64. data/spec/lib/roo/spreadsheet_spec.rb +60 -12
  65. data/spec/lib/roo/utils_spec.rb +106 -0
  66. data/spec/spec_helper.rb +7 -6
  67. data/test/all_ss.rb +12 -11
  68. data/test/excelx/cell/test_base.rb +63 -0
  69. data/test/excelx/cell/test_boolean.rb +36 -0
  70. data/test/excelx/cell/test_date.rb +38 -0
  71. data/test/excelx/cell/test_datetime.rb +45 -0
  72. data/test/excelx/cell/test_empty.rb +7 -0
  73. data/test/excelx/cell/test_number.rb +74 -0
  74. data/test/excelx/cell/test_string.rb +28 -0
  75. data/test/excelx/cell/test_time.rb +30 -0
  76. data/test/formatters/test_csv.rb +119 -0
  77. data/test/formatters/test_matrix.rb +76 -0
  78. data/test/formatters/test_xml.rb +74 -0
  79. data/test/formatters/test_yaml.rb +20 -0
  80. data/test/roo/test_csv.rb +52 -0
  81. data/test/roo/test_excelx.rb +186 -0
  82. data/test/roo/test_libre_office.rb +9 -0
  83. data/test/roo/test_open_office.rb +126 -0
  84. data/test/test_helper.rb +73 -53
  85. data/test/test_roo.rb +1211 -2292
  86. metadata +119 -298
  87. data/CHANGELOG +0 -417
  88. data/Gemfile.lock +0 -78
  89. data/README.markdown +0 -126
  90. data/VERSION +0 -1
  91. data/lib/roo/excel.rb +0 -355
  92. data/lib/roo/excel2003xml.rb +0 -300
  93. data/lib/roo/google.rb +0 -292
  94. data/lib/roo/openoffice.rb +0 -496
  95. data/lib/roo/roo_rails_helper.rb +0 -83
  96. data/lib/roo/worksheet.rb +0 -18
  97. data/scripts/txt2html +0 -67
  98. data/spec/lib/roo/excel2003xml_spec.rb +0 -15
  99. data/spec/lib/roo/excel_spec.rb +0 -17
  100. data/spec/lib/roo/google_spec.rb +0 -64
  101. data/test/files/1900_base.xls +0 -0
  102. data/test/files/1900_base.xlsx +0 -0
  103. data/test/files/1904_base.xls +0 -0
  104. data/test/files/1904_base.xlsx +0 -0
  105. data/test/files/Bibelbund.csv +0 -3741
  106. data/test/files/Bibelbund.ods +0 -0
  107. data/test/files/Bibelbund.xls +0 -0
  108. data/test/files/Bibelbund.xlsx +0 -0
  109. data/test/files/Bibelbund.xml +0 -62518
  110. data/test/files/Bibelbund1.ods +0 -0
  111. data/test/files/Pfand_from_windows_phone.xlsx +0 -0
  112. data/test/files/bad_excel_date.xls +0 -0
  113. data/test/files/bbu.ods +0 -0
  114. data/test/files/bbu.xls +0 -0
  115. data/test/files/bbu.xlsx +0 -0
  116. data/test/files/bbu.xml +0 -152
  117. data/test/files/bode-v1.ods.zip +0 -0
  118. data/test/files/bode-v1.xls.zip +0 -0
  119. data/test/files/boolean.csv +0 -2
  120. data/test/files/boolean.ods +0 -0
  121. data/test/files/boolean.xls +0 -0
  122. data/test/files/boolean.xlsx +0 -0
  123. data/test/files/boolean.xml +0 -112
  124. data/test/files/borders.ods +0 -0
  125. data/test/files/borders.xls +0 -0
  126. data/test/files/borders.xlsx +0 -0
  127. data/test/files/borders.xml +0 -144
  128. data/test/files/bug-numbered-sheet-names.xlsx +0 -0
  129. data/test/files/bug-row-column-fixnum-float.xls +0 -0
  130. data/test/files/bug-row-column-fixnum-float.xml +0 -127
  131. data/test/files/comments.ods +0 -0
  132. data/test/files/comments.xls +0 -0
  133. data/test/files/comments.xlsx +0 -0
  134. data/test/files/csvtypes.csv +0 -1
  135. data/test/files/datetime.ods +0 -0
  136. data/test/files/datetime.xls +0 -0
  137. data/test/files/datetime.xlsx +0 -0
  138. data/test/files/datetime.xml +0 -142
  139. data/test/files/datetime_floatconv.xls +0 -0
  140. data/test/files/datetime_floatconv.xml +0 -148
  141. data/test/files/dreimalvier.ods +0 -0
  142. data/test/files/emptysheets.ods +0 -0
  143. data/test/files/emptysheets.xls +0 -0
  144. data/test/files/emptysheets.xlsx +0 -0
  145. data/test/files/emptysheets.xml +0 -105
  146. data/test/files/excel2003.xml +0 -21140
  147. data/test/files/false_encoding.xls +0 -0
  148. data/test/files/false_encoding.xml +0 -132
  149. data/test/files/file_item_error.xlsx +0 -0
  150. data/test/files/formula.ods +0 -0
  151. data/test/files/formula.xls +0 -0
  152. data/test/files/formula.xlsx +0 -0
  153. data/test/files/formula.xml +0 -134
  154. data/test/files/formula_parse_error.xls +0 -0
  155. data/test/files/formula_parse_error.xml +0 -1833
  156. data/test/files/formula_string_error.xlsx +0 -0
  157. data/test/files/html-escape.ods +0 -0
  158. data/test/files/link.xls +0 -0
  159. data/test/files/link.xlsx +0 -0
  160. data/test/files/matrix.ods +0 -0
  161. data/test/files/matrix.xls +0 -0
  162. data/test/files/named_cells.ods +0 -0
  163. data/test/files/named_cells.xls +0 -0
  164. data/test/files/named_cells.xlsx +0 -0
  165. data/test/files/no_spreadsheet_file.txt +0 -1
  166. data/test/files/numbers1.csv +0 -18
  167. data/test/files/numbers1.ods +0 -0
  168. data/test/files/numbers1.xls +0 -0
  169. data/test/files/numbers1.xlsx +0 -0
  170. data/test/files/numbers1.xml +0 -312
  171. data/test/files/numeric-link.xlsx +0 -0
  172. data/test/files/only_one_sheet.ods +0 -0
  173. data/test/files/only_one_sheet.xls +0 -0
  174. data/test/files/only_one_sheet.xlsx +0 -0
  175. data/test/files/only_one_sheet.xml +0 -67
  176. data/test/files/paragraph.ods +0 -0
  177. data/test/files/paragraph.xls +0 -0
  178. data/test/files/paragraph.xlsx +0 -0
  179. data/test/files/paragraph.xml +0 -127
  180. data/test/files/prova.xls +0 -0
  181. data/test/files/ric.ods +0 -0
  182. data/test/files/simple_spreadsheet.ods +0 -0
  183. data/test/files/simple_spreadsheet.xls +0 -0
  184. data/test/files/simple_spreadsheet.xlsx +0 -0
  185. data/test/files/simple_spreadsheet.xml +0 -225
  186. data/test/files/simple_spreadsheet_from_italo.ods +0 -0
  187. data/test/files/simple_spreadsheet_from_italo.xls +0 -0
  188. data/test/files/simple_spreadsheet_from_italo.xml +0 -242
  189. data/test/files/so_datetime.csv +0 -7
  190. data/test/files/style.ods +0 -0
  191. data/test/files/style.xls +0 -0
  192. data/test/files/style.xlsx +0 -0
  193. data/test/files/style.xml +0 -154
  194. data/test/files/time-test.csv +0 -2
  195. data/test/files/time-test.ods +0 -0
  196. data/test/files/time-test.xls +0 -0
  197. data/test/files/time-test.xlsx +0 -0
  198. data/test/files/time-test.xml +0 -131
  199. data/test/files/type_excel.ods +0 -0
  200. data/test/files/type_excel.xlsx +0 -0
  201. data/test/files/type_excelx.ods +0 -0
  202. data/test/files/type_excelx.xls +0 -0
  203. data/test/files/type_openoffice.xls +0 -0
  204. data/test/files/type_openoffice.xlsx +0 -0
  205. data/test/files/whitespace.ods +0 -0
  206. data/test/files/whitespace.xls +0 -0
  207. data/test/files/whitespace.xlsx +0 -0
  208. data/test/files/whitespace.xml +0 -184
  209. data/test/rm_sub_test.rb +0 -12
  210. data/test/rm_test.rb +0 -7
  211. data/test/test_generic_spreadsheet.rb +0 -259
  212. data/website/index.html +0 -385
  213. data/website/index.txt +0 -423
  214. data/website/javascripts/rounded_corners_lite.inc.js +0 -285
  215. data/website/stylesheets/screen.css +0 -130
  216. data/website/template.rhtml +0 -48
data/lib/roo/base.rb CHANGED
@@ -2,50 +2,41 @@
2
2
 
3
3
  require 'tmpdir'
4
4
  require 'stringio'
5
-
6
- begin
7
- require 'zip/zipfilesystem'
8
- Roo::ZipFile = Zip::ZipFile
9
- rescue LoadError
10
- # For rubyzip >= 1.0.0
11
- require 'zip/filesystem'
12
- Roo::ZipFile = Zip::File
13
- end
5
+ require 'nokogiri'
6
+ require 'roo/utils'
7
+ require "roo/formatters/base"
8
+ require "roo/formatters/csv"
9
+ require "roo/formatters/matrix"
10
+ require "roo/formatters/xml"
11
+ require "roo/formatters/yaml"
14
12
 
15
13
  # Base class for all other types of spreadsheets
16
14
  class Roo::Base
17
15
  include Enumerable
16
+ include Roo::Formatters::Base
17
+ include Roo::Formatters::CSV
18
+ include Roo::Formatters::Matrix
19
+ include Roo::Formatters::XML
20
+ include Roo::Formatters::YAML
18
21
 
19
- TEMP_PREFIX = "oo_"
22
+ MAX_ROW_COL = 999_999.freeze
23
+ MIN_ROW_COL = 0.freeze
20
24
 
21
- attr_reader :default_sheet, :headers
25
+ attr_reader :headers
22
26
 
23
27
  # sets the line with attribute names (default: 1)
24
28
  attr_accessor :header_line
25
29
 
26
- protected
27
-
28
- def self.split_coordinate(str)
29
- letter,number = Roo::Base.split_coord(str)
30
- x = letter_to_number(letter)
31
- y = number
32
- return y, x
30
+ def self.TEMP_PREFIX
31
+ warn '[DEPRECATION] please access TEMP_PREFIX via Roo::TEMP_PREFIX'
32
+ Roo::TEMP_PREFIX
33
33
  end
34
34
 
35
- def self.split_coord(s)
36
- if s =~ /([a-zA-Z]+)([0-9]+)/
37
- letter = $1
38
- number = $2.to_i
39
- else
40
- raise ArgumentError
41
- end
42
- return letter, number
35
+ def self.finalize(object_id)
36
+ proc { finalize_tempdirs(object_id) }
43
37
  end
44
38
 
45
-
46
- public
47
-
48
- def initialize(filename, options={}, file_warning=:error, tmpdir=nil)
39
+ def initialize(filename, options = {}, _file_warning = :error, _tmpdir = nil)
49
40
  @filename = filename
50
41
  @options = options
51
42
 
@@ -59,8 +50,17 @@ class Roo::Base
59
50
  @last_column = {}
60
51
 
61
52
  @header_line = 1
62
- @default_sheet = self.sheets.first
63
- @header_line = 1
53
+ end
54
+
55
+ def close
56
+ if self.class.respond_to?(:finalize_tempdirs)
57
+ self.class.finalize_tempdirs(object_id)
58
+ end
59
+ nil
60
+ end
61
+
62
+ def default_sheet
63
+ @default_sheet ||= sheets.first
64
64
  end
65
65
 
66
66
  # sets the working sheet in the document
@@ -73,144 +73,54 @@ class Roo::Base
73
73
  end
74
74
 
75
75
  # first non-empty column as a letter
76
- def first_column_as_letter(sheet=nil)
77
- Roo::Base.number_to_letter(first_column(sheet))
76
+ def first_column_as_letter(sheet = default_sheet)
77
+ ::Roo::Utils.number_to_letter(first_column(sheet))
78
78
  end
79
79
 
80
80
  # last non-empty column as a letter
81
- def last_column_as_letter(sheet=nil)
82
- Roo::Base.number_to_letter(last_column(sheet))
83
- end
84
-
85
- # returns the number of the first non-empty row
86
- def first_row(sheet=nil)
87
- sheet ||= @default_sheet
88
- read_cells(sheet)
89
- if @first_row[sheet]
90
- return @first_row[sheet]
91
- end
92
- impossible_value = 999_999 # more than a spreadsheet can hold
93
- result = impossible_value
94
- @cell[sheet].each_pair {|key,value|
95
- y = key.first.to_i # _to_string(key).split(',')
96
- result = [result, y].min if value
97
- } if @cell[sheet]
98
- result = nil if result == impossible_value
99
- @first_row[sheet] = result
100
- result
101
- end
102
-
103
- # returns the number of the last non-empty row
104
- def last_row(sheet=nil)
105
- sheet ||= @default_sheet
106
- read_cells(sheet)
107
- if @last_row[sheet]
108
- return @last_row[sheet]
109
- end
110
- impossible_value = 0
111
- result = impossible_value
112
- @cell[sheet].each_pair {|key,value|
113
- y = key.first.to_i # _to_string(key).split(',')
114
- result = [result, y].max if value
115
- } if @cell[sheet]
116
- result = nil if result == impossible_value
117
- @last_row[sheet] = result
118
- result
119
- end
120
-
121
- # returns the number of the first non-empty column
122
- def first_column(sheet=nil)
123
- sheet ||= @default_sheet
124
- read_cells(sheet)
125
- if @first_column[sheet]
126
- return @first_column[sheet]
127
- end
128
- impossible_value = 999_999 # more than a spreadsheet can hold
129
- result = impossible_value
130
- @cell[sheet].each_pair {|key,value|
131
- x = key.last.to_i # _to_string(key).split(',')
132
- result = [result, x].min if value
133
- } if @cell[sheet]
134
- result = nil if result == impossible_value
135
- @first_column[sheet] = result
136
- result
137
- end
138
-
139
- # returns the number of the last non-empty column
140
- def last_column(sheet=nil)
141
- sheet ||= @default_sheet
142
- read_cells(sheet)
143
- if @last_column[sheet]
144
- return @last_column[sheet]
145
- end
146
- impossible_value = 0
147
- result = impossible_value
148
- @cell[sheet].each_pair {|key,value|
149
- x = key.last.to_i # _to_string(key).split(',')
150
- result = [result, x].max if value
151
- } if @cell[sheet]
152
- result = nil if result == impossible_value
153
- @last_column[sheet] = result
154
- result
155
- end
156
-
157
- # returns a rectangular area (default: all cells) as yaml-output
158
- # you can add additional attributes with the prefix parameter like:
159
- # oo.to_yaml({"file"=>"flightdata_2007-06-26", "sheet" => "1"})
160
- def to_yaml(prefix={}, from_row=nil, from_column=nil, to_row=nil, to_column=nil,sheet=nil)
161
- sheet ||= @default_sheet
162
- result = "--- \n"
163
- return '' unless first_row # empty result if there is no first_row in a sheet
164
-
165
- (from_row||first_row(sheet)).upto(to_row||last_row(sheet)) do |row|
166
- (from_column||first_column(sheet)).upto(to_column||last_column(sheet)) do |col|
167
- unless empty?(row,col,sheet)
168
- result << "cell_#{row}_#{col}: \n"
169
- prefix.each {|k,v|
170
- result << " #{k}: #{v} \n"
171
- }
172
- result << " row: #{row} \n"
173
- result << " col: #{col} \n"
174
- result << " celltype: #{self.celltype(row,col,sheet)} \n"
175
- if self.celltype(row,col,sheet) == :time
176
- result << " value: #{Roo::Base.integer_to_timestring( self.cell(row,col,sheet))} \n"
177
- else
178
- result << " value: #{self.cell(row,col,sheet)} \n"
179
- end
180
- end
181
- end
81
+ def last_column_as_letter(sheet = default_sheet)
82
+ ::Roo::Utils.number_to_letter(last_column(sheet))
83
+ end
84
+
85
+ # Set first/last row/column for sheet
86
+ def first_last_row_col_for_sheet(sheet)
87
+ @first_last_row_cols ||= {}
88
+ @first_last_row_cols[sheet] ||= begin
89
+ result = collect_last_row_col_for_sheet(sheet)
90
+ {
91
+ first_row: result[:first_row] == MAX_ROW_COL ? nil : result[:first_row],
92
+ first_column: result[:first_column] == MAX_ROW_COL ? nil : result[:first_column],
93
+ last_row: result[:last_row] == MIN_ROW_COL ? nil : result[:last_row],
94
+ last_column: result[:last_column] == MIN_ROW_COL ? nil : result[:last_column]
95
+ }
182
96
  end
183
- result
184
97
  end
185
98
 
186
- # write the current spreadsheet to stdout or into a file
187
- def to_csv(filename=nil,sheet=nil)
188
- sheet ||= @default_sheet
189
- if filename
190
- File.open(filename,"w") do |file|
191
- write_csv_content(file,sheet)
192
- end
193
- return true
194
- else
195
- sio = StringIO.new
196
- write_csv_content(sio,sheet)
197
- sio.rewind
198
- return sio.read
199
- end
99
+ # Collect first/last row/column from sheet
100
+ def collect_last_row_col_for_sheet(sheet)
101
+ first_row = first_column = MAX_ROW_COL
102
+ last_row = last_column = MIN_ROW_COL
103
+ @cell[sheet].each_pair do|key, value|
104
+ next unless value
105
+ first_row = [first_row, key.first.to_i].min
106
+ last_row = [last_row, key.first.to_i].max
107
+ first_column = [first_column, key.last.to_i].min
108
+ last_column = [last_column, key.last.to_i].max
109
+ end if @cell[sheet]
110
+ { first_row: first_row, first_column: first_column, last_row: last_row, last_column: last_column }
200
111
  end
201
112
 
202
- # returns a matrix object from the whole sheet or a rectangular area of a sheet
203
- def to_matrix(from_row=nil, from_column=nil, to_row=nil, to_column=nil,sheet=nil)
204
- require 'matrix'
205
-
206
- sheet ||= @default_sheet
207
- return Matrix.empty unless first_row
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
120
+ end
208
121
 
209
- Matrix.rows((from_row||first_row(sheet)).upto(to_row||last_row(sheet)).map do |row|
210
- (from_column||first_column(sheet)).upto(to_column||last_column(sheet)).map do |col|
211
- cell(row,col,sheet)
212
- end
213
- end)
122
+ def inspect
123
+ "<##{self.class}:#{object_id.to_s(8)} #{instance_variables.join(' ')}>"
214
124
  end
215
125
 
216
126
  # find a row either by row number or a condition
@@ -218,98 +128,68 @@ class Roo::Base
218
128
  # (experimental. see examples in the test_roo.rb file)
219
129
  def find(*args) # :nodoc
220
130
  options = (args.last.is_a?(Hash) ? args.pop : {})
221
- result_array = options[:array]
222
- header_for = Hash[1.upto(last_column).map do |col|
223
- [col, cell(@header_line,col)]
224
- end]
225
- #-- id
226
- if args[0].class == Fixnum
227
- rownum = args[0]
228
- if @header_line
229
- [Hash[1.upto(self.row().size).map {|j|
230
- [header_for.fetch(j), cell(rownum,j)]
231
- }]]
232
- else
233
- self.row(rownum).size.times.map {|j|
234
- cell(rownum,j + 1)
235
- }
236
- end
237
- #-- :all
238
- elsif args[0] == :all
239
- rows = first_row.upto(last_row)
240
-
241
- # are all conditions met?
242
- if (conditions = options[:conditions]) && !conditions.empty?
243
- column_with = header_for.invert
244
- rows = rows.select do |i|
245
- conditions.all? { |key,val| cell(i,column_with[key]) == val }
246
- end
247
- end
248
131
 
249
- rows.map do |i|
250
- if result_array
251
- self.row(i)
252
- else
253
- Hash[1.upto(self.row(i).size).map do |j|
254
- [header_for.fetch(j), cell(i,j)]
255
- end]
256
- end
257
- end
132
+ case args[0]
133
+ when Integer
134
+ find_by_row(args[0])
135
+ when :all
136
+ find_by_conditions(options)
137
+ else
138
+ fail ArgumentError, "unexpected arg #{args[0].inspect}, pass a row index or :all"
258
139
  end
259
140
  end
260
141
 
261
142
  # returns all values in this row as an array
262
143
  # row numbers are 1,2,3,... like in the spreadsheet
263
- def row(rownumber,sheet=nil)
264
- sheet ||= @default_sheet
144
+ def row(row_number, sheet = default_sheet)
265
145
  read_cells(sheet)
266
146
  first_column(sheet).upto(last_column(sheet)).map do |col|
267
- cell(rownumber,col,sheet)
147
+ cell(row_number, col, sheet)
268
148
  end
269
149
  end
270
150
 
271
151
  # returns all values in this column as an array
272
152
  # column numbers are 1,2,3,... like in the spreadsheet
273
- def column(columnnumber,sheet=nil)
274
- if columnnumber.class == String
275
- columnnumber = Roo::Excel.letter_to_number(columnnumber)
153
+ def column(column_number, sheet = default_sheet)
154
+ if column_number.is_a?(::String)
155
+ column_number = ::Roo::Utils.letter_to_number(column_number)
276
156
  end
277
- sheet ||= @default_sheet
278
157
  read_cells(sheet)
279
158
  first_row(sheet).upto(last_row(sheet)).map do |row|
280
- cell(row,columnnumber,sheet)
159
+ cell(row, column_number, sheet)
281
160
  end
282
161
  end
283
162
 
284
163
  # set a cell to a certain value
285
164
  # (this will not be saved back to the spreadsheet file!)
286
- def set(row,col,value,sheet=nil) #:nodoc:
287
- sheet ||= @default_sheet
165
+ def set(row, col, value, sheet = default_sheet) #:nodoc:
288
166
  read_cells(sheet)
289
- row, col = normalize(row,col)
290
- cell_type = case value
291
- when Fixnum then :float
292
- when String, Float then :string
293
- else
294
- raise ArgumentError, "Type for #{value} not set"
295
- end
167
+ row, col = normalize(row, col)
168
+ cell_type = cell_type_by_value(value)
169
+ set_value(row, col, value, sheet)
170
+ set_type(row, col, cell_type, sheet)
171
+ end
296
172
 
297
- set_value(row,col,value,sheet)
298
- set_type(row,col,cell_type,sheet)
173
+ def cell_type_by_value(value)
174
+ case value
175
+ when Integer then :float
176
+ when String, Float then :string
177
+ else
178
+ fail ArgumentError, "Type for #{value} not set"
179
+ end
299
180
  end
300
181
 
301
182
  # reopens and read a spreadsheet document
302
183
  def reload
303
- ds = @default_sheet
184
+ ds = default_sheet
304
185
  reinitialize
305
186
  self.default_sheet = ds
306
187
  end
307
188
 
308
189
  # true if cell is empty
309
- def empty?(row, col, sheet=nil)
310
- sheet ||= @default_sheet
190
+ def empty?(row, col, sheet = default_sheet)
311
191
  read_cells(sheet)
312
- row,col = normalize(row,col)
192
+ row, col = normalize(row, col)
313
193
  contents = cell(row, col, sheet)
314
194
  !contents || (celltype(row, col, sheet) == :string && contents.empty?) \
315
195
  || (row < first_row(sheet) || row > last_row(sheet) || col < first_column(sheet) || col > last_column(sheet))
@@ -319,66 +199,40 @@ class Roo::Base
319
199
  # this document.
320
200
  def info
321
201
  without_changing_default_sheet do
322
- result = "File: #{File.basename(@filename)}\n"+
323
- "Number of sheets: #{sheets.size}\n"+
202
+ result = "File: #{File.basename(@filename)}\n"\
203
+ "Number of sheets: #{sheets.size}\n"\
324
204
  "Sheets: #{sheets.join(', ')}\n"
325
205
  n = 1
326
- sheets.each {|sheet|
206
+ sheets.each do|sheet|
327
207
  self.default_sheet = sheet
328
- result << "Sheet " + n.to_s + ":\n"
329
- unless first_row
330
- result << " - empty -"
331
- else
208
+ result << 'Sheet ' + n.to_s + ":\n"
209
+ if first_row
332
210
  result << " First row: #{first_row}\n"
333
211
  result << " Last row: #{last_row}\n"
334
- result << " First column: #{Roo::Base.number_to_letter(first_column)}\n"
335
- result << " Last column: #{Roo::Base.number_to_letter(last_column)}"
212
+ result << " First column: #{::Roo::Utils.number_to_letter(first_column)}\n"
213
+ result << " Last column: #{::Roo::Utils.number_to_letter(last_column)}"
214
+ else
215
+ result << ' - empty -'
336
216
  end
337
217
  result << "\n" if sheet != sheets.last
338
218
  n += 1
339
- }
219
+ end
340
220
  result
341
221
  end
342
222
  end
343
223
 
344
- # returns an XML representation of all sheets of a spreadsheet file
345
- def to_xml
346
- Nokogiri::XML::Builder.new do |xml|
347
- xml.spreadsheet {
348
- self.sheets.each do |sheet|
349
- self.default_sheet = sheet
350
- xml.sheet(:name => sheet) { |x|
351
- if first_row and last_row and first_column and last_column
352
- # sonst gibt es Fehler bei leeren Blaettern
353
- first_row.upto(last_row) do |row|
354
- first_column.upto(last_column) do |col|
355
- unless empty?(row,col)
356
- x.cell(cell(row,col),
357
- :row =>row,
358
- :column => col,
359
- :type => celltype(row,col))
360
- end
361
- end
362
- end
363
- end
364
- }
365
- end
366
- }
367
- end.to_xml
368
- end
369
-
370
224
  # when a method like spreadsheet.a42 is called
371
225
  # convert it to a call of spreadsheet.cell('a',42)
372
226
  def method_missing(m, *args)
373
227
  # #aa42 => #cell('aa',42)
374
228
  # #aa42('Sheet1') => #cell('aa',42,'Sheet1')
375
- if m =~ /^([a-z]+)(\d)$/
376
- col = Roo::Base.letter_to_number($1)
377
- row = $2.to_i
229
+ if m =~ /^([a-z]+)(\d+)$/
230
+ col = ::Roo::Utils.letter_to_number(Regexp.last_match[1])
231
+ row = Regexp.last_match[2].to_i
378
232
  if args.empty?
379
- cell(row,col)
233
+ cell(row, col)
380
234
  else
381
- cell(row,col,args.first)
235
+ cell(row, col, args.first)
382
236
  end
383
237
  else
384
238
  super
@@ -387,15 +241,15 @@ class Roo::Base
387
241
 
388
242
  # access different worksheets by calling spreadsheet.sheet(1)
389
243
  # or spreadsheet.sheet('SHEETNAME')
390
- def sheet(index,name=false)
391
- @default_sheet = String === index ? index : self.sheets[index]
392
- name ? [@default_sheet,self] : self
244
+ def sheet(index, name = false)
245
+ self.default_sheet = index.is_a?(::String) ? index : sheets[index]
246
+ name ? [default_sheet, self] : self
393
247
  end
394
248
 
395
249
  # iterate through all worksheets of a document
396
250
  def each_with_pagename
397
- self.sheets.each do |s|
398
- yield sheet(s,true)
251
+ sheets.each do |s|
252
+ yield sheet(s, true)
399
253
  end
400
254
  end
401
255
 
@@ -409,7 +263,6 @@ class Roo::Base
409
263
  # such as :price => '^(Cost|Price)'
410
264
  # case insensitive by default
411
265
 
412
-
413
266
  # by using the :header_search option, you can query for headers
414
267
  # and return a hash of every row with the keys set to the header result
415
268
  # for example:
@@ -420,116 +273,82 @@ class Roo::Base
420
273
  # * is the wildcard character
421
274
 
422
275
  # you can also pass in a :clean => true option to strip the sheet of
423
- # odd unicode characters and white spaces around columns
276
+ # control characters and white spaces around columns
277
+
278
+ def each(options = {})
279
+ return to_enum(:each, options) unless block_given?
424
280
 
425
- def each(options={})
426
281
  if options.empty?
427
282
  1.upto(last_row) do |line|
428
283
  yield row(line)
429
284
  end
430
285
  else
431
- if options[:clean]
432
- options.delete(:clean)
433
- @cleaned ||= {}
434
- @cleaned[@default_sheet] || clean_sheet(@default_sheet)
435
- end
436
-
437
- if options[:header_search]
438
- @headers = nil
439
- @header_line = row_with(options[:header_search])
440
- elsif [:first_row,true].include?(options[:headers])
441
- @headers = []
442
- row(first_row).each_with_index {|x,i| @headers << [x,i + 1]}
443
- else
444
- set_headers(options)
445
- end
446
-
286
+ clean_sheet_if_need(options)
287
+ search_or_set_header(options)
447
288
  headers = @headers ||
448
- Hash[(first_column..last_column).map do |col|
449
- [cell(@header_line,col), col]
450
- end]
289
+ Hash[(first_column..last_column).map do |col|
290
+ [cell(@header_line, col), col]
291
+ end]
451
292
 
452
293
  @header_line.upto(last_row) do |line|
453
- yield(Hash[headers.map {|k,v| [k,cell(line,v)]}])
294
+ yield(Hash[headers.map { |k, v| [k, cell(line, v)] }])
454
295
  end
455
296
  end
456
297
  end
457
298
 
458
- def parse(options={})
299
+ def parse(options = {})
459
300
  ary = []
460
- if block_given?
461
- each(options) {|row| ary << yield(row)}
462
- else
463
- each(options) {|row| ary << row}
301
+ each(options) do |row|
302
+ yield(row) if block_given?
303
+ ary << row
464
304
  end
465
305
  ary
466
306
  end
467
307
 
468
- def row_with(query,return_headers=false)
469
- query.map! {|x| Array(x.split('*'))}
308
+ def row_with(query, return_headers = false)
470
309
  line_no = 0
471
310
  each do |row|
472
311
  line_no += 1
473
- # makes sure headers is the first part of wildcard search for priority
474
- # ex. if UPC and SKU exist for UPC*SKU search, UPC takes the cake
475
- headers = query.map do |q|
476
- q.map {|i| row.grep(/#{i}/i)[0]}.compact[0]
477
- end.compact
312
+ headers = query.map { |q| row.grep(q)[0] }.compact
478
313
 
479
314
  if headers.length == query.length
480
315
  @header_line = line_no
481
316
  return return_headers ? headers : line_no
482
317
  elsif line_no > 100
483
- raise "Couldn't find header row."
318
+ raise Roo::HeaderRowNotFoundError
484
319
  end
485
320
  end
321
+ raise Roo::HeaderRowNotFoundError
486
322
  end
487
323
 
488
324
  protected
489
325
 
490
- def load_xml(path)
491
- File.open(path) do |file|
492
- Nokogiri::XML(file)
493
- end
494
- end
495
-
496
- def file_type_check(filename, ext, name, warning_level, packed=nil)
497
- new_expression = {
498
- '.ods' => 'Roo::OpenOffice.new',
499
- '.xls' => 'Roo::Excel.new',
500
- '.xlsx' => 'Roo::Excelx.new',
501
- '.csv' => 'Roo::CSV.new',
502
- '.xml' => 'Roo::Excel2003XML.new',
503
- }
326
+ def file_type_check(filename, exts, name, warning_level, packed = nil)
504
327
  if packed == :zip
505
- # lalala.ods.zip => lalala.ods
506
- # hier wird KEIN unzip gemacht, sondern nur der Name der Datei
507
- # getestet, falls es eine gepackte Datei ist.
508
- filename = File.basename(filename,File.extname(filename))
509
- end
510
- case ext
511
- when '.ods', '.xls', '.xlsx', '.csv', '.xml'
512
- correct_class = "use #{new_expression[ext]} to handle #{ext} spreadsheet files. This has #{File.extname(filename).downcase}"
513
- else
514
- raise "unknown file type: #{ext}"
328
+ # spreadsheet.ods.zip => spreadsheet.ods
329
+ # Decompression is not performed here, only the 'zip' extension
330
+ # is removed from the file.
331
+ filename = File.basename(filename, File.extname(filename))
515
332
  end
516
333
 
517
- if uri?(filename) && qs_begin = filename.rindex('?')
518
- filename = filename[0..qs_begin-1]
334
+ if uri?(filename) && (qs_begin = filename.rindex('?'))
335
+ filename = filename[0..qs_begin - 1]
519
336
  end
520
- if File.extname(filename).downcase != ext
521
- case warning_level
522
- when :error
523
- warn correct_class
524
- raise TypeError, "#{filename} is not #{name} file"
525
- when :warning
526
- warn "are you sure, this is #{name} spreadsheet file?"
527
- warn correct_class
528
- when :ignore
529
- # ignore
530
- else
531
- raise "#{warning_level} illegal state of file_warning"
532
- end
337
+ exts = Array(exts)
338
+
339
+ return if exts.include?(File.extname(filename).downcase)
340
+
341
+ case warning_level
342
+ when :error
343
+ warn file_type_warning_message(filename, exts)
344
+ fail TypeError, "#{filename} is not #{name} file"
345
+ when :warning
346
+ warn "are you sure, this is #{name} spreadsheet file?"
347
+ warn file_type_warning_message(filename, exts)
348
+ when :ignore
349
+ # ignore
350
+ else
351
+ fail "#{warning_level} illegal state of file_warning"
533
352
  end
534
353
  end
535
354
 
@@ -538,8 +357,8 @@ class Roo::Base
538
357
  # Diese Methode ist eine temp. Loesung, um zu erforschen, ob der
539
358
  # Zugriff mit numerischen Keys schneller ist.
540
359
  def key_to_num(str)
541
- r,c = str.split(',')
542
- [r.to_i,c.to_i]
360
+ r, c = str.split(',')
361
+ [r.to_i, c.to_i]
543
362
  end
544
363
 
545
364
  # see: key_to_num
@@ -547,8 +366,83 @@ class Roo::Base
547
366
  "#{arr[0]},#{arr[1]}"
548
367
  end
549
368
 
369
+ def is_stream?(filename_or_stream)
370
+ filename_or_stream.respond_to?(:seek)
371
+ end
372
+
550
373
  private
551
374
 
375
+ def clean_sheet_if_need(options)
376
+ return unless options[:clean]
377
+ options.delete(:clean)
378
+ @cleaned ||= {}
379
+ clean_sheet(default_sheet) unless @cleaned[default_sheet]
380
+ end
381
+
382
+ def search_or_set_header(options)
383
+ if options[:header_search]
384
+ @headers = nil
385
+ @header_line = row_with(options[:header_search])
386
+ elsif [:first_row, true].include?(options[:headers])
387
+ @headers = []
388
+ row(first_row).each_with_index { |x, i| @headers << [x, i + 1] }
389
+ else
390
+ set_headers(options)
391
+ end
392
+ end
393
+
394
+ def local_filename(filename, tmpdir, packed)
395
+ return if is_stream?(filename)
396
+ filename = download_uri(filename, tmpdir) if uri?(filename)
397
+ filename = unzip(filename, tmpdir) if packed == :zip
398
+
399
+ fail IOError, "file #{filename} does not exist" unless File.file?(filename)
400
+
401
+ filename
402
+ end
403
+
404
+ def file_type_warning_message(filename, exts)
405
+ *rest, last_ext = exts
406
+ ext_list = rest.any? ? "#{rest.join(', ')} or #{last_ext}" : last_ext
407
+ "use #{Roo::CLASS_FOR_EXTENSION.fetch(last_ext.sub('.', '').to_sym)}.new to handle #{ext_list} spreadsheet files. This has #{File.extname(filename).downcase}"
408
+ rescue KeyError
409
+ raise "unknown file types: #{ext_list}"
410
+ end
411
+
412
+ def find_by_row(row_index)
413
+ row_index += (header_line - 1) if @header_line
414
+
415
+ row(row_index).size.times.map do |cell_index|
416
+ cell(row_index, cell_index + 1)
417
+ end
418
+ end
419
+
420
+ def find_by_conditions(options)
421
+ rows = first_row.upto(last_row)
422
+ header_for = Hash[1.upto(last_column).map do |col|
423
+ [col, cell(@header_line, col)]
424
+ end]
425
+
426
+ # are all conditions met?
427
+ conditions = options[:conditions]
428
+ if conditions && !conditions.empty?
429
+ column_with = header_for.invert
430
+ rows = rows.select do |i|
431
+ conditions.all? { |key, val| cell(i, column_with[key]) == val }
432
+ end
433
+ end
434
+
435
+ if options[:array]
436
+ rows.map { |i| row(i) }
437
+ else
438
+ rows.map do |i|
439
+ Hash[1.upto(row(i).size).map do |j|
440
+ [header_for.fetch(j), cell(i, j)]
441
+ end]
442
+ end
443
+ end
444
+ end
445
+
552
446
  def without_changing_default_sheet
553
447
  original_default_sheet = default_sheet
554
448
  yield
@@ -560,77 +454,91 @@ class Roo::Base
560
454
  initialize(@filename)
561
455
  end
562
456
 
563
- def make_tmpdir(tmp_root = nil)
564
- Dir.mktmpdir(TEMP_PREFIX, tmp_root || ENV['ROO_TMP']) do |tmpdir|
565
- yield tmpdir
457
+ def find_basename(filename)
458
+ if uri?(filename)
459
+ require 'uri'
460
+ uri = URI.parse filename
461
+ File.basename(uri.path)
462
+ elsif !is_stream?(filename)
463
+ File.basename(filename)
464
+ end
465
+ end
466
+
467
+ def make_tmpdir(prefix = nil, root = nil, &block)
468
+ warn '[DEPRECATION] extend Roo::Tempdir and use its .make_tempdir instead'
469
+ prefix = "#{Roo::TEMP_PREFIX}#{prefix}"
470
+ root ||= ENV['ROO_TMP']
471
+
472
+ if block_given?
473
+ # folder is deleted at end of block
474
+ ::Dir.mktmpdir(prefix, root, &block)
475
+ else
476
+ self.class.make_tempdir(self, prefix, root)
566
477
  end
567
478
  end
568
479
 
569
480
  def clean_sheet(sheet)
570
481
  read_cells(sheet)
571
- @cell[sheet].each_pair do |coord,value|
572
- if String === value
573
- @cell[sheet][coord] = sanitize_value(value)
574
- end
482
+ @cell[sheet].each_pair do |coord, value|
483
+ @cell[sheet][coord] = sanitize_value(value) if value.is_a?(::String)
575
484
  end
576
485
  @cleaned[sheet] = true
577
486
  end
578
487
 
579
488
  def sanitize_value(v)
580
- v.strip.unpack('U*').select {|b| b < 127}.pack('U*')
489
+ v.gsub(/[[:cntrl:]]|^[\p{Space}]+|[\p{Space}]+$/, '')
581
490
  end
582
491
 
583
- def set_headers(hash={})
492
+ def set_headers(hash = {})
584
493
  # try to find header row with all values or give an error
585
494
  # then create new hash by indexing strings and keeping integers for header array
586
- @headers = row_with(hash.values,true)
587
- @headers = Hash[hash.keys.zip(@headers.map {|x| header_index(x)})]
495
+ @headers = row_with(hash.values, true)
496
+ @headers = Hash[hash.keys.zip(@headers.map { |x| header_index(x) })]
588
497
  end
589
498
 
590
499
  def header_index(query)
591
500
  row(@header_line).index(query) + first_column
592
501
  end
593
502
 
594
- def set_value(row,col,value,sheet=nil)
595
- sheet ||= @default_sheet
596
- @cell[sheet][[row,col]] = value
503
+ def set_value(row, col, value, sheet = default_sheet)
504
+ @cell[sheet][[row, col]] = value
597
505
  end
598
506
 
599
- def set_type(row,col,type,sheet=nil)
600
- sheet ||= @default_sheet
601
- @cell_type[sheet][[row,col]] = type
507
+ def set_type(row, col, type, sheet = default_sheet)
508
+ @cell_type[sheet][[row, col]] = type
602
509
  end
603
510
 
604
511
  # converts cell coordinate to numeric values of row,col
605
- def normalize(row,col)
606
- if row.class == String
607
- if col.class == Fixnum
512
+ def normalize(row, col)
513
+ if row.is_a?(::String)
514
+ if col.is_a?(::Integer)
608
515
  # ('A',1):
609
516
  # ('B', 5) -> (5, 2)
610
517
  row, col = col, row
611
518
  else
612
- raise ArgumentError
519
+ fail ArgumentError
613
520
  end
614
521
  end
615
- if col.class == String
616
- col = Roo::Base.letter_to_number(col)
617
- end
618
- return row,col
522
+
523
+ col = ::Roo::Utils.letter_to_number(col) if col.is_a?(::String)
524
+
525
+ [row, col]
619
526
  end
620
527
 
621
528
  def uri?(filename)
622
- filename.start_with?("http://", "https://")
529
+ filename.start_with?('http://', 'https://', 'ftp://')
530
+ rescue
531
+ false
623
532
  end
624
533
 
625
534
  def download_uri(uri, tmpdir)
626
535
  require 'open-uri'
627
- tempfilename = File.join(tmpdir, File.basename(uri))
628
- response = ''
536
+ tempfilename = File.join(tmpdir, find_basename(uri))
629
537
  begin
630
- File.open(tempfilename,"wb") do |file|
631
- open(uri, "User-Agent" => "Ruby/#{RUBY_VERSION}") { |net|
538
+ File.open(tempfilename, 'wb') do |file|
539
+ open(uri, 'User-Agent' => "Ruby/#{RUBY_VERSION}") do |net|
632
540
  file.write(net.read)
633
- }
541
+ end
634
542
  end
635
543
  rescue OpenURI::HTTPError
636
544
  raise "could not open #{uri}"
@@ -639,50 +547,17 @@ class Roo::Base
639
547
  end
640
548
 
641
549
  def open_from_stream(stream, tmpdir)
642
- tempfilename = File.join(tmpdir, "spreadsheet")
643
- File.open(tempfilename,"wb") do |file|
550
+ tempfilename = File.join(tmpdir, 'spreadsheet')
551
+ File.open(tempfilename, 'wb') do |file|
644
552
  file.write(stream[7..-1])
645
553
  end
646
- File.join(tmpdir, "spreadsheet")
647
- end
648
-
649
- LETTERS = %w{A B C D E F G H I J K L M N O P Q R S T U V W X Y Z}
650
-
651
- # convert a number to something like 'AB' (1 => 'A', 2 => 'B', ...)
652
- def self.number_to_letter(n)
653
- letters=""
654
- if n > 26
655
- while n % 26 == 0 && n != 0
656
- letters << 'Z'
657
- n = (n - 26) / 26
658
- end
659
- while n > 0
660
- num = n%26
661
- letters = LETTERS[num-1] + letters
662
- n = (n / 26)
663
- end
664
- else
665
- letters = LETTERS[n-1]
666
- end
667
- letters
668
- end
669
-
670
- # convert letters like 'AB' to a number ('A' => 1, 'B' => 2, ...)
671
- def self.letter_to_number(letters)
672
- result = 0
673
- while letters && letters.length > 0
674
- character = letters[0,1].upcase
675
- num = LETTERS.index(character)
676
- raise ArgumentError, "invalid column character '#{letters[0,1]}'" if num == nil
677
- num += 1
678
- result = result * 26 + num
679
- letters = letters[1..-1]
680
- end
681
- result
554
+ File.join(tmpdir, 'spreadsheet')
682
555
  end
683
556
 
684
557
  def unzip(filename, tmpdir)
685
- Roo::ZipFile.open(filename) do |zip|
558
+ require 'zip/filesystem'
559
+
560
+ Zip::File.open(filename) do |zip|
686
561
  process_zipfile_packed(zip, tmpdir)
687
562
  end
688
563
  end
@@ -691,29 +566,29 @@ class Roo::Base
691
566
  def validate_sheet!(sheet)
692
567
  case sheet
693
568
  when nil
694
- raise ArgumentError, "Error: sheet 'nil' not valid"
695
- when Fixnum
696
- self.sheets.fetch(sheet-1) do
697
- raise RangeError, "sheet index #{sheet} not found"
569
+ fail ArgumentError, "Error: sheet 'nil' not valid"
570
+ when Integer
571
+ sheets.fetch(sheet - 1) do
572
+ fail RangeError, "sheet index #{sheet} not found"
698
573
  end
699
574
  when String
700
- if !sheets.include? sheet
701
- raise RangeError, "sheet '#{sheet}' not found"
575
+ unless sheets.include?(sheet)
576
+ fail RangeError, "sheet '#{sheet}' not found"
702
577
  end
703
578
  else
704
- raise TypeError, "not a valid sheet type: #{sheet.inspect}"
579
+ fail TypeError, "not a valid sheet type: #{sheet.inspect}"
705
580
  end
706
581
  end
707
582
 
708
- def process_zipfile_packed(zip, tmpdir, path='')
583
+ def process_zipfile_packed(zip, tmpdir, path = '')
709
584
  if zip.file.file? path
710
585
  # extract and return filename
711
- File.open(File.join(tmpdir, path),"wb") do |file|
586
+ File.open(File.join(tmpdir, path), 'wb') do |file|
712
587
  file.write(zip.read(path))
713
588
  end
714
589
  File.join(tmpdir, path)
715
590
  else
716
- ret=nil
591
+ ret = nil
717
592
  path += '/' unless path.empty?
718
593
  zip.dir.foreach(path) do |filename|
719
594
  ret = process_zipfile_packed(zip, tmpdir, path + filename)
@@ -721,76 +596,4 @@ class Roo::Base
721
596
  ret
722
597
  end
723
598
  end
724
-
725
- # Write all cells to the csv file. File can be a filename or nil. If the this
726
- # parameter is nil the output goes to STDOUT
727
- def write_csv_content(file=nil,sheet=nil)
728
- file ||= STDOUT
729
- if first_row(sheet) # sheet is not empty
730
- 1.upto(last_row(sheet)) do |row|
731
- 1.upto(last_column(sheet)) do |col|
732
- file.print(",") if col > 1
733
- file.print cell_to_csv(row,col,sheet)
734
- end
735
- file.print("\n")
736
- end # sheet not empty
737
- end
738
- end
739
-
740
- # The content of a cell in the csv output
741
- def cell_to_csv(row, col, sheet)
742
- if empty?(row,col,sheet)
743
- ''
744
- else
745
- onecell = cell(row,col,sheet)
746
-
747
- case celltype(row,col,sheet)
748
- when :string
749
- unless onecell.empty?
750
- %{"#{onecell.gsub(/"/,'""')}"}
751
- end
752
- when :boolean
753
- %{"#{onecell.gsub(/"/,'""').downcase}"}
754
- when :float, :percentage
755
- if onecell == onecell.to_i
756
- onecell.to_i.to_s
757
- else
758
- onecell.to_s
759
- end
760
- when :formula
761
- case onecell
762
- when String
763
- unless onecell.empty?
764
- %{"#{onecell.gsub(/"/,'""')}"}
765
- end
766
- when Float
767
- if onecell == onecell.to_i
768
- onecell.to_i.to_s
769
- else
770
- onecell.to_s
771
- end
772
- when DateTime
773
- onecell.to_s
774
- else
775
- raise "unhandled onecell-class #{onecell.class}"
776
- end
777
- when :date, :datetime
778
- onecell.to_s
779
- when :time
780
- Roo::Base.integer_to_timestring(onecell)
781
- else
782
- raise "unhandled celltype #{celltype(row,col,sheet)}"
783
- end || ""
784
- end
785
- end
786
-
787
- # converts an integer value to a time string like '02:05:06'
788
- def self.integer_to_timestring(content)
789
- h = (content/3600.0).floor
790
- content = content - h*3600
791
- m = (content/60.0).floor
792
- content = content - m*60
793
- s = content
794
- sprintf("%02d:%02d:%02d",h,m,s)
795
- end
796
599
  end