cevennes 1.3.0 → 1.4.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: 435cddee7086da63e735457390edf766d42522bf58626b95ead034c418899719
4
- data.tar.gz: 20f5371e968decbfc05d062d44ae2cef6f1856716a9e58d321133b6d3089267e
3
+ metadata.gz: fca28f00b6cf84aa0856a46139b68b338509c834e2b1d274692da6e91285a47f
4
+ data.tar.gz: 5f3bbcc3b08ecdfa698e1ffa83b75832d938558533a9c2bff25dde682ba441e1
5
5
  SHA512:
6
- metadata.gz: ac1aa7f98147a393812a2fec1fdf6e6c714d35a5e325a7a2912f484db7d59212ee66daed23fdbe0013d11b18e737ba2b5e7f9527021ef7300cc9742c0313f80f
7
- data.tar.gz: 44b7de7c27260dd623a466c880e4f9c789cf429efc6aa53671f092bfd9449f5d7c5841796da5b517faa34f9dfad202527b98ff8816b940ab4e345e0bfeb4f81b
6
+ metadata.gz: 39686c8f08786c2bd98367832b5cd08d2c40720a2cdc12dc6525dd328312a8352224c9727d62656fd0d740babc0584116ead5d284b378ef707a9b8c9d13abd59
7
+ data.tar.gz: 6929bc321836901230437ced040830ccdd40c10aebfad03a6383c4ee700597d4813f2e85d0a93a3eccf168181deadd85493717362fec285875b7015c25b2bd57
data/CHANGELOG.md CHANGED
@@ -2,55 +2,60 @@
2
2
  # CHANGELOG.md
3
3
 
4
4
 
5
+ ## cevennes 1.4.0 released 2026-10-19
6
+
7
+ * Bring in .to_pretty_s(diff)
8
+
9
+
5
10
  ## cevennes 1.3.0 released 2022-07-24
6
11
 
7
- - Diff already parsed CSV as well
12
+ * Diff already parsed CSV as well
8
13
 
9
14
 
10
15
  ## cevennes 1.2.0 released 2022-07-05
11
16
 
12
- - Introduce `drop_equals: true`
17
+ * Introduce `drop_equals: true`
13
18
 
14
19
 
15
20
  ## cevennes 1.1.1 released 2021-02-25
16
21
 
17
- - Refine UTF-8 re-encoding
18
- - Fix `ignore_key_case: true` :-(
19
- - Clarify reencode
22
+ * Refine UTF-8 re-encoding
23
+ * Fix `ignore_key_case: true` :-(
24
+ * Clarify reencode
20
25
 
21
26
 
22
27
  ## cevennes 1.1.0 released 2021-02-24
23
28
 
24
- - Introduce `ignore_key_case: true`
29
+ * Introduce `ignore_key_case: true`
25
30
 
26
31
 
27
32
  ## cevennes 1.0.0 released 2018-09-07
28
33
 
29
- - Align the "keys" entry on the =+-! entries
34
+ * Align the "keys" entry on the =+-! entries
30
35
 
31
36
 
32
37
  ## cevennes 0.13.0 released 2018-09-07
33
38
 
34
- - Trim cells and keys
39
+ * Trim cells and keys
35
40
 
36
41
 
37
42
  ## cevennes 0.12.0 released 2018-09-06
38
43
 
39
- - Fail with IndexError on missing id in CSV old or new
44
+ * Fail with IndexError on missing id in CSV old or new
40
45
 
41
46
 
42
47
  ## cevennes 0.11.0 released 2018-09-06
43
48
 
44
- - Fix l0 and l1 stats
49
+ * Fix l0 and l1 stats
45
50
 
46
51
 
47
52
  ## cevennes 0.10.0 released 2018-09-03
48
53
 
49
- - Weave in additions at the right spot
50
- - Indicate line numbers
54
+ * Weave in additions at the right spot
55
+ * Indicate line numbers
51
56
 
52
57
 
53
58
  ## cevennes 0.9.0 released 2018-09-03
54
59
 
55
- - Implement first version of Cevennes.diff(id, cvs0, cvs1)
60
+ * Implement first version of Cevennes.diff(id, cvs0, cvs1)
56
61
 
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
@@ -5,14 +5,14 @@ require 'csv'
5
5
 
6
6
  module Cevennes
7
7
 
8
- VERSION = '1.3.0'
8
+ VERSION = '1.4.0'
9
9
 
10
10
  class << self
11
11
 
12
12
  def diff(id, csv0, csv1, opts={})
13
13
 
14
- h0 = hash('old', id, csv0, opts)
15
- h1 = hash('new', id, csv1, opts)
14
+ h0 = hajh('old', id, csv0, opts)
15
+ h1 = hajh('new', id, csv1, opts)
16
16
 
17
17
  ks0 = h0.delete(:keys)
18
18
  ks1 = h1.delete(:keys)
@@ -21,12 +21,9 @@ module Cevennes
21
21
  h0
22
22
  .collect { |k, v|
23
23
  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 ]
24
+ if v1 == nil then [ '-', *v, -1, nil ]
25
+ elsif v1[1] == v[1] then [ '=', *v, v[0], nil ]
26
+ else [ '!', *v, *v1 ]
30
27
  end }
31
28
 
32
29
  (h1.keys - h0.keys)
@@ -45,6 +42,101 @@ module Cevennes
45
42
  [ [ 'keys', *ks0, *ks1 ], [ 'stats', s ] ] + d
46
43
  end
47
44
 
45
+ def to_pretty_s(diff, opts={})
46
+
47
+ fail ArgumentError.new("to_pretty_s() expects a diff array") \
48
+ unless is_diff_array?(diff)
49
+
50
+ drop_eq = !! (opts[:drop_equals] || opts[:drop_eq])
51
+
52
+ c =
53
+ opts[:no_colours] || opts[:no_colors] ||
54
+ opts[:no_colour] || opts[:no_color] ?
55
+ Colorato.no_colours :
56
+ Colorato.colour_if_supported
57
+
58
+ cml = opts[:cell_max_len] || opts[:cell_maxlen] || 28
59
+
60
+ _, kl0, ks0, kl1, ks1 = diff[0]
61
+ _, stats = diff[1]
62
+
63
+ dis = diff[2..-1]
64
+
65
+ lnl = dis
66
+ .inject([]) { |a, di| a << di[1].to_s.length << di[3].to_s.length; a }
67
+ .max
68
+
69
+ o = StringIO.new
70
+
71
+ o << c.dg << "# diff\n" << c.rs
72
+
73
+ o << "\n* length: #{diff.length}\n"
74
+ o << "\n"
75
+
76
+ o << c.dg << "## keys\n\n" << c.rs
77
+
78
+ o << "```\n"
79
+ o << "### ks0\n"
80
+ o << (' ' * (lnl + 3)) << "# line: #{kl0}, count: #{ks0.size}\n"
81
+ o << render_row(
82
+ #ks0, ck: true, rn: kl0, colours: c, bc: c.dark_gray, indent: lnl + 3)
83
+ ks0, ck: true, colours: c, bc: c.dark_gray, indent: lnl + 3, cml: cml)
84
+ o << "\n### ks1\n"
85
+ o << c.rs << (' ' * (lnl + 3)) << "# line: #{kl1}, count: #{ks1.size}\n"
86
+ if ks1 == ks0
87
+ o << c.dg << (' ' * (lnl + 3)) << "# (identical)\n" << c.rs
88
+ else
89
+ o << render_row(
90
+ #ks1, ck: true, rn: kl1, colours: c, indent: lnl + 3) << "\n"
91
+ ks1, ck: true, colours: c, indent: lnl + 3, cml: cml) << "\n"
92
+ end
93
+ o << "```\n\n"
94
+
95
+ o << c.dg << "## stats\n\n" << c.rs
96
+ o << "```\n " << stats.inspect << "\n```\n\n"
97
+
98
+ o << c.dg << "## diffs\n\n"
99
+ o << "```\n"
100
+
101
+ diff[2..-1].each do |di|
102
+
103
+ next if drop_eq && (di[0] == '=')
104
+
105
+ bc =
106
+ case di[0]
107
+ when '+' then c.green
108
+ when '-' then c.red
109
+ else c.dg; end
110
+ o << bc
111
+
112
+ lnl1 = lnl + 3
113
+ os = { indent: lnl1, ck: true, colours: c, bc: bc, cml: cml }
114
+
115
+ r0 = di[2] ? render_row(di[2], os) : ' ' * (lnl1)
116
+ r1 = di[4] ? render_row(di[4], os.merge(vs: di[2])) : ' ' * (lnl1)
117
+
118
+ o << di[0] << " %#{lnl}d " % di[1] << r0.lstrip << "\n"
119
+ o << ' ' << " %#{lnl}d " % di[3] << r1.lstrip << "\n"
120
+
121
+ o << c.rs
122
+ end
123
+ o << "```\n"
124
+
125
+ o << c.dg << "\n## done |:." << c.rs
126
+
127
+ o.string
128
+ end
129
+
130
+ def is_diff_array?(x)
131
+
132
+ return false unless x.is_a?(Array)
133
+ return false unless x.length > 1
134
+ return false unless x.all? { |e| e.is_a?(Array) }
135
+ return false unless x[0][0] == 'keys'
136
+ return false unless x[1][0] == 'stats'
137
+ true
138
+ end
139
+
48
140
  protected
49
141
 
50
142
  def strip(row)
@@ -55,12 +147,14 @@ module Cevennes
55
147
  DOWNCASE = lambda { |x| x.respond_to?(:downcase) ? x.downcase : x }
56
148
  IDENTITY = lambda { |x| x }
57
149
 
58
- def hash(version, id, csv, opts)
150
+ # was named "hash", but since it shadowed a core method, let's use "hajh"
151
+ #
152
+ def hajh(version, id, csv, opts)
59
153
 
60
154
  d = opts[:ignore_key_case] ? DOWNCASE : IDENTITY
61
155
  did = d[id]
62
156
 
63
- csva = parse(csv)
157
+ csva = parse(csv, opts)
64
158
  .each_with_index.collect { |row, i| [ 1 + i, strip(row) ] }
65
159
  .reject { |i, row| row.compact.empty? }
66
160
  .drop_while { |i, row| ! row.find { |cell| d[cell] == did } }
@@ -84,33 +178,97 @@ module Cevennes
84
178
  h }
85
179
  end
86
180
 
87
- def parse(csv)
181
+ def parse(csv, opts=nil)
88
182
 
89
- return csv if csv.is_a?(Array)
90
- ::CSV.parse(reencode(csv))
183
+ csv.is_a?(Array) ? csv :
184
+ ::CSV.parse(reencode(csv, opts))
91
185
  end
92
186
 
93
- #def deflate(row)
94
- # ::CSV.generate(encoding: 'UTF-8') { |csv| csv << row }.strip
95
- #end
187
+ def reencode(s, opts)
188
+
189
+ enc = opts ? opts[:encoding] : nil
190
+
191
+ (enc ? [ enc ] : [ 'UTF-8', 'Windows-1252', 'ISO-8859-1' ])
192
+ .each do |enc|
193
+ s1 = s.dup.force_encoding(enc); next unless s1.valid_encoding?
194
+ return s1.encode('UTF-8')
195
+ rescue Encoding::UndefinedConversionError
196
+ next
197
+ end
198
+
199
+ fail "failed to reencode #{(s.encoding rescue '(unknown encoding)')}"
200
+ end
201
+
202
+ require 'io/console'
203
+
204
+ def term_width
205
+
206
+ w =
207
+ ENV['COLUMNS'] ||
208
+ ($stdout.winsize[1] rescue nil) ||
209
+ (IO.console.winsize[1] rescue nil) ||
210
+ (`tput cols`.to_i rescue nil) ||
211
+ (`stty size`.split[1].to_i rescue nil)
212
+
213
+ w.is_a?(Integer) && w > 0 ? w : 80
214
+ end
96
215
 
97
- ENCODINGS = %w[ Windows-1252 ISO-8859-1 UTF-8 ].freeze
216
+ def render_row(row, opts={})
98
217
 
99
- def reencode(s)
218
+ c = opts[:colours] || Colorato.no_colours
219
+ bc = opts[:base_colour] || opts[:bc] || ''
100
220
 
101
- #s = unzip(s) if s[0, 2] == 'PK'
102
- # no dependency on rubyzip
221
+ rs = c.reset + bc
103
222
 
104
- #return s if s.encoding == Encoding::UTF_8
105
- # NO! have to force_encoding for UTF-8 as well!
223
+ indent = opts[:indent] || 0
224
+ ck = opts[:col_keys] || opts[:ck]
106
225
 
107
- s = s.dup if s.frozen?
226
+ rn = opts[:row_number] || opts[:rn]
227
+ rn = rn.to_s if rn
108
228
 
109
- ENCODINGS.each do |e|
110
- (return s.force_encoding(e).encode('UTF-8')) rescue nil
229
+ cml = opts[:cml] || 28
230
+
231
+ r0 = opts[:vs]
232
+
233
+ ni = ' ' * indent
234
+
235
+ tw = term_width - 1
236
+ o = StringIO.new
237
+
238
+ line = ni + bc + '[ '
239
+ llen = Colorato.no_colour_length(line)
240
+ #
241
+ row.each_with_index do |cell, i|
242
+
243
+ id = (ck ? xelcol(1 + i) : '') + (rn ? rn : '') + (ck || rn ? ' ' : '')
244
+ id = c.rs + c.light_gray + id + rs if id.length > 0
245
+
246
+ t = cell.is_a?(String) ? Colorato.truncate_string(cell, cml, '…') : cell
247
+ ce = t.inspect
248
+ ce = c.rs + c.yellow + ce + rs if r0 && cell != r0[i]
249
+
250
+ cl = id + ce + ', '
251
+ clen = Colorato.no_colour_length(cl)
252
+
253
+ if indent + llen + clen > tw
254
+ o << line << "\n"
255
+ line = ni + ' '
256
+ llen = line.length
257
+ end
258
+ line += cl
259
+ llen += clen
111
260
  end
261
+ o << line << ']'
262
+
263
+ o.string
264
+ end
265
+
266
+ def xelcol(n)
267
+
268
+ r = []
269
+ while n > 0; n -= 1; r.unshift(('A'.ord + (n % 26)).chr); n /= 26; end
112
270
 
113
- nil
271
+ r.join
114
272
  end
115
273
  end
116
274
  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.0
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: []