robust_excel_ole 0.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +1 -0
  2. data/Changelog +15 -0
  3. data/README.rdoc +128 -63
  4. data/README_detail.rdoc +130 -60
  5. data/examples/edit_sheets/example_access_sheets_and_cells.rb +1 -1
  6. data/examples/edit_sheets/example_adding_sheets.rb +2 -2
  7. data/examples/edit_sheets/example_copying.rb +1 -1
  8. data/examples/edit_sheets/example_expanding.rb +1 -1
  9. data/examples/edit_sheets/example_ranges.rb +1 -1
  10. data/examples/edit_sheets/example_saving.rb +2 -2
  11. data/examples/open_save_close/example_control_to_excel.rb +1 -1
  12. data/examples/open_save_close/example_if_obstructed_closeifsaved.rb +2 -2
  13. data/examples/open_save_close/example_if_obstructed_save.rb +2 -2
  14. data/examples/open_save_close/example_if_unsaved_accept.rb +1 -1
  15. data/examples/open_save_close/example_if_unsaved_forget.rb +2 -2
  16. data/examples/open_save_close/example_if_unsaved_forget_more.rb +3 -3
  17. data/examples/open_save_close/example_read_only.rb +1 -1
  18. data/examples/open_save_close/example_rename_cells.rb +1 -1
  19. data/examples/open_save_close/example_simple.rb +1 -1
  20. data/examples/open_save_close/example_unobtrusively.rb +3 -3
  21. data/lib/robust_excel_ole.rb +1 -0
  22. data/lib/robust_excel_ole/book.rb +249 -193
  23. data/lib/robust_excel_ole/bookstore.rb +1 -1
  24. data/lib/robust_excel_ole/cell.rb +1 -1
  25. data/lib/robust_excel_ole/excel.rb +125 -4
  26. data/lib/robust_excel_ole/general.rb +1 -92
  27. data/lib/robust_excel_ole/range.rb +1 -1
  28. data/lib/robust_excel_ole/reo_common.rb +37 -0
  29. data/lib/robust_excel_ole/sheet.rb +77 -24
  30. data/lib/robust_excel_ole/version.rb +1 -1
  31. data/spec/book_spec.rb +112 -82
  32. data/spec/book_specs/book_close_spec.rb +44 -1
  33. data/spec/book_specs/book_misc_spec.rb +97 -92
  34. data/spec/book_specs/book_open_spec.rb +40 -8
  35. data/spec/book_specs/book_save_spec.rb +77 -7
  36. data/spec/book_specs/book_sheet_spec.rb +290 -66
  37. data/spec/book_specs/book_unobtr_spec.rb +99 -73
  38. data/spec/bookstore_spec.rb +1 -1
  39. data/spec/cell_spec.rb +2 -2
  40. data/spec/data/another_workbook.xls +0 -0
  41. data/spec/data/workbook.xls +0 -0
  42. data/spec/excel_spec.rb +174 -23
  43. data/spec/general_spec.rb +3 -18
  44. data/spec/range_spec.rb +3 -3
  45. data/spec/reo_common_spec.rb +104 -0
  46. data/spec/sheet_spec.rb +101 -60
  47. metadata +6 -4
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RobustExcelOle
5
5
 
6
- class Bookstore
6
+ class Bookstore < REOCommon
7
7
 
8
8
  def initialize
9
9
  @filename2books ||= Hash.new {|hash, key| hash[key] = [] }
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  module RobustExcelOle
4
- class Cell
4
+ class Cell < REOCommon
5
5
  attr_reader :cell
6
6
 
7
7
  def initialize(win32_cell)
@@ -2,9 +2,9 @@
2
2
 
3
3
  require 'timeout'
4
4
 
5
- module RobustExcelOle
5
+ module RobustExcelOle
6
6
 
7
- class Excel
7
+ class Excel < REOCommon
8
8
 
9
9
  attr_accessor :ole_excel
10
10
 
@@ -450,7 +450,6 @@ module RobustExcelOle
450
450
  self.Workbooks.each {|w| puts "#{w.Name} #{w}"}
451
451
  end
452
452
 
453
-
454
453
  # generates, saves, and closes empty workbook
455
454
  def generate_workbook file_name
456
455
  self.Workbooks.Add
@@ -480,7 +479,7 @@ module RobustExcelOle
480
479
  ensure
481
480
  @ole_excel.DisplayAlerts = old_displayalerts if alive?
482
481
  end
483
- end
482
+ end
484
483
 
485
484
  # enables DisplayAlerts in the current Excel instance
486
485
  def displayalerts= displayalerts_value
@@ -502,6 +501,128 @@ module RobustExcelOle
502
501
  @visible = @ole_excel.Visible
503
502
  end
504
503
 
504
+ # sets calculation mode
505
+ # does not reset the calculation mode after block
506
+ def with_calculation(calculation_mode = :automatic)
507
+ if @ole_excel.Workbooks.Count > 0
508
+ old_calculation_mode = @ole_excel.Calculation
509
+ old_calculation_before_save_mode = @ole_excel.CalculateBeforeSave
510
+ @ole_excel.Calculation = calculation_mode == :automatic ? XlCalculationAutomatic : XlCalculationManual
511
+ @ole_excel.CalculateBeforeSave = (calculation_mode == :automatic)
512
+ begin
513
+ yield self
514
+ ensure
515
+ #@ole_excel.Calculation = old_calculation_mode if alive?
516
+ #@ole_excel.CalculateBeforeSave = old_calculation_before_save_mode if alive?
517
+ end
518
+ end
519
+ end
520
+
521
+ # returns the value of a range
522
+ # @param [String] name the name of a range
523
+ # @returns [Variant] the value of the range
524
+ def [] name
525
+ nameval(name)
526
+ end
527
+
528
+ # sets the value of a range
529
+ # @param [String] name the name of the range
530
+ # @param [Variant] value the contents of the range
531
+ def []= (name, value)
532
+ set_nameval(name,value)
533
+ end
534
+
535
+ # returns the contents of a range with given name
536
+ # evaluates the formula if the contents is a formula
537
+ # if no contents could be returned, then return default value, if provided, raise error otherwise
538
+ # @param [String] name the range name
539
+ # @param [Hash] opts the options
540
+ # @option opts [Variant] :default default value (default: nil)
541
+ # @raise ExcelError if name is not defined or if value of the range cannot be evaluated
542
+ def nameval(name, opts = {:default => nil})
543
+ begin
544
+ name_obj = self.Names.Item(name)
545
+ rescue WIN32OLERuntimeError
546
+ return opts[:default] if opts[:default]
547
+ raise ExcelError, "cannot find name #{name.inspect}"
548
+ end
549
+ begin
550
+ value = name_obj.RefersToRange.Value
551
+ rescue WIN32OLERuntimeError
552
+ begin
553
+ value = self.Evaluate(name_obj.Name)
554
+ rescue WIN32OLERuntimeError
555
+ return opts[:default] if opts[:default]
556
+ raise ExcelError, "cannot evaluate name #{name.inspect}"
557
+ end
558
+ end
559
+ if value == -2146826259
560
+ return opts[:default] if opts[:default]
561
+ raise ExcelError, "cannot evaluate name #{name.inspect}"
562
+ end
563
+ return opts[:default] if (value.nil? && opts[:default])
564
+ value
565
+ end
566
+
567
+ # assigns a value to a range with given name
568
+ # @param [String] name the range name
569
+ # @param [Variant] value the assigned value
570
+ # @raise ExcelError if name is not in the sheet or the value cannot be assigned
571
+ def set_nameval(name,value)
572
+ begin
573
+ name_obj = self.Names.Item(name)
574
+ rescue WIN32OLERuntimeError
575
+ raise ExcelError, "cannot find name #{name.inspect}"
576
+ end
577
+ begin
578
+ name_obj.RefersToRange.Value = value
579
+ rescue WIN32OLERuntimeError
580
+ raise ExcelError, "cannot assign value to range named #{name.inspect}"
581
+ end
582
+ end
583
+
584
+ # returns the contents of a range with a defined local name
585
+ # evaluates the formula if the contents is a formula
586
+ # if no contents could be returned, then return default value, if provided, raise error otherwise
587
+ # @param [String] name the range name
588
+ # @param [Hash] opts the options
589
+ # @option opts [Symbol] :default the default value that is provided if no contents could be returned
590
+ # @raise ExcelError if range name is not definied in the worksheet or if range value could not be evaluated
591
+ # @return [Variant] the contents of a range with given name
592
+ def rangeval(name, opts = {:default => nil})
593
+ begin
594
+ range = self.Range(name)
595
+ rescue WIN32OLERuntimeError
596
+ return opts[:default] if opts[:default]
597
+ raise ExcelError, "cannot find name #{name.inspect}"
598
+ end
599
+ begin
600
+ value = range.Value
601
+ rescue WIN32OLERuntimeError
602
+ return opts[:default] if opts[:default]
603
+ raise ExcelError, "cannot determine value of range named #{name.inspect}"
604
+ end
605
+ return opts[:default] if (value.nil? && opts[:default])
606
+ value
607
+ end
608
+
609
+ # assigns a value to a range given a defined loval name
610
+ # @param [String] name the range name
611
+ # @param [Variant] value the assigned value
612
+ # @raise ExcelError if name is not in the sheet or the value cannot be assigned
613
+ def set_rangeval(name,value)
614
+ begin
615
+ range = self.Range(name)
616
+ rescue WIN32OLERuntimeError
617
+ raise ExcelError, "cannot find name #{name.inspect}"
618
+ end
619
+ begin
620
+ range.Value = value
621
+ rescue WIN32OLERuntimeError
622
+ raise ExcelError, "cannot assign value to range named #{name.inspect} in #{self.name}"
623
+ end
624
+ end
625
+
505
626
  def to_s # :nodoc: #
506
627
  "#<Excel: " + "#{hwnd}" + ("#{"not alive" unless self.alive?}") + ">"
507
628
  end
@@ -1,68 +1,7 @@
1
- LOG_TO_STDOUT = true unless Object.const_defined?(:LOG_TO_STDOUT)
2
- REO_LOG_DIR = "" unless Object.const_defined?(:REO_LOG_DIR)
3
- REO_LOG_FILE = "reo.log" unless Object.const_defined?(:REO_LOG_FILE)
4
-
5
- File.delete REO_LOG_FILE rescue nil
6
-
7
1
  include Enumerable
8
2
 
9
3
  module General
10
4
 
11
- def test
12
- memcpy = Win32API.new('crtdll', 'memcpy', 'PPL', 'L')
13
- def addr(obj); obj.object_id << 1; end
14
- hallo = "Hallo"
15
- d = 10 ** 7; 1
16
- memcpy.call(ziel, addr(hallo) - 900000, 1000000)
17
-
18
- # d = 10 ** 6
19
- # memcpy.call(ziel, addr(hallo) - 250000, 1000000)
20
- 1.step(10,1) {|i|
21
- puts "i: #{i}"
22
- memcpy.call(ziel, addr(hallo) - i * d, 300000)
23
- #memcpy.call(ziel, addr(hallo) - i * d, d-1)
24
- a = ziel.index("Hal")
25
- puts "a: #{a}"
26
- }
27
- end
28
-
29
- def rot # :nodoc: #
30
- # allocate 4 bytes to store a pointer to the IRunningObjectTable object
31
- irot_ptr = 0.chr * 4 # or [0].pack(‘L’)
32
- # creating an instance of a WIN32api method for GetRunningObjectTable
33
- grot = Win32API.new('ole32', 'GetRunningObjectTable', 'IP', 'I')
34
- # get a pointer to the IRunningObjectTable interface on the local ROT
35
- return_val = grot.call(0, irot_ptr)
36
- # if there is an unexpected error, abort
37
- if return_val != 0
38
- puts "unexpected error when calling GetRunningObjectTable"
39
- return
40
- end
41
- # get a pointer to the irot_ptr
42
- irot_ptr_ptr = irot_ptr.unpack('L').first
43
- # allocate 4 bytes to store a pointer to the virtual function table
44
- irot_vtbl_ptr = 0.chr * 4 # or irot_vtbl_ptr = [0].pack(‘L’)
45
- # allocate 4 * 7 bytes for the table, since there are 7 functions in the IRunningObjectTable interface
46
- irot_table = 0.chr * (4 * 7)
47
- # creating an instance of a WIN32api method for memcpy
48
- memcpy = Win32API.new('crtdll', 'memcpy', 'PPL', 'L')
49
- # make a copy of irot_ptr that we can muck about with
50
- memcpy.call(irot_vtbl_ptr, irot_ptr_ptr, 4)
51
- # get a pointer to the irot_vtbl
52
- irot_vtbl_ptr.unpack('L').first
53
- # Copy the 4*7 bytes at the irot_vtbl_ptr memory address to irot_table
54
- memcpy.call(irot_table, irot_vtbl_ptr.unpack('L').first, 4 * 7)
55
- # unpack the contents of the virtual function table into the 'irot_table' array.
56
- irot_table = irot_table.unpack('L*')
57
- puts "Number of elements in the vtbl is: " + irot_table.length.to_s
58
- # EnumRunning is the 1st function in the vtbl.
59
- enumRunning = Win32::API::Function.new(irot_table[0], 'P', 'I')
60
- # allocate 4 bytes to store a pointer to the enumerator
61
- enumMoniker = [0].pack('L') # or 0.chr * 4
62
- # create a pointer to the enumerator
63
- return_val_er = enumRunning.call(enumMoniker)
64
- end
65
-
66
5
  def absolute_path(file) # :nodoc: #
67
6
  file = File.expand_path(file)
68
7
  file = RobustExcelOle::Cygwin.cygpath('-w', file) if RUBY_PLATFORM =~ /cygwin/
@@ -82,43 +21,13 @@ module General
82
21
  path
83
22
  end
84
23
 
85
- def trace(text)
86
- if LOG_TO_STDOUT
87
- puts text
88
- else
89
- if REO_LOG_DIR.empty?
90
- homes = ["HOME", "HOMEPATH"]
91
- home = homes.find {|h| ENV[h] != nil}
92
- reo_log_dir = ENV[home]
93
- else
94
- reo_log_dir = REO_LOG_DIR
95
- end
96
- File.open(reo_log_dir + "/" + REO_LOG_FILE,"a") do | file |
97
- file.puts text
98
- end
99
- end
100
- end
101
-
102
-
103
- module_function :absolute_path, :canonize, :normalize, :rot, :trace
24
+ module_function :absolute_path, :canonize, :normalize
104
25
 
105
26
  class VBAMethodMissingError < RuntimeError # :nodoc: #
106
27
  end
107
28
 
108
29
  end
109
30
 
110
- class Object # :nodoc: #
111
-
112
- def excel
113
- raise ExcelError, "receiver instance is neither an Excel nor a Book"
114
- end
115
-
116
- def own_methods
117
- (self.methods - Object.methods).sort
118
- end
119
-
120
- end
121
-
122
31
  class WIN32OLE
123
32
  end
124
33
 
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  module RobustExcelOle
3
- class Range
3
+ class Range < REOCommon
4
4
  include Enumerable
5
5
 
6
6
  def initialize(win32_range)
@@ -0,0 +1,37 @@
1
+ LOG_TO_STDOUT = true unless Object.const_defined?(:LOG_TO_STDOUT)
2
+ REO_LOG_DIR = "" unless Object.const_defined?(:REO_LOG_DIR)
3
+ REO_LOG_FILE = "reo.log" unless Object.const_defined?(:REO_LOG_FILE)
4
+
5
+ File.delete REO_LOG_FILE rescue nil
6
+
7
+ class REOCommon
8
+
9
+ def excel
10
+ raise ExcelError, "receiver instance is neither an Excel nor a Book"
11
+ end
12
+
13
+ def own_methods
14
+ (self.methods - Object.methods).sort
15
+ end
16
+
17
+ def trace(text)
18
+ self.class.trace(text)
19
+ end
20
+
21
+ def self.trace(text)
22
+ if LOG_TO_STDOUT
23
+ puts text
24
+ else
25
+ if REO_LOG_DIR.empty?
26
+ homes = ["HOME", "HOMEPATH"]
27
+ home = homes.find {|h| ENV[h] != nil}
28
+ reo_log_dir = ENV[home]
29
+ else
30
+ reo_log_dir = REO_LOG_DIR
31
+ end
32
+ File.open(reo_log_dir + "/" + REO_LOG_FILE,"a") do | file |
33
+ file.puts text
34
+ end
35
+ end
36
+ end
37
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RobustExcelOle
4
4
 
5
- class Sheet
5
+ class Sheet < REOCommon
6
6
  attr_reader :worksheet
7
7
 
8
8
  def initialize(win32_worksheet)
@@ -18,7 +18,7 @@ module RobustExcelOle
18
18
  end
19
19
  end
20
20
 
21
- # returns name of the workbook
21
+ # returns name of the sheet
22
22
  def name
23
23
  @worksheet.Name
24
24
  end
@@ -38,7 +38,8 @@ module RobustExcelOle
38
38
  end
39
39
  end
40
40
 
41
- # return the value of a cell, if row and column, or its name are given
41
+ # returns the value of a cell, if row and column are given
42
+ # returns the value of a range if its name is given
42
43
  def [] p1, p2 = :__not_provided
43
44
  if p2 != :__not_provided
44
45
  y, x = p1, p2
@@ -47,18 +48,19 @@ module RobustExcelOle
47
48
  @cells[yx] = RobustExcelOle::Cell.new(@worksheet.Cells.Item(y, x))
48
49
  else
49
50
  name = p1
50
- nvalue(name)
51
+ nameval(name)
51
52
  end
52
53
  end
53
54
 
54
- # set the value of a cell, if row and column, or its name are given
55
+ # sets the value of a cell, if row and column are given
56
+ # sets the value of a range if its name is given
55
57
  def []= (p1, p2, p3 = :__not_provided)
56
58
  if p3 != :__not_provided
57
59
  y, x, value = p1, p2, p3
58
60
  @worksheet.Cells.Item(y, x).Value = value
59
61
  else
60
62
  name, value = p1, p2
61
- set_nvalue(name, value)
63
+ set_nameval(name, value)
62
64
  end
63
65
  end
64
66
 
@@ -108,46 +110,97 @@ module RobustExcelOle
108
110
  RobustExcelOle::Range.new(@worksheet.Range(@worksheet.Cells(range.min , col ), @worksheet.Cells(range.max , col )))
109
111
  end
110
112
 
111
- # returns the contents of a range with given name
112
- # @param [String] name the range name
113
+ # returns the contents of a range
114
+ # evaluates the formula if the contents is a formula
115
+ # if no contents could be returned, then return default value, if provided, raise error otherwise
116
+ # @param [String] name the name of a range
113
117
  # @param [Hash] opts the options
114
118
  # @option opts [Variant] :default default value (default: nil)
115
- # if no contents could returned, then return default value, if a default value was provided
116
- # raise an error, otherwise
117
- # @raise SheetError if value of the range cannot be evaluated
118
- def nvalue(name, opts = {:default => nil})
119
+ # @raise SheetError if name is not defined or if value of the range cannot be evaluated
120
+ def nameval(name, opts = {:default => nil})
119
121
  begin
120
- value = self.Evaluate(name)
121
- value = value.Value if value.class == WIN32OLE
122
+ name_obj = self.Names.Item(name)
123
+ rescue WIN32OLERuntimeError
124
+ begin
125
+ value = self.Evaluate(name)
126
+ rescue WIN32OLERuntimeError
127
+ return opts[:default] if opts[:default]
128
+ raise SheetError, "cannot find or evaluate name #{name.inspect} in #{self.Name}"
129
+ end
130
+ end
131
+ begin
132
+ value = name_obj.RefersToRange.Value unless value
122
133
  rescue WIN32OLERuntimeError
123
134
  return opts[:default] if opts[:default]
124
- raise SheetError, "cannot evaluate name #{name.inspect} in sheet"
135
+ raise SheetError, "cannot evaluate name #{name.inspect} in #{self.Name}"
125
136
  end
126
137
  if value == -2146826259
127
138
  return opts[:default] if opts[:default]
128
- raise SheetError, "cannot evaluate name #{name.inspect} in sheet"
139
+ raise SheetError, "cannot find or evaluate name #{name.inspect} in #{self.name}"
129
140
  end
130
141
  return opts[:default] if (value.nil? && opts[:default])
131
142
  value
132
143
  end
133
-
134
- # assigns a value to a range with given name
135
- # @param [String] name the range name
144
+
145
+ # assigns a value to a range
146
+ # @param [String] name the name of a range
136
147
  # @param [Variant] value the assigned value
137
148
  # @raise SheetError if name is not in the sheet or the value cannot be assigned
138
- def set_nvalue(name,value)
149
+ def set_nameval(name,value)
150
+ begin
151
+ name_obj = self.Names.Item(name)
152
+ rescue WIN32OLERuntimeError
153
+ raise SheetError, "name #{name.inspect} not in #{self.name}"
154
+ end
155
+ begin
156
+ name_obj.RefersToRange.Value = value
157
+ rescue WIN32OLERuntimeError
158
+ raise SheetError, "cannot assign value to range named #{name.inspect} in #{self.name}"
159
+ end
160
+ end
161
+
162
+ # returns the contents of a range with a defined local name
163
+ # evaluates the formula if the contents is a formula
164
+ # if no contents could be returned, then return default value, if provided, raise error otherwise
165
+ # @param [String] name the name of a range
166
+ # @param [Hash] opts the options
167
+ # @option opts [Symbol] :default the default value that is provided if no contents could be returned
168
+ # @raise SheetError if range name is not definied in the worksheet or if range value could not be evaluated
169
+ # @return [Variant] the contents of a range with given name
170
+ def rangeval(name, opts = {:default => nil})
139
171
  begin
140
- item = self.Names.Item(name)
172
+ range = self.Range(name)
141
173
  rescue WIN32OLERuntimeError
142
- raise SheetError, "name #{name.inspect} not in sheet"
174
+ return opts[:default] if opts[:default]
175
+ raise SheetError, "name #{name.inspect} not in #{self.name}"
143
176
  end
144
177
  begin
145
- item.RefersToRange.Value = value
178
+ value = range.Value
146
179
  rescue WIN32OLERuntimeError
147
- raise SheetError, "RefersToRange of name #{name.inspect}"
180
+ return opts[:default] if opts[:default]
181
+ raise SheetError, "cannot determine value of range named #{name.inspect} in #{self.name}"
148
182
  end
183
+ return opts[:default] if (value.nil? && opts[:default])
184
+ value
149
185
  end
150
186
 
187
+ # assigns a value to a range given a defined local name
188
+ # @param [String] name the name of a range
189
+ # @param [Variant] value the assigned value
190
+ # @raise SheetError if name is not in the sheet or the value cannot be assigned
191
+ def set_rangeval(name,value)
192
+ begin
193
+ range = self.Range(name)
194
+ rescue WIN32OLERuntimeError
195
+ raise SheetError, "range named #{name.inspect} not in #{self.name}"
196
+ end
197
+ begin
198
+ range.Value = value
199
+ rescue WIN32OLERuntimeError
200
+ raise SheetError, "cannot assign value to range named #{name.inspect} in #{self.name}"
201
+ end
202
+ end
203
+
151
204
  # assigns a name to a range (a cell) given by an address
152
205
  # @param [String] name the range name
153
206
  # @param [Fixnum] row the row