robust_excel_ole 1.31 → 1.32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog +20 -1
  3. data/README.rdoc +118 -18
  4. data/___dummy_workbook.xls +0 -0
  5. data/benchmarking/creek_example.rb +1 -1
  6. data/benchmarking/roo_example.rb +1 -1
  7. data/benchmarking/simple_xlsx_reader_example.rb +1 -1
  8. data/benchmarking/spreadsheet_example.rb +1 -1
  9. data/docs/README_excel.rdoc +16 -24
  10. data/docs/README_listobjects.rdoc +176 -0
  11. data/docs/README_open.rdoc +12 -12
  12. data/docs/README_ranges.rdoc +72 -55
  13. data/docs/README_save_close.rdoc +3 -3
  14. data/docs/README_sheet.rdoc +18 -13
  15. data/examples/example_ruby_library.rb +2 -2
  16. data/examples/introductory_examples/example_range.rb +2 -2
  17. data/examples/modifying_sheets/example_access_sheets_and_cells.rb +6 -6
  18. data/examples/modifying_sheets/example_add_names.rb +1 -1
  19. data/examples/modifying_sheets/example_concating.rb +1 -1
  20. data/examples/modifying_sheets/example_copying.rb +2 -2
  21. data/examples/modifying_sheets/example_listobjects.rb +86 -0
  22. data/examples/modifying_sheets/example_naming.rb +1 -1
  23. data/examples/modifying_sheets/example_ranges.rb +1 -1
  24. data/examples/open_save_close/example_control_to_excel.rb +1 -1
  25. data/examples/open_save_close/example_if_obstructed_closeifsaved.rb +1 -1
  26. data/examples/open_save_close/example_if_obstructed_save.rb +3 -3
  27. data/examples/open_save_close/example_if_unsaved_accept.rb +1 -1
  28. data/examples/open_save_close/example_if_unsaved_forget.rb +3 -3
  29. data/examples/open_save_close/example_if_unsaved_forget_more.rb +4 -4
  30. data/examples/open_save_close/example_read_only.rb +1 -1
  31. data/examples/open_save_close/example_simple.rb +1 -1
  32. data/examples/open_save_close/example_unobtrusively.rb +3 -3
  33. data/lib/robust_excel_ole/address_tool.rb +54 -44
  34. data/lib/robust_excel_ole/base.rb +4 -6
  35. data/lib/robust_excel_ole/bookstore.rb +2 -16
  36. data/lib/robust_excel_ole/cell.rb +16 -21
  37. data/lib/robust_excel_ole/excel.rb +131 -186
  38. data/lib/robust_excel_ole/general.rb +82 -55
  39. data/lib/robust_excel_ole/list_object.rb +182 -109
  40. data/lib/robust_excel_ole/list_row.rb +65 -38
  41. data/lib/robust_excel_ole/range.rb +125 -93
  42. data/lib/robust_excel_ole/range_owners.rb +52 -66
  43. data/lib/robust_excel_ole/version.rb +1 -1
  44. data/lib/robust_excel_ole/workbook.rb +168 -176
  45. data/lib/robust_excel_ole/worksheet.rb +177 -141
  46. data/robust_excel_ole.gemspec +4 -3
  47. data/spec/bookstore_spec.rb +2 -3
  48. data/spec/cell_spec.rb +9 -9
  49. data/spec/data/more_data/workbook.xls +0 -0
  50. data/spec/excel_spec.rb +132 -85
  51. data/spec/general_spec.rb +47 -15
  52. data/spec/list_object_spec.rb +258 -145
  53. data/spec/list_row_spec.rb +218 -0
  54. data/spec/range_spec.rb +76 -29
  55. data/spec/spec_helper.rb +15 -1
  56. data/spec/workbook_spec.rb +75 -34
  57. data/spec/workbook_specs/workbook_all_spec.rb +2 -1
  58. data/spec/workbook_specs/workbook_misc_spec.rb +20 -13
  59. data/spec/workbook_specs/workbook_open_spec.rb +47 -45
  60. data/spec/workbook_specs/workbook_save_spec.rb +21 -22
  61. data/spec/workbook_specs/workbook_sheet_spec.rb +3 -3
  62. data/spec/workbook_specs/workbook_unobtr_spec.rb +303 -303
  63. data/spec/worksheet_spec.rb +522 -318
  64. metadata +37 -2
@@ -1,33 +1,54 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require 'pathname'
3
3
 
4
- # @private
5
- #class WIN32OLE
6
4
  module ToReoRefinement
7
5
 
8
6
  refine WIN32OLE do
9
7
 
10
8
  # type-lifting WIN32OLE objects to RobustExcelOle objects
11
9
  def to_reo
12
- General.class2method.each do |element|
13
- classname = element.first.first
14
- method = element.first.last
15
- begin
16
- self.send(method)
17
- if classname == RobustExcelOle::Range && self.Rows.Count == 1 && self.Columns.Count == 1
18
- return Cell.new(self, self.Parent)
19
- else
20
- return classname.new(self)
10
+ General.main_classes_ole_types_and_recognising_methods.each do |classname, ole_type, methods|
11
+ if !::OLETYPE_JRUBY_BUG
12
+ if self.ole_type.name == ole_type
13
+ if classname != RobustExcelOle::Range
14
+ return classname.new(self)
15
+ elsif self.Rows.Count == 1 && self.Columns.Count == 1
16
+ return RobustExcelOle::Cell.new(self, self.Parent)
17
+ else
18
+ return RobustExcelOle::Range.new(self, self.Parent)
19
+ end
20
+ end
21
+ else
22
+ begin
23
+ recognising_method, no_method = methods
24
+ self.send(recognising_method)
25
+ unless no_method.nil?
26
+ begin
27
+ self.send(no_method[:no_method])
28
+ next
29
+ rescue NoMethodError
30
+ return classname.new(self)
31
+ end
32
+ end
33
+ if classname != RobustExcelOle::Range
34
+ return classname.new(self)
35
+ elsif self.Rows.Count == 1 && self.Columns.Count == 1
36
+ return RobustExcelOle::Cell.new(self, self.Parent)
37
+ else
38
+ return RobustExcelOle::Range.new(self, self.Parent)
39
+ end
40
+ rescue Java::OrgRacobCom::ComFailException => msg # NoMethodError
41
+ #if $!.message =~ /undefined method/ &&
42
+ # main_classes_ole_types_and_recognising_methods.any?{ |_c, _o, recognising_method| $!.message.include?(recognising_method.to_s) }
43
+ next
44
+ #end
21
45
  end
22
- rescue
23
- next
24
46
  end
25
47
  end
26
- raise TypeREOError, "given object cannot be type-lifted to a RobustExcelOle object"
48
+ raise RobustExcelOle::TypeREOError, "given object cannot be type-lifted to a RobustExcelOle object"
27
49
  end
28
50
 
29
51
  end
30
-
31
52
  end
32
53
 
33
54
  # @private
@@ -43,6 +64,7 @@ module FindAllIndicesRefinement
43
64
  refine Array do
44
65
 
45
66
  def find_all_indices elem
67
+ elem = elem.encode('utf-8') if elem.respond_to?(:gsub)
46
68
  found, index, result = -1, -1, []
47
69
  while found
48
70
  found = self[index+1..-1].index(elem)
@@ -58,39 +80,32 @@ module FindAllIndicesRefinement
58
80
 
59
81
  end
60
82
 
83
+ TRANSLATION_TABLE = {
84
+ 'ä' => 'ae', 'ö' => 'oe', 'ü' => 'ue', 'Ä' => 'Ae', 'Ö' => 'Oe', 'Ü' => 'Ue',
85
+ 'ß' => 'ss', '²' => '2', '³' => '3'
86
+ }
87
+
61
88
  # @private
62
89
  module StringRefinement
63
90
 
64
91
  refine String do
65
92
 
66
93
  def / path_part
67
- if empty?
68
- path_part
69
- else
70
- if path_part.nil? || path_part.empty?
71
- self
72
- else
73
- begin
74
- path_part = path_part.strip
75
- (path_part =~ /^(\/|([A-Z]:\/))/i) ? path_part : (self.chomp('/') + '/' + path_part)
76
- rescue TypeError
77
- raise TypeError, "Only strings can be parts of paths (given: #{path_part.inspect} of class #{path_part.class})"
78
- end
79
- end
94
+ return path_part if empty?
95
+ return self if path_part.nil? || path_part.empty?
96
+ begin
97
+ path_part = path_part.strip
98
+ (path_part =~ /^(\/|([A-Z]:\/))/i) ? path_part : (chomp('/') + '/' + path_part)
99
+ rescue TypeError
100
+ raise TypeError, "Only strings can be parts of paths (given: #{path_part.inspect} of class #{path_part.class})"
80
101
  end
81
- end
102
+ end
82
103
 
83
104
  def replace_umlauts
84
- word = self.force_encoding('iso-8859-1').encode('utf-8')
85
- #word = self.encode("UTF-8")
86
- #word = self.encode("UTF-8", "Windows-1252")
87
- word.gsub('ä','ae').gsub('Ä','Ae').gsub('ö','oe').gsub('Ö','Oe').gsub('ü','ue').gsub('Ü','Ue')
88
- word.gsub('ß','ss').gsub('²','2').gsub('³','3')
89
- #word.gsub("\x84",'ae').gsub("\x8E",'Ae').gsub("\x94",'oe').gsub("\x99",'Oe').gsub("\x81",'ue').gsub("\x9A",'Ue')
90
- #word.gsub("\xE1",'ss').gsub("\xFD",'2').gsub("\xFC",'3')
91
- word
105
+ TRANSLATION_TABLE.inject(encode('utf-8')) { |word,(umlaut, replacement)| word.gsub(umlaut, replacement) }
92
106
  end
93
107
 
108
+
94
109
  # taken from http://apidock.com/rails/ActiveSupport/Inflector/underscore
95
110
  def underscore
96
111
  word = gsub('::', '/')
@@ -163,7 +178,6 @@ class Integer
163
178
  alias old_spaceship <=>
164
179
 
165
180
  def <=> other
166
- # p other
167
181
  if other.is_a? Array
168
182
  self <=> other.first
169
183
  else
@@ -201,6 +215,7 @@ module General
201
215
  ::ERRORMESSAGE_JRUBY_BUG = IS_JRUBY_PLATFORM && true
202
216
  ::CONNECT_EXCEL_JRUBY_BUG = IS_JRUBY_PLATFORM && true
203
217
  ::RANGES_JRUBY_BUG = IS_JRUBY_PLATFORM && true
218
+ ::OLETYPE_JRUBY_BUG = IS_JRUBY_PLATFORM && true
204
219
 
205
220
  # @private
206
221
  NetworkDrive = Struct.new(:drive_letter, :network_name) do
@@ -208,12 +223,8 @@ module General
208
223
  def self.get_all_drives
209
224
  network = WIN32OLE.new('WScript.Network')
210
225
  drives = network.enumnetworkdrives
211
- ndrives = []
212
226
  count = drives.Count
213
- (0..(count - 1)).step(2) do |i|
214
- ndrives << NetworkDrive.new( drives.Item(i), drives.Item(i + 1).tr('\\','/'))
215
- end
216
- ndrives
227
+ (0..(count - 1)).step(2).map{ |i| NetworkDrive.new( drives.Item(i), drives.Item(i + 1).tr('\\','/')) }
217
228
  end
218
229
  end
219
230
 
@@ -258,30 +269,46 @@ module General
258
269
  end
259
270
 
260
271
  # @private
261
- def class2method
262
- [{RobustExcelOle::Excel => :Hwnd},
263
- {RobustExcelOle::Workbook => :FullName},
264
- {RobustExcelOle::Worksheet => :UsedRange},
265
- {RobustExcelOle::Range => :Row},
266
- {RobustExcelOle::ListObject => :ListRows}]
272
+ def main_classes_ole_types_and_recognising_methods
273
+ [[RobustExcelOle::Range , 'Range' , :Row],
274
+ [RobustExcelOle::Worksheet , '_Worksheet' , :UsedRange],
275
+ [RobustExcelOle::Workbook , '_Workbook' , :FullName],
276
+ [RobustExcelOle::Excel , '_Application', :Hwnd],
277
+ [RobustExcelOle::ListObject, 'ListObject' , :ListRows],
278
+ [RobustExcelOle::ListRow , 'ListRow' , [:Creator, :no_method => :Row]]]
267
279
  end
268
280
 
269
281
  # @private
270
282
  # enable RobustExcelOle methods to Win32Ole objects
271
283
  def init_reo_for_win32ole
272
- class2method.each do |element|
273
- classname = element.first.first
284
+ main_classes_ole_types_and_recognising_methods.each do |classname, _ole_type, _recognising_method|
274
285
  meths = (classname.instance_methods(false) - WIN32OLE.instance_methods(false) - Object.methods - Enumerable.instance_methods(false) - [:Calculation=])
275
286
  meths.each do |inst_method|
276
- WIN32OLE.send(:define_method, inst_method) do |*args, &blk|
277
- self.to_reo.send(inst_method, *args, &blk)
287
+ if classname.method_defined?(inst_method)
288
+ WIN32OLE.send(:define_method, inst_method) do |*args, &blk|
289
+ begin
290
+ obj = to_reo
291
+ rescue
292
+ return self.send(inst_method.capitalize, *args, &blk)
293
+ end
294
+ obj.send(inst_method, *args, &blk)
295
+ end
296
+ else
297
+ WIN32OLE.send(:define_method, inst_method) do |*args, &blk|
298
+ begin
299
+ obj = classname.constantize.new(self)
300
+ rescue
301
+ return self.send(inst_method.capitalize, *args, &blk)
302
+ end
303
+ obj.send(inst_method, *args, &blk)
304
+ end
278
305
  end
279
306
  end
280
307
  end
281
- nil
282
308
  end
283
309
 
284
- module_function :absolute_path, :canonize, :normalize, :change_current_binding, :class2method,
310
+ module_function :absolute_path, :canonize, :normalize, :change_current_binding,
311
+ :main_classes_ole_types_and_recognising_methods,
285
312
  :init_reo_for_win32ole, :hostnameshare2networkpath, :test
286
313
 
287
314
  end
@@ -1,5 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
-
3
2
  module RobustExcelOle
4
3
 
5
4
  using ToReoRefinement
@@ -12,6 +11,8 @@ module RobustExcelOle
12
11
 
13
12
  class ListObject < VbaObjects
14
13
 
14
+ include Enumerable
15
+
15
16
  attr_reader :ole_table
16
17
 
17
18
  alias ole_object ole_table
@@ -55,7 +56,7 @@ module RobustExcelOle
55
56
  @ole_table.Name = table_name_or_number
56
57
  @ole_table.HeaderRowRange.Value = [column_names] unless column_names.empty?
57
58
  rescue WIN32OLERuntimeError => msg # , Java::OrgRacobCom::ComFailException => msg
58
- raise TableError, "error #{$!.message}"
59
+ raise TableError, "#{$!.message}"
59
60
  end
60
61
  end
61
62
 
@@ -73,32 +74,119 @@ module RobustExcelOle
73
74
 
74
75
  end
75
76
 
77
+ # @return [Enumerator] traversing all list row objects
78
+ def each
79
+ if block_given?
80
+ @ole_table.ListRows.lazy.each do |ole_listrow|
81
+ yield @row_class.new(ole_listrow)
82
+ end
83
+ else
84
+ to_enum(:each).lazy
85
+ end
86
+ end
87
+
76
88
  # accesses a table row object
77
- # @param [Integer] a row number (>= 1)
78
- # @return [ListRow] a object of dynamically constructed class with superclass ListRow
79
- def [] row_number
80
- @row_class.new(row_number)
89
+ # @param [Variant] a hash of key (key column: value) or a row number (>= 1)
90
+ # @option opts [Variant] limit: maximal number of matching table rows to return, or return the first matching table row (default :first)
91
+ # @return [Variant] a listrow, if limit == :first
92
+ # an array of listrows, with maximal number=limit, if list rows were found and limit is not :first
93
+ # nil, if no list object was found
94
+ def [] (key_hash_or_number, options = { })
95
+ return @row_class.new(key_hash_or_number) if key_hash_or_number.respond_to?(:succ)
96
+ options = {limit: :first}.merge(options)
97
+ opts = options.dup
98
+ opts[:limit] = 1 if options[:limit] == :first
99
+ key_hash = key_hash_or_number.transform_keys{|k| k.downcase.to_sym}
100
+ matching = if @ole_table.ListRows.Count < 150
101
+ matching_via_traversing(key_hash, opts)
102
+ else
103
+ matching_via_filter(key_hash, opts)
104
+ end
105
+ matching_listrows = matching.map{ |r| @row_class.new(r) }
106
+ options[:limit] == :first ? matching_listrows.first : matching_listrows
107
+ end
108
+
109
+ private
110
+
111
+ def matching_via_traversing(key_hash, opts)
112
+ encode_utf8 = ->(val) {val.respond_to?(:gsub) ? val.encode('utf-8') : val}
113
+ cn2i = column_names_to_index
114
+ max_matching_num = opts[:limit] || 65536
115
+ matching_rows = @ole_table.ListRows.lazy.select { |listrow|
116
+ rowvalues = listrow.Range.Value.first
117
+ key_hash.all?{ |key,val| encode_utf8.(rowvalues[cn2i[key]])==val}
118
+ }.take(max_matching_num).to_a
119
+ rescue
120
+ raise(TableError, "cannot find row with key #{key_hash}")
81
121
  end
82
122
 
123
+ def matching_via_filter(key_hash, opts)
124
+ ole_worksheet = self.Parent
125
+ ole_workbook = ole_worksheet.Parent
126
+ row_numbers = []
127
+ ole_workbook.retain_saved do
128
+ added_ole_worksheet = ole_workbook.Worksheets.Add
129
+ criteria = Table.new(added_ole_worksheet, "criteria", [2,1], 2, key_hash.keys.map{|s| s.to_s})
130
+ criteria[1].values = key_hash.values
131
+ self.Range.AdvancedFilter({
132
+ Action: XlFilterInPlace,
133
+ CriteriaRange: added_ole_worksheet.range([2..3,1..key_hash.length]).ole_range, Unique: false})
134
+ filtered_ole_range = self.DataBodyRange.SpecialCells(XlCellTypeVisible) rescue nil
135
+ ole_worksheet.ShowAllData
136
+ self.Range.AdvancedFilter({Action: XlFilterInPlace,
137
+ CriteriaRange: added_ole_worksheet.range([1,1]).ole_range, Unique: false})
138
+ ole_workbook.Parent.with_displayalerts(false){added_ole_worksheet.Delete}
139
+ if filtered_ole_range
140
+ filtered_ole_range.Areas.each do |area|
141
+ break if area.Rows.each do |row|
142
+ row_numbers << row.Row-position.first
143
+ break true if row_numbers.count == opts[:limit]
144
+ end
145
+ end
146
+ end
147
+ @ole_table = ole_worksheet.table(self.Name)
148
+ end
149
+ row_numbers
150
+ rescue
151
+ raise(TableError, "cannot find row with key #{key_hash}")
152
+ end
153
+
154
+ public
155
+
156
+ # @return [Integer] number of rows
157
+ def rows_number
158
+ @ole_table.ListRows.Count
159
+ end
160
+
161
+ # @return [Array] values of the table
162
+ def value
163
+ [column_names] + self.DataBodyRange.Value
164
+ end
165
+
83
166
  # @return [Array] a list of column names
84
167
  def column_names
85
- begin
86
- @ole_table.HeaderRowRange.Value.first
87
- rescue WIN32OLERuntimeError
88
- raise TableError, "could not determine column names"
89
- end
168
+ @ole_table.HeaderRowRange.Value.first.map{|v| v.encode('utf-8')}
169
+ rescue WIN32OLERuntimeError
170
+ raise TableError, "could not determine column names\n#{$!.message}"
171
+ end
172
+
173
+ # @return [Hash] pairs of column names and index
174
+ def column_names_to_index
175
+ header_row_values = @ole_table.HeaderRowRange.Value.first
176
+ header_row_values.map{|v| v.encode('utf-8').downcase.to_sym}.zip(0..header_row_values.size-1).to_h
177
+ rescue WIN32OLERuntimeError
178
+ raise TableError, "could not determine column names\n#{$!.message}"
90
179
  end
91
180
 
181
+
92
182
  # adds a row
93
183
  # @param [Integer] position of the new row
94
184
  # @param [Array] values of the column
95
185
  def add_row(position = nil, contents = nil)
96
- begin
97
- @ole_table.ListRows.Add(position)
98
- set_row_values(position, contents) if contents
99
- rescue WIN32OLERuntimeError
100
- raise TableError, ("could not add row" + (" at position #{position.inspect}" if position))
101
- end
186
+ @ole_table.ListRows.Add(position)
187
+ set_row_values(position, contents) if contents
188
+ rescue WIN32OLERuntimeError
189
+ raise TableError, ("could not add row" + (" at position #{position.inspect}" if position) + "\n#{$!.message}")
102
190
  end
103
191
 
104
192
  # adds a column
@@ -106,116 +194,96 @@ module RobustExcelOle
106
194
  # @param [Integer] position of the new column
107
195
  # @param [Array] values of the column
108
196
  def add_column(column_name = nil, position = nil, contents = nil)
109
- begin
110
- new_column = @ole_table.ListColumns.Add(position)
111
- new_column.Name = column_name if column_name
112
- set_column_values(column_name, contents) if contents
113
- rescue WIN32OLERuntimeError, TableError
114
- raise TableError, ("could not add column"+ ("at position #{position.inspect} with name #{column_name.inspect}" if position))
115
- end
197
+ new_column = @ole_table.ListColumns.Add(position)
198
+ new_column.Name = column_name if column_name
199
+ set_column_values(column_name, contents) if contents
200
+ rescue WIN32OLERuntimeError, TableError
201
+ raise TableError, ("could not add column"+ ("at position #{position.inspect} with name #{column_name.inspect}" if position) + "\n#{$!.message}")
116
202
  end
117
203
 
118
204
  # deletes a row
119
205
  # @param [Integer] position of the old row
120
206
  def delete_row(row_number) # :nodoc: #
121
- begin
122
- @ole_table.ListRows.Item(row_number).Delete
123
- rescue WIN32OLERuntimeError
124
- raise TableError, "could not delete row #{row_number.inspect}"
125
- end
207
+ @ole_table.ListRows.Item(row_number).Delete
208
+ rescue WIN32OLERuntimeError
209
+ raise TableError, "could not delete row #{row_number.inspect}\n#{$!.message}"
126
210
  end
127
211
 
128
212
  # deletes a column
129
213
  # @param [Variant] column number or column name
130
214
  def delete_column(column_number_or_name) # :nodoc: #
131
- begin
132
- @ole_table.ListColumns.Item(column_number_or_name).Delete
133
- rescue WIN32OLERuntimeError
134
- raise TableError, "could not delete column #{column_number_or_name.inspect}"
135
- end
215
+ @ole_table.ListColumns.Item(column_number_or_name).Delete
216
+ rescue WIN32OLERuntimeError
217
+ raise TableError, "could not delete column #{column_number_or_name.inspect}\n#{$!.message}"
136
218
  end
137
219
 
138
220
  # deletes the contents of a row
139
221
  # @param [Integer] row number
140
222
  def delete_row_values(row_number)
141
- begin
142
- @ole_table.ListRows.Item(row_number).Range.Value = [[].fill(nil,0..(@ole_table.ListColumns.Count-1))]
143
- nil
144
- rescue WIN32OLERuntimeError
145
- raise TableError, "could not delete contents of row #{row_number.inspect}"
146
- end
223
+ @ole_table.ListRows.Item(row_number).Range.Value = [[].fill(nil,0..(@ole_table.ListColumns.Count-1))]
224
+ nil
225
+ rescue WIN32OLERuntimeError
226
+ raise TableError, "could not delete contents of row #{row_number.inspect}\n#{$!.message}"
147
227
  end
148
228
 
149
229
  # deletes the contents of a column
150
230
  # @param [Variant] column number or column name
151
231
  def delete_column_values(column_number_or_name)
152
- begin
153
- column_name = @ole_table.ListColumns.Item(column_number_or_name).Range.Value.first
154
- @ole_table.ListColumns.Item(column_number_or_name).Range.Value = [column_name] + [].fill([nil],0..(@ole_table.ListRows.Count-1))
155
- nil
156
- rescue WIN32OLERuntimeError
157
- raise TableError, "could not delete contents of column #{column_number_or_name.inspect}"
158
- end
232
+ column_name = @ole_table.ListColumns.Item(column_number_or_name).Range.Value.first
233
+ @ole_table.ListColumns.Item(column_number_or_name).Range.Value = [column_name] + [].fill([nil],0..(@ole_table.ListRows.Count-1))
234
+ nil
235
+ rescue WIN32OLERuntimeError
236
+ raise TableError, "could not delete contents of column #{column_number_or_name.inspect}\n#{$!.message}"
159
237
  end
160
238
 
161
239
  # renames a row
162
240
  # @param [String] previous name or number of the column
163
241
  # @param [String] new name of the column
164
242
  def rename_column(name_or_number, new_name) # :nodoc: #
165
- begin
166
- @ole_table.ListColumns.Item(name_or_number).Name = new_name
167
- rescue
168
- raise TableError, "could not rename column #{name_or_number.inspect} to #{new_name.inspect}"
169
- end
243
+ @ole_table.ListColumns.Item(name_or_number).Name = new_name
244
+ rescue
245
+ raise TableError, "could not rename column #{name_or_number.inspect} to #{new_name.inspect}\n#{$!.message}"
170
246
  end
171
247
 
172
248
  # contents of a row
173
249
  # @param [Integer] row number
174
250
  # @return [Array] contents of a row
175
251
  def row_values(row_number)
176
- begin
177
- @ole_table.ListRows.Item(row_number).Range.Value.first
178
- rescue WIN32OLERuntimeError
179
- raise TableError, "could not read the values of row #{row_number.inspect}"
180
- end
252
+ @ole_table.ListRows.Item(row_number).Range.Value.first.map{|v| v.respond_to?(:gsub) ? v.encode('utf-8') : v}
253
+ rescue WIN32OLERuntimeError
254
+ raise TableError, "could not read the values of row #{row_number.inspect}\n#{$!.message}"
181
255
  end
182
256
 
183
257
  # sets the contents of a row
184
258
  # @param [Integer] row number
185
259
  # @param [Array] values of the row
186
260
  def set_row_values(row_number, values)
187
- begin
188
- updated_values = row_values(row_number)
189
- updated_values[0,values.length] = values
190
- @ole_table.ListRows.Item(row_number).Range.Value = [updated_values]
191
- values
192
- rescue WIN32OLERuntimeError
193
- raise TableError, "could not set the values of row #{row_number.inspect}"
194
- end
261
+ updated_values = row_values(row_number)
262
+ updated_values[0,values.length] = values
263
+ @ole_table.ListRows.Item(row_number).Range.Value = [updated_values]
264
+ values
265
+ rescue WIN32OLERuntimeError
266
+ raise TableError, "could not set the values of row #{row_number.inspect}\n#{$!.message}"
195
267
  end
196
268
 
197
269
  # @return [Array] contents of a column
198
270
  def column_values(column_number_or_name)
199
- begin
200
- @ole_table.ListColumns.Item(column_number_or_name).Range.Value[1,@ole_table.ListRows.Count].flatten
201
- rescue WIN32OLERuntimeError
202
- raise TableError, "could not read the values of column #{column_number_or_name.inspect}"
203
- end
271
+ @ole_table.ListColumns.Item(column_number_or_name).Range.Value[1,@ole_table.ListRows.Count].flatten.map{|v| v.respond_to?(:gsub) ? v.encode('utf-8') : v}
272
+ rescue WIN32OLERuntimeError
273
+ raise TableError, "could not read the values of column #{column_number_or_name.inspect}\n#{$!.message}"
204
274
  end
205
275
 
206
276
  # sets the contents of a column
207
277
  # @param [Integer] column name or column number
208
278
  # @param [Array] contents of the column
209
279
  def set_column_values(column_number_or_name, values)
210
- begin
211
- updated_values = column_values(column_number_or_name)
212
- updated_values[0,values.length] = values
213
- column_name = @ole_table.ListColumns.Item(column_number_or_name).Range.Value.first
214
- @ole_table.ListColumns.Item(column_number_or_name).Range.Value = column_name + updated_values.map{|v| [v]}
215
- values
216
- rescue WIN32OLERuntimeError
217
- raise TableError, "could not read the values of column #{column_number_or_name.inspect}"
218
- end
280
+ updated_values = column_values(column_number_or_name)
281
+ updated_values[0,values.length] = values
282
+ column_name = @ole_table.ListColumns.Item(column_number_or_name).Range.Value.first
283
+ @ole_table.ListColumns.Item(column_number_or_name).Range.Value = column_name + updated_values.map{|v| [v]}
284
+ values
285
+ rescue WIN32OLERuntimeError
286
+ raise TableError, "could not read the values of column #{column_number_or_name.inspect}\n#{$!.message}"
219
287
  end
220
288
 
221
289
  # deletes rows that have an empty contents
@@ -228,7 +296,7 @@ module RobustExcelOle
228
296
  if row.Range.Value == nil_array
229
297
  row.Delete
230
298
  else
231
- i = i+1
299
+ i += 1
232
300
  end
233
301
  end
234
302
  end
@@ -243,7 +311,7 @@ module RobustExcelOle
243
311
  if column.Range.Value[1..-1] == nil_array
244
312
  column.Delete
245
313
  else
246
- i = i+1
314
+ i += 1
247
315
  end
248
316
  end
249
317
  end
@@ -252,15 +320,14 @@ module RobustExcelOle
252
320
  # @param[Variant] value to find
253
321
  # @return [Array] win32ole cells containing the given value
254
322
  def find_cells(value)
255
- listrows = @ole_table.ListRows
256
- result = []
257
- (1..listrows.Count).each do |row_number|
258
- row_values(row_number).find_all_indices(value).each do |col_number|
259
- result << @ole_table.Application.Intersect(listrows.Item(row_number).Range,
260
- @ole_table.ListColumns.Item(col_number+1).Range).to_reo
323
+ encode_utf8 = ->(val) {val.respond_to?(:gsub) ? val.encode('utf-8') : val}
324
+ listrows = @ole_table.ListRows
325
+ listrows.map { |listrow|
326
+ listrow_range = listrow.Range
327
+ listrow_range.Value.first.map{ |v| encode_utf8.(v) }.find_all_indices(value).map do |col_number|
328
+ listrow_range.Cells(1,col_number+1).to_reo
261
329
  end
262
- end
263
- result
330
+ }.flatten
264
331
  end
265
332
 
266
333
  # sorts the rows of the list object according to the given column
@@ -274,6 +341,19 @@ module RobustExcelOle
274
341
  @ole_table.Sort.Apply
275
342
  end
276
343
 
344
+ # @return [Array] position of the first cell of the table
345
+ def position
346
+ first_cell = self.Range.Cells(1,1)
347
+ @position = [first_cell.Row, first_cell.Column]
348
+ end
349
+
350
+ def == other_table
351
+ other_table.is_a?(ListObject) &&
352
+ self.HeaderRowRange.Value == other_table.HeaderRowRange.Value &&
353
+ self.DataBodyRange.Value == other_table.DataBodyRange.Value
354
+ end
355
+
356
+
277
357
  # @private
278
358
  # returns true, if the list object responds to VBA methods, false otherwise
279
359
  def alive?
@@ -291,9 +371,9 @@ module RobustExcelOle
291
371
 
292
372
  # @private
293
373
  def inspect
294
- "#<ListObject:" + "#{@ole_table.Name}" +
374
+ "#<ListObject:#{@ole_table.Name}" +
295
375
  " #{@ole_table.ListRows.Count}x#{@ole_table.ListColumns.Count}" +
296
- " #{@ole_table.Parent.Name}" + " #{@ole_table.Parent.Parent.Name}" + ">"
376
+ " #{@ole_table.Parent.Name} #{@ole_table.Parent.Parent.Name}>"
297
377
  end
298
378
 
299
379
  include MethodHelpers
@@ -301,22 +381,19 @@ module RobustExcelOle
301
381
  private
302
382
 
303
383
  def method_missing(name, *args)
304
- if name.to_s[0,1] =~ /[A-Z]/
305
- if ::ERRORMESSAGE_JRUBY_BUG
306
- begin
307
- @ole_table.send(name, *args)
308
- rescue Java::OrgRacobCom::ComFailException
309
- raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
310
- end
311
- else
312
- begin
313
- @ole_table.send(name, *args)
314
- rescue NoMethodError
315
- raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
316
- end
384
+ super unless name.to_s[0,1] =~ /[A-Z]/
385
+ if ::ERRORMESSAGE_JRUBY_BUG
386
+ begin
387
+ @ole_table.send(name, *args)
388
+ rescue Java::OrgRacobCom::ComFailException
389
+ raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
317
390
  end
318
391
  else
319
- super
392
+ begin
393
+ @ole_table.send(name, *args)
394
+ rescue NoMethodError
395
+ raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
396
+ end
320
397
  end
321
398
  end
322
399
  end
@@ -325,10 +402,6 @@ module RobustExcelOle
325
402
  class TableError < WorksheetREOError
326
403
  end
327
404
 
328
- # @private
329
- class TableRowError < WorksheetREOError
330
- end
331
-
332
405
  Table = ListObject
333
406
 
334
407
  end