robust_excel_ole 1.26 → 1.31

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog +17 -0
  3. data/README.rdoc +12 -5
  4. data/benchmarking/reo_example.rb +1 -1
  5. data/benchmarking/reo_example1.rb +1 -1
  6. data/benchmarking/reo_example2.rb +1 -1
  7. data/bin/jreo +20 -1
  8. data/bin/reo +20 -1
  9. data/docs/README_open.rdoc +8 -4
  10. data/docs/README_sheet.rdoc +1 -7
  11. data/examples/introductory_examples/example_open.rb +11 -0
  12. data/lib/robust_excel_ole.rb +19 -16
  13. data/lib/robust_excel_ole/base.rb +5 -0
  14. data/lib/robust_excel_ole/bookstore.rb +1 -1
  15. data/lib/robust_excel_ole/cell.rb +1 -1
  16. data/lib/robust_excel_ole/cygwin.rb +2 -0
  17. data/lib/robust_excel_ole/excel.rb +12 -22
  18. data/lib/robust_excel_ole/general.rb +270 -206
  19. data/lib/robust_excel_ole/list_object.rb +18 -115
  20. data/lib/robust_excel_ole/list_row.rb +128 -0
  21. data/lib/robust_excel_ole/range.rb +5 -1
  22. data/lib/robust_excel_ole/range_owners.rb +4 -71
  23. data/lib/robust_excel_ole/version.rb +1 -1
  24. data/lib/robust_excel_ole/workbook.rb +71 -29
  25. data/lib/robust_excel_ole/worksheet.rb +107 -22
  26. data/lib/spec_helper.rb +1 -1
  27. data/spec/address_tool_spec.rb +2 -2
  28. data/spec/base_spec.rb +19 -17
  29. data/spec/bookstore_spec.rb +1 -1
  30. data/spec/cell_spec.rb +1 -1
  31. data/spec/cygwin_spec.rb +1 -1
  32. data/spec/data/more_data/workbook.xls +0 -0
  33. data/spec/excel_spec.rb +1 -1
  34. data/spec/general_spec.rb +64 -7
  35. data/spec/list_object_spec.rb +85 -20
  36. data/spec/range_spec.rb +1 -14
  37. data/spec/spec_helper.rb +1 -1
  38. data/spec/workbook_spec.rb +12 -12
  39. data/spec/workbook_specs/workbook_all_spec.rb +8 -28
  40. data/spec/workbook_specs/workbook_close_spec.rb +1 -1
  41. data/spec/workbook_specs/workbook_misc_spec.rb +33 -33
  42. data/spec/workbook_specs/workbook_open_spec.rb +56 -5
  43. data/spec/workbook_specs/workbook_save_spec.rb +1 -1
  44. data/spec/workbook_specs/workbook_sheet_spec.rb +1 -1
  45. data/spec/workbook_specs/workbook_subclass_spec.rb +1 -1
  46. data/spec/workbook_specs/workbook_unobtr_spec.rb +273 -115
  47. data/spec/worksheet_spec.rb +51 -19
  48. metadata +3 -5
  49. data/jreo.bat +0 -3
  50. data/lib/reo_console.rb +0 -68
  51. data/reo.bat +0 -3
@@ -2,9 +2,9 @@
2
2
 
3
3
  module RobustExcelOle
4
4
 
5
- class ListRow
6
- end
7
-
5
+ using ToReoRefinement
6
+ using FindAllIndicesRefinement
7
+
8
8
  # This class essentially wraps a Win32Ole ListObject.
9
9
  # You can apply all VBA methods (starting with a capital letter)
10
10
  # that you would apply for a ListObject.
@@ -59,121 +59,25 @@ module RobustExcelOle
59
59
  end
60
60
  end
61
61
 
62
-
63
62
  ole_table = @ole_table
63
+
64
64
  @row_class = Class.new(ListRow) do
65
65
 
66
66
  @@ole_table = ole_table
67
67
 
68
- def initialize(row_number)
69
- @ole_listrow = @@ole_table.ListRows.Item(row_number)
70
- end
71
-
72
- # returns the value of the cell with given column name or number
73
- # @param [Variant] column number or column name
74
- # @return [Variant] value of the cell
75
- def [] column_number_or_name
76
- begin
77
- ole_cell = @@ole_table.Application.Intersect(
78
- @ole_listrow.Range, @@ole_table.ListColumns.Item(column_number_or_name).Range)
79
- ole_cell.Value
80
- rescue WIN32OLERuntimeError
81
- raise TableRowError, "could not determine the value at column #{column_number_or_name}"
82
- end
68
+ def ole_table
69
+ @@ole_table
83
70
  end
71
+
72
+ end
84
73
 
74
+ end
85
75
 
86
- def []=(column_number_or_name, value)
87
- begin
88
- ole_cell = @@ole_table.Application.Intersect(
89
- @ole_listrow.Range, @@ole_table.ListColumns.Item(column_number_or_name).Range)
90
- ole_cell.Value = value
91
- rescue WIN32OLERuntimeError
92
- raise TableRowError, "could not assign value #{value.inspect} to cell at column #{column_number_or_name}"
93
- end
94
- end
95
-
96
- # values of the row
97
- # @return [Array] values of the row
98
- def values
99
- begin
100
- @ole_listrow.Range.Value.first
101
- rescue WIN32OLERuntimeError
102
- raise TableError, "could not read values"
103
- end
104
- end
105
-
106
- # sets the values of the row
107
- # @param [Array] values of the row
108
- def set_values values
109
- begin
110
- updated_values = self.values
111
- updated_values[0,values.length] = values
112
- @ole_listrow.Range.Value = [updated_values]
113
- values
114
- rescue WIN32OLERuntimeError
115
- raise TableError, "could not set values #{values.inspect}"
116
- end
117
- end
118
-
119
- # deletes the values of the row
120
- def delete_values
121
- begin
122
- @ole_listrow.Range.Value = [[].fill(nil,0..(@@ole_table.ListColumns.Count)-1)]
123
- nil
124
- rescue WIN32OLERuntimeError
125
- raise TableError, "could not delete values"
126
- end
127
- end
128
-
129
- def method_missing(name, *args)
130
- name_str = name.to_s
131
- core_name = name_str[-1]!='=' ? name_str : name_str[0..-2]
132
- column_names = @@ole_table.HeaderRowRange.Value.first
133
- column_name = column_names.find do |c|
134
- c == core_name ||
135
- c.gsub(/\W/,'') == core_name ||
136
- c.replace_umlauts == core_name ||
137
- c.gsub(/\W/,'').replace_umlauts == core_name ||
138
- c.gsub(/\W/,'').replace_umlauts.underscore.gsub(/[^[\w\d]]/, '_').delete_multiple_underscores == core_name
139
- end
140
- if column_name
141
- method_name = core_name.gsub(/\W/,'') + (name_str[-1]!='=' ? "" : "=")
142
- define_and_call_method(column_name,method_name,*args)
143
- else
144
- super(name, *args)
145
- end
146
- end
147
-
148
- private
149
-
150
- def define_and_call_method(column_name,method_name,*args)
151
- ole_cell = @@ole_table.Application.Intersect(
152
- @ole_listrow.Range, @@ole_table.ListColumns.Item(column_name).Range)
153
- define_getting_setting_method(ole_cell,method_name)
154
- self.send(method_name, *args)
155
- end
156
-
157
- def define_getting_setting_method(ole_cell,name)
158
- if name[-1] != '='
159
- self.class.define_method(name) do
160
- ole_cell.Value
161
- end
162
- else
163
- self.class.define_method(name) do |value|
164
- ole_cell.Value = value
165
- end
166
- end
167
- end
168
- end
169
-
170
- # accesses a table row object
171
- # @param [Integer] a row number (>= 1)
172
- # @return [ListRow] a object of dynamically constructed class with superclass ListRow
173
- def [] row_number
174
- @row_class.new(row_number)
175
- end
176
-
76
+ # accesses a table row object
77
+ # @param [Integer] a row number (>= 1)
78
+ # @return [ListRow] a object of dynamically constructed class with superclass ListRow
79
+ def [] row_number
80
+ @row_class.new(row_number)
177
81
  end
178
82
 
179
83
  # @return [Array] a list of column names
@@ -213,7 +117,7 @@ module RobustExcelOle
213
117
 
214
118
  # deletes a row
215
119
  # @param [Integer] position of the old row
216
- def delete_row(row_number)
120
+ def delete_row(row_number) # :nodoc: #
217
121
  begin
218
122
  @ole_table.ListRows.Item(row_number).Delete
219
123
  rescue WIN32OLERuntimeError
@@ -223,7 +127,7 @@ module RobustExcelOle
223
127
 
224
128
  # deletes a column
225
129
  # @param [Variant] column number or column name
226
- def delete_column(column_number_or_name)
130
+ def delete_column(column_number_or_name) # :nodoc: #
227
131
  begin
228
132
  @ole_table.ListColumns.Item(column_number_or_name).Delete
229
133
  rescue WIN32OLERuntimeError
@@ -257,7 +161,7 @@ module RobustExcelOle
257
161
  # renames a row
258
162
  # @param [String] previous name or number of the column
259
163
  # @param [String] new name of the column
260
- def rename_column(name_or_number, new_name)
164
+ def rename_column(name_or_number, new_name) # :nodoc: #
261
165
  begin
262
166
  @ole_table.ListColumns.Item(name_or_number).Name = new_name
263
167
  rescue
@@ -351,7 +255,7 @@ module RobustExcelOle
351
255
  listrows = @ole_table.ListRows
352
256
  result = []
353
257
  (1..listrows.Count).each do |row_number|
354
- row_values(row_number).find_each_index(value).each do |col_number|
258
+ row_values(row_number).find_all_indices(value).each do |col_number|
355
259
  result << @ole_table.Application.Intersect(listrows.Item(row_number).Range,
356
260
  @ole_table.ListColumns.Item(col_number+1).Range).to_reo
357
261
  end
@@ -426,6 +330,5 @@ module RobustExcelOle
426
330
  end
427
331
 
428
332
  Table = ListObject
429
- TableRow = ListRow
430
333
 
431
334
  end
@@ -0,0 +1,128 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module RobustExcelOle
4
+
5
+ using StringRefinement
6
+
7
+ class ListRow
8
+
9
+ def initialize(row_number)
10
+ @ole_listrow = ole_table.ListRows.Item(row_number)
11
+ end
12
+
13
+ # returns the value of the cell with given column name or number
14
+ # @param [Variant] column number or column name
15
+ # @return [Variant] value of the cell
16
+ def [] column_number_or_name
17
+ begin
18
+ ole_cell = ole_table.Application.Intersect(
19
+ @ole_listrow.Range, ole_table.ListColumns.Item(column_number_or_name).Range)
20
+ ole_cell.Value
21
+ rescue WIN32OLERuntimeError
22
+ raise TableRowError, "could not determine the value at column #{column_number_or_name}"
23
+ end
24
+ end
25
+
26
+ # sets the value of the cell with given column name or number
27
+ # @param [Variant] column number or column name
28
+ # @param [Variant] value of the cell
29
+ def []=(column_number_or_name, value)
30
+ begin
31
+ ole_cell = ole_table.Application.Intersect(
32
+ @ole_listrow.Range, ole_table.ListColumns.Item(column_number_or_name).Range)
33
+ ole_cell.Value = value
34
+ rescue WIN32OLERuntimeError
35
+ raise TableRowError, "could not assign value #{value.inspect} to cell at column #{column_number_or_name}"
36
+ end
37
+ end
38
+
39
+ # values of the row
40
+ # @return [Array] values of the row
41
+ def values
42
+ begin
43
+ @ole_listrow.Range.Value.first
44
+ rescue WIN32OLERuntimeError
45
+ raise TableError, "could not read values"
46
+ end
47
+ end
48
+
49
+ # sets the values of the row
50
+ # @param [Array] values of the row
51
+ def set_values values
52
+ begin
53
+ updated_values = self.values
54
+ updated_values[0,values.length] = values
55
+ @ole_listrow.Range.Value = [updated_values]
56
+ values
57
+ rescue WIN32OLERuntimeError
58
+ raise TableError, "could not set values #{values.inspect}"
59
+ end
60
+ end
61
+
62
+ # deletes the values of the row
63
+ def delete_values
64
+ begin
65
+ @ole_listrow.Range.Value = [[].fill(nil,0..(ole_table.ListColumns.Count)-1)]
66
+ nil
67
+ rescue WIN32OLERuntimeError
68
+ raise TableError, "could not delete values"
69
+ end
70
+ end
71
+
72
+ def method_missing(name, *args)
73
+ name_str = name.to_s
74
+ core_name = name_str[-1]!='=' ? name_str : name_str[0..-2]
75
+ column_names = ole_table.HeaderRowRange.Value.first
76
+ column_name = column_names.find do |c|
77
+ c == core_name ||
78
+ c.gsub(/\W/,'_') == core_name ||
79
+ c.underscore == core_name ||
80
+ c.underscore.gsub(/\W/,'_') == core_name ||
81
+ c.replace_umlauts.gsub(/\W/,'_') == core_name ||
82
+ c.replace_umlauts.underscore.gsub(/\W/,'_') == core_name
83
+ end
84
+ if column_name
85
+ appended_eq = (name_str[-1]!='=' ? "" : "=")
86
+ method_name = core_name.replace_umlauts.underscore + appended_eq
87
+ define_and_call_method(column_name,method_name,*args)
88
+ else
89
+ super(name, *args)
90
+ end
91
+ end
92
+
93
+ # @private
94
+ def to_s
95
+ inspect
96
+ end
97
+
98
+ # @private
99
+ def inspect
100
+ "#<ListRow: " + "index:#{@ole_listrow.Index}" + " size:#{ole_table.ListColumns.Count}" + " #{ole_table.Name}" + ">"
101
+ end
102
+
103
+ private
104
+
105
+ def define_and_call_method(column_name,method_name,*args)
106
+ ole_cell = ole_table.Application.Intersect(
107
+ @ole_listrow.Range, ole_table.ListColumns.Item(column_name).Range)
108
+ define_getting_setting_method(ole_cell,method_name)
109
+ self.send(method_name, *args)
110
+ end
111
+
112
+ def define_getting_setting_method(ole_cell,name)
113
+ if name[-1] != '='
114
+ self.class.define_method(name) do
115
+ ole_cell.Value
116
+ end
117
+ else
118
+ self.class.define_method(name) do |value|
119
+ ole_cell.Value = value
120
+ end
121
+ end
122
+ end
123
+
124
+ end
125
+
126
+ TableRow = ListRow
127
+
128
+ end
@@ -16,6 +16,7 @@ module RobustExcelOle
16
16
 
17
17
  alias ole_object ole_range
18
18
 
19
+ using ToReoRefinement
19
20
 
20
21
  def initialize(win32_range, worksheet = nil)
21
22
  @ole_range = win32_range
@@ -221,11 +222,14 @@ module RobustExcelOle
221
222
  to_s
222
223
  end
223
224
 
225
+ using ParentRefinement
226
+ using StringRefinement
227
+
224
228
  # @private
225
229
  def self.worksheet_class
226
230
  @worksheet_class ||= begin
227
231
  module_name = parent_name
228
- "#{module_name}::Worksheet".constantize
232
+ "#{module_name}::Worksheet".constantize
229
233
  rescue NameError => e
230
234
  Worksheet
231
235
  end
@@ -14,7 +14,7 @@ module RobustExcelOle
14
14
  # @param [Hash] opts the options
15
15
  # @option opts [Symbol] :default the default value that is provided if no contents could be returned
16
16
  # @return [Variant] the contents of a range with given name
17
- def namevalue_glob(name, opts = { :default => :__not_provided })
17
+ def namevalue_global(name, opts = { :default => :__not_provided })
18
18
  name_obj = begin
19
19
  name_object(name)
20
20
  rescue NameNotFound => msg
@@ -63,7 +63,7 @@ module RobustExcelOle
63
63
  # @param [String] name the name of a range
64
64
  # @param [Variant] value the contents of the range
65
65
  # @option opts [Symbol] :color the color of the range when set
66
- def set_namevalue_glob(name, value, opts = { })
66
+ def set_namevalue_global(name, value, opts = { })
67
67
  begin
68
68
  name_obj = begin
69
69
  name_object(name)
@@ -89,81 +89,14 @@ module RobustExcelOle
89
89
  end
90
90
  end
91
91
 
92
- # returns the contents of a range with a locally defined name
93
- # evaluates the formula if the contents is a formula
94
- # if the name could not be found or the range or value could not be determined,
95
- # then return default value, if provided, raise error otherwise
96
- # @param [String] name the name of a range
97
- # @param [Hash] opts the options
98
- # @option opts [Symbol] :default the default value that is provided if no contents could be returned
99
- # @return [Variant] the contents of a range with given name
100
- def namevalue(name, opts = { :default => :__not_provided })
101
- return namevalue_glob(name, opts) if self.is_a?(Workbook)
102
- begin
103
- ole_range = self.Range(name)
104
- rescue # WIN32OLERuntimeError, VBAMethodMissingError, Java::OrgRacobCom::ComFailException
105
- return opts[:default] unless opts[:default] == :__not_provided
106
- raise NameNotFound, "name #{name.inspect} not in #{self.inspect}"
107
- end
108
- begin
109
- worksheet = self if self.is_a?(Worksheet)
110
- #value = ole_range.Value
111
- value = if !::RANGES_JRUBY_BUG
112
- ole_range.Value
113
- else
114
- values = RobustExcelOle::Range.new(ole_range, worksheet).v
115
- (values.size==1 && values.first.size==1) ? values.first.first : values
116
- end
117
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
118
- return opts[:default] unless opts[:default] == :__not_provided
119
- raise RangeNotEvaluatable, "cannot determine value of range named #{name.inspect} in #{self.inspect}"
120
- end
121
- if value == -2146828288 + RobustExcelOle::XlErrName
122
- return opts[:default] unless opts[:default] == __not_provided
123
- raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect} in #{File.basename(workbook.stored_filename).inspect rescue nil}"
124
- end
125
- return opts[:default] unless (opts[:default] == :__not_provided) || value.nil?
126
- value
127
- end
128
-
129
- # assigns a value to a range given a locally defined name
130
- # @param [String] name the name of a range
131
- # @param [Variant] value the assigned value
132
- # @option opts [Symbol] :color the color of the cell when set
133
- def set_namevalue(name, value, opts = { })
134
- begin
135
- return set_namevalue_glob(name, value, opts) if self.is_a?(Workbook)
136
- ole_range = self.Range(name)
137
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException, VBAMethodMissingError
138
- raise NameNotFound, "name #{name.inspect} not in #{self.inspect}"
139
- end
140
- begin
141
- ole_range.Interior.ColorIndex = opts[:color] unless opts[:color].nil?
142
- if !::RANGES_JRUBY_BUG
143
- ole_range.Value = value
144
- else
145
- address_r1c1 = ole_range.AddressLocal(true,true,XlR1C1)
146
- row, col = address_tool.as_integer_ranges(address_r1c1)
147
- row.each_with_index do |r,i|
148
- col.each_with_index do |c,j|
149
- ole_range.Cells(i+1,j+1).Value = (value.respond_to?(:first) ? value[i][j] : value)
150
- end
151
- end
152
- end
153
- value
154
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
155
- raise RangeNotEvaluatable, "cannot assign value to range named #{name.inspect} in #{self.inspect}"
156
- end
157
- end
158
-
159
92
  # @private
160
93
  def nameval(name, opts = { :default => :__not_provided }) # :deprecated: #
161
- namevalue_glob(name, opts)
94
+ namevalue_global(name, opts)
162
95
  end
163
96
 
164
97
  # @private
165
98
  def set_nameval(name, value) # :deprecated: #
166
- set_namevalue_glob(name, value)
99
+ set_namevalue_global(name, value)
167
100
  end
168
101
 
169
102
  # @private
@@ -1,3 +1,3 @@
1
1
  module RobustExcelOle
2
- VERSION = "1.26"
2
+ VERSION = "1.31"
3
3
  end
@@ -11,14 +11,14 @@ module RobustExcelOle
11
11
 
12
12
  class Workbook < RangeOwners
13
13
 
14
- #include General
15
-
16
14
  attr_reader :ole_workbook
17
15
  attr_reader :excel
18
16
  attr_reader :stored_filename
19
17
 
20
18
  alias ole_object ole_workbook
21
19
 
20
+ using ToReoRefinement
21
+
22
22
  CORE_DEFAULT_OPEN_OPTS = {
23
23
  :default => {:excel => :current},
24
24
  :force => {},
@@ -42,7 +42,7 @@ module RobustExcelOle
42
42
 
43
43
 
44
44
  # opens a workbook.
45
- # @param [String] file_or_workbook a file name or WIN32OLE workbook
45
+ # @param [String,Pathname] file_or_workbook a file name (string or pathname) or WIN32OLE workbook
46
46
  # @param [Hash] opts the options
47
47
  # @option opts [Hash] :default or :d
48
48
  # @option opts [Hash] :force or :f
@@ -158,7 +158,7 @@ module RobustExcelOle
158
158
  @excel = excel_class.new(ole_excel)
159
159
  filename = @ole_workbook.Fullname.tr('\\','/')
160
160
  else
161
- filename = file_or_workbook
161
+ filename = file_or_workbook
162
162
  ensure_workbook(filename, opts)
163
163
  end
164
164
  apply_options(filename, opts)
@@ -235,13 +235,12 @@ module RobustExcelOle
235
235
  raise ExcelREOError, "Excel is not alive" unless @excel && @excel.alive?
236
236
  end
237
237
 
238
-
239
238
  # @private
240
- def ensure_workbook(filename, options)
239
+ def ensure_workbook(filename, options)
241
240
  set_was_open options, true
242
241
  return if (@ole_workbook && alive? && (options[:read_only].nil? || @ole_workbook.ReadOnly == options[:read_only]))
243
242
  set_was_open options, false
244
- if options[:if_unsaved]==:accept &&
243
+ if options[:if_unsaved]==:accept && alive? &&
245
244
  ((options[:read_only]==true && self.ReadOnly==false) || (options[:read_only]==false && self.ReadOnly==true))
246
245
  raise OptionInvalid, ":if_unsaved:accept and change of read-only mode is not possible"
247
246
  end
@@ -254,7 +253,7 @@ module RobustExcelOle
254
253
  if @ole_workbook && alive?
255
254
  set_was_open options, true
256
255
  manage_blocking_or_unsaved_workbook(filename,options)
257
- open_or_create_workbook(filename,options) if @ole_workbook.ReadOnly != options[:read_only]
256
+ open_or_create_workbook(filename,options) if (!options[:read_only].nil?) && options[:read_only] != @ole_workbook.ReadOnly
258
257
  else
259
258
  if (excel_option.nil? || excel_option == :current) &&
260
259
  !(::CONNECT_JRUBY_BUG && filename[0] == '/')
@@ -270,7 +269,6 @@ module RobustExcelOle
270
269
  # applies options to workbook named with filename
271
270
  def apply_options(filename, options)
272
271
  # changing read-only mode
273
- #ensure_workbook(filename, options) if options[:read_only] && options[:read_only] != @ole_workbook.ReadOnly
274
272
  if (!options[:read_only].nil?) && options[:read_only] != @ole_workbook.ReadOnly
275
273
  ensure_workbook(filename, options)
276
274
  end
@@ -327,7 +325,7 @@ module RobustExcelOle
327
325
  def manage_blocking_or_unsaved_workbook(filename, options)
328
326
  filename = General.absolute_path(filename)
329
327
  filename = General.canonize(filename)
330
- previous_file = General.canonize(@ole_workbook.Fullname)
328
+ previous_file = General.canonize(@ole_workbook.Fullname.gsub('\\','/'))
331
329
  obstructed_by_other_book = (File.basename(filename) == File.basename(previous_file)) &&
332
330
  (File.dirname(filename) != File.dirname(previous_file))
333
331
  if obstructed_by_other_book
@@ -341,11 +339,12 @@ module RobustExcelOle
341
339
  end
342
340
  end
343
341
 
344
- def manage_blocking_workbook(filename, options)
342
+ def manage_blocking_workbook(filename, options)
343
+ blocked_filename = -> { General.canonize(@ole_workbook.Fullname.tr('\\','/')) }
345
344
  case options[:if_obstructed]
346
345
  when :raise
347
346
  raise WorkbookBlocked, "can't open workbook #{filename},
348
- because it is being blocked by #{@ole_workbook.Fullname.tr('\\','/')} with the same name in a different path." +
347
+ because it is being blocked by #{blocked_filename.call} with the same name in a different path." +
349
348
  "\nHint: Use the option :if_blocked with values :forget or :save,
350
349
  to allow automatic closing of the old workbook (without or with saving before, respectively),
351
350
  before the new workbook is being opened."
@@ -355,7 +354,7 @@ module RobustExcelOle
355
354
  manage_saving_workbook(filename, options)
356
355
  when :close_if_saved
357
356
  if !@ole_workbook.Saved
358
- raise WorkbookBlocked, "workbook with the same name in a different path is unsaved: #{@ole_workbook.Fullname.tr('\\','/')}" +
357
+ raise WorkbookBlocked, "workbook with the same name in a different path is unsaved: #{blocked_filename.call}" +
359
358
  "\nHint: Use the option :if_blocked => :save to save the workbook"
360
359
  else
361
360
  manage_forgetting_workbook(filename, options)
@@ -371,9 +370,14 @@ module RobustExcelOle
371
370
  def manage_unsaved_workbook(filename, options)
372
371
  case options[:if_unsaved]
373
372
  when :raise
374
- raise WorkbookNotSaved, "workbook is already open but not saved: #{File.basename(filename).inspect}" +
375
- "\nHint: Save the workbook or open the workbook using option :if_unsaved with values :forget and :accept to
376
- close the unsaved workbook and reopen it, or to let the unsaved workbook open, respectively"
373
+ msg = if !options[:read_only].nil? && @ole_workbook.ReadOnly != options[:read_only]
374
+ "cannot change read-only mode of the workbook #{File.basename(filename).inspect}, because it contains unsaved changes"
375
+ else
376
+ "workbook is already open but not saved: #{File.basename(filename).inspect}"
377
+ end
378
+ raise WorkbookNotSaved, msg +
379
+ "\nHint: Use the option :if_unsaved with values :forget to close the unsaved workbook,
380
+ :accept to let it open, or :save to save it, respectivly"
377
381
  when :forget
378
382
  manage_forgetting_workbook(filename, options)
379
383
  when :accept
@@ -406,7 +410,24 @@ module RobustExcelOle
406
410
  @ole_workbook = nil
407
411
  open_or_create_workbook(filename, options)
408
412
  end
409
-
413
+
414
+ def explore_workbook_error(msg, want_change_readonly = nil)
415
+ if msg.message =~ /800A03EC/ && msg.message =~ /0x80020009/
416
+ # error message:
417
+ # 'This workbook is currently referenced by another workbook and cannot be closed'
418
+ # 'Diese Arbeitsmappe wird momentan von einer anderen Arbeitsmappe verwendet und kann nicht geschlossen werden.'
419
+ if want_change_readonly==true
420
+ raise WorkbookLinked, "read-only mode of this workbook cannot be changed, because it is being used by another workbook"
421
+ elsif want_change_readonly.nil?
422
+ raise WorkbookLinked, "workbook is being used by another workbook"
423
+ else
424
+ raise UnexpectedREOError, "unknown WIN32OLERuntimeError:\n#{msg.message}"
425
+ end
426
+ else
427
+ raise UnexpectedREOError, "unknown WIN32OLERuntimeError:\n#{msg.message}"
428
+ end
429
+ end
430
+
410
431
  def open_or_create_workbook(filename, options)
411
432
  return if @ole_workbook && options[:if_unsaved] != :alert && options[:if_unsaved] != :excel &&
412
433
  (options[:read_only].nil? || options[:read_only]==@ole_workbook.ReadOnly )
@@ -427,7 +448,8 @@ module RobustExcelOle
427
448
  rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
428
449
  # for Excel2007: for option :if_unsaved => :alert and user cancels: this error appears?
429
450
  # if yes: distinguish these events
430
- raise UnexpectedREOError, "cannot open workbook: #{msg.message} #{msg.backtrace}"
451
+ want_change_readonly = !options[:read_only].nil? && (options[:read_only] != @ole_workbook.ReadOnly)
452
+ explore_workbook_error(msg, want_change_readonly)
431
453
  end
432
454
  begin
433
455
  # workaround for bug in Excel 2010: workbook.Open does not always return the workbook when given file name
@@ -522,7 +544,14 @@ module RobustExcelOle
522
544
  private
523
545
 
524
546
  def close_workbook
525
- @ole_workbook.Close if alive?
547
+ #@ole_workbook.Close if alive?
548
+ if alive?
549
+ begin
550
+ @ole_workbook.Close
551
+ rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
552
+ explore_workbook_error(msg)
553
+ end
554
+ end
526
555
  @ole_workbook = nil unless alive?
527
556
  end
528
557
 
@@ -557,11 +586,13 @@ module RobustExcelOle
557
586
  # allows to read or modify a workbook such that its state remains unchanged
558
587
  # state comprises: open, saved, writable, visible, calculation mode, check compatibility
559
588
  # @param [String] file_or_workbook a file name or WIN32OLE workbook
560
- # @param [Hash] opts the options
561
- # @option opts [Variant] :if_closed :current (default), :new or an Excel instance
562
- # @option opts [Boolean] :read_only true/false (default), open the workbook in read-only/read-write modus (save changes)
563
- # @option opts [Boolean] :writable true (default)/false changes of the workbook shall be saved/not saved
589
+ # @param [Hash] opts the options
590
+ # @option opts [Boolean] :read_only true/false (default), force to open the workbook in read-only/read-write mode
591
+ # @option opts [Boolean] :writable true (default)/false changes of the workbook shall be saved/not saved,
592
+ # and the workbook is being opened in read-only/read-write mode by default
593
+ # (when the workbook was not open before)
564
594
  # @option opts [Boolean] :keep_open whether the workbook shall be kept open after unobtrusively opening (default: false)
595
+ # @option opts [Variant] :if_closed :current (default), :new or an Excel instance
565
596
  # @return [Workbook] a workbook
566
597
  def self.unobtrusively(file_or_workbook, opts = { }, &block)
567
598
  file = (file_or_workbook.is_a? WIN32OLE) ? file_or_workbook.Fullname.tr('\\','/') : file_or_workbook
@@ -591,20 +622,24 @@ module RobustExcelOle
591
622
  end
592
623
  open_opts = excel_opts.merge({:if_unsaved => :accept})
593
624
  begin
594
- open_opts[:was_open] = nil
625
+ open_opts[:was_open] = nil
595
626
  book = open(file, open_opts)
627
+ #book = open(file, :read_only => !opts[:writable]) if !opts[:writable].nil? && !open_opts[:was_open]
596
628
  was_visible = book.visible
597
629
  was_writable = book.writable
598
630
  was_saved = book.saved
599
631
  was_check_compatibility = book.check_compatibility
600
632
  was_calculation = book.excel.properties[:calculation]
633
+ #opts[:read_only] = !opts[:writable] unless (opts[:writable].nil? || open_opts[:was_open])
634
+ opts[:read_only] = !opts[:writable] unless (!opts[:read_only].nil? || opts[:writable].nil? || open_opts[:was_open])
601
635
  book.send :apply_options, file, opts
602
636
  yield book
603
637
  ensure
604
638
  if book && book.alive?
605
639
  do_not_write = opts[:read_only] || opts[:writable]==false
606
640
  book.save unless book.saved || do_not_write || !book.writable
607
- if (opts[:read_only] && was_writable) || (!opts[:read_only] && !was_writable)
641
+ #if opts[:writable].nil? && opts[:was_open] &&
642
+ if ((opts[:read_only] && was_writable) || (!opts[:read_only] && !was_writable))
608
643
  book.send :apply_options, file, opts.merge({:read_only => !was_writable,
609
644
  :if_unsaved => (opts[:writable]==false ? :forget : :save)})
610
645
  end
@@ -904,14 +939,14 @@ module RobustExcelOle
904
939
  # @param [String] name the name of a range
905
940
  # @returns [Variant] the value of the range
906
941
  def [] name
907
- namevalue_glob(name)
942
+ namevalue_global(name)
908
943
  end
909
944
 
910
945
  # sets the value of a range
911
946
  # @param [String] name the name of the range
912
947
  # @param [Variant] value the contents of the range
913
948
  def []= (name, value)
914
- set_namevalue_glob(name, value)
949
+ set_namevalue_global(name, value)
915
950
  end
916
951
 
917
952
  # sets options
@@ -1026,11 +1061,14 @@ module RobustExcelOle
1026
1061
  '#<Workbook: ' + ('not alive ' unless alive?).to_s + (File.basename(self.filename) if alive?).to_s + " #{@excel}" + '>'
1027
1062
  end
1028
1063
 
1064
+ using ParentRefinement
1065
+ using StringRefinement
1066
+
1029
1067
  # @private
1030
1068
  def self.excel_class
1031
1069
  @excel_class ||= begin
1032
1070
  module_name = self.parent_name
1033
- "#{module_name}::Excel".constantize
1071
+ "#{module_name}::Excel".constantize
1034
1072
  rescue NameError => e
1035
1073
  # trace "excel_class: NameError: #{e}"
1036
1074
  Excel
@@ -1041,7 +1079,7 @@ module RobustExcelOle
1041
1079
  def self.worksheet_class
1042
1080
  @worksheet_class ||= begin
1043
1081
  module_name = self.parent_name
1044
- "#{module_name}::Worksheet".constantize
1082
+ "#{module_name}::Worksheet".constantize
1045
1083
  rescue NameError => e
1046
1084
  Worksheet
1047
1085
  end
@@ -1094,6 +1132,10 @@ public
1094
1132
  class WorkbookNotSaved < WorkbookREOError
1095
1133
  end
1096
1134
 
1135
+ # @private
1136
+ class WorkbookLinked < WorkbookREOError
1137
+ end
1138
+
1097
1139
  # @private
1098
1140
  class WorkbookReadOnly < WorkbookREOError
1099
1141
  end