fix_tsv_conflict 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9bb0ced2121afe878ea9dcbdcf17ce08560341027047494f7a3ec51f3bdeaac3
4
- data.tar.gz: 32d3363b7f894d6356624bc894a18552d54e63df7094ea65c7a8898ec1e36658
3
+ metadata.gz: 073b4ff002e1a394af88ca8d2415786d8ee40c69c77af3741bbedc1585f5cd52
4
+ data.tar.gz: ab701259a3360cef72e8d37c0f9bed15c0bc6abcb1ff178afa1392d9f965f443
5
5
  SHA512:
6
- metadata.gz: c2d320c077a00c9cae3c013eddde5fb6dd1721568df8bbb7e3afcd16fb9f99f20c332bd20b7f8c0108a3fbdca16ef1d9ac28aa83d666059fce45b89aec7065fb
7
- data.tar.gz: 4fc7b148692e8f96f9308d90866945aebd9ceee6ec2019f819f70d81bfb6e6f210ce8eef4e11ce13428530d6b9aaa54461a6ac06eb97246eb6d9e96ebdb5d62e
6
+ metadata.gz: 6ab5861f20e17388137f1c3a83c03b6c13e05bc0865e80894557849c701379f9ac9e9c56ed73b96102447b431758d01f8da0366f67531f9b9ac9b618f26ed0e1
7
+ data.tar.gz: bf957e7a2d6614b233cc7e0769a1130f8c6b0f32523761546947e2d7b3f399e2461633d90fbddf2698fafc7c998ae540db71ecf2f1c25b3c7181307190f56be4
data/Guardfile CHANGED
@@ -1,4 +1,4 @@
1
- guard :minitest, include: %w(test) do
1
+ guard :minitest, include: %w(test), all_after_pass: true do
2
2
  watch(%r{^test/(.*)\/?(.*)_test\.rb$})
3
3
  watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}#{m[2]}_test.rb"}
4
4
  watch(%r{^test/test_helper\.rb$}) { 'test' }
@@ -0,0 +1,30 @@
1
+ module FixTSVConflict
2
+ class Conflict
3
+ attr_reader :left, :lbranch, :right, :rbranch
4
+
5
+ def initialize(left, lbranch, right, rbranch)
6
+ @left = left
7
+ @lbranch = lbranch
8
+ @right = right
9
+ @rbranch = rbranch
10
+ end
11
+
12
+ ID_REGEXP = /\A[0-9]+\t/
13
+ NL_REGEXP = /\A\n/
14
+
15
+ def valid?
16
+ left.all? { |line| ID_REGEXP =~ line || NL_REGEXP =~ line} &&
17
+ right.all? { |line| ID_REGEXP =~ line || NL_REGEXP =~ line }
18
+ end
19
+
20
+ def to_a
21
+ result = []
22
+ result << "#{LEFT} #{lbranch}\n"
23
+ result += left
24
+ result << "#{SEP}\n"
25
+ result += right
26
+ result << "#{RIGHT} #{rbranch}\n"
27
+ result
28
+ end
29
+ end
30
+ end
@@ -1,48 +1,46 @@
1
- require "fix_tsv_conflict/diff_printer"
1
+ require "fix_tsv_conflict/conflict"
2
2
  require "fix_tsv_conflict/logging"
3
+ require "fix_tsv_conflict/resolver"
3
4
 
4
5
  module FixTSVConflict
5
6
  class Repairman
6
7
  include Logging
7
8
 
8
- BLANK_RE = /\A[[:space:]]*\z/
9
- using Module.new {
10
- refine String do
11
- def blank?
12
- BLANK_RE === self
13
- end
14
- end
15
- }
16
-
17
- attr_reader :stderr
9
+ attr_reader :stdin, :stderr
18
10
 
19
11
  def initialize(stdin: $stdin, stderr: $stderr)
20
12
  @stdin = stdin
21
13
  @stderr = stderr
22
14
  end
23
15
 
16
+ def resolver
17
+ @resolver ||= Resolver.new(stdin: stdin, stderr: stderr)
18
+ end
19
+
24
20
  def repair(source)
25
21
  result = []
26
- branch, left, right = nil, [], []
22
+ branch = nil
23
+ left, lbranch = [], nil
24
+ right, rbranch = [], nil
27
25
 
28
26
  source.each_line.with_index do |line, i|
29
- parse_header(line) if i.zero?
30
- if branch
31
- if line.start_with?(RIGHT)
32
- @rbranch = line.chomp.split(" ").last
33
- result += resolve(left, right)
34
- branch = nil
35
- left.clear
36
- right.clear
37
- elsif line.start_with?(SEP)
38
- branch = right
39
- else
40
- branch << line
41
- end
27
+ if i.zero?
28
+ load_tabs_count(line)
29
+ result << line
30
+ elsif line.start_with?(LEFT)
31
+ lbranch = line.chomp.split(" ").last
32
+ branch = left
33
+ elsif line.start_with?(SEP)
34
+ branch = right
35
+ elsif line.start_with?(RIGHT)
36
+ rbranch = line.chomp.split(" ").last
37
+ result += handle(left, lbranch, right, rbranch)
38
+ branch = nil
39
+ left.clear
40
+ right.clear
42
41
  else
43
- if line.start_with?(LEFT)
44
- @lbranch = line.chomp.split(" ").last
45
- branch = left
42
+ if branch
43
+ branch << line
46
44
  else
47
45
  result << line
48
46
  end
@@ -51,130 +49,31 @@ module FixTSVConflict
51
49
  result.join
52
50
  end
53
51
 
54
- def resolve(left, right)
55
- print_conflicts(left, right)
56
- result = resolve_conflicts(left, right)
52
+ def load_tabs_count(header)
53
+ resolver.tabs = header.count(TAB)
54
+ end
55
+
56
+ def handle(left, lbranch, right, rbranch)
57
+ conflict = Conflict.new(left, lbranch, right, rbranch)
58
+ print_conflict(conflict)
59
+ result = resolver.resolve(conflict)
57
60
  print_result(result)
58
61
  result
59
62
  end
60
63
 
61
- def print_conflicts(left, right)
62
- warn "A conflict found:"
64
+ def print_conflict(conflict)
65
+ info "Found a conflict:"
63
66
  blank
64
- dump "#{LEFT} #{@lbranch}"
65
- dump left
66
- dump SEP
67
- dump right
68
- dump "#{RIGHT} #{@rbranch}"
67
+ dump conflict.to_a
69
68
  blank
70
69
  end
71
70
 
72
71
  def print_result(result)
73
- notice "The conflict was fixed to:"
72
+ notice "Resolved to:"
74
73
  blank
75
74
  dump result
76
75
  blank
77
76
  blank
78
77
  end
79
-
80
- def resolve_conflicts(left, right)
81
- left = index_by_id(left.reject(&:blank?))
82
- right = index_by_id(right.reject(&:blank?))
83
- (left.keys + right.keys).uniq.sort.map do |id|
84
- l = left[id]
85
- r = right[id]
86
- if l && r
87
- select(l, r)
88
- else
89
- l || r
90
- end
91
- end
92
- end
93
-
94
- def select(l, r)
95
- selected = if l.rstrip == r.rstrip
96
- pick_by_trailing_tabs(l, r)
97
- else
98
- prompt(l, r)
99
- end
100
- end
101
-
102
- def prompt(l, r)
103
- print_diff(l, r)
104
- prompt_select(l, r)
105
- end
106
-
107
- def print_diff(l, r)
108
- log "Diff by columns:"
109
- blank
110
- printer = DiffPrinter.new(stderr: @stderr)
111
- printer.print(@cols, l, @lbranch, r, @rbranch)
112
- end
113
-
114
- def prompt_select(l, r)
115
- text = <<-TEXT
116
-
117
- Which do you want keep?
118
-
119
- 1) #{@lbranch}
120
- 2) #{@rbranch}
121
- k) keep as is
122
-
123
- Please enter 1, 2, or k:
124
- TEXT
125
-
126
- info text.chomp, no_newline: true
127
-
128
- loop do
129
- case selected = @stdin.gets.strip
130
- when "1"
131
- break l
132
- when "2"
133
- break r
134
- when "k"
135
- break "#{LEFT} #{@lbranch}\n#{l}#{SEP}\n#{r}#{RIGHT} #{@rbranch}\n"
136
- else
137
- text = <<-TEXT
138
- Invalid input: #{selected}
139
- Please enter 1, 2, or k:
140
- TEXT
141
- @stderr.print text.chomp
142
- end
143
- end
144
- end
145
-
146
- def index_by_id(lines)
147
- result = {}
148
- lines.each do |line|
149
- id = line.split(TAB, 2).first
150
- result[id] = line
151
- end
152
- result
153
- end
154
-
155
- def parse_header(line)
156
- @cols = {}
157
- line.chomp.split(TAB).each.with_index do |col, i|
158
- @cols[col] = i
159
- end
160
- @tabs = @cols.length - 1
161
- end
162
-
163
- def pick_by_trailing_tabs(l, r)
164
- info "Trailing tab conflicts were fixed automatically."
165
-
166
- ltabs = l.count(TAB)
167
- rtabs = r.count(TAB)
168
-
169
- if ltabs == @tabs
170
- l
171
- elsif rtabs == @tabs
172
- r
173
- else
174
- # both are wrong.
175
- # so this is a determistic picking.
176
- ltabs < rtabs ? l : r
177
- end
178
- end
179
78
  end
180
79
  end
@@ -0,0 +1,111 @@
1
+ require "fix_tsv_conflict/logging"
2
+
3
+ module FixTSVConflict
4
+ class Resolver
5
+ include Logging
6
+
7
+ BLANK_RE = /\A[[:space:]]*\z/
8
+ using Module.new {
9
+ refine String do
10
+ def blank?
11
+ BLANK_RE === self
12
+ end
13
+ end
14
+ }
15
+
16
+ attr_reader :stdin, :stderr
17
+ attr_accessor :tabs
18
+
19
+ def initialize(stdin: $stdin, stderr: $stderr)
20
+ @stdin = stdin
21
+ @stderr = stderr
22
+
23
+ @tabs = 0
24
+ end
25
+
26
+ def resolve(conflict)
27
+ unless conflict.valid?
28
+ return conflict.to_a
29
+ end
30
+
31
+ result = try(conflict)
32
+ if result
33
+ return result
34
+ end
35
+
36
+ warn "Failed to resolve it automatically."
37
+ select(conflict)
38
+ end
39
+
40
+ def try(conflict)
41
+ result = []
42
+ left = index_by_id(conflict.left.reject(&:blank?))
43
+ right = index_by_id(conflict.right.reject(&:blank?))
44
+ (left.keys + right.keys).uniq.sort.each do |id|
45
+ l = left[id]
46
+ r = right[id]
47
+ if l && r
48
+ if l.rstrip == r.rstrip
49
+ result << pick_by_tabs(l, r)
50
+ else
51
+ return false
52
+ end
53
+ else
54
+ result << (l || r)
55
+ end
56
+ end
57
+ result
58
+ end
59
+
60
+ def select(conflict)
61
+ text = <<-TEXT
62
+ Which do you want keep?
63
+
64
+ 1) #{conflict.lbranch}
65
+ 2) #{conflict.rbranch}
66
+ k) keep as is
67
+
68
+ TEXT
69
+
70
+ info text
71
+
72
+ loop do
73
+ info "Please enter 1, 2, or k: ", no_newline: true
74
+ case selected = stdin.gets.strip
75
+ when "1"
76
+ return conflict.left
77
+ when "2"
78
+ return conflict.right
79
+ when "k"
80
+ return conflict.to_a
81
+ else
82
+ info "Invalid input: #{selected}"
83
+ end
84
+ end
85
+ end
86
+
87
+ def index_by_id(lines)
88
+ result = {}
89
+ lines.each do |line|
90
+ id = line.split(TAB, 2).first
91
+ result[id] = line
92
+ end
93
+ result
94
+ end
95
+
96
+ def pick_by_tabs(l, r)
97
+ ltabs = l.count(TAB)
98
+ rtabs = r.count(TAB)
99
+
100
+ if ltabs == tabs
101
+ l
102
+ elsif rtabs == tabs
103
+ r
104
+ else
105
+ # both are wrong.
106
+ # so this is a determistic picking.
107
+ ltabs < rtabs ? l : r
108
+ end
109
+ end
110
+ end
111
+ end
@@ -1,3 +1,3 @@
1
1
  module FixTSVConflict
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fix_tsv_conflict
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masato Ikeda
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-02-20 00:00:00.000000000 Z
11
+ date: 2018-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -100,9 +100,10 @@ files:
100
100
  - fix_tsv_conflict.gemspec
101
101
  - lib/fix_tsv_conflict.rb
102
102
  - lib/fix_tsv_conflict/cli.rb
103
- - lib/fix_tsv_conflict/diff_printer.rb
103
+ - lib/fix_tsv_conflict/conflict.rb
104
104
  - lib/fix_tsv_conflict/logging.rb
105
105
  - lib/fix_tsv_conflict/repairman.rb
106
+ - lib/fix_tsv_conflict/resolver.rb
106
107
  - lib/fix_tsv_conflict/version.rb
107
108
  homepage: https://github.com/a2ikm/fix_tsv_conflict
108
109
  licenses: []
@@ -1,53 +0,0 @@
1
- require "fix_tsv_conflict/logging"
2
-
3
- module FixTSVConflict
4
- class DiffPrinter
5
- include Logging
6
-
7
- attr_reader :stderr
8
-
9
- def initialize(stderr: $stderr)
10
- @stderr = stderr
11
- @left, @right = {}, {}
12
- end
13
-
14
- def print(cols, left, lbranch, right, rbranch)
15
- @lbranch, @rbranch = lbranch, rbranch
16
-
17
- left = left.chomp.split(TAB)
18
- right = right.chomp.split(TAB)
19
-
20
- cols.each do |col, i|
21
- l, r = left[i], right[i]
22
- if l == r
23
- flush_conflicts if in_conflict?
24
- dump [col, l].join(TAB)
25
- else
26
- @left[col] = l
27
- @right[col] = r
28
- end
29
- end
30
-
31
- flush_conflicts
32
- end
33
-
34
- def flush_conflicts
35
- dump "#{LEFT} #{@lbranch}"
36
- dump @left.map { |c, v| [c, v].join(TAB) }
37
- dump SEP
38
- dump @right.map { |c, v| [c, v].join(TAB) }
39
- dump "#{RIGHT} #{@rbranch}"
40
-
41
- @left.clear
42
- @right.clear
43
- end
44
-
45
- def print_col_and_value(col, value)
46
- @stderr.puts [col, value].join(TAB)
47
- end
48
-
49
- def in_conflict?
50
- @left.length > 0 || @right.length > 0
51
- end
52
- end
53
- end