roo 1.13.2 → 2.7.0

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