robust_excel_ole 1.19.13 → 1.22.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69409f50b60be33d067f1e3ab2e53af4c467d0cf9a685f2ea6d37ce0edc92da0
4
- data.tar.gz: efd36f474dfde3e684d65addd41bc02c2b4582ad48caccb28d4013ee59baec1e
3
+ metadata.gz: d60404c6338fa948418c3381d8f83b80147861694abd07956009fd332c923bc7
4
+ data.tar.gz: 1f50eae2f0c909a712563dd6ae2df38e45d9924ece79f1915987346bc3b6d1e1
5
5
  SHA512:
6
- metadata.gz: bc7549286d189d53ba13e2c5c90bc94fe137777828665d8380c32508344f03a500cacecd10d6ec9218523202676214432a6f18f77abfb485a53eb3376ff4133e
7
- data.tar.gz: 507f721508cfbe119637f6f8fc49f13ec4e02cd020b4409a2162674062cc6aedc9301e20ab821733c05fd858c34c15019281a302b2e78d2c3d80dcb664787926
6
+ metadata.gz: c5067ff3c58c8d9e83715db4cdba7ffd18305b27e77eb8e9b420f91fdffba784c6545453419e23af643372d1e16b2d328ef839ae7fb75b927611f235d2deab2c
7
+ data.tar.gz: 78080f4450242c98edb442d8a519ae2a0713fb3bedaf5fdb7500e8b9430856dace574f79d98bc45faedc72b0267bbf11e068d71a3e7dab2121a22c6188e70792
data/Changelog CHANGED
@@ -1,8 +1,14 @@
1
1
  # Change Log
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## [1.22] 2020-10-08
4
5
 
5
- ## [1.20] 2020-14-07
6
+ ## [1.21] 2020-05-08
7
+
8
+ ### Added
9
+ - Range#rows, columns
10
+
11
+ ## [1.20] 2020-23-07
6
12
 
7
13
  ### Changed
8
14
  - using pry in console
@@ -11,7 +11,7 @@ It supports handling workbooks across Excel instances by keeping track of workbo
11
11
 
12
12
  Library references are supported.
13
13
 
14
- RobustExcelOle works by sending VBA methods via Win32OlE.
14
+ RobustExcelOle works by sending VBA methods via WIN32OlE.
15
15
  Therefore, it runs on Windows only.
16
16
 
17
17
  == Features
@@ -27,7 +27,6 @@ RobustExcelOle supports
27
27
  - availability of all VBA methods
28
28
  - availability of the Excel constants (in form if Ruby constants: Excel constant.capitalize)
29
29
  - all standard Excel file formats (.xlsx, .xls, .xlsm)
30
- - list objects
31
30
  - reopening workbooks after closing them
32
31
  - unobtrusively opening workbooks, i.e. opening and processing workbooks
33
32
  while preserving their status, e.g. saved, readonly
@@ -77,10 +76,10 @@ Let's open a workbook.
77
76
 
78
77
  workbook = Workbook.open 'spec/data/workbook.xls'
79
78
 
80
- Now we have a Workbook object that wraps a WIN32OLE object. That is, you can send any WIN32OLE (VBA) method to it. See
79
+ Now we have a Workbook object that wraps a WIN32OLE object. That is, we can send any WIN32OLE (VBA) method to it. See
81
80
  https://docs.microsoft.com/en-us/office/vba/api/excel.workbook#methods.
82
81
 
83
- For example, you can determine the name of the workbook.
82
+ For example, we can determine the name of the workbook.
84
83
 
85
84
  workbook.Name
86
85
  # => "workbook.xls"
@@ -122,11 +121,11 @@ RobustExcelOle allows unobtrusively reading and modifying workbooks, i.e. access
122
121
  # do something
123
122
  end
124
123
 
125
- You can also create a new, empty workbook.
124
+ We can also create a new, empty workbook.
126
125
 
127
126
  Workbook.create('spec/data/new_workbook.xls', :visible => true)
128
127
 
129
- Moreover, you can open the workbook using a block, similar to, e.g., +File.open+.
128
+ Moreover, we can open the workbook using a block, similar to, e.g., +File.open+.
130
129
 
131
130
  Workbook.open('spec/data/workbook.xls') do |workbook|
132
131
  # do something
@@ -318,33 +317,6 @@ and set another value to that range.
318
317
 
319
318
  For more details about reading and writing contents of cells and ranges see {README_ranges}[https://github.com/Thomas008/robust_excel_ole/blob/master/docs/README_ranges.rdoc]
320
319
 
321
- === Reading and writing list objects
322
-
323
- We can define a list object (or table) from scratch.
324
-
325
- table = ListObject.new(worksheet, "table 1", [1,1], 3,["Person","Amount"])
326
-
327
- This command creates a list object in worksheet named "table 1", with upper left corner at position [1,1] (first cell), with 3 rows and the columns "Person" and "Amount".
328
-
329
- A row in this table can be accessed with help of [], e.g.
330
-
331
- table[1]
332
-
333
- Now we can set and get the value of a cell of the table, e.g.
334
-
335
- table[1].person = "John"
336
- table[1].person
337
- # => "John"
338
-
339
- Likewise we can get a table with help of an existing Win32OlE table.
340
-
341
- ole_listobject = worksheet.ListObjects.Item("Table 1")
342
- table = ListObject.new(ole_listobject)
343
-
344
- r4 = t4[1]
345
- r4.person
346
- # =>
347
- r.person = "John"
348
320
 
349
321
  === More things
350
322
 
data/bin/jreo CHANGED
@@ -7,18 +7,21 @@ require 'robust_excel_ole'
7
7
  include REO
8
8
  include General
9
9
 
10
- puts 'REO console started'
11
- puts
12
-
13
10
  # some pry configuration
14
11
  Pry.config.windows_console_warning = false
15
- Pry.config.history.should_save = true
12
+ #Pry.config.history_save = true
16
13
  Pry.config.color = false
17
14
  #Pry.editor = 'notepad' # 'subl', 'vi'
18
15
  #Pry.config.prompt =
19
- # [
20
- # ->(_obj, _nest_level, _) { ">> " },
21
- # ->(*) { " " }
22
- # ]
16
+ #[
17
+ #->(_obj, _nest_level, _) { ">> " },
18
+ #->(*) { " " }
19
+ #]
23
20
 
24
- pry
21
+ hooks = Pry::Hooks.new
22
+
23
+ hooks.add_hook :when_started, :hook12 do
24
+ puts 'REO console started'
25
+ puts
26
+ end
27
+ Pry.start(nil, hooks: hooks)
data/bin/reo CHANGED
@@ -7,18 +7,21 @@ require 'robust_excel_ole'
7
7
  include REO
8
8
  include General
9
9
 
10
- puts 'REO console started'
11
- puts
12
-
13
10
  # some pry configuration
14
11
  Pry.config.windows_console_warning = false
15
- Pry.config.history.should_save = true
12
+ #Pry.config.history_save = true
16
13
  Pry.config.color = false
17
14
  #Pry.editor = 'notepad' # 'subl', 'vi'
18
15
  #Pry.config.prompt =
19
- # [
20
- # ->(_obj, _nest_level, _) { ">> " },
21
- # ->(*) { " " }
22
- # ]
16
+ #[
17
+ #->(_obj, _nest_level, _) { ">> " },
18
+ #->(*) { " " }
19
+ #]
23
20
 
24
- pry
21
+ hooks = Pry::Hooks.new
22
+
23
+ hooks.add_hook :when_started, :hook12 do
24
+ puts 'REO console started'
25
+ puts
26
+ end
27
+ Pry.start(nil, hooks: hooks)
data/jreo.bat CHANGED
@@ -1,3 +1,3 @@
1
1
  @echo off
2
2
 
3
- jirb -f -r ./lib/robust_excel_ole -r ./lib/jreo_console.rb
3
+ jruby lib/reo_console.rb
@@ -1,21 +1,24 @@
1
1
  require 'pry'
2
- require 'robust_excel_ole'
2
+ require '../robust_excel_ole/lib/robust_excel_ole'
3
3
 
4
4
  include REO
5
5
  include General
6
6
 
7
- puts 'REO console started'
8
- puts
9
-
10
7
  # some pry configuration
11
8
  Pry.config.windows_console_warning = false
12
- Pry.config.history.should_save = true
9
+ #Pry.config.history_save = true
13
10
  Pry.config.color = false
14
11
  #Pry.editor = 'notepad' # 'subl', 'vi'
15
12
  #Pry.config.prompt =
16
- # [
17
- # ->(_obj, _nest_level, _) { ">> " },
18
- # ->(*) { " " }
19
- # ]
13
+ #[
14
+ #->(_obj, _nest_level, _) { ">> " },
15
+ #->(*) { " " }
16
+ #]
20
17
 
21
- pry
18
+ hooks = Pry::Hooks.new
19
+
20
+ hooks.add_hook :when_started, :hook12 do
21
+ puts 'REO console started'
22
+ puts
23
+ end
24
+ Pry.start(nil, hooks: hooks)
@@ -2,6 +2,7 @@
2
2
 
3
3
  module RobustExcelOle
4
4
 
5
+ # @private
5
6
  class Bookstore < Base
6
7
 
7
8
  def initialize
@@ -31,7 +32,7 @@ module RobustExcelOle
31
32
  def fetch(filename, options = { :prefer_writable => true })
32
33
  return nil unless filename
33
34
  filename = General.absolute_path(filename)
34
- filename_key = General.canonize(filename)
35
+ filename_key = General.canonize(filename).downcase
35
36
  weakref_books = @filename2books[filename_key]
36
37
  return nil if weakref_books.nil? || weakref_books.empty?
37
38
 
@@ -69,9 +70,9 @@ module RobustExcelOle
69
70
  # stores a workbook
70
71
  # @param [Workbook] book a given book
71
72
  def store(book)
72
- filename_key = General.canonize(book.filename)
73
+ filename_key = General.canonize(book.filename).downcase
73
74
  if book.stored_filename
74
- old_filename_key = General.canonize(book.stored_filename)
75
+ old_filename_key = General.canonize(book.stored_filename).downcase
75
76
  # deletes the weak reference to the book
76
77
  @filename2books[old_filename_key].delete(book)
77
78
  end
@@ -20,10 +20,21 @@ module RobustExcelOle
20
20
  self.Value = value
21
21
  end
22
22
 
23
+ # @private
23
24
  def ole_cell
24
25
  @ole_range = @ole_range.MergeArea.Item(1,1) if @ole_range.MergeCells
25
26
  end
26
27
 
28
+ # @private
29
+ def to_s
30
+ "#<Cell:" + " (#{@ole_range.Row},#{@ole_range.Column})" + ">"
31
+ end
32
+
33
+ # @private
34
+ def inspect
35
+ self.to_s[0..-2] + " #{@ole_range.Parent.Name}" + ">"
36
+ end
37
+
27
38
  private
28
39
 
29
40
  # @private
@@ -701,7 +701,7 @@ module RobustExcelOle
701
701
  # @param [String] name the name of the range
702
702
  # @param [Variant] value the contents of the range
703
703
  def []=(name, value)
704
- set_namevalue_glob(name, value, :color => 42)
704
+ set_namevalue_glob(name, value)
705
705
  end
706
706
 
707
707
  # @private
@@ -11,29 +11,36 @@ module General
11
11
  ::RANGES_JRUBY_BUG = IS_JRUBY_PLATFORM && true
12
12
 
13
13
  @private
14
- def network2hostnamesharepath(filename)
15
- network = WIN32OLE.new('WScript.Network')
16
- drives = network.enumnetworkdrives
17
- drive_letter, filename_after_drive_letter = filename.split(':')
18
- # if filename starts with a drive letter not c and this drive exists,
19
- # then determine the corresponding host_share_path
20
- default_drive = File.absolute_path(".")[0]
21
- if drive_letter != default_drive && drive_letter != filename
22
- for i in 0 .. drives.Count-1
23
- next if i % 2 == 1
24
- if drives.Item(i).gsub(':','') == drive_letter
25
- hostname_share = drives.Item(i+1) #.gsub('\\','/').gsub('//','')
26
- break
27
- end
14
+ NetworkDrive = Struct.new(:drive_letter, :network_name) do
15
+
16
+ def self.get_all(drives)
17
+ ndrives = []
18
+ count = drives.Count
19
+ (0..(count - 1)).step(2) do |i|
20
+ ndrives << NetworkDrive.new( drives.Item(i), drives.Item(i + 1).tr('\\','/'))
28
21
  end
29
- hostname_share + filename_after_drive_letter if hostname_share
30
- else
31
- return filename
22
+ ndrives
32
23
  end
24
+
33
25
  end
34
26
 
27
+ @private
28
+ def hostnameshare2networkpath(filename)
29
+ return filename unless filename[0,2] == "//"
30
+ network = WIN32OLE.new('WScript.Network')
31
+ drives = network.enumnetworkdrives
32
+ network_drives = NetworkDrive.get_all(drives)
33
+ f_c = filename.dup
34
+ network_drive = network_drives.find do |d|
35
+ e = f_c.sub!(d.network_name,d.drive_letter)
36
+ return e if e
37
+ end
38
+ filename
39
+ end
40
+
35
41
  # @private
36
- def absolute_path(file)
42
+ def absolute_path(file)
43
+ return file if file[0,2] == "//"
37
44
  file[0,2] = './' if ::EXPANDPATH_JRUBY_BUG && file =~ /[A-Z]:[^\/]/
38
45
  file = File.expand_path(file)
39
46
  file = RobustExcelOle::Cygwin.cygpath('-w', file) if RUBY_PLATFORM =~ /cygwin/
@@ -43,12 +50,13 @@ module General
43
50
  # @private
44
51
  def canonize(filename)
45
52
  raise TypeREOError, "No string given to canonize, but #{filename.inspect}" unless filename.is_a?(String)
46
- filename = network2hostnamesharepath(filename)
47
- normalize(filename).downcase
53
+ filename = hostnameshare2networkpath(filename)
54
+ normalize(filename) if filename
48
55
  end
49
56
 
50
57
  # @private
51
- def normalize(path)
58
+ def normalize(path)
59
+ return unless path
52
60
  path = path.gsub('/./', '/') + '/'
53
61
  path = path.gsub(/[\/\\]+/, '/')
54
62
  nil while path.gsub!(/(\/|^)(?!\.\.?)([^\/]+)\/\.\.\//, '\1')
@@ -90,9 +98,19 @@ class Array
90
98
  end
91
99
  end
92
100
 
101
+ def find_each_index find
102
+ found, index, q = -1, -1, []
103
+ while found
104
+ found = self[index+1..-1].index(find)
105
+ if found
106
+ index = index + found + 1
107
+ q << index
108
+ end
109
+ end
110
+ q
111
+ end
93
112
  end
94
113
 
95
-
96
114
  # @private
97
115
  class WIN32OLE
98
116
 
@@ -103,7 +121,7 @@ class WIN32OLE
103
121
  class2method = [
104
122
  {Excel => :Hwnd},
105
123
  {Workbook => :FullName},
106
- {Worksheet => :Copy},
124
+ {Worksheet => :UsedRange},
107
125
  {RobustExcelOle::Range => :Row},
108
126
  {ListObject => :ListRows}
109
127
  ]
@@ -113,7 +131,7 @@ class WIN32OLE
113
131
  begin
114
132
  self.send(method)
115
133
  if classname == RobustExcelOle::Range && self.Rows.Count == 1 && self.Columns.Count == 1
116
- return Cell.new(self)
134
+ return Cell.new(self, self.Parent)
117
135
  else
118
136
  return classname.new(self)
119
137
  end
@@ -164,6 +182,31 @@ class ::String
164
182
  word
165
183
  end
166
184
 
185
+ def delete_multiple_underscores
186
+ word = self
187
+ while word.index('__') do
188
+ word.gsub!('__','_')
189
+ end
190
+ word
191
+ end
192
+
193
+ def replace_umlauts
194
+ word = self
195
+ word.gsub!('ä','ae')
196
+ word.gsub!('Ä','Ae')
197
+ word.gsub!('ö','oe')
198
+ word.gsub!('Ö','Oe')
199
+ word.gsub!('ü','ue')
200
+ word.gsub!('Ü','Ue')
201
+ #word.gsub!(/\x84/,'ae')
202
+ #word.gsub!(/\x8E/,'Ae')
203
+ #word.gsub!(/\x94/,'oe')
204
+ #word.gsub!(/\x99/,'Oe')
205
+ #word.gsub!(/\x81/,'ue')
206
+ #word.gsub!(/\x9A/,'Ue')
207
+ word
208
+ end
209
+
167
210
  # taken from http://apidock.com/rails/ActiveSupport/Inflector/constantize
168
211
  # File activesupport/lib/active_support/inflector/methods.rb, line 226
169
212
  def constantize # (camel_cased_word)
@@ -14,6 +14,8 @@ module RobustExcelOle
14
14
 
15
15
  attr_reader :ole_table
16
16
 
17
+ alias ole_object ole_table
18
+
17
19
  # constructs a list object (or table).
18
20
  # @param [Variable] worksheet_or_ole_listobject a worksheet or a Win32Ole list object
19
21
  # @param [Variable] table_name_or_number a table name or table number
@@ -58,31 +60,99 @@ module RobustExcelOle
58
60
  def initialize(row_number)
59
61
  @ole_listrow = @@ole_table.ListRows.Item(row_number)
60
62
  end
61
-
63
+
64
+ # returns the value of the cell with given column name or number
65
+ # @param [Variant] column number or column name
66
+ # @return [Variant] value of the cell
67
+ def [] column_number_or_name
68
+ begin
69
+ ole_cell = @@ole_table.Application.Intersect(
70
+ @ole_listrow.Range, @@ole_table.ListColumns.Item(column_number_or_name).Range)
71
+ ole_cell.Value
72
+ rescue WIN32OLERuntimeError
73
+ raise TableRowError, "could not determine the value at column #{column_number_or_name}"
74
+ end
75
+ end
76
+
77
+
78
+ def []=(column_number_or_name, value)
79
+ begin
80
+ ole_cell = @@ole_table.Application.Intersect(
81
+ @ole_listrow.Range, @@ole_table.ListColumns.Item(column_number_or_name).Range)
82
+ ole_cell.Value = value
83
+ rescue WIN32OLERuntimeError
84
+ raise TableRowError, "could not assign value #{value.inspect} to cell at column #{column_number_or_name}"
85
+ end
86
+ end
87
+
88
+ # values of the row
89
+ # @return [Array] values of the row
90
+ def values
91
+ begin
92
+ @ole_listrow.Range.Value.first
93
+ rescue WIN32OLERuntimeError
94
+ raise TableError, "could not read values"
95
+ end
96
+ end
97
+
98
+ # sets the values of the row
99
+ # @param [Array] values of the row
100
+ def set_values values
101
+ begin
102
+ updated_values = self.values
103
+ updated_values[0,values.length] = values
104
+ @ole_listrow.Range.Value = [updated_values]
105
+ values
106
+ rescue WIN32OLERuntimeError
107
+ raise TableError, "could not set values #{values.inspect}"
108
+ end
109
+ end
110
+
111
+ # deletes the values of the row
112
+ def delete_values
113
+ begin
114
+ @ole_listrow.Range.Value = [[].fill(nil,0..(@@ole_table.ListColumns.Count)-1)]
115
+ nil
116
+ rescue WIN32OLERuntimeError
117
+ raise TableError, "could not delete values"
118
+ end
119
+ end
120
+
62
121
  def method_missing(name, *args)
63
- name_before_last_equal = name.to_s.split('=').first
122
+ name_str = name.to_s
123
+ core_name = name_str[-1]!='=' ? name_str : name_str[0..-2]
64
124
  column_names = @@ole_table.HeaderRowRange.Value.first
65
- method_names = column_names.map{|c| c.underscore.gsub(/[^[\w\d]]/, '_')}
66
- column_name = column_names[method_names.index(name_before_last_equal)]
125
+ column_name = column_names.find do |c|
126
+ c == core_name ||
127
+ c.gsub(/\W/,'') == core_name ||
128
+ c.replace_umlauts == core_name ||
129
+ c.gsub(/\W/,'').replace_umlauts == core_name ||
130
+ c.gsub(/\W/,'').replace_umlauts.underscore.gsub(/[^[\w\d]]/, '_').delete_multiple_underscores == core_name
131
+ end
67
132
  if column_name
68
- ole_cell = @@ole_table.Application.Intersect(
69
- @ole_listrow.Range, @@ole_table.ListColumns(column_name).Range)
70
- define_getting_setting_method(ole_cell,name.to_s)
71
- self.send(name, *args)
133
+ method_name = core_name.gsub(/\W/,'') + (name_str[-1]!='=' ? "" : "=")
134
+ define_and_call_method(column_name,method_name,*args)
72
135
  else
73
- super
136
+ super(name, *args)
74
137
  end
75
138
  end
76
139
 
77
140
  private
78
141
 
79
- def define_getting_setting_method(ole_cell,name_str)
80
- if name_str[-1] != '='
81
- self.class.define_method(name_str) do
142
+ def define_and_call_method(column_name,method_name,*args)
143
+ ole_cell = @@ole_table.Application.Intersect(
144
+ @ole_listrow.Range, @@ole_table.ListColumns.Item(column_name).Range)
145
+ define_getting_setting_method(ole_cell,method_name)
146
+ self.send(method_name, *args)
147
+ end
148
+
149
+ def define_getting_setting_method(ole_cell,name)
150
+ if name[-1] != '='
151
+ self.class.define_method(name) do
82
152
  ole_cell.Value
83
153
  end
84
154
  else
85
- self.class.define_method(name_str) do |value|
155
+ self.class.define_method(name) do |value|
86
156
  ole_cell.Value = value
87
157
  end
88
158
  end
@@ -98,34 +168,208 @@ module RobustExcelOle
98
168
 
99
169
  end
100
170
 
171
+ # @return [Array] a list of column names
101
172
  def column_names
102
- @ole_table.HeaderRowRange.Value.first
173
+ begin
174
+ @ole_table.HeaderRowRange.Value.first
175
+ rescue WIN32OLERuntimeError
176
+ raise TableError, "could not determine column names"
177
+ end
103
178
  end
104
179
 
105
- def insert_column(position = 1, column_name = "")
106
- @ole_table.ListColumns.Add
107
- rename_column(position,column_name)
180
+ # adds a row
181
+ # @param [Integer] position of the new row
182
+ # @param [Array] values of the column
183
+ def add_row(position = nil, contents = nil)
184
+ begin
185
+ @ole_table.ListRows.Add(position)
186
+ set_row_values(position, contents) if contents
187
+ rescue WIN32OLERuntimeError
188
+ raise TableError, ("could not add row" + (" at position #{position.inspect}" if position))
189
+ end
190
+ end
191
+
192
+ # adds a column
193
+ # @param [String] name of the column
194
+ # @param [Integer] position of the new column
195
+ # @param [Array] values of the column
196
+ def add_column(column_name = nil, position = nil, contents = nil)
197
+ begin
198
+ new_column = @ole_table.ListColumns.Add(position)
199
+ new_column.Name = column_name if column_name
200
+ set_column_values(column_name, contents) if contents
201
+ rescue WIN32OLERuntimeError, TableError
202
+ raise TableError, ("could not add column"+ ("at position #{position.inspect} with name #{column_name.inspect}" if position))
203
+ end
108
204
  end
109
205
 
110
- def delete_column(column_name_or_number)
111
- @ole_table.ListColumns.Item(row_number).Delete
206
+ # deletes a row
207
+ # @param [Integer] position of the old row
208
+ def delete_row(row_number)
209
+ begin
210
+ @ole_table.ListRows.Item(row_number).Delete
211
+ rescue WIN32OLERuntimeError
212
+ raise TableError, "could not delete row #{row_number.inspect}"
213
+ end
112
214
  end
113
215
 
114
- # insert row below row_number
115
- def insert_row(position = 1)
116
- @ole_table.ListRows.Add(position)
216
+ # deletes a column
217
+ # @param [Variant] column number or column name
218
+ def delete_column(column_number_or_name)
219
+ begin
220
+ @ole_table.ListColumns.Item(column_number_or_name).Delete
221
+ rescue WIN32OLERuntimeError
222
+ raise TableError, "could not delete column #{column_number_or_name.inspect}"
223
+ end
117
224
  end
118
225
 
119
- def delete_row(row_number)
120
- @ole_table.ListRows.Item(row_number).Delete
226
+ # deletes the contents of a row
227
+ # @param [Integer] row number
228
+ def delete_row_values(row_number)
229
+ begin
230
+ @ole_table.ListRows.Item(row_number).Range.Value = [[].fill(nil,0..(@ole_table.ListColumns.Count-1))]
231
+ nil
232
+ rescue WIN32OLERuntimeError
233
+ raise TableError, "could not delete contents of row #{row_number.inspect}"
234
+ end
121
235
  end
122
236
 
237
+ # deletes the contents of a column
238
+ # @param [Variant] column number or column name
239
+ def delete_column_values(column_number_or_name)
240
+ begin
241
+ column_name = @ole_table.ListColumns.Item(column_number_or_name).Range.Value.first
242
+ @ole_table.ListColumns.Item(column_number_or_name).Range.Value = [column_name] + [].fill([nil],0..(@ole_table.ListRows.Count-1))
243
+ nil
244
+ rescue WIN32OLERuntimeError
245
+ raise TableError, "could not delete contents of column #{column_number_or_name.inspect}"
246
+ end
247
+ end
248
+
249
+ # renames a row
250
+ # @param [String] previous name or number of the column
251
+ # @param [String] new name of the column
123
252
  def rename_column(name_or_number, new_name)
124
- column_names = @ole_table.HeaderRowRange.Value.first
125
- position = name_or_number.respond_to?(:abs) ? name_or_number : (column_names.index(name_or_number) + 1)
126
- column_names[position-1] = new_name
127
- @ole_table.HeaderRowRange.Value = [column_names]
128
- new_name
253
+ begin
254
+ @ole_table.ListColumns.Item(name_or_number).Name = new_name
255
+ rescue
256
+ raise TableError, "could not rename column #{name_or_number.inspect} to #{new_name.inspect}"
257
+ end
258
+ end
259
+
260
+ # contents of a row
261
+ # @param [Integer] row number
262
+ # @return [Array] contents of a row
263
+ def row_values(row_number)
264
+ begin
265
+ @ole_table.ListRows.Item(row_number).Range.Value.first
266
+ rescue WIN32OLERuntimeError
267
+ raise TableError, "could not read the values of row #{row_number.inspect}"
268
+ end
269
+ end
270
+
271
+ # sets the contents of a row
272
+ # @param [Integer] row number
273
+ # @param [Array] values of the row
274
+ def set_row_values(row_number, values)
275
+ begin
276
+ updated_values = row_values(row_number)
277
+ updated_values[0,values.length] = values
278
+ @ole_table.ListRows.Item(row_number).Range.Value = [updated_values]
279
+ values
280
+ rescue WIN32OLERuntimeError
281
+ raise TableError, "could not set the values of row #{row_number.inspect}"
282
+ end
283
+ end
284
+
285
+ # @return [Array] contents of a column
286
+ def column_values(column_number_or_name)
287
+ begin
288
+ @ole_table.ListColumns.Item(column_number_or_name).Range.Value[1,@ole_table.ListRows.Count].flatten
289
+ rescue WIN32OLERuntimeError
290
+ raise TableError, "could not read the values of column #{column_number_or_name.inspect}"
291
+ end
292
+ end
293
+
294
+ # sets the contents of a column
295
+ # @param [Integer] column name or column number
296
+ # @param [Array] contents of the column
297
+ def set_column_values(column_number_or_name, values)
298
+ begin
299
+ updated_values = column_values(column_number_or_name)
300
+ updated_values[0,values.length] = values
301
+ column_name = @ole_table.ListColumns.Item(column_number_or_name).Range.Value.first
302
+ @ole_table.ListColumns.Item(column_number_or_name).Range.Value = column_name + updated_values.map{|v| [v]}
303
+ values
304
+ rescue WIN32OLERuntimeError
305
+ raise TableError, "could not read the values of column #{column_number_or_name.inspect}"
306
+ end
307
+ end
308
+
309
+ # deletes rows that have an empty contents
310
+ def delete_empty_rows
311
+ listrows = @ole_table.ListRows
312
+ nil_array = [[].fill(nil,0..(@ole_table.ListColumns.Count-1))]
313
+ i = 1
314
+ while i <= listrows.Count do
315
+ row = listrows.Item(i)
316
+ if row.Range.Value == nil_array
317
+ row.Delete
318
+ else
319
+ i = i+1
320
+ end
321
+ end
322
+ end
323
+
324
+ # deletes columns that have an empty contents
325
+ def delete_empty_columns
326
+ listcolumns = @ole_table.ListColumns
327
+ nil_array = [].fill([nil],0..(@ole_table.ListRows.Count-1))
328
+ i = 1
329
+ while i <= listcolumns.Count do
330
+ column = listcolumns.Item(i)
331
+ if column.Range.Value[1..-1] == nil_array
332
+ column.Delete
333
+ else
334
+ i = i+1
335
+ end
336
+ end
337
+ end
338
+
339
+ # finds all cells containing a given value
340
+ # @param[Variant] value to find
341
+ # @return [Array] win32ole cells containing the given value
342
+ def find_cells(value)
343
+ listrows = @ole_table.ListRows
344
+ result = []
345
+ (1..listrows.Count).each do |row_number|
346
+ row_values(row_number).find_each_index(value).each do |col_number|
347
+ result << @ole_table.Application.Intersect(listrows.Item(row_number).Range,
348
+ @ole_table.ListColumns.Item(col_number+1).Range).to_reo
349
+ end
350
+ end
351
+ result
352
+ end
353
+
354
+ # sorts the rows of the list object according to the given column
355
+ # @param [Variant] column number or name
356
+ # @option opts [Symbol] sort order
357
+ def sort(column_number_or_name, sort_order = :ascending)
358
+ key_range = @ole_table.ListColumns.Item(column_number_or_name).Range
359
+ @ole_table.Sort.SortFields.Clear
360
+ sort_order_option = sort_order == :ascending ? XlAscending : XlDescending
361
+ @ole_table.Sort.SortFields.Add(key_range, XlSortOnValues,sort_order_option,XlSortNormal)
362
+ @ole_table.Sort.Apply
363
+ end
364
+
365
+ # @private
366
+ # returns true, if the list object responds to VBA methods, false otherwise
367
+ def alive?
368
+ @ole_table.ListRows
369
+ true
370
+ rescue
371
+ # trace $!.message
372
+ false
129
373
  end
130
374
 
131
375
  # @private
@@ -136,10 +380,12 @@ module RobustExcelOle
136
380
  # @private
137
381
  def inspect
138
382
  "#<ListObject:" + "#{@ole_table.Name}" +
139
- " size:#{@ole_table.ListRows.Count}x#{@ole_table.ListColumns.Count}" +
140
- " worksheet:#{@ole_table.Parent.Name}" + " workbook:#{@ole_table.Parent.Parent.Name}" + ">"
383
+ " #{@ole_table.ListRows.Count}x#{@ole_table.ListColumns.Count}" +
384
+ " #{@ole_table.Parent.Name}" + " #{@ole_table.Parent.Parent.Name}" + ">"
141
385
  end
142
386
 
387
+ include MethodHelpers
388
+
143
389
  private
144
390
 
145
391
  def method_missing(name, *args)
@@ -167,6 +413,10 @@ module RobustExcelOle
167
413
  class TableError < WorksheetREOError
168
414
  end
169
415
 
416
+ # @private
417
+ class TableRowError < WorksheetREOError
418
+ end
419
+
170
420
  Table = ListObject
171
421
  TableRow = ListRow
172
422