coopy 0.6.4.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +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