robust_excel_ole 1.13 → 1.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,17 +6,17 @@
6
6
 
7
7
  Imagine, you have opened a workbook with
8
8
 
9
- book = Workbook.open('spec/data/workbook.xls', :visible => true)
9
+ workbook = Workbook.open('spec/data/workbook.xls', :visible => true)
10
10
 
11
11
  and have modified it.
12
12
 
13
13
  You can save the workbook by
14
14
 
15
- book.save
15
+ workbook.save
16
16
 
17
17
  If you want to save a workbook with a file name, then use
18
18
 
19
- book.save_as('new_workbook.xls')
19
+ workbook.save_as('new_workbook.xls')
20
20
 
21
21
  The options and respective valid values are the following:
22
22
 
@@ -33,19 +33,19 @@ If a workbook with the file name already exists, then
33
33
 
34
34
  For example, you want to save a workbook and overwrite the file if it exists before, then use
35
35
 
36
- book.save_as('another_workbook.xls', :if_exists => :overwrite)
36
+ workbook.save_as('another_workbook.xls', :if_exists => :overwrite)
37
37
 
38
38
  If a workbook blocks the workbook that should be saved, then the former one can be saved and closed before.
39
39
 
40
- book = Workbook.open('spec/data/workbook.xls')
41
- book2 = Workbook.open('spec/data/another_workbook.xls')
42
- book2.save_as('dir/workbook.xls', :if_exists => :overwrite, :if_obstructed => :save)
40
+ workbook = Workbook.open('spec/data/workbook.xls')
41
+ workbook2 = Workbook.open('spec/data/another_workbook.xls')
42
+ workbook2.save_as('dir/workbook.xls', :if_exists => :overwrite, :if_obstructed => :save)
43
43
 
44
44
  === Closing a workbook.
45
45
 
46
46
  You can close the workbook with the command
47
47
 
48
- book.close
48
+ workbook.close
49
49
 
50
50
  There is one option: +:if_unsaved+ . It can have one of the following values:
51
51
 
@@ -62,7 +62,7 @@ The option specifies: If the workbook is unsaved, then
62
62
 
63
63
  You can open a workbook with given file name.
64
64
 
65
- book = Workbook.open('spec/data/workbook.xls')
65
+ workbook = Workbook.open('spec/data/workbook.xls')
66
66
 
67
67
  You can save a workbook with given file name, if it is open.
68
68
 
@@ -6,30 +6,30 @@ Worksheets are represented by Worksheet objects.
6
6
 
7
7
  Assume you have opened a workbook
8
8
 
9
- book = Workbook.open('spec/data/workbook.xls', :visible => true)
9
+ workbook = Workbook.open('spec/data/workbook.xls', :visible => true)
10
10
 
11
11
  === Accessing a worksheet.
12
12
 
13
13
  You can access a worksheet by giving the number
14
14
 
15
- sheet = book.sheet(1)
15
+ worksheet = book.sheet(1)
16
16
 
17
17
  or its name
18
18
 
19
- sheet = book.sheet('Sheet1')
19
+ worksheet = book.sheet('Sheet1')
20
20
 
21
21
  You can get the first and last worksheet with
22
22
 
23
- sheet = book.first_sheet
23
+ worksheet = book.first_sheet
24
24
 
25
25
  and
26
26
 
27
- sheet = book.last_sheet
27
+ worksheet = book.last_sheet
28
28
 
29
- You can access all Sheet objects by using the methods Workbook#each.
29
+ You can access all Worksheet objects by using the methods Workbook#each.
30
30
 
31
- book.each do |sheet|
32
- # do something with sheet
31
+ workbook.each do |worksheet|
32
+ # do something with worksheet
33
33
  end
34
34
 
35
35
  Once you have got a Sheet object (in RobustExcelOle), you can apply all VBA methods that you would apply to a VBA Worksheet object
@@ -40,38 +40,38 @@ For some common and complex tasks you can apply methods of RobustExcelOle.
40
40
 
41
41
  You can read and change the worksheet name.
42
42
 
43
- sheet1.name
43
+ worksheet1.name
44
44
  # => "Sheet1"
45
45
 
46
- sheet1.name = "new_sheet"
46
+ worksheet1.name = "new_sheet"
47
47
 
48
48
  === Adding and copying a worksheet.
49
49
 
50
50
  You can add (append) an empty worksheet using
51
51
 
52
- book.add_empty_sheet
52
+ workbook.add_empty_sheet
53
53
 
54
54
  Additionally you can name it.
55
55
 
56
- book.add_empty_sheet(:as => 'sheet_name')
56
+ workbook.add_empty_sheet(:as => 'sheet_name')
57
57
 
58
58
  You can specify the position of the added empty worksheet.
59
59
 
60
- book.add_empty_sheet(:as => 'new_name', :before => another_sheet)
60
+ workbook.add_empty_sheet(:as => 'new_name', :before => another_sheet)
61
61
 
62
62
  You can copy a worksheet and add it.
63
63
 
64
- book.copy_sheet sheet
64
+ workbook.copy_sheet sheet
65
65
 
66
66
  Additionally you can specify a name and a position.
67
67
 
68
- book.copy_sheet(sheet, :as => 'new_name', :after => another_sheet)
68
+ workbook.copy_sheet(sheet, :as => 'new_name', :after => another_sheet)
69
69
 
70
70
  If you want to copy a worksheet, if a worksheet +sheet+ is given, and add an empty worksheet, if no worksheet is given, then use
71
71
 
72
- book.add_or_copy_sheet
72
+ workbook.add_or_copy_sheet
73
73
 
74
- book.add_or_copy_sheet(sheet, :as => 'new_name', :after => another_sheet)
74
+ workbook.add_or_copy_sheet(sheet, :as => 'new_name', :after => another_sheet)
75
75
 
76
76
  Note, that running in jruby, due to some restrictions of jruby, there is a workaround when adding or copy a worksheet at the end (appending): the last worksheet is being copied and deleted afterwards, in order to serve as a dummy worksheet. This may cause a different behaviour.
77
77
 
@@ -14,5 +14,7 @@ require File.join(File.dirname(__FILE__), 'robust_excel_ole/worksheet')
14
14
  require File.join(File.dirname(__FILE__), 'robust_excel_ole/cell')
15
15
  require File.join(File.dirname(__FILE__), 'robust_excel_ole/range')
16
16
  require File.join(File.dirname(__FILE__), 'robust_excel_ole/cygwin') if RUBY_PLATFORM =~ /cygwin/
17
- #+#require "robust_excel_ole/version"
18
17
  require File.join(File.dirname(__FILE__), 'robust_excel_ole/version')
18
+
19
+ include RobustExcelOle
20
+ include General
@@ -76,12 +76,7 @@ module RobustExcelOle
76
76
  ole_xl = win32ole_excel unless win32ole_excel.nil?
77
77
  options = { :reuse => true }.merge(options)
78
78
  if options[:reuse] == true && ole_xl.nil?
79
- ole_xl = if JRUBY_BUG_CONNECTEXCEL
80
- excel_instance = known_excel_instance
81
- excel_instance.ole_excel unless excel_instance.nil?
82
- else
83
- current_excel
84
- end
79
+ ole_xl = current_ole_excel
85
80
  end
86
81
  connected = (not ole_xl.nil?) && win32ole_excel.nil?
87
82
  ole_xl ||= WIN32OLE.new('Excel.Application')
@@ -142,33 +137,6 @@ module RobustExcelOle
142
137
 
143
138
  private
144
139
 
145
- # returns a Win32OLE object that represents a Excel instance to which Excel connects
146
- # connects to the first opened Excel instance
147
- # if this Excel instance is being closed, then Excel creates a new Excel instance
148
- # @private
149
- def self.current_excel
150
- result = if result.nil?
151
- begin
152
- WIN32OLE.connect('Excel.Application')
153
- rescue
154
- nil
155
- end
156
- end
157
- if result
158
- begin
159
- result.Visible # send any method, just to see if it responds
160
- rescue
161
- trace 'dead excel ' + (begin
162
- "Window-handle = #{result.HWnd}"
163
- rescue
164
- 'without window handle'
165
- end)
166
- return nil
167
- end
168
- end
169
- result
170
- end
171
-
172
140
  # retain the saved status of all workbooks
173
141
  # @private
174
142
  def retain_saved_workbooks
@@ -424,6 +392,43 @@ module RobustExcelOle
424
392
  @@hwnd2excel.size
425
393
  end
426
394
 
395
+ #private
396
+
397
+ # returns a Win32OLE object that represents a Excel instance to which Excel connects
398
+ # connects to the first opened Excel instance
399
+ # if this Excel instance is being closed, then Excel creates a new Excel instance
400
+ # @private
401
+ def self.current_ole_excel
402
+ if JRUBY_BUG_CONNECT
403
+ result = known_excel_instance
404
+ if result.nil?
405
+ if excels_number > 0
406
+ dummy_ole_workbook = WIN32OLE.connect(General.absolute_path('___dummy_workbook.xls')) rescue nil
407
+ result = dummy_ole_workbook.Application
408
+ visible_status = result.Visible
409
+ dummy_ole_workbook.Close
410
+ dummy_ole_workbook = nil
411
+ result.Visible = visible_status
412
+ end
413
+ end
414
+ else
415
+ result = WIN32OLE.connect('Excel.Application') rescue nil
416
+ end
417
+ if result
418
+ begin
419
+ result.Visible # send any method, just to see if it responds
420
+ rescue
421
+ trace 'dead excel ' + (begin
422
+ "Window-handle = #{result.HWnd}"
423
+ rescue
424
+ 'without window handle'
425
+ end)
426
+ return nil
427
+ end
428
+ end
429
+ result
430
+ end
431
+
427
432
  # returns an Excel instance
428
433
  def self.known_excel_instance
429
434
  @@hwnd2excel.each do |hwnd, wr_excel|
@@ -173,8 +173,7 @@ module RobustExcelOle
173
173
  home = homes.find { |h| !ENV[h].nil? }
174
174
  reo_log_dir = ENV[home]
175
175
  else
176
- #reo_log_dir = REO_LOG_DIR
177
- reo_log_dir = "C:/Users/User"
176
+ reo_log_dir = REO_LOG_DIR
178
177
  end
179
178
  File.open(reo_log_dir + '/' + REO_LOG_FILE,'a') do |file|
180
179
  file.puts text
@@ -1,3 +1,3 @@
1
1
  module RobustExcelOle
2
- VERSION = "1.13"
2
+ VERSION = "1.14"
3
3
  end
@@ -11,10 +11,13 @@ module RobustExcelOle
11
11
 
12
12
  class Workbook < RangeOwners
13
13
 
14
+ #include General
15
+
14
16
  attr_accessor :excel
15
17
  attr_accessor :ole_workbook
16
18
  attr_accessor :stored_filename
17
19
  attr_accessor :color_if_modified
20
+ attr_accessor :was_open
18
21
  attr_reader :workbook
19
22
 
20
23
  alias ole_object ole_workbook
@@ -39,7 +42,7 @@ module RobustExcelOle
39
42
 
40
43
 
41
44
  # opens a workbook.
42
- # @param [String] file the file name
45
+ # @param [String] file_or_workbook a file name or WIN32OLE workbook
43
46
  # @param [Hash] opts the options
44
47
  # @option opts [Hash] :default or :d
45
48
  # @option opts [Hash] :force or :f
@@ -106,7 +109,8 @@ module RobustExcelOle
106
109
  rescue
107
110
  trace "#{$!.message}"
108
111
  end
109
- if book
112
+ if book
113
+ book.was_open = book.alive?
110
114
  # drop the fetched workbook if it shall be opened in another Excel instance
111
115
  # or the workbook is an unsaved workbook that should not be accepted
112
116
  if (options[:force][:excel].nil? || options[:force][:excel] == :current || forced_excel == book.excel) &&
@@ -226,51 +230,33 @@ module RobustExcelOle
226
230
  raise ExcelREOError, "excel is not alive" unless @excel && @excel.alive?
227
231
  end
228
232
 
229
- # @private
230
- # restriction for jruby: does not manage conflicts with blocking or unsaved workbooks
233
+ # @private
231
234
  def ensure_workbook(filename, options)
232
- if options[:if_unsaved]==:accept &&
233
- ((options[:read_only]==true && self.ReadOnly==false) || (options[:read_only]==false && self.ReadOnly==true))
234
- raise OptionInvalid, ":if_unsaved:accept and change of read-only mode is not possible"
235
- end
236
235
  unless @ole_workbook && alive?
237
236
  filename = @stored_filename ? @stored_filename : filename
238
237
  manage_nonexisting_file(filename,options)
239
238
  excel_option = options[:force][:excel].nil? ? options[:default][:excel] : options[:force][:excel]
240
- if JRUBY_BUG_CONNECT
241
- (excel_option.nil? || excel_option == :current) && filename[0] != '/'
242
- begin
239
+ ensure_excel(options)
240
+ workbooks = @excel.Workbooks
241
+ @ole_workbook = workbooks.Item(File.basename(filename)) rescue nil if @ole_workbook.nil?
242
+ if @ole_workbook
243
+ @was_open = true
244
+ manage_blocking_or_unsaved_workbook(filename,options)
245
+ else
246
+ if excel_option.nil? || excel_option == :current &&
247
+ (!JRUBY_BUG_CONNECT || filename[0] != '/')
243
248
  connect(filename,options)
244
- rescue WorkbookConnectingUnsavedError
245
- raise WorkbookNotSaved, "workbook is already open but not saved: #{File.basename(filename).inspect}"
246
- rescue WorkbookConnectingBlockingError
247
- raise WorkbookBlocked, "can't open workbook #{filename}"+
248
- "\nbecause it is being blocked by a workbook with the same name in a different path."
249
- rescue WorkbookConnectingUnknownError
250
- raise WorkbookREOError, "can't connect to workbook #{filename}"
249
+ else
250
+ open_or_create_workbook(filename,options)
251
251
  end
252
- manage_unsaved_workbook(filename,options) unless @ole_workbook.Saved
253
- else
254
- ensure_excel(options)
255
- workbooks = @excel.Workbooks
256
- @ole_workbook = workbooks.Item(File.basename(filename)) rescue nil if @ole_workbook.nil?
257
- if @ole_workbook
258
- manage_blocking_or_unsaved_workbook(filename,options)
259
- else
260
- if excel_option.nil? || excel_option == :current &&
261
- (!JRUBY_BUG_CONNECT || filename[0] != '/')
262
- connect(filename,options)
263
- else
264
- open_or_create_workbook(filename,options)
265
- end
266
- end
267
- end
252
+ end
268
253
  end
269
254
  end
270
255
 
271
256
  # @private
272
257
  def set_options(filename, options)
273
258
  if (!options[:read_only].nil?) && options[:read_only] != @ole_workbook.ReadOnly
259
+ #raise OptionInvalid, ":if_unsaved:accept and changing read-only mode is not possible" if options[:if_unsaved]==:accept
274
260
  @excel.with_displayalerts(false) { @ole_workbook.Close }
275
261
  @ole_workbook = nil
276
262
  open_or_create_workbook(filename, options)
@@ -282,11 +268,17 @@ module RobustExcelOle
282
268
  end
283
269
  end
284
270
 
271
+
285
272
  private
286
273
 
287
274
  # @private
288
- # connects to an unknown workbook and returns true, if it exists
275
+ # connects to an unknown workbook
289
276
  def connect(filename,options)
277
+ excels_number = excel_class.excels_number
278
+ workbooks_number = if excels_number>0
279
+ excel_class.current.Workbooks.Count
280
+ else 0
281
+ end
290
282
  abs_filename = General.absolute_path(filename)
291
283
  @ole_workbook = begin
292
284
  WIN32OLE.connect(abs_filename)
@@ -307,10 +299,13 @@ module RobustExcelOle
307
299
  end
308
300
  end
309
301
  @excel = excel_class.new(ole_excel)
302
+ excels_number_after = excel_class.excels_number
303
+ workbooks_number_after = ole_excel.Workbooks.Count
304
+ @was_open = (excels_number_after==excels_number) && (workbooks_number_after==workbooks_number)
310
305
  end
311
306
 
312
307
  # @private
313
- def manage_nonexisting_file(filename,options)
308
+ def manage_nonexisting_file(filename,options)
314
309
  return if File.exist?(filename)
315
310
  abs_filename = General.absolute_path(filename)
316
311
  if options[:if_absent] == :create
@@ -566,75 +561,107 @@ module RobustExcelOle
566
561
  unobtrusively(*args, &block)
567
562
  end
568
563
 
569
-
570
564
  # allows to read or modify a workbook such that its state remains unchanged
571
565
  # state comprises: open, saved, writable, visible, calculation mode, check compatibility
572
- # @param [String] file the file name
566
+ # @param [String] file_or_workbook a file name or WIN32OLE workbook
573
567
  # @param [Hash] opts the options
574
568
  # @option opts [Variant] :if_closed :current (default), :new or an Excel instance
575
- # @option opts [Boolean] :read_only true/false, open the workbook in read-only/read-write modus (save changes)
576
- # @option opts [Boolean] :writable true/false changes of the workbook shall be saved/not saved
577
- # @option opts [Boolean] :rw_change_excel Excel instance in which the workbook with the new
578
- # write permissions shall be opened :current (default), :new or an Excel instance
579
- # @option opts [Boolean] :keep_open whether the workbook shall be kept open after unobtrusively opening
569
+ # @option opts [Boolean] :read_only true/false (default), open the workbook in read-only/read-write modus (save changes)
570
+ # @option opts [Boolean] :writable true (default)/false changes of the workbook shall be saved/not saved
571
+ # @option opts [Boolean] :keep_open whether the workbook shall be kept open after unobtrusively opening (default: false)
580
572
  # @return [Workbook] a workbook
581
- def self.unobtrusively(file, opts = { })
573
+ def self.unobtrusively(file_or_workbook, opts = { })
582
574
  opts = process_options(opts, :use_defaults => false)
583
- opts = {:if_closed => :current,
584
- :rw_change_excel => :current,
585
- :keep_open => false}.merge(opts)
586
- raise OptionInvalid, 'contradicting options' if opts[:writable] && opts[:read_only]
587
- prefer_writable = ((!(opts[:read_only]) || opts[:writable] == true) &&
588
- !(opts[:read_only].nil? && opts[:writable] == false))
589
- do_not_write = (opts[:read_only] || (opts[:read_only].nil? && opts[:writable] == false))
590
- book = bookstore.fetch(file, :prefer_writable => prefer_writable)
591
- was_open = book && book.alive?
592
- if was_open
593
- was_saved = book.saved
594
- was_writable = book.writable
575
+ raise OptionInvalid, 'contradicting options' if opts[:writable] && opts[:read_only]
576
+ opts = opts.merge({:force => {:excel => opts[:if_closed]}, :if_closed => :current,
577
+ :if_unsaved => :accept, :keep_open => false})
578
+ opts = opts.merge({:read_only => opts[:read_only]}) unless opts[:read_only].nil?
579
+ file = (file_or_workbook.is_a? WIN32OLE) ? file_or_workbook.Fullname.tr('\\','/') : file_or_workbook
580
+ begin
581
+ book = open(file)
595
582
  was_visible = book.visible
596
- was_calculation = book.calculation
583
+ was_writable = book.writable
584
+ was_saved = book.saved
597
585
  was_check_compatibility = book.check_compatibility
598
- if (opts[:writable] && !was_writable && !was_saved) ||
599
- (opts[:read_only] && was_writable && !was_saved)
586
+ was_calculation = book.excel.calculation
587
+ book.set_options(file,opts)
588
+ if !was_saved && ((opts[:writable] && !was_writable) || (opts[:read_only] && was_writable))
600
589
  raise NotImplementedREOError, 'unsaved read-only workbook shall be written'
601
590
  end
602
- opts[:rw_change_excel] = book.excel if opts[:rw_change_excel] == :current
591
+ yield book
592
+ ensure
593
+ if book && book.alive?
594
+ do_not_write = (opts[:read_only] || (opts[:read_only].nil? && opts[:writable] == false))
595
+ book.save unless was_saved || do_not_write || book.ReadOnly
596
+ # open and close if the read_only mode has changed
597
+ if (opts[:read_only] && was_writable) || (!opts[:read_only] && !was_writable)
598
+ book = open(file, :read_only => !was_writable, :if_unsaved => :forget)
599
+ end
600
+ if book.was_open
601
+ book.visible = was_visible
602
+ book.CheckCompatibility = was_check_compatibility
603
+ book.excel.calculation = was_calculation
604
+ end
605
+ book.Saved = (was_saved || !book.was_open)
606
+ book.close unless book.was_open || opts[:keep_open]
607
+ end
603
608
  end
604
- change_rw_mode = ((opts[:read_only] && was_writable) || (opts[:writable] && !was_writable))
609
+ end
610
+
611
+ # allows to read or modify a workbook such that its state remains unchanged
612
+ # state comprises: open, saved, writable, visible, calculation mode, check compatibility
613
+ # @param [String] file_or_workbook a file name or WIN32OLE workbook
614
+ # @param [Hash] opts the options
615
+ # @option opts [Variant] :if_closed :current (default), :new or an Excel instance
616
+ # @option opts [Boolean] :read_only true/false (default), open the workbook in read-only/read-write modus (save changes)
617
+ # @option opts [Boolean] :writable true (default)/false changes of the workbook shall be saved/not saved
618
+ # @option opts [Boolean] :keep_open whether the workbook shall be kept open after unobtrusively opening (default: false)
619
+ # @return [Workbook] a workbook
620
+ =begin
621
+ def self.unobtrusively(file_or_workbook, opts = { })
622
+ book = new(file_or_workbook)
623
+ book.unobtrusively(opts)
624
+ end
625
+ =end
626
+
627
+ def unobtrusively(opts = { })
628
+ opts = process_options(opts, :use_defaults => false)
629
+ raise OptionInvalid, 'contradicting options' if opts[:writable] && opts[:read_only]
630
+ opts = opts.merge({:force => {:excel => opts[:if_closed]}, :if_closed => :current,
631
+ :if_unsaved => :accept, :keep_open => false})
632
+ opts = opts.merge({:read_only => opts[:read_only]}) unless opts[:read_only].nil?
633
+ file = stored_filename
605
634
  begin
606
- book =
607
- if was_open
608
- if change_rw_mode
609
- opts = opts.merge({:force => {:excel => opts[:rw_change_excel]}, :read_only => do_not_write})
610
- open(file, opts)
611
- else
612
- book
613
- end
614
- else
615
- opts = opts.merge({:force => {:excel => opts[:if_closed]}, :read_only => do_not_write})
616
- open(file, opts)
617
- end
635
+ book = open(file, opts)
636
+ was_visible = book.visible
637
+ was_saved = book.saved
638
+ was_writable = book.was_writable
639
+ was_check_compatibility = book.CheckCompatibility
640
+ was_calculation = book.excel.calculation
641
+ if !was_saved && ((opts[:writable] && !was_writable) || (opts[:read_only] && was_writable))
642
+ raise NotImplementedREOError, 'unsaved read-only workbook shall be written'
643
+ end
618
644
  yield book
619
645
  ensure
620
646
  if book && book.alive?
647
+ do_not_write = (opts[:read_only] || (opts[:read_only].nil? && opts[:writable] == false))
621
648
  book.save unless book.saved || do_not_write || book.ReadOnly
622
- if was_open
623
- if opts[:rw_change_excel] == book.excel && change_rw_mode
624
- book.close
625
- opts = opts.merge({:force => {:excel => opts[:rw_change_excel]}, :read_only => !was_writable})
626
- book = open(file, opts)
627
- end
649
+ # open and close if the read_only mode has changed
650
+ if (opts[:read_only] && was_writable) || (!opts[:read_only] && !was_writable)
651
+ book = open(file, :read_only => !was_writable, :if_unsaved => :forget)
652
+ end
653
+ if book.was_open
654
+ book.visible = was_visible
655
+ book.CheckCompatibility = was_check_compatibility
628
656
  book.excel.calculation = was_calculation
629
- book.CheckCompatibility = was_check_compatibility
630
- # book.visible = was_visible # not necessary
631
657
  end
632
- book.Saved = (was_saved || !was_open)
633
- book.close unless was_open || opts[:keep_open]
658
+ book.Saved = (was_saved || !book.was_open)
659
+ book.close unless book.was_open || opts[:keep_open]
634
660
  end
635
661
  end
636
662
  end
637
663
 
664
+
638
665
  # reopens a closed workbook
639
666
  # @options options
640
667
  def reopen(options = { })