robust_excel_ole 1.8 → 1.9
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.
- checksums.yaml +5 -5
- data/Changelog +8 -0
- data/README.rdoc +17 -3
- data/docs/README_excel.rdoc +23 -0
- data/docs/README_ranges.rdoc +73 -5
- data/examples/modifying_sheets/example_add_names.rb +57 -0
- data/examples/modifying_sheets/example_adding_sheets.rb +1 -1
- data/examples/open_save_close/example_if_obstructed_closeifsaved.rb +1 -1
- data/examples/open_save_close/example_if_unsaved_forget.rb +1 -1
- data/examples/open_save_close/example_reuse.rb +1 -1
- data/lib/robust_excel_ole.rb +2 -0
- data/lib/robust_excel_ole/address.rb +114 -0
- data/lib/robust_excel_ole/excel.rb +81 -53
- data/lib/robust_excel_ole/general.rb +37 -3
- data/lib/robust_excel_ole/range.rb +20 -20
- data/lib/robust_excel_ole/range_owners.rb +236 -0
- data/lib/robust_excel_ole/reo_common.rb +18 -276
- data/lib/robust_excel_ole/version.rb +1 -1
- data/lib/robust_excel_ole/workbook.rb +177 -91
- data/lib/robust_excel_ole/worksheet.rb +12 -4
- data/reo.bat +1 -1
- data/spec/address_spec.rb +174 -0
- data/spec/data/another_workbook.xls +0 -0
- data/spec/data/different_workbook.xls +0 -0
- data/spec/data/workbook.xls +0 -0
- data/spec/excel_spec.rb +168 -29
- data/spec/reo_common_spec.rb +0 -103
- data/spec/workbook_spec.rb +12 -2
- data/spec/workbook_specs/workbook_close_spec.rb +2 -1
- data/spec/workbook_specs/workbook_misc_spec.rb +41 -7
- data/spec/workbook_specs/workbook_open_spec.rb +8 -3
- data/spec/workbook_specs/workbook_save_spec.rb +5 -3
- data/spec/worksheet_spec.rb +83 -10
- metadata +7 -3
@@ -33,15 +33,49 @@ module General
|
|
33
33
|
|
34
34
|
end
|
35
35
|
|
36
|
+
# @private
|
37
|
+
class Integer
|
38
|
+
|
39
|
+
alias old_spaceship <=>
|
40
|
+
|
41
|
+
def <=> other
|
42
|
+
# p other
|
43
|
+
if other.is_a? Array
|
44
|
+
self <=> other.first
|
45
|
+
else
|
46
|
+
old_spaceship other
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
# @private
|
53
|
+
class Array
|
54
|
+
|
55
|
+
alias old_spaceship <=>
|
56
|
+
|
57
|
+
def <=> other
|
58
|
+
# p other
|
59
|
+
if other.is_a? Integer
|
60
|
+
self <=> [other]
|
61
|
+
else
|
62
|
+
old_spaceship other
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
|
36
69
|
# @private
|
37
70
|
class WIN32OLE
|
71
|
+
|
38
72
|
# promoting WIN32OLE objects to RobustExcelOle objects
|
39
73
|
def to_reo
|
40
74
|
case ole_type.name
|
41
75
|
when 'Range' then RobustExcelOle::Range.new(self)
|
42
|
-
when '_Worksheet' then Worksheet.new(self)
|
43
|
-
when '_Workbook' then Workbook.new(self)
|
44
|
-
when '_Application' then Excel.new(self)
|
76
|
+
when '_Worksheet' then RobustExcelOle::Worksheet.new(self)
|
77
|
+
when '_Workbook' then RobustExcelOle::Workbook.new(self)
|
78
|
+
when '_Application' then RobustExcelOle::Excel.new(self)
|
45
79
|
else
|
46
80
|
self
|
47
81
|
end
|
@@ -73,18 +73,18 @@ module RobustExcelOle
|
|
73
73
|
{ }
|
74
74
|
end
|
75
75
|
end
|
76
|
-
|
76
|
+
rows, columns = Address.int_range(dest_address)
|
77
77
|
dest_sheet = @worksheet if dest_sheet == :__not_provided
|
78
|
-
dest_address_is_position = (
|
78
|
+
dest_address_is_position = (rows.min == rows.max && columns.min == columns.max)
|
79
79
|
dest_range_address = if (not dest_address_is_position)
|
80
|
-
[
|
80
|
+
[rows.min..rows.max,columns.min..columns.max]
|
81
81
|
else
|
82
82
|
if (not options[:transpose])
|
83
|
-
[
|
84
|
-
|
83
|
+
[rows.min..rows.min+self.Rows.Count-1,
|
84
|
+
columns.min..columns.min+self.Columns.Count-1]
|
85
85
|
else
|
86
|
-
[
|
87
|
-
|
86
|
+
[rows.min..rows.min+self.Columns.Count-1,
|
87
|
+
columns.min..columns.min+self.Rows.Count-1]
|
88
88
|
end
|
89
89
|
end
|
90
90
|
dest_range = dest_sheet.range(dest_range_address)
|
@@ -95,9 +95,9 @@ module RobustExcelOle
|
|
95
95
|
if dest_range.worksheet.workbook.excel == @worksheet.workbook.excel
|
96
96
|
if options[:transpose]
|
97
97
|
self.Copy
|
98
|
-
dest_range.PasteSpecial(
|
98
|
+
dest_range.PasteSpecial('transpose' => true)
|
99
99
|
else
|
100
|
-
self.Copy(
|
100
|
+
self.Copy('destination' => dest_range.ole_range)
|
101
101
|
end
|
102
102
|
else
|
103
103
|
if options[:transpose]
|
@@ -107,7 +107,7 @@ module RobustExcelOle
|
|
107
107
|
@worksheet.workbook.excel.with_displayalerts(false) {added_sheet.Delete}
|
108
108
|
else
|
109
109
|
self.Copy
|
110
|
-
dest_sheet.Paste(
|
110
|
+
dest_sheet.Paste('destination' => dest_range.ole_range)
|
111
111
|
end
|
112
112
|
end
|
113
113
|
end
|
@@ -122,18 +122,18 @@ module RobustExcelOle
|
|
122
122
|
# @options [Worksheet] the destination worksheet
|
123
123
|
# @options [Hash] options: :transpose, :values_only
|
124
124
|
def copy_special(dest_address, dest_sheet = :__not_provided, options = { })
|
125
|
-
|
125
|
+
rows, columns = Address.int_range(dest_address)
|
126
126
|
dest_sheet = @worksheet if dest_sheet == :__not_provided
|
127
|
-
dest_address_is_position = (
|
127
|
+
dest_address_is_position = (rows.min == rows.max && columns.min == columns.max)
|
128
128
|
dest_range_address = if (not dest_address_is_position)
|
129
|
-
[
|
129
|
+
[rows.min..rows.max,columns.min..columns.max]
|
130
130
|
else
|
131
131
|
if (not options[:transpose])
|
132
|
-
[
|
133
|
-
|
132
|
+
[rows.min..rows.min+self.Rows.Count-1,
|
133
|
+
columns.min..columns.min+self.Columns.Count-1]
|
134
134
|
else
|
135
|
-
[
|
136
|
-
|
135
|
+
[rows.min..rows.min+self.Columns.Count-1,
|
136
|
+
columns.min..columns.min+self.Rows.Count-1]
|
137
137
|
end
|
138
138
|
end
|
139
139
|
dest_range = dest_sheet.range(dest_range_address)
|
@@ -144,9 +144,9 @@ module RobustExcelOle
|
|
144
144
|
if dest_range.worksheet.workbook.excel == @worksheet.workbook.excel
|
145
145
|
if options[:transpose]
|
146
146
|
self.Copy
|
147
|
-
dest_range.PasteSpecial(
|
147
|
+
dest_range.PasteSpecial('transpose' => true)
|
148
148
|
else
|
149
|
-
self.Copy(
|
149
|
+
self.Copy('destination' => dest_range.ole_range)
|
150
150
|
end
|
151
151
|
else
|
152
152
|
if options[:transpose]
|
@@ -156,7 +156,7 @@ module RobustExcelOle
|
|
156
156
|
@worksheet.workbook.excel.with_displayalerts(false) {added_sheet.Delete}
|
157
157
|
else
|
158
158
|
self.Copy
|
159
|
-
dest_sheet.Paste(
|
159
|
+
dest_sheet.Paste('destination' => dest_range.ole_range)
|
160
160
|
end
|
161
161
|
end
|
162
162
|
end
|
@@ -0,0 +1,236 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module RobustExcelOle
|
4
|
+
|
5
|
+
class RangeOwners < REOCommon
|
6
|
+
|
7
|
+
# returns the contents of a range with given name
|
8
|
+
# if the name could not be found or the value could not be determined,
|
9
|
+
# then return default value, if provided, raise error otherwise
|
10
|
+
# Excel Bug: if a local name without a qualifier is given,
|
11
|
+
# then by default Excel takes the first worksheet,
|
12
|
+
# even if a different worksheet is active
|
13
|
+
# @param [String] name the name of the range
|
14
|
+
# @param [Hash] opts the options
|
15
|
+
# @option opts [Symbol] :default the default value that is provided if no contents could be returned
|
16
|
+
# @return [Variant] the contents of a range with given name
|
17
|
+
def namevalue_glob(name, opts = { :default => :__not_provided })
|
18
|
+
name_obj = begin
|
19
|
+
name_object(name)
|
20
|
+
rescue NameNotFound => msg
|
21
|
+
return opts[:default] unless opts[:default] == :__not_provided
|
22
|
+
raise
|
23
|
+
end
|
24
|
+
value = begin
|
25
|
+
name_obj.RefersToRange.Value
|
26
|
+
rescue WIN32OLERuntimeError
|
27
|
+
sheet = if self.is_a?(Worksheet) then self
|
28
|
+
elsif self.is_a?(Workbook) then self.sheet(1)
|
29
|
+
elsif self.is_a?(Excel) then self.workbook.sheet(1)
|
30
|
+
end
|
31
|
+
begin
|
32
|
+
sheet.Evaluate(name_obj.Name).Value
|
33
|
+
rescue # WIN32OLERuntimeError
|
34
|
+
return opts[:default] unless opts[:default] == :__not_provided
|
35
|
+
raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect} in #{self}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
if value == -2146828288 + RobustExcelOle::XlErrName
|
39
|
+
return opts[:default] unless opts[:default] == :__not_provided
|
40
|
+
raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect} in #{File.basename(workbook.stored_filename).inspect rescue nil}"
|
41
|
+
end
|
42
|
+
return opts[:default] unless (opts[:default] == :__not_provided) || value.nil?
|
43
|
+
value
|
44
|
+
end
|
45
|
+
|
46
|
+
# sets the contents of a range
|
47
|
+
# @param [String] name the name of a range
|
48
|
+
# @param [Variant] value the contents of the range
|
49
|
+
# @param [FixNum] color the color when setting a value
|
50
|
+
# @param [Hash] opts :color [FixNum] the color when setting the contents
|
51
|
+
def set_namevalue_glob(name, value, opts = { :color => 0 })
|
52
|
+
cell = name_object(name).RefersToRange
|
53
|
+
cell.Interior.ColorIndex = opts[:color]
|
54
|
+
workbook.modified_cells << cell if workbook # unless cell_modified?(cell)
|
55
|
+
cell.Value = value
|
56
|
+
rescue WIN32OLERuntimeError
|
57
|
+
raise RangeNotEvaluatable, "cannot assign value to range named #{name.inspect} in #{self.inspect}"
|
58
|
+
end
|
59
|
+
|
60
|
+
# returns the contents of a range with a locally defined name
|
61
|
+
# evaluates the formula if the contents is a formula
|
62
|
+
# if the name could not be found or the range or value could not be determined,
|
63
|
+
# then return default value, if provided, raise error otherwise
|
64
|
+
# @param [String] name the name of a range
|
65
|
+
# @param [Hash] opts the options
|
66
|
+
# @option opts [Symbol] :default the default value that is provided if no contents could be returned
|
67
|
+
# @return [Variant] the contents of a range with given name
|
68
|
+
def namevalue(name, opts = { :default => :__not_provided })
|
69
|
+
return namevalue_glob(name, opts) if self.is_a?(Workbook)
|
70
|
+
begin
|
71
|
+
range = self.Range(name)
|
72
|
+
rescue WIN32OLERuntimeError
|
73
|
+
return opts[:default] unless opts[:default] == :__not_provided
|
74
|
+
raise NameNotFound, "name #{name.inspect} not in #{self.inspect}"
|
75
|
+
end
|
76
|
+
begin
|
77
|
+
value = range.Value
|
78
|
+
rescue WIN32OLERuntimeError
|
79
|
+
return opts[:default] unless opts[:default] == :__not_provided
|
80
|
+
raise RangeNotEvaluatable, "cannot determine value of range named #{name.inspect} in #{self.inspect}"
|
81
|
+
end
|
82
|
+
if value == -2146828288 + RobustExcelOle::XlErrName
|
83
|
+
return opts[:default] unless opts[:default] == __not_provided
|
84
|
+
raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect} in #{File.basename(workbook.stored_filename).inspect rescue nil}"
|
85
|
+
end
|
86
|
+
return opts[:default] unless (opts[:default] == :__not_provided) || value.nil?
|
87
|
+
value
|
88
|
+
end
|
89
|
+
|
90
|
+
# assigns a value to a range given a locally defined name
|
91
|
+
# @param [String] name the name of a range
|
92
|
+
# @param [Variant] value the assigned value
|
93
|
+
# @param [Hash] opts :color [FixNum] the color when setting the contents
|
94
|
+
def set_namevalue(name, value, opts = { :color => 0 })
|
95
|
+
begin
|
96
|
+
return set_namevalue_glob(name, value, opts) if self.is_a?(Workbook)
|
97
|
+
range = self.Range(name)
|
98
|
+
rescue WIN32OLERuntimeError
|
99
|
+
raise NameNotFound, "name #{name.inspect} not in #{self.inspect}"
|
100
|
+
end
|
101
|
+
begin
|
102
|
+
range.Interior.ColorIndex = opts[:color]
|
103
|
+
workbook.modified_cells << range if workbook # unless cell_modified?(range)
|
104
|
+
range.Value = value
|
105
|
+
rescue WIN32OLERuntimeError
|
106
|
+
raise RangeNotEvaluatable, "cannot assign value to range named #{name.inspect} in #{self.inspect}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# @private
|
111
|
+
def nameval(name, opts = { :default => :__not_provided }) # :deprecated: #
|
112
|
+
namevalue_glob(name, opts)
|
113
|
+
end
|
114
|
+
|
115
|
+
# @private
|
116
|
+
def set_nameval(name, value, opts = { :color => 0 }) # :deprecated: #
|
117
|
+
set_namevalue_glob(name, value, opts)
|
118
|
+
end
|
119
|
+
|
120
|
+
# @private
|
121
|
+
def rangeval(name, opts = { :default => :__not_provided }) # :deprecated: #
|
122
|
+
namevalue(name, opts)
|
123
|
+
end
|
124
|
+
|
125
|
+
# @private
|
126
|
+
def set_rangeval(name, value, opts = { :color => 0 }) # :deprecated: #
|
127
|
+
set_namevalue(name, value, opts)
|
128
|
+
end
|
129
|
+
|
130
|
+
# creates a range from a given defined name or address
|
131
|
+
# range(address) does work for Worksheet objects only
|
132
|
+
# @params [Variant] range name or address
|
133
|
+
# @return [Range] a range
|
134
|
+
def range(name_or_address, address2 = :__not_provided)
|
135
|
+
begin
|
136
|
+
if address2 == :__not_provided
|
137
|
+
range = begin
|
138
|
+
RobustExcelOle::Range.new(name_object(name_or_address).RefersToRange)
|
139
|
+
rescue NameNotFound
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
if self.is_a?(Worksheet) && (range.nil? || (address2 != :__not_provided))
|
144
|
+
address = name_or_address
|
145
|
+
address = [name_or_address,address2] unless address2 == :__not_provided
|
146
|
+
self.Names.Add('Name' => '__dummy001', 'RefersToR1C1' => '=' + Address.r1c1(address))
|
147
|
+
range = RobustExcelOle::Range.new(name_object('__dummy001').RefersToRange)
|
148
|
+
self.Names.Item('__dummy001').Delete
|
149
|
+
range
|
150
|
+
# variant via rows, columns:
|
151
|
+
#rows, columns = Address.int_range(address)
|
152
|
+
#ole_range = if rows.nil?
|
153
|
+
# self.Range(self.Columns(columns.min), self.Columns(columns.max))
|
154
|
+
#elsif columns.nil?
|
155
|
+
# self.Range(self.Rows(rows.min), self.Rows(rows.max))
|
156
|
+
#else
|
157
|
+
# self.Range(self.Cells(rows.min, columns.min), self.Cells(rows.max, columns.max))
|
158
|
+
#end
|
159
|
+
##range = ole_range.to_reo
|
160
|
+
#range = Range.new(ole_range)
|
161
|
+
end
|
162
|
+
rescue WIN32OLERuntimeError
|
163
|
+
address2_string = address2.nil? ? "" : ", #{address2.inspect}"
|
164
|
+
raise RangeNotCreated, "cannot create range (#{name_or_address.inspect}#{address2_string})"
|
165
|
+
end
|
166
|
+
range
|
167
|
+
end
|
168
|
+
|
169
|
+
def name2range(name) # :deprecated: #
|
170
|
+
range(name)
|
171
|
+
end
|
172
|
+
|
173
|
+
# adds a name referring to a range given by the row and column
|
174
|
+
# @param [String] name the range name
|
175
|
+
# @params [Address] address of the range
|
176
|
+
def add_name(name, addr, addr_deprecated = :__not_provided)
|
177
|
+
addr = [addr,addr_deprecated] unless addr_deprecated == :__not_provided
|
178
|
+
begin
|
179
|
+
self.Names.Add('Name' => name, 'RefersToR1C1' => '=' + Address.r1c1(addr))
|
180
|
+
rescue WIN32OLERuntimeError => msg
|
181
|
+
raise RangeNotEvaluatable, "cannot add name #{name.inspect} to range #{addr.inspect}"
|
182
|
+
end
|
183
|
+
name
|
184
|
+
end
|
185
|
+
|
186
|
+
def set_name(name,row,column) # :deprecated :#
|
187
|
+
add_name(name,row,column)
|
188
|
+
end
|
189
|
+
|
190
|
+
# renames a range
|
191
|
+
# @param [String] name the previous range name
|
192
|
+
# @param [String] new_name the new range name
|
193
|
+
def rename_range(name, new_name)
|
194
|
+
begin
|
195
|
+
item = self.Names.Item(name)
|
196
|
+
rescue WIN32OLERuntimeError
|
197
|
+
raise NameNotFound, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
|
198
|
+
end
|
199
|
+
begin
|
200
|
+
item.Name = new_name
|
201
|
+
rescue WIN32OLERuntimeError
|
202
|
+
raise UnexpectedREOError, "name error in #{File.basename(self.stored_filename).inspect}"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# deletes a name of a range
|
207
|
+
# @param [String] name the previous range name
|
208
|
+
# @param [String] new_name the new range name
|
209
|
+
def delete_name(name)
|
210
|
+
begin
|
211
|
+
item = self.Names.Item(name)
|
212
|
+
rescue WIN32OLERuntimeError
|
213
|
+
raise NameNotFound, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
|
214
|
+
end
|
215
|
+
begin
|
216
|
+
item.Delete
|
217
|
+
rescue WIN32OLERuntimeError
|
218
|
+
raise UnexpectedREOError, "name error in #{File.basename(self.stored_filename).inspect}"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
private
|
223
|
+
|
224
|
+
def name_object(name)
|
225
|
+
self.Names.Item(name)
|
226
|
+
rescue WIN32OLERuntimeError
|
227
|
+
begin
|
228
|
+
self.Parent.Names.Item(name)
|
229
|
+
rescue WIN32OLERuntimeError
|
230
|
+
raise RobustExcelOle::NameNotFound, "name #{name.inspect} not in #{self.inspect}"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
@@ -1,11 +1,21 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
|
-
LOG_TO_STDOUT =
|
3
|
+
LOG_TO_STDOUT = false unless Object.const_defined?(:LOG_TO_STDOUT)
|
4
4
|
REO_LOG_DIR = ''.freeze unless Object.const_defined?(:REO_LOG_DIR)
|
5
5
|
REO_LOG_FILE = 'reo.log'.freeze unless Object.const_defined?(:REO_LOG_FILE)
|
6
6
|
|
7
7
|
File.delete REO_LOG_FILE rescue nil
|
8
8
|
|
9
|
+
unless "any string".respond_to?(:end_with?)
|
10
|
+
class String
|
11
|
+
def end_with?(*suffixes)
|
12
|
+
suffixes.any? do |suffix|
|
13
|
+
self[-suffix.size .. -1] == suffix
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
9
19
|
module RobustExcelOle
|
10
20
|
|
11
21
|
# @private
|
@@ -21,7 +31,7 @@ module RobustExcelOle
|
|
21
31
|
end
|
22
32
|
|
23
33
|
# @private
|
24
|
-
class
|
34
|
+
class WorksheetREOError < REOError
|
25
35
|
end
|
26
36
|
|
27
37
|
# @private
|
@@ -122,18 +132,22 @@ module RobustExcelOle
|
|
122
132
|
|
123
133
|
class REOCommon
|
124
134
|
|
135
|
+
# @private
|
125
136
|
def excel
|
126
137
|
raise TypeREOError, 'receiver instance is neither an Excel nor a Workbook'
|
127
138
|
end
|
128
139
|
|
140
|
+
# @private
|
129
141
|
def own_methods
|
130
142
|
(self.methods - Object.methods).sort
|
131
143
|
end
|
132
144
|
|
145
|
+
# @private
|
133
146
|
def self.tr1(_text)
|
134
147
|
puts :text
|
135
148
|
end
|
136
149
|
|
150
|
+
# @private
|
137
151
|
def self.trace(text)
|
138
152
|
if LOG_TO_STDOUT
|
139
153
|
puts text
|
@@ -144,6 +158,7 @@ module RobustExcelOle
|
|
144
158
|
reo_log_dir = ENV[home]
|
145
159
|
else
|
146
160
|
reo_log_dir = REO_LOG_DIR
|
161
|
+
#reo_log_dir = "C:/Users/User"
|
147
162
|
end
|
148
163
|
File.open(reo_log_dir + '/' + REO_LOG_FILE,'a') do |file|
|
149
164
|
file.puts text
|
@@ -151,6 +166,7 @@ module RobustExcelOle
|
|
151
166
|
end
|
152
167
|
end
|
153
168
|
|
169
|
+
# @private
|
154
170
|
def self.puts_hash(hash)
|
155
171
|
hash.each do |e|
|
156
172
|
if e[1].is_a?(Hash)
|
@@ -166,278 +182,4 @@ module RobustExcelOle
|
|
166
182
|
|
167
183
|
end
|
168
184
|
|
169
|
-
class Address < REOCommon
|
170
|
-
|
171
|
-
attr_reader :rows
|
172
|
-
attr_reader :columns
|
173
|
-
|
174
|
-
def initialize(address)
|
175
|
-
address = [address] unless address.is_a?(Array)
|
176
|
-
raise AddressInvalid, 'more than two components' if address.size > 2
|
177
|
-
begin
|
178
|
-
if address.size == 1
|
179
|
-
comp1, comp2 = address[0].split(':')
|
180
|
-
address_comp1 = comp1.gsub(/[A-Z]/,'')
|
181
|
-
address_comp2 = comp1.gsub(/[0-9]/,'')
|
182
|
-
if comp1 != address_comp2 + address_comp1
|
183
|
-
raise AddressInvalid, "address #{comp1.inspect} not in A1-format"
|
184
|
-
end
|
185
|
-
unless comp2.nil?
|
186
|
-
address_comp3 = comp2.gsub(/[A-Z]/,'')
|
187
|
-
address_comp4 = comp2.gsub(/[0-9]/,'')
|
188
|
-
if comp2 != address_comp4 + address_comp3
|
189
|
-
raise AddressInvalid, "address #{comp2.inspect} not in A1-format"
|
190
|
-
end
|
191
|
-
address_comp1 = address_comp1..address_comp3
|
192
|
-
address_comp2 = address_comp2..address_comp4
|
193
|
-
end
|
194
|
-
else
|
195
|
-
address_comp1, address_comp2 = address
|
196
|
-
end
|
197
|
-
address_comp1 = address_comp1..address_comp1 unless address_comp1.is_a?(Object::Range)
|
198
|
-
address_comp2 = address_comp2..address_comp2 unless address_comp2.is_a?(Object::Range)
|
199
|
-
@rows = address_comp1.min.to_i..address_comp1.max.to_i
|
200
|
-
if address_comp2.min.to_i == 0
|
201
|
-
raise AddressInvalid, "address (#{address_comp1.inspect}, #{address_comp2.inspect}) not in A1-format" if address_comp1.min.to_i == 0
|
202
|
-
@columns = str2num(address_comp2.begin)..str2num(address_comp2.end)
|
203
|
-
else
|
204
|
-
@columns = address_comp2.min.to_i..address_comp2.max.to_i
|
205
|
-
end
|
206
|
-
rescue
|
207
|
-
raise AddressInvalid, "address (#{address.inspect}) not in A1- or R1C1-format"
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
private
|
212
|
-
|
213
|
-
def str2num(str)
|
214
|
-
str = str.upcase
|
215
|
-
sum = 0
|
216
|
-
(1..str.length).each { |i| sum += (str[i - 1].ord - 64) * 26**(str.length - i) }
|
217
|
-
sum
|
218
|
-
end
|
219
|
-
|
220
|
-
end
|
221
|
-
|
222
|
-
class RangeOwners < REOCommon
|
223
|
-
|
224
|
-
# returns the contents of a range with given name
|
225
|
-
# if the name could not be found or the value could not be determined,
|
226
|
-
# then return default value, if provided, raise error otherwise
|
227
|
-
# Excel Bug: if a local name without a qualifier is given,
|
228
|
-
# then by default Excel takes the first worksheet,
|
229
|
-
# even if a different worksheet is active
|
230
|
-
# @param [String] name the name of the range
|
231
|
-
# @param [Hash] opts the options
|
232
|
-
# @option opts [Symbol] :default the default value that is provided if no contents could be returned
|
233
|
-
# @return [Variant] the contents of a range with given name
|
234
|
-
def namevalue_glob(name, opts = { :default => :__not_provided })
|
235
|
-
name_obj = begin
|
236
|
-
name_object(name)
|
237
|
-
rescue NameNotFound => msg
|
238
|
-
return opts[:default] unless opts[:default] == :__not_provided
|
239
|
-
raise
|
240
|
-
end
|
241
|
-
value = begin
|
242
|
-
name_obj.RefersToRange.Value
|
243
|
-
rescue WIN32OLERuntimeError
|
244
|
-
sheet = if self.is_a?(Worksheet) then self
|
245
|
-
elsif self.is_a?(Workbook) then self.sheet(1)
|
246
|
-
elsif self.is_a?(Excel) then self.workbook.sheet(1)
|
247
|
-
end
|
248
|
-
begin
|
249
|
-
sheet.Evaluate(name_obj.Name).Value
|
250
|
-
rescue # WIN32OLERuntimeError
|
251
|
-
return opts[:default] unless opts[:default] == :__not_provided
|
252
|
-
raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect} in #{self}"
|
253
|
-
end
|
254
|
-
end
|
255
|
-
if value == -2146828288 + RobustExcelOle::XlErrName
|
256
|
-
return opts[:default] unless opts[:default] == :__not_provided
|
257
|
-
raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect} in #{File.basename(workbook.stored_filename).inspect rescue nil}"
|
258
|
-
end
|
259
|
-
return opts[:default] unless (opts[:default] == :__not_provided) || value.nil?
|
260
|
-
value
|
261
|
-
end
|
262
|
-
|
263
|
-
# sets the contents of a range
|
264
|
-
# @param [String] name the name of a range
|
265
|
-
# @param [Variant] value the contents of the range
|
266
|
-
# @param [FixNum] color the color when setting a value
|
267
|
-
# @param [Hash] opts :color [FixNum] the color when setting the contents
|
268
|
-
def set_namevalue_glob(name, value, opts = { :color => 0 })
|
269
|
-
cell = name_object(name).RefersToRange
|
270
|
-
cell.Interior.ColorIndex = opts[:color]
|
271
|
-
workbook.modified_cells << cell if workbook # unless cell_modified?(cell)
|
272
|
-
cell.Value = value
|
273
|
-
rescue WIN32OLERuntimeError
|
274
|
-
raise RangeNotEvaluatable, "cannot assign value to range named #{name.inspect} in #{self.inspect}"
|
275
|
-
end
|
276
|
-
|
277
|
-
# returns the contents of a range with a locally defined name
|
278
|
-
# evaluates the formula if the contents is a formula
|
279
|
-
# if the name could not be found or the range or value could not be determined,
|
280
|
-
# then return default value, if provided, raise error otherwise
|
281
|
-
# @param [String] name the name of a range
|
282
|
-
# @param [Hash] opts the options
|
283
|
-
# @option opts [Symbol] :default the default value that is provided if no contents could be returned
|
284
|
-
# @return [Variant] the contents of a range with given name
|
285
|
-
def namevalue(name, opts = { :default => :__not_provided })
|
286
|
-
return namevalue_glob(name, opts) if self.is_a?(Workbook)
|
287
|
-
begin
|
288
|
-
range = self.Range(name)
|
289
|
-
rescue WIN32OLERuntimeError
|
290
|
-
return opts[:default] unless opts[:default] == :__not_provided
|
291
|
-
raise NameNotFound, "name #{name.inspect} not in #{self.inspect}"
|
292
|
-
end
|
293
|
-
begin
|
294
|
-
value = range.Value
|
295
|
-
rescue WIN32OLERuntimeError
|
296
|
-
return opts[:default] unless opts[:default] == :__not_provided
|
297
|
-
raise RangeNotEvaluatable, "cannot determine value of range named #{name.inspect} in #{self.inspect}"
|
298
|
-
end
|
299
|
-
if value == -2146828288 + RobustExcelOle::XlErrName
|
300
|
-
return opts[:default] unless opts[:default] == __not_provided
|
301
|
-
raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect} in #{File.basename(workbook.stored_filename).inspect rescue nil}"
|
302
|
-
end
|
303
|
-
return opts[:default] unless (opts[:default] == :__not_provided) || value.nil?
|
304
|
-
value
|
305
|
-
end
|
306
|
-
|
307
|
-
# assigns a value to a range given a locally defined name
|
308
|
-
# @param [String] name the name of a range
|
309
|
-
# @param [Variant] value the assigned value
|
310
|
-
# @param [Hash] opts :color [FixNum] the color when setting the contents
|
311
|
-
def set_namevalue(name, value, opts = { :color => 0 })
|
312
|
-
begin
|
313
|
-
return set_namevalue_glob(name, value, opts) if self.is_a?(Workbook)
|
314
|
-
range = self.Range(name)
|
315
|
-
rescue WIN32OLERuntimeError
|
316
|
-
raise NameNotFound, "name #{name.inspect} not in #{self.inspect}"
|
317
|
-
end
|
318
|
-
begin
|
319
|
-
range.Interior.ColorIndex = opts[:color]
|
320
|
-
workbook.modified_cells << range if workbook # unless cell_modified?(range)
|
321
|
-
range.Value = value
|
322
|
-
rescue WIN32OLERuntimeError
|
323
|
-
raise RangeNotEvaluatable, "cannot assign value to range named #{name.inspect} in #{self.inspect}"
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
def nameval(name, opts = { :default => :__not_provided }) # :deprecated: #
|
328
|
-
namevalue_glob(name, opts)
|
329
|
-
end
|
330
|
-
|
331
|
-
def set_nameval(name, value, opts = { :color => 0 }) # :deprecated: #
|
332
|
-
set_namevalue_glob(name, value, opts)
|
333
|
-
end
|
334
|
-
|
335
|
-
def rangeval(name, opts = { :default => :__not_provided }) # :deprecated: #
|
336
|
-
namevalue(name, opts)
|
337
|
-
end
|
338
|
-
|
339
|
-
def set_rangeval(name, value, opts = { :color => 0 }) # :deprecated: #
|
340
|
-
set_namevalue(name, value, opts)
|
341
|
-
end
|
342
|
-
|
343
|
-
# creates a range from a given defined name or address
|
344
|
-
# @params [Variant] range name or address
|
345
|
-
# @return [Range] a range
|
346
|
-
def range(name_or_address, address2 = :__not_provided)
|
347
|
-
begin
|
348
|
-
if address2 == :__not_provided
|
349
|
-
range = RobustExcelOle::Range.new(name_object(name_or_address).RefersToRange) rescue nil
|
350
|
-
end
|
351
|
-
if self.is_a?(Worksheet) && (range.nil? || (address2 != :__not_provided))
|
352
|
-
address = name_or_address
|
353
|
-
address = [name_or_address,address2] unless address2 == :__not_provided
|
354
|
-
address = Address.new(address)
|
355
|
-
range = RobustExcelOle::Range.new(@ole_worksheet.Range(
|
356
|
-
@ole_worksheet.Cells(address.rows.min, address.columns.min),
|
357
|
-
@ole_worksheet.Cells(address.rows.max, address.columns.max)
|
358
|
-
))
|
359
|
-
end
|
360
|
-
rescue WIN32OLERuntimeError
|
361
|
-
address2_string = address2.nil? ? "" : ", #{address2.inspect}"
|
362
|
-
raise RangeNotCreated, "cannot create range (#{name_or_address.inspect}#{address2_string})"
|
363
|
-
end
|
364
|
-
range
|
365
|
-
end
|
366
|
-
|
367
|
-
def name2range(name) # :deprecated: #
|
368
|
-
range(name)
|
369
|
-
end
|
370
|
-
|
371
|
-
# adds a name referring to a range given by the row and column
|
372
|
-
# @param [String] name the range name
|
373
|
-
# @params [Address] address of the range
|
374
|
-
def add_name(name, addr, addr_deprecated = :__not_provided)
|
375
|
-
addr = [addr,addr_deprecated] unless addr_deprecated == :__not_provided
|
376
|
-
address = Address.new(addr)
|
377
|
-
address_string = 'Z' + address.rows.min.to_s + 'S' + address.columns.min.to_s +
|
378
|
-
':Z' + address.rows.max.to_s + 'S' + address.columns.max.to_s
|
379
|
-
begin
|
380
|
-
self.Names.Add('Name' => name, 'RefersToR1C1' => '=' + address_string)
|
381
|
-
rescue WIN32OLERuntimeError => msg
|
382
|
-
# trace "WIN32OLERuntimeError: #{msg.message}"
|
383
|
-
raise RangeNotEvaluatable, "cannot add name #{name.inspect} to range #{addr.inspect}"
|
384
|
-
end
|
385
|
-
name
|
386
|
-
end
|
387
|
-
|
388
|
-
def set_name(name,row,column) # :deprecated :#
|
389
|
-
add_name(name,row,column)
|
390
|
-
end
|
391
|
-
|
392
|
-
# renames a range
|
393
|
-
# @param [String] name the previous range name
|
394
|
-
# @param [String] new_name the new range name
|
395
|
-
def rename_range(name, new_name)
|
396
|
-
begin
|
397
|
-
item = self.Names.Item(name)
|
398
|
-
rescue WIN32OLERuntimeError
|
399
|
-
raise NameNotFound, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
|
400
|
-
end
|
401
|
-
begin
|
402
|
-
item.Name = new_name
|
403
|
-
rescue WIN32OLERuntimeError
|
404
|
-
raise UnexpectedREOError, "name error in #{File.basename(self.stored_filename).inspect}"
|
405
|
-
end
|
406
|
-
end
|
407
|
-
|
408
|
-
# deletes a name of a range
|
409
|
-
# @param [String] name the previous range name
|
410
|
-
# @param [String] new_name the new range name
|
411
|
-
def delete_name(name)
|
412
|
-
begin
|
413
|
-
item = self.Names.Item(name)
|
414
|
-
rescue WIN32OLERuntimeError
|
415
|
-
raise NameNotFound, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
|
416
|
-
end
|
417
|
-
begin
|
418
|
-
item.Delete
|
419
|
-
rescue WIN32OLERuntimeError
|
420
|
-
raise UnexpectedREOError, "name error in #{File.basename(self.stored_filename).inspect}"
|
421
|
-
end
|
422
|
-
end
|
423
|
-
|
424
|
-
private
|
425
|
-
|
426
|
-
def name_object(name)
|
427
|
-
self.Names.Item(name)
|
428
|
-
rescue WIN32OLERuntimeError
|
429
|
-
begin
|
430
|
-
self.Parent.Names.Item(name)
|
431
|
-
rescue WIN32OLERuntimeError
|
432
|
-
raise RobustExcelOle::NameNotFound, "name #{name.inspect} not in #{self.inspect}"
|
433
|
-
end
|
434
|
-
end
|
435
|
-
|
436
|
-
# def cell_modified?(cell)
|
437
|
-
# workbook.modified_cells.each{|c| return true if c.Name.Value == cell.Name.Value}
|
438
|
-
# false
|
439
|
-
# end
|
440
|
-
|
441
|
-
end
|
442
|
-
|
443
185
|
end
|