robust_excel_ole 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/Gemfile +4 -0
- data/Guardfile +10 -0
- data/LICENSE +7 -0
- data/README.rdoc +148 -0
- data/Rakefile +9 -0
- data/lib/robust_excel_ole.rb +13 -0
- data/lib/robust_excel_ole/book.rb +310 -0
- data/lib/robust_excel_ole/cell.rb +19 -0
- data/lib/robust_excel_ole/cygwin.rb +40 -0
- data/lib/robust_excel_ole/excel_app.rb +182 -0
- data/lib/robust_excel_ole/range.rb +38 -0
- data/lib/robust_excel_ole/robustexcelole.sublime-project +8 -0
- data/lib/robust_excel_ole/robustexcelole.sublime-workspace +347 -0
- data/lib/robust_excel_ole/sheet.rb +103 -0
- data/lib/robust_excel_ole/sp +3 -0
- data/lib/robust_excel_ole/version.rb +3 -0
- data/lib/spec_helper.rb +35 -0
- data/robust_excel_ole.gemspec +32 -0
- data/robust_excel_ole_example.rb +29 -0
- data/spec/book_spec.rb +867 -0
- data/spec/cell_spec.rb +66 -0
- data/spec/cygwin_spec.rb +42 -0
- data/spec/data/book_with_blank.xls +0 -0
- data/spec/data/different_simple.xls +0 -0
- data/spec/data/merge_cells.xls +0 -0
- data/spec/data/more_data/simple.xls +0 -0
- data/spec/data/protected_sheet.xls +0 -0
- data/spec/data/simple.xls +0 -0
- data/spec/data/simple.xlsm +0 -0
- data/spec/data/simple.xlsx +0 -0
- data/spec/excel_app_spec.rb +168 -0
- data/spec/helpers/key_sender.rb +68 -0
- data/spec/range_spec.rb +120 -0
- data/spec/sheet_spec.rb +355 -0
- data/spec/spec_helper.rb +35 -0
- data/version.rb +3 -0
- metadata +203 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# -*- mode: ruby -*-
|
2
|
+
# A sample Guardfile
|
3
|
+
# More info at https://github.com/guard/guard#readme
|
4
|
+
|
5
|
+
guard 'rspec', cli: "--color", all_after_pass: false, all_on_start: false do
|
6
|
+
watch(%r{^spec/.+_spec\.rb$})
|
7
|
+
watch(%r{^lib/robust_excel_ole/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
8
|
+
watch('lib/robust_excel_ole.rb') { "spec" }
|
9
|
+
watch('spec/spec_helper.rb') { "spec" }
|
10
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright (c) 2011-2013 tomi
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
= RobustExcelOle
|
2
|
+
|
3
|
+
This is a simple fork from tomiacannondale's wrap_excel adapted to Ruby 1.8.6.
|
4
|
+
The functionality of wrap_excel is optimised and extended by new features.
|
5
|
+
|
6
|
+
|
7
|
+
== Description
|
8
|
+
|
9
|
+
RobustExcelOle wraps the win32ole, and uses the Excel operations with ruby.
|
10
|
+
|
11
|
+
== Requirements
|
12
|
+
|
13
|
+
* Ruby 1.9.2 or higher (platform is windows)
|
14
|
+
* also works with Ruby 1.8.6
|
15
|
+
|
16
|
+
== Install
|
17
|
+
|
18
|
+
gem install robust_excel_ole
|
19
|
+
|
20
|
+
== Usage
|
21
|
+
=== access book
|
22
|
+
|
23
|
+
Read with block.
|
24
|
+
|
25
|
+
RobustExcelOle::Book.open('./sample.xls') do |book|
|
26
|
+
# do something
|
27
|
+
end
|
28
|
+
|
29
|
+
Read without block.
|
30
|
+
|
31
|
+
book = RobustExcelOle::Book.open('./sample.xls')
|
32
|
+
book.close
|
33
|
+
|
34
|
+
Options are the following.
|
35
|
+
|
36
|
+
[read_only] boolean(default true)
|
37
|
+
[displayalerts] boolean(default false)
|
38
|
+
[visible] boolean(default false)
|
39
|
+
|
40
|
+
=== access sheet
|
41
|
+
|
42
|
+
Sheet object can access with Book#[] method.
|
43
|
+
|
44
|
+
sheet = book[0]
|
45
|
+
|
46
|
+
Access with sheet name.
|
47
|
+
|
48
|
+
book['Sheet1']
|
49
|
+
|
50
|
+
=== access row or column
|
51
|
+
|
52
|
+
Sheet object is included enumerable. Use Sheet#each_column or Sheet#each_row or Sheet#each method.
|
53
|
+
|
54
|
+
sheet.each do |cell|
|
55
|
+
# do something with cell
|
56
|
+
# read every row every column
|
57
|
+
end
|
58
|
+
|
59
|
+
sheet.each_row do |row|
|
60
|
+
# do something with row_range
|
61
|
+
end
|
62
|
+
|
63
|
+
sheet.each_column do |column_range|
|
64
|
+
# do something with column_range
|
65
|
+
end
|
66
|
+
|
67
|
+
=== access cell
|
68
|
+
|
69
|
+
Read from sheet object.
|
70
|
+
|
71
|
+
sheet[0, 0] => first cell.
|
72
|
+
|
73
|
+
Read from range object
|
74
|
+
|
75
|
+
row_range[0] => first cell in row_range
|
76
|
+
column_range[1] => second cell in column_range
|
77
|
+
|
78
|
+
=== write excel
|
79
|
+
|
80
|
+
Can save an existing file.
|
81
|
+
|
82
|
+
Saves a file, if no file with the same name exists.
|
83
|
+
|
84
|
+
Proceeds acocording to the options, otherwise.
|
85
|
+
|
86
|
+
RobustExcelOle::Book.open('./sample.xls', :read_only => false) do |book|
|
87
|
+
# do something
|
88
|
+
book.save '.\sample.xls'
|
89
|
+
end
|
90
|
+
|
91
|
+
or
|
92
|
+
|
93
|
+
book = RobustExcelOle::Book.open('./sample.xls', :read_only => false)
|
94
|
+
book.save '.\sample.xls'
|
95
|
+
book.close
|
96
|
+
|
97
|
+
Can save with an another file name.
|
98
|
+
|
99
|
+
RobustExcelOle::Book.open('./sample.xls', :read_only => false) do |book|
|
100
|
+
# do something
|
101
|
+
book.save './another_file.xls'
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
Save to another_file.xls.
|
106
|
+
|
107
|
+
Options are the following:
|
108
|
+
|
109
|
+
[:if_exists] :overwrite, :excel, :raise (default)
|
110
|
+
|
111
|
+
:overwrite:
|
112
|
+
|
113
|
+
Delete the old file and write the file.
|
114
|
+
|
115
|
+
:excel
|
116
|
+
|
117
|
+
Give the control to Excel.
|
118
|
+
|
119
|
+
:raise
|
120
|
+
|
121
|
+
Throw an exception. Don't write the file.
|
122
|
+
|
123
|
+
Example:
|
124
|
+
|
125
|
+
RobustExcelOle::Book.open('./sample.xls', :read_only => false) do |book|
|
126
|
+
# do something
|
127
|
+
book.save('./another_file.xls', :if_exists => :overwrite)
|
128
|
+
end
|
129
|
+
|
130
|
+
=== Want to do more things
|
131
|
+
|
132
|
+
All RobustExcelOle object include win32ole instance. If you want to do something that not provide a function, you can use win32ole methods.
|
133
|
+
|
134
|
+
== Support
|
135
|
+
|
136
|
+
Report issues and feature requests to github Issues. https://github.com/Thomas008/robust_excel_ole/issues
|
137
|
+
|
138
|
+
== Collaborate
|
139
|
+
|
140
|
+
Please pull request on github.
|
141
|
+
|
142
|
+
== Author
|
143
|
+
|
144
|
+
thomas mailto:traths@cs.uni-potsdam.de
|
145
|
+
|
146
|
+
== License
|
147
|
+
|
148
|
+
MIT License. For more imformation, please see LICENSE.
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "win32ole"
|
2
|
+
require File.join(File.dirname(__FILE__), 'robust_excel_ole/excel_app')
|
3
|
+
require File.join(File.dirname(__FILE__), 'robust_excel_ole/book')
|
4
|
+
require File.join(File.dirname(__FILE__), 'robust_excel_ole/sheet')
|
5
|
+
require File.join(File.dirname(__FILE__), 'robust_excel_ole/cell')
|
6
|
+
require File.join(File.dirname(__FILE__), 'robust_excel_ole/range')
|
7
|
+
require File.join(File.dirname(__FILE__), 'robust_excel_ole/cygwin') if RUBY_PLATFORM =~ /cygwin/
|
8
|
+
#+#require "robust_excel_ole/version"
|
9
|
+
require File.join(File.dirname(__FILE__), 'robust_excel_ole/version')
|
10
|
+
|
11
|
+
module RobustExcelOle
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,310 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'weakref'
|
3
|
+
|
4
|
+
module RobustExcelOle
|
5
|
+
|
6
|
+
class Book
|
7
|
+
attr_reader :workbook
|
8
|
+
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
# opens a book.
|
13
|
+
#
|
14
|
+
# options:
|
15
|
+
# :reuse (boolean) use an already open Excel-application (default: true)
|
16
|
+
# :read_only (boolean) open in read-only mode (default: false)
|
17
|
+
# :displayalerts (boolean) allow display alerts in Excel (default: false)
|
18
|
+
# :visible (boolean) make visibe in Excel (default: false)
|
19
|
+
# :if_unsaved if an unsaved book with the same name is open, then
|
20
|
+
# :raise -> raise an exception (default)
|
21
|
+
# :accept -> let the unsaved book open
|
22
|
+
# :forget -> close the unsaved book, open the new book
|
23
|
+
# :excel -> give control to excel
|
24
|
+
# :new_app -> open the new book in a new excel application
|
25
|
+
# :if_unsaved_other_book if an unsaved book with the same name in a different path is open, then
|
26
|
+
# :raise -> raise an exception (default)
|
27
|
+
# :save -> save and close the unsaved book and open the new book
|
28
|
+
# :forget -> close the unsaved book, open the new book
|
29
|
+
# :new_app -> open the new book in a new excel application
|
30
|
+
def open(file, options={ :reuse => true}, &block)
|
31
|
+
new(file, options, &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(file, opts={ }, &block)
|
37
|
+
@options = {
|
38
|
+
:reuse => true,
|
39
|
+
:read_only => false,
|
40
|
+
:if_unsaved => :raise,
|
41
|
+
:if_unsaved_other_book => :raise
|
42
|
+
}.merge(opts)
|
43
|
+
excel_app_options = {:reuse => true}.merge(opts).delete_if{|k,v|
|
44
|
+
k== :if_read_only || k== :unsaved || k == :if_unsaved_other_book}
|
45
|
+
if not File.exist?(file)
|
46
|
+
raise ExcelErrorOpen, "file #{file} not found"
|
47
|
+
end
|
48
|
+
@excel_app = ExcelApp.new(excel_app_options)
|
49
|
+
workbooks = @excel_app.Workbooks
|
50
|
+
@workbook = workbooks.Item(File.basename(file)) rescue nil
|
51
|
+
if @workbook then
|
52
|
+
blocked_by_other_book = (File.basename(file) == File.basename(@workbook.Fullname)) &&
|
53
|
+
(not (file == @workbook.Fullname.gsub("\\","/")))
|
54
|
+
if blocked_by_other_book then
|
55
|
+
case @options[:if_unsaved_other_book]
|
56
|
+
when :raise
|
57
|
+
raise ExcelErrorOpen, "blocked by an unsaved book with the same name in a different path"
|
58
|
+
when :save
|
59
|
+
#nothing
|
60
|
+
when :forget
|
61
|
+
@workbook.Close
|
62
|
+
when :new_app
|
63
|
+
@options[:reuse] = false
|
64
|
+
@excel_app = ExcelApp.new(@options)
|
65
|
+
@workbook = nil
|
66
|
+
else
|
67
|
+
raise ExcelErrorOpen, ":if_unsaved_other_book: invalid option"
|
68
|
+
end
|
69
|
+
else
|
70
|
+
# book open, not saved, not blocked by other book
|
71
|
+
if (not @workbook.Saved) then
|
72
|
+
#p "book not saved"
|
73
|
+
case @options[:if_unsaved]
|
74
|
+
when :raise
|
75
|
+
raise ExcelErrorOpen, "book is already open but not saved (#{File.basename(file)})"
|
76
|
+
when :accept
|
77
|
+
#nothing
|
78
|
+
when :forget
|
79
|
+
@workbook.Close
|
80
|
+
when :excel
|
81
|
+
old_displayalerts = @excel_app.DisplayAlerts
|
82
|
+
@excel_app.DisplayAlerts = true
|
83
|
+
when :new_app
|
84
|
+
@options[:reuse] = false
|
85
|
+
@excel_app = ExcelApp.new(@options)
|
86
|
+
@workbook = nil
|
87
|
+
else
|
88
|
+
raise ExcelErrorOpen, ":if_unsaved: invalid option"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
begin
|
94
|
+
# if book not open (was not open,was closed with option :forget or shall be opened in new application)
|
95
|
+
# or :if_unsaved => :excel
|
96
|
+
if ((not alive?) || (@options[:if_unsaved] == :excel)) then
|
97
|
+
begin
|
98
|
+
@workbook = @excel_app.Workbooks.Open(absolute_path(file),{ 'ReadOnly' => @options[:read_only] })
|
99
|
+
rescue WIN32OLERuntimeError
|
100
|
+
raise ExcelUserCanceled, "open: canceled by user"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
ensure
|
104
|
+
if @options[:if_unsaved] == :excel then
|
105
|
+
@excel_app.DisplayAlerts = old_displayalerts
|
106
|
+
end
|
107
|
+
end
|
108
|
+
if block
|
109
|
+
begin
|
110
|
+
yield self
|
111
|
+
ensure
|
112
|
+
close
|
113
|
+
end
|
114
|
+
end
|
115
|
+
@workbook
|
116
|
+
end
|
117
|
+
|
118
|
+
# closes the book, if it is alive
|
119
|
+
#
|
120
|
+
# options:
|
121
|
+
# :if_unsaved if book is unsaved
|
122
|
+
# :raise -> raise an exception (default)
|
123
|
+
# :save -> save the book before it is closed
|
124
|
+
# :forget -> close the book
|
125
|
+
# :excel -> give control to excel
|
126
|
+
def close(opts={ })
|
127
|
+
@options = {
|
128
|
+
:if_unsaved => :raise,
|
129
|
+
}.merge(opts)
|
130
|
+
if ((alive?) && (not @workbook.Saved)) then
|
131
|
+
#puts "book not saved"
|
132
|
+
case @options[:if_unsaved]
|
133
|
+
when :raise
|
134
|
+
raise ExcelErrorClose, "book is unsaved (#{File.basename(filename)})"
|
135
|
+
when :save
|
136
|
+
save
|
137
|
+
when :forget
|
138
|
+
#nothing
|
139
|
+
when :excel
|
140
|
+
old_displayalerts = @excel_app.DisplayAlerts
|
141
|
+
@excel_app.DisplayAlerts = true
|
142
|
+
else
|
143
|
+
raise ExcelErrorClose, ":if_unsaved: invalid option"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
begin
|
147
|
+
@workbook.Close if alive?
|
148
|
+
@workbook = nil unless alive?
|
149
|
+
raise ExcelUserCanceled, "close: canceled by user" if alive? && @options[:if_unsaved] == :excel && (not @workbook.Saved)
|
150
|
+
ensure
|
151
|
+
if @options[:if_unsaved] == :excel then
|
152
|
+
@excel_app.DisplayAlerts = old_displayalerts
|
153
|
+
end
|
154
|
+
end
|
155
|
+
#@excel_app.Workbooks.Close
|
156
|
+
#@excel_app.Quit
|
157
|
+
end
|
158
|
+
|
159
|
+
# returns true, if the work book is alive, false otherwise
|
160
|
+
def alive?
|
161
|
+
begin
|
162
|
+
@workbook.Name
|
163
|
+
true
|
164
|
+
rescue
|
165
|
+
@workbook = nil # dead object won't be alive again
|
166
|
+
#puts $!.message
|
167
|
+
false
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# returns the full file name of the book
|
172
|
+
def filename
|
173
|
+
@workbook.Fullname.tr('\\','/') rescue nil
|
174
|
+
end
|
175
|
+
|
176
|
+
#returns true, if the full book names and excel appications are identical, false, otherwise
|
177
|
+
def == other_book
|
178
|
+
other_book.is_a?(Book) &&
|
179
|
+
@excel_app == other_book.excel_app &&
|
180
|
+
self.filename == other_book.filename
|
181
|
+
end
|
182
|
+
|
183
|
+
|
184
|
+
attr_reader :excel_app
|
185
|
+
|
186
|
+
# saves a book.
|
187
|
+
# returns true, if successfully saved, nil otherwise
|
188
|
+
def save
|
189
|
+
raise IOError, "Not opened for writing(open with :read_only option)" if @options[:read_only]
|
190
|
+
if @workbook then
|
191
|
+
@workbook.Save
|
192
|
+
true
|
193
|
+
else
|
194
|
+
nil
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# saves a book.
|
199
|
+
#
|
200
|
+
# options:
|
201
|
+
# :if_exists if a file with the same name exists, then
|
202
|
+
# :raise -> raise an exception, dont't write the file (default)
|
203
|
+
# :overwrite -> write the file, delete the old file
|
204
|
+
# :excel -> give control to Excel
|
205
|
+
# returns true, if successfully saved, nil otherwise
|
206
|
+
def save_as(file = nil, opts = {:if_exists => :raise} )
|
207
|
+
raise IOError, "Not opened for writing(open with :read_only option)" if @options[:read_only]
|
208
|
+
dirname, basename = File.split(file)
|
209
|
+
file_format =
|
210
|
+
case File.extname(basename)
|
211
|
+
when '.xls' : RobustExcelOle::XlExcel8
|
212
|
+
when '.xlsx': RobustExcelOle::XlOpenXMLWorkbook
|
213
|
+
when '.xlsm': RobustExcelOle::XlOpenXMLWorkbookMacroEnabled
|
214
|
+
end
|
215
|
+
if File.exist?(file) then
|
216
|
+
case opts[:if_exists]
|
217
|
+
when :overwrite
|
218
|
+
File.delete(file)
|
219
|
+
#File.delete(absolute_path(File.join(dirname, basename)))
|
220
|
+
when :excel
|
221
|
+
old_displayalerts = @excel_app.DisplayAlerts
|
222
|
+
@excel_app.DisplayAlerts = true
|
223
|
+
when :raise
|
224
|
+
raise ExcelErrorSave, "book already exists: #{basename}"
|
225
|
+
else
|
226
|
+
raise ExcelErrorSave, ":if_exists: invalid option"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
begin
|
230
|
+
@workbook.SaveAs(absolute_path(File.join(dirname, basename)), file_format)
|
231
|
+
rescue WIN32OLERuntimeError => msg
|
232
|
+
if msg.message =~ /SaveAs/ and msg.message =~ /Workbook/ then
|
233
|
+
#toDo: more condition for cancel. if user cancels: raise an exception
|
234
|
+
if opts[:if_exists] == :excel then
|
235
|
+
raise ExcelUserCanceled, "save: canceled by user"
|
236
|
+
else
|
237
|
+
return nil
|
238
|
+
end
|
239
|
+
# another possible semantics. raise ExcelErrorSaveFailed, "could not save Workbook"
|
240
|
+
else
|
241
|
+
raise ExcelErrorSaveUnknown, "unknown WIN32OELERuntimeError:\n#{msg.message}"
|
242
|
+
end
|
243
|
+
ensure
|
244
|
+
if opts[:if_exists] == :excel then
|
245
|
+
@excel_app.DisplayAlerts = old_displayalerts
|
246
|
+
end
|
247
|
+
end
|
248
|
+
true
|
249
|
+
end
|
250
|
+
|
251
|
+
|
252
|
+
def [] sheet
|
253
|
+
sheet += 1 if sheet.is_a? Numeric
|
254
|
+
RobustExcelOle::Sheet.new(@workbook.Worksheets.Item(sheet))
|
255
|
+
end
|
256
|
+
|
257
|
+
def each
|
258
|
+
@workbook.Worksheets.each do |sheet|
|
259
|
+
yield RobustExcelOle::Sheet.new(sheet)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# adds a sheet
|
264
|
+
def add_sheet(sheet = nil, opts = { })
|
265
|
+
if sheet.is_a? Hash
|
266
|
+
opts = sheet
|
267
|
+
sheet = nil
|
268
|
+
end
|
269
|
+
|
270
|
+
new_sheet_name = opts.delete(:as)
|
271
|
+
|
272
|
+
after_or_before, base_sheet = opts.to_a.first || [:after, RobustExcelOle::Sheet.new(@workbook.Worksheets.Item(@workbook.Worksheets.Count))]
|
273
|
+
base_sheet = base_sheet.sheet
|
274
|
+
sheet ? sheet.Copy({ after_or_before.to_s => base_sheet }) : @workbook.WorkSheets.Add({ after_or_before.to_s => base_sheet })
|
275
|
+
|
276
|
+
new_sheet = RobustExcelOle::Sheet.new(@excel_app.Activesheet)
|
277
|
+
new_sheet.name = new_sheet_name if new_sheet_name
|
278
|
+
new_sheet
|
279
|
+
end
|
280
|
+
|
281
|
+
# absolute path of the file
|
282
|
+
def absolute_path(file)
|
283
|
+
file = File.expand_path(file)
|
284
|
+
file = RobustExcelOle::Cygwin.cygpath('-w', file) if RUBY_PLATFORM =~ /cygwin/
|
285
|
+
WIN32OLE.new('Scripting.FileSystemObject').GetAbsolutePathName(file)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|
290
|
+
|
291
|
+
class ExcelUserCanceled < RuntimeError
|
292
|
+
end
|
293
|
+
|
294
|
+
class ExcelError < RuntimeError
|
295
|
+
end
|
296
|
+
|
297
|
+
class ExcelErrorSave < ExcelError
|
298
|
+
end
|
299
|
+
|
300
|
+
class ExcelErrorSaveFailed < ExcelErrorSave
|
301
|
+
end
|
302
|
+
|
303
|
+
class ExcelErrorSaveUnknown < ExcelErrorSave
|
304
|
+
end
|
305
|
+
|
306
|
+
class ExcelErrorOpen < ExcelError
|
307
|
+
end
|
308
|
+
|
309
|
+
class ExcelErrorClose < ExcelError
|
310
|
+
end
|