coopy 0.6.4.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/CHANGELOG.md +7 -0
  5. data/Gemfile +7 -0
  6. data/LICENSE.md +22 -0
  7. data/README.md +59 -0
  8. data/Rakefile +4 -6
  9. data/coopy.gemspec +26 -0
  10. data/lib/coopy.rb +32 -175
  11. data/lib/coopy/alignment.rb +260 -0
  12. data/lib/coopy/bag.rb +17 -0
  13. data/lib/coopy/cell_info.rb +24 -0
  14. data/lib/coopy/change_type.rb +10 -0
  15. data/lib/coopy/compare_flags.rb +62 -0
  16. data/lib/coopy/compare_table.rb +327 -0
  17. data/lib/coopy/coopy.rb +22 -0
  18. data/lib/coopy/cross_match.rb +10 -0
  19. data/lib/coopy/csv_table.rb +51 -0
  20. data/lib/coopy/diff_render.rb +307 -0
  21. data/lib/coopy/index.rb +73 -0
  22. data/lib/coopy/index_item.rb +17 -0
  23. data/lib/coopy/index_pair.rb +72 -0
  24. data/lib/coopy/mover.rb +123 -0
  25. data/lib/coopy/ordering.rb +27 -0
  26. data/lib/coopy/row.rb +9 -0
  27. data/lib/coopy/simple_cell.rb +15 -0
  28. data/lib/coopy/simple_table.rb +144 -0
  29. data/lib/coopy/simple_view.rb +36 -0
  30. data/lib/coopy/table.rb +44 -0
  31. data/lib/coopy/table_comparison_state.rb +33 -0
  32. data/lib/coopy/table_diff.rb +634 -0
  33. data/lib/coopy/table_text.rb +14 -0
  34. data/lib/coopy/table_view.rb +31 -0
  35. data/lib/coopy/unit.rb +53 -0
  36. data/lib/coopy/version.rb +3 -0
  37. data/lib/coopy/view.rb +34 -0
  38. data/spec/fixtures/bridges.html +10 -0
  39. data/spec/fixtures/bridges_diff.csv +8 -0
  40. data/spec/fixtures/bridges_new.csv +9 -0
  41. data/spec/fixtures/bridges_old.csv +9 -0
  42. data/spec/fixtures/planetary_bodies.html +22 -0
  43. data/spec/fixtures/planetary_bodies_diff.csv +19 -0
  44. data/spec/fixtures/planetary_bodies_new.csv +20 -0
  45. data/spec/fixtures/planetary_bodies_old.csv +19 -0
  46. data/spec/fixtures/quote_me.csv +10 -0
  47. data/spec/fixtures/quote_me2.csv +11 -0
  48. data/spec/integration/table_diff_spec.rb +57 -0
  49. data/spec/libs/compare_flags_spec.rb +40 -0
  50. data/spec/libs/coopy_spec.rb +14 -0
  51. data/spec/libs/ordering_spec.rb +28 -0
  52. data/spec/libs/unit_spec.rb +31 -0
  53. data/spec/spec_helper.rb +29 -0
  54. metadata +153 -46
  55. data/bin/sqlite_diff +0 -4
  56. data/bin/sqlite_patch +0 -4
  57. data/bin/sqlite_rediff +0 -4
  58. data/lib/coopy/dbi_sql_wrapper.rb +0 -89
  59. data/lib/coopy/diff_apply_sql.rb +0 -35
  60. data/lib/coopy/diff_columns.rb +0 -33
  61. data/lib/coopy/diff_output.rb +0 -21
  62. data/lib/coopy/diff_output_action.rb +0 -34
  63. data/lib/coopy/diff_output_group.rb +0 -40
  64. data/lib/coopy/diff_output_raw.rb +0 -17
  65. data/lib/coopy/diff_output_stats.rb +0 -45
  66. data/lib/coopy/diff_output_table.rb +0 -49
  67. data/lib/coopy/diff_output_tdiff.rb +0 -48
  68. data/lib/coopy/diff_parser.rb +0 -92
  69. data/lib/coopy/diff_render_csv.rb +0 -29
  70. data/lib/coopy/diff_render_html.rb +0 -74
  71. data/lib/coopy/diff_render_log.rb +0 -52
  72. data/lib/coopy/row_change.rb +0 -25
  73. data/lib/coopy/scraperwiki_sql_wrapper.rb +0 -8
  74. data/lib/coopy/scraperwiki_utils.rb +0 -23
  75. data/lib/coopy/sequel_sql_wrapper.rb +0 -73
  76. data/lib/coopy/sql_compare.rb +0 -222
  77. data/lib/coopy/sql_wrapper.rb +0 -34
  78. data/lib/coopy/sqlite_sql_wrapper.rb +0 -143
  79. data/test/test_coopy.rb +0 -126
@@ -0,0 +1,33 @@
1
+ module Coopy
2
+ class TableComparisonState
3
+
4
+ attr_accessor :p # Table
5
+ attr_accessor :a # Table
6
+ attr_accessor :b # Table
7
+
8
+ attr_accessor :completed # boolean
9
+ attr_accessor :run_to_completion # boolean
10
+
11
+ # Are tables trivially equal?
12
+ attr_accessor :is_equal # boolean
13
+ attr_accessor :is_equal_known # boolean
14
+
15
+ # Do tables have blatantly same set of columns?
16
+ attr_accessor :has_same_columns # boolean
17
+ attr_accessor :has_same_columns_known # boolean
18
+
19
+ def initialize
20
+ reset
21
+ end
22
+
23
+ def reset
24
+ @completed = false
25
+ @run_to_completion = true
26
+ @is_equal_known = false
27
+ @is_equal = false
28
+ @has_same_columns = false
29
+ @has_same_columns_known = false
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,634 @@
1
+ module Coopy
2
+ class TableDiff
3
+
4
+ def initialize(align, flags)
5
+ @align = align # Alignment
6
+ @flags = flags # CompareFlags
7
+ end
8
+
9
+ def get_separator(t, t2, root)
10
+ sep = root
11
+ w = t.width
12
+ h = t.height
13
+ view = t.get_cell_view
14
+ (0..h-1).each do |y|
15
+ (0..w-1).each do |x|
16
+ txt = view.to_s(t.get_cell(x,y))
17
+ next if (txt.nil?)
18
+ while (txt.index(sep))
19
+ sep = "-" + sep
20
+ end
21
+ end
22
+ end
23
+ if (t2)
24
+ w = t2.width
25
+ h = t2.height
26
+ (0..h-1).each do |y|
27
+ (0..w-1).each do |x|
28
+ txt = view.to_s(t2.get_cell(x,y))
29
+ next if (txt.nil?)
30
+ while (txt.index(sep))
31
+ sep = "-" + sep
32
+ end
33
+ end
34
+ end
35
+ end
36
+ return sep
37
+ end
38
+
39
+ def quote_for_diff(v, d)
40
+ nil_str = "nil"
41
+ if (v.equals(d,nil))
42
+ return nil_str
43
+ end
44
+ str = v.to_s(d)
45
+ score = 0
46
+ (0..str.length-1).each do |i|
47
+ break if (str[score]!='_')
48
+ score+=1
49
+ end
50
+ if (str.slice(score)==nil_str)
51
+ str = "_" + str
52
+ end
53
+ return str
54
+ end
55
+
56
+ def is_reordered(m, ct)
57
+ reordered = false
58
+ l = -1
59
+ r = -1
60
+ (0..ct-1).each do |i|
61
+ unit = m[i]
62
+ next if (unit.nil?)
63
+ if (unit.l>=0)
64
+ if (unit.l<l)
65
+ reordered = true
66
+ break
67
+ end
68
+ l = unit.l
69
+ end
70
+ if (unit.r>=0)
71
+ if (unit.r<r)
72
+ reordered = true
73
+ break
74
+ end
75
+ r = unit.r
76
+ end
77
+ end
78
+ return reordered
79
+ end
80
+
81
+
82
+ def spread_context(units, del, active)
83
+ if (del>0 && active != nil)
84
+ # forward
85
+ mark = -del-1
86
+ skips = 0
87
+ (0..units.length-1).each do |i|
88
+ if (active[i]==-3)
89
+ # inserted/deleted row that is not to be shown, ignore
90
+ skips+=1
91
+ next
92
+ end
93
+ if (active[i]==0||active[i]==3)
94
+ if (i-mark<=del+skips)
95
+ active[i] = 2
96
+ elsif (i-mark==del+1+skips)
97
+ active[i] = 3
98
+ end
99
+ elsif (active[i]==1)
100
+ mark = i
101
+ skips = 0
102
+ end
103
+ end
104
+
105
+ # reverse
106
+ mark = units.length + del + 1
107
+ skips = 0
108
+ (0..units.length-1).each do |j|
109
+ i = units.length-1-j
110
+ if (active[i]==-3)
111
+ # inserted/deleted row that is not to be shown, ignore
112
+ skips+=1
113
+ next
114
+ end
115
+ if (active[i]==0||active[i]==3)
116
+ if (mark-i<=del+skips)
117
+ active[i] = 2
118
+ elsif (mark-i==del+1+skips)
119
+ active[i] = 3
120
+ end
121
+ elsif (active[i]==1)
122
+ mark = i
123
+ skips = 0
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ def report_unit(unit)
130
+ txt = unit.to_s
131
+ reordered = false
132
+ if (unit.l>=0)
133
+ if (unit.l<@l_prev)
134
+ reordered = true
135
+ end
136
+ @l_prev = unit.l
137
+ end
138
+ if (unit.r>=0)
139
+ if (unit.r<@r_prev)
140
+ reordered = true
141
+ end
142
+ @r_prev = unit.r
143
+ end
144
+ txt = "[" + txt + "]" if (reordered)
145
+ return txt
146
+ end
147
+
148
+ def hilite(output)
149
+ return false if (!output.is_resizable?)
150
+ output.resize(0,0)
151
+ output.clear
152
+
153
+ row_map = {}
154
+ col_map = {}
155
+
156
+ order = @align.to_order
157
+ units = order.get_list
158
+ has_parent = (@align.reference != nil)
159
+ a = nil
160
+ b = nil
161
+ p = nil
162
+ ra_header = 0
163
+ rb_header = 0
164
+ is_index_p = {}
165
+ is_index_a = {}
166
+ is_index_b = {}
167
+ if (has_parent)
168
+ p = @align.get_source
169
+ a = @align.reference.get_target
170
+ b = @align.get_target
171
+ ra_header = @align.reference.meta.get_target_header
172
+ rb_header = @align.meta.get_target_header
173
+ if (@align.get_index_columns)
174
+ @align.get_index_columns.each do |p2b|
175
+ is_index_p.set(p2b.l,true) if (p2b.l>=0)
176
+ is_index_b.set(p2b.r,true) if (p2b.r>=0)
177
+ end
178
+ end
179
+ if (@align.reference.get_index_columns)
180
+ @align.reference.get_index_columns.each do |p2a|
181
+ is_index_p.set(p2a.l,true) if (p2a.l>=0)
182
+ is_index_a.set(p2a.r,true) if (p2a.r>=0)
183
+ end
184
+ end
185
+ else
186
+ a = @align.get_source
187
+ b = @align.get_target
188
+ p = a
189
+ ra_header = @align.meta.get_source_header
190
+ rb_header = @align.meta.get_target_header
191
+ if (@align.get_index_columns)
192
+ @align.get_index_columns.each do |a2b|
193
+ is_index_a[a2b.l] = true if (a2b.l>=0)
194
+ is_index_b[a2b.r] = true if (a2b.r>=0)
195
+ end
196
+ end
197
+ end
198
+
199
+ column_order = @align.meta.to_order
200
+ column_units = column_order.get_list
201
+
202
+ show_rc_numbers = false
203
+ row_moves = nil
204
+ col_moves = nil
205
+ if (@flags.ordered)
206
+ row_moves = {}
207
+ moves = Mover.move_units(units)
208
+ (0...moves.length).each do |i|
209
+ row_moves[moves[i]] = i
210
+ end
211
+ col_moves = {}
212
+ moves = Mover.move_units(column_units)
213
+ (0...moves.length).each do |i|
214
+ col_moves[moves[i]] = i
215
+ end
216
+ end
217
+
218
+ active = []
219
+ active_column = nil
220
+ if (!@flags.show_unchanged)
221
+ (0...units.length).each do |i|
222
+ active[i] = 0
223
+ end
224
+ end
225
+
226
+ allow_insert = @flags.allow_insert
227
+ allow_delete = @flags.allow_delete
228
+ allow_update = @flags.allow_update
229
+
230
+ if (!@flags.show_unchanged_columns)
231
+ active_column = []
232
+ (0..column_units.length-1).each do |i|
233
+ v = 0
234
+ unit = column_units[i]
235
+ v = 1 if (unit.l>=0 && is_index_a[unit.l])
236
+ v = 1 if (unit.r>=0 && is_index_b[unit.r])
237
+ v = 1 if (unit.p>=0 && is_index_p[unit.p])
238
+ active_column[i] = v
239
+ end
240
+ end
241
+
242
+ outer_reps_needed =
243
+ (@flags.show_unchanged&&@flags.show_unchanged_columns) ? 1 : 2
244
+
245
+ v = a.get_cell_view
246
+ sep = ""
247
+ conflict_sep = ""
248
+
249
+ schema = []
250
+ have_schema = false
251
+ (0...column_units.length).each do |j|
252
+ cunit = column_units[j]
253
+ reordered = false
254
+
255
+ if (@flags.ordered)
256
+ if (col_moves.has_key?(j))
257
+ reordered = true
258
+ end
259
+ show_rc_numbers = true if (reordered)
260
+ end
261
+
262
+ act = ""
263
+ if (cunit.r>=0 && cunit.lp==-1)
264
+ have_schema = true
265
+ act = "+++"
266
+ if (active_column)
267
+ active_column[j] = 1 if (allow_update)
268
+ end
269
+ end
270
+ if (cunit.r<0 && cunit.lp>=0)
271
+ have_schema = true
272
+ act = "---"
273
+ if (active_column)
274
+ active_column[j] = 1 if (allow_update)
275
+ end
276
+ end
277
+ if (cunit.r>=0 && cunit.lp>=0)
278
+ if (a.height>=ra_header && b.height>=rb_header)
279
+ aa = a.get_cell(cunit.lp,ra_header)
280
+ bb = b.get_cell(cunit.r,rb_header)
281
+ if (!v.equals(aa,bb))
282
+ have_schema = true
283
+ act = "("
284
+ act += v.to_s(aa)
285
+ act += ")"
286
+ active_column[j] = 1 if (active_column)
287
+ end
288
+ end
289
+ end
290
+ if (reordered)
291
+ act = ":" + act
292
+ have_schema = true
293
+ active_column = nil if (active_column) # bail
294
+ end
295
+
296
+ schema << act
297
+ end
298
+ if (have_schema)
299
+ at = output.height
300
+ output.resize(column_units.length+1,at+1)
301
+ output.set_cell(0,at,v.to_datum("!"))
302
+ (0...column_units.length).each do |j|
303
+ output.set_cell(j+1,at,v.to_datum(schema[j]))
304
+ end
305
+ end
306
+
307
+ top_line_done = false
308
+ if (@flags.always_show_header)
309
+ at = output.height
310
+ output.resize(column_units.length+1,at+1)
311
+ output.set_cell(0,at,v.to_datum("@@"))
312
+ (0...column_units.length).each do |j|
313
+ cunit = column_units[j]
314
+ if (cunit.r>=0)
315
+ if (b.height>0)
316
+ output.set_cell(j+1,at,
317
+ b.get_cell(cunit.r,rb_header))
318
+ end
319
+ elsif (cunit.lp>=0)
320
+ if (a.height>0)
321
+ output.set_cell(j+1,at,
322
+ a.get_cell(cunit.lp,ra_header))
323
+ end
324
+ end
325
+ col_map[j+1] = cunit
326
+ end
327
+ top_line_done = true
328
+ end
329
+
330
+ # If we are dropping unchanged rows/cols, we repeat this loop twice.
331
+ (0..outer_reps_needed-1).each do |out|
332
+ if (out==1)
333
+ spread_context(units,@flags.unchanged_context,active)
334
+ spread_context(column_units,@flags.unchanged_column_context,
335
+ active_column)
336
+ if (active_column)
337
+ (0..column_units.length).each do |i|
338
+ if (active_column[i]==3)
339
+ active_column[i] = 0
340
+ end
341
+ end
342
+ end
343
+ end
344
+
345
+ showed_dummy = false
346
+ l = -1
347
+ r = -1
348
+ (0..units.length-1).each do |i|
349
+ unit = units[i]
350
+ reordered = false
351
+
352
+ if (@flags.ordered)
353
+ if (row_moves.has_key?(i))
354
+ reordered = true
355
+ end
356
+ show_rc_numbers = true if (reordered)
357
+ end
358
+
359
+ next if (unit.r<0 && unit.l<0)
360
+
361
+ next if (unit.r==0 && unit.lp==0 && top_line_done)
362
+
363
+ act = ""
364
+
365
+ act = ":" if (reordered)
366
+
367
+ publish = @flags.show_unchanged
368
+ dummy = false
369
+ if (out==1)
370
+ publish = active[i]>0
371
+ dummy = active[i]==3
372
+ next if (dummy&&showed_dummy)
373
+ next if (!publish)
374
+ end
375
+
376
+ showed_dummy = false if (!dummy)
377
+
378
+ at = output.height
379
+ if (publish)
380
+ output.resize(column_units.length+1,at+1)
381
+ end
382
+
383
+ if (dummy)
384
+ (0...column_units.length+1).each do |j|
385
+ output.set_cell(j,at,v.to_datum("..."))
386
+ showed_dummy = true
387
+ end
388
+ next
389
+ end
390
+
391
+ have_addition = false
392
+ skip = false
393
+
394
+ if (unit.p<0 && unit.l<0 && unit.r>=0)
395
+ skip = true if (!allow_insert)
396
+ act = "+++"
397
+ end
398
+ if ((unit.p>=0||!has_parent) && unit.l>=0 && unit.r<0)
399
+ skip = true if (!allow_delete)
400
+ act = "---"
401
+ end
402
+
403
+ if (skip)
404
+ if (!publish)
405
+ if (active)
406
+ active[i] = -3
407
+ end
408
+ end
409
+ next
410
+ end
411
+
412
+ (0...column_units.length).each do |j|
413
+ cunit = column_units[j]
414
+ pp = nil
415
+ ll = nil
416
+ rr = nil
417
+ dd = nil
418
+ dd_to = nil
419
+ have_dd_to = false
420
+ dd_to_alt = nil
421
+ have_dd_to_alt = false
422
+ have_pp = false
423
+ have_ll = false
424
+ have_rr = false
425
+ if (cunit.p>=0 && unit.p>=0)
426
+ pp = p.get_cell(cunit.p,unit.p)
427
+ have_pp = true
428
+ end
429
+ if (cunit.l>=0 && unit.l>=0)
430
+ ll = a.get_cell(cunit.l,unit.l)
431
+ have_ll = true
432
+ end
433
+ if (cunit.r>=0 && unit.r>=0)
434
+ rr = b.get_cell(cunit.r,unit.r)
435
+ have_rr = true
436
+ if ((have_pp ? cunit.p : cunit.l)<0)
437
+ if (rr != nil)
438
+ if (v.to_s(rr) != "")
439
+ if (@flags.allow_update)
440
+ have_addition = true
441
+ end
442
+ end
443
+ end
444
+ end
445
+ end
446
+
447
+ # for now, just interested in p->r
448
+ if (have_pp)
449
+ if (!have_rr)
450
+ dd = pp
451
+ else
452
+ # have_pp, have_rr
453
+ if (v.equals(pp,rr))
454
+ dd = pp
455
+ else
456
+ # rr is different
457
+ dd = pp
458
+ dd_to = rr
459
+ have_dd_to = true
460
+
461
+ if (!v.equals(pp,ll))
462
+ if (!v.equals(pp,rr))
463
+ dd_to_alt = ll
464
+ have_dd_to_alt = true
465
+ end
466
+ end
467
+ end
468
+ end
469
+ elsif (have_ll)
470
+ if (!have_rr)
471
+ dd = ll
472
+ else
473
+ if (v.equals(ll,rr))
474
+ dd = ll
475
+ else
476
+ # rr is different
477
+ dd = ll
478
+ dd_to = rr
479
+ have_dd_to = true
480
+ end
481
+ end
482
+ else
483
+ dd = rr
484
+ end
485
+
486
+ txt = nil
487
+ if (have_dd_to&&allow_update)
488
+ if (active_column)
489
+ active_column[j] = 1
490
+ end
491
+ txt = quote_for_diff(v,dd)
492
+ # modification: x -> y
493
+ if (sep=="")
494
+ # strictly speaking getSeparator(a,nil,..)
495
+ # would be ok - but very confusing
496
+ sep = get_separator(a,b,"->")
497
+ end
498
+ is_conflict = false
499
+ if (have_dd_to_alt)
500
+ if (!v.equals(dd_to,dd_to_alt))
501
+ is_conflict = true
502
+ end
503
+ end
504
+ if (!is_conflict)
505
+ txt = txt + sep + quote_for_diff(v,dd_to)
506
+ if (sep.length>act.length)
507
+ act = sep
508
+ end
509
+ else
510
+ if (conflict_sep=="")
511
+ conflict_sep = get_separator(p,a,"!") + sep
512
+ end
513
+ txt = txt +
514
+ conflict_sep + quote_for_diff(v,dd_to_alt) +
515
+ conflict_sep + quote_for_diff(v,dd_to)
516
+ act = conflict_sep
517
+ end
518
+ end
519
+ if (act == "" && have_addition)
520
+ act = "+"
521
+ end
522
+ if (act == "+++")
523
+ if (have_rr)
524
+ if (active_column)
525
+ active_column[j] = 1
526
+ end
527
+ end
528
+ end
529
+ if (publish)
530
+ if (active_column.nil? || active_column[j]>0)
531
+ if (txt != nil)
532
+ output.set_cell(j+1,at,v.to_datum(txt))
533
+ else
534
+ output.set_cell(j+1,at,dd)
535
+ end
536
+ end
537
+ end
538
+ end
539
+
540
+ if (publish)
541
+ output.set_cell(0,at,v.to_datum(act))
542
+ row_map[at] = unit
543
+ end
544
+ if (act!="")
545
+ if (!publish)
546
+ if (active)
547
+ active[i] = 1
548
+ end
549
+ end
550
+ end
551
+ end
552
+ end
553
+
554
+ # add row/col numbers?
555
+ if (!show_rc_numbers)
556
+ if (@flags.always_show_order)
557
+ show_rc_numbers = true
558
+ elsif (@flags.ordered)
559
+ show_rc_numbers = is_reordered(row_map,output.height)
560
+ if (!show_rc_numbers)
561
+ show_rc_numbers = is_reordered(col_map,output.width)
562
+ end
563
+ end
564
+ end
565
+
566
+ admin_w = 1
567
+ if (show_rc_numbers&&!@flags.never_show_order)
568
+ admin_w+=1
569
+ target = new Array<Int>
570
+ (0..output.width-1).each do |i|
571
+ target.push(i+1)
572
+ end
573
+ output.insert_or_delete_columns(target,output.width+1)
574
+ @l_prev = -1
575
+ @r_prev = -1
576
+ (0..output.height-1).each do |i|
577
+ unit = row_map.get(i)
578
+ next if (unit.nil?)
579
+ output.setCell(0,i,reportUnit(unit))
580
+ end
581
+ target = []
582
+ (0..output.height-1).each do |i|
583
+ target.push(i+1)
584
+ end
585
+ output.insert_or_delete_rows(target,output.height+1)
586
+ @l_prev = -1
587
+ @r_prev = -1
588
+ (1..output.width-1).each do |i|
589
+ unit = col_map.get(i-1)
590
+ next if (unit.nil?)
591
+ output.setCell(i,0,reportUnit(unit))
592
+ end
593
+ output.setCell(0,0,"@:@")
594
+ end
595
+
596
+ if (active_column)
597
+ all_active = true
598
+ (0..active_column.length-1).each do |i|
599
+ if (active_column[i]==0)
600
+ all_active = false
601
+ break
602
+ end
603
+ end
604
+ if (!all_active)
605
+ fate = new Array<Int>
606
+ (0..admin_w-1).each do |i|
607
+ fate.push(i)
608
+ end
609
+ at = admin_w
610
+ ct = 0
611
+ dots = new Array<Int>
612
+ (0..active_column.length-1).each do |i|
613
+ off = (active_column[i]==0)
614
+ ct = off ? (ct+1) : 0
615
+ if (off && ct>1)
616
+ fate.push(-1)
617
+ else
618
+ dots.push(at) if (off)
619
+ fate.push(at)
620
+ at+=1
621
+ end
622
+ end
623
+ output.insertOrDeleteColumns(fate,at)
624
+ dots.each do |d|
625
+ (0..output.height-1).each do |j|
626
+ output.setCell(d,j,"...")
627
+ end
628
+ end
629
+ end
630
+ end
631
+ return true
632
+ end
633
+ end
634
+ end