fix_tsv_conflict 0.1.2 → 0.2.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.
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