akainaa 0.1.0 → 0.1.2

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: 305e44e37e3cdb6430af4d8bc1135516f8d0fa050af34c84684ec1bc03ace5fd
4
- data.tar.gz: 3360375cb4c185139a82c5c3694cb01cd174f3be3a4d45cd58ea7544239bdb09
3
+ metadata.gz: 6451f71725cda32349ab87402b32e6e132d8185274ba55a930fa2389fdd12279
4
+ data.tar.gz: 2b40049516aa512d8a89b607d327b1a5571b3c4221e351248d4ac13bbdc31dfd
5
5
  SHA512:
6
- metadata.gz: a7e609d7142282087b333c4eb842e77c4e8136ec7d90abbed23b4e32afa07541a46c5435ca0402a33c8b1755df1ea99b3aa85edfc8710805dfaf4d3dfbec0ace
7
- data.tar.gz: de14f04bbe4c5cf13355311c4550a931128964adf26d8316944adaa8c8e32f6f63bad1a9ae35e73b39489a3c299f8a64d115ae285e4c2ce1b3633d86d1bab63f
6
+ metadata.gz: f3199e1047c1295040cf043154128fcef145da13815f1cf8b69c84ec4a3237e44c525107ccb97abcc115d9d12f810e40623685843e190bb07cb99f95e35b104d
7
+ data.tar.gz: 74c12cf59e645eef87f2811d95d584a32ed5ec54b6f75019114c8f595fa206e0121cf7413c87a3b53fb4523f62cc7b45ee004aab467594f4dffd2153af00d045
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.2] - 2024-05-06
4
+
5
+ - Coloring all line of the method which located on multiple lines
6
+
7
+ ## [0.1.1] - 2024-04-28
8
+
9
+ - Show the most executed file if path query isn't provided
10
+
3
11
  ## [0.1.0] - 2024-03-09
4
12
 
5
13
  - Initial release
data/README.md CHANGED
@@ -1,15 +1,15 @@
1
1
  # Akainaa (赤いなぁ)
2
2
 
3
- ![page view](./img/webui.png)
3
+ ![page view](./img/webui-rev02.png)
4
4
 
5
- Akainaa is a gem that employs the Coverage library to record
6
- the number of executed count of code in a rack application
7
- and provides a Web UI that shows the recorded status.
5
+ Akainaa is a gem to visualize code hotspot based on Coverage.
8
6
  This gem can be used for the following purposes:
9
7
 
10
8
  - super rough code profiler: The intensity of the red background for each line is proportional to the number of times it has been executed.
11
9
  - A tool helps to understand what happened in a request: Akainaa have the reset button on Web UI, so user can easily record code execution for only one request.
12
10
 
11
+ You can see actual result view from [here](https://riseshia.github.io/akainaa/isucon13-baseline.html), which recorded on [isucon13](https://github.com/isucon/isucon13/tree/main/webapp/ruby) code in one benchmark run on [ISUNARABE](https://isunarabe.org/). And also [here](https://riseshia.github.io/akainaa/coverage/index.html) is the result of [simplecov-html](https://github.com/simplecov-ruby/simplecov-html)(0.12.3) from same coverage, so you can compare the result.
12
+
13
13
  ## Installation
14
14
 
15
15
  Install the gem and add to the application's Gemfile by executing:
data/akainaa.gemspec CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.description = spec.summary
13
13
  spec.homepage = "https://github.com/riseshia/akainaa"
14
14
  spec.license = "MIT"
15
- spec.required_ruby_version = ">= 3.0.0"
15
+ spec.required_ruby_version = ">= 3.2.0"
16
16
 
17
17
  spec.metadata["homepage_uri"] = spec.homepage
18
18
  spec.metadata["source_code_uri"] = spec.homepage
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.files = Dir.chdir(__dir__) do
24
24
  `git ls-files -z`.split("\x0").reject do |f|
25
25
  (File.expand_path(f) == __FILE__) ||
26
- f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
26
+ f.start_with?(*%w[bin/ test/ sample/ docs/ img/ .git .github Gemfile Rakefile])
27
27
  end
28
28
  end
29
29
  spec.bindir = "exe"
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ module Akainaa
6
+ class CallNodeVisitor < Prism::Visitor
7
+ MethodRange = Data.define(:name, :start_line, :end_line) do
8
+ def start_line_as_idx
9
+ start_line - 1
10
+ end
11
+
12
+ def method_row_range_as_idx
13
+ (start_line - 1)..(end_line - 1)
14
+ end
15
+ end
16
+
17
+ attr_reader :multiline_method_calls
18
+
19
+ def initialize
20
+ super
21
+
22
+ @multiline_method_calls = []
23
+ end
24
+
25
+ def visit_call_node(node)
26
+ if node.arguments &&
27
+ node.message_loc.start_line != node.arguments.location&.end_line
28
+
29
+ @multiline_method_calls << MethodRange.new(
30
+ name: node.message,
31
+ start_line: node.message_loc.start_line,
32
+ end_line: node.arguments.location.end_line,
33
+ )
34
+ end
35
+
36
+ super
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Akainaa
4
+ module Util
5
+ module_function
6
+
7
+ # @param [String] source_path
8
+ # @param [Array<Integer>] lines
9
+ #
10
+ # @return [Array<Integer>]
11
+ def fullfill_multiline_method_calls(source_path, lines)
12
+ result = Prism.parse_file(source_path)
13
+ prog_node = result.value
14
+
15
+ visitor = ::Akainaa::CallNodeVisitor.new
16
+ visitor.visit(prog_node)
17
+
18
+ fullfilled_lines = lines.dup
19
+
20
+ visitor.multiline_method_calls.each do |method_range|
21
+ call_count = lines[method_range.start_line_as_idx]
22
+
23
+ method_range.method_row_range_as_idx.each do |idx|
24
+ if fullfilled_lines[idx].nil?
25
+ fullfilled_lines[idx] = call_count
26
+ elsif fullfilled_lines[idx] < call_count
27
+ fullfilled_lines[idx] = call_count
28
+ else
29
+ # use as it is
30
+ end
31
+ end
32
+ end
33
+
34
+ fullfilled_lines
35
+ end
36
+ end
37
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Akainaa
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.2"
5
5
  end
data/lib/akainaa.rb CHANGED
@@ -3,6 +3,8 @@
3
3
  require 'coverage'
4
4
 
5
5
  require_relative 'akainaa/version'
6
+ require_relative 'akainaa/call_node_visitor'
7
+ require_relative 'akainaa/util'
6
8
 
7
9
  module Akainaa
8
10
  class Error < StandardError; end
@@ -54,7 +56,7 @@ module Akainaa
54
56
 
55
57
  private def extract_path_from_query(env)
56
58
  matched = env['QUERY_STRING'].match(/path=([^&]+)/)
57
- matched ? matched[1] : 'app.rb'
59
+ matched ? matched[1] : nil
58
60
  end
59
61
 
60
62
  private def render_line(lineno, code, count, count_top)
@@ -74,13 +76,10 @@ module Akainaa
74
76
  HTML
75
77
  end
76
78
 
77
- private def render_filelist(coverage_result, current_path:)
79
+ private def render_filelist(coverage_result, summary:, current_path:)
78
80
  files = coverage_result.keys
79
- max_count_on_proj = coverage_result
80
- .values
81
- .map { |cv| cv[:lines].reject(&:nil?).sum }
82
- .max + 1
83
- max_count_witdh = max_count_on_proj.to_s.size
81
+ max_count_on_proj = summary.max_count_on_proj
82
+ max_count_width = max_count_on_proj.to_s.size
84
83
 
85
84
  li_elements = files.sort.map do |file|
86
85
  total_count_on_file = coverage_result[file][:lines].reject(&:nil?).sum
@@ -89,7 +88,7 @@ module Akainaa
89
88
  class_suffix = file == current_path ? ' current' : ''
90
89
  <<~HTML
91
90
  <li class="pure-menu-item">
92
- <a href="/akainaa?path=#{file}" class="pure-menu-link filepath#{class_suffix} count-p#{count_top}"">(#{total_count_on_file.to_s.rjust(max_count_witdh)}) #{file}</a>
91
+ <a href="/akainaa?path=#{file}" class="pure-menu-link filepath#{class_suffix} count-p#{count_top}"">(#{total_count_on_file.to_s.rjust(max_count_width)}) #{file}</a>
93
92
  </li>
94
93
  HTML
95
94
  end.join
@@ -110,9 +109,25 @@ module Akainaa
110
109
  HTML
111
110
  end
112
111
 
112
+ CoverageSummary = Data.define(:file_path_has_max_count, :max_count_on_proj)
113
+
114
+ private def generate_summary(coverage_result)
115
+ file_path_has_max_count = coverage_result.max_by { |_, cv| cv[:lines].reject(&:nil?).sum }.first
116
+ max_count_on_proj = coverage_result
117
+ .values
118
+ .map { |cv| cv[:lines].reject(&:nil?).sum }
119
+ .max + 1
120
+
121
+ CoverageSummary.new(file_path_has_max_count:, max_count_on_proj:)
122
+ end
123
+
113
124
  private def render_page(path)
114
125
  result = Akainaa.peek_result
115
126
 
127
+ summary = generate_summary(result)
128
+
129
+ path = summary.file_path_has_max_count if path.nil?
130
+
116
131
  path_result = result[path]
117
132
 
118
133
  if path_result.nil?
@@ -123,7 +138,7 @@ module Akainaa
123
138
  return "<" + "p>#{path} not found.<" + "/p>"
124
139
  end
125
140
 
126
- coverage_on_line = path_result[:lines]
141
+ coverage_on_line = Akainaa::Util.fullfill_multiline_method_calls(path, path_result[:lines])
127
142
  max_count_on_file = coverage_on_line.reject(&:nil?).max + 1
128
143
 
129
144
  lines = []
@@ -135,7 +150,7 @@ module Akainaa
135
150
  lines << line
136
151
  end
137
152
 
138
- filelist = render_filelist(result, current_path: path)
153
+ filelist = render_filelist(result, summary:, current_path: path)
139
154
 
140
155
  <<~HTML
141
156
  <!DOCTYPE html>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: akainaa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shia
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-03-14 00:00:00.000000000 Z
11
+ date: 2024-05-06 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Minimum rack middleware for coverage
14
14
  email:
@@ -20,20 +20,11 @@ files:
20
20
  - CHANGELOG.md
21
21
  - LICENSE
22
22
  - README.md
23
- - Rakefile
24
23
  - akainaa.gemspec
25
- - img/webui.png
26
24
  - lib/akainaa.rb
25
+ - lib/akainaa/call_node_visitor.rb
26
+ - lib/akainaa/util.rb
27
27
  - lib/akainaa/version.rb
28
- - sample/.ruby-version
29
- - sample/Gemfile
30
- - sample/Gemfile.lock
31
- - sample/app.rb
32
- - sample/config.ru
33
- - sample/notification.rb
34
- - sample/theme.rb
35
- - sample/user.rb
36
- - sample/util.rb
37
28
  homepage: https://github.com/riseshia/akainaa
38
29
  licenses:
39
30
  - MIT
@@ -49,7 +40,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
49
40
  requirements:
50
41
  - - ">="
51
42
  - !ruby/object:Gem::Version
52
- version: 3.0.0
43
+ version: 3.2.0
53
44
  required_rubygems_version: !ruby/object:Gem::Requirement
54
45
  requirements:
55
46
  - - ">="
data/Rakefile DELETED
@@ -1,4 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- task default: %i[]
data/img/webui.png DELETED
Binary file
data/sample/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 3.3.0
data/sample/Gemfile DELETED
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "sinatra", "~> 4.0"
6
- gem "sinatra-contrib", "~> 4.0"
7
-
8
- gem "puma", "~> 6.4"
9
-
10
- gem "akainaa", path: "../"
data/sample/Gemfile.lock DELETED
@@ -1,48 +0,0 @@
1
- PATH
2
- remote: ..
3
- specs:
4
- akainaa (0.1.0)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- base64 (0.2.0)
10
- multi_json (1.15.0)
11
- mustermann (3.0.0)
12
- ruby2_keywords (~> 0.0.1)
13
- nio4r (2.7.0)
14
- puma (6.4.2)
15
- nio4r (~> 2.0)
16
- rack (3.0.9.1)
17
- rack-protection (4.0.0)
18
- base64 (>= 0.1.0)
19
- rack (>= 3.0.0, < 4)
20
- rack-session (2.0.0)
21
- rack (>= 3.0.0)
22
- ruby2_keywords (0.0.5)
23
- sinatra (4.0.0)
24
- mustermann (~> 3.0)
25
- rack (>= 3.0.0, < 4)
26
- rack-protection (= 4.0.0)
27
- rack-session (>= 2.0.0, < 3)
28
- tilt (~> 2.0)
29
- sinatra-contrib (4.0.0)
30
- multi_json (>= 0.0.2)
31
- mustermann (~> 3.0)
32
- rack-protection (= 4.0.0)
33
- sinatra (= 4.0.0)
34
- tilt (~> 2.0)
35
- tilt (2.3.0)
36
-
37
- PLATFORMS
38
- ruby
39
- x86_64-linux
40
-
41
- DEPENDENCIES
42
- akainaa!
43
- puma (~> 6.4)
44
- sinatra (~> 4.0)
45
- sinatra-contrib (~> 4.0)
46
-
47
- BUNDLED WITH
48
- 2.5.3
data/sample/app.rb DELETED
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'sinatra/base'
4
- require 'sinatra/json'
5
- require 'securerandom'
6
-
7
- require_relative './user'
8
- require_relative './notification'
9
- require_relative './util'
10
-
11
- class App < Sinatra::Base
12
- enable :logging
13
-
14
- get '/api/me' do
15
- 1.times { Util.do_something }
16
- 2.times { Util.do_something }
17
- 3.times { Util.do_something }
18
- 4.times { Util.do_something }
19
- 5.times { Util.do_something }
20
- 6.times { Util.do_something }
21
- 7.times { Util.do_something }
22
- 8.times { Util.do_something }
23
- 9.times { Util.do_something }
24
- 10.times { Util.do_something }
25
-
26
- json(
27
- name: "riseshia",
28
- )
29
- end
30
- end
data/sample/config.ru DELETED
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'akainaa'
4
-
5
- Akainaa.start(project_dir: File.expand_path(__dir__))
6
-
7
- require_relative 'app'
8
-
9
- use Akainaa::Middleware
10
- run App
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Notification
4
- 50.times { nil }
5
-
6
- module_function
7
-
8
- def where(id:) = []
9
- end
data/sample/theme.rb DELETED
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Theme
4
- 40.times { nil }
5
- module_function
6
-
7
- def find_by(user_id:)
8
- "light"
9
- end
10
- end
data/sample/user.rb DELETED
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative './theme'
4
-
5
- module User
6
- 30.times { nil }
7
- module_function
8
-
9
- def find(id)
10
- {
11
- id: id,
12
- theme: Theme.find_by(user_id: id),
13
- }
14
- end
15
- end
data/sample/util.rb DELETED
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Util
4
- module_function
5
-
6
- def do_something = []
7
- end