robust_excel_ole 1.30 → 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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog +5 -0
  3. data/README.rdoc +4 -2
  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/docs/README_sheet.rdoc +1 -7
  8. data/examples/introductory_examples/example_open.rb +11 -0
  9. data/lib/robust_excel_ole.rb +18 -14
  10. data/lib/robust_excel_ole/cell.rb +1 -1
  11. data/lib/robust_excel_ole/cygwin.rb +2 -0
  12. data/lib/robust_excel_ole/general.rb +30 -80
  13. data/lib/robust_excel_ole/list_object.rb +17 -118
  14. data/lib/robust_excel_ole/list_row.rb +128 -0
  15. data/lib/robust_excel_ole/version.rb +1 -1
  16. data/lib/robust_excel_ole/workbook.rb +47 -14
  17. data/lib/robust_excel_ole/worksheet.rb +35 -18
  18. data/lib/spec_helper.rb +1 -1
  19. data/spec/address_tool_spec.rb +2 -2
  20. data/spec/base_spec.rb +2 -2
  21. data/spec/bookstore_spec.rb +1 -1
  22. data/spec/cell_spec.rb +1 -1
  23. data/spec/cygwin_spec.rb +1 -1
  24. data/spec/data/more_data/workbook.xls +0 -0
  25. data/spec/excel_spec.rb +1 -1
  26. data/spec/general_spec.rb +34 -7
  27. data/spec/list_object_spec.rb +85 -20
  28. data/spec/range_spec.rb +1 -14
  29. data/spec/spec_helper.rb +1 -1
  30. data/spec/workbook_spec.rb +1 -1
  31. data/spec/workbook_specs/workbook_all_spec.rb +8 -28
  32. data/spec/workbook_specs/workbook_close_spec.rb +1 -1
  33. data/spec/workbook_specs/workbook_misc_spec.rb +3 -3
  34. data/spec/workbook_specs/workbook_open_spec.rb +45 -5
  35. data/spec/workbook_specs/workbook_save_spec.rb +1 -1
  36. data/spec/workbook_specs/workbook_sheet_spec.rb +1 -1
  37. data/spec/workbook_specs/workbook_subclass_spec.rb +1 -1
  38. data/spec/workbook_specs/workbook_unobtr_spec.rb +40 -62
  39. data/spec/worksheet_spec.rb +33 -1
  40. metadata +3 -2
@@ -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
@@ -1,3 +1,3 @@
1
1
  module RobustExcelOle
2
- VERSION = "1.30"
2
+ VERSION = "1.31"
3
3
  end
@@ -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)
@@ -236,7 +236,7 @@ module RobustExcelOle
236
236
  end
237
237
 
238
238
  # @private
239
- def ensure_workbook(filename, options)
239
+ def ensure_workbook(filename, options)
240
240
  set_was_open options, true
241
241
  return if (@ole_workbook && alive? && (options[:read_only].nil? || @ole_workbook.ReadOnly == options[:read_only]))
242
242
  set_was_open options, false
@@ -253,9 +253,7 @@ module RobustExcelOle
253
253
  if @ole_workbook && alive?
254
254
  set_was_open options, true
255
255
  manage_blocking_or_unsaved_workbook(filename,options)
256
- if @ole_workbook.ReadOnly != options[:read_only]
257
- end
258
- 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
259
257
  else
260
258
  if (excel_option.nil? || excel_option == :current) &&
261
259
  !(::CONNECT_JRUBY_BUG && filename[0] == '/')
@@ -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
 
@@ -1103,6 +1132,10 @@ public
1103
1132
  class WorkbookNotSaved < WorkbookREOError
1104
1133
  end
1105
1134
 
1135
+ # @private
1136
+ class WorkbookLinked < WorkbookREOError
1137
+ end
1138
+
1106
1139
  # @private
1107
1140
  class WorkbookReadOnly < WorkbookREOError
1108
1141
  end
@@ -11,6 +11,8 @@ module RobustExcelOle
11
11
  # worksheet: see https://github.com/Thomas008/robust_excel_ole/blob/master/lib/robust_excel_ole/worksheet.rb
12
12
  class Worksheet < RangeOwners
13
13
 
14
+ using ToReoRefinement
15
+
14
16
  attr_reader :ole_worksheet
15
17
  attr_reader :workbook
16
18
 
@@ -194,10 +196,12 @@ module RobustExcelOle
194
196
  raise RangeNotEvaluatable, "cannot assign value #{value.inspect} to cell (#{y.inspect},#{x.inspect})"
195
197
  end
196
198
 
199
+ # provides a 2-dimensional array that contains the values in each row
197
200
  def values
198
201
  @ole_worksheet.UsedRange.Value
199
202
  end
200
203
 
204
+ # enumerator for accessing cells
201
205
  def each
202
206
  each_row do |row_range|
203
207
  row_range.each do |cell|
@@ -216,24 +220,7 @@ module RobustExcelOle
216
220
  end
217
221
  end
218
222
 
219
- def each_rowvalue
220
- @ole_worksheet.UsedRange.Value.each do |row_values|
221
- yield row_values
222
- end
223
- end
224
-
225
- def each_value # :deprecated: #
226
- each_rowvalue
227
- end
228
-
229
- def each_rowvalue_with_index(offset = 0)
230
- i = offset
231
- @ole_worksheet.UsedRange.Value.each do |row_values|
232
- yield row_values, i
233
- i += 1
234
- end
235
- end
236
-
223
+ # enumerator for accessing rows
237
224
  def each_row(offset = 0)
238
225
  offset += 1
239
226
  1.upto(@end_row) do |row|
@@ -248,6 +235,7 @@ module RobustExcelOle
248
235
  end
249
236
  end
250
237
 
238
+ # enumerator for accessing columns
251
239
  def each_column(offset = 0)
252
240
  offset += 1
253
241
  1.upto(@end_column) do |column|
@@ -262,6 +250,24 @@ module RobustExcelOle
262
250
  end
263
251
  end
264
252
 
253
+ def each_rowvalue # :deprecated: #
254
+ values.each do |row_values|
255
+ yield row_values
256
+ end
257
+ end
258
+
259
+ def each_value # :deprecated: #
260
+ each_rowvalue
261
+ end
262
+
263
+ def each_rowvalue_with_index(offset = 0) # :deprecated: #
264
+ i = offset
265
+ values.each do |row_values|
266
+ yield row_values, i
267
+ i += 1
268
+ end
269
+ end
270
+
265
271
  def row_range(row, integer_range = nil)
266
272
  integer_range ||= 1..@end_column
267
273
  RobustExcelOle::Range.new(@ole_worksheet.Range(@ole_worksheet.Cells(row, integer_range.min), @ole_worksheet.Cells(row, integer_range.max)), self)
@@ -303,6 +309,17 @@ module RobustExcelOle
303
309
  range
304
310
  end
305
311
 
312
+ # @params [Variant] table (listobject) name or number
313
+ # @return [ListObject] a table (listobject)
314
+ def table(number_or_name)
315
+ begin
316
+ ole_listobject = @ole_worksheet.ListObjects.Item(number_or_name)
317
+ rescue
318
+ raise WorksheetREOError, "table #{number_or_name} not found"
319
+ end
320
+ ListObject.new(ole_listobject)
321
+ end
322
+
306
323
  # @private
307
324
  # returns true, if the worksheet object responds to VBA methods, false otherwise
308
325
  def alive?
@@ -2,7 +2,7 @@
2
2
  require 'rspec'
3
3
  require 'tmpdir'
4
4
  require 'fileutils'
5
- require File.join(File.dirname(__FILE__), '../lib/robust_excel_ole')
5
+ require_relative '../lib/robust_excel_ole'
6
6
 
7
7
  # @private
8
8
  module RobustExcelOle::SpecHelpers
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- require File.join(File.dirname(__FILE__), './spec_helper')
4
- require File.expand_path( '../../lib/robust_excel_ole/base', __FILE__)
3
+ require_relative 'spec_helper'
4
+ require_relative '../lib/robust_excel_ole/base'
5
5
 
6
6
  $VERBOSE = nil
7
7
 
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- require File.join(File.dirname(__FILE__), './spec_helper')
4
- require File.expand_path( '../../lib/robust_excel_ole/base', __FILE__)
3
+ require_relative 'spec_helper'
4
+ require_relative '../lib/robust_excel_ole/base'
5
5
 
6
6
  $VERBOSE = nil
7
7
 
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- require File.join(File.dirname(__FILE__), './spec_helper')
3
+ require_relative 'spec_helper'
4
4
 
5
5
  =begin
6
6
  RSpec.configure do |config|
@@ -1,5 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
- require File.join(File.dirname(__FILE__), './spec_helper')
2
+ require_relative 'spec_helper'
3
3
 
4
4
  include RobustExcelOle
5
5
  include General
@@ -1,5 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
- require File.join(File.dirname(__FILE__), './spec_helper')
2
+ require_relative 'spec_helper'
3
3
 
4
4
  describe "on cygwin", :if => RUBY_PLATFORM =~ /cygwin/ do
5
5
  describe ".cygpath" do
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- require File.join(File.dirname(__FILE__), './spec_helper')
3
+ require_relative 'spec_helper'
4
4
 
5
5
  $VERBOSE = nil
6
6
 
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- require File.join(File.dirname(__FILE__), './spec_helper')
3
+ require_relative 'spec_helper'
4
4
 
5
5
  $VERBOSE = nil
6
6
 
@@ -34,6 +34,8 @@ module RobustExcelOle
34
34
  @simple_file_xlsx = @dir + '/workbook.xlsx'
35
35
  @network_path = "N:/data/workbook.xls"
36
36
  @hostname_share_path = "//DESKTOP-A3C5CJ6/spec/data/workbook.xls"
37
+ @network_path_downcase = "n:/data/workbook.xls"
38
+ @hostname_share_path_downcase = "//desktop-a3c5cj6/spec/data/workbook.xls"
37
39
  @simple_file_extern = "D:/data/workbook.xls"
38
40
  @hostname_share_path = "//DESKTOP-A3C5CJ6/spec/data/workbook.xls"
39
41
  end
@@ -43,6 +45,23 @@ module RobustExcelOle
43
45
  rm_tmp(@dir)
44
46
  end
45
47
 
48
+ describe "relace_umlauts, underscore" do
49
+
50
+ it "should" do
51
+ "ä".replace_umlauts.should == "ae"
52
+ end
53
+
54
+ it "should replace umlauts" do
55
+ new_word = "BeforeÄÖÜäöüßAfter".replace_umlauts
56
+ new_word.should == "BeforeAeOeUeaeoeuessAfter"
57
+ end
58
+
59
+ it "should underscore" do
60
+ "BeforeAfter".underscore.should == "before_after"
61
+ end
62
+
63
+ end
64
+
46
65
  describe "to_reo" do
47
66
 
48
67
  before do
@@ -269,6 +288,8 @@ module RobustExcelOle
269
288
  General.canonize(@network_path).should == @network_path
270
289
  General.canonize(@simple_file).should == @simple_file
271
290
  General.canonize(@simple_file_extern).should == @simple_file_extern
291
+ General.canonize(@hostname_share_path_downcase).should == @network_path
292
+ General.canonize(@network_path_downcase).should == @network_path_downcase
272
293
  end
273
294
 
274
295
  end
@@ -279,12 +300,18 @@ module RobustExcelOle
279
300
  it "should create a path" do
280
301
  path1 = "this" / "is" / "a" / "path"
281
302
  path1.should == "this/is/a/path"
282
- path2 = "this" / "is" / "a" / "path" /
283
- #path2.should == "this/is/a/path/"
284
- path3 = "this" /
285
- #path3.should == "this/"
286
- path4 = "this" / nil
287
- path4.should == "this"
303
+ path2 = "this" / nil
304
+ path2.should == "this"
305
+ path3 = "N:/E2" / "C:/gim/E2/workbook.xls"
306
+ path3.should == "C:/gim/E2/workbook.xls"
307
+ path4 = "N:/E2/" / "/gim/E2/workbook.xls"
308
+ path4.should == "/gim/E2/workbook.xls"
309
+ path5 = "N:/E2/" / "gim/E2/workbook.xls"
310
+ path5.should == "N:/E2/gim/E2/workbook.xls"
311
+ path6 = "N:/E2" / "spec/data/workbook.xls"
312
+ path6.should == "N:/E2/spec/data/workbook.xls"
313
+ path7 = "N:/E2" / "c:/gim/E2/workbook.xls"
314
+ path7.should == "c:/gim/E2/workbook.xls"
288
315
  end
289
316
  end
290
317