libis-tools 1.0.5-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +40 -0
  6. data/Gemfile +7 -0
  7. data/README.md +202 -0
  8. data/Rakefile +11 -0
  9. data/bin/libis_tool +5 -0
  10. data/lib/libis-tools.rb +1 -0
  11. data/lib/libis/tools.rb +25 -0
  12. data/lib/libis/tools/assert.rb +52 -0
  13. data/lib/libis/tools/checksum.rb +106 -0
  14. data/lib/libis/tools/cli/cli_helper.rb +189 -0
  15. data/lib/libis/tools/cli/reorg.rb +416 -0
  16. data/lib/libis/tools/command.rb +133 -0
  17. data/lib/libis/tools/command_line.rb +23 -0
  18. data/lib/libis/tools/config.rb +147 -0
  19. data/lib/libis/tools/config_file.rb +85 -0
  20. data/lib/libis/tools/csv.rb +38 -0
  21. data/lib/libis/tools/deep_struct.rb +71 -0
  22. data/lib/libis/tools/extend/array.rb +16 -0
  23. data/lib/libis/tools/extend/empty.rb +7 -0
  24. data/lib/libis/tools/extend/hash.rb +147 -0
  25. data/lib/libis/tools/extend/kernel.rb +25 -0
  26. data/lib/libis/tools/extend/ostruct.rb +3 -0
  27. data/lib/libis/tools/extend/roo.rb +91 -0
  28. data/lib/libis/tools/extend/string.rb +94 -0
  29. data/lib/libis/tools/extend/struct.rb +29 -0
  30. data/lib/libis/tools/extend/symbol.rb +8 -0
  31. data/lib/libis/tools/logger.rb +130 -0
  32. data/lib/libis/tools/mets_dnx.rb +61 -0
  33. data/lib/libis/tools/mets_file.rb +504 -0
  34. data/lib/libis/tools/mets_objects.rb +547 -0
  35. data/lib/libis/tools/parameter.rb +372 -0
  36. data/lib/libis/tools/spreadsheet.rb +196 -0
  37. data/lib/libis/tools/temp_file.rb +42 -0
  38. data/lib/libis/tools/thread_safe.rb +31 -0
  39. data/lib/libis/tools/version.rb +5 -0
  40. data/lib/libis/tools/xml_document.rb +583 -0
  41. data/libis-tools.gemspec +55 -0
  42. data/spec/assert_spec.rb +65 -0
  43. data/spec/checksum_spec.rb +68 -0
  44. data/spec/command_spec.rb +90 -0
  45. data/spec/config_file_spec.rb +83 -0
  46. data/spec/config_spec.rb +113 -0
  47. data/spec/csv_spec.rb +159 -0
  48. data/spec/data/test-headers.csv +2 -0
  49. data/spec/data/test-headers.tsv +2 -0
  50. data/spec/data/test-noheaders.csv +1 -0
  51. data/spec/data/test-noheaders.tsv +1 -0
  52. data/spec/data/test.data +9 -0
  53. data/spec/data/test.xlsx +0 -0
  54. data/spec/data/test.xml +8 -0
  55. data/spec/data/test.yml +2 -0
  56. data/spec/data/test_config.yml +15 -0
  57. data/spec/deep_struct_spec.rb +138 -0
  58. data/spec/logger_spec.rb +165 -0
  59. data/spec/mets_file_spec.rb +223 -0
  60. data/spec/parameter_container_spec.rb +152 -0
  61. data/spec/parameter_spec.rb +148 -0
  62. data/spec/spec_helper.rb +29 -0
  63. data/spec/spreadsheet_spec.rb +1820 -0
  64. data/spec/temp_file_spec.rb +76 -0
  65. data/spec/test.xsd +20 -0
  66. data/spec/thread_safe_spec.rb +64 -0
  67. data/spec/xmldocument_spec.rb +421 -0
  68. data/test/test_helper.rb +7 -0
  69. data/test/webservices/test_ca_item_info.rb +59 -0
  70. data/test/webservices/test_ca_search.rb +35 -0
  71. metadata +437 -0
@@ -0,0 +1,29 @@
1
+ # require 'codeclimate-test-reporter'
2
+ # ::CodeClimate::TestReporter.start
3
+
4
+ # if !defined?(RUBY_ENGINE) || RUBY_ENGINE != 'jruby'
5
+ require 'coveralls'
6
+ Coveralls.wear!
7
+ # end
8
+
9
+ # noinspection RubyResolve
10
+ require 'bundler/setup'
11
+ # noinspection RubyResolve
12
+ Bundler.setup
13
+
14
+ require 'rspec'
15
+ require 'libis-tools'
16
+
17
+ # RSpec.configure do |config|
18
+ # original_stderr = $stderr
19
+ # original_stdout = $stdout
20
+ # config.before(:all) do
21
+ # # Redirect stderr and stdout
22
+ # $stderr = File.open(File::NULL, 'w')
23
+ # $stdout = File.open(File::NULL, 'w')
24
+ # end
25
+ # config.after(:all) do
26
+ # $stderr = original_stderr
27
+ # $stdout = original_stdout
28
+ # end
29
+ # end
@@ -0,0 +1,1820 @@
1
+ # encoding: utf-8
2
+ require_relative 'spec_helper'
3
+ require 'rspec/matchers'
4
+ require 'libis/tools/spreadsheet'
5
+
6
+ describe 'Libis::Tools::Spreadsheet' do
7
+
8
+ let(:path) {File.absolute_path('data', File.dirname(__FILE__))}
9
+ let(:options) { {} }
10
+ let(:ss) {
11
+ Libis::Tools::Spreadsheet.new(
12
+ File.join(path, file_name),
13
+ { required: required_headers,
14
+ optional: optional_headers
15
+ }.merge(options)
16
+ )
17
+ }
18
+
19
+ let(:optional_headers) {[]}
20
+
21
+ context 'CSV file' do
22
+ context 'with headers' do
23
+ let(:file_name) {'test-headers.csv'}
24
+
25
+ context 'well-formed' do
26
+
27
+ let(:required_headers) {%w'FirstName LastName'}
28
+
29
+ it 'opens correctly' do
30
+ expect {ss}.not_to raise_error
31
+ end
32
+
33
+ it 'contains expected headers' do
34
+ required_headers.each do |header|
35
+ expect(ss.headers).to include header
36
+ end
37
+ expect(ss.headers).to eq %w'FirstName LastName address'
38
+ end
39
+
40
+ it '#shift returns Hash object' do
41
+ row = ss.shift
42
+ expect(row).to be_a Hash
43
+ expect(row['FirstName']).to eq 'John'
44
+ expect(row['LastName']).to eq 'Smith'
45
+ expect(row['address']).to eq 'mystreet 1, myplace'
46
+ expect(row['phone']).to be_nil
47
+ end
48
+
49
+ it '#parse returns Array of Hash objects' do
50
+ rows = ss.parse
51
+ expect(rows).to be_a Array
52
+ expect(rows.size).to eq 1
53
+ row = rows[0]
54
+ expect(row).to be_a Hash
55
+ expect(row['FirstName']).to eq 'John'
56
+ expect(row['LastName']).to eq 'Smith'
57
+ expect(row['address']).to eq 'mystreet 1, myplace'
58
+ expect(row['phone']).to be_nil
59
+ end
60
+
61
+ end
62
+
63
+ context 'not specified' do
64
+
65
+ let(:required_headers) {[]}
66
+
67
+ it 'opens correctly' do
68
+ expect {ss}.not_to raise_error
69
+ end
70
+
71
+ it 'contains expected headers' do
72
+ expect(ss.headers).to eq %w'FirstName LastName address'
73
+ end
74
+
75
+ it '#shift returns Hash object' do
76
+ row = ss.shift
77
+ expect(row).to be_a Hash
78
+ expect(row['FirstName']).to eq 'John'
79
+ expect(row['LastName']).to eq 'Smith'
80
+ expect(row['address']).to eq 'mystreet 1, myplace'
81
+ expect(row['phone']).to be_nil
82
+ end
83
+
84
+ it '#parse returns Array of Hash objects' do
85
+ rows = ss.parse
86
+ expect(rows).to be_a Array
87
+ expect(rows.size).to eq 1
88
+ row = rows[0]
89
+ expect(row).to be_a Hash
90
+ expect(row['FirstName']).to eq 'John'
91
+ expect(row['LastName']).to eq 'Smith'
92
+ expect(row['address']).to eq 'mystreet 1, myplace'
93
+ expect(row['phone']).to be_nil
94
+ end
95
+
96
+ end
97
+
98
+ context 'not well-formed' do
99
+
100
+ let(:required_headers) {%w'FirstName LastName address phone'}
101
+
102
+ it 'throws error when opened' do
103
+ expect {ss}.to raise_error(RuntimeError, 'Headers not found: ["phone"].')
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ context 'without headers' do
110
+ let(:file_name) {'test-noheaders.csv'}
111
+
112
+ context 'well-formed and strict' do
113
+ let(:required_headers) {%w'FirstName LastName'}
114
+
115
+ it 'opens correctly' do
116
+ expect {ss}.not_to raise_error
117
+ end
118
+
119
+ it 'contains only required headers' do
120
+ required_headers.each do |header|
121
+ expect(ss.headers).to include header
122
+ end
123
+ expect(ss.headers).to eq %w'FirstName LastName'
124
+ end
125
+
126
+ it '#shift returns Hash object' do
127
+ row = ss.shift
128
+ expect(row).to be_a Hash
129
+ expect(row['FirstName']).to eq 'John'
130
+ expect(row['LastName']).to eq 'Smith'
131
+ expect(row['address']).to be_nil
132
+ expect(row['phone']).to be_nil
133
+ end
134
+
135
+ it '#parse returns Array of Hash objects' do
136
+ rows = ss.parse
137
+ expect(rows).to be_a Array
138
+ expect(rows.size).to eq 1
139
+ row = rows[0]
140
+ expect(row).to be_a Hash
141
+ expect(row['FirstName']).to eq 'John'
142
+ expect(row['LastName']).to eq 'Smith'
143
+ expect(row['address']).to be_nil
144
+ expect(row['phone']).to be_nil
145
+ end
146
+
147
+ end
148
+
149
+ context 'well-formed with optional headers' do
150
+ let(:required_headers) {%w'FirstName LastName'}
151
+ let(:optional_headers) {%w'address'}
152
+
153
+ it 'opens correctly' do
154
+ expect {ss}.not_to raise_error
155
+ end
156
+
157
+ it 'contains required and optional headers' do
158
+ required_headers.each do |header|
159
+ expect(ss.headers).to include header
160
+ end
161
+ optional_headers.each do |header|
162
+ expect(ss.headers).to include header
163
+ end
164
+ expect(ss.headers).to eq %w'FirstName LastName address'
165
+ end
166
+
167
+ it '#shift returns Hash object' do
168
+ row = ss.shift
169
+ expect(row).to be_a Hash
170
+ expect(row['FirstName']).to eq 'John'
171
+ expect(row['LastName']).to eq 'Smith'
172
+ expect(row['address']).to eq 'mystreet 1, myplace'
173
+ expect(row['phone']).to be_nil
174
+ end
175
+
176
+ it '#parse returns Array of Hash objects' do
177
+ rows = ss.parse
178
+ expect(rows).to be_a Array
179
+ expect(rows.size).to eq 1
180
+ row = rows[0]
181
+ expect(row).to be_a Hash
182
+ expect(row['FirstName']).to eq 'John'
183
+ expect(row['LastName']).to eq 'Smith'
184
+ expect(row['address']).to eq 'mystreet 1, myplace'
185
+ expect(row['phone']).to be_nil
186
+ end
187
+
188
+ end
189
+
190
+ context 'missing optional headers' do
191
+
192
+ let(:required_headers) {%w'FirstName LastName address'}
193
+ let(:optional_headers) {%w'phone'}
194
+
195
+ it 'opens correctly' do
196
+ expect {ss}.not_to raise_error
197
+ end
198
+
199
+ it 'contains only required headers' do
200
+ required_headers.each do |header|
201
+ expect(ss.headers).to include header
202
+ end
203
+ optional_headers.each do |header|
204
+ expect(ss.headers).not_to include header
205
+ end
206
+ expect(ss.headers).to eq %w'FirstName LastName address'
207
+ end
208
+
209
+ it '#shift returns Hash object' do
210
+ row = ss.shift
211
+ expect(row).to be_a Hash
212
+ expect(row['FirstName']).to eq 'John'
213
+ expect(row['LastName']).to eq 'Smith'
214
+ expect(row['address']).to eq 'mystreet 1, myplace'
215
+ expect(row['phone']).to be_nil
216
+ end
217
+
218
+ it '#parse returns Array of Hash objects' do
219
+ rows = ss.parse
220
+ expect(rows).to be_a Array
221
+ expect(rows.size).to eq 1
222
+ row = rows[0]
223
+ expect(row).to be_a Hash
224
+ expect(row['FirstName']).to eq 'John'
225
+ expect(row['LastName']).to eq 'Smith'
226
+ expect(row['address']).to eq 'mystreet 1, myplace'
227
+ expect(row['phone']).to be_nil
228
+ end
229
+
230
+ end
231
+
232
+ context 'missing required header' do
233
+ let(:required_headers) {%w'FirstName LastName address phone'}
234
+
235
+ it 'throws error when opened' do
236
+ expect {ss}.to raise_error(RuntimeError, 'Sheet does not contain enough columns.')
237
+ end
238
+
239
+ end
240
+
241
+ end
242
+
243
+ end
244
+
245
+ context 'TSV file' do
246
+
247
+ let(:options) { {
248
+ col_sep: "\t",
249
+ extension: 'csv'
250
+ }}
251
+
252
+ context 'with headers' do
253
+
254
+ let(:file_name) {'test-headers.tsv'}
255
+
256
+ context 'well-formed' do
257
+
258
+ let(:required_headers) {%w'FirstName LastName'}
259
+
260
+ it 'opens correctly' do
261
+ expect {ss}.not_to raise_error
262
+ end
263
+
264
+ it 'contains expected headers' do
265
+ required_headers.each do |header|
266
+ expect(ss.headers).to include header
267
+ end
268
+ expect(ss.headers).to eq %w'FirstName LastName address'
269
+ end
270
+
271
+ it '#shift returns Hash object' do
272
+ row = ss.shift
273
+ expect(row).to be_a Hash
274
+ expect(row['FirstName']).to eq 'John'
275
+ expect(row['LastName']).to eq 'Smith'
276
+ expect(row['address']).to eq 'mystreet 1, myplace'
277
+ expect(row['phone']).to be_nil
278
+ end
279
+
280
+ it '#parse returns Array of Hash objects' do
281
+ rows = ss.parse
282
+ expect(rows).to be_a Array
283
+ expect(rows.size).to eq 1
284
+ row = rows[0]
285
+ expect(row).to be_a Hash
286
+ expect(row['FirstName']).to eq 'John'
287
+ expect(row['LastName']).to eq 'Smith'
288
+ expect(row['address']).to eq 'mystreet 1, myplace'
289
+ expect(row['phone']).to be_nil
290
+ end
291
+
292
+ end
293
+
294
+ context 'not specified' do
295
+
296
+ let(:required_headers) {[]}
297
+
298
+ it 'opens correctly' do
299
+ expect {ss}.not_to raise_error
300
+ end
301
+
302
+ it 'contains expected headers' do
303
+ expect(ss.headers).to eq %w'FirstName LastName address'
304
+ end
305
+
306
+ it '#shift returns Hash object' do
307
+ row = ss.shift
308
+ expect(row).to be_a Hash
309
+ expect(row['FirstName']).to eq 'John'
310
+ expect(row['LastName']).to eq 'Smith'
311
+ expect(row['address']).to eq 'mystreet 1, myplace'
312
+ expect(row['phone']).to be_nil
313
+ end
314
+
315
+ it '#parse returns Array of Hash objects' do
316
+ rows = ss.parse
317
+ expect(rows).to be_a Array
318
+ expect(rows.size).to eq 1
319
+ row = rows[0]
320
+ expect(row).to be_a Hash
321
+ expect(row['FirstName']).to eq 'John'
322
+ expect(row['LastName']).to eq 'Smith'
323
+ expect(row['address']).to eq 'mystreet 1, myplace'
324
+ expect(row['phone']).to be_nil
325
+ end
326
+
327
+ end
328
+
329
+ context 'not well-formed' do
330
+
331
+ let(:required_headers) {%w'FirstName LastName address phone'}
332
+
333
+ it 'throws error when opened' do
334
+ expect {ss}.to raise_error(RuntimeError, 'Headers not found: ["phone"].')
335
+ end
336
+ end
337
+
338
+ end
339
+
340
+ context 'without headers' do
341
+ let(:file_name) {'test-noheaders.tsv'}
342
+
343
+ context 'well-formed and strict' do
344
+ let(:required_headers) {%w'FirstName LastName'}
345
+
346
+ it 'opens correctly' do
347
+ expect {ss}.not_to raise_error
348
+ end
349
+
350
+ it 'contains only required headers' do
351
+ required_headers.each do |header|
352
+ expect(ss.headers).to include header
353
+ end
354
+ expect(ss.headers).to eq %w'FirstName LastName'
355
+ end
356
+
357
+ it '#shift returns Hash object' do
358
+ row = ss.shift
359
+ expect(row).to be_a Hash
360
+ expect(row['FirstName']).to eq 'John'
361
+ expect(row['LastName']).to eq 'Smith'
362
+ expect(row['address']).to be_nil
363
+ expect(row['phone']).to be_nil
364
+ end
365
+
366
+ it '#parse returns Array of Hash objects' do
367
+ rows = ss.parse
368
+ expect(rows).to be_a Array
369
+ expect(rows.size).to eq 1
370
+ row = rows[0]
371
+ expect(row).to be_a Hash
372
+ expect(row['FirstName']).to eq 'John'
373
+ expect(row['LastName']).to eq 'Smith'
374
+ expect(row['address']).to be_nil
375
+ expect(row['phone']).to be_nil
376
+ end
377
+
378
+ end
379
+
380
+ context 'well-formed with optional headers' do
381
+ let(:required_headers) {%w'FirstName LastName'}
382
+ let(:optional_headers) {%w'address'}
383
+
384
+ it 'opens correctly' do
385
+ expect {ss}.not_to raise_error
386
+ end
387
+
388
+ it 'contains required and optional headers' do
389
+ required_headers.each do |header|
390
+ expect(ss.headers).to include header
391
+ end
392
+ optional_headers.each do |header|
393
+ expect(ss.headers).to include header
394
+ end
395
+ expect(ss.headers).to eq %w'FirstName LastName address'
396
+ end
397
+
398
+ it '#shift returns Hash object' do
399
+ row = ss.shift
400
+ expect(row).to be_a Hash
401
+ expect(row['FirstName']).to eq 'John'
402
+ expect(row['LastName']).to eq 'Smith'
403
+ expect(row['address']).to eq 'mystreet 1, myplace'
404
+ expect(row['phone']).to be_nil
405
+ end
406
+
407
+ it '#parse returns Array of Hash objects' do
408
+ rows = ss.parse
409
+ expect(rows).to be_a Array
410
+ expect(rows.size).to eq 1
411
+ row = rows[0]
412
+ expect(row).to be_a Hash
413
+ expect(row['FirstName']).to eq 'John'
414
+ expect(row['LastName']).to eq 'Smith'
415
+ expect(row['address']).to eq 'mystreet 1, myplace'
416
+ expect(row['phone']).to be_nil
417
+ end
418
+
419
+ end
420
+
421
+ context 'missing optional headers' do
422
+
423
+ let(:required_headers) {%w'FirstName LastName address'}
424
+ let(:optional_headers) {%w'phone'}
425
+
426
+ it 'opens correctly' do
427
+ expect {ss}.not_to raise_error
428
+ end
429
+
430
+ it 'contains only required headers' do
431
+ required_headers.each do |header|
432
+ expect(ss.headers).to include header
433
+ end
434
+ optional_headers.each do |header|
435
+ expect(ss.headers).not_to include header
436
+ end
437
+ expect(ss.headers).to eq %w'FirstName LastName address'
438
+ end
439
+
440
+ it '#shift returns Hash object' do
441
+ row = ss.shift
442
+ expect(row).to be_a Hash
443
+ expect(row['FirstName']).to eq 'John'
444
+ expect(row['LastName']).to eq 'Smith'
445
+ expect(row['address']).to eq 'mystreet 1, myplace'
446
+ expect(row['phone']).to be_nil
447
+ end
448
+
449
+ it '#parse returns Array of Hash objects' do
450
+ rows = ss.parse
451
+ expect(rows).to be_a Array
452
+ expect(rows.size).to eq 1
453
+ row = rows[0]
454
+ expect(row).to be_a Hash
455
+ expect(row['FirstName']).to eq 'John'
456
+ expect(row['LastName']).to eq 'Smith'
457
+ expect(row['address']).to eq 'mystreet 1, myplace'
458
+ expect(row['phone']).to be_nil
459
+ end
460
+
461
+ end
462
+
463
+ context 'missing required header' do
464
+ let(:required_headers) {%w'FirstName LastName address phone'}
465
+
466
+ it 'throws error when opened' do
467
+ expect {ss}.to raise_error(RuntimeError, 'Sheet does not contain enough columns.')
468
+ end
469
+
470
+ end
471
+
472
+ end
473
+
474
+ end
475
+
476
+ context 'XLSX file' do
477
+
478
+ let(:real_headers) {%w'Date Amount Code Remark'}
479
+ # noinspection RubyStringKeysInHashInspection
480
+ let(:header_row) {{'Date' => 'Date', 'Amount' => 'Amount', 'Code' => 'Code', 'Remark' => 'Remark'}}
481
+ # noinspection RubyStringKeysInHashInspection
482
+ let(:first_data_row) {{'Date' => Date.new(2016, 05, 10), 'Amount' => 1270.0, 'Code' => 1, 'Remark' => 'a'}}
483
+ # noinspection RubyStringKeysInHashInspection
484
+ let(:data_row_13) {{'Date' => Date.new(2016, 7, 1), 'Amount' => 3705.0, 'Code' => 3, 'Remark' => 'b'}}
485
+ let(:size_with_headers) { 18 }
486
+ let(:size_without_headers) { 17 }
487
+
488
+ context 'with headers' do
489
+ let(:file_name) {'test.xlsx|Expenses'}
490
+
491
+ context 'well-formed' do
492
+
493
+ let(:required_headers) {%w'Date Amount'}
494
+
495
+ it 'opens correctly' do
496
+ expect {ss}.not_to raise_error
497
+ end
498
+
499
+ it 'contains expected headers' do
500
+ required_headers.each do |header|
501
+ expect(ss.headers).to include header
502
+ end
503
+ expect(ss.headers).to eq real_headers
504
+ end
505
+
506
+ it 'each returns header and data rows' do
507
+ expect(ss.each.count).to eq size_with_headers
508
+ expect(ss.each.first).to eq header_row
509
+ end
510
+
511
+ it '#shift returns Hash object' do
512
+ row = ss.shift
513
+ expect(row).to be_a Hash
514
+ expect(row['Date']).to eq first_data_row['Date']
515
+ expect(row['Amount']).to eq first_data_row['Amount']
516
+ expect(row['Code']).to eq first_data_row['Code']
517
+ expect(row['Remark']).to eq first_data_row['Remark']
518
+ expect(row['dummy']).to be_nil
519
+ end
520
+
521
+ it '#parse returns Array of Hash objects' do
522
+ rows = ss.parse
523
+ expect(rows).to be_a Array
524
+ expect(rows.size).to eq size_without_headers
525
+ row = rows[0]
526
+ expect(row).to be_a Hash
527
+ expect(row['Date']).to eq first_data_row['Date']
528
+ expect(row['Amount']).to eq first_data_row['Amount']
529
+ expect(row['Code']).to eq first_data_row['Code']
530
+ expect(row['Remark']).to eq first_data_row['Remark']
531
+ expect(row['dummy']).to be_nil
532
+ row = rows[13]
533
+ expect(row).to be_a Hash
534
+ expect(row['Date']).to eq data_row_13['Date']
535
+ expect(row['Amount']).to eq data_row_13['Amount']
536
+ expect(row['Code']).to eq data_row_13['Code']
537
+ expect(row['Remark']).to eq data_row_13['Remark']
538
+ expect(row['dummy']).to be_nil
539
+ end
540
+
541
+ end
542
+
543
+ context 'not specified' do
544
+
545
+ let(:required_headers) {[]}
546
+
547
+
548
+ it 'opens correctly' do
549
+ expect {ss}.not_to raise_error
550
+ end
551
+
552
+ it 'contains expected headers' do
553
+ required_headers.each do |header|
554
+ expect(ss.headers).to include header
555
+ end
556
+ expect(ss.headers).to eq real_headers
557
+ end
558
+
559
+ it 'each returns header and data rows' do
560
+ expect(ss.each.count).to eq size_with_headers
561
+ expect(ss.each.first).to eq header_row
562
+ end
563
+
564
+ it '#shift returns Hash object' do
565
+ row = ss.shift
566
+ expect(row).to be_a Hash
567
+ expect(row['Date']).to eq first_data_row['Date']
568
+ expect(row['Amount']).to eq first_data_row['Amount']
569
+ expect(row['Code']).to eq first_data_row['Code']
570
+ expect(row['Remark']).to eq first_data_row['Remark']
571
+ expect(row['dummy']).to be_nil
572
+ end
573
+
574
+ it '#parse returns Array of Hash objects' do
575
+ rows = ss.parse
576
+ expect(rows).to be_a Array
577
+ expect(rows.size).to eq size_without_headers
578
+ row = rows[0]
579
+ expect(row).to be_a Hash
580
+ expect(row['Date']).to eq first_data_row['Date']
581
+ expect(row['Amount']).to eq first_data_row['Amount']
582
+ expect(row['Code']).to eq first_data_row['Code']
583
+ expect(row['Remark']).to eq first_data_row['Remark']
584
+ expect(row['dummy']).to be_nil
585
+ row = rows[13]
586
+ expect(row).to be_a Hash
587
+ expect(row['Date']).to eq data_row_13['Date']
588
+ expect(row['Amount']).to eq data_row_13['Amount']
589
+ expect(row['Code']).to eq data_row_13['Code']
590
+ expect(row['Remark']).to eq data_row_13['Remark']
591
+ expect(row['dummy']).to be_nil
592
+ end
593
+
594
+ end
595
+
596
+ context 'not well-formed' do
597
+
598
+ let(:required_headers) {%w'Date dummy1 Amount dummy2'}
599
+
600
+ it 'throws error when opened' do
601
+ expect {ss}.to raise_error(RuntimeError, 'Headers not found: ["dummy1", "dummy2"].')
602
+ end
603
+ end
604
+
605
+ end
606
+
607
+ context 'without headers' do
608
+ let(:file_name) {'test.xlsx|ExpensesNoHeaders'}
609
+
610
+ context 'well-formed and strict' do
611
+ let(:required_headers) {%w'Date Amount'}
612
+
613
+ it 'opens correctly' do
614
+ expect {ss}.not_to raise_error
615
+ end
616
+
617
+ it 'contains only required headers' do
618
+ required_headers.each do |header|
619
+ expect(ss.headers).to include header
620
+ end
621
+ expect(ss.headers).to eq required_headers
622
+ end
623
+
624
+ it 'each returns header and data rows' do
625
+ expect(ss.each.count).to eq size_with_headers
626
+ expect(ss.each.first.keys).to eq required_headers
627
+ end
628
+
629
+ it '#shift returns Hash object' do
630
+ row = ss.shift
631
+ expect(row).to be_a Hash
632
+ expect(row['Date']).to eq first_data_row['Date']
633
+ expect(row['Amount']).to eq first_data_row['Amount']
634
+ expect(row['Code']).to be_nil
635
+ expect(row['Remark']).to be_nil
636
+ expect(row['dummy']).to be_nil
637
+ end
638
+
639
+ it '#parse returns Array of Hash objects' do
640
+ rows = ss.parse
641
+ expect(rows).to be_a Array
642
+ expect(rows.size).to eq size_without_headers
643
+ row = rows[0]
644
+ expect(row).to be_a Hash
645
+ expect(row['Date']).to eq first_data_row['Date']
646
+ expect(row['Amount']).to eq first_data_row['Amount']
647
+ expect(row['Code']).to be_nil
648
+ expect(row['Remark']).to be_nil
649
+ expect(row['dummy']).to be_nil
650
+ row = rows[13]
651
+ expect(row).to be_a Hash
652
+ expect(row['Date']).to eq data_row_13['Date']
653
+ expect(row['Amount']).to eq data_row_13['Amount']
654
+ expect(row['Code']).to be_nil
655
+ expect(row['Remark']).to be_nil
656
+ expect(row['dummy']).to be_nil
657
+ end
658
+
659
+ end
660
+
661
+ context 'well-formed with optional headers' do
662
+ let(:required_headers) {%w'Date Amount'}
663
+ let(:optional_headers) {%w'Code'}
664
+
665
+ it 'opens correctly' do
666
+ expect {ss}.not_to raise_error
667
+ end
668
+
669
+ it 'contains required and optional headers' do
670
+ required_headers.each do |header|
671
+ expect(ss.headers).to include header
672
+ end
673
+ optional_headers.each do |header|
674
+ expect(ss.headers).to include header
675
+ end
676
+ expect(ss.headers).to eq required_headers + optional_headers
677
+ end
678
+
679
+ it 'each returns header and data rows' do
680
+ expect(ss.each.count).to eq size_with_headers
681
+ expect(ss.each.first.keys).to eq required_headers + optional_headers
682
+ end
683
+
684
+ it '#shift returns Hash object' do
685
+ row = ss.shift
686
+ expect(row).to be_a Hash
687
+ expect(row['Date']).to eq first_data_row['Date']
688
+ expect(row['Amount']).to eq first_data_row['Amount']
689
+ expect(row['Code']).to eq first_data_row['Code']
690
+ expect(row['Remark']).to be_nil
691
+ expect(row['dummy']).to be_nil
692
+ end
693
+
694
+ it '#parse returns Array of Hash objects' do
695
+ rows = ss.parse
696
+ expect(rows).to be_a Array
697
+ expect(rows.size).to eq size_without_headers
698
+ row = rows[0]
699
+ expect(row).to be_a Hash
700
+ expect(row['Date']).to eq first_data_row['Date']
701
+ expect(row['Amount']).to eq first_data_row['Amount']
702
+ expect(row['Code']).to eq first_data_row['Code']
703
+ expect(row['Remark']).to be_nil
704
+ expect(row['dummy']).to be_nil
705
+ row = rows[13]
706
+ expect(row).to be_a Hash
707
+ expect(row['Date']).to eq data_row_13['Date']
708
+ expect(row['Amount']).to eq data_row_13['Amount']
709
+ expect(row['Code']).to eq data_row_13['Code']
710
+ expect(row['Remark']).to be_nil
711
+ expect(row['dummy']).to be_nil
712
+ end
713
+
714
+ end
715
+
716
+ context 'missing optional headers' do
717
+
718
+ let(:required_headers) {%w'Date Amount Code Remark'}
719
+ let(:optional_headers) {%w'dummy'}
720
+
721
+ it 'opens correctly' do
722
+ expect {ss}.not_to raise_error
723
+ end
724
+
725
+ it 'contains only required headers' do
726
+ required_headers.each do |header|
727
+ expect(ss.headers).to include header
728
+ end
729
+ optional_headers.each do |header|
730
+ expect(ss.headers).not_to include header
731
+ end
732
+ expect(ss.headers).to eq required_headers
733
+ end
734
+
735
+ it 'each returns header and data rows' do
736
+ expect(ss.each.count).to eq size_with_headers
737
+ expect(ss.each.first.keys).to eq required_headers
738
+ end
739
+
740
+ it '#shift returns Hash object' do
741
+ row = ss.shift
742
+ expect(row).to be_a Hash
743
+ expect(row['Date']).to eq first_data_row['Date']
744
+ expect(row['Amount']).to eq first_data_row['Amount']
745
+ expect(row['Code']).to eq first_data_row['Code']
746
+ expect(row['Remark']).to eq first_data_row['Remark']
747
+ expect(row['dummy']).to be_nil
748
+ end
749
+
750
+ it '#parse returns Array of Hash objects' do
751
+ rows = ss.parse
752
+ expect(rows).to be_a Array
753
+ expect(rows.size).to eq size_without_headers
754
+ row = rows[0]
755
+ expect(row).to be_a Hash
756
+ expect(row['Date']).to eq first_data_row['Date']
757
+ expect(row['Amount']).to eq first_data_row['Amount']
758
+ expect(row['Code']).to eq first_data_row['Code']
759
+ expect(row['Remark']).to eq first_data_row['Remark']
760
+ expect(row['dummy']).to be_nil
761
+ row = rows[13]
762
+ expect(row).to be_a Hash
763
+ expect(row['Date']).to eq data_row_13['Date']
764
+ expect(row['Amount']).to eq data_row_13['Amount']
765
+ expect(row['Code']).to eq data_row_13['Code']
766
+ expect(row['Remark']).to eq data_row_13['Remark']
767
+ expect(row['dummy']).to be_nil
768
+ end
769
+
770
+ end
771
+
772
+ context 'missing required header' do
773
+ let(:required_headers) {%w'Date Amount Code Remark dummy'}
774
+
775
+ it 'throws error when opened' do
776
+ expect {ss}.to raise_error(RuntimeError, 'Sheet does not contain enough columns.')
777
+ end
778
+
779
+ end
780
+
781
+ end
782
+
783
+ context 'blank rows with headers' do
784
+ let(:file_name) {'test.xlsx|ExpensesBlankRows'}
785
+
786
+ context 'well-formed' do
787
+
788
+ let(:required_headers) {%w'Date Amount'}
789
+
790
+ it 'opens correctly' do
791
+ expect {ss}.not_to raise_error
792
+ end
793
+
794
+ it 'contains expected headers' do
795
+ required_headers.each do |header|
796
+ expect(ss.headers).to include header
797
+ end
798
+ expect(ss.headers).to eq real_headers
799
+ end
800
+
801
+ it 'each returns header and data rows' do
802
+ expect(ss.each.count).to eq size_with_headers
803
+ expect(ss.each.first).to eq header_row
804
+ end
805
+
806
+ it '#shift returns Hash object' do
807
+ row = ss.shift
808
+ expect(row).to be_a Hash
809
+ expect(row['Date']).to eq first_data_row['Date']
810
+ expect(row['Amount']).to eq first_data_row['Amount']
811
+ expect(row['Code']).to eq first_data_row['Code']
812
+ expect(row['Remark']).to eq first_data_row['Remark']
813
+ expect(row['dummy']).to be_nil
814
+ end
815
+
816
+ it '#parse returns Array of Hash objects' do
817
+ rows = ss.parse
818
+ expect(rows).to be_a Array
819
+ expect(rows.size).to eq size_without_headers
820
+ row = rows[0]
821
+ expect(row).to be_a Hash
822
+ expect(row['Date']).to eq first_data_row['Date']
823
+ expect(row['Amount']).to eq first_data_row['Amount']
824
+ expect(row['Code']).to eq first_data_row['Code']
825
+ expect(row['Remark']).to eq first_data_row['Remark']
826
+ expect(row['dummy']).to be_nil
827
+ row = rows[13]
828
+ expect(row).to be_a Hash
829
+ expect(row['Date']).to eq data_row_13['Date']
830
+ expect(row['Amount']).to eq data_row_13['Amount']
831
+ expect(row['Code']).to eq data_row_13['Code']
832
+ expect(row['Remark']).to eq data_row_13['Remark']
833
+ expect(row['dummy']).to be_nil
834
+ end
835
+
836
+ end
837
+
838
+ context 'not specified' do
839
+
840
+ let(:required_headers) {[]}
841
+
842
+ it 'opens correctly' do
843
+ expect {ss}.not_to raise_error
844
+ end
845
+
846
+ it 'contains expected headers' do
847
+ required_headers.each do |header|
848
+ expect(ss.headers).to include header
849
+ end
850
+ expect(ss.headers).to eq real_headers
851
+ end
852
+
853
+ it 'each returns header and data rows' do
854
+ expect(ss.each.count).to eq size_with_headers
855
+ expect(ss.each.first).to eq header_row
856
+ end
857
+
858
+ it '#shift returns Hash object' do
859
+ row = ss.shift
860
+ expect(row).to be_a Hash
861
+ expect(row['Date']).to eq first_data_row['Date']
862
+ expect(row['Amount']).to eq first_data_row['Amount']
863
+ expect(row['Code']).to eq first_data_row['Code']
864
+ expect(row['Remark']).to eq first_data_row['Remark']
865
+ expect(row['dummy']).to be_nil
866
+ end
867
+
868
+ it '#parse returns Array of Hash objects' do
869
+ rows = ss.parse
870
+ expect(rows).to be_a Array
871
+ expect(rows.size).to eq size_without_headers
872
+ row = rows[0]
873
+ expect(row).to be_a Hash
874
+ expect(row['Date']).to eq first_data_row['Date']
875
+ expect(row['Amount']).to eq first_data_row['Amount']
876
+ expect(row['Code']).to eq first_data_row['Code']
877
+ expect(row['Remark']).to eq first_data_row['Remark']
878
+ expect(row['dummy']).to be_nil
879
+ row = rows[13]
880
+ expect(row).to be_a Hash
881
+ expect(row['Date']).to eq data_row_13['Date']
882
+ expect(row['Amount']).to eq data_row_13['Amount']
883
+ expect(row['Code']).to eq data_row_13['Code']
884
+ expect(row['Remark']).to eq data_row_13['Remark']
885
+ expect(row['dummy']).to be_nil
886
+ end
887
+
888
+ end
889
+
890
+ context 'not well-formed' do
891
+
892
+ let(:required_headers) {%w'Date dummy1 Amount dummy2'}
893
+
894
+ it 'throws error when opened' do
895
+ expect {ss}.to raise_error(RuntimeError, 'Headers not found: ["dummy1", "dummy2"].')
896
+ end
897
+ end
898
+
899
+ end
900
+
901
+ context 'blank rows without headers' do
902
+ let(:file_name) {'test.xlsx|ExpensesBlankRowsNoHeaders'}
903
+
904
+ context 'well-formed and strict' do
905
+ let(:required_headers) {%w'Date Amount'}
906
+
907
+ it 'opens correctly' do
908
+ expect {ss}.not_to raise_error
909
+ end
910
+
911
+ it 'contains only required headers' do
912
+ required_headers.each do |header|
913
+ expect(ss.headers).to include header
914
+ end
915
+ expect(ss.headers).to eq required_headers
916
+ end
917
+
918
+ it 'each returns header and data rows' do
919
+ expect(ss.each.count).to eq size_with_headers
920
+ expect(ss.each.first.keys).to eq required_headers
921
+ end
922
+
923
+ it '#shift returns Hash object' do
924
+ row = ss.shift
925
+ expect(row).to be_a Hash
926
+ expect(row['Date']).to eq first_data_row['Date']
927
+ expect(row['Amount']).to eq first_data_row['Amount']
928
+ expect(row['Code']).to be_nil
929
+ expect(row['Remark']).to be_nil
930
+ expect(row['dummy']).to be_nil
931
+ end
932
+
933
+ it '#parse returns Array of Hash objects' do
934
+ rows = ss.parse
935
+ expect(rows).to be_a Array
936
+ expect(rows.size).to eq size_without_headers
937
+ row = rows[0]
938
+ expect(row).to be_a Hash
939
+ expect(row['Date']).to eq first_data_row['Date']
940
+ expect(row['Amount']).to eq first_data_row['Amount']
941
+ expect(row['Code']).to be_nil
942
+ expect(row['Remark']).to be_nil
943
+ expect(row['dummy']).to be_nil
944
+ row = rows[13]
945
+ expect(row).to be_a Hash
946
+ expect(row['Date']).to eq data_row_13['Date']
947
+ expect(row['Amount']).to eq data_row_13['Amount']
948
+ expect(row['Code']).to be_nil
949
+ expect(row['Remark']).to be_nil
950
+ expect(row['dummy']).to be_nil
951
+ end
952
+
953
+ end
954
+
955
+ context 'well-formed with optional headers' do
956
+ let(:required_headers) {%w'Date Amount'}
957
+ let(:optional_headers) {%w'Code'}
958
+
959
+ it 'opens correctly' do
960
+ expect {ss}.not_to raise_error
961
+ end
962
+
963
+ it 'contains required and optional headers' do
964
+ required_headers.each do |header|
965
+ expect(ss.headers).to include header
966
+ end
967
+ optional_headers.each do |header|
968
+ expect(ss.headers).to include header
969
+ end
970
+ expect(ss.headers).to eq required_headers + optional_headers
971
+ end
972
+
973
+ it 'each returns header and data rows' do
974
+ expect(ss.each.count).to eq size_with_headers
975
+ expect(ss.each.first.keys).to eq required_headers + optional_headers
976
+ end
977
+
978
+ it '#shift returns Hash object' do
979
+ row = ss.shift
980
+ expect(row).to be_a Hash
981
+ expect(row['Date']).to eq first_data_row['Date']
982
+ expect(row['Amount']).to eq first_data_row['Amount']
983
+ expect(row['Code']).to eq first_data_row['Code']
984
+ expect(row['Remark']).to be_nil
985
+ expect(row['dummy']).to be_nil
986
+ end
987
+
988
+ it '#parse returns Array of Hash objects' do
989
+ rows = ss.parse
990
+ expect(rows).to be_a Array
991
+ expect(rows.size).to eq size_without_headers
992
+ row = rows[0]
993
+ expect(row).to be_a Hash
994
+ expect(row['Date']).to eq first_data_row['Date']
995
+ expect(row['Amount']).to eq first_data_row['Amount']
996
+ expect(row['Code']).to eq first_data_row['Code']
997
+ expect(row['Remark']).to be_nil
998
+ expect(row['dummy']).to be_nil
999
+ row = rows[13]
1000
+ expect(row).to be_a Hash
1001
+ expect(row['Date']).to eq data_row_13['Date']
1002
+ expect(row['Amount']).to eq data_row_13['Amount']
1003
+ expect(row['Code']).to eq data_row_13['Code']
1004
+ expect(row['Remark']).to be_nil
1005
+ expect(row['dummy']).to be_nil
1006
+ end
1007
+
1008
+ end
1009
+
1010
+ context 'missing optional headers' do
1011
+
1012
+ let(:required_headers) {%w'Date Amount Code Remark'}
1013
+ let(:optional_headers) {%w'dummy'}
1014
+
1015
+ it 'opens correctly' do
1016
+ expect {ss}.not_to raise_error
1017
+ end
1018
+
1019
+ it 'contains only required headers' do
1020
+ required_headers.each do |header|
1021
+ expect(ss.headers).to include header
1022
+ end
1023
+ optional_headers.each do |header|
1024
+ expect(ss.headers).not_to include header
1025
+ end
1026
+ expect(ss.headers).to eq required_headers
1027
+ end
1028
+
1029
+ it 'each returns header and data rows' do
1030
+ expect(ss.each.count).to eq size_with_headers
1031
+ expect(ss.each.first.keys).to eq required_headers
1032
+ end
1033
+
1034
+ it '#shift returns Hash object' do
1035
+ row = ss.shift
1036
+ expect(row).to be_a Hash
1037
+ expect(row['Date']).to eq first_data_row['Date']
1038
+ expect(row['Amount']).to eq first_data_row['Amount']
1039
+ expect(row['Code']).to eq first_data_row['Code']
1040
+ expect(row['Remark']).to eq first_data_row['Remark']
1041
+ expect(row['dummy']).to be_nil
1042
+ end
1043
+
1044
+ it '#parse returns Array of Hash objects' do
1045
+ rows = ss.parse
1046
+ expect(rows).to be_a Array
1047
+ expect(rows.size).to eq size_without_headers
1048
+ row = rows[0]
1049
+ expect(row).to be_a Hash
1050
+ expect(row['Date']).to eq first_data_row['Date']
1051
+ expect(row['Amount']).to eq first_data_row['Amount']
1052
+ expect(row['Code']).to eq first_data_row['Code']
1053
+ expect(row['Remark']).to eq first_data_row['Remark']
1054
+ expect(row['dummy']).to be_nil
1055
+ row = rows[13]
1056
+ expect(row).to be_a Hash
1057
+ expect(row['Date']).to eq data_row_13['Date']
1058
+ expect(row['Amount']).to eq data_row_13['Amount']
1059
+ expect(row['Code']).to eq data_row_13['Code']
1060
+ expect(row['Remark']).to eq data_row_13['Remark']
1061
+ expect(row['dummy']).to be_nil
1062
+ end
1063
+
1064
+ end
1065
+
1066
+ context 'missing required header' do
1067
+ let(:required_headers) {%w'Date Amount Code Remark dummy'}
1068
+
1069
+ it 'throws error when opened' do
1070
+ expect {ss}.to raise_error(RuntimeError, 'Sheet does not contain enough columns.')
1071
+ end
1072
+
1073
+ end
1074
+
1075
+ end
1076
+
1077
+ context 'blank columns with headers' do
1078
+ let(:file_name) {'test.xlsx|ExpensesBlankColumns'}
1079
+
1080
+ context 'well-formed' do
1081
+
1082
+ let(:required_headers) {%w'Date Amount'}
1083
+
1084
+ it 'opens correctly' do
1085
+ expect {ss}.not_to raise_error
1086
+ end
1087
+
1088
+ it 'contains expected headers' do
1089
+ required_headers.each do |header|
1090
+ expect(ss.headers).to include header
1091
+ end
1092
+ expect(ss.headers).to eq real_headers
1093
+ end
1094
+
1095
+ it 'each returns header and data rows' do
1096
+ expect(ss.each.count).to eq size_with_headers
1097
+ expect(ss.each.first).to eq header_row
1098
+ end
1099
+
1100
+ it '#shift returns Hash object' do
1101
+ row = ss.shift
1102
+ expect(row).to be_a Hash
1103
+ expect(row['Date']).to eq first_data_row['Date']
1104
+ expect(row['Amount']).to eq first_data_row['Amount']
1105
+ expect(row['Code']).to eq first_data_row['Code']
1106
+ expect(row['Remark']).to eq first_data_row['Remark']
1107
+ expect(row['dummy']).to be_nil
1108
+ end
1109
+
1110
+ it '#parse returns Array of Hash objects' do
1111
+ rows = ss.parse
1112
+ expect(rows).to be_a Array
1113
+ expect(rows.size).to eq size_without_headers
1114
+ row = rows[0]
1115
+ expect(row).to be_a Hash
1116
+ expect(row['Date']).to eq first_data_row['Date']
1117
+ expect(row['Amount']).to eq first_data_row['Amount']
1118
+ expect(row['Code']).to eq first_data_row['Code']
1119
+ expect(row['Remark']).to eq first_data_row['Remark']
1120
+ expect(row['dummy']).to be_nil
1121
+ row = rows[13]
1122
+ expect(row).to be_a Hash
1123
+ expect(row['Date']).to eq data_row_13['Date']
1124
+ expect(row['Amount']).to eq data_row_13['Amount']
1125
+ expect(row['Code']).to eq data_row_13['Code']
1126
+ expect(row['Remark']).to eq data_row_13['Remark']
1127
+ expect(row['dummy']).to be_nil
1128
+ end
1129
+
1130
+ end
1131
+
1132
+ context 'not specified' do
1133
+
1134
+ let(:required_headers) {[]}
1135
+
1136
+ it 'opens correctly' do
1137
+ expect {ss}.not_to raise_error
1138
+ end
1139
+
1140
+ it 'contains expected headers' do
1141
+ required_headers.each do |header|
1142
+ expect(ss.headers).to include header
1143
+ end
1144
+ expect(ss.headers).to eq real_headers
1145
+ end
1146
+
1147
+ it 'each returns header and data rows' do
1148
+ expect(ss.each.count).to eq size_with_headers
1149
+ expect(ss.each.first).to eq header_row
1150
+ end
1151
+
1152
+ it '#shift returns Hash object' do
1153
+ row = ss.shift
1154
+ expect(row).to be_a Hash
1155
+ expect(row['Date']).to eq first_data_row['Date']
1156
+ expect(row['Amount']).to eq first_data_row['Amount']
1157
+ expect(row['Code']).to eq first_data_row['Code']
1158
+ expect(row['Remark']).to eq first_data_row['Remark']
1159
+ expect(row['dummy']).to be_nil
1160
+ end
1161
+
1162
+ it '#parse returns Array of Hash objects' do
1163
+ rows = ss.parse
1164
+ expect(rows).to be_a Array
1165
+ expect(rows.size).to eq size_without_headers
1166
+ row = rows[0]
1167
+ expect(row).to be_a Hash
1168
+ expect(row['Date']).to eq first_data_row['Date']
1169
+ expect(row['Amount']).to eq first_data_row['Amount']
1170
+ expect(row['Code']).to eq first_data_row['Code']
1171
+ expect(row['Remark']).to eq first_data_row['Remark']
1172
+ expect(row['dummy']).to be_nil
1173
+ row = rows[13]
1174
+ expect(row).to be_a Hash
1175
+ expect(row['Date']).to eq data_row_13['Date']
1176
+ expect(row['Amount']).to eq data_row_13['Amount']
1177
+ expect(row['Code']).to eq data_row_13['Code']
1178
+ expect(row['Remark']).to eq data_row_13['Remark']
1179
+ expect(row['dummy']).to be_nil
1180
+ end
1181
+
1182
+ end
1183
+
1184
+ context 'not well-formed' do
1185
+
1186
+ let(:required_headers) {%w'Date dummy1 Amount dummy2'}
1187
+
1188
+ it 'throws error when opened' do
1189
+ expect {ss}.to raise_error(RuntimeError, 'Headers not found: ["dummy1", "dummy2"].')
1190
+ end
1191
+ end
1192
+
1193
+ end
1194
+
1195
+ context 'blank columns without headers' do
1196
+ let(:file_name) {'test.xlsx|ExpensesBlankColumnsNoHeaders'}
1197
+
1198
+ context 'well-formed and strict' do
1199
+ let(:required_headers) {%w'Date Amount'}
1200
+
1201
+ it 'opens correctly' do
1202
+ expect {ss}.not_to raise_error
1203
+ end
1204
+
1205
+ it 'contains only required headers' do
1206
+ required_headers.each do |header|
1207
+ expect(ss.headers).to include header
1208
+ end
1209
+ expect(ss.headers).to eq required_headers
1210
+ end
1211
+
1212
+ it 'each returns header and data rows' do
1213
+ expect(ss.each.count).to eq size_with_headers
1214
+ expect(ss.each.first.keys).to eq required_headers
1215
+ end
1216
+
1217
+ it '#shift returns Hash object' do
1218
+ row = ss.shift
1219
+ expect(row).to be_a Hash
1220
+ expect(row['Date']).to eq first_data_row['Date']
1221
+ expect(row['Amount']).to eq first_data_row['Amount']
1222
+ expect(row['Code']).to be_nil
1223
+ expect(row['Remark']).to be_nil
1224
+ expect(row['dummy']).to be_nil
1225
+ end
1226
+
1227
+ it '#parse returns Array of Hash objects' do
1228
+ rows = ss.parse
1229
+ expect(rows).to be_a Array
1230
+ expect(rows.size).to eq size_without_headers
1231
+ row = rows[0]
1232
+ expect(row).to be_a Hash
1233
+ expect(row['Date']).to eq first_data_row['Date']
1234
+ expect(row['Amount']).to eq first_data_row['Amount']
1235
+ expect(row['Code']).to be_nil
1236
+ expect(row['Remark']).to be_nil
1237
+ expect(row['dummy']).to be_nil
1238
+ row = rows[13]
1239
+ expect(row).to be_a Hash
1240
+ expect(row['Date']).to eq data_row_13['Date']
1241
+ expect(row['Amount']).to eq data_row_13['Amount']
1242
+ expect(row['Code']).to be_nil
1243
+ expect(row['Remark']).to be_nil
1244
+ expect(row['dummy']).to be_nil
1245
+ end
1246
+
1247
+ end
1248
+
1249
+ context 'well-formed with optional headers' do
1250
+ let(:required_headers) {%w'Date Amount'}
1251
+ let(:optional_headers) {%w'Code'}
1252
+
1253
+ it 'opens correctly' do
1254
+ expect {ss}.not_to raise_error
1255
+ end
1256
+
1257
+ it 'contains required and optional headers' do
1258
+ required_headers.each do |header|
1259
+ expect(ss.headers).to include header
1260
+ end
1261
+ optional_headers.each do |header|
1262
+ expect(ss.headers).to include header
1263
+ end
1264
+ expect(ss.headers).to eq required_headers + optional_headers
1265
+ end
1266
+
1267
+ it 'each returns header and data rows' do
1268
+ expect(ss.each.count).to eq size_with_headers
1269
+ expect(ss.each.first.keys).to eq required_headers + optional_headers
1270
+ end
1271
+
1272
+ it '#shift returns Hash object' do
1273
+ row = ss.shift
1274
+ expect(row).to be_a Hash
1275
+ expect(row['Date']).to eq first_data_row['Date']
1276
+ expect(row['Amount']).to eq first_data_row['Amount']
1277
+ expect(row['Code']).to eq first_data_row['Code']
1278
+ expect(row['Remark']).to be_nil
1279
+ expect(row['dummy']).to be_nil
1280
+ end
1281
+
1282
+ it '#parse returns Array of Hash objects' do
1283
+ rows = ss.parse
1284
+ expect(rows).to be_a Array
1285
+ expect(rows.size).to eq size_without_headers
1286
+ row = rows[0]
1287
+ expect(row).to be_a Hash
1288
+ expect(row['Date']).to eq first_data_row['Date']
1289
+ expect(row['Amount']).to eq first_data_row['Amount']
1290
+ expect(row['Code']).to eq first_data_row['Code']
1291
+ expect(row['Remark']).to be_nil
1292
+ expect(row['dummy']).to be_nil
1293
+ row = rows[13]
1294
+ expect(row).to be_a Hash
1295
+ expect(row['Date']).to eq data_row_13['Date']
1296
+ expect(row['Amount']).to eq data_row_13['Amount']
1297
+ expect(row['Code']).to eq data_row_13['Code']
1298
+ expect(row['Remark']).to be_nil
1299
+ expect(row['dummy']).to be_nil
1300
+ end
1301
+
1302
+ end
1303
+
1304
+ context 'missing optional headers' do
1305
+
1306
+ let(:required_headers) {%w'Date Amount Code Remark'}
1307
+ let(:optional_headers) {%w'dummy'}
1308
+
1309
+ it 'opens correctly' do
1310
+ expect {ss}.not_to raise_error
1311
+ end
1312
+
1313
+ it 'contains only required headers' do
1314
+ required_headers.each do |header|
1315
+ expect(ss.headers).to include header
1316
+ end
1317
+ optional_headers.each do |header|
1318
+ expect(ss.headers).not_to include header
1319
+ end
1320
+ expect(ss.headers).to eq required_headers
1321
+ end
1322
+
1323
+ it 'each returns header and data rows' do
1324
+ expect(ss.each.count).to eq size_with_headers
1325
+ expect(ss.each.first.keys).to eq required_headers
1326
+ end
1327
+
1328
+ it '#shift returns Hash object' do
1329
+ row = ss.shift
1330
+ expect(row).to be_a Hash
1331
+ expect(row['Date']).to eq first_data_row['Date']
1332
+ expect(row['Amount']).to eq first_data_row['Amount']
1333
+ expect(row['Code']).to eq first_data_row['Code']
1334
+ expect(row['Remark']).to eq first_data_row['Remark']
1335
+ expect(row['dummy']).to be_nil
1336
+ end
1337
+
1338
+ it '#parse returns Array of Hash objects' do
1339
+ rows = ss.parse
1340
+ expect(rows).to be_a Array
1341
+ expect(rows.size).to eq size_without_headers
1342
+ row = rows[0]
1343
+ expect(row).to be_a Hash
1344
+ expect(row['Date']).to eq first_data_row['Date']
1345
+ expect(row['Amount']).to eq first_data_row['Amount']
1346
+ expect(row['Code']).to eq first_data_row['Code']
1347
+ expect(row['Remark']).to eq first_data_row['Remark']
1348
+ expect(row['dummy']).to be_nil
1349
+ row = rows[13]
1350
+ expect(row).to be_a Hash
1351
+ expect(row['Date']).to eq data_row_13['Date']
1352
+ expect(row['Amount']).to eq data_row_13['Amount']
1353
+ expect(row['Code']).to eq data_row_13['Code']
1354
+ expect(row['Remark']).to eq data_row_13['Remark']
1355
+ expect(row['dummy']).to be_nil
1356
+ end
1357
+
1358
+ end
1359
+
1360
+ context 'missing required header' do
1361
+ let(:required_headers) {%w'Date Amount Code Remark dummy'}
1362
+
1363
+ it 'throws error when opened' do
1364
+ expect {ss}.to raise_error(RuntimeError, 'Sheet does not contain enough columns.')
1365
+ end
1366
+
1367
+ end
1368
+
1369
+ end
1370
+
1371
+ context 'blank row and columns with headers' do
1372
+ let(:file_name) {'test.xlsx|ExpensesBlankRowsAndColumns'}
1373
+
1374
+ context 'well-formed' do
1375
+
1376
+ let(:required_headers) {%w'Date Amount'}
1377
+
1378
+ it 'opens correctly' do
1379
+ expect {ss}.not_to raise_error
1380
+ end
1381
+
1382
+ it 'contains expected headers' do
1383
+ required_headers.each do |header|
1384
+ expect(ss.headers).to include header
1385
+ end
1386
+ expect(ss.headers).to eq real_headers
1387
+ end
1388
+
1389
+ it 'each returns header and data rows' do
1390
+ expect(ss.each.count).to eq size_with_headers
1391
+ expect(ss.each.first).to eq header_row
1392
+ end
1393
+
1394
+ it '#shift returns Hash object' do
1395
+ row = ss.shift
1396
+ expect(row).to be_a Hash
1397
+ expect(row['Date']).to eq first_data_row['Date']
1398
+ expect(row['Amount']).to eq first_data_row['Amount']
1399
+ expect(row['Code']).to eq first_data_row['Code']
1400
+ expect(row['Remark']).to eq first_data_row['Remark']
1401
+ expect(row['dummy']).to be_nil
1402
+ end
1403
+
1404
+ it '#parse returns Array of Hash objects' do
1405
+ rows = ss.parse
1406
+ expect(rows).to be_a Array
1407
+ expect(rows.size).to eq size_without_headers
1408
+ row = rows[0]
1409
+ expect(row).to be_a Hash
1410
+ expect(row['Date']).to eq first_data_row['Date']
1411
+ expect(row['Amount']).to eq first_data_row['Amount']
1412
+ expect(row['Code']).to eq first_data_row['Code']
1413
+ expect(row['Remark']).to eq first_data_row['Remark']
1414
+ expect(row['dummy']).to be_nil
1415
+ row = rows[13]
1416
+ expect(row).to be_a Hash
1417
+ expect(row['Date']).to eq data_row_13['Date']
1418
+ expect(row['Amount']).to eq data_row_13['Amount']
1419
+ expect(row['Code']).to eq data_row_13['Code']
1420
+ expect(row['Remark']).to eq data_row_13['Remark']
1421
+ expect(row['dummy']).to be_nil
1422
+ end
1423
+
1424
+ end
1425
+
1426
+ context 'not specified' do
1427
+
1428
+ let(:required_headers) {[]}
1429
+
1430
+ it 'opens correctly' do
1431
+ expect {ss}.not_to raise_error
1432
+ end
1433
+
1434
+ it 'contains expected headers' do
1435
+ required_headers.each do |header|
1436
+ expect(ss.headers).to include header
1437
+ end
1438
+ expect(ss.headers).to eq real_headers
1439
+ end
1440
+
1441
+ it 'each returns header and data rows' do
1442
+ expect(ss.each.count).to eq size_with_headers
1443
+ expect(ss.each.first).to eq header_row
1444
+ end
1445
+
1446
+ it '#shift returns Hash object' do
1447
+ row = ss.shift
1448
+ expect(row).to be_a Hash
1449
+ expect(row['Date']).to eq first_data_row['Date']
1450
+ expect(row['Amount']).to eq first_data_row['Amount']
1451
+ expect(row['Code']).to eq first_data_row['Code']
1452
+ expect(row['Remark']).to eq first_data_row['Remark']
1453
+ expect(row['dummy']).to be_nil
1454
+ end
1455
+
1456
+ it '#parse returns Array of Hash objects' do
1457
+ rows = ss.parse
1458
+ expect(rows).to be_a Array
1459
+ expect(rows.size).to eq size_without_headers
1460
+ row = rows[0]
1461
+ expect(row).to be_a Hash
1462
+ expect(row['Date']).to eq first_data_row['Date']
1463
+ expect(row['Amount']).to eq first_data_row['Amount']
1464
+ expect(row['Code']).to eq first_data_row['Code']
1465
+ expect(row['Remark']).to eq first_data_row['Remark']
1466
+ expect(row['dummy']).to be_nil
1467
+ row = rows[13]
1468
+ expect(row).to be_a Hash
1469
+ expect(row['Date']).to eq data_row_13['Date']
1470
+ expect(row['Amount']).to eq data_row_13['Amount']
1471
+ expect(row['Code']).to eq data_row_13['Code']
1472
+ expect(row['Remark']).to eq data_row_13['Remark']
1473
+ expect(row['dummy']).to be_nil
1474
+ end
1475
+
1476
+ end
1477
+
1478
+ context 'not well-formed' do
1479
+
1480
+ let(:required_headers) {%w'Date dummy1 Amount dummy2'}
1481
+
1482
+ it 'throws error when opened' do
1483
+ expect {ss}.to raise_error(RuntimeError, 'Headers not found: ["dummy1", "dummy2"].')
1484
+ end
1485
+ end
1486
+
1487
+ end
1488
+
1489
+ context 'blank row and columns without headers' do
1490
+ let(:file_name) {'test.xlsx|ExpensesBlankRowsAndColumnsNoH'}
1491
+
1492
+ context 'well-formed and strict' do
1493
+ let(:required_headers) {%w'Date Amount'}
1494
+
1495
+ it 'opens correctly' do
1496
+ expect {ss}.not_to raise_error
1497
+ end
1498
+
1499
+ it 'contains only required headers' do
1500
+ required_headers.each do |header|
1501
+ expect(ss.headers).to include header
1502
+ end
1503
+ expect(ss.headers).to eq required_headers
1504
+ end
1505
+
1506
+ it 'each returns header and data rows' do
1507
+ expect(ss.each.count).to eq size_with_headers
1508
+ expect(ss.each.first.keys).to eq required_headers
1509
+ end
1510
+
1511
+ it '#shift returns Hash object' do
1512
+ row = ss.shift
1513
+ expect(row).to be_a Hash
1514
+ expect(row['Date']).to eq first_data_row['Date']
1515
+ expect(row['Amount']).to eq first_data_row['Amount']
1516
+ expect(row['Code']).to be_nil
1517
+ expect(row['Remark']).to be_nil
1518
+ expect(row['dummy']).to be_nil
1519
+ end
1520
+
1521
+ it '#parse returns Array of Hash objects' do
1522
+ rows = ss.parse
1523
+ expect(rows).to be_a Array
1524
+ expect(rows.size).to eq size_without_headers
1525
+ row = rows[0]
1526
+ expect(row).to be_a Hash
1527
+ expect(row['Date']).to eq first_data_row['Date']
1528
+ expect(row['Amount']).to eq first_data_row['Amount']
1529
+ expect(row['Code']).to be_nil
1530
+ expect(row['Remark']).to be_nil
1531
+ expect(row['dummy']).to be_nil
1532
+ row = rows[13]
1533
+ expect(row).to be_a Hash
1534
+ expect(row['Date']).to eq data_row_13['Date']
1535
+ expect(row['Amount']).to eq data_row_13['Amount']
1536
+ expect(row['Code']).to be_nil
1537
+ expect(row['Remark']).to be_nil
1538
+ expect(row['dummy']).to be_nil
1539
+ end
1540
+
1541
+ end
1542
+
1543
+ context 'well-formed with optional headers' do
1544
+ let(:required_headers) {%w'Date Amount'}
1545
+ let(:optional_headers) {%w'Code'}
1546
+
1547
+ it 'opens correctly' do
1548
+ expect {ss}.not_to raise_error
1549
+ end
1550
+
1551
+ it 'contains required and optional headers' do
1552
+ required_headers.each do |header|
1553
+ expect(ss.headers).to include header
1554
+ end
1555
+ optional_headers.each do |header|
1556
+ expect(ss.headers).to include header
1557
+ end
1558
+ expect(ss.headers).to eq required_headers + optional_headers
1559
+ end
1560
+
1561
+ it 'each returns header and data rows' do
1562
+ expect(ss.each.count).to eq size_with_headers
1563
+ expect(ss.each.first.keys).to eq required_headers + optional_headers
1564
+ end
1565
+
1566
+ it '#shift returns Hash object' do
1567
+ row = ss.shift
1568
+ expect(row).to be_a Hash
1569
+ expect(row['Date']).to eq first_data_row['Date']
1570
+ expect(row['Amount']).to eq first_data_row['Amount']
1571
+ expect(row['Code']).to eq first_data_row['Code']
1572
+ expect(row['Remark']).to be_nil
1573
+ expect(row['dummy']).to be_nil
1574
+ end
1575
+
1576
+ it '#parse returns Array of Hash objects' do
1577
+ rows = ss.parse
1578
+ expect(rows).to be_a Array
1579
+ expect(rows.size).to eq size_without_headers
1580
+ row = rows[0]
1581
+ expect(row).to be_a Hash
1582
+ expect(row['Date']).to eq first_data_row['Date']
1583
+ expect(row['Amount']).to eq first_data_row['Amount']
1584
+ expect(row['Code']).to eq first_data_row['Code']
1585
+ expect(row['Remark']).to be_nil
1586
+ expect(row['dummy']).to be_nil
1587
+ row = rows[13]
1588
+ expect(row).to be_a Hash
1589
+ expect(row['Date']).to eq data_row_13['Date']
1590
+ expect(row['Amount']).to eq data_row_13['Amount']
1591
+ expect(row['Code']).to eq data_row_13['Code']
1592
+ expect(row['Remark']).to be_nil
1593
+ expect(row['dummy']).to be_nil
1594
+ end
1595
+
1596
+ end
1597
+
1598
+ context 'missing optional headers' do
1599
+
1600
+ let(:required_headers) {%w'Date Amount Code Remark'}
1601
+ let(:optional_headers) {%w'dummy'}
1602
+
1603
+ it 'opens correctly' do
1604
+ expect {ss}.not_to raise_error
1605
+ end
1606
+
1607
+ it 'contains only required headers' do
1608
+ required_headers.each do |header|
1609
+ expect(ss.headers).to include header
1610
+ end
1611
+ optional_headers.each do |header|
1612
+ expect(ss.headers).not_to include header
1613
+ end
1614
+ expect(ss.headers).to eq required_headers
1615
+ end
1616
+
1617
+ it 'each returns header and data rows' do
1618
+ expect(ss.each.count).to eq size_with_headers
1619
+ expect(ss.each.first.keys).to eq required_headers
1620
+ end
1621
+
1622
+ it '#shift returns Hash object' do
1623
+ row = ss.shift
1624
+ expect(row).to be_a Hash
1625
+ expect(row['Date']).to eq first_data_row['Date']
1626
+ expect(row['Amount']).to eq first_data_row['Amount']
1627
+ expect(row['Code']).to eq first_data_row['Code']
1628
+ expect(row['Remark']).to eq first_data_row['Remark']
1629
+ expect(row['dummy']).to be_nil
1630
+ end
1631
+
1632
+ it '#parse returns Array of Hash objects' do
1633
+ rows = ss.parse
1634
+ expect(rows).to be_a Array
1635
+ expect(rows.size).to eq size_without_headers
1636
+ row = rows[0]
1637
+ expect(row).to be_a Hash
1638
+ expect(row['Date']).to eq first_data_row['Date']
1639
+ expect(row['Amount']).to eq first_data_row['Amount']
1640
+ expect(row['Code']).to eq first_data_row['Code']
1641
+ expect(row['Remark']).to eq first_data_row['Remark']
1642
+ expect(row['dummy']).to be_nil
1643
+ row = rows[13]
1644
+ expect(row).to be_a Hash
1645
+ expect(row['Date']).to eq data_row_13['Date']
1646
+ expect(row['Amount']).to eq data_row_13['Amount']
1647
+ expect(row['Code']).to eq data_row_13['Code']
1648
+ expect(row['Remark']).to eq data_row_13['Remark']
1649
+ expect(row['dummy']).to be_nil
1650
+ end
1651
+
1652
+ end
1653
+
1654
+ context 'missing required header' do
1655
+ let(:required_headers) {%w'Date Amount Code Remark dummy'}
1656
+
1657
+ it 'throws error when opened' do
1658
+ expect {ss}.to raise_error(RuntimeError, 'Sheet does not contain enough columns.')
1659
+ end
1660
+
1661
+ end
1662
+
1663
+ end
1664
+
1665
+ context 'Only headers' do
1666
+ let(:file_name) {'test.xlsx|ExpensesOnlyHeaders'}
1667
+
1668
+ context 'well-formed' do
1669
+
1670
+ let(:required_headers) {%w'Date Amount'}
1671
+
1672
+ it 'opens correctly' do
1673
+ expect {ss}.not_to raise_error
1674
+ end
1675
+
1676
+ it 'contains expected headers' do
1677
+ required_headers.each do |header|
1678
+ expect(ss.headers).to include header
1679
+ end
1680
+ expect(ss.headers).to eq real_headers
1681
+ end
1682
+
1683
+ it 'each returns header and data rows' do
1684
+ expect(ss.each.count).to be 1
1685
+ expect(ss.each.first).to eq header_row
1686
+ end
1687
+
1688
+ it '#shift returns nil' do
1689
+ row = ss.shift
1690
+ expect(row).to be_nil
1691
+ end
1692
+
1693
+ it '#parse returns empty Array of Hash objects' do
1694
+ rows = ss.parse
1695
+ expect(rows).to be_a Array
1696
+ # noinspection RubyResolve
1697
+ expect(rows).to be_empty
1698
+ expect(rows.size).to eq 0
1699
+ end
1700
+
1701
+ end
1702
+
1703
+ context 'not specified' do
1704
+
1705
+ let(:required_headers) {[]}
1706
+
1707
+ it 'opens correctly' do
1708
+ expect {ss}.not_to raise_error
1709
+ end
1710
+
1711
+ it 'contains expected headers' do
1712
+ required_headers.each do |header|
1713
+ expect(ss.headers).to include header
1714
+ end
1715
+ expect(ss.headers).to eq %w'Date Amount Code Remark'
1716
+ end
1717
+
1718
+ it '#shift returns nil' do
1719
+ row = ss.shift
1720
+ expect(row).to be_nil
1721
+ end
1722
+
1723
+ it '#parse returns empty Array of Hash objects' do
1724
+ rows = ss.parse
1725
+ expect(rows).to be_a Array
1726
+ # noinspection RubyResolve
1727
+ expect(rows).to be_empty
1728
+ expect(rows.size).to eq 0
1729
+ end
1730
+
1731
+ end
1732
+
1733
+ context 'not well-formed' do
1734
+
1735
+ let(:required_headers) {%w'Date dummy1 Amount dummy2'}
1736
+
1737
+ it 'throws error when opened' do
1738
+ expect {ss}.to raise_error(RuntimeError, 'Headers not found: ["dummy1", "dummy2"].')
1739
+ end
1740
+ end
1741
+
1742
+ end
1743
+
1744
+ context 'Only headers with blank rows and columns' do
1745
+ let(:file_name) {'test.xlsx|ExpensesOnlyHeadersBlankRowsAndColumns'}
1746
+
1747
+ context 'well-formed' do
1748
+
1749
+ let(:required_headers) {%w'Date Amount'}
1750
+
1751
+ it 'opens correctly' do
1752
+ expect {ss}.not_to raise_error
1753
+ end
1754
+
1755
+ it 'contains expected headers' do
1756
+ required_headers.each do |header|
1757
+ expect(ss.headers).to include header
1758
+ end
1759
+ expect(ss.headers).to eq %w'Date Amount Code Remark'
1760
+ end
1761
+
1762
+ it '#shift returns nil' do
1763
+ row = ss.shift
1764
+ expect(row).to be_nil
1765
+ end
1766
+
1767
+ it '#parse returns empty Array of Hash objects' do
1768
+ rows = ss.parse
1769
+ expect(rows).to be_a Array
1770
+ # noinspection RubyResolve
1771
+ expect(rows).to be_empty
1772
+ expect(rows.size).to eq 0
1773
+ end
1774
+
1775
+ end
1776
+
1777
+ context 'not specified' do
1778
+
1779
+ let(:required_headers) {[]}
1780
+
1781
+ it 'opens correctly' do
1782
+ expect {ss}.not_to raise_error
1783
+ end
1784
+
1785
+ it 'contains expected headers' do
1786
+ required_headers.each do |header|
1787
+ expect(ss.headers).to include header
1788
+ end
1789
+ expect(ss.headers).to eq %w'Date Amount Code Remark'
1790
+ end
1791
+
1792
+ it '#shift returns nil' do
1793
+ row = ss.shift
1794
+ expect(row).to be_nil
1795
+ end
1796
+
1797
+ it '#parse returns empty Array of Hash objects' do
1798
+ rows = ss.parse
1799
+ expect(rows).to be_a Array
1800
+ # noinspection RubyResolve
1801
+ expect(rows).to be_empty
1802
+ expect(rows.size).to eq 0
1803
+ end
1804
+
1805
+ end
1806
+
1807
+ context 'not well-formed' do
1808
+
1809
+ let(:required_headers) {%w'Date dummy1 Amount dummy2'}
1810
+
1811
+ it 'throws error when opened' do
1812
+ expect {ss}.to raise_error(RuntimeError, 'Headers not found: ["dummy1", "dummy2"].')
1813
+ end
1814
+ end
1815
+
1816
+ end
1817
+
1818
+ end
1819
+
1820
+ end