robust_excel_ole 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -14,9 +14,14 @@ module RobustExcelOle
14
14
 
15
15
  # opens a book.
16
16
  #
17
+ # when reopening a book that was opened and closed before, transparency identity is ensured:
18
+ # same Book objects refer to the same Excel files, and vice versa
19
+ #
17
20
  # options:
18
- # :default_excel if the book was already open in an Excel instance, then open it there, otherwise:
19
- # :reuse (default) -> connect to a running Excel instance if it exists, open in a new Excel otherwise
21
+ # :default_excel if the book was already open in an Excel instance, then open it there.
22
+ # Otherwise, i.e. if the book was not open before or the Excel instance
23
+ # :reuse (default) -> connect to a (the first opened) running Excel instance
24
+ # if it exists, open in a new Excel otherwise
20
25
  # :new -> open in a new Excel instance
21
26
  # <instance> -> open in the given Excel instance
22
27
  # :force_excel no matter whether the book was already open
@@ -28,53 +33,62 @@ module RobustExcelOle
28
33
  # :take_writable -> use the Excel instance in which the book is writable,
29
34
  # if such an Excel instance exists
30
35
  # :force_writability -> make it writable in the desired Excel
36
+ # (does not work yet)
31
37
  # :if_locked_unsaved if the book is open in another Excel instance and contains unsaved changes
32
38
  # :raise -> raise an exception
33
39
  # :save -> save the unsaved book
34
40
  # (not implemented yet)
35
41
  # :if_unsaved if an unsaved book with the same name is open, then
36
- # :raise (default) -> raise an exception
37
- # :forget -> close the unsaved book, open the new book
38
- # :accept -> let the unsaved book open
39
- # :alert -> give control to Excel
40
- # :new_excel -> open the new book in a new Excel
42
+ # :raise -> raise an exception
43
+ # :forget -> close the unsaved book, open the new book
44
+ # :accept -> let the unsaved book open
45
+ # :alert -> give control to Excel
46
+ # :new_excel (default) -> open the new book in a new Excel
41
47
  # :if_obstructed if a book with the same name in a different path is open, then
42
- # :raise (default) -> raise an exception
43
- # :forget -> close the old book, open the new book
44
- # :save -> save the old book, close it, open the new book
45
- # :close_if_saved -> close the old book and open the new book, if the old book is saved
46
- # raise an exception otherwise
47
- # :new_excel -> open the new book in a new Excel
48
- # :reuse_excel -> try the next free running Excel, if it exists, open a new Excel, else
49
- # :read_only open in read-only mode (default: false)
48
+ # :raise -> raise an exception
49
+ # :forget -> close the old book, open the new book
50
+ # :save -> save the old book, close it, open the new book
51
+ # :close_if_saved -> close the old book and open the new book, if the old book is saved
52
+ # raise an exception otherwise
53
+ # :new_excel (default) -> open the new book in a new Excel
54
+ # :read_only open in read-only mode (default: false)
50
55
  # :displayalerts enable DisplayAlerts in Excel (default: false)
51
56
  # :visible make visibe in Excel (default: false)
52
- # If :default_excel is set, then DisplayAlerts and Visible are set only if these parameters are given,
53
- # not set by default
54
-
55
-
57
+ # if :default_excel is set, then DisplayAlerts and Visible are set only if these parameters are given
58
+
56
59
  def open(file, opts={ }, &block)
57
60
  @options = {
58
61
  :excel => :reuse,
59
62
  :default_excel => :reuse,
60
63
  :if_locked => :readonly,
61
- :if_unsaved => :raise,
62
- :if_obstructed => :raise,
64
+ :if_unsaved => :new_excel,
65
+ :if_obstructed => :new_excel,
63
66
  :read_only => false
64
67
  }.merge(opts)
65
68
  #self.set_defaults(opts) ???
66
69
  book = nil
67
70
  if (not (@options[:force_excel] == :new && (not @options[:if_locked] == :take_writable)))
68
- book = book_store.fetch(file, :readonly_excel => (@options[:read_only] ? @options[:force_excel] : nil)) rescue nil
71
+ # if readonly is true, then prefer a book that is given in force_excel if this option is set
72
+ book = book_store.fetch(file, :prefer_writable => (not @options[:read_only]), :prefer_excel => (@options[:read_only] ? @options[:force_excel] : nil)) rescue nil
69
73
  if book
70
- if (not @options[:force_excel] || (@options[:force_excel] == book.excel))
74
+ if ((not @options[:force_excel]) || (@options[:force_excel] == book.excel))
75
+ if (not book.excel.alive?)
76
+ book.get_excel
77
+ end
71
78
  if book.excel.alive?
72
79
  # condition: :if_unsaved is not set or :accept or workbook is not unsaved
73
80
  if_unsaved_not_set_or_accept_or_workbook_saved = (@options[:if_unsaved] == :accept || @options[:if_unsaved] == :raise || (not book.workbook) || book.workbook.Saved)
74
- if ((not book.alive?) || if_unsaved_not_set_or_accept_or_workbook_saved)
81
+ if ((not book.alive?) || if_unsaved_not_set_or_accept_or_workbook_saved)
75
82
  book.set_defaults(opts)
83
+ # if the book is opened with a different readonly mode in the same Excel,
84
+ # then save it, close and open the book with the new readonly mode
85
+ if (book.alive? && (not book.readonly == @options[:read_only]))
86
+ book.Save unless (book.readonly || book.saved)
87
+ book.workbook.Close if book.alive?
88
+ book.workbook = nil unless book.alive?
89
+ end
76
90
  # reopen the book
77
- book.get_workbook
91
+ book.get_workbook
78
92
  end
79
93
  return book if book.alive? && if_unsaved_not_set_or_accept_or_workbook_saved
80
94
  end
@@ -107,8 +121,8 @@ module RobustExcelOle
107
121
  :excel => :reuse,
108
122
  :default_excel => :reuse,
109
123
  :if_locked => :readonly,
110
- :if_unsaved => :raise,
111
- :if_obstructed => :raise,
124
+ :if_unsaved => :new_excel,
125
+ :if_obstructed => :new_excel,
112
126
  :read_only => false
113
127
  }.merge(opts)
114
128
  end
@@ -203,8 +217,6 @@ module RobustExcelOle
203
217
  end
204
218
 
205
219
  def open_workbook
206
- #p "open_workbook:"
207
- #p "@file:#{@file}"
208
220
  if ((not @workbook) || (@options[:if_unsaved] == :alert) || @options[:if_obstructed]) then
209
221
  begin
210
222
  filename = RobustExcelOle::absolute_path(@file)
@@ -225,10 +237,10 @@ module RobustExcelOle
225
237
  #
226
238
  # options:
227
239
  # :if_unsaved if book is unsaved
228
- # :raise -> raise an exception (default)
229
- # :save -> save the book before it is closed
230
- # :forget -> close the book
231
- # :alert -> give control to excel
240
+ # :raise -> raise an exception
241
+ # :save (default) -> save the book before it is closed
242
+ # :forget -> close the book
243
+ # :alert -> give control to excel
232
244
  def close(opts = {:if_unsaved => :raise})
233
245
  if ((alive?) && (not @workbook.Saved) && (not @workbook.ReadOnly)) then
234
246
  case opts[:if_unsaved]
@@ -261,37 +273,42 @@ module RobustExcelOle
261
273
 
262
274
  public
263
275
 
264
- # modify a book such that its state remains unchanged.
276
+ # modify a book such that its state (open/close, saved/unsaved, readonly/writable) remains unchanged.
265
277
  # options:
266
- # :visible: Make the book visible in the Excel (default: false)
278
+ # :visible: Make the book visible in the Excel (default: false)
267
279
  # :keep_open: let the book open after modification (default: false)
268
- # if the book is read_only and modified (unsaved), then
269
- # only the saved version of the book is unobtrusively modified,
270
- # not the current changed version
271
- # returns the block result
280
+ # :read_only: Open the book unobtrusively for reading only (default: false)
281
+ # :use_this: if the book is opened only as ReadOnly and shall be modified, then
282
+ # true: close it and open it as writable in the excel instance where it was open so far
283
+ # false(default) open it as writable in another running excel instance, if it exists
284
+ # in a new excel instance, otherwise
272
285
  def self.unobtrusively(file, opts = { })
273
286
  options = {
274
287
  :keep_open => false,
275
- :visible => false
288
+ :visible => false,
289
+ :read_only => false,
290
+ :use_this => false
276
291
  }.merge(opts)
277
- book = book_store.fetch(file)
292
+ book = book_store.fetch(file, :prefer_writable => (not options[:read_only]))
278
293
  was_not_alive_or_nil = book.nil? || (not book.alive?)
279
- was_saved = was_not_alive_or_nil ? true : book.Saved
280
- was_readonly = was_not_alive_or_nil ? false : book.ReadOnly
294
+ was_saved = was_not_alive_or_nil ? true : book.saved
295
+ was_readonly = book.readonly unless was_not_alive_or_nil
281
296
  old_book = book if was_readonly
282
- old_visible = book ? book.visible : false
283
- begin
284
- book = was_not_alive_or_nil ? open(file, :if_obstructed => :new_excel) :
285
- (was_readonly ? open(file, :force_excel => :new) : book)
286
- book.visible = options[:visible]
297
+ old_visible = (book && book.excel.alive?) ? book.excel.visible : false
298
+ begin
299
+ book = was_not_alive_or_nil ? open(file) :
300
+ (((not was_readonly) || options[:read_only]) ? book :
301
+ (options[:use_this] ? open(file, :force_excel => book.excel) : open(file, :force_excel => :new)))
302
+ book.excel.visible = options[:visible]
287
303
  yield book
288
304
  ensure
289
- book.save if (was_not_alive_or_nil || was_saved || was_readonly) && (not book.Saved)
290
- if was_readonly
305
+ book.save if (was_not_alive_or_nil || was_saved || (was_readonly && (not options[:read_only]))) && (not book.saved)
306
+ # book was open, readonly and shoud be modified
307
+ if (not was_not_alive_or_nil) && (not options[:read_only]) && was_readonly && options[:use_this]
291
308
  book.close
292
- book = old_book
309
+ open(file, :if_obstructed => :new_excel, :read_only => true)
293
310
  end
294
- book.visible = old_visible
311
+ book.excel.visible = old_visible
295
312
  book.close if (was_not_alive_or_nil && (not opts[:keep_open]))
296
313
  end
297
314
  end
@@ -332,35 +349,20 @@ module RobustExcelOle
332
349
  @workbook.Fullname.tr('\\','/') rescue nil
333
350
  end
334
351
 
352
+ def readonly
353
+ @workbook.ReadOnly
354
+ end
355
+
356
+ def saved
357
+ @workbook.Saved
358
+ end
359
+
335
360
  # returns true, if the full book names and excel appications are identical, false, otherwise
336
361
  def == other_book
337
362
  other_book.is_a?(Book) &&
338
363
  @excel == other_book.excel &&
339
364
  self.filename == other_book.filename
340
365
  end
341
-
342
- # returns if the Excel instance is visible
343
- def visible
344
- @excel.visible
345
- end
346
-
347
- # make the Excel instance visible or invisible
348
- # option: visible_value true -> make Excel visible, false -> make Excel invisible
349
- def visible= visible_value
350
- @excel.visible = visible_value
351
- end
352
-
353
- # returns if DisplayAlerts is enabed in the Excel instance
354
- def displayalerts
355
- @excel.displayalerts
356
- end
357
-
358
- # enable in the Excel instance Dispayalerts
359
- # option: displayalerts_value true -> enable DisplayAlerts, false -> disable DispayAlerts
360
- def displayalerts= displayalerts_value
361
- @excel.displayalerts = displayalerts_value
362
- end
363
-
364
366
 
365
367
  # saves a book.
366
368
  # returns true, if successfully saved, nil otherwise
@@ -10,33 +10,34 @@ module RobustExcelOle
10
10
  end
11
11
 
12
12
  # returns a book with the given filename, if it was open once
13
- # preference order: writable book, readonly unsaved book, readonly book (the last one), closed book
14
- # options:
15
- # :readonly_excel => <instance> -> return the book that was open in the given excel instance,
16
- # even if it is not writable, if such a book exists
17
- # prefer the writable book as described above, otherwise
18
- def fetch(filename, options = { })
13
+ # prefers open books to closed books, and among them, prefers more recently opened books
14
+ # options: :prefer_writable return the writable book, if it is open (default: true)
15
+ # return the book according to the preference order mentioned above, otherwise
16
+ # :prefer_excel return the book in the given excel instance, if it exists
17
+ # proceed according to prefer_writable otherwise
18
+ def fetch(filename, options = {:prefer_writable => true })
19
19
  filename_key = RobustExcelOle::canonize(filename)
20
- readonly_book = readonly_unsaved_book = closed_book = result = nil
21
- books = @filename2books[filename_key]
22
- return nil unless books
23
- books.each do |book|
24
- return book if options[:readonly_excel] && book.excel == options[:readonly_excel]
25
- if book.alive?
26
- if (not book.ReadOnly)
27
- if options[:readonly_excel]
28
- result = book
29
- else
30
- return book
31
- end
20
+ weakref_books = @filename2books[filename_key]
21
+ return nil unless weakref_books
22
+ result = open_book = closed_book = nil
23
+ weakref_books.each do |wr_book|
24
+ if (not wr_book.weakref_alive?)
25
+ @filename2books[filename_key].delete(wr_book)
26
+ else
27
+ if options[:prefer_excel] && wr_book.excel == options[:prefer_excel]
28
+ result = wr_book
29
+ break
30
+ end
31
+ if wr_book.alive?
32
+ open_book = wr_book
33
+ break if ((not wr_book.readonly) && options[:prefer_writable])
32
34
  else
33
- book.Saved ? readonly_book = book : readonly_unsaved_book = book
35
+ closed_book = wr_book
34
36
  end
35
- else
36
- closed_book = book
37
37
  end
38
38
  end
39
- result ? result : (readonly_unsaved_book ? readonly_unsaved_book : (readonly_book ? readonly_book : closed_book))
39
+ result = result ? result : (open_book ? open_book : closed_book)
40
+ result.__getobj__ if result
40
41
  end
41
42
 
42
43
  # stores a book
@@ -44,12 +45,29 @@ module RobustExcelOle
44
45
  filename_key = RobustExcelOle::canonize(book.filename)
45
46
  if book.stored_filename
46
47
  old_filename_key = RobustExcelOle::canonize(book.stored_filename)
48
+ # deletes the weak reference to the book
47
49
  @filename2books[old_filename_key].delete(book)
48
50
  end
49
- @filename2books[filename_key] |= [book]
51
+ @filename2books[filename_key] |= [WeakRef.new(book)]
50
52
  book.stored_filename = book.filename
51
53
  end
52
54
 
55
+ # returns all excel instances and the workbooks that are open in them
56
+ # first: only the stored excel instances are considered
57
+ def excel_list
58
+ excel2books = Hash.new {|hash, key| hash[key] = [] }
59
+ if @filename2books
60
+ @filename2books.each do |filename,books|
61
+ if books
62
+ books.each do |book|
63
+ excel2books[book.excel] |= [book.workbook]
64
+ end
65
+ end
66
+ end
67
+ end
68
+ excel2books
69
+ end
70
+
53
71
  # prints the book store
54
72
  def print
55
73
  p "@filename2books:"
@@ -58,10 +76,16 @@ module RobustExcelOle
58
76
  p " filename: #{filename}"
59
77
  p " books:"
60
78
  p " []" if books == []
61
- books.each do |book|
62
- p "#{book}"
63
- p "excel: #{book.excel}"
64
- p "alive: #{book.alive?}"
79
+ if books
80
+ books.each do |book|
81
+ if book.weakref_alive?
82
+ p "#{book}"
83
+ p "excel: #{book.excel}"
84
+ p "alive: #{book.alive?}"
85
+ else
86
+ p "weakref not alive"
87
+ end
88
+ end
65
89
  end
66
90
  end
67
91
  end
@@ -139,7 +139,6 @@ module RobustExcelOle
139
139
  excel.Workbooks.Close
140
140
  excel_hwnd = excel.HWnd
141
141
  excel.Quit
142
- #excel.ole_free
143
142
  weak_excel_ref = WeakRef.new(excel)
144
143
  excel = nil
145
144
  GC.start
@@ -1,3 +1,3 @@
1
1
  module RobustExcelOle
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
@@ -8,8 +8,8 @@ Gem::Specification.new do |s|
8
8
  s.authors = ["traths"]
9
9
  s.email = ["Thomas.Raths@gmx.net"]
10
10
  s.homepage = "https://github.com/Thomas008/robust_excel_ole"
11
- s.summary = "RobustExcelOle wraps the win32ole library and implements various operations in Excel"
12
- s.description = "RobustExcelOle wraps the win32ole library, and allows to perform various operation in Excel with ruby in a reliable way. Detailed description please see the README."
11
+ s.summary = "RobustExcelOle processes Excel files and wraps the win32ole library."
12
+ s.description = "RobustExcelOle processes Excel files, provides all win32ole operations, convenient methods for opening, saving and closing, and implements an Excel file management system."
13
13
 
14
14
  s.rubyforge_project = "robust_excel_ole"
15
15
 
@@ -23,11 +23,6 @@ Gem::Specification.new do |s|
23
23
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
24
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
25
  s.require_paths = ["lib"]
26
- s.add_development_dependency "rake", '>= 0.9.2'
27
26
  s.add_development_dependency "rspec", '>= 2.6.0'
28
- s.add_development_dependency "rb-fchange", '>= 0.0.5'
29
- s.add_development_dependency "wdm", '>= 0.0.3'
30
- s.add_development_dependency "win32console", '>= 1.3.2'
31
- s.add_development_dependency "guard-rspec", '>= 2.1.1'
32
27
  s.required_ruby_version = '>= 1.8.6'
33
28
  end