robust_excel_ole 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog +12 -1
- data/README.rdoc +114 -276
- data/README_detail.rdoc +31 -14
- data/lib/robust_excel_ole/book.rb +89 -113
- data/lib/robust_excel_ole/excel.rb +38 -10
- data/lib/robust_excel_ole/sheet.rb +2 -2
- data/lib/robust_excel_ole/version.rb +1 -1
- data/spec/book_spec.rb +23 -20
- data/spec/book_specs/book_misc_spec.rb +6 -12
- data/spec/book_specs/book_open_spec.rb +22 -63
- data/spec/book_specs/book_unobtr_spec.rb +174 -231
- data/spec/data/another_workbook.xls +0 -0
- data/spec/data/different_workbook.xls +0 -0
- data/spec/data/more_data/workbook.xls +0 -0
- data/spec/data/workbook.xls +0 -0
- data/spec/excel_spec.rb +32 -5
- metadata +4 -4
data/Changelog
CHANGED
@@ -1,8 +1,19 @@
|
|
1
1
|
# Change Log
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
+
## [1.0.2] - 2017-07-26
|
4
5
|
|
5
|
-
|
6
|
+
### Added
|
7
|
+
|
8
|
+
- Excel#set_options
|
9
|
+
- Excel#retain_saved_workbooks
|
10
|
+
- Book#retain_saved
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
|
14
|
+
- Book#unobtrusively: option: :if_closed =>
|
15
|
+
|
16
|
+
## [1.0.1] - 2017-16-04
|
6
17
|
|
7
18
|
### Added
|
8
19
|
|
data/README.rdoc
CHANGED
@@ -5,37 +5,11 @@ It supports simultaneously running Excel instances and user interactions.
|
|
5
5
|
RobustExcelOle deals with various cases of Excel and user behaviour,
|
6
6
|
and implements workarounds for some Excel bugs.
|
7
7
|
The gem provides convenient methods for common tasks, and facilitates referenced libraries.
|
8
|
-
It supports Excel 2010
|
8
|
+
It supports Excel 2010.
|
9
9
|
|
10
10
|
RobustExcelOle works by sending VBA methods via Win32OLE.
|
11
11
|
Moreover, it implements a management system and keeps track of Excel files and Excel instances.
|
12
12
|
|
13
|
-
In the following, some features of RobustExcelOle are depicted.
|
14
|
-
|
15
|
-
RobustExcelOle allows a "script mode" and an "interactive mode": Commands enable to open Excel files (or workbooks) in various Excel instances, enable to close, reopen, modify and save the Excel files, without the need of the user's interaction, and even without the user noticing. While running this script, the user can open and mofify any Excel files at any time. RobustExcelOle manages the complex cases of conflicts that might occur such that the user does not need to interfere and the script can continue.
|
16
|
-
|
17
|
-
For example, suppose you want to process a list of workbooks (Excel files). RobustExcelOle allows to rapidly open, manipulate, close and save these workbooks (script mode). Now assume, the workbook "workbook.xls" is being processed, while the user has opened this workbook, has manipulated but not saved it yet. Excel would prompt a message and ask the user what to do. RobustExcelOle solves this conflict by using several options that state whether the changes of the user should be saved (accepted) or discarded before opening the workbook.
|
18
|
-
|
19
|
-
book1 = Book.open('workbook.xls') # user
|
20
|
-
...
|
21
|
-
book2 = Book.open('workbook.xls', :if_unsaved => accept) # script
|
22
|
-
|
23
|
-
Similarly, if the user has opened a workbook that has the same name but a different path, the conflict is solved via options.
|
24
|
-
|
25
|
-
book1 = Book.open('path1/workbook.xls')
|
26
|
-
...
|
27
|
-
book2 = Book.open('workbook.xls', :if_obstructed => :forget)
|
28
|
-
|
29
|
-
Another feature that RobustExcelOle povides is reopening workbooks after closing them. A workbook is opened by default in the Excel instance where it was open before most recently. If this Excel instance is damaged or closed, then RobustExcelIle controls via options whether the workbook is opened in the current (active) Excel instance, a new or a given Excel instance.
|
30
|
-
|
31
|
-
book1 = Book.open('workbook.xls', :default => {:excel => :new})
|
32
|
-
|
33
|
-
Moreover, RobustExcelOle allows unobtrusively reading and modifying workbooks, i.e. accessing workbooks without changing their "status". The status comprises whether the workbook is open in some Excel instance , saved and writable.
|
34
|
-
|
35
|
-
Book.for_modifying('workbook.xls') do |book|
|
36
|
-
...
|
37
|
-
end
|
38
|
-
|
39
13
|
== Requirements
|
40
14
|
|
41
15
|
* Ruby 1.8.6 or higher
|
@@ -49,58 +23,37 @@ Moreover, RobustExcelOle allows unobtrusively reading and modifying workbooks, i
|
|
49
23
|
require 'robust_excel_ole'
|
50
24
|
include RobustExcelOle
|
51
25
|
|
52
|
-
|
53
|
-
|
54
|
-
book = Book.open('workbook.xls')
|
55
|
-
|
56
|
-
You can also open a workbook with a block.
|
57
|
-
The semantics is similar to, e.g., +File.open+.
|
58
|
-
|
59
|
-
Book.open('workbook.xls') do |book|
|
60
|
-
# do something
|
61
|
-
end
|
62
|
-
|
63
|
-
There are some options that determine the Excel instance in which the workbook is opened, the visibility, calculation mode, and solving conflicts when the workbook is unsaved or blocked. Here are a few examples:
|
64
|
-
|
65
|
-
If you want to open a workbook that was not opened before, or reopen a workbook that was open in an Excel instance that is now closed, in the current (active) Excel instance, then use
|
66
|
-
|
67
|
-
Book.open('workbook.xls', :default => {:excel => :current})
|
68
|
-
|
69
|
-
or simply
|
70
|
-
|
71
|
-
Book.open('workbook.xls')
|
72
|
-
|
73
|
-
If you want to open a workbook in a new Excel instance, no matter if it was opened before, you can write
|
74
|
-
|
75
|
-
Bool.open('workbook.xls', :force => {:excel => :new})
|
26
|
+
== Description
|
76
27
|
|
77
|
-
|
78
|
-
|
79
|
-
book = Book.open('workbook.xls', :visible => true)
|
80
|
-
|
81
|
-
If a workbook contains unsaved changes, a workbook with the same filename shall be opened and the former one shall remain open, you apply
|
28
|
+
In the following, some features of RobustExcelOle are depicted.
|
82
29
|
|
83
|
-
|
30
|
+
RobustExcelOle allows a "script mode" and an "interactive mode": Commands enable to open Excel files (or workbooks) in various Excel instances, enable to close, reopen, modify and save the Excel files, without the need of the user's interaction, and even without the user noticing. While running this script mode, the user can open and modify any Excel files in any Excel instances at any time. RobustExcelOle manages the complex cases of conflicts that might occur such that the user does not need to interfere and the script can continue.
|
84
31
|
|
85
|
-
|
32
|
+
For example, suppose you want to process a list of workbooks (Excel files). RobustExcelOle allows to rapidly open, manipulate, close and save these workbooks (script mode). Now assume, the workbook "workbook.xls" is being processed, while the user has opened this workbook, has manipulated but not saved it yet. Excel would prompt a message and ask the user what to do. RobustExcelOle solves this conflict by using several options that state whether the changes of the user should be saved (accepted) or discarded before opening the workbook.
|
86
33
|
|
87
|
-
|
34
|
+
book1 = Book.open('workbook.xls') # user
|
35
|
+
...
|
36
|
+
book2 = Book.open('workbook.xls', :if_unsaved => accept) # script
|
88
37
|
|
89
|
-
|
38
|
+
Similarly, if the user has opened a workbook that has the same name but a different path, the conflict is solved via options.
|
90
39
|
|
91
|
-
|
40
|
+
book1 = Book.open('path1/workbook.xls')
|
41
|
+
...
|
42
|
+
book2 = Book.open('workbook.xls', :if_obstructed => :forget)
|
92
43
|
|
93
|
-
|
44
|
+
Another feature that RobustExcelOle povides is reopening workbooks after closing them. A workbook is opened by default in the Excel instance where it was open before most recently. If this Excel instance is damaged or closed, then RobustExcelIle controls via options whether the workbook is opened in the current (active) Excel instance, a new or a given Excel instance, e.g.
|
94
45
|
|
95
|
-
|
46
|
+
book1 = Book.open('workbook.xls', :default => {:excel => :new})
|
96
47
|
|
97
|
-
|
48
|
+
Moreover, RobustExcelOle allows unobtrusively reading and modifying workbooks, i.e. accessing workbooks without changing their "status". The status comprises whether the workbook is open or closed, saved or unsaved, read-only or writable, visible or invisible, calculation mode is manual or automatic, and checking compatibility is done or not done.
|
98
49
|
|
99
|
-
|
50
|
+
Book.for_modifying('workbook.xls') do |book|
|
51
|
+
...
|
52
|
+
end
|
100
53
|
|
101
|
-
|
102
|
-
|
103
|
-
|
54
|
+
Book.for_reading('workbook.xls') do |book|
|
55
|
+
...
|
56
|
+
end
|
104
57
|
|
105
58
|
=== The Book objects and transperence identity
|
106
59
|
|
@@ -108,311 +61,196 @@ An Excel file (or workbook) is represented by a Book object. A Book object is de
|
|
108
61
|
Identity transparence means that the same Book objects refer to the same Excel files, and vice versa.
|
109
62
|
In other words, a Book objects is a proxy of an Excel file.
|
110
63
|
|
111
|
-
===
|
112
|
-
|
113
|
-
A Book object can be created when giving an Excel workbook.
|
114
|
-
|
115
|
-
book = Book.new(win32ole_workbook)
|
116
|
-
|
117
|
-
=== Saving a workbook.
|
118
|
-
|
119
|
-
book.save
|
120
|
-
|
121
|
-
Saving a workbook with a file name, is done by
|
122
|
-
|
123
|
-
book.save_as('another_workbook.xls')
|
124
|
-
|
125
|
-
When you want to overwrite a workbook, then use
|
126
|
-
|
127
|
-
book.save_as('another_workbook.xls', :if_exists => :overwrite)
|
128
|
-
|
129
|
-
If a workbook blocks the workbook that should be saved, then the former one can be saved and closed before.
|
130
|
-
|
131
|
-
book.save_as('workbook.xls', :if_exists => :overwrite, :if_obstructed => :save)
|
132
|
-
|
133
|
-
=== Unobtrusively opening a workbook
|
64
|
+
=== Opening and closing workbooks
|
134
65
|
|
135
|
-
|
136
|
-
|
137
|
-
Book.unobtrusively('workbook.xls') do |book|
|
138
|
-
# some modification
|
139
|
-
end
|
140
|
-
|
141
|
-
Likewise, the methods +for_reading+ and +for_modifying+ are methods for unobtrusively reading or modifying.
|
142
|
-
|
143
|
-
=== Retaining the saved-status
|
144
|
-
|
145
|
-
This method ensures keeping the save status of the workbook
|
66
|
+
Let's have a look at an example. Suppose, we want to open a workbook.
|
146
67
|
|
147
68
|
book = Book.open('workbook.xls')
|
148
|
-
book.retain_saved do
|
149
|
-
# some reading or modifying
|
150
|
-
end
|
151
|
-
|
152
|
-
=== Checking whether the workbook is alive.
|
153
|
-
|
154
|
-
This method finds out whether the workbook that is referenced by the Book object responds to methods.
|
155
|
-
|
156
|
-
if book.alive? then sheet = book[0] end
|
157
69
|
|
158
|
-
|
70
|
+
We could do this in a block as well. The semantics is similar to, e.g., +File.open+.
|
159
71
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
=> "value"
|
164
|
-
|
165
|
-
or
|
166
|
-
|
167
|
-
book.nameval("name")
|
168
|
-
=> "value"
|
169
|
-
|
170
|
-
You can set the contents of a range with
|
171
|
-
|
172
|
-
book["name"] = "value"
|
173
|
-
|
174
|
-
or
|
175
|
-
|
176
|
-
book.set_nameval("name") = "value"
|
177
|
-
|
178
|
-
=== Bringing a workbook to the focus.
|
179
|
-
|
180
|
-
If you want to make the workbook visible and available for keyboard inputs, use
|
181
|
-
|
182
|
-
book.focus
|
183
|
-
|
184
|
-
=== Making the window of the workbook visible
|
72
|
+
Book.open('workbook.xls') do |book|
|
73
|
+
# do something with book
|
74
|
+
end
|
185
75
|
|
186
|
-
|
76
|
+
Now let's make the workbook visible.
|
187
77
|
|
188
|
-
book.visible = false
|
189
78
|
book.visible = true
|
190
79
|
|
191
|
-
|
192
|
-
|
193
|
-
You can make an Excel instance visible with
|
80
|
+
We can do this in one step as well.
|
194
81
|
|
195
|
-
book.
|
196
|
-
|
197
|
-
and enable DisplayAlerts using
|
198
|
-
|
199
|
-
book.excel.displayalerts = true
|
82
|
+
book = Book.open('workbook.xls', :visible => true)
|
200
83
|
|
84
|
+
Now we want to open another workbook in a new Excel instance.
|
201
85
|
|
202
|
-
|
86
|
+
book2 = Book.open('another_workbook.xls', :force => {:excel => :new}, :visible => true)
|
203
87
|
|
204
|
-
|
88
|
+
Then we open a third workbook in the second Excel instance.
|
205
89
|
|
206
|
-
|
90
|
+
book3 = Book.open('different_workbook.xls', :force => {:excel => book2.excel})
|
207
91
|
|
208
|
-
|
92
|
+
We close the second workbook.
|
209
93
|
|
210
|
-
|
94
|
+
book2.close
|
211
95
|
|
212
|
-
|
96
|
+
Reopening this workbook is done by
|
213
97
|
|
214
|
-
|
98
|
+
book2.reopen
|
215
99
|
|
216
|
-
|
100
|
+
=== Modifying workbooks
|
217
101
|
|
218
|
-
|
102
|
+
We can get the value of a named range.
|
219
103
|
|
220
|
-
|
104
|
+
book2["new"] # => foo
|
221
105
|
|
222
|
-
|
223
|
-
# do something with sheet
|
224
|
-
end
|
106
|
+
or
|
225
107
|
|
226
|
-
|
108
|
+
book2.nameval("new") # => "foo"
|
227
109
|
|
228
|
-
|
110
|
+
Now we assign a new value to this range.
|
229
111
|
|
230
|
-
|
231
|
-
# do something with cell
|
232
|
-
end
|
112
|
+
book2["new"] = "bar"
|
233
113
|
|
234
|
-
|
235
|
-
# do something with row
|
236
|
-
end
|
114
|
+
or
|
237
115
|
|
238
|
-
|
239
|
-
# do something with column
|
240
|
-
end
|
116
|
+
book2.set_nameval("new", "bar")
|
241
117
|
|
242
|
-
|
118
|
+
Now we access the first worksheet by
|
243
119
|
|
244
|
-
|
120
|
+
sheet1 = book2.sheet(1)
|
245
121
|
|
246
|
-
|
122
|
+
or
|
247
123
|
|
248
|
-
|
124
|
+
sheet1 = book2.sheet('Sheet1')
|
249
125
|
|
250
|
-
|
251
|
-
|
126
|
+
or
|
127
|
+
|
128
|
+
sheet1 = book2.first_sheet
|
252
129
|
|
253
|
-
|
130
|
+
We can read the first three cells of the first row
|
254
131
|
|
255
|
-
|
256
|
-
cell.Value => value of the cell.
|
257
|
-
sheet[1,1] = "new_value"
|
132
|
+
sheet1.row_range(1, 1..3).values # => ["foo","workbook","sheet1"]
|
258
133
|
|
259
|
-
|
134
|
+
and the third column
|
260
135
|
|
261
|
-
|
136
|
+
sheet1.col_range(3).values # => ["sheet1", 2.0, 4.0]
|
262
137
|
|
263
|
-
|
264
|
-
sheet.row_range(1, 1..3 ) => first three cells of the first row
|
138
|
+
Then we read first cell
|
265
139
|
|
266
|
-
|
140
|
+
sheet1[1,1].value # => "foo"
|
267
141
|
|
268
|
-
|
269
|
-
sheet.col_range(3, 1..2) => first two cells of the third column
|
142
|
+
or
|
270
143
|
|
271
|
-
|
144
|
+
sheet1.row_range(1)[0].value # => "foo"
|
272
145
|
|
273
|
-
|
146
|
+
Then we modify it
|
274
147
|
|
275
|
-
|
148
|
+
sheet1[1,1] = "hello"
|
276
149
|
|
277
|
-
|
150
|
+
We get the value of a range
|
278
151
|
|
279
|
-
|
152
|
+
sheet1.nameval("firstcell") # => "hello"
|
280
153
|
|
281
|
-
|
282
|
-
|
283
|
-
or
|
154
|
+
and set the value
|
284
155
|
|
285
|
-
|
156
|
+
sheet1.set_nameval("firstcell", "foo")
|
286
157
|
|
287
|
-
|
158
|
+
We get the value of a range of a locally defined name.
|
288
159
|
|
289
|
-
|
160
|
+
sheet1.rangeval("firstcell") # => "foo"
|
290
161
|
|
291
162
|
or
|
292
163
|
|
293
|
-
|
164
|
+
sheet1.rangeval("A1") # => "foo"
|
294
165
|
|
295
|
-
|
166
|
+
Then we set the value of this range.
|
296
167
|
|
297
|
-
|
168
|
+
sheet1.set_rangeval("firstcell", "bar")
|
298
169
|
|
299
|
-
|
170
|
+
We can copy the first worksheet, name it and add it before the third worksheet.
|
300
171
|
|
301
|
-
|
172
|
+
book2.add_or_copy_sheet(sheet1, :as => "copied_name, :before => book2.last_sheet)
|
302
173
|
|
303
|
-
|
174
|
+
=== Saving workbooks
|
304
175
|
|
305
|
-
|
176
|
+
Simple save is done by
|
306
177
|
|
307
|
-
|
178
|
+
book2.save
|
308
179
|
|
309
|
-
|
180
|
+
We could save the workbook under a different name, and overwrite, if a file with the same name exists.
|
310
181
|
|
311
|
-
|
182
|
+
book2.save_as('example_workbook.xls', :if_exists => :overwrite)
|
312
183
|
|
313
|
-
|
184
|
+
Then we close this workbook.
|
314
185
|
|
315
|
-
|
186
|
+
book2.close
|
316
187
|
|
317
|
-
|
188
|
+
Simple saving and closing can be also done in one step by
|
318
189
|
|
319
|
-
|
190
|
+
book2.close(:if_unsaved => :save)
|
320
191
|
|
321
|
-
|
192
|
+
=== Operating on Excel instances.
|
322
193
|
|
323
|
-
|
324
|
-
|
325
|
-
or
|
194
|
+
Suppose we want to create an Excel object by connecting to the already running Excel instance.
|
326
195
|
|
327
|
-
excel1 = Excel.
|
328
|
-
|
329
|
-
In case you want to reuse an already running Excel instance, write
|
330
|
-
|
331
|
-
excel2 = Excel.current
|
196
|
+
excel1 = Excel.current
|
332
197
|
|
333
198
|
or
|
334
199
|
|
335
|
-
|
336
|
-
|
337
|
-
Further options specify the visibility, displayalerts, screen updating and calculation mode, e.g.
|
338
|
-
|
339
|
-
excel1 = Excel.new(:reuse => false, :visible => true, :displayalerts => true, :calculation => :manual)
|
340
|
-
|
341
|
-
You can also promote an Excel instance represented as WIN32OLE object to an Excel object.
|
342
|
-
|
343
|
-
excel = Excel.new(win32ole_object)
|
344
|
-
|
345
|
-
=== Making all workbooks visible or invisible
|
346
|
-
|
347
|
-
excel1.workbooks_visible true
|
200
|
+
excel1 = Excel.new(:reuse => true)
|
348
201
|
|
349
|
-
=== Bringing an Excel instance to the foreground
|
350
202
|
|
351
|
-
|
203
|
+
Now we want to start a new, visible Excel instance.
|
352
204
|
|
353
|
-
|
205
|
+
excel2 = Excel.create(:visible => true)
|
354
206
|
|
355
|
-
|
356
|
-
|
357
|
-
=== Closing all Excel instances
|
358
|
-
|
359
|
-
Excel.close_all
|
207
|
+
or
|
360
208
|
|
361
|
-
|
209
|
+
excel2 = Excel.new(:reuse => false, :visible => true)
|
362
210
|
|
363
|
-
|
211
|
+
We open a workbook in this Excel instance.
|
364
212
|
|
365
|
-
|
213
|
+
book4 = Book.open('more_data/workbook', {:force => {:excel => excel2}})
|
366
214
|
|
367
|
-
|
215
|
+
We can close Closing an Excel instance is dony by
|
368
216
|
|
369
|
-
|
217
|
+
excel1.close
|
370
218
|
|
371
219
|
Closed Excel instances can be reopened.
|
372
220
|
|
373
|
-
|
374
|
-
|
375
|
-
=== Providing Excel instances
|
376
|
-
|
377
|
-
Providing all Excel instances (opened via RobustExcelOle) as objects of the class Excel
|
378
|
-
|
379
|
-
Excel.excel_processes
|
380
|
-
|
381
|
-
=== Setting Calculation mode.
|
221
|
+
excel1.recreate(:reopen_workbooks => true, :visible => true)
|
382
222
|
|
383
|
-
|
223
|
+
We can get the value of a range in an Excel instance by
|
384
224
|
|
385
|
-
|
386
|
-
|
387
|
-
|
225
|
+
excel2["firstcell"] => "foo"
|
226
|
+
|
227
|
+
or
|
388
228
|
|
389
|
-
|
390
|
-
# do something
|
391
|
-
end
|
229
|
+
excel2.nameval("firstcell") = "foo"
|
392
230
|
|
393
|
-
|
231
|
+
and set the value of this range by using
|
394
232
|
|
395
|
-
|
233
|
+
excel2["firstcell"] = "bar"
|
396
234
|
|
397
|
-
excel[name]
|
398
|
-
|
399
235
|
or
|
400
236
|
|
401
|
-
|
237
|
+
excel2.set_nameval("firstcell", "bar")
|
402
238
|
|
403
|
-
and set the value of a range by
|
239
|
+
We get and set the value of a range with a locally defined named by
|
404
240
|
|
405
|
-
|
241
|
+
excel2.rangeval("firstcell")
|
406
242
|
|
407
|
-
|
243
|
+
and set its value by
|
408
244
|
|
409
|
-
|
245
|
+
excel2.set_rangeval("firstcell, "bar")
|
410
246
|
|
411
|
-
|
247
|
+
Closing all Excel instances ist done by
|
412
248
|
|
413
|
-
|
249
|
+
Excel.close_all(:if_unsaved => :forget)
|
414
250
|
|
415
|
-
|
251
|
+
Hard terminating all Excel processes is done by
|
252
|
+
|
253
|
+
Excel.kill_all
|
416
254
|
|
417
255
|
=== More details
|
418
256
|
|