cevennes 1.2.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: 598f7e128effec146dbe0acc01bd26101b689a6efdcf647c061416aa43accb53
4
- data.tar.gz: b1cf17c58da5e0562100feff9fe40c1eee3cf07a2fc6fab0b724fbd792a240f9
3
+ metadata.gz: fca28f00b6cf84aa0856a46139b68b338509c834e2b1d274692da6e91285a47f
4
+ data.tar.gz: 5f3bbcc3b08ecdfa698e1ffa83b75832d938558533a9c2bff25dde682ba441e1
5
5
  SHA512:
6
- metadata.gz: 9686e580322ab076d7e85506d716aaac8e2d131d9af350b31b2985539bfc8d7a01f2b40dfd23e285e78e620e82dbc2cede2d1840c5d60f72d130b22f16cf3502
7
- data.tar.gz: 5bcef831927839276c9ca7f4304d5c6904f37587d4f4654ba88962250eafd59bfc7036ba1ff9f25824e898a2e8f5f87a250f3aa0317a7e1fadd696c4efe97453
6
+ metadata.gz: 39686c8f08786c2bd98367832b5cd08d2c40720a2cdc12dc6525dd328312a8352224c9727d62656fd0d740babc0584116ead5d284b378ef707a9b8c9d13abd59
7
+ data.tar.gz: 6929bc321836901230437ced040830ccdd40c10aebfad03a6383c4ee700597d4813f2e85d0a93a3eccf168181deadd85493717362fec285875b7015c25b2bd57
data/CHANGELOG.md CHANGED
@@ -2,50 +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
+
10
+ ## cevennes 1.3.0 released 2022-07-24
11
+
12
+ * Diff already parsed CSV as well
13
+
14
+
5
15
  ## cevennes 1.2.0 released 2022-07-05
6
16
 
7
- - Introduce `drop_equals: true`
17
+ * Introduce `drop_equals: true`
8
18
 
9
19
 
10
20
  ## cevennes 1.1.1 released 2021-02-25
11
21
 
12
- - Refine UTF-8 re-encoding
13
- - Fix `ignore_key_case: true` :-(
14
- - Clarify reencode
22
+ * Refine UTF-8 re-encoding
23
+ * Fix `ignore_key_case: true` :-(
24
+ * Clarify reencode
15
25
 
16
26
 
17
27
  ## cevennes 1.1.0 released 2021-02-24
18
28
 
19
- - Introduce `ignore_key_case: true`
29
+ * Introduce `ignore_key_case: true`
20
30
 
21
31
 
22
32
  ## cevennes 1.0.0 released 2018-09-07
23
33
 
24
- - Align the "keys" entry on the =+-! entries
34
+ * Align the "keys" entry on the =+-! entries
25
35
 
26
36
 
27
37
  ## cevennes 0.13.0 released 2018-09-07
28
38
 
29
- - Trim cells and keys
39
+ * Trim cells and keys
30
40
 
31
41
 
32
42
  ## cevennes 0.12.0 released 2018-09-06
33
43
 
34
- - Fail with IndexError on missing id in CSV old or new
44
+ * Fail with IndexError on missing id in CSV old or new
35
45
 
36
46
 
37
47
  ## cevennes 0.11.0 released 2018-09-06
38
48
 
39
- - Fix l0 and l1 stats
49
+ * Fix l0 and l1 stats
40
50
 
41
51
 
42
52
  ## cevennes 0.10.0 released 2018-09-03
43
53
 
44
- - Weave in additions at the right spot
45
- - Indicate line numbers
54
+ * Weave in additions at the right spot
55
+ * Indicate line numbers
46
56
 
47
57
 
48
58
  ## cevennes 0.9.0 released 2018-09-03
49
59
 
50
- - Implement first version of Cevennes.diff(id, cvs0, cvs1)
60
+ * Implement first version of Cevennes.diff(id, cvs0, cvs1)
51
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/README.md CHANGED
@@ -56,6 +56,8 @@ The second entry is a summary of the changes, altered `!` line count, removed `-
56
56
 
57
57
  The remaining entries are the (non-)changes themselves, from line 1 to the end.
58
58
 
59
+ Since 1.3.0, Cevennes accepts CSV strings and parsed CSV (array of arrays).
60
+
59
61
 
60
62
  ### drop_equals: true
61
63
 
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.2.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 = ::CSV.parse(reencode(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,27 +178,97 @@ module Cevennes
84
178
  h }
85
179
  end
86
180
 
87
- #def deflate(row)
88
- # ::CSV.generate(encoding: 'UTF-8') { |csv| csv << row }.strip
89
- #end
181
+ def parse(csv, opts=nil)
182
+
183
+ csv.is_a?(Array) ? csv :
184
+ ::CSV.parse(reencode(csv, opts))
185
+ end
186
+
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'
90
203
 
91
- ENCODINGS = %w[ Windows-1252 ISO-8859-1 UTF-8 ].freeze
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
92
215
 
93
- def reencode(s)
216
+ def render_row(row, opts={})
94
217
 
95
- #s = unzip(s) if s[0, 2] == 'PK'
96
- # no dependency on rubyzip
218
+ c = opts[:colours] || Colorato.no_colours
219
+ bc = opts[:base_colour] || opts[:bc] || ''
97
220
 
98
- #return s if s.encoding == Encoding::UTF_8
99
- # NO! have to force_encoding for UTF-8 as well!
221
+ rs = c.reset + bc
100
222
 
101
- s = s.dup if s.frozen?
223
+ indent = opts[:indent] || 0
224
+ ck = opts[:col_keys] || opts[:ck]
102
225
 
103
- ENCODINGS.each do |e|
104
- (return s.force_encoding(e).encode('UTF-8')) rescue nil
226
+ rn = opts[:row_number] || opts[:rn]
227
+ rn = rn.to_s if rn
228
+
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
105
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
106
270
 
107
- nil
271
+ r.join
108
272
  end
109
273
  end
110
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.2.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-05 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
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.2'
33
+ type: :runtime
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
14
42
  requirement: !ruby/object:Gem::Requirement
15
43
  requirements:
16
44
  - - "~>"
17
45
  - !ruby/object:Gem::Version
18
- version: '3.7'
19
- name: rspec
46
+ version: '1.6'
20
47
  type: :development
21
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.0.6
66
- signing_key:
92
+ rubygems_version: 3.6.9
67
93
  specification_version: 4
68
94
  summary: CSV diff library
69
95
  test_files: []