robust_excel_ole 0.3.0 → 0.3.1

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.
@@ -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