robust_excel_ole 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/README.rdoc +266 -71
  2. data/TodoList.md +33 -0
  3. data/examples/edit_sheets/example_adding_sheets.rb +7 -0
  4. data/examples/open_save_close/example_control_to_excel.rb +4 -4
  5. data/examples/open_save_close/example_default_excel.rb +49 -0
  6. data/examples/open_save_close/example_force_excel.rb +34 -0
  7. data/examples/open_save_close/example_if_obstructed_closeifsaved.rb +1 -1
  8. data/examples/open_save_close/example_if_obstructed_forget.rb +3 -3
  9. data/examples/open_save_close/example_if_unsaved_accept.rb +1 -1
  10. data/examples/open_save_close/example_if_unsaved_forget.rb +3 -3
  11. data/examples/open_save_close/example_if_unsaved_forget_more.rb +3 -3
  12. data/examples/open_save_close/example_read_only.rb +1 -1
  13. data/examples/open_save_close/example_rename_cells.rb +69 -0
  14. data/examples/open_save_close/example_reuse.rb +8 -8
  15. data/examples/open_save_close/example_simple.rb +3 -3
  16. data/examples/open_save_close/example_unobtrusively.rb +28 -0
  17. data/examples/save_sheets/example_save_sheets.rb +2 -6
  18. data/lib/robust_excel_ole.rb +18 -0
  19. data/lib/robust_excel_ole/book.rb +356 -123
  20. data/lib/robust_excel_ole/book_store.rb +75 -0
  21. data/lib/robust_excel_ole/excel.rb +105 -53
  22. data/lib/robust_excel_ole/robustexcelole.sublime-project +8 -8
  23. data/lib/robust_excel_ole/sheet.rb +29 -1
  24. data/lib/robust_excel_ole/version.rb +1 -1
  25. data/robust_excel_ole.gemspec +3 -3
  26. data/spec/book_spec.rb +1031 -247
  27. data/spec/book_store_spec.rb +306 -0
  28. data/spec/data/book_with_blank.xls +0 -0
  29. data/spec/data/merge_cells.xls +0 -0
  30. data/spec/data/more_simple.xls +0 -0
  31. data/spec/data/simple.xls +0 -0
  32. data/spec/excel_spec.rb +145 -90
  33. data/spec/sheet_spec.rb +31 -7
  34. metadata +15 -7
@@ -0,0 +1,306 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require File.join(File.dirname(__FILE__), './spec_helper')
4
+
5
+ RSpec.configure do |config|
6
+
7
+ config.mock_with :rspec do |mocks|
8
+ mocks.syntax = :should
9
+ end
10
+ end
11
+
12
+
13
+ $VERBOSE = nil
14
+
15
+ include RobustExcelOle
16
+ module RobustExcelOle
17
+ class MockBookstore
18
+ def fetch(filename, options = { })
19
+ nil
20
+ end
21
+ def store(book)
22
+ end
23
+ def print
24
+ puts "MockBookstore is always empty"
25
+ end
26
+ end
27
+ end
28
+
29
+
30
+ $mock_bookstore = MockBookstore.new
31
+
32
+ class Book
33
+ @@bookstore = $mock_bookstore
34
+ end
35
+
36
+
37
+ describe BookStore do
38
+
39
+ before(:all) do
40
+ excel = Excel.new(:reuse => true)
41
+ open_books = excel == nil ? 0 : excel.Workbooks.Count
42
+ puts "*** open books *** : #{open_books}" if open_books > 0
43
+ Excel.close_all
44
+ end
45
+
46
+ before do
47
+ @bookstore = BookStore.new
48
+ @dir = create_tmpdir
49
+ @simple_file = @dir + '/simple.xls'
50
+ @simple_save_file = @dir + '/simple_save.xls'
51
+ @different_file = @dir + '/different_simple.xls'
52
+ @simple_file_other_path = @dir + '/more_data/simple.xls'
53
+ end
54
+
55
+ after do
56
+ Excel.close_all
57
+ rm_tmp(@dir)
58
+ end
59
+
60
+ describe "create bookstore" do
61
+ context "with standard" do
62
+ it "should create book store" do
63
+ expect {
64
+ @book_store = BookStore.new
65
+ }.to_not raise_error
66
+ @book_store.should be_a BookStore
67
+ end
68
+ end
69
+ end
70
+
71
+ describe "Mock-Test" do
72
+ it "should never store any book" do
73
+ b1 = Book.open(@simple_file)
74
+ b2 = Book.open(@simple_file)
75
+ b2.object_id.should_not == b1.object_id
76
+ end
77
+ end
78
+
79
+
80
+ describe "fetch" do
81
+
82
+ context "with one open book" do
83
+
84
+ before do
85
+ @book = Book.open(@simple_file)
86
+ end
87
+
88
+ after do
89
+ @book.close
90
+ end
91
+
92
+ it "should do simple store and fetch" do
93
+ @bookstore.store(@book)
94
+ new_book = @bookstore.fetch(@simple_file)
95
+ new_book.should be_a Book
96
+ new_book.should be_alive
97
+ new_book.should == @book
98
+ new_book.close
99
+ end
100
+
101
+ it "should fetch one book several times" do
102
+ @bookstore.store(@book)
103
+ book1 = @bookstore.fetch(@simple_file)
104
+ book2 = @bookstore.fetch(@simple_file)
105
+ expect(book1).to be_a Book
106
+ book1.should be_alive
107
+ book1.should == @book
108
+ book2.should be_a Book
109
+ book2.should be_alive
110
+ book2.should == @book
111
+ book1.should == book2
112
+ book1.close
113
+ book2.close
114
+ end
115
+
116
+ it "should fetch nothing without stóring before" do
117
+ new_book = @bookstore.fetch(@simple_file)
118
+ new_book.should == nil
119
+ end
120
+
121
+ it "should fetch nothing when fetching a different book" do
122
+ @bookstore.store(@book)
123
+ new_book = @bookstore.fetch(@different_file)
124
+ new_book.should == nil
125
+ end
126
+
127
+ it "should fetch nothing when fetching a non-existing book" do
128
+ @bookstore.store(@book)
129
+ new_book = @bookstore.fetch("foo")
130
+ new_book.should == nil
131
+ end
132
+
133
+ end
134
+
135
+ context "with several books" do
136
+
137
+ before do
138
+ @book = Book.open(@simple_file)
139
+ @bookstore.store(@book)
140
+ end
141
+
142
+ after do
143
+ @book.close
144
+ @book2.close(:if_unsaved => :forget)
145
+ end
146
+
147
+ it "should store and open two different books" do
148
+ @book2 = Book.open(@different_file)
149
+ @bookstore.store(@book2)
150
+ new_book = @bookstore.fetch(@simple_file)
151
+ new_book2 = @bookstore.fetch(@different_file)
152
+ new_book.should be_a Book
153
+ new_book.should be_alive
154
+ new_book.should == @book
155
+ new_book2.should be_a Book
156
+ new_book2.should be_alive
157
+ new_book2.should == @book2
158
+ new_book.should_not == new_book2
159
+ new_book.close
160
+ new_book2.close
161
+ end
162
+
163
+ it "should fetch the first, writable book" do
164
+ @book2 = Book.open(@simple_file, :force_excel => :new)
165
+ @bookstore.store(@book2)
166
+ @book.ReadOnly.should be_false
167
+ @book2.ReadOnly.should be_true
168
+ new_book = @bookstore.fetch(@simple_file)
169
+ new_book.should == @book
170
+ new_book.should_not == @book2
171
+ new_book.close
172
+ end
173
+
174
+ it "should fetch the writable book even if the readonly book has unsaved changes" do
175
+ @book2 = Book.open(@simple_file, :force_excel => :new)
176
+ sheet = @book2[0]
177
+ @bookstore.store(@book2)
178
+ sheet[0,0] = sheet[0,0].value == "simple" ? "complex" : "simple"
179
+ @book.ReadOnly.should be_false
180
+ @book2.ReadOnly.should be_true
181
+ @book2.Saved. should be_false
182
+ new_book = @bookstore.fetch(@simple_file)
183
+ new_book.should == @book
184
+ new_book.should_not == @book2
185
+ new_book.close
186
+ end
187
+
188
+ end
189
+
190
+ context "with readonly book" do
191
+
192
+ before do
193
+ @book = Book.open(@simple_file, :read_only => true)
194
+ @bookstore.store(@book)
195
+ end
196
+
197
+ after do
198
+ @book.close
199
+ @book2.close(:if_unsaved => :forget)
200
+ end
201
+
202
+ it "should fetch the second, writable book" do
203
+ @book2 = Book.open(@simple_file, :force_excel => :new)
204
+ @bookstore.store(@book2)
205
+ @book.ReadOnly.should be_true
206
+ @book2.ReadOnly.should be_false
207
+ new_book = @bookstore.fetch(@simple_file)
208
+ new_book.should == @book2
209
+ new_book.should_not == @book
210
+ new_book.close
211
+ end
212
+
213
+ it "should fetch the recent readonly book when there are only readonly books" do
214
+ @book2 = Book.open(@simple_file, :force_excel => :new, :read_only => true)
215
+ @bookstore.store(@book2)
216
+ @book.ReadOnly.should be_true
217
+ @book2.ReadOnly.should be_true
218
+ new_book = @bookstore.fetch(@simple_file)
219
+ new_book.should == @book2
220
+ new_book.should_not == @book
221
+ new_book.close
222
+ end
223
+
224
+ it "should fetch the second readonly book with unsaved changes" do
225
+ @book2 = Book.open(@simple_file, :force_excel => :new, :read_only => true)
226
+ sheet = @book2[0]
227
+ @bookstore.store(@book2)
228
+ sheet[0,0] = sheet[0,0].value == "simple" ? "complex" : "simple"
229
+ @book.ReadOnly.should be_true
230
+ @book2.ReadOnly.should be_true
231
+ @book2.Saved.should be_false
232
+ new_book = @bookstore.fetch(@simple_file)
233
+ new_book.should == @book2
234
+ new_book.should_not == @book
235
+ new_book.close
236
+ end
237
+
238
+ it "should fetch the second, writable book, if a writable, a readonly and an unsaved readonly book exist" do
239
+ @book2 = Book.open(@simple_file, :force_excel => :new)
240
+ @book3 = Book.open(@simple_file, :force_excel => :new)
241
+ @bookstore.store(@book2)
242
+ @bookstore.store(@book3)
243
+ sheet = @book3[0]
244
+ sheet[0,0] = sheet[0,0].value == "simple" ? "complex" : "simple"
245
+ @book.ReadOnly.should be_true
246
+ @book2.ReadOnly.should be_false
247
+ @book3.ReadOnly.should be_true
248
+ @book3.Saved.should be_false
249
+ new_book = @bookstore.fetch(@simple_file)
250
+ new_book.should == @book2
251
+ new_book.should_not == @book
252
+ new_book.should_not == @book3
253
+ new_book.close
254
+ end
255
+ end
256
+
257
+ context "with changing file name" do
258
+
259
+ before do
260
+ @book = Book.open(@simple_file)
261
+ @book.save_as(@simple_save_file, :if_exists => :overwrite)
262
+ @bookstore = @book.book_store
263
+ end
264
+
265
+ after do
266
+ @book.close
267
+ end
268
+
269
+ it "should return only book with correct file name" do
270
+ book1 = @bookstore.fetch(@simple_save_file)
271
+ book1.should == @book
272
+ end
273
+
274
+ it "should return only book with correct file name" do
275
+ book1 = @bookstore.fetch(@simple_file)
276
+ book1.should == nil
277
+ end
278
+ end
279
+
280
+ context "with given excel instance and fetching readonly" do
281
+
282
+ before do
283
+ @book = Book.open(@simple_file)
284
+ @bookstore.store(@book)
285
+ @book2 = Book.open(@simple_file, :force_excel => :new)
286
+ @bookstore.store(@book2)
287
+ end
288
+
289
+ after do
290
+ @book.close
291
+ end
292
+
293
+ it "should fetch the book in the given excel instance" do
294
+ @book.ReadOnly.should be_false
295
+ @book2.ReadOnly.should be_true
296
+ book_new = @bookstore.fetch(@simple_file, :excel => @book2.excel)
297
+ book_new.should be_a Book
298
+ book_new.should be_alive
299
+ book_new.should == @book2
300
+ end
301
+
302
+ end
303
+
304
+
305
+ end
306
+ end
Binary file
Binary file
Binary file
data/spec/data/simple.xls CHANGED
Binary file
data/spec/excel_spec.rb CHANGED
@@ -8,65 +8,61 @@ module RobustExcelOle
8
8
 
9
9
  describe Excel do
10
10
 
11
- context "app creation" do
12
- after do
13
- Excel.close_all
14
- end
11
+ before do
12
+ Excel.close_all
13
+ end
15
14
 
15
+ context "excel creation" do
16
+
16
17
  def creation_ok? # :nodoc: #
17
- @app.alive?.should == true
18
- @app.Visible.should == false
19
- @app.DisplayAlerts.should == false
20
- @app.Name.should == "Microsoft Excel"
18
+ @excel.alive?.should be_true
19
+ @excel.Visible.should be_false
20
+ @excel.DisplayAlerts.should be_false
21
+ @excel.Name.should == "Microsoft Excel"
21
22
  end
22
23
 
23
24
  it "should work with 'new' " do
24
- @app = Excel.new
25
+ @excel = Excel.new
25
26
  creation_ok?
26
27
  end
27
28
 
28
29
  it "should work with 'new' " do
29
- @app = Excel.new(:reuse => false)
30
+ @excel = Excel.new(:reuse => false)
30
31
  creation_ok?
31
32
  end
32
33
 
33
34
  it "should work with 'create' " do
34
- @app = Excel.create
35
+ @excel = Excel.create
35
36
  creation_ok?
36
37
  end
37
38
 
38
39
  end
39
40
 
40
- context "with existing app" do
41
+ context "with existing excel" do
41
42
 
42
43
  before do
43
- Excel.close_all
44
- @app1 = Excel.create
44
+ @excel1 = Excel.create
45
45
  end
46
46
 
47
- after do
48
- Excel.close_all
47
+ it "should create different excel" do
48
+ excel2 = Excel.create
49
+ #puts "@excel1 #{@excel1.Hwnd}"
50
+ #puts "excel2 #{excel2.Hwnd}"
51
+ excel2.Hwnd.should_not == @excel1.Hwnd
49
52
  end
50
53
 
51
- it "should create different app" do
52
- app2 = Excel.create
53
- #puts "@app1 #{@app1.Hwnd}"
54
- #puts "app2 #{app2.Hwnd}"
55
- app2.Hwnd.should_not == @app1.Hwnd
54
+ it "should reuse existing excel" do
55
+ excel2 = Excel.current
56
+ #puts "@excel1 #{@excel1.Hwnd}"
57
+ #puts "excel2 #{excel2.Hwnd}"
58
+ excel2.Hwnd.should == @excel1.Hwnd
56
59
  end
57
60
 
58
- it "should reuse existing app" do
59
- app2 = Excel.current
60
- #puts "@app1 #{@app1.Hwnd}"
61
- #puts "app2 #{app2.Hwnd}"
62
- app2.Hwnd.should == @app1.Hwnd
63
- end
64
-
65
- it "should reuse existing app with default options for 'new'" do
66
- app2 = Excel.new
67
- #puts "@app1 #{@app1.Hwnd}"
68
- #puts "app2 #{app2.Hwnd}"
69
- app2.Hwnd.should == @app1.Hwnd
61
+ it "should reuse existing excel with default options for 'new'" do
62
+ excel2 = Excel.new
63
+ #puts "@excel1 #{@excel1.Hwnd}"
64
+ #puts "excel2 #{excel2.Hwnd}"
65
+ excel2.Hwnd.should == @excel1.Hwnd
70
66
  end
71
67
 
72
68
  end
@@ -75,17 +71,17 @@ module RobustExcelOle
75
71
  def direct_excel_creation_helper # :nodoc: #
76
72
  expect { WIN32OLE.connect("Excel.Application") }.to raise_error
77
73
  sleep 0.1
78
- exl1 = WIN32OLE.new("Excel.Application")
79
- exl1.Workbooks.Add
80
- exl2 = WIN32OLE.new("Excel.Application")
81
- exl2.Workbooks.Add
74
+ excel1 = WIN32OLE.new("Excel.Application")
75
+ excel1.Workbooks.Add
76
+ excel2 = WIN32OLE.new("Excel.Application")
77
+ excel2.Workbooks.Add
82
78
  expect { WIN32OLE.connect("Excel.Application") }.to_not raise_error
83
79
  end
84
80
 
85
81
  it "simple file with default" do
86
- RobustExcelOle::Excel.close_all
82
+ Excel.close_all
87
83
  direct_excel_creation_helper
88
- RobustExcelOle::Excel.close_all
84
+ Excel.close_all
89
85
  sleep 0.1
90
86
  expect { WIN32OLE.connect("Excel.Application") }.to raise_error
91
87
  end
@@ -93,89 +89,145 @@ module RobustExcelOle
93
89
 
94
90
  describe "==" do
95
91
  before do
96
- Excel.close_all
97
- @app1 = Excel.create
98
- end
99
-
100
- after do
101
- Excel.close_all
92
+ @excel1 = Excel.create
102
93
  end
103
94
 
104
- it "should be true with two identical excel applications" do
105
- app2 = Excel.current
106
- app2.should == @app1
95
+ it "should be true with two identical excel instances" do
96
+ excel2 = Excel.current
97
+ excel2.should == @excel1
107
98
  end
108
99
 
109
- it "should be false with two different excel applications" do
110
- app2 = Excel.create
111
- app2.should_not == @app1
100
+ it "should be false with two different excel instances" do
101
+ excel2 = Excel.create
102
+ excel2.should_not == @excel1
112
103
  end
113
104
 
114
105
  it "should be false with non-Excel objects" do
115
- @app1.should_not == "hallo"
116
- @app1.should_not == 7
117
- @app1.should_not == nil
106
+ @excel1.should_not == "hallo"
107
+ @excel1.should_not == 7
108
+ @excel1.should_not == nil
118
109
  end
119
110
 
120
111
  end
121
112
 
113
+ context "with Visible and DisplayAlerts" do
122
114
 
123
- context "with :excel" do
124
-
125
- before do
126
- Excel.close_all
115
+ it "should create Excel visible" do
116
+ excel = Excel.new(:visible => true)
117
+ excel.Visible.should be_true
118
+ excel.visible.should be_true
119
+ excel.DisplayAlerts.should be_false
120
+ excel.displayalerts.should be_false
121
+ excel.visible = false
122
+ excel.Visible.should be_false
123
+ excel.visible.should be_false
124
+ end
125
+
126
+ it "should create Excel with DispayAlerts enabled" do
127
+ excel = Excel.new(:displayalerts => true)
128
+ excel.DisplayAlerts.should be_true
129
+ excel.displayalerts.should be_true
130
+ excel.Visible.should be_false
131
+ excel.visible.should be_false
132
+ excel.displayalerts = false
133
+ excel.DisplayAlerts.should be_false
134
+ excel.displayalerts.should be_false
135
+ end
136
+
137
+ it "should keep visible and displayalerts values when reusing Excel" do
138
+ excel = Excel.new(:visible => true)
139
+ excel.visible.should be_true
140
+ excel.displayalerts.should be_false
141
+ excel2 = Excel.new(:displayalerts => true)
142
+ excel2.should == excel
143
+ excel.visible.should be_true
144
+ excel.displayalerts.should be_true
145
+ end
146
+
147
+ it "should keep displayalerts and visible values when reusing Excel" do
148
+ excel = Excel.new(:displayalerts => true)
149
+ excel.visible.should be_false
150
+ excel.displayalerts.should be_true
151
+ excel2 = Excel.new(:visible => true)
152
+ excel2.should == excel
153
+ excel.visible.should be_true
154
+ excel.displayalerts.should be_true
127
155
  end
128
156
 
129
- after (:each) do
130
- Excel.close_all
131
- end
157
+ end
132
158
 
133
- it "should reuse in given excel app" do
134
- app1 = Excel.new(:reuse => false)
135
- app2 = Excel.new(:reuse => false)
136
- app3 = Excel.new(:excel => app1)
137
- app4 = Excel.new(:excel => app2)
138
- app3.should == app1
139
- app4.should == app2
159
+
160
+ context "with displayalerts" do
161
+ before do
162
+ @excel1 = Excel.new(:displayalerts => true)
163
+ @excel2 = Excel.new(:displayalerts => false, :reuse => false)
164
+ end
165
+
166
+ it "should turn off displayalerts" do
167
+ @excel1.DisplayAlerts.should be_true
168
+ begin
169
+ @excel1.with_displayalerts false do
170
+ @excel1.DisplayAlerts.should be_false
171
+ raise TestError, "any_error"
172
+ end
173
+ rescue TestError
174
+ @excel1.DisplayAlerts.should be_true
175
+ end
176
+ end
177
+
178
+ it "should turn on displayalerts" do
179
+ @excel2.DisplayAlerts.should be_false
180
+ begin
181
+ @excel1.with_displayalerts true do
182
+ @excel1.DisplayAlerts.should be_true
183
+ raise TestError, "any_error"
184
+ end
185
+ rescue TestError
186
+ @excel2.DisplayAlerts.should be_false
187
+ end
140
188
  end
141
189
 
142
190
  end
143
191
 
144
- context "with Visible and DisplayAlerts" do
145
-
192
+ context "method delegation for capitalized methods" do
146
193
  before do
147
- Excel.close_all
194
+ @excel1 = Excel.new
148
195
  end
149
196
 
150
- after (:each) do
151
- Excel.close_all
197
+ it "should raise WIN32OLERuntimeError" do
198
+ expect{ @excel1.NonexistingMethod }.to raise_error(VBAMethodMissingError)
152
199
  end
153
200
 
154
- it "should be visible" do
155
- app = Excel.new(:visible => true)
156
- app.Visible.should == true
157
- app.DisplayAlerts.should == false
201
+ it "should raise NoMethodError for uncapitalized methods" do
202
+ expect{ @excel1.nonexisting_method }.to raise_error(NoMethodError)
158
203
  end
204
+ end
159
205
 
160
- it "should displayalerts" do
161
- app = Excel.new(:displayalerts => true)
162
- app.DisplayAlerts.should == true
163
- app.Visible.should == false
206
+ context "with hwnd and hwnd2excel" do
207
+
208
+ before do
209
+ @excel1 = Excel.new
210
+ @excel2 = Excel.new(:reuse => false)
164
211
  end
165
212
 
166
- it "should visible and displayalerts" do
167
- app = Excel.new(:visible => true)
168
- app.Visible.should == true
169
- app.DisplayAlerts.should == false
170
- app2 = Excel.new(:displayalerts => true)
171
- app2.Visible.should == true
172
- app2.DisplayAlerts.should == true
213
+ it "should yield the correct hwnd" do
214
+ @excel1.Hwnd.should == @excel1.hwnd
215
+ @excel2.Hwnd.should == @excel2.hwnd
173
216
  end
174
217
 
218
+ it "should provide the same excel instances" do
219
+ @excel1.should_not == @excel2
220
+ excel3 = Excel.hwnd2excel(@excel1.hwnd)
221
+ excel4 = Excel.hwnd2excel(@excel2.hwnd)
222
+ @excel1.should == excel3
223
+ @excel2.should == excel4
224
+ excel3.should_not == excel4
225
+ end
175
226
  end
176
-
177
227
  end
178
228
 
229
+
230
+
179
231
  describe "RobustExcelOle" do
180
232
  context "#absolute_path" do
181
233
  it "should work" do
@@ -185,7 +237,7 @@ module RobustExcelOle
185
237
  RobustExcelOle::absolute_path("C:abc").should == File.expand_path("abc").gsub("/","\\")
186
238
  end
187
239
 
188
- it "should return right absoute path name" do
240
+ it "should return right absolute path name" do
189
241
  @filename = 'C:/Dokumente und Einstellungen/Zauberthomas/Eigene Dateien/robust_excel_ole/spec/book_spec.rb'
190
242
  RobustExcelOle::absolute_path(@filename).gsub("\\","/").should == @filename
191
243
  end
@@ -194,3 +246,6 @@ module RobustExcelOle
194
246
  end
195
247
 
196
248
  end
249
+
250
+ class TestError < RuntimeError
251
+ end