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 +4 -4
- data/Changelog +7 -1
- data/README.rdoc +5 -33
- data/bin/jreo +12 -9
- data/bin/reo +12 -9
- data/jreo.bat +1 -1
- data/lib/reo_console.rb +13 -10
- data/lib/robust_excel_ole/bookstore.rb +4 -3
- data/lib/robust_excel_ole/cell.rb +11 -0
- data/lib/robust_excel_ole/excel.rb +1 -1
- data/lib/robust_excel_ole/general.rb +67 -24
- data/lib/robust_excel_ole/list_object.rb +281 -31
- data/lib/robust_excel_ole/range.rb +60 -100
- data/lib/robust_excel_ole/version.rb +1 -1
- data/lib/robust_excel_ole/workbook.rb +12 -8
- data/lib/robust_excel_ole/worksheet.rb +16 -2
- data/reo.bat +3 -0
- data/spec/bookstore_spec.rb +1 -1
- data/spec/general_spec.rb +17 -6
- data/spec/list_object_spec.rb +226 -13
- data/spec/workbook_spec.rb +12 -0
- data/spec/workbook_specs/workbook_misc_spec.rb +2 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d60404c6338fa948418c3381d8f83b80147861694abd07956009fd332c923bc7
|
4
|
+
data.tar.gz: 1f50eae2f0c909a712563dd6ae2df38e45d9924ece79f1915987346bc3b6d1e1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
data/README.rdoc
CHANGED
@@ -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
|
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,
|
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,
|
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
|
-
|
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,
|
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.
|
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
|
-
|
21
|
-
|
22
|
-
#
|
16
|
+
#[
|
17
|
+
#->(_obj, _nest_level, _) { ">> " },
|
18
|
+
#->(*) { " " }
|
19
|
+
#]
|
23
20
|
|
24
|
-
|
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.
|
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
|
-
|
21
|
-
|
22
|
-
#
|
16
|
+
#[
|
17
|
+
#->(_obj, _nest_level, _) { ">> " },
|
18
|
+
#->(*) { " " }
|
19
|
+
#]
|
23
20
|
|
24
|
-
|
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
data/lib/reo_console.rb
CHANGED
@@ -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.
|
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
|
-
|
18
|
-
|
19
|
-
#
|
13
|
+
#[
|
14
|
+
#->(_obj, _nest_level, _) { ">> " },
|
15
|
+
#->(*) { " " }
|
16
|
+
#]
|
20
17
|
|
21
|
-
|
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
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
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 =
|
47
|
-
normalize(filename)
|
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 => :
|
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
|
-
|
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
|
-
|
66
|
-
|
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
|
-
|
69
|
-
|
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
|
80
|
-
|
81
|
-
|
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(
|
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
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
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
|
-
|
111
|
-
|
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
|
-
#
|
115
|
-
|
116
|
-
|
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
|
-
|
120
|
-
|
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
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
"
|
140
|
-
"
|
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
|
|