cevennes 1.3.0 → 1.4.1

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: 435cddee7086da63e735457390edf766d42522bf58626b95ead034c418899719
4
- data.tar.gz: 20f5371e968decbfc05d062d44ae2cef6f1856716a9e58d321133b6d3089267e
3
+ metadata.gz: 8b3b29edbb31eb7fab04bc638ea9b9abf85ed2a0fd4563c6dc89a3e433684581
4
+ data.tar.gz: 5a1345bc9079f6fff54ca054347bd05e1631a7e995c386a60ee91ffdbc81ae85
5
5
  SHA512:
6
- metadata.gz: ac1aa7f98147a393812a2fec1fdf6e6c714d35a5e325a7a2912f484db7d59212ee66daed23fdbe0013d11b18e737ba2b5e7f9527021ef7300cc9742c0313f80f
7
- data.tar.gz: 44b7de7c27260dd623a466c880e4f9c789cf429efc6aa53671f092bfd9449f5d7c5841796da5b517faa34f9dfad202527b98ff8816b940ab4e345e0bfeb4f81b
6
+ metadata.gz: 9ba5e464074b0b3e21791a4476f3fc062d9b6de49643cdee32353a1dd794343e365a6b65c9145bce73bf0ee247427309aa214ea6bf507331b4171d6a0b6e82ae
7
+ data.tar.gz: 95e839e3aba7eedc8672902ec426f1752495f8a0d6890494dc5b92df75e9455e132befcc993c7fe3bb262b7bc7629a1b2373d447e9cabb019c66c9825f66449f
data/CHANGELOG.md CHANGED
@@ -2,55 +2,65 @@
2
2
  # CHANGELOG.md
3
3
 
4
4
 
5
+ ## cevennes 1.4.1 released 2026-10-20
6
+
7
+ * Require colorato
8
+
9
+
10
+ ## cevennes 1.4.0 released 2026-10-19
11
+
12
+ * Bring in .to_pretty_s(diff)
13
+
14
+
5
15
  ## cevennes 1.3.0 released 2022-07-24
6
16
 
7
- - Diff already parsed CSV as well
17
+ * Diff already parsed CSV as well
8
18
 
9
19
 
10
20
  ## cevennes 1.2.0 released 2022-07-05
11
21
 
12
- - Introduce `drop_equals: true`
22
+ * Introduce `drop_equals: true`
13
23
 
14
24
 
15
25
  ## cevennes 1.1.1 released 2021-02-25
16
26
 
17
- - Refine UTF-8 re-encoding
18
- - Fix `ignore_key_case: true` :-(
19
- - Clarify reencode
27
+ * Refine UTF-8 re-encoding
28
+ * Fix `ignore_key_case: true` :-(
29
+ * Clarify reencode
20
30
 
21
31
 
22
32
  ## cevennes 1.1.0 released 2021-02-24
23
33
 
24
- - Introduce `ignore_key_case: true`
34
+ * Introduce `ignore_key_case: true`
25
35
 
26
36
 
27
37
  ## cevennes 1.0.0 released 2018-09-07
28
38
 
29
- - Align the "keys" entry on the =+-! entries
39
+ * Align the "keys" entry on the =+-! entries
30
40
 
31
41
 
32
42
  ## cevennes 0.13.0 released 2018-09-07
33
43
 
34
- - Trim cells and keys
44
+ * Trim cells and keys
35
45
 
36
46
 
37
47
  ## cevennes 0.12.0 released 2018-09-06
38
48
 
39
- - Fail with IndexError on missing id in CSV old or new
49
+ * Fail with IndexError on missing id in CSV old or new
40
50
 
41
51
 
42
52
  ## cevennes 0.11.0 released 2018-09-06
43
53
 
44
- - Fix l0 and l1 stats
54
+ * Fix l0 and l1 stats
45
55
 
46
56
 
47
57
  ## cevennes 0.10.0 released 2018-09-03
48
58
 
49
- - Weave in additions at the right spot
50
- - Indicate line numbers
59
+ * Weave in additions at the right spot
60
+ * Indicate line numbers
51
61
 
52
62
 
53
63
  ## cevennes 0.9.0 released 2018-09-03
54
64
 
55
- - Implement first version of Cevennes.diff(id, cvs0, cvs1)
65
+ * Implement first version of Cevennes.diff(id, cvs0, cvs1)
56
66
 
data/LICENSE.txt CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- Copyright (c) 2018-2022, John Mettraux, jmettraux@gmail.com
2
+ Copyright (c) 2018-2026, John Mettraux, jmettraux@gmail.com
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to deal
data/Makefile CHANGED
@@ -1,16 +1,19 @@
1
1
 
2
2
  ## gem tasks ##
3
3
 
4
- NAME = \
5
- $(shell ruby -e "s = eval(File.read(Dir['*.gemspec'][0])); puts s.name")
6
- VERSION = \
7
- $(shell ruby -e "s = eval(File.read(Dir['*.gemspec'][0])); puts s.version")
4
+ NAME != \
5
+ ruby -e "s = eval(File.read(Dir['*.gemspec'][0])); puts s.name"
6
+ VERSION != \
7
+ ruby -e "s = eval(File.read(Dir['*.gemspec'][0])); puts s.version"
8
8
 
9
9
  count_lines:
10
10
  find lib -name "*.rb" | xargs cat | ruby -e "p STDIN.readlines.count { |l| l = l.strip; l[0, 1] != '#' && l != '' }"
11
11
  find spec -name "*_spec.rb" | xargs cat | ruby -e "p STDIN.readlines.count { |l| l = l.strip; l[0, 1] != '#' && l != '' }"
12
12
  cl: count_lines
13
13
 
14
+ scan:
15
+ scan lib/**/*.rb
16
+
14
17
  gemspec_validate:
15
18
  @echo "---"
16
19
  ruby -e "s = eval(File.read(Dir['*.gemspec'].first)); p s.validate"
@@ -30,10 +33,14 @@ build: gemspec_validate
30
33
  push: build
31
34
  gem push --otp "$(OTP)" pkg/$(NAME)-$(VERSION).gem
32
35
 
33
- spec:
34
- bundle exec rspec
35
- test: spec
36
+ test:
37
+ bundle exec proba
38
+ spec: test
39
+ t: test
40
+
41
+ colors:
42
+ bundle exec ruby -rcolorato -e "Colorato.show_colours"
36
43
 
37
44
 
38
- .PHONY: count_lines gemspec_validate name cw build push spec
45
+ .PHONY: count_lines scan gemspec_validate name cw build push test
39
46
 
data/cevennes.gemspec CHANGED
@@ -26,6 +26,7 @@ Diffs CSVs by lines, focusing on a single ID
26
26
  'homepage_uri' => s.homepage,
27
27
  'source_code_uri' => s.homepage,
28
28
  #'wiki_uri' => s.homepage + '/wiki',
29
+ 'rubygems_mfa_required' => 'true',
29
30
  }
30
31
 
31
32
  #s.files = `git ls-files`.split("\n")
@@ -39,8 +40,11 @@ Diffs CSVs by lines, focusing on a single ID
39
40
 
40
41
  #s.add_runtime_dependency 'tzinfo'
41
42
  #s.add_runtime_dependency 'raabro', '~> 1.1'
43
+ s.add_runtime_dependency 'csv'
44
+ s.add_runtime_dependency 'colorato', '~> 1.2'
42
45
 
43
- s.add_development_dependency 'rspec', '~> 3.7'
46
+ #s.add_development_dependency 'rspec', '~> 3.7'
47
+ s.add_development_dependency 'probatio', '~> 1.6'
44
48
 
45
49
  s.require_path = 'lib'
46
50
  end
data/lib/cevennes.rb CHANGED
@@ -2,17 +2,19 @@
2
2
 
3
3
  require 'csv'
4
4
 
5
+ require 'colorato'
6
+
5
7
 
6
8
  module Cevennes
7
9
 
8
- VERSION = '1.3.0'
10
+ VERSION = '1.4.1'
9
11
 
10
12
  class << self
11
13
 
12
14
  def diff(id, csv0, csv1, opts={})
13
15
 
14
- h0 = hash('old', id, csv0, opts)
15
- h1 = hash('new', id, csv1, opts)
16
+ h0 = hajh('old', id, csv0, opts)
17
+ h1 = hajh('new', id, csv1, opts)
16
18
 
17
19
  ks0 = h0.delete(:keys)
18
20
  ks1 = h1.delete(:keys)
@@ -21,12 +23,9 @@ module Cevennes
21
23
  h0
22
24
  .collect { |k, v|
23
25
  v1 = h1[k]
24
- if v1 == nil
25
- [ '-', *v, -1, nil ]
26
- elsif v1[1] == v[1]
27
- [ '=', *v, v[0], nil ]
28
- else
29
- [ '!', *v, *v1 ]
26
+ if v1 == nil then [ '-', *v, -1, nil ]
27
+ elsif v1[1] == v[1] then [ '=', *v, v[0], nil ]
28
+ else [ '!', *v, *v1 ]
30
29
  end }
31
30
 
32
31
  (h1.keys - h0.keys)
@@ -45,6 +44,101 @@ module Cevennes
45
44
  [ [ 'keys', *ks0, *ks1 ], [ 'stats', s ] ] + d
46
45
  end
47
46
 
47
+ def to_pretty_s(diff, opts={})
48
+
49
+ fail ArgumentError.new("to_pretty_s() expects a diff array") \
50
+ unless is_diff_array?(diff)
51
+
52
+ drop_eq = !! (opts[:drop_equals] || opts[:drop_eq])
53
+
54
+ c =
55
+ opts[:no_colours] || opts[:no_colors] ||
56
+ opts[:no_colour] || opts[:no_color] ?
57
+ Colorato.no_colours :
58
+ Colorato.colour_if_supported
59
+
60
+ cml = opts[:cell_max_len] || opts[:cell_maxlen] || 28
61
+
62
+ _, kl0, ks0, kl1, ks1 = diff[0]
63
+ _, stats = diff[1]
64
+
65
+ dis = diff[2..-1]
66
+
67
+ lnl = dis
68
+ .inject([]) { |a, di| a << di[1].to_s.length << di[3].to_s.length; a }
69
+ .max
70
+
71
+ o = StringIO.new
72
+
73
+ o << c.dg << "# diff\n" << c.rs
74
+
75
+ o << "\n* length: #{diff.length}\n"
76
+ o << "\n"
77
+
78
+ o << c.dg << "## keys\n\n" << c.rs
79
+
80
+ o << "```\n"
81
+ o << "### ks0\n"
82
+ o << (' ' * (lnl + 3)) << "# line: #{kl0}, count: #{ks0.size}\n"
83
+ o << render_row(
84
+ #ks0, ck: true, rn: kl0, colours: c, bc: c.dark_gray, indent: lnl + 3)
85
+ ks0, ck: true, colours: c, bc: c.dark_gray, indent: lnl + 3, cml: cml)
86
+ o << "\n### ks1\n"
87
+ o << c.rs << (' ' * (lnl + 3)) << "# line: #{kl1}, count: #{ks1.size}\n"
88
+ if ks1 == ks0
89
+ o << c.dg << (' ' * (lnl + 3)) << "# (identical)\n" << c.rs
90
+ else
91
+ o << render_row(
92
+ #ks1, ck: true, rn: kl1, colours: c, indent: lnl + 3) << "\n"
93
+ ks1, ck: true, colours: c, indent: lnl + 3, cml: cml) << "\n"
94
+ end
95
+ o << "```\n\n"
96
+
97
+ o << c.dg << "## stats\n\n" << c.rs
98
+ o << "```\n " << stats.inspect << "\n```\n\n"
99
+
100
+ o << c.dg << "## diffs\n\n"
101
+ o << "```\n"
102
+
103
+ diff[2..-1].each do |di|
104
+
105
+ next if drop_eq && (di[0] == '=')
106
+
107
+ bc =
108
+ case di[0]
109
+ when '+' then c.green
110
+ when '-' then c.red
111
+ else c.dg; end
112
+ o << bc
113
+
114
+ lnl1 = lnl + 3
115
+ os = { indent: lnl1, ck: true, colours: c, bc: bc, cml: cml }
116
+
117
+ r0 = di[2] ? render_row(di[2], os) : ' ' * (lnl1)
118
+ r1 = di[4] ? render_row(di[4], os.merge(vs: di[2])) : ' ' * (lnl1)
119
+
120
+ o << di[0] << " %#{lnl}d " % di[1] << r0.lstrip << "\n"
121
+ o << ' ' << " %#{lnl}d " % di[3] << r1.lstrip << "\n"
122
+
123
+ o << c.rs
124
+ end
125
+ o << "```\n"
126
+
127
+ o << c.dg << "\n## done |:." << c.rs
128
+
129
+ o.string
130
+ end
131
+
132
+ def is_diff_array?(x)
133
+
134
+ return false unless x.is_a?(Array)
135
+ return false unless x.length > 1
136
+ return false unless x.all? { |e| e.is_a?(Array) }
137
+ return false unless x[0][0] == 'keys'
138
+ return false unless x[1][0] == 'stats'
139
+ true
140
+ end
141
+
48
142
  protected
49
143
 
50
144
  def strip(row)
@@ -55,12 +149,14 @@ module Cevennes
55
149
  DOWNCASE = lambda { |x| x.respond_to?(:downcase) ? x.downcase : x }
56
150
  IDENTITY = lambda { |x| x }
57
151
 
58
- def hash(version, id, csv, opts)
152
+ # was named "hash", but since it shadowed a core method, let's use "hajh"
153
+ #
154
+ def hajh(version, id, csv, opts)
59
155
 
60
156
  d = opts[:ignore_key_case] ? DOWNCASE : IDENTITY
61
157
  did = d[id]
62
158
 
63
- csva = parse(csv)
159
+ csva = parse(csv, opts)
64
160
  .each_with_index.collect { |row, i| [ 1 + i, strip(row) ] }
65
161
  .reject { |i, row| row.compact.empty? }
66
162
  .drop_while { |i, row| ! row.find { |cell| d[cell] == did } }
@@ -84,33 +180,97 @@ module Cevennes
84
180
  h }
85
181
  end
86
182
 
87
- def parse(csv)
183
+ def parse(csv, opts=nil)
184
+
185
+ csv.is_a?(Array) ? csv :
186
+ ::CSV.parse(reencode(csv, opts))
187
+ end
188
+
189
+ def reencode(s, opts)
190
+
191
+ enc = opts ? opts[:encoding] : nil
192
+
193
+ (enc ? [ enc ] : [ 'UTF-8', 'Windows-1252', 'ISO-8859-1' ])
194
+ .each do |enc|
195
+ s1 = s.dup.force_encoding(enc); next unless s1.valid_encoding?
196
+ return s1.encode('UTF-8')
197
+ rescue Encoding::UndefinedConversionError
198
+ next
199
+ end
200
+
201
+ fail "failed to reencode #{(s.encoding rescue '(unknown encoding)')}"
202
+ end
203
+
204
+ require 'io/console'
88
205
 
89
- return csv if csv.is_a?(Array)
90
- ::CSV.parse(reencode(csv))
206
+ def term_width
207
+
208
+ w =
209
+ ENV['COLUMNS'] ||
210
+ ($stdout.winsize[1] rescue nil) ||
211
+ (IO.console.winsize[1] rescue nil) ||
212
+ (`tput cols`.to_i rescue nil) ||
213
+ (`stty size`.split[1].to_i rescue nil)
214
+
215
+ w.is_a?(Integer) && w > 0 ? w : 80
91
216
  end
92
217
 
93
- #def deflate(row)
94
- # ::CSV.generate(encoding: 'UTF-8') { |csv| csv << row }.strip
95
- #end
218
+ def render_row(row, opts={})
219
+
220
+ c = opts[:colours] || Colorato.no_colours
221
+ bc = opts[:base_colour] || opts[:bc] || ''
222
+
223
+ rs = c.reset + bc
224
+
225
+ indent = opts[:indent] || 0
226
+ ck = opts[:col_keys] || opts[:ck]
227
+
228
+ rn = opts[:row_number] || opts[:rn]
229
+ rn = rn.to_s if rn
230
+
231
+ cml = opts[:cml] || 28
96
232
 
97
- ENCODINGS = %w[ Windows-1252 ISO-8859-1 UTF-8 ].freeze
233
+ r0 = opts[:vs]
98
234
 
99
- def reencode(s)
235
+ ni = ' ' * indent
100
236
 
101
- #s = unzip(s) if s[0, 2] == 'PK'
102
- # no dependency on rubyzip
237
+ tw = term_width - 1
238
+ o = StringIO.new
103
239
 
104
- #return s if s.encoding == Encoding::UTF_8
105
- # NO! have to force_encoding for UTF-8 as well!
240
+ line = ni + bc + '[ '
241
+ llen = Colorato.no_colour_length(line)
242
+ #
243
+ row.each_with_index do |cell, i|
106
244
 
107
- s = s.dup if s.frozen?
245
+ id = (ck ? xelcol(1 + i) : '') + (rn ? rn : '') + (ck || rn ? ' ' : '')
246
+ id = c.rs + c.light_gray + id + rs if id.length > 0
108
247
 
109
- ENCODINGS.each do |e|
110
- (return s.force_encoding(e).encode('UTF-8')) rescue nil
248
+ t = cell.is_a?(String) ? Colorato.truncate_string(cell, cml, '…') : cell
249
+ ce = t.inspect
250
+ ce = c.rs + c.yellow + ce + rs if r0 && cell != r0[i]
251
+
252
+ cl = id + ce + ', '
253
+ clen = Colorato.no_colour_length(cl)
254
+
255
+ if indent + llen + clen > tw
256
+ o << line << "\n"
257
+ line = ni + ' '
258
+ llen = line.length
259
+ end
260
+ line += cl
261
+ llen += clen
111
262
  end
263
+ o << line << ']'
264
+
265
+ o.string
266
+ end
267
+
268
+ def xelcol(n)
269
+
270
+ r = []
271
+ while n > 0; n -= 1; r.unshift(('A'.ord + (n % 26)).chr); n /= 26; end
112
272
 
113
- nil
273
+ r.join
114
274
  end
115
275
  end
116
276
  end
metadata CHANGED
@@ -1,29 +1,56 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cevennes
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Mettraux
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2022-07-24 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
13
+ name: csv
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: colorato
14
28
  requirement: !ruby/object:Gem::Requirement
15
29
  requirements:
16
30
  - - "~>"
17
31
  - !ruby/object:Gem::Version
18
- version: '3.7'
19
- name: rspec
32
+ version: '1.2'
33
+ type: :runtime
20
34
  prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.2'
40
+ - !ruby/object:Gem::Dependency
41
+ name: probatio
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.6'
21
47
  type: :development
48
+ prerelease: false
22
49
  version_requirements: !ruby/object:Gem::Requirement
23
50
  requirements:
24
51
  - - "~>"
25
52
  - !ruby/object:Gem::Version
26
- version: '3.7'
53
+ version: '1.6'
27
54
  description: Diffs CSVs by lines, focusing on a single ID
28
55
  email:
29
56
  - jmettraux@gmail.com
@@ -47,7 +74,7 @@ metadata:
47
74
  bug_tracker_uri: https://github.com/jmettraux/cevennes/issues
48
75
  homepage_uri: https://github.com/jmettraux/cevennes
49
76
  source_code_uri: https://github.com/jmettraux/cevennes
50
- post_install_message:
77
+ rubygems_mfa_required: 'true'
51
78
  rdoc_options: []
52
79
  require_paths:
53
80
  - lib
@@ -62,8 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
62
89
  - !ruby/object:Gem::Version
63
90
  version: '0'
64
91
  requirements: []
65
- rubygems_version: 3.2.3
66
- signing_key:
92
+ rubygems_version: 3.6.9
67
93
  specification_version: 4
68
94
  summary: CSV diff library
69
95
  test_files: []