akainaa 0.1.0 → 0.1.2

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: 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