robust_excel_ole 1.19.11 → 1.22

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a834f5ca858ecdc71fee9cc5a6b6d146755910c5dbc64720465540cc1e964d9
4
- data.tar.gz: 377ef0d7047756df9a64384c25d6cc13e40454a428ae8302b7bac73a0dacd0f2
3
+ metadata.gz: f5739475bb50983be2c17b084b939ec1143d9508f280dfd8bf80445d5fbce521
4
+ data.tar.gz: 1ecdceba6d53cf8a231d81425ca2c8f9b02a6fff2f6e2ad4aa90813b1223febf
5
5
  SHA512:
6
- metadata.gz: cdbe008783fb6c781a9e6b20408fc820f32e8308e501c86f025bf22f43c50b8da5f595766e217b4c295e447adc89905d9ebcd89533883257d621e6688eb4a13c
7
- data.tar.gz: 3e5a3c904854aca246f269be56eba6c19394900dccee6d6a08dda702a4ede2605ff62cd14b3687921e8b13275fc33e5141859ca4b3fed60aa4dd819a27f0879b
6
+ metadata.gz: a4b326afc9230578f4f10bbbd4b2bb12d0536b3104ed0cc897b96a7ed3afe8ee6016181aa0603c8c9f260fa1966b19cd5b9ab07af5dda8312f9b7b363ee7ac7a
7
+ data.tar.gz: 06b76a2ecbab24b208d449d858329eb997e6375b2e5533c2ee0525fc1316ca249aa4594a9feb70525a9f492ba92153fdd9264caa23a6ec3c1af02547debab2d8
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-12-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
@@ -2,23 +2,26 @@
2
2
  # -*- jruby -*-
3
3
 
4
4
  require 'pry'
5
- require '../lib/robust_excel_ole'
5
+ require '../robust_excel_ole/lib/robust_excel_ole'
6
6
 
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
@@ -2,23 +2,26 @@
2
2
  # -*- ruby -*-
3
3
 
4
4
  require 'pry'
5
- require '../lib/robust_excel_ole'
5
+ require '../robust_excel_ole/lib/robust_excel_ole'
6
6
 
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