robust_excel_ole 1.27 → 1.32

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog +36 -2
  3. data/README.rdoc +121 -19
  4. data/___dummy_workbook.xls +0 -0
  5. data/benchmarking/creek_example.rb +1 -1
  6. data/benchmarking/reo_example.rb +1 -1
  7. data/benchmarking/reo_example1.rb +1 -1
  8. data/benchmarking/reo_example2.rb +1 -1
  9. data/benchmarking/roo_example.rb +1 -1
  10. data/benchmarking/simple_xlsx_reader_example.rb +1 -1
  11. data/benchmarking/spreadsheet_example.rb +1 -1
  12. data/bin/jreo +19 -0
  13. data/bin/reo +19 -0
  14. data/docs/README_excel.rdoc +16 -24
  15. data/docs/README_listobjects.rdoc +176 -0
  16. data/docs/README_open.rdoc +20 -16
  17. data/docs/README_ranges.rdoc +72 -55
  18. data/docs/README_save_close.rdoc +3 -3
  19. data/docs/README_sheet.rdoc +19 -20
  20. data/examples/example_ruby_library.rb +2 -2
  21. data/examples/introductory_examples/example_open.rb +11 -0
  22. data/examples/introductory_examples/example_range.rb +2 -2
  23. data/examples/modifying_sheets/example_access_sheets_and_cells.rb +6 -6
  24. data/examples/modifying_sheets/example_add_names.rb +1 -1
  25. data/examples/modifying_sheets/example_concating.rb +1 -1
  26. data/examples/modifying_sheets/example_copying.rb +2 -2
  27. data/examples/modifying_sheets/example_listobjects.rb +86 -0
  28. data/examples/modifying_sheets/example_naming.rb +1 -1
  29. data/examples/modifying_sheets/example_ranges.rb +1 -1
  30. data/examples/open_save_close/example_control_to_excel.rb +1 -1
  31. data/examples/open_save_close/example_if_obstructed_closeifsaved.rb +1 -1
  32. data/examples/open_save_close/example_if_obstructed_save.rb +3 -3
  33. data/examples/open_save_close/example_if_unsaved_accept.rb +1 -1
  34. data/examples/open_save_close/example_if_unsaved_forget.rb +3 -3
  35. data/examples/open_save_close/example_if_unsaved_forget_more.rb +4 -4
  36. data/examples/open_save_close/example_read_only.rb +1 -1
  37. data/examples/open_save_close/example_simple.rb +1 -1
  38. data/examples/open_save_close/example_unobtrusively.rb +3 -3
  39. data/lib/robust_excel_ole.rb +19 -16
  40. data/lib/robust_excel_ole/address_tool.rb +54 -44
  41. data/lib/robust_excel_ole/base.rb +9 -6
  42. data/lib/robust_excel_ole/bookstore.rb +3 -17
  43. data/lib/robust_excel_ole/cell.rb +17 -22
  44. data/lib/robust_excel_ole/cygwin.rb +2 -0
  45. data/lib/robust_excel_ole/excel.rb +136 -201
  46. data/lib/robust_excel_ole/general.rb +249 -238
  47. data/lib/robust_excel_ole/list_object.rb +186 -210
  48. data/lib/robust_excel_ole/list_row.rb +155 -0
  49. data/lib/robust_excel_ole/range.rb +130 -94
  50. data/lib/robust_excel_ole/range_owners.rb +54 -135
  51. data/lib/robust_excel_ole/version.rb +1 -1
  52. data/lib/robust_excel_ole/workbook.rb +230 -196
  53. data/lib/robust_excel_ole/worksheet.rb +254 -133
  54. data/lib/spec_helper.rb +1 -1
  55. data/robust_excel_ole.gemspec +4 -3
  56. data/spec/address_tool_spec.rb +2 -2
  57. data/spec/base_spec.rb +19 -17
  58. data/spec/bookstore_spec.rb +3 -4
  59. data/spec/cell_spec.rb +10 -10
  60. data/spec/cygwin_spec.rb +1 -1
  61. data/spec/data/more_data/workbook.xls +0 -0
  62. data/spec/excel_spec.rb +133 -86
  63. data/spec/general_spec.rb +79 -18
  64. data/spec/list_object_spec.rb +259 -81
  65. data/spec/list_row_spec.rb +218 -0
  66. data/spec/range_spec.rb +75 -41
  67. data/spec/spec_helper.rb +16 -2
  68. data/spec/workbook_spec.rb +87 -46
  69. data/spec/workbook_specs/workbook_all_spec.rb +9 -28
  70. data/spec/workbook_specs/workbook_close_spec.rb +1 -1
  71. data/spec/workbook_specs/workbook_misc_spec.rb +52 -45
  72. data/spec/workbook_specs/workbook_open_spec.rb +103 -50
  73. data/spec/workbook_specs/workbook_save_spec.rb +22 -23
  74. data/spec/workbook_specs/workbook_sheet_spec.rb +4 -4
  75. data/spec/workbook_specs/workbook_subclass_spec.rb +1 -1
  76. data/spec/workbook_specs/workbook_unobtr_spec.rb +553 -395
  77. data/spec/worksheet_spec.rb +544 -308
  78. metadata +38 -3
  79. data/lib/reo_console.rb +0 -42
@@ -1,7 +1,213 @@
1
1
  # -*- coding: utf-8 -*-
2
+ require 'pathname'
3
+
4
+ module ToReoRefinement
5
+
6
+ refine WIN32OLE do
7
+
8
+ # type-lifting WIN32OLE objects to RobustExcelOle objects
9
+ def to_reo
10
+ General.main_classes_ole_types_and_recognising_methods.each do |classname, ole_type, methods|
11
+ if !::OLETYPE_JRUBY_BUG
12
+ if self.ole_type.name == ole_type
13
+ if classname != RobustExcelOle::Range
14
+ return classname.new(self)
15
+ elsif self.Rows.Count == 1 && self.Columns.Count == 1
16
+ return RobustExcelOle::Cell.new(self, self.Parent)
17
+ else
18
+ return RobustExcelOle::Range.new(self, self.Parent)
19
+ end
20
+ end
21
+ else
22
+ begin
23
+ recognising_method, no_method = methods
24
+ self.send(recognising_method)
25
+ unless no_method.nil?
26
+ begin
27
+ self.send(no_method[:no_method])
28
+ next
29
+ rescue NoMethodError
30
+ return classname.new(self)
31
+ end
32
+ end
33
+ if classname != RobustExcelOle::Range
34
+ return classname.new(self)
35
+ elsif self.Rows.Count == 1 && self.Columns.Count == 1
36
+ return RobustExcelOle::Cell.new(self, self.Parent)
37
+ else
38
+ return RobustExcelOle::Range.new(self, self.Parent)
39
+ end
40
+ rescue Java::OrgRacobCom::ComFailException => msg # NoMethodError
41
+ #if $!.message =~ /undefined method/ &&
42
+ # main_classes_ole_types_and_recognising_methods.any?{ |_c, _o, recognising_method| $!.message.include?(recognising_method.to_s) }
43
+ next
44
+ #end
45
+ end
46
+ end
47
+ end
48
+ raise RobustExcelOle::TypeREOError, "given object cannot be type-lifted to a RobustExcelOle object"
49
+ end
50
+
51
+ end
52
+ end
53
+
54
+ # @private
55
+ class WIN32OLE
56
+
57
+ include Enumerable
58
+
59
+ end
60
+
61
+ # @private
62
+ module FindAllIndicesRefinement
63
+
64
+ refine Array do
65
+
66
+ def find_all_indices elem
67
+ elem = elem.encode('utf-8') if elem.respond_to?(:gsub)
68
+ found, index, result = -1, -1, []
69
+ while found
70
+ found = self[index+1..-1].index(elem)
71
+ if found
72
+ index = index + found + 1
73
+ result << index
74
+ end
75
+ end
76
+ result
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+
83
+ TRANSLATION_TABLE = {
84
+ 'ä' => 'ae', 'ö' => 'oe', 'ü' => 'ue', 'Ä' => 'Ae', 'Ö' => 'Oe', 'Ü' => 'Ue',
85
+ 'ß' => 'ss', '²' => '2', '³' => '3'
86
+ }
87
+
88
+ # @private
89
+ module StringRefinement
90
+
91
+ refine String do
92
+
93
+ def / path_part
94
+ return path_part if empty?
95
+ return self if path_part.nil? || path_part.empty?
96
+ begin
97
+ path_part = path_part.strip
98
+ (path_part =~ /^(\/|([A-Z]:\/))/i) ? path_part : (chomp('/') + '/' + path_part)
99
+ rescue TypeError
100
+ raise TypeError, "Only strings can be parts of paths (given: #{path_part.inspect} of class #{path_part.class})"
101
+ end
102
+ end
103
+
104
+ def replace_umlauts
105
+ TRANSLATION_TABLE.inject(encode('utf-8')) { |word,(umlaut, replacement)| word.gsub(umlaut, replacement) }
106
+ end
107
+
108
+
109
+ # taken from http://apidock.com/rails/ActiveSupport/Inflector/underscore
110
+ def underscore
111
+ word = gsub('::', '/')
112
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
113
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
114
+ word.tr!('-', '_')
115
+ word.downcase!
116
+ word
117
+ end
118
+
119
+ # taken from http://apidock.com/rails/ActiveSupport/Inflector/constantize
120
+ # File activesupport/lib/active_support/inflector/methods.rb, line 226
121
+ def constantize # (camel_cased_word)
122
+ names = split('::')
123
+
124
+ # Trigger a builtin NameError exception including the ill-formed constant in the message.
125
+ Object.const_get(self) if names.empty?
126
+
127
+ # Remove the first blank element in case of '::ClassName' notation.
128
+ names.shift if names.size > 1 && names.first.empty?
129
+
130
+ names.inject(Object) do |constant, name|
131
+ if constant == Object
132
+ constant.const_get(name)
133
+ else
134
+ candidate = constant.const_get(name)
135
+ next candidate if constant.const_defined?(name)
136
+ next candidate unless Object.const_defined?(name)
137
+
138
+ # Go down the ancestors to check it it's owned
139
+ # directly before we reach Object or the end of ancestors.
140
+ constant = constant.ancestors.inject do |const, ancestor|
141
+ break const if ancestor == Object
142
+ break ancestor if ancestor.const_defined?(name)
143
+
144
+ const
145
+ end
146
+
147
+ # owner is in Object, so raise
148
+ constant.const_get(name)
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+
155
+ # @private
156
+ module ParentRefinement
157
+
158
+ using StringRefinement
159
+
160
+ # taken from http://api.rubyonrails.org/v2.3.8/classes/ActiveSupport/CoreExtensions/Module.html#M000806
161
+ refine Module do
162
+
163
+ def parent_name
164
+ unless defined? @parent_name
165
+ @parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil
166
+ end
167
+ @parent_name
168
+ end
169
+
170
+ def parent
171
+ parent_name ? parent_name.constantize : Object
172
+ end
173
+ end
174
+ end
175
+
176
+ class Integer
177
+
178
+ alias old_spaceship <=>
179
+
180
+ def <=> other
181
+ if other.is_a? Array
182
+ self <=> other.first
183
+ else
184
+ old_spaceship other
185
+ end
186
+ end
187
+
188
+ end
189
+
190
+ # @private
191
+ class Array
192
+
193
+ alias old_spaceship <=>
194
+
195
+ def <=> other
196
+ # p other
197
+ if other.is_a? Integer
198
+ self <=> [other]
199
+ else
200
+ old_spaceship other
201
+ end
202
+ end
203
+
204
+ end
205
+
2
206
 
3
207
  module General
4
208
 
209
+ using ToReoRefinement
210
+
5
211
  IS_JRUBY_PLATFORM = (RUBY_PLATFORM =~ /java/)
6
212
  ::EXPANDPATH_JRUBY_BUG = IS_JRUBY_PLATFORM && true
7
213
  ::CONNECT_JRUBY_BUG = IS_JRUBY_PLATFORM && true
@@ -9,37 +215,30 @@ module General
9
215
  ::ERRORMESSAGE_JRUBY_BUG = IS_JRUBY_PLATFORM && true
10
216
  ::CONNECT_EXCEL_JRUBY_BUG = IS_JRUBY_PLATFORM && true
11
217
  ::RANGES_JRUBY_BUG = IS_JRUBY_PLATFORM && true
218
+ ::OLETYPE_JRUBY_BUG = IS_JRUBY_PLATFORM && true
12
219
 
13
220
  # @private
14
221
  NetworkDrive = Struct.new(:drive_letter, :network_name) do
15
222
 
16
- def self.get_all(drives)
17
- ndrives = []
223
+ def self.get_all_drives
224
+ network = WIN32OLE.new('WScript.Network')
225
+ drives = network.enumnetworkdrives
18
226
  count = drives.Count
19
- (0..(count - 1)).step(2) do |i|
20
- ndrives << NetworkDrive.new( drives.Item(i), drives.Item(i + 1).tr('\\','/'))
21
- end
22
- ndrives
227
+ (0..(count - 1)).step(2).map{ |i| NetworkDrive.new( drives.Item(i), drives.Item(i + 1).tr('\\','/')) }
23
228
  end
24
-
25
229
  end
26
230
 
27
231
  # @private
28
232
  def hostnameshare2networkpath(filename)
29
233
  return filename unless filename[0,2] == "//"
30
- network = WIN32OLE.new('WScript.Network')
31
- drives = network.enumnetworkdrives
32
- network_drives = NetworkDrive.get_all(drives)
33
- f_c = filename.dup
34
- network_drive = network_drives.find do |d|
35
- e = f_c.sub!(d.network_name,d.drive_letter)
36
- return e if e
234
+ NetworkDrive.get_all_drives.inject(filename) do |fn_modified, d|
235
+ fn_modified.sub(/#{(Regexp.escape(d.network_name))}/i,d.drive_letter)
37
236
  end
38
- filename
39
237
  end
40
238
 
41
239
  # @private
42
- def absolute_path(file)
240
+ def absolute_path(file)
241
+ file = file.to_path if file.respond_to?(:to_path)
43
242
  return file if file[0,2] == "//"
44
243
  file[0,2] = './' if ::EXPANDPATH_JRUBY_BUG && file =~ /[A-Z]:[^\/]/
45
244
  file = File.expand_path(file)
@@ -64,40 +263,57 @@ module General
64
263
  path
65
264
  end
66
265
 
266
+ # @private
67
267
  def change_current_binding(current_object)
68
268
  Pry.change_current_binding(current_object)
69
269
  end
70
270
 
71
- def class2method
72
- [{Excel => :Hwnd},
73
- {Workbook => :FullName},
74
- {Worksheet => :UsedRange},
75
- {RobustExcelOle::Range => :Row},
76
- {ListObject => :ListRows}]
271
+ # @private
272
+ def main_classes_ole_types_and_recognising_methods
273
+ [[RobustExcelOle::Range , 'Range' , :Row],
274
+ [RobustExcelOle::Worksheet , '_Worksheet' , :UsedRange],
275
+ [RobustExcelOle::Workbook , '_Workbook' , :FullName],
276
+ [RobustExcelOle::Excel , '_Application', :Hwnd],
277
+ [RobustExcelOle::ListObject, 'ListObject' , :ListRows],
278
+ [RobustExcelOle::ListRow , 'ListRow' , [:Creator, :no_method => :Row]]]
77
279
  end
78
280
 
79
-
281
+ # @private
80
282
  # enable RobustExcelOle methods to Win32Ole objects
81
- def uplift_to_reo
82
- exclude_list = [:each, :inspect]
83
- class2method.each do |element|
84
- classname = element.first.first
85
- method = element.first.last
86
- classname.instance_methods(false).each do |inst_method|
87
- if !exclude_list.include?(inst_method)
283
+ def init_reo_for_win32ole
284
+ main_classes_ole_types_and_recognising_methods.each do |classname, _ole_type, _recognising_method|
285
+ meths = (classname.instance_methods(false) - WIN32OLE.instance_methods(false) - Object.methods - Enumerable.instance_methods(false) - [:Calculation=])
286
+ meths.each do |inst_method|
287
+ if classname.method_defined?(inst_method)
88
288
  WIN32OLE.send(:define_method, inst_method) do |*args, &blk|
89
- self.to_reo.send(inst_method, *args, &blk)
289
+ begin
290
+ obj = to_reo
291
+ rescue
292
+ return self.send(inst_method.capitalize, *args, &blk)
293
+ end
294
+ obj.send(inst_method, *args, &blk)
295
+ end
296
+ else
297
+ WIN32OLE.send(:define_method, inst_method) do |*args, &blk|
298
+ begin
299
+ obj = classname.constantize.new(self)
300
+ rescue
301
+ return self.send(inst_method.capitalize, *args, &blk)
302
+ end
303
+ obj.send(inst_method, *args, &blk)
90
304
  end
91
305
  end
92
306
  end
93
307
  end
94
- nil
95
308
  end
96
309
 
97
- module_function :absolute_path, :canonize, :normalize, :change_current_binding, :class2method, :uplift_to_reo
310
+ module_function :absolute_path, :canonize, :normalize, :change_current_binding,
311
+ :main_classes_ole_types_and_recognising_methods,
312
+ :init_reo_for_win32ole, :hostnameshare2networkpath, :test
98
313
 
99
314
  end
100
315
 
316
+
101
317
  # @private
102
318
  class Pry
103
319
 
@@ -159,209 +375,6 @@ class Pry
159
375
  end
160
376
  end
161
377
 
162
- # @private
163
- class Integer
164
-
165
- alias old_spaceship <=>
166
-
167
- def <=> other
168
- # p other
169
- if other.is_a? Array
170
- self <=> other.first
171
- else
172
- old_spaceship other
173
- end
174
- end
175
-
176
- end
177
-
178
- # @private
179
- class Array
180
-
181
- alias old_spaceship <=>
182
-
183
- def <=> other
184
- # p other
185
- if other.is_a? Integer
186
- self <=> [other]
187
- else
188
- old_spaceship other
189
- end
190
- end
191
-
192
- def find_each_index find
193
- found, index, q = -1, -1, []
194
- while found
195
- found = self[index+1..-1].index(find)
196
- if found
197
- index = index + found + 1
198
- q << index
199
- end
200
- end
201
- q
202
- end
203
- end
204
-
205
- # @private
206
- class WIN32OLE
207
-
208
- include Enumerable
209
-
210
- # type-lifting WIN32OLE objects to RobustExcelOle objects
211
- def to_reo
212
- General.class2method.each do |element|
213
- classname = element.first.first
214
- method = element.first.last
215
- begin
216
- self.send(method)
217
- if classname == RobustExcelOle::Range && self.Rows.Count == 1 && self.Columns.Count == 1
218
- return Cell.new(self, self.Parent)
219
- else
220
- return classname.new(self)
221
- end
222
- rescue
223
- next
224
- end
225
- end
226
- raise TypeREOError, "given object cannot be type-lifted to a RobustExcelOle object"
227
- end
228
-
229
- =begin
230
- def to_reo
231
- case ole_type.name
232
- when 'Range' then RobustExcelOle::Range.new(self)
233
- when '_Worksheet' then RobustExcelOle::Worksheet.new(self)
234
- when '_Workbook' then RobustExcelOle::Workbook.new(self)
235
- when '_Application' then RobustExcelOle::Excel.new(self)
236
- else
237
- self
238
- end
239
- end
240
- =end
241
-
242
- =begin
243
- alias method_missing_before_implicit_typelift method_missing
244
-
245
- def method_missing(name, *args, &blk)
246
- puts "method_missing:"
247
- puts "name: #{name.inspect}"
248
- #raise NoMethodError if name.to_s == "Hwnd" or name.to_s == "FullName" or name.to_s == "UsedRange" or name.to_s == "Row" or name.to_s == "ListRows"
249
- begin
250
- reo_obj = self.to_reo
251
- puts "reo_obj: #{reo_obj.inspect}"
252
- rescue
253
- puts "$!.message: #{$!.message}"
254
- method_missing_before_implicit_typelift(name, *args, &blk)
255
- end
256
- reo_obj.send(name, *args, &blk)
257
- end
258
- =end
259
-
260
- end
261
-
262
- # @private
263
- class ::String
264
- def / path_part
265
- if empty?
266
- path_part
267
- else
268
- if path_part.nil? || path_part.empty?
269
- self
270
- else
271
- begin
272
- File.join self, path_part
273
- rescue TypeError
274
- raise TypeError, "Only strings can be parts of paths (given: #{path_part.inspect} of class #{path_part.class})"
275
- end
276
- end
277
- end
278
- end
279
-
280
- # taken from http://apidock.com/rails/ActiveSupport/Inflector/underscore
281
- def underscore
282
- word = gsub('::', '/')
283
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
284
- word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
285
- word.tr!('-', '_')
286
- word.downcase!
287
- word
288
- end
289
-
290
- def delete_multiple_underscores
291
- word = self
292
- while word.index('__') do
293
- word.gsub!('__','_')
294
- end
295
- word
296
- end
297
-
298
- def replace_umlauts
299
- word = self
300
- word.gsub!('ä','ae')
301
- word.gsub!('Ä','Ae')
302
- word.gsub!('ö','oe')
303
- word.gsub!('Ö','Oe')
304
- word.gsub!('ü','ue')
305
- word.gsub!('Ü','Ue')
306
- #word.gsub!(/\x84/,'ae')
307
- #word.gsub!(/\x8E/,'Ae')
308
- #word.gsub!(/\x94/,'oe')
309
- #word.gsub!(/\x99/,'Oe')
310
- #word.gsub!(/\x81/,'ue')
311
- #word.gsub!(/\x9A/,'Ue')
312
- word
313
- end
314
-
315
- # taken from http://apidock.com/rails/ActiveSupport/Inflector/constantize
316
- # File activesupport/lib/active_support/inflector/methods.rb, line 226
317
- def constantize # (camel_cased_word)
318
- names = split('::')
319
-
320
- # Trigger a builtin NameError exception including the ill-formed constant in the message.
321
- Object.const_get(self) if names.empty?
322
-
323
- # Remove the first blank element in case of '::ClassName' notation.
324
- names.shift if names.size > 1 && names.first.empty?
325
-
326
- names.inject(Object) do |constant, name|
327
- if constant == Object
328
- constant.const_get(name)
329
- else
330
- candidate = constant.const_get(name)
331
- next candidate if constant.const_defined?(name)
332
- next candidate unless Object.const_defined?(name)
333
-
334
- # Go down the ancestors to check it it's owned
335
- # directly before we reach Object or the end of ancestors.
336
- constant = constant.ancestors.inject do |const, ancestor|
337
- break const if ancestor == Object
338
- break ancestor if ancestor.const_defined?(name)
339
-
340
- const
341
- end
342
-
343
- # owner is in Object, so raise
344
- constant.const_get(name)
345
- end
346
- end
347
- end
348
- end
349
-
350
- # taken from http://api.rubyonrails.org/v2.3.8/classes/ActiveSupport/CoreExtensions/Module.html#M000806
351
- # @private
352
- class Module
353
- def parent_name
354
- unless defined? @parent_name
355
- @parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil
356
- end
357
- @parent_name
358
- end
359
-
360
- def parent
361
- parent_name ? parent_name.constantize : Object
362
- end
363
- end
364
-
365
378
  module MethodHelpers
366
379
 
367
380
  # @private
@@ -382,5 +395,3 @@ module MethodHelpers
382
395
  end
383
396
  end
384
397
  end
385
-
386
- REO = RobustExcelOle