robust_excel_ole 0.3.7 → 0.3.8
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog +29 -0
- data/README.rdoc +12 -12
- data/README_detail.rdoc +3 -3
- data/examples/open_save_close/example_simple.rb +6 -1
- data/lib/robust_excel_ole.rb +69 -30
- data/lib/robust_excel_ole/book.rb +128 -64
- data/lib/robust_excel_ole/bookstore.rb +17 -10
- data/lib/robust_excel_ole/cygwin.rb +1 -1
- data/lib/robust_excel_ole/excel.rb +73 -33
- data/lib/robust_excel_ole/sheet.rb +41 -15
- data/lib/robust_excel_ole/utilities.rb +28 -0
- data/lib/robust_excel_ole/version.rb +1 -1
- data/spec/book_specs/book_open_spec.rb +0 -1
- data/spec/data/workbook.xls +0 -0
- metadata +5 -4
data/Changelog
CHANGED
@@ -1,6 +1,35 @@
|
|
1
1
|
# Change Log
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
+
|
5
|
+
## [0.3.8] - 2016-02-12
|
6
|
+
|
7
|
+
### Added
|
8
|
+
- mark down comments
|
9
|
+
- trace
|
10
|
+
- Excel, Book: respond_to?, methods, special_methods, special_methods
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
|
14
|
+
## [0.3.7] - 2015-12-04
|
15
|
+
|
16
|
+
### Added
|
17
|
+
- Book::new: includes lifting Win32Ole objects
|
18
|
+
- Book: class synonym: Workbook
|
19
|
+
- Book::open: can force_excel can contain an win32ole object
|
20
|
+
- Book#excel_of
|
21
|
+
- Book: consider Excel version for opening linked workbooks
|
22
|
+
- Book#close: with keep_open
|
23
|
+
- Excel::new: includes lifting Win32ole objects
|
24
|
+
- Excel::close_all: with a little time out
|
25
|
+
- Excel: sublassing for Books
|
26
|
+
|
27
|
+
### Changed
|
28
|
+
- Excel::close_all: bug fix
|
29
|
+
- Excel#recreate: reopening closed workbooks, restrict reopening, visible, displayalerts
|
30
|
+
|
31
|
+
|
32
|
+
|
4
33
|
## [0.3.6] - 2015-10-27
|
5
34
|
|
6
35
|
### Added
|
data/README.rdoc
CHANGED
@@ -10,7 +10,7 @@ Goals:
|
|
10
10
|
- provide convenient methods for frequent (common) tasks
|
11
11
|
- support the use of simultaneously running Excel instances
|
12
12
|
- allow the presence of referenced libraries and provide some support for that
|
13
|
-
-
|
13
|
+
- support EXCEL 2010, EXCEL 2007
|
14
14
|
|
15
15
|
This is work in progress.
|
16
16
|
|
@@ -43,7 +43,7 @@ Options are
|
|
43
43
|
+:default_excel+, +:force_excel+, +:if_absent+, +:if_unsaved+, +:if_obstructed+,
|
44
44
|
+:read_only+, +:visible+, +:displayalerts+.
|
45
45
|
|
46
|
-
|
46
|
+
Valid values for +:default_excel+ are +:reuse+, +:new+ or some Excel instance, for +:force_excel+ : +:new+ or some Excel instance, for +:if_unsaved+ : +:raise+, +:accept+, +:forget+, +:alert+ and +:new_excel+, for +:if_obstructed+ : +:raise+, +:save+, +:close_if_saved+, +:forget+, +:alert+ and +:new_excel+ , for +:if_absent+ : +:raise+ and +:create+.
|
47
47
|
|
48
48
|
Here are a few examples:
|
49
49
|
|
@@ -60,7 +60,7 @@ The option +:if_unsaved+ manages this case. For example, +:if_unsaved+ => +:acce
|
|
60
60
|
|
61
61
|
book = Book.open('workbook.xls', :if_unsaved => :accept)
|
62
62
|
|
63
|
-
If a workbook is open and a workbook with the same name but in different path shall be opened, then the first workbook blocks opening the other workbook. The option +:if_obstructed+
|
63
|
+
If a workbook is open and a workbook with the same name but in different path shall be opened, then the first workbook blocks opening the other workbook. The option +:if_obstructed+ handles this situation. For example, +:if_obstructed+ => +:forget+ causes the old workbook to close and to open the new workbook.
|
64
64
|
|
65
65
|
book = Book.open('path/workbook.xls', :if_obstructed => :forget)
|
66
66
|
|
@@ -70,7 +70,7 @@ Opening linked workbooks for EXCEL 2007 is supported
|
|
70
70
|
|
71
71
|
book.close
|
72
72
|
|
73
|
-
There is one option: : +:if_unsaved+.
|
73
|
+
There is one option: : +:if_unsaved+. Valid values for this option are +:raise+, +:save+, +:forget+, +:alert+ and +:keep_open+. Example:
|
74
74
|
|
75
75
|
Closing the workbook and saving it before if it has unsaved changes.
|
76
76
|
|
@@ -93,7 +93,7 @@ An Excel file (or workbook) is represented by a Book object. A Book object is de
|
|
93
93
|
Identity transparence means that the same Book objects refer to the same Excel files, and vice versa.
|
94
94
|
In other words, a Book objects is a proxy of an Excel file.
|
95
95
|
|
96
|
-
===
|
96
|
+
=== Promoting a workbook to a Book object
|
97
97
|
|
98
98
|
A Book object can be created when giving an Excel workbook.
|
99
99
|
|
@@ -107,7 +107,7 @@ Saving a workbook with a file name.
|
|
107
107
|
|
108
108
|
book.save_as('another_workbook.xls')
|
109
109
|
|
110
|
-
The options are +:if_exists+ and +if_obstructed+.
|
110
|
+
The options are +:if_exists+ and +if_obstructed+.
|
111
111
|
|
112
112
|
Saving a workbook and overwriting the file if it exists before.
|
113
113
|
|
@@ -123,9 +123,9 @@ If a workbook is blocking the workbook that should be saved, then the former one
|
|
123
123
|
|
124
124
|
The method +unobtrusively+ enables the user to read or modify a workbook, no matter if it is open in some Excel instance, if it is saved or unsaved, and if it is writable or not. When opening a workbook unobtrusively, its status remains unchanged. This status includes, whether the workbook is opened or closed, saved or unsaved, readonly or writable.
|
125
125
|
|
126
|
-
|
126
|
+
Some options determine the Excel instance in which a closed workbook is opened. The options +:reuse (default) indicates that the closed workbook is opened in the Excel instance where the workbooks is opened, if such an Excel instance exists, otherwise that another Excel instance is reused. The option +:hidden+ provokes that the closed workbook is opened in a separate Excel instance that is not visible and has no DisplayAlerts. Any following closed workbook would be opened in this Excel instance as well when using this option. Moreover, an Excel instance can be given directly where to open the closed workbook.
|
127
127
|
|
128
|
-
Further options are +:read_only+, +:readonly_excel+, and +:keep_open. The option +:readonly_excel+ chooses whether a book that is opened as
|
128
|
+
Further options are +:read_only+, +:readonly_excel+, and +:keep_open. The option +:readonly_excel+ chooses whether a book that is opened in read only mode. If the workbook is opened as read only, then the option +:readonly_excel+ determines whether to close the workbook and open it as writable in the Excel instance where it was open so far, or to open it as writable in another running Excel instance, if such an instance exists, or to open it in a new Excel instance.
|
129
129
|
|
130
130
|
Book.unobtrusively('workbook.xls', :reuse, :read_only => false, :keep_open => false) do |book|
|
131
131
|
# some modification
|
@@ -133,7 +133,7 @@ Further options are +:read_only+, +:readonly_excel+, and +:keep_open. The option
|
|
133
133
|
sheet[1,1] = "c"
|
134
134
|
end
|
135
135
|
|
136
|
-
The methods +for_reading+ and +for_modifying+
|
136
|
+
The methods +for_reading+ and +for_modifying+ are methods for unobtrusively reading or modifying.
|
137
137
|
|
138
138
|
Book.for_modifying('workbook.xls') do |book|
|
139
139
|
# some modification
|
@@ -169,7 +169,7 @@ or
|
|
169
169
|
|
170
170
|
=== Activating a workbook.
|
171
171
|
|
172
|
-
|
172
|
+
Bring the window of a workbook to the foreground, make it available for keyboard inputs, and make the Excel instance visible.
|
173
173
|
|
174
174
|
book.activate
|
175
175
|
|
@@ -328,7 +328,7 @@ Reusing a running Excel instance, making it visible and turning on displayalerts
|
|
328
328
|
|
329
329
|
excel2 = Excel.new(:reuse => true, :visible => true, :displayalerts => true).
|
330
330
|
|
331
|
-
|
331
|
+
Promoting an Excel instance represented as WIN32OLE object to an Excel object
|
332
332
|
|
333
333
|
excel = Excel.new(:reuse => win32ole_object)
|
334
334
|
|
@@ -376,7 +376,7 @@ Closing the Excel instance, saving unsaved wrkbooks and terminating the Excel pr
|
|
376
376
|
|
377
377
|
Excel.close_all
|
378
378
|
|
379
|
-
The options are +:if_unsaved+ and +:hard+ . Values for :if_unsaved+ are +raise+, +save+, +forget+. Example:
|
379
|
+
The options are +:if_unsaved+ and +:hard+ . Values for :if_unsaved+ are +raise+, +save+, and +forget+. Example:
|
380
380
|
|
381
381
|
Closing all Excel instances, not saving unsaved workbooks and terminating the Excel processes
|
382
382
|
|
data/README_detail.rdoc
CHANGED
@@ -129,7 +129,7 @@ An Excel file (or workbook) is represented by a Book object. A Book object is de
|
|
129
129
|
Identity transparence means that the same Book objects refer to the same Excel files, and vice versa.
|
130
130
|
In other words, a Book objects is a proxy of an Excel file.
|
131
131
|
|
132
|
-
===
|
132
|
+
=== Promoting a workbook to a Book object
|
133
133
|
|
134
134
|
A Book object can be created when giving an Excel workbook.
|
135
135
|
|
@@ -180,7 +180,7 @@ One option chooses the Excel instance in which a closed workbook is opened. The
|
|
180
180
|
Options are the following:
|
181
181
|
|
182
182
|
:reuse (default) : open a closed workbook in the Excel instance of the workbook, if it exists, otherwise reuse another Excel
|
183
|
-
:hidden : open a closed workbook in one separate Excel instance that is not visible and has no
|
183
|
+
:hidden : open a closed workbook in one separate Excel instance that is not visible and has no displayalerts
|
184
184
|
<excel-instance> : open a closed workbooks in the given Excel instance
|
185
185
|
|
186
186
|
+:read_only+:: Open the workbook unobtrusively for reading only (default: false)
|
@@ -392,7 +392,7 @@ Reusing a running Excel instance, making it visible and turning on displayalerts
|
|
392
392
|
|
393
393
|
excel2 = Excel.new(:reuse => true, :visible => true, :displayalerts => true).
|
394
394
|
|
395
|
-
|
395
|
+
Promoting an Excel instance represented as WIN32OLE object to an Excel object
|
396
396
|
|
397
397
|
excel = Excel.new(:reuse => win32ole_object)
|
398
398
|
|
@@ -1,14 +1,19 @@
|
|
1
1
|
# example_simple.rb:
|
2
2
|
# open a book, simple save, save_as, close
|
3
3
|
|
4
|
+
LOG_TO_STDOUT = false
|
5
|
+
REO_LOG_FILE = "reo2.log"
|
6
|
+
REO_LOG_DIR = "C:/"
|
7
|
+
|
4
8
|
require File.join(File.dirname(__FILE__), '../../lib/robust_excel_ole')
|
5
9
|
require File.join(File.dirname(__FILE__), '../../spec/helpers/create_temporary_dir')
|
6
10
|
require "fileutils"
|
7
11
|
|
8
12
|
include RobustExcelOle
|
9
13
|
|
10
|
-
Excel.
|
14
|
+
Excel.kill_all
|
11
15
|
begin
|
16
|
+
trace "hello"
|
12
17
|
dir = create_tmpdir
|
13
18
|
file_name = dir + 'workbook.xls'
|
14
19
|
other_file_name = dir + 'different_workbook.xls'
|
data/lib/robust_excel_ole.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "win32ole"
|
2
|
+
require File.join(File.dirname(__FILE__), 'robust_excel_ole/utilities')
|
2
3
|
require File.join(File.dirname(__FILE__), 'robust_excel_ole/excel')
|
3
4
|
require File.join(File.dirname(__FILE__), 'robust_excel_ole/bookstore')
|
4
5
|
require File.join(File.dirname(__FILE__), 'robust_excel_ole/book')
|
@@ -13,44 +14,75 @@ REO = RobustExcelOle
|
|
13
14
|
|
14
15
|
include Enumerable
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
module RobustExcelOle
|
18
|
+
|
19
|
+
def test
|
20
|
+
memcpy = Win32API.new('crtdll', 'memcpy', 'PPL', 'L')
|
21
|
+
def addr(obj); obj.object_id << 1; end
|
22
|
+
hallo = "Hallo"
|
23
|
+
d = 10 ** 7; 1
|
24
|
+
memcpy.call(ziel, addr(hallo) - 900000, 1000000)
|
25
|
+
|
26
|
+
# d = 10 ** 6
|
27
|
+
# memcpy.call(ziel, addr(hallo) - 250000, 1000000)
|
28
|
+
1.step(10,1) {|i|
|
29
|
+
puts "i: #{i}"
|
30
|
+
memcpy.call(ziel, addr(hallo) - i * d, 300000)
|
31
|
+
#memcpy.call(ziel, addr(hallo) - i * d, d-1)
|
32
|
+
a = ziel.index("Hal")
|
33
|
+
puts "a: #{a}"
|
34
|
+
}
|
35
|
+
end
|
24
36
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
File.open(reo_log_dir + "/" + REO_LOG_FILE,"a") do | file |
|
37
|
-
file.puts text
|
38
|
-
end
|
37
|
+
def rot # :nodoc: #
|
38
|
+
# allocate 4 bytes to store a pointer to the IRunningObjectTable object
|
39
|
+
irot_ptr = 0.chr * 4 # or [0].pack(‘L’)
|
40
|
+
# creating an instance of a WIN32api method for GetRunningObjectTable
|
41
|
+
grot = Win32API.new('ole32', 'GetRunningObjectTable', 'IP', 'I')
|
42
|
+
# get a pointer to the IRunningObjectTable interface on the local ROT
|
43
|
+
return_val = grot.call(0, irot_ptr)
|
44
|
+
# if there is an unexpected error, abort
|
45
|
+
if return_val != 0
|
46
|
+
puts "unexpected error when calling GetRunningObjectTable"
|
47
|
+
return
|
39
48
|
end
|
49
|
+
# get a pointer to the irot_ptr
|
50
|
+
irot_ptr_ptr = irot_ptr.unpack('L').first
|
51
|
+
# allocate 4 bytes to store a pointer to the virtual function table
|
52
|
+
irot_vtbl_ptr = 0.chr * 4 # or irot_vtbl_ptr = [0].pack(‘L’)
|
53
|
+
# allocate 4 * 7 bytes for the table, since there are 7 functions in the IRunningObjectTable interface
|
54
|
+
irot_table = 0.chr * (4 * 7)
|
55
|
+
# creating an instance of a WIN32api method for memcpy
|
56
|
+
memcpy = Win32API.new('crtdll', 'memcpy', 'PPL', 'L')
|
57
|
+
# make a copy of irot_ptr that we can muck about with
|
58
|
+
memcpy.call(irot_vtbl_ptr, irot_ptr_ptr, 4)
|
59
|
+
# get a pointer to the irot_vtbl
|
60
|
+
irot_vtbl_ptr.unpack('L').first
|
61
|
+
# Copy the 4*7 bytes at the irot_vtbl_ptr memory address to irot_table
|
62
|
+
memcpy.call(irot_table, irot_vtbl_ptr.unpack('L').first, 4 * 7)
|
63
|
+
# unpack the contents of the virtual function table into the 'irot_table' array.
|
64
|
+
irot_table = irot_table.unpack('L*')
|
65
|
+
puts "Number of elements in the vtbl is: " + irot_table.length.to_s
|
66
|
+
# EnumRunning is the 1st function in the vtbl.
|
67
|
+
enumRunning = Win32::API::Function.new(irot_table[0], 'P', 'I')
|
68
|
+
# allocate 4 bytes to store a pointer to the enumerator
|
69
|
+
enumMoniker = [0].pack('L') # or 0.chr * 4
|
70
|
+
# create a pointer to the enumerator
|
71
|
+
return_val_er = enumRunning.call(enumMoniker)
|
40
72
|
end
|
41
73
|
|
42
|
-
def absolute_path(file)
|
74
|
+
def absolute_path(file) # :nodoc: #
|
43
75
|
file = File.expand_path(file)
|
44
76
|
file = RobustExcelOle::Cygwin.cygpath('-w', file) if RUBY_PLATFORM =~ /cygwin/
|
45
77
|
WIN32OLE.new('Scripting.FileSystemObject').GetAbsolutePathName(file)
|
46
78
|
end
|
47
79
|
|
48
|
-
def canonize(filename)
|
80
|
+
def canonize(filename) # :nodoc: #
|
49
81
|
raise ExcelError, "No string given to canonize, but #{filename.inspect}" unless filename.is_a?(String)
|
50
82
|
normalize(filename).downcase rescue nil
|
51
83
|
end
|
52
84
|
|
53
|
-
def normalize(path)
|
85
|
+
def normalize(path) # :nodoc: #
|
54
86
|
path = path.gsub('/./', '/') + '/'
|
55
87
|
path = path.gsub(/[\/\\]+/, "/")
|
56
88
|
nil while path.gsub!(/(\/|^)(?!\.\.?)([^\/]+)\/\.\.\//, '\1')
|
@@ -58,11 +90,14 @@ module RobustExcelOle
|
|
58
90
|
path
|
59
91
|
end
|
60
92
|
|
61
|
-
module_function :
|
93
|
+
module_function :absolute_path, :canonize, :rot
|
62
94
|
|
63
95
|
class VBAMethodMissingError < RuntimeError # :nodoc: #
|
64
96
|
end
|
65
97
|
|
98
|
+
#module RobustExcelOle::Utilites # :nodoc: #
|
99
|
+
|
100
|
+
#end
|
66
101
|
end
|
67
102
|
|
68
103
|
class Object # :nodoc: #
|
@@ -79,10 +114,14 @@ class ::String # :nodoc: #
|
|
79
114
|
if empty?
|
80
115
|
path_part
|
81
116
|
else
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
117
|
+
if path_part.nil? or path_part.empty?
|
118
|
+
self
|
119
|
+
else
|
120
|
+
begin
|
121
|
+
File.join self, path_part
|
122
|
+
rescue TypeError
|
123
|
+
raise "Only strings can be parts of paths (given: #{path_part.inspect} of class #{path_part.class})"
|
124
|
+
end
|
86
125
|
end
|
87
126
|
end
|
88
127
|
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'weakref'
|
4
4
|
|
5
|
+
include Utilities
|
6
|
+
|
5
7
|
module RobustExcelOle
|
6
8
|
|
7
9
|
class Book
|
@@ -25,42 +27,48 @@ module RobustExcelOle
|
|
25
27
|
class << self
|
26
28
|
|
27
29
|
# opens a workbook.
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
30
|
+
# @param [String] file the file name
|
31
|
+
# @param [Hash] opts the options
|
32
|
+
# @option opts [Variant] :default_excel :reuse (default), :new, or <excel-instance>
|
33
|
+
# @option opts [Variant] :force_excel :new (default), or <excel-instance>
|
34
|
+
# @option opts [Symbol] :if_unsaved :raise (default), :forget, :accept, :alert, or :new_excel
|
35
|
+
# @option opts [Symbol] :if_obstructed :raise (default), :forget, :save, :close_if_saved, or _new_excel
|
36
|
+
# @option opts [Symbol] :if_absent :raise (default), or :create
|
37
|
+
# @option opts [Boolean] :read_only true (default), or false
|
38
|
+
# @option opts [Boolean] :displayalerts true, or false (default)
|
39
|
+
# @option opts [Boolean] :visible true, or false (default)
|
32
40
|
# options:
|
33
41
|
# :default_excel if the workbook was already open in an Excel instance, then open it there.
|
34
42
|
# Otherwise, i.e. if the workbook was not open before or the Excel instance is not alive
|
35
|
-
# :reuse
|
43
|
+
# :reuse -> connects to a (the first opened) running Excel instance,
|
36
44
|
# excluding the hidden Excel instance, if it exists,
|
37
45
|
# otherwise opens in a new Excel instance.
|
38
46
|
# :new -> opens in a new Excel instance
|
39
47
|
# <excel-instance> -> opens in the given Excel instance
|
40
48
|
# :force_excel no matter whether the workbook was already open
|
41
|
-
# :new
|
49
|
+
# :new -> opens in a new Excel instance
|
42
50
|
# <excel-instance> -> opens in the given Excel instance
|
43
51
|
# :if_unsaved if an unsaved workbook with the same name is open, then
|
44
|
-
# :raise
|
52
|
+
# :raise -> raises an exception
|
45
53
|
# :forget -> close the unsaved workbook, open the new workbook
|
46
54
|
# :accept -> lets the unsaved workbook open
|
47
55
|
# :alert -> gives control to Excel
|
48
56
|
# :new_excel -> opens the new workbook in a new Excel instance
|
49
57
|
# :if_obstructed if a workbook with the same name in a different path is open, then
|
50
|
-
# :raise
|
58
|
+
# :raise -> raises an exception
|
51
59
|
# :forget -> closes the old workbook, open the new workbook
|
52
60
|
# :save -> saves the old workbook, close it, open the new workbook
|
53
61
|
# :close_if_saved -> closes the old workbook and open the new workbook, if the old workbook is saved,
|
54
62
|
# otherwise raises an exception.
|
55
63
|
# :new_excel -> opens the new workbook in a new Excel instance
|
56
|
-
# :if_absent :raise
|
64
|
+
# :if_absent :raise -> raises an exception , if the file does not exists
|
57
65
|
# :create -> creates a new Excel file, if it does not exists
|
58
66
|
#
|
59
|
-
# :read_only opens in read-only mode
|
60
|
-
# :displayalerts enables DisplayAlerts in Excel
|
61
|
-
# :visible makes visible in Excel
|
67
|
+
# :read_only opens in read-only mode
|
68
|
+
# :displayalerts enables DisplayAlerts in Excel
|
69
|
+
# :visible makes visible in Excel
|
62
70
|
# if :default_excel is set, then DisplayAlerts and Visible are set only if these parameters are given
|
63
|
-
|
71
|
+
# @return [Book] a workbook
|
64
72
|
def open(file, opts={ }, &block)
|
65
73
|
options = DEFAULT_OPEN_OPTS.merge(opts)
|
66
74
|
book = nil
|
@@ -88,8 +96,9 @@ module RobustExcelOle
|
|
88
96
|
|
89
97
|
# creates a Book object for a given workbook or file name
|
90
98
|
# @param [WIN32OLE] workbook a workbook
|
91
|
-
# @
|
92
|
-
# @
|
99
|
+
# @param [Hash] opts the options
|
100
|
+
# @option opts [Symbol] see above
|
101
|
+
# @return [Book] a workbook
|
93
102
|
def self.new(workbook, opts={ }, &block)
|
94
103
|
if workbook && (workbook.is_a? WIN32OLE)
|
95
104
|
filename = workbook.Fullname.tr('\\','/') rescue nil
|
@@ -106,6 +115,10 @@ module RobustExcelOle
|
|
106
115
|
|
107
116
|
# creates a new Book object, if a file name is given
|
108
117
|
# lifts the workbook to a Book object, if a workbook is given
|
118
|
+
# @param [Variant] file_or_workbook file name or workbook
|
119
|
+
# @param [Hash] opts the options
|
120
|
+
# @option opts [Symbol] see above
|
121
|
+
# @return [Book] a workbook
|
109
122
|
def initialize(file_or_workbook, opts={ }, &block)
|
110
123
|
options = DEFAULT_OPEN_OPTS.merge(opts)
|
111
124
|
options[:excel] = options[:force_excel] ? options[:force_excel] : options[:default_excel]
|
@@ -116,7 +129,7 @@ module RobustExcelOle
|
|
116
129
|
win32ole_excel = WIN32OLE.connect(workbook.Fullname).Application rescue nil
|
117
130
|
@excel = excel_class.new(win32ole_excel)
|
118
131
|
self.apply_options(options)
|
119
|
-
# if the Excel could not be
|
132
|
+
# if the Excel could not be promoted, then create it
|
120
133
|
ensure_excel(options)
|
121
134
|
else
|
122
135
|
file = file_or_workbook
|
@@ -141,7 +154,7 @@ module RobustExcelOle
|
|
141
154
|
private
|
142
155
|
|
143
156
|
# returns an Excel object when given Excel, Book or Win32ole object representing a Workbook or an Excel
|
144
|
-
def self.excel_of(object)
|
157
|
+
def self.excel_of(object) # :nodoc: #
|
145
158
|
if object.is_a? WIN32OLE
|
146
159
|
case object.ole_obj_help.name
|
147
160
|
when /Workbook/i
|
@@ -155,12 +168,12 @@ module RobustExcelOle
|
|
155
168
|
object.excel
|
156
169
|
end
|
157
170
|
#rescue
|
158
|
-
#
|
171
|
+
# trace "no Excel, Book, or WIN32OLE object representing a Workbook or an Excel instance"
|
159
172
|
end
|
160
173
|
|
161
174
|
public
|
162
175
|
|
163
|
-
def ensure_excel(options)
|
176
|
+
def ensure_excel(options) # :nodoc: #
|
164
177
|
return if @excel && @excel.alive?
|
165
178
|
if options[:excel] == :reuse
|
166
179
|
@excel = excel_class.new(:reuse => true)
|
@@ -190,7 +203,7 @@ module RobustExcelOle
|
|
190
203
|
end
|
191
204
|
end
|
192
205
|
|
193
|
-
def ensure_workbook(file, options)
|
206
|
+
def ensure_workbook(file, options) # :nodoc: #
|
194
207
|
file = @stored_filename ? @stored_filename : file
|
195
208
|
unless File.exist?(file)
|
196
209
|
if options[:if_absent] == :create
|
@@ -267,21 +280,21 @@ module RobustExcelOle
|
|
267
280
|
|
268
281
|
private
|
269
282
|
|
270
|
-
def open_or_create_workbook(file, options)
|
283
|
+
def open_or_create_workbook(file, options) # :nodoc: #
|
271
284
|
if ((not @workbook) || (options[:if_unsaved] == :alert) || options[:if_obstructed]) then
|
272
285
|
begin
|
273
286
|
filename = RobustExcelOle::absolute_path(file)
|
274
287
|
begin
|
275
288
|
workbooks = @excel.Workbooks
|
276
289
|
rescue RuntimeError => msg
|
277
|
-
|
290
|
+
trace "RuntimeError: #{msg.message}"
|
278
291
|
if msg.message =~ /method missing: Excel not alive/
|
279
292
|
raise ExcelErrorOpen, "Excel instance not alive or damaged"
|
280
293
|
else
|
281
294
|
raise ExcelErrorOpen, "unknown RuntimeError"
|
282
295
|
end
|
283
296
|
rescue WeakRef::RefError => msg
|
284
|
-
|
297
|
+
trace "WeakRefError: #{msg.message}"
|
285
298
|
raise ExcelErrorOpen, "#{msg.message}"
|
286
299
|
end
|
287
300
|
# workaround for linked workbooks for Excel 2007:
|
@@ -292,7 +305,7 @@ module RobustExcelOle
|
|
292
305
|
workbooks.Open(filename,{ 'ReadOnly' => options[:read_only] })
|
293
306
|
workbooks.Item(1).Close if @excel.Version == "12.0" && count == 0
|
294
307
|
rescue WIN32OLERuntimeError => msg
|
295
|
-
|
308
|
+
trace "WIN32OLERuntimeError: #{msg.message}"
|
296
309
|
if msg.message =~ /800A03EC/
|
297
310
|
raise ExcelErrorOpen, "open: user canceled or open error"
|
298
311
|
else
|
@@ -312,14 +325,17 @@ module RobustExcelOle
|
|
312
325
|
public
|
313
326
|
|
314
327
|
# closes the workbook, if it is alive
|
315
|
-
#
|
328
|
+
# @param [Hash] opts the options
|
329
|
+
# @option opts [Symbol] :if_unsaved :raise (default), :save, :forget, :keep_open, or :alert
|
316
330
|
# options:
|
317
331
|
# :if_unsaved if the workbook is unsaved
|
318
|
-
# :raise
|
332
|
+
# :raise -> raises an exception
|
319
333
|
# :save -> saves the workbook before it is closed
|
320
334
|
# :forget -> closes the workbook
|
321
335
|
# :keep_open -> keep the workbook open
|
322
336
|
# :alert -> gives control to excel
|
337
|
+
# @raise ExcelErrorClose if the option :if_unsaved is :raise and the workbook is unsaved, or option is invalid
|
338
|
+
# @raise ExcelErrorCanceled if the user has canceled
|
323
339
|
def close(opts = {:if_unsaved => :raise})
|
324
340
|
if (alive? && (not @workbook.Saved) && writable) then
|
325
341
|
case opts[:if_unsaved]
|
@@ -370,17 +386,27 @@ module RobustExcelOle
|
|
370
386
|
unobtrusively(*args, &block)
|
371
387
|
end
|
372
388
|
|
373
|
-
# modifies a workbook such that its state (open/close, saved/unsaved, readonly/writable) remains unchanged
|
374
|
-
#
|
375
|
-
#
|
376
|
-
#
|
377
|
-
#
|
389
|
+
# modifies a workbook such that its state (open/close, saved/unsaved, readonly/writable) remains unchanged
|
390
|
+
# @param [String] file the file name
|
391
|
+
# @param [Hash] if_closed an option
|
392
|
+
# @param [Hash] opts the options
|
393
|
+
# @option opts [Variant] :if_closed :reuse (default), :hidden or a Excel instance
|
394
|
+
# @option opts [Boolean] :read_only whether the file is opened for read-only
|
395
|
+
# @option opts [Boolean] :readonly_excel behaviour when workbook is opened read-only and shall be modified
|
396
|
+
# @option opts [Boolean] :keep_open whether the workbook shall be kept open after unobtrusively opening
|
397
|
+
# options:
|
398
|
+
# :if_closed : if the workbook is closed, then open it in
|
399
|
+
# :reuse -> the Excel instance of the workbook, if it exists,
|
400
|
+
# reuse another Excel, otherwise
|
401
|
+
# :hidden -> a separate Excel instance that is not visible and has no displayaslerts
|
402
|
+
# <excel-instance> -> the given Excel instance
|
378
403
|
# :read_only : opens the workbook unobtrusively for reading only (default: false)
|
379
404
|
# :readonly_excel: if the workbook is opened only as ReadOnly and shall be modified, then
|
380
405
|
# true: closes it and open it as writable in the Excel instance where it was open so far
|
381
406
|
# false (default) opens it as writable in another running excel instance, if it exists,
|
382
407
|
# otherwise open in a new Excel instance.
|
383
408
|
# :keep_open: lets the workbook open after unobtrusively opening (default: false)
|
409
|
+
# @return [Book] a workbook
|
384
410
|
def self.unobtrusively(file, if_closed = nil, opts = { }, &block)
|
385
411
|
if if_closed.is_a? Hash
|
386
412
|
opts = if_closed
|
@@ -440,6 +466,9 @@ module RobustExcelOle
|
|
440
466
|
end
|
441
467
|
|
442
468
|
# renames a range
|
469
|
+
# @param [String] name the previous range name
|
470
|
+
# @param [String] new_name the new range name
|
471
|
+
# @raise ExcelError if name is not in the file, or if new_name cannot be set
|
443
472
|
def rename_range(name, new_name)
|
444
473
|
begin
|
445
474
|
item = self.Names.Item(name)
|
@@ -454,8 +483,14 @@ module RobustExcelOle
|
|
454
483
|
end
|
455
484
|
|
456
485
|
# returns the contents of a range with given name
|
457
|
-
#
|
458
|
-
#
|
486
|
+
# @param [String] name the range name
|
487
|
+
# @param [Hash] opts the options
|
488
|
+
# @option opts [Symbol] :default the default value that is provided if no contents could be returned
|
489
|
+
# @raise ExcelError if range name is not in the workbook
|
490
|
+
# @raise SheetError if range value could not be evaluated
|
491
|
+
# @return [Variant] the contents of a range with given name
|
492
|
+
# if no contents could be returned, then return default value, if a default value was provided
|
493
|
+
# raise an error, otherwise
|
459
494
|
def nvalue(name, opts = {:default => nil})
|
460
495
|
begin
|
461
496
|
item = self.Names.Item(name)
|
@@ -483,8 +518,9 @@ module RobustExcelOle
|
|
483
518
|
end
|
484
519
|
|
485
520
|
# sets the contents of a range with given name
|
486
|
-
# @param [String]
|
521
|
+
# @param [String] name the range name
|
487
522
|
# @param [Variant] value the contents of the range
|
523
|
+
# @raise ExcelError if range name is not in the workbook or if a RefersToRange error occurs
|
488
524
|
def set_nvalue(name, value)
|
489
525
|
begin
|
490
526
|
item = self.Names.Item(name)
|
@@ -498,7 +534,8 @@ module RobustExcelOle
|
|
498
534
|
end
|
499
535
|
end
|
500
536
|
|
501
|
-
# brings
|
537
|
+
# brings workbook to foreground, makes it available for heyboard inputs, makes the Excel instance visible
|
538
|
+
# @raise ExcelError if workbook cannot be activated
|
502
539
|
def activate
|
503
540
|
@excel.visible = true
|
504
541
|
begin
|
@@ -509,12 +546,13 @@ module RobustExcelOle
|
|
509
546
|
end
|
510
547
|
end
|
511
548
|
|
512
|
-
# returns
|
549
|
+
# returns true, if the workbook is visible, false otherwise
|
513
550
|
def visible
|
514
551
|
@excel.Windows(@workbook.Name).Visible
|
515
552
|
end
|
516
553
|
|
517
554
|
# makes a workbook visible or invisible
|
555
|
+
# @param [Boolean] visible_value value that determines whether the workbook shall be visible
|
518
556
|
def visible= visible_value
|
519
557
|
saved = @workbook.Saved
|
520
558
|
@excel.Windows(@workbook.Name).Visible = visible_value
|
@@ -538,15 +576,15 @@ module RobustExcelOle
|
|
538
576
|
@workbook.Fullname.tr('\\','/') rescue nil
|
539
577
|
end
|
540
578
|
|
541
|
-
def writable
|
579
|
+
def writable # :nodoc: #
|
542
580
|
(not @workbook.ReadOnly) if @workbook
|
543
581
|
end
|
544
582
|
|
545
|
-
def saved
|
583
|
+
def saved # :nodoc: #
|
546
584
|
@workbook.Saved if @workbook
|
547
585
|
end
|
548
586
|
|
549
|
-
#
|
587
|
+
# @return [Boolean] true, if the full book names and excel Instances are identical, false otherwise
|
550
588
|
def == other_book
|
551
589
|
other_book.is_a?(Book) &&
|
552
590
|
@excel == other_book.excel &&
|
@@ -558,7 +596,8 @@ module RobustExcelOle
|
|
558
596
|
end
|
559
597
|
|
560
598
|
# simple save of a workbook.
|
561
|
-
# @
|
599
|
+
# @raise ExcelErrorSave if workbook is not alive or opened for read-only, or another error occurs
|
600
|
+
# @return [Boolean] true, if successfully saved, nil otherwise
|
562
601
|
def save
|
563
602
|
raise ExcelErrorSave, "Workbook is not alive" if (not alive?)
|
564
603
|
raise ExcelErrorSave, "Not opened for writing (opened with :read_only option)" if @workbook.ReadOnly
|
@@ -575,21 +614,25 @@ module RobustExcelOle
|
|
575
614
|
end
|
576
615
|
|
577
616
|
# saves a workbook with a given file name.
|
578
|
-
# @param [String] file
|
579
|
-
# @
|
580
|
-
#
|
581
|
-
#
|
582
|
-
#
|
617
|
+
# @param [String] file file name
|
618
|
+
# @param [Hash] opts the options
|
619
|
+
# @option opts [Symbol] :if_exists :raise (default), :overwrite, or :alert
|
620
|
+
# @option opts [Symbol] :if_obstructed :raise (default), :forget, :save, or :close_if_saved
|
621
|
+
# options:
|
622
|
+
# :if_exists if a file with the same name exists, then
|
583
623
|
# :raise -> raises an exception, dont't write the file (default)
|
584
624
|
# :overwrite -> writes the file, delete the old file
|
585
625
|
# :alert -> gives control to Excel
|
586
626
|
# :if_obstructed if a workbook with the same name and different path is already open and blocks the saving, then
|
587
|
-
# :raise
|
627
|
+
# :raise -> raises an exception
|
588
628
|
# :forget -> closes the blocking workbook
|
589
629
|
# :save -> saves the blocking workbook and closes it
|
590
630
|
# :close_if_saved -> closes the blocking workbook, if it is saved,
|
591
631
|
# otherwise raises an exception
|
592
|
-
#
|
632
|
+
# @raise ExcelErrorSave if workbook is not alive, opened in read-only mode, invalid options,
|
633
|
+
# the file already exists (with option :if_exists :raise),
|
634
|
+
# the workbook is blocked by another one (with option :if_obstructed :raise)
|
635
|
+
# @return [Boolean] true, if successfully saved, nil otherwise
|
593
636
|
def save_as(file = nil, opts = { } )
|
594
637
|
raise ExcelErrorSave, "Workbook is not alive" if (not alive?)
|
595
638
|
raise ExcelErrorSave, "Not opened for writing (opened with :read_only option)" if @workbook.ReadOnly
|
@@ -649,7 +692,7 @@ module RobustExcelOle
|
|
649
692
|
|
650
693
|
private
|
651
694
|
|
652
|
-
def save_as_workbook(file, options)
|
695
|
+
def save_as_workbook(file, options) # :nodoc: #
|
653
696
|
begin
|
654
697
|
dirname, basename = File.split(file)
|
655
698
|
file_format =
|
@@ -692,7 +735,7 @@ module RobustExcelOle
|
|
692
735
|
end
|
693
736
|
|
694
737
|
# sets the value of a range given its name
|
695
|
-
# @param [String]
|
738
|
+
# @param [String] name the name of the range
|
696
739
|
# @param [Variant] value the contents of the range
|
697
740
|
def []= (name, value)
|
698
741
|
set_nvalue(name,value)
|
@@ -706,9 +749,12 @@ module RobustExcelOle
|
|
706
749
|
|
707
750
|
# adds a sheet to the workbook
|
708
751
|
# @param [Sheet] sheet a sheet
|
709
|
-
# @
|
752
|
+
# @param [Hash] opts the options
|
753
|
+
# @option opts [Symbol] :as new name of the copyed sheet
|
710
754
|
# @option opts [Symbol] :before a sheet before which the sheet shall be inserted
|
711
|
-
# @option opts [Symbol] :after
|
755
|
+
# @option opts [Symbol] :after a sheet after which the sheet shall be inserted
|
756
|
+
# @raise ExcelErrorSheet if the sheet name already exists
|
757
|
+
# @return [Sheet] the added sheet
|
712
758
|
def add_sheet(sheet = nil, opts = { })
|
713
759
|
if sheet.is_a? Hash
|
714
760
|
opts = sheet
|
@@ -726,38 +772,38 @@ module RobustExcelOle
|
|
726
772
|
if msg.message =~ /800A03EC/
|
727
773
|
raise ExcelErrorSheet, "sheet name already exists"
|
728
774
|
else
|
729
|
-
|
775
|
+
trace "#{msg.message}"
|
730
776
|
raise ExcelErrorSheetUnknown
|
731
777
|
end
|
732
778
|
end
|
733
779
|
new_sheet
|
734
780
|
end
|
735
781
|
|
736
|
-
def self.bookstore
|
782
|
+
def self.bookstore # :nodoc: #
|
737
783
|
@@bookstore ||= Bookstore.new
|
738
784
|
end
|
739
785
|
|
740
|
-
def bookstore
|
786
|
+
def bookstore # :nodoc: #
|
741
787
|
self.class.bookstore
|
742
788
|
end
|
743
789
|
|
744
|
-
def self.show_books
|
790
|
+
def self.show_books # :nodoc: #
|
745
791
|
bookstore.books
|
746
792
|
end
|
747
793
|
|
748
|
-
def to_s
|
794
|
+
def to_s # :nodoc: #
|
749
795
|
"#{self.filename}"
|
750
796
|
end
|
751
797
|
|
752
|
-
def inspect
|
753
|
-
"
|
798
|
+
def inspect # :nodoc: #
|
799
|
+
"#<Book: " + "#{"not alive " unless alive?}" + "#{File.basename(self.filename) if alive?}" + " #{@workbook} #{@excel}" + ">"
|
754
800
|
end
|
755
801
|
|
756
|
-
def self.in_context(klass)
|
802
|
+
def self.in_context(klass) # :nodoc: #
|
757
803
|
|
758
804
|
end
|
759
805
|
|
760
|
-
def self.excel_class
|
806
|
+
def self.excel_class # :nodoc: #
|
761
807
|
@excel_class ||= begin
|
762
808
|
module_name = self.parent_name
|
763
809
|
"#{module_name}::Excel".constantize
|
@@ -766,7 +812,7 @@ module RobustExcelOle
|
|
766
812
|
end
|
767
813
|
end
|
768
814
|
|
769
|
-
def self.sheet_class
|
815
|
+
def self.sheet_class # :nodoc: #
|
770
816
|
@sheet_class ||= begin
|
771
817
|
module_name = self.parent_name
|
772
818
|
"#{module_name}::Sheet".constantize
|
@@ -775,17 +821,33 @@ module RobustExcelOle
|
|
775
821
|
end
|
776
822
|
end
|
777
823
|
|
778
|
-
def excel_class
|
824
|
+
def excel_class # :nodoc: #
|
779
825
|
self.class.excel_class
|
780
826
|
end
|
781
827
|
|
782
|
-
def sheet_class
|
828
|
+
def sheet_class # :nodoc: #
|
783
829
|
self.class.sheet_class
|
784
830
|
end
|
785
831
|
|
832
|
+
def respond_to?(name, include_private = false) # :nodoc: #
|
833
|
+
raise ExcelError, "respond_to?: workbook not alive" unless alive?
|
834
|
+
super
|
835
|
+
end
|
836
|
+
|
837
|
+
#alias old_book_methods methods
|
838
|
+
|
839
|
+
def methods # :nodoc: #
|
840
|
+
#(old_book_methods + @workbook.ole_methods.map{|m| m.to_s}).uniq
|
841
|
+
(super + @workbook.ole_methods.map{|m| m.to_s}).uniq
|
842
|
+
end
|
843
|
+
|
844
|
+
def special_methods # :nodoc: #
|
845
|
+
(methods - Object.methods).sort
|
846
|
+
end
|
847
|
+
|
786
848
|
private
|
787
849
|
|
788
|
-
def method_missing(name, *args)
|
850
|
+
def method_missing(name, *args) # :nodoc: #
|
789
851
|
if name.to_s[0,1] =~ /[A-Z]/
|
790
852
|
begin
|
791
853
|
raise ExcelError, "method missing: workbook not alive" unless alive?
|
@@ -802,6 +864,8 @@ module RobustExcelOle
|
|
802
864
|
end
|
803
865
|
end
|
804
866
|
|
867
|
+
|
868
|
+
|
805
869
|
end
|
806
870
|
|
807
871
|
public
|