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 ADDED
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ vendor/bundle
6
+ *~
7
+ .DS_Store
8
+ doc/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in robust_excel_ole.gemspec
4
+ gemspec
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,9 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require "rake"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec) do |s|
7
+ s.rspec_opts = '-f d'
8
+ end
9
+ task :default => :spec
@@ -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