robust_excel_ole 1.27 → 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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog +36 -2
  3. data/README.rdoc +121 -19
  4. data/___dummy_workbook.xls +0 -0
  5. data/benchmarking/creek_example.rb +1 -1
  6. data/benchmarking/reo_example.rb +1 -1
  7. data/benchmarking/reo_example1.rb +1 -1
  8. data/benchmarking/reo_example2.rb +1 -1
  9. data/benchmarking/roo_example.rb +1 -1
  10. data/benchmarking/simple_xlsx_reader_example.rb +1 -1
  11. data/benchmarking/spreadsheet_example.rb +1 -1
  12. data/bin/jreo +19 -0
  13. data/bin/reo +19 -0
  14. data/docs/README_excel.rdoc +16 -24
  15. data/docs/README_listobjects.rdoc +176 -0
  16. data/docs/README_open.rdoc +20 -16
  17. data/docs/README_ranges.rdoc +72 -55
  18. data/docs/README_save_close.rdoc +3 -3
  19. data/docs/README_sheet.rdoc +19 -20
  20. data/examples/example_ruby_library.rb +2 -2
  21. data/examples/introductory_examples/example_open.rb +11 -0
  22. data/examples/introductory_examples/example_range.rb +2 -2
  23. data/examples/modifying_sheets/example_access_sheets_and_cells.rb +6 -6
  24. data/examples/modifying_sheets/example_add_names.rb +1 -1
  25. data/examples/modifying_sheets/example_concating.rb +1 -1
  26. data/examples/modifying_sheets/example_copying.rb +2 -2
  27. data/examples/modifying_sheets/example_listobjects.rb +86 -0
  28. data/examples/modifying_sheets/example_naming.rb +1 -1
  29. data/examples/modifying_sheets/example_ranges.rb +1 -1
  30. data/examples/open_save_close/example_control_to_excel.rb +1 -1
  31. data/examples/open_save_close/example_if_obstructed_closeifsaved.rb +1 -1
  32. data/examples/open_save_close/example_if_obstructed_save.rb +3 -3
  33. data/examples/open_save_close/example_if_unsaved_accept.rb +1 -1
  34. data/examples/open_save_close/example_if_unsaved_forget.rb +3 -3
  35. data/examples/open_save_close/example_if_unsaved_forget_more.rb +4 -4
  36. data/examples/open_save_close/example_read_only.rb +1 -1
  37. data/examples/open_save_close/example_simple.rb +1 -1
  38. data/examples/open_save_close/example_unobtrusively.rb +3 -3
  39. data/lib/robust_excel_ole.rb +19 -16
  40. data/lib/robust_excel_ole/address_tool.rb +54 -44
  41. data/lib/robust_excel_ole/base.rb +9 -6
  42. data/lib/robust_excel_ole/bookstore.rb +3 -17
  43. data/lib/robust_excel_ole/cell.rb +17 -22
  44. data/lib/robust_excel_ole/cygwin.rb +2 -0
  45. data/lib/robust_excel_ole/excel.rb +136 -201
  46. data/lib/robust_excel_ole/general.rb +249 -238
  47. data/lib/robust_excel_ole/list_object.rb +186 -210
  48. data/lib/robust_excel_ole/list_row.rb +155 -0
  49. data/lib/robust_excel_ole/range.rb +130 -94
  50. data/lib/robust_excel_ole/range_owners.rb +54 -135
  51. data/lib/robust_excel_ole/version.rb +1 -1
  52. data/lib/robust_excel_ole/workbook.rb +230 -196
  53. data/lib/robust_excel_ole/worksheet.rb +254 -133
  54. data/lib/spec_helper.rb +1 -1
  55. data/robust_excel_ole.gemspec +4 -3
  56. data/spec/address_tool_spec.rb +2 -2
  57. data/spec/base_spec.rb +19 -17
  58. data/spec/bookstore_spec.rb +3 -4
  59. data/spec/cell_spec.rb +10 -10
  60. data/spec/cygwin_spec.rb +1 -1
  61. data/spec/data/more_data/workbook.xls +0 -0
  62. data/spec/excel_spec.rb +133 -86
  63. data/spec/general_spec.rb +79 -18
  64. data/spec/list_object_spec.rb +259 -81
  65. data/spec/list_row_spec.rb +218 -0
  66. data/spec/range_spec.rb +75 -41
  67. data/spec/spec_helper.rb +16 -2
  68. data/spec/workbook_spec.rb +87 -46
  69. data/spec/workbook_specs/workbook_all_spec.rb +9 -28
  70. data/spec/workbook_specs/workbook_close_spec.rb +1 -1
  71. data/spec/workbook_specs/workbook_misc_spec.rb +52 -45
  72. data/spec/workbook_specs/workbook_open_spec.rb +103 -50
  73. data/spec/workbook_specs/workbook_save_spec.rb +22 -23
  74. data/spec/workbook_specs/workbook_sheet_spec.rb +4 -4
  75. data/spec/workbook_specs/workbook_subclass_spec.rb +1 -1
  76. data/spec/workbook_specs/workbook_unobtr_spec.rb +553 -395
  77. data/spec/worksheet_spec.rb +544 -308
  78. metadata +38 -3
  79. data/lib/reo_console.rb +0 -42
@@ -1,10 +1,9 @@
1
1
  # -*- coding: utf-8 -*-
2
-
3
2
  module RobustExcelOle
4
3
 
5
- class ListRow
6
- end
7
-
4
+ using ToReoRefinement
5
+ using FindAllIndicesRefinement
6
+
8
7
  # This class essentially wraps a Win32Ole ListObject.
9
8
  # You can apply all VBA methods (starting with a capital letter)
10
9
  # that you would apply for a ListObject.
@@ -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,146 +56,137 @@ 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
 
62
-
63
63
  ole_table = @ole_table
64
+
64
65
  @row_class = Class.new(ListRow) do
65
66
 
66
67
  @@ole_table = ole_table
67
68
 
68
- def initialize(row_number)
69
- @ole_listrow = @@ole_table.ListRows.Item(row_number)
70
- end
71
-
72
- # returns the value of the cell with given column name or number
73
- # @param [Variant] column number or column name
74
- # @return [Variant] value of the cell
75
- def [] column_number_or_name
76
- begin
77
- ole_cell = @@ole_table.Application.Intersect(
78
- @ole_listrow.Range, @@ole_table.ListColumns.Item(column_number_or_name).Range)
79
- ole_cell.Value
80
- rescue WIN32OLERuntimeError
81
- raise TableRowError, "could not determine the value at column #{column_number_or_name}"
82
- end
83
- end
84
-
85
-
86
- def []=(column_number_or_name, value)
87
- begin
88
- ole_cell = @@ole_table.Application.Intersect(
89
- @ole_listrow.Range, @@ole_table.ListColumns.Item(column_number_or_name).Range)
90
- ole_cell.Value = value
91
- rescue WIN32OLERuntimeError
92
- raise TableRowError, "could not assign value #{value.inspect} to cell at column #{column_number_or_name}"
93
- end
69
+ def ole_table
70
+ @@ole_table
94
71
  end
72
+
73
+ end
95
74
 
96
- # values of the row
97
- # @return [Array] values of the row
98
- def values
99
- begin
100
- @ole_listrow.Range.Value.first
101
- rescue WIN32OLERuntimeError
102
- raise TableError, "could not read values"
103
- end
104
- end
75
+ end
105
76
 
106
- # sets the values of the row
107
- # @param [Array] values of the row
108
- def set_values values
109
- begin
110
- updated_values = self.values
111
- updated_values[0,values.length] = values
112
- @ole_listrow.Range.Value = [updated_values]
113
- values
114
- rescue WIN32OLERuntimeError
115
- raise TableError, "could not set values #{values.inspect}"
116
- end
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)
117
82
  end
83
+ else
84
+ to_enum(:each).lazy
85
+ end
86
+ end
118
87
 
119
- # deletes the values of the row
120
- def delete_values
121
- begin
122
- @ole_listrow.Range.Value = [[].fill(nil,0..(@@ole_table.ListColumns.Count)-1)]
123
- nil
124
- rescue WIN32OLERuntimeError
125
- raise TableError, "could not delete values"
126
- end
127
- end
88
+ # accesses a table row object
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
128
108
 
129
- def method_missing(name, *args)
130
- name_str = name.to_s
131
- core_name = name_str[-1]!='=' ? name_str : name_str[0..-2]
132
- column_names = @@ole_table.HeaderRowRange.Value.first
133
- column_name = column_names.find do |c|
134
- c == core_name ||
135
- c.gsub(/\W/,'') == core_name ||
136
- c.replace_umlauts == core_name ||
137
- c.gsub(/\W/,'').replace_umlauts == core_name ||
138
- c.gsub(/\W/,'').replace_umlauts.underscore.gsub(/[^[\w\d]]/, '_').delete_multiple_underscores == core_name
139
- end
140
- if column_name
141
- method_name = core_name.gsub(/\W/,'') + (name_str[-1]!='=' ? "" : "=")
142
- define_and_call_method(column_name,method_name,*args)
143
- else
144
- super(name, *args)
145
- end
146
- end
109
+ private
147
110
 
148
- private
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}")
121
+ end
149
122
 
150
- def define_and_call_method(column_name,method_name,*args)
151
- ole_cell = @@ole_table.Application.Intersect(
152
- @ole_listrow.Range, @@ole_table.ListColumns.Item(column_name).Range)
153
- define_getting_setting_method(ole_cell,method_name)
154
- self.send(method_name, *args)
155
- end
156
-
157
- def define_getting_setting_method(ole_cell,name)
158
- if name[-1] != '='
159
- self.class.define_method(name) do
160
- ole_cell.Value
161
- end
162
- else
163
- self.class.define_method(name) do |value|
164
- ole_cell.Value = value
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]
165
144
  end
166
145
  end
167
- end
168
- 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
169
153
 
170
- # accesses a table row object
171
- # @param [Integer] a row number (>= 1)
172
- # @return [ListRow] a object of dynamically constructed class with superclass ListRow
173
- def [] row_number
174
- @row_class.new(row_number)
175
- end
154
+ public
176
155
 
156
+ # @return [Integer] number of rows
157
+ def rows_number
158
+ @ole_table.ListRows.Count
177
159
  end
178
160
 
161
+ # @return [Array] values of the table
162
+ def value
163
+ [column_names] + self.DataBodyRange.Value
164
+ end
165
+
179
166
  # @return [Array] a list of column names
180
167
  def column_names
181
- begin
182
- @ole_table.HeaderRowRange.Value.first
183
- rescue WIN32OLERuntimeError
184
- raise TableError, "could not determine column names"
185
- 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}"
186
171
  end
187
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}"
179
+ end
180
+
181
+
188
182
  # adds a row
189
183
  # @param [Integer] position of the new row
190
184
  # @param [Array] values of the column
191
185
  def add_row(position = nil, contents = nil)
192
- begin
193
- @ole_table.ListRows.Add(position)
194
- set_row_values(position, contents) if contents
195
- rescue WIN32OLERuntimeError
196
- raise TableError, ("could not add row" + (" at position #{position.inspect}" if position))
197
- 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}")
198
190
  end
199
191
 
200
192
  # adds a column
@@ -202,116 +194,96 @@ module RobustExcelOle
202
194
  # @param [Integer] position of the new column
203
195
  # @param [Array] values of the column
204
196
  def add_column(column_name = nil, position = nil, contents = nil)
205
- begin
206
- new_column = @ole_table.ListColumns.Add(position)
207
- new_column.Name = column_name if column_name
208
- set_column_values(column_name, contents) if contents
209
- rescue WIN32OLERuntimeError, TableError
210
- raise TableError, ("could not add column"+ ("at position #{position.inspect} with name #{column_name.inspect}" if position))
211
- 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}")
212
202
  end
213
203
 
214
204
  # deletes a row
215
205
  # @param [Integer] position of the old row
216
- def delete_row(row_number)
217
- begin
218
- @ole_table.ListRows.Item(row_number).Delete
219
- rescue WIN32OLERuntimeError
220
- raise TableError, "could not delete row #{row_number.inspect}"
221
- end
206
+ def delete_row(row_number) # :nodoc: #
207
+ @ole_table.ListRows.Item(row_number).Delete
208
+ rescue WIN32OLERuntimeError
209
+ raise TableError, "could not delete row #{row_number.inspect}\n#{$!.message}"
222
210
  end
223
211
 
224
212
  # deletes a column
225
213
  # @param [Variant] column number or column name
226
- def delete_column(column_number_or_name)
227
- begin
228
- @ole_table.ListColumns.Item(column_number_or_name).Delete
229
- rescue WIN32OLERuntimeError
230
- raise TableError, "could not delete column #{column_number_or_name.inspect}"
231
- end
214
+ def delete_column(column_number_or_name) # :nodoc: #
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}"
232
218
  end
233
219
 
234
220
  # deletes the contents of a row
235
221
  # @param [Integer] row number
236
222
  def delete_row_values(row_number)
237
- begin
238
- @ole_table.ListRows.Item(row_number).Range.Value = [[].fill(nil,0..(@ole_table.ListColumns.Count-1))]
239
- nil
240
- rescue WIN32OLERuntimeError
241
- raise TableError, "could not delete contents of row #{row_number.inspect}"
242
- 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}"
243
227
  end
244
228
 
245
229
  # deletes the contents of a column
246
230
  # @param [Variant] column number or column name
247
231
  def delete_column_values(column_number_or_name)
248
- begin
249
- column_name = @ole_table.ListColumns.Item(column_number_or_name).Range.Value.first
250
- @ole_table.ListColumns.Item(column_number_or_name).Range.Value = [column_name] + [].fill([nil],0..(@ole_table.ListRows.Count-1))
251
- nil
252
- rescue WIN32OLERuntimeError
253
- raise TableError, "could not delete contents of column #{column_number_or_name.inspect}"
254
- 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}"
255
237
  end
256
238
 
257
239
  # renames a row
258
240
  # @param [String] previous name or number of the column
259
241
  # @param [String] new name of the column
260
- def rename_column(name_or_number, new_name)
261
- begin
262
- @ole_table.ListColumns.Item(name_or_number).Name = new_name
263
- rescue
264
- raise TableError, "could not rename column #{name_or_number.inspect} to #{new_name.inspect}"
265
- end
242
+ def rename_column(name_or_number, new_name) # :nodoc: #
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}"
266
246
  end
267
247
 
268
248
  # contents of a row
269
249
  # @param [Integer] row number
270
250
  # @return [Array] contents of a row
271
251
  def row_values(row_number)
272
- begin
273
- @ole_table.ListRows.Item(row_number).Range.Value.first
274
- rescue WIN32OLERuntimeError
275
- raise TableError, "could not read the values of row #{row_number.inspect}"
276
- 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}"
277
255
  end
278
256
 
279
257
  # sets the contents of a row
280
258
  # @param [Integer] row number
281
259
  # @param [Array] values of the row
282
260
  def set_row_values(row_number, values)
283
- begin
284
- updated_values = row_values(row_number)
285
- updated_values[0,values.length] = values
286
- @ole_table.ListRows.Item(row_number).Range.Value = [updated_values]
287
- values
288
- rescue WIN32OLERuntimeError
289
- raise TableError, "could not set the values of row #{row_number.inspect}"
290
- 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}"
291
267
  end
292
268
 
293
269
  # @return [Array] contents of a column
294
270
  def column_values(column_number_or_name)
295
- begin
296
- @ole_table.ListColumns.Item(column_number_or_name).Range.Value[1,@ole_table.ListRows.Count].flatten
297
- rescue WIN32OLERuntimeError
298
- raise TableError, "could not read the values of column #{column_number_or_name.inspect}"
299
- 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}"
300
274
  end
301
275
 
302
276
  # sets the contents of a column
303
277
  # @param [Integer] column name or column number
304
278
  # @param [Array] contents of the column
305
279
  def set_column_values(column_number_or_name, values)
306
- begin
307
- updated_values = column_values(column_number_or_name)
308
- updated_values[0,values.length] = values
309
- column_name = @ole_table.ListColumns.Item(column_number_or_name).Range.Value.first
310
- @ole_table.ListColumns.Item(column_number_or_name).Range.Value = column_name + updated_values.map{|v| [v]}
311
- values
312
- rescue WIN32OLERuntimeError
313
- raise TableError, "could not read the values of column #{column_number_or_name.inspect}"
314
- 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}"
315
287
  end
316
288
 
317
289
  # deletes rows that have an empty contents
@@ -324,7 +296,7 @@ module RobustExcelOle
324
296
  if row.Range.Value == nil_array
325
297
  row.Delete
326
298
  else
327
- i = i+1
299
+ i += 1
328
300
  end
329
301
  end
330
302
  end
@@ -339,7 +311,7 @@ module RobustExcelOle
339
311
  if column.Range.Value[1..-1] == nil_array
340
312
  column.Delete
341
313
  else
342
- i = i+1
314
+ i += 1
343
315
  end
344
316
  end
345
317
  end
@@ -348,15 +320,14 @@ module RobustExcelOle
348
320
  # @param[Variant] value to find
349
321
  # @return [Array] win32ole cells containing the given value
350
322
  def find_cells(value)
351
- listrows = @ole_table.ListRows
352
- result = []
353
- (1..listrows.Count).each do |row_number|
354
- row_values(row_number).find_each_index(value).each do |col_number|
355
- result << @ole_table.Application.Intersect(listrows.Item(row_number).Range,
356
- @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
357
329
  end
358
- end
359
- result
330
+ }.flatten
360
331
  end
361
332
 
362
333
  # sorts the rows of the list object according to the given column
@@ -370,6 +341,19 @@ module RobustExcelOle
370
341
  @ole_table.Sort.Apply
371
342
  end
372
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
+
373
357
  # @private
374
358
  # returns true, if the list object responds to VBA methods, false otherwise
375
359
  def alive?
@@ -387,9 +371,9 @@ module RobustExcelOle
387
371
 
388
372
  # @private
389
373
  def inspect
390
- "#<ListObject:" + "#{@ole_table.Name}" +
374
+ "#<ListObject:#{@ole_table.Name}" +
391
375
  " #{@ole_table.ListRows.Count}x#{@ole_table.ListColumns.Count}" +
392
- " #{@ole_table.Parent.Name}" + " #{@ole_table.Parent.Parent.Name}" + ">"
376
+ " #{@ole_table.Parent.Name} #{@ole_table.Parent.Parent.Name}>"
393
377
  end
394
378
 
395
379
  include MethodHelpers
@@ -397,22 +381,19 @@ module RobustExcelOle
397
381
  private
398
382
 
399
383
  def method_missing(name, *args)
400
- if name.to_s[0,1] =~ /[A-Z]/
401
- if ::ERRORMESSAGE_JRUBY_BUG
402
- begin
403
- @ole_table.send(name, *args)
404
- rescue Java::OrgRacobCom::ComFailException
405
- raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
406
- end
407
- else
408
- begin
409
- @ole_table.send(name, *args)
410
- rescue NoMethodError
411
- raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
412
- 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}"
413
390
  end
414
391
  else
415
- super
392
+ begin
393
+ @ole_table.send(name, *args)
394
+ rescue NoMethodError
395
+ raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
396
+ end
416
397
  end
417
398
  end
418
399
  end
@@ -421,11 +402,6 @@ module RobustExcelOle
421
402
  class TableError < WorksheetREOError
422
403
  end
423
404
 
424
- # @private
425
- class TableRowError < WorksheetREOError
426
- end
427
-
428
405
  Table = ListObject
429
- TableRow = ListRow
430
406
 
431
407
  end