robust_excel_ole 0.4 → 0.5

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