zubat 0.0.1 → 0.0.3

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: 3f4eb5790313a7a672cad41fcf05c8c5515659f265a60952efbf3bb28fad7fb1
4
- data.tar.gz: 0b94a1a09d207f79c9a32414655c4ec9706f47323659fc64d7f433f60ded012b
3
+ metadata.gz: baa42361d5aa2f8268e3d5be1e21cc2e0ae7e6b1ce839b43fe9b410f8678e92c
4
+ data.tar.gz: da15e758d143aa4ae0521d2281577e33cbd08dccd209de20e116068846bc715c
5
5
  SHA512:
6
- metadata.gz: 52c4ba88c4e15d3e947c0022b15f3da7477f7e8d5d7fbda9a0b43d7e8e61e13da446c59b7b6e54f425e32c31bf008bdbe1e3f19021f25c3c9ce728630e4eb586
7
- data.tar.gz: 34b2ad121d67f463e93f1cb19153dba16764e33b59e73aef0a7e7704610b695d6a5320fd8a19bb4108787973aa19cc4ad1a3a2b56aa6e4e76835df034de8138c
6
+ metadata.gz: 824f35f0dbaa6fa6ddb9cb37bf47f2f8d3221c393beae470465a979fa6773a807c07a305dfd737e81c53935c7f6cb6f7daba525c1204243803d8d36096656625
7
+ data.tar.gz: abd8a26623ed6a4d60bec503551463de0c9cbb7e551b57e6c69702d37280f616fd2158cecfe78906252de3db40ccc6b0f2d8aeeb28154a25e67ea78e061c7033
data/exe/zubat CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'zubat/cli'
4
+ require 'zubat/cli/app'
5
5
 
6
- Zubat::CLI.new.start(ARGV)
6
+ Zubat::CLI::App.new.start(ARGV)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Zubat
4
4
  class Analizer
5
- AnalizedResult = Data.define(:label, :stat)
5
+ AnalizedResult = Data.define(:label, :commit, :stat)
6
6
 
7
7
  # @param stat [Hash<String, Hash<String, Integer>>]
8
8
  Stat = Data.define(:stat) do
@@ -24,6 +24,10 @@ module Zubat
24
24
  stat.fetch(file).fetch(:smells).fetch(label, 0)
25
25
  end
26
26
 
27
+ def diff(file)
28
+ stat.fetch(file, {}).fetch(:diff, nil)
29
+ end
30
+
27
31
  def each(*, **, &block)
28
32
  stat.each(*, **, &block)
29
33
  end
@@ -33,24 +37,31 @@ module Zubat
33
37
  stat = {}
34
38
 
35
39
  files.each do |file|
36
- next unless commit.exists?(file:)
40
+ diff = commit.diff(file:)
41
+
42
+ next if diff.empty?
43
+
44
+ stat[file] = {}
45
+
46
+ stat[file].merge!(diff:)
37
47
 
38
48
  code = commit.show(file:)
39
49
 
40
50
  next unless RubyCode.new(code:).valid?
41
51
 
42
- stat[file] = FlogWrapper.new.examine(code).then do |examined|
43
- { average: examined.average, total_score: examined.total_score }
52
+ FlogWrapper.new.examine(code).then do |examined|
53
+ stat[file][:average] = examined.average
54
+ stat[file][:total_score] = examined.average
44
55
  end
45
56
 
46
57
  stat[file][:smells] = ReekWrapper.new.examine(code).map(&:smell_type).tally
47
58
  end
48
59
 
49
- # return if smells.empty? && stat.empty?
50
-
51
60
  stat = Stat.new(stat:)
52
61
 
53
- AnalizedResult.new(label: "#{commit.time.iso8601} (#{commit.sha})", stat:)
62
+ AnalizedResult.new(label: "#{commit.time.iso8601} (#{commit.sha})",
63
+ commit:,
64
+ stat:)
54
65
  end
55
66
  end
56
67
  end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zubat'
4
+ require 'optparse'
5
+
6
+ module Zubat
7
+ module CLI
8
+ class App
9
+ Argv = Data.define(:files, :silent, :root) do
10
+ def self.parse!(argv)
11
+ opt = OptionParser.new
12
+
13
+ root = nil
14
+ silent = false
15
+
16
+ opt.on('--silent', '-s') { silent = true }
17
+ opt.on('--root=ROOT') { |v| root = v }
18
+
19
+ files = opt.parse!(argv).uniq
20
+
21
+ abort 'no files to process, aborting.' if argv.empty?
22
+
23
+ new(files:, silent:, root:)
24
+ end
25
+ end
26
+
27
+ class GitRepo
28
+ URL_FORMATS = [
29
+ %r[https://(.+)/(.+)/(.+).git],
30
+ %r[git@(.+):(.+)/(.+).git],
31
+ %r[ssh://(.+)/(.+)/(.+)],
32
+ ]
33
+
34
+ def self.guess
35
+ url = GitCommandWrapper.new.remote_origin_url
36
+
37
+ matched = URL_FORMATS
38
+ .find { |format| format.match?(url) }
39
+ &.match(url)
40
+
41
+ return unless matched
42
+
43
+ new(hostname: matched[1], org: matched[2], repo: matched[3])
44
+ end
45
+
46
+ def initialize(hostname:, org:, repo:)
47
+ @hostname = hostname
48
+ @org = org
49
+ @repo = repo
50
+ end
51
+
52
+ def site_url
53
+ "https://#{@hostname}/#{@org}/#{@repo}"
54
+ end
55
+ end
56
+
57
+ class Progress
58
+ include Enumerable
59
+
60
+ def initialize(enum, silent:)
61
+ @enum = enum
62
+ @silent = silent
63
+ end
64
+
65
+ def each(&block)
66
+ @enum.each_with_index do |elem, i|
67
+ $stderr.print "\r#{%w[| / - \\][i % 4]} Analyzing... #{100 * i / @enum.size}%" unless @silent
68
+
69
+ block.call(elem)
70
+ end
71
+
72
+ warn "\r✨ Analized \n\n" unless @silent
73
+
74
+ @enum
75
+ end
76
+ end
77
+
78
+ def start(argv)
79
+ argv = Argv.parse!(argv)
80
+
81
+ files = argv.files
82
+
83
+ repo = nil
84
+
85
+ results = Dir.chdir(argv.root || Dir.pwd) do
86
+ repo = GitRepo.guess
87
+
88
+ commits = Zubat::Commit.find(files:)
89
+
90
+ Progress
91
+ .new(commits, silent: argv.silent)
92
+ .map { |commit| Zubat::Analizer.new.analize(files:, commit:) }
93
+ end
94
+
95
+ file = Generator.new.generate(results:, site_url: repo&.site_url)
96
+
97
+ puts "Generated - #{file}\n"
98
+ end
99
+ end
100
+ end
101
+ end
data/lib/zubat/commit.rb CHANGED
@@ -26,5 +26,9 @@ module Zubat
26
26
  def show(file:)
27
27
  GitCommandWrapper.new.show(sha: @sha, file:)
28
28
  end
29
+
30
+ def diff(file:)
31
+ GitCommandWrapper.new.diff(sha: @sha, file:)
32
+ end
29
33
  end
30
34
  end
@@ -4,8 +4,10 @@ module Zubat
4
4
  class Generator
5
5
  FILE = 'tmp/zubat/index.html'
6
6
 
7
- def generate(results:)
8
- erb = Zubat.root.join('templates/chart.html.erb').read
7
+ TEMPLATE = 'templates/chart.html.erb'
8
+
9
+ def generate(results:, site_url:)
10
+ erb = Zubat.root.join(TEMPLATE).read
9
11
 
10
12
  ylabels = []
11
13
 
@@ -33,14 +35,16 @@ module Zubat
33
35
 
34
36
  dataset[:data] << {
35
37
  label: xlabel,
38
+ commit_sha: result.commit.sha,
39
+ commit_diff: result.stat.diff(ylabel),
36
40
  complexity_total: result.stat.complexity_total(ylabel),
37
41
  complexity_average: result.stat.complexity_average(ylabel),
38
- smells_scores: result.stat.smell_scores(ylabel).map { |type, value| { type:, value: } }
42
+ smells_scores: result.stat.smell_scores(ylabel).map { |type, value| { type:, value: } },
39
43
  }
40
44
  end
41
45
  end
42
46
 
43
- html = ERB.new(erb).result_with_hash(datasets:)
47
+ html = ERB.new(erb).result_with_hash(datasets:, site_url:)
44
48
 
45
49
  file = File.expand_path(FILE)
46
50
 
@@ -7,6 +7,10 @@ require 'securerandom'
7
7
  module Zubat
8
8
  class GitCommandWrapper
9
9
  module Stub
10
+ def remote_origin_url
11
+ "https://github.com/mizoR/zubat.git"
12
+ end
13
+
10
14
  def log(files:)
11
15
  logs = files.map do
12
16
  Log.new(sha: SecureRandom.hex(3), time: Time.at(rand(1_900_000_000..1_900_999_999)))
@@ -32,6 +36,17 @@ module Zubat
32
36
  end
33
37
  SCRIPT
34
38
  end
39
+
40
+ def diff(sha:, file:) # rubocop:disable Lint/UnusedMethodArgument
41
+ <<~SCRIPT
42
+ diff --git a/hello_world.rb b/hello_world.rb
43
+ class HelloWorld
44
+ def show
45
+ + puts "Hello World"
46
+ end
47
+ end
48
+ SCRIPT
49
+ end
35
50
  end
36
51
 
37
52
  Log = Data.define(:sha, :time)
@@ -42,6 +57,10 @@ module Zubat
42
57
  instance
43
58
  end
44
59
 
60
+ def remote_origin_url
61
+ `git config --get remote.origin.url`.chomp
62
+ end
63
+
45
64
  def log(files:)
46
65
  logs = `git log --oneline --pretty=format:'{ "sha": "%h", "time": "%ad" }' -- #{files.join(' ')}`.split("\n")
47
66
 
@@ -65,5 +84,9 @@ module Zubat
65
84
  def show(sha:, file:)
66
85
  `git show #{sha}:#{file}`
67
86
  end
87
+
88
+ def diff(sha:, file:)
89
+ `git show #{sha} -- #{file}`
90
+ end
68
91
  end
69
92
  end
data/lib/zubat/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zubat
4
- VERSION = '0.0.1'
4
+ VERSION = '0.0.3'
5
5
  end
@@ -4,6 +4,10 @@
4
4
  <meta name="robots" content="noindex,nofollow">
5
5
  <meta charset="UTF-8">
6
6
 
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css" integrity="sha512-hasIneQUHlh06VNBe7f6ZcHmeRTLIaQWFd43YriJ0UND19bvYRauxthDg8E4eVNPm9bRUhr5JGeqH7FRFXQu5g==" crossorigin="anonymous" referrerpolicy="no-referrer" />
8
+
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js" integrity="sha512-D9gUyxqja7hBtkWpPWGt9wfbfaMGVt9gnyCvYa+jojwwPHLCzUm5i8rpk7vD7wNee9bA35eYIjobYPaQuKS1MQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
10
+
7
11
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/purecss@3.0.0/build/pure-min.css" integrity="sha384-X38yfunGUhNzHpBaEBsWLO+A0HDYOQi8ufWDkZ0k9e0eXz/tH3II7uKZ9msv++Ls" crossorigin="anonymous">
8
12
 
9
13
  <style>
@@ -253,6 +257,12 @@
253
257
  left: 200px;
254
258
  }
255
259
  }
260
+
261
+ #diffArea {
262
+ max-height: 400px;
263
+ overflow: auto;
264
+ }
265
+
256
266
  </style>
257
267
  <title>zubat</title>
258
268
  </head>
@@ -284,23 +294,17 @@
284
294
  </div>
285
295
 
286
296
  <div class="content">
297
+ <p><pre id="diffArea"></pre></p>
298
+
287
299
  <h2 id="complexity_total" class="content-subhead">Complexity Total</h2>
288
- <p>
289
- <canvas id="chart_of_code_complexity_total_trend" width="400" height="300"></canvas>
290
- </p>
300
+ <p><canvas id="chart_of_code_complexity_total_trend" width="400" height="200"></canvas></p>
291
301
 
292
302
  <h2 id="complexity_average" class="content-subhead">Complexity Average <small>- Complexity score per methods</small></h2>
293
- <p>
294
- <canvas id="chart_of_code_complexity_average_trend" width="400" height="300"></canvas>
295
- </p>
303
+ <p><canvas id="chart_of_code_complexity_average_trend" width="400" height="200"></canvas></p>
296
304
 
297
305
  <h2 id="code_smells" class="content-subhead">Code smells</h2>
298
- <p>
299
- <div id="select_of_code_smell_scores_trend"></div>
300
- </p>
301
- <p>
302
- <canvas id="chart_of_code_smell_scores_trend" width="400" height="300"></canvas>
303
- </p>
306
+ <p><div id="select_of_code_smell_scores_trend"></div></p>
307
+ <p><canvas id="chart_of_code_smell_scores_trend" width="400" height="200"></canvas></p>
304
308
  </div>
305
309
  </div>
306
310
  </div>
@@ -308,11 +312,34 @@
308
312
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
309
313
 
310
314
  <script>
315
+ var siteUrl = <%= site_url.to_json %>;
316
+
311
317
  var datasets = <%= datasets.to_json %>;
312
318
 
319
+ var onClickWith = function (chart) {
320
+ return function (e) {
321
+ const points = chart.getElementsAtEventForMode(e, 'nearest', { intersect: true }, true);
322
+
323
+ if (points.length === 0) return;
324
+
325
+ const point = points[0];
326
+ const label = chart.data.labels[point.index];
327
+ const value = chart.data.datasets[point.datasetIndex].data[point.index];
328
+
329
+ const code = document.createElement('code');
330
+ code.classList.add('language-diff');
331
+ code.textContent = value.commit_diff;
332
+ hljs.highlightBlock(code);
333
+
334
+ const diffArea = document.getElementById('diffArea');
335
+ diffArea.innerHTML = '';
336
+ diffArea.appendChild(code);
337
+ };
338
+ };
339
+
313
340
  var ctx0 = document.getElementById("chart_of_code_complexity_total_trend");
314
341
 
315
- new Chart(ctx0, {
342
+ var chart0 = new Chart(ctx0, {
316
343
  type: 'line',
317
344
  data: {
318
345
  datasets: datasets
@@ -333,9 +360,11 @@
333
360
  }
334
361
  });
335
362
 
363
+ ctx0.addEventListener('click', onClickWith(chart0));
364
+
336
365
  var ctx1 = document.getElementById("chart_of_code_complexity_average_trend");
337
366
 
338
- new Chart(ctx1, {
367
+ var chart1 = new Chart(ctx1, {
339
368
  type: 'line',
340
369
  data: {
341
370
  datasets: datasets
@@ -356,6 +385,8 @@
356
385
  }
357
386
  });
358
387
 
388
+ ctx1.addEventListener('click', onClickWith(chart1));
389
+
359
390
  var ctx2 = document.getElementById("chart_of_code_smell_scores_trend");
360
391
 
361
392
  var select = document.createElement('select');
@@ -369,8 +400,6 @@
369
400
  select.add(option);
370
401
  });
371
402
 
372
-
373
-
374
403
  select.addEventListener('change', function () {
375
404
  var file = select.value;
376
405
 
@@ -379,7 +408,7 @@
379
408
 
380
409
  document.getElementById("select_of_code_smell_scores_trend").appendChild(select);
381
410
 
382
- var chart3 = null;
411
+ var chart2 = null;
383
412
 
384
413
  var showCodeSmellChartFor = function (file) {
385
414
  var data = datasets.find(item => item.label === file).data;
@@ -400,9 +429,9 @@
400
429
  };
401
430
  });
402
431
 
403
- if (chart3) chart3.destroy();
432
+ if (chart2) chart2.destroy();
404
433
 
405
- chart3 = new Chart(ctx2, {
434
+ chart2 = new Chart(ctx2, {
406
435
  type: 'line',
407
436
  data: {
408
437
  datasets: smellsets
@@ -422,6 +451,8 @@
422
451
  }
423
452
  }
424
453
  });
454
+
455
+ ctx2.addEventListener('click', onClickWith(chart2));
425
456
  };
426
457
 
427
458
  showCodeSmellChartFor(datasets[0].label);
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zubat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - mizokami
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-08 00:00:00.000000000 Z
11
+ date: 2023-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: flog
@@ -54,7 +54,7 @@ files:
54
54
  - exe/zubat
55
55
  - lib/zubat.rb
56
56
  - lib/zubat/analizer.rb
57
- - lib/zubat/cli.rb
57
+ - lib/zubat/cli/app.rb
58
58
  - lib/zubat/commit.rb
59
59
  - lib/zubat/flog_wrapper.rb
60
60
  - lib/zubat/generator.rb
data/lib/zubat/cli.rb DELETED
@@ -1,73 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'zubat'
4
- require 'optparse'
5
-
6
- module Zubat
7
- class CLI
8
- Argv = Data.define(:files, :silent, :root) do
9
- def self.parse!(argv)
10
- opt = OptionParser.new
11
-
12
- root = nil
13
- silent = false
14
-
15
- opt.on('--silent', '-s') { silent = true }
16
- opt.on('--root=ROOT') { |v| root = v }
17
-
18
- files = opt.parse!(argv)
19
-
20
- abort 'no files to process, aborting.' if files.empty? && argv.empty?
21
-
22
- Dir.chdir(root || Dir.pwd) do
23
- files = files.uniq
24
- end
25
-
26
- new(files:, silent:, root:)
27
- end
28
- end
29
-
30
- class Progress
31
- include Enumerable
32
-
33
- def initialize(enum, silent:)
34
- @enum = enum
35
- @silent = silent
36
- end
37
-
38
- def each(&block)
39
- @enum.each_with_index do |elem, i|
40
- $stderr.print "\r#{%w[| / - \\][i % 4]} Analyzing... #{100 * i / @enum.size}%" unless @silent
41
-
42
- block.call(elem)
43
- end
44
-
45
- warn "\r✨ Analized \n\n" unless @silent
46
-
47
- @enum
48
- end
49
- end
50
-
51
- def start(argv)
52
- argv = Argv.parse!(argv)
53
-
54
- generator = Generator.new
55
-
56
- results = Dir.chdir(argv.root || Dir.pwd) do
57
- files = argv.files
58
-
59
- analizer = Zubat::Analizer.new
60
-
61
- commits = Zubat::Commit.find(files:)
62
-
63
- progress = Progress.new(commits, silent: argv.silent)
64
-
65
- progress.map { |commit| analizer.analize(files:, commit:) }
66
- end
67
-
68
- file = generator.generate(results:)
69
-
70
- puts "Generated - #{file}\n"
71
- end
72
- end
73
- end