robust_excel_ole 1.31 → 1.32

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