test-prof 0.4.9 → 0.5.0.pre1

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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +63 -20
  3. data/README.md +10 -57
  4. data/assets/tagprof.demo.html +447 -0
  5. data/assets/tagprof.template.html +447 -0
  6. data/lib/minitest/event_prof_formatter.rb +18 -16
  7. data/lib/test_prof.rb +2 -1
  8. data/lib/test_prof/any_fixture.rb +80 -4
  9. data/lib/test_prof/any_fixture/dsl.rb +18 -0
  10. data/lib/test_prof/event_prof.rb +10 -108
  11. data/lib/test_prof/event_prof/custom_events.rb +30 -0
  12. data/lib/test_prof/event_prof/custom_events/factory_create.rb +1 -1
  13. data/lib/test_prof/event_prof/custom_events/sidekiq_inline.rb +1 -1
  14. data/lib/test_prof/event_prof/custom_events/sidekiq_jobs.rb +1 -1
  15. data/lib/test_prof/event_prof/minitest.rb +6 -0
  16. data/lib/test_prof/event_prof/profiler.rb +129 -0
  17. data/lib/test_prof/event_prof/rspec.rb +20 -11
  18. data/lib/test_prof/factory_all_stub.rb +32 -0
  19. data/lib/test_prof/factory_all_stub/factory_bot_patch.rb +13 -0
  20. data/lib/test_prof/factory_doctor/rspec.rb +3 -2
  21. data/lib/test_prof/factory_prof/printers/flamegraph.rb +9 -13
  22. data/lib/test_prof/recipes/active_record_shared_connection.rb +55 -0
  23. data/lib/test_prof/recipes/logging.rb +37 -0
  24. data/lib/test_prof/recipes/rspec/any_fixture.rb +4 -1
  25. data/lib/test_prof/recipes/rspec/factory_all_stub.rb +10 -0
  26. data/lib/test_prof/rspec_dissect/rspec.rb +4 -2
  27. data/lib/test_prof/rspec_stamp/rspec.rb +3 -2
  28. data/lib/test_prof/tag_prof.rb +4 -0
  29. data/lib/test_prof/tag_prof/printers/html.rb +24 -0
  30. data/lib/test_prof/tag_prof/printers/simple.rb +82 -0
  31. data/lib/test_prof/tag_prof/result.rb +38 -0
  32. data/lib/test_prof/tag_prof/rspec.rb +43 -40
  33. data/lib/test_prof/utils/html_builder.rb +21 -0
  34. data/lib/test_prof/version.rb +1 -1
  35. metadata +20 -24
  36. data/assets/logo.svg +0 -1
  37. data/assets/testprof.png +0 -0
  38. data/guides/.rubocop.yml +0 -1
  39. data/guides/any_fixture.md +0 -114
  40. data/guides/before_all.md +0 -98
  41. data/guides/event_prof.md +0 -177
  42. data/guides/factory_default.md +0 -111
  43. data/guides/factory_doctor.md +0 -119
  44. data/guides/factory_prof.md +0 -86
  45. data/guides/let_it_be.md +0 -97
  46. data/guides/rspec_dissect.md +0 -60
  47. data/guides/rspec_stamp.md +0 -52
  48. data/guides/rubocop.md +0 -48
  49. data/guides/ruby_prof.md +0 -63
  50. data/guides/stack_prof.md +0 -47
  51. data/guides/tag_prof.md +0 -51
  52. data/guides/tests_sampling.md +0 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34e45def589431c2dd53ec256e009877c9a611ad1fa1cd7799cf6ec75c15745c
4
- data.tar.gz: 2230613ed9c7e5bf9733a16824f7e61daa77ed524b846de4195168e89a8b5d0e
3
+ metadata.gz: 8953166be33111ca0da788c4a867d5bda07d8df917cd486dda045d5464ce44ab
4
+ data.tar.gz: fbfb78a5ff1e616f0d31af9d581193d5be8e2623615c55e7ceeb785d2e5ade75
5
5
  SHA512:
6
- metadata.gz: 176beb48fdd32511a3516bcd28392cc9b9677b7cc6b1946c25f83111eeb1c0b4602387913bda61a9524c7211d044c1c16cf47d6a48fbadf803145e660d7e03ff
7
- data.tar.gz: ed688acfb83aae1e7906c01daf363f676d72109cb3f29cfb04923288fba3b0af89c1d72d1e544d7887a940bee2fde79b7f5bb35a4a364a88fa728e3f178e77f4
6
+ metadata.gz: 2c1f8760b6579505b15a5866491719002d46adb2a9b7706352f41d626d4cfb813a15540aa699ccd3220333f12ec44fbbbd98f42ec46f8bf862f82595b0dad241
7
+ data.tar.gz: 3bf3d97c16cd4793729b9f03d3891c127f8560a83c06881d3ccd627ae8d3714650721a09e106ad6cc7ba8130300f917e5bc3482e80165a738701fb23419c9494
@@ -2,7 +2,50 @@
2
2
 
3
3
  ## master
4
4
 
5
- ## 0.4.9
5
+ - Add events support to TagProf. ([@palkan][])
6
+
7
+ Example usage:
8
+
9
+ ```sh
10
+ TAG_PROF=type TAG_PROF_EVENT=sql.active_record rspec
11
+ ```
12
+
13
+ - Add logging helpers for Rails. ([@palkan][])
14
+
15
+ - Add ability to track multiple events at the same time with `EventProf`. ([@palkan][])
16
+
17
+ - Add HTML report for `TagProf`. ([@palkan][])
18
+
19
+ Generate HTML report by setting `TAG_PROF_FORMAT` to `html`.
20
+
21
+ - Add `AnyFixture` DSL. ([@palkan][])
22
+
23
+ Example:
24
+
25
+ ```ruby
26
+ # Enable DSL
27
+ using TestProf::AnyFixture::DSL
28
+
29
+ # and then you can use `fixture` method (which is just an alias for `TestProf::AnyFixture.register`)
30
+ before(:all) { fixture(:account) }
31
+
32
+ # You can also use it to fetch the record (instead of storing it in instance variable)
33
+ let(:account) { fixture(:account) }
34
+ ```
35
+
36
+ - Add `AnyFixture` usage report. ([@palkan][])
37
+
38
+ Enable `AnyFixture` usage reporting with:
39
+
40
+ ```ruby
41
+ TestProf::AnyFixture.reporting_enabled = true
42
+ ```
43
+
44
+ - Add `ActiveRecordSharedConnection` recipe. ([@palkan][])
45
+
46
+ - [#70](https://github.com/palkan/test-prof/pull/70) Add `FactoryAllStub` recipe. ([@palkan][])
47
+
48
+ ## 0.4.9 (2018-03-20)
6
49
 
7
50
  - [Fix [#64](https://github.com/palkan/test-prof/issues/64)] Fix dependencies requiring for FactoryDefault. ([@palkan][])
8
51
 
@@ -11,7 +54,7 @@
11
54
  Consider only `example_failed` and `example_passed` to ensure that the `run_time`
12
55
  is available.
13
56
 
14
- ## 0.4.8
57
+ ## 0.4.8 (2018-01-17)
15
58
 
16
59
  - Add `minitest` 5.11 support. ([@palkan][])
17
60
 
@@ -24,36 +67,36 @@
24
67
 
25
68
  Possibly fixes [#47](https://github.com/palkan/test-prof/issues/47).
26
69
 
27
- ## 0.4.7
70
+ ## 0.4.7 (2017-12-25)
28
71
 
29
72
  - [#57](https://github.com/palkan/test-prof/pull/57) Fix RubyProf Printers Support ([@rabotyaga][])
30
73
 
31
- ## 0.4.6
74
+ ## 0.4.6 (2017-12-17)
32
75
 
33
76
  - Upgrade RSpec/AggregateFailures to RuboCop 0.52.0. ([@palkan][])
34
77
 
35
78
  RuboCop < 0.51.0 is not supported anymore.
36
79
 
37
- - [Fixes [#49](https://github.com/palkan/test-prof/issues/49)] Correcly detect RSpec version in `let_it_be`. ([@desoleary][])
80
+ - [Fixes [#49](https://github.com/palkan/test-prof/issues/49)] Correctly detect RSpec version in `let_it_be`. ([@desoleary][])
38
81
 
39
- ## 0.4.5
82
+ ## 0.4.5 (2017-12-09)
40
83
 
41
84
  - Fix circular require in `lib/factory_doctor/minitest`. ([@palkan][])
42
85
 
43
- ## 0.4.4
86
+ ## 0.4.4 (2017-11-08)
44
87
 
45
88
  - [Fixes [#48](https://github.com/palkan/test-prof/issues/48)] Respect RubyProf reports files extensions. ([@palkan][])
46
89
 
47
- ## 0.4.3
90
+ ## 0.4.3 (2017-10-26)
48
91
 
49
92
  - [#46](https://github.com/palkan/test-prof/pull/46) Support FactoryBot, which is [former FactoryGirl](https://github.com/thoughtbot/factory_bot/pull/1051),
50
93
  while maintaining compatibility with latter. ([@Shkrt][])
51
94
 
52
- ## 0.4.2
95
+ ## 0.4.2 (2017-10-23)
53
96
 
54
97
  - Fix bug with multiple `before_all` within one group. ([@palkan][])
55
98
 
56
- ## 0.4.1
99
+ ## 0.4.1 (2017-10-18)
57
100
 
58
101
  - [#44](https://github.com/palkan/test-prof/pull/44) Support older versions of RSpec. ([@palkan][])
59
102
 
@@ -67,7 +110,7 @@ RSpecDissect `let` tracking supports only RSpec 3.3.0+.
67
110
 
68
111
  It is possible now to use Factory Doctor with Minitest
69
112
 
70
- ## 0.4.0
113
+ ## 0.4.0 (2017-10-03)
71
114
 
72
115
  ### Features:
73
116
 
@@ -79,7 +122,7 @@ It is possible now to use Event Prof with Minitest
79
122
 
80
123
  FactoryProf now also accounts objects created by Fabrication gem (in addition to FactoryGirl)
81
124
 
82
- ## 0.3.0
125
+ ## 0.3.0 (2017-09-21)
83
126
 
84
127
  ### Features:
85
128
 
@@ -116,21 +159,21 @@ Just like `let`, but persist the result for the whole group (i.e. `let` + `befor
116
159
 
117
160
  - Use RubyProf `FlatPrinter` by default (was `CallStackPrinter`). ([@palkan][])
118
161
 
119
- ## 0.2.5
162
+ ## 0.2.5 (2017-08-30)
120
163
 
121
164
  - [#16](https://github.com/palkan/test-prof/pull/16) Support Ruby >= 2.2.0 (was >= 2.3.0). ([@palkan][])
122
165
 
123
- ## 0.2.4
166
+ ## 0.2.4 (2017-08-29)
124
167
 
125
168
  - EventProf: Fix regression bug with examples profiling. ([@palkan][])
126
169
 
127
170
  There was a bug when an event occurs before the example has started (e.g. in `before(:context)` hook).
128
171
 
129
- ## 0.2.3
172
+ ## 0.2.3 (2017-08-28)
130
173
 
131
174
  - Minor improvements. ([@palkan][])
132
175
 
133
- ## 0.2.2
176
+ ## 0.2.2 (2017-08-23)
134
177
 
135
178
  - Fix time calculation when Time class is monkey-patched. ([@palkan][])
136
179
 
@@ -139,13 +182,13 @@ use it everywhere.
139
182
 
140
183
  Fixes [#10](https://github.com/palkan/test-prof/issues/10).
141
184
 
142
- ## 0.2.1
185
+ ## 0.2.1 (2017-08-19)
143
186
 
144
187
  - Detect `RSpec` by checking the presence of `RSpec::Core`. ([@palkan][])
145
188
 
146
189
  Fixes [#8](https://github.com/palkan/test-prof/issues/8).
147
190
 
148
- ## 0.2.0
191
+ ## 0.2.0 (2017-08-18)
149
192
 
150
193
  - Ensure output directory exists. ([@danielwestendorf][])
151
194
 
@@ -157,11 +200,11 @@ Ensure output dir exists in `#artifact_path` method.
157
200
 
158
201
  - FactoryDoctor: print success message when no bad examples found. ([@palkan][])
159
202
 
160
- ## 0.1.1
203
+ ## 0.1.1 (2017-08-17)
161
204
 
162
205
  - AnyFixture: clean tables in reverse order to not fail when foreign keys exist. ([@marshall-lee][])
163
206
 
164
- ## 0.1.0
207
+ ## 0.1.0 (2017-08-15)
165
208
 
166
209
  - Initial version. ([@palkan][])
167
210
 
data/README.md CHANGED
@@ -1,9 +1,10 @@
1
- [![Gem Version](https://badge.fury.io/rb/test-prof.svg)](https://rubygems.org/gems/test-prof) [![Build Status](https://travis-ci.org/palkan/test-prof.svg?branch=master)](https://travis-ci.org/palkan/test-prof) [![CircleCI](https://circleci.com/gh/palkan/test-prof.svg?style=svg)](https://circleci.com/gh/palkan/test-prof) [![Code Triagers Badge](https://www.codetriage.com/palkan/test-prof/badges/users.svg)](https://www.codetriage.com/palkan/test-prof)
1
+ [![Gem Version](https://badge.fury.io/rb/test-prof.svg)](https://rubygems.org/gems/test-prof) [![Build Status](https://travis-ci.org/palkan/test-prof.svg?branch=master)](https://travis-ci.org/palkan/test-prof) [![Code Triagers Badge](https://www.codetriage.com/palkan/test-prof/badges/users.svg)](https://www.codetriage.com/palkan/test-prof)
2
+ [![Documentation](https://img.shields.io/badge/docs-link-brightgreen.svg)](https://test-prof.evilmartians.io)
2
3
 
3
4
  # Ruby Tests Profiling Toolbox
4
5
 
5
6
  <img align="right" height="150" width="129"
6
- title="TestProf logo" src="./assets/logo.svg">
7
+ title="TestProf logo" src="./docs/assets/images/logo.svg">
7
8
 
8
9
  TestProf is a collection of different tools to analyze your test suite performance.
9
10
 
@@ -23,11 +24,11 @@ TestProf toolbox aims to help you identify bottlenecks in your test suite. It co
23
24
 
24
25
  - etc.
25
26
 
26
- Of course, we have some [solutions](#tips-and-tricks-or-recipes) for common performance issues too, bundled into the gem.
27
+ Of course, we have some [solutions](https://test-prof.evilmartians.io/#/?id=recipes) for common performance issues too, bundled into the gem.
27
28
 
28
- [![](https://s3-us-west-1.amazonaws.com/vladem/test-prof/TestProf.png)](https://coggle.it/diagram/Wa7tW-87jAAB0ExN)
29
+ [![](./docs/assets/images/coggle.png)](http://bit.ly/test-prof-map)
29
30
 
30
- See [Table of Contents](#table-of-contents) for more.
31
+ 📑 [Documentation](https://test-prof.evilmartians.io)
31
32
 
32
33
  Supported Ruby versions:
33
34
 
@@ -60,65 +61,17 @@ end
60
61
 
61
62
  And that's it)
62
63
 
63
- ## Table of Contents
64
+ ## Usage
64
65
 
65
- Checkout our guides for each specific tool:
66
-
67
- - [RubyProf Integration](https://github.com/palkan/test-prof/tree/master/guides/ruby_prof.md)
68
-
69
- - [StackProf Integration](https://github.com/palkan/test-prof/tree/master/guides/stack_prof.md)
70
-
71
- - [Event Profiler](https://github.com/palkan/test-prof/tree/master/guides/event_prof.md) (e.g. ActiveSupport notifications)
72
-
73
- - [Tag Profiler](https://github.com/palkan/test-prof/tree/master/guides/tag_prof.md)
74
-
75
- - [Factory Doctor](https://github.com/palkan/test-prof/tree/master/guides/factory_doctor.md)
76
-
77
- - [Factory Profiler](https://github.com/palkan/test-prof/tree/master/guides/factory_prof.md)
78
-
79
- - [RSpecDissect Profiler](https://github.com/palkan/test-prof/tree/master/guides/rspec_dissect.md)
80
-
81
- - [RuboCop cops](https://github.com/palkan/test-prof/tree/master/guides/rubocop.md)
82
-
83
- ## Tips and Tricks (or _Recipes_)
84
-
85
- We also want to share some small code tricks which can help you to improve your test suite performance and efficiency:
86
-
87
- - [`before_all` Hook](https://github.com/palkan/test-prof/tree/master/guides/before_all.md)
88
-
89
- - [`let_it_be` Helper](https://github.com/palkan/test-prof/tree/master/guides/let_it_be.md)
90
-
91
- - [AnyFixture](https://github.com/palkan/test-prof/tree/master/guides/any_fixture.md)
92
-
93
- - [FactoryDefault](https://github.com/palkan/test-prof/tree/master/guides/factory_default.md)
94
-
95
- - [RSpec Stamp](https://github.com/palkan/test-prof/tree/master/guides/rspec_stamp.md)
96
-
97
- - [Tests Sampling](https://github.com/palkan/test-prof/tree/master/guides/tests_sampling.md)
98
-
99
- ## Configuration
100
-
101
- TestProf global configuration is used by most of the profilers:
102
-
103
- ```ruby
104
- TestProf.configure do |config|
105
- # the directory to put artifacts (reports) in ("tmp/test_prof" by default)
106
- config.output_dir = "tmp/test_prof"
107
-
108
- # use unique filenames for reports (by simply appending current timestamp)
109
- config.timestamps = true
110
-
111
- # color output
112
- config.color = true
113
- end
114
- ```
66
+ Check out our [docs][].
115
67
 
116
68
  ## What's next?
117
69
 
118
-
119
70
  Have an idea? [Propose](https://github.com/palkan/test-prof/issues/new) a feature request!
120
71
 
121
72
 
122
73
  ## License
123
74
 
124
75
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
76
+
77
+ [docs]: https://test-prof.evilmartians.io
@@ -0,0 +1,447 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>TestProf: TagProf report</title>
5
+ <meta name="viewport" content="width=device-width">
6
+ <meta charset="UTF-8">
7
+ <style>
8
+ html, body {
9
+ height: 100%;
10
+ width: 100%;
11
+ min-width: 300px;
12
+ }
13
+
14
+ body {
15
+ background: #f9f9f9;
16
+ color: #363636;
17
+ font: 18px/30px "Arial",sans-serif;
18
+ margin: 0;
19
+ padding: 0;
20
+ }
21
+
22
+ a {
23
+ text-decoration: none;
24
+ color: #ff5e5e
25
+ }
26
+
27
+ a:visited, a:active{
28
+ color: #ff5e5e;
29
+ }
30
+
31
+ a:hover{
32
+ opacity: 0.8;
33
+ }
34
+
35
+ h1 {
36
+ font-size: 30px;
37
+ line-height: 32px;
38
+ letter-spacing: 2px;
39
+ font-weight: bold;
40
+ padding: 30px 0 30px 0;
41
+ margin: 0;
42
+ }
43
+
44
+ .main {
45
+ height: 100%;
46
+ width: 100%;
47
+ margin: 0;
48
+ padding: 0;
49
+ }
50
+
51
+ header {
52
+ position: relative;
53
+ text-align: center;
54
+ background: #fff;
55
+ overflow: hidden;
56
+ display: flex;
57
+ align-items: center;
58
+ flex-direction: column;
59
+ }
60
+
61
+ .chart {
62
+ display: flex;
63
+ justify-content: center;
64
+ position: relative;
65
+ box-sizing: border-box;
66
+ flex-direction: row;
67
+ padding: 20px;
68
+ }
69
+
70
+ .tooltip {
71
+ background: #eee;
72
+ box-shadow: 0 0 5px #999999;
73
+ color: #333;
74
+ display: none;
75
+ font-size: 14px;
76
+ left: 130px;
77
+ padding: 10px;
78
+ position: absolute;
79
+ text-align: left;
80
+ top: 95px;
81
+ z-index: 10;
82
+ }
83
+
84
+ .tooltip .label.active {
85
+ color: #ff5e5e;
86
+ }
87
+
88
+ .tooltip .value-label {
89
+ font-weight: bold;
90
+ margin-right: 10px;
91
+ }
92
+
93
+ .legend, .totals {
94
+ font-size: 14px;
95
+ }
96
+
97
+ #selector {
98
+ font-size: 14px;
99
+ display: flex;
100
+ align-items: flex-start;
101
+ position: relative;
102
+ box-sizing: border-box;
103
+ flex-direction: column;
104
+ padding: 20px;
105
+ }
106
+
107
+ rect {
108
+ stroke-width: 2;
109
+ }
110
+
111
+ rect.disabled { /* NEW */
112
+ fill: transparent !important; /* NEW */
113
+ }
114
+
115
+ .d3-donut-path {
116
+ transition: opacity 200ms;
117
+ opacity: 1;
118
+ cursor: pointer;
119
+ }
120
+
121
+ .d3-donut-path:hover {
122
+ opacity: 0.8;
123
+ }
124
+
125
+ .humanoids__link {
126
+ width: 130px;
127
+ }
128
+
129
+ .humanoids{
130
+ position: absolute;
131
+ bottom: -23px;
132
+ width: 150px;
133
+ height: 60px;
134
+ margin: 0 auto 0 -10px;
135
+ font-size: 0;
136
+ transform: scale(0.6);
137
+ }
138
+ .humanoids circle{
139
+ transform: scaleY(1);
140
+ transform-origin: 50%;
141
+ animation-duration: 8s;
142
+ animation-name: humanoids-blink;
143
+ animation-iteration-count: infinite;
144
+ }
145
+ .humanoids svg{
146
+ height: 60px;
147
+ }
148
+ .humanoids__human, .humanoids__martian{
149
+ position: absolute;
150
+ width: 80px;
151
+ height: 90px;
152
+ transition: transform 300ms cubic-bezier(0.68, -0.55, 0.265, 1.55);
153
+ transform: translateY(0);
154
+ }
155
+ .humanoids__human:hover, .humanoids__martian:hover{
156
+ transform: translateY(-12px);
157
+ }
158
+ .humanoids__martian{
159
+ left: 0;
160
+ }
161
+ .humanoids__human{
162
+ right: 0;
163
+ }
164
+ .humanoids__human circle{
165
+ animation-delay: 0.5s;
166
+ }
167
+
168
+ [name="root"] .label {
169
+ text-align: center;
170
+ }
171
+ </style>
172
+ </head>
173
+ <body>
174
+ <div class="main">
175
+ <header>
176
+ <h1>TagProf Report</h1>
177
+ <a class="humanoids__link" href="https://evilmartians.com">
178
+ <div class="humanoids">
179
+ <div class="humanoids__martian"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 108 92"><path fill="#f64242" d="M26 88L8 78l18-10"/><path fill="#f64242" d="M94 92v-6H44c-5.5 0-10-4.5-10-10s4.5-10 10-10h50V32c0-14-7.9-22-22-22H48c-14.1 0-22 8-22 22v60h68z"/><circle fill="#FFF" cx="48" cy="50" r="8"/><circle fill="#FFF" cx="72" cy="50" r="8"/><circle fill="#BF6C35" cx="48" cy="50" r="4"/><circle fill="#BF6C35" cx="72" cy="50" r="4"/><g fill="#663F4C"><path d="M48 60c-5.5 0-10-4.5-10-10s4.5-10 10-10 10 4.5 10 10-4.5 10-10 10zm0-16c-3.3 0-6 2.7-6 6s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6zM82 50c0 5.5-4.5 10-10 10s-10-4.5-10-10 4.5-10 10-10 10 4.5 10 10zm-16 0c0 3.3 2.7 6 6 6s6-2.7 6-6-2.7-6-6-6-6 2.7-6 6z"/><path d="M102 8c-3.8 0-6-2.2-6-6 0-1.1-.9-2-2-2s-2 .9-2 2c0 2.2.5 4.1 1.5 5.7L88.2 13c-4-3.3-9.5-5-16.2-5H48c-6.7 0-12.2 1.7-16.2 4.9l-5.3-5.3C27.5 6.1 28 4.2 28 2c0-1.1-.9-2-2-2s-2 .9-2 2c0 3.8-2.2 6-6 6-1.1 0-2 .9-2 2s.9 2 2 2c2.2 0 4.1-.5 5.7-1.5l5.3 5.3c-3.2 4-4.9 9.5-4.9 16.2v34.8L3.9 78 24 89v3h4V32c0-12.9 7.1-20 20-20h24c12.9 0 20 7.1 20 20v32H44c-6.6 0-12 5.4-12 12s5.4 12 12 12h48v4h4v-8h-2l-4-8-4 8h-4l-4-8-4 8h-4l-4-8-4 8h-4l-4-8-4 8h-4l-4-8-3.1 6.2C37.1 80.7 36 78.5 36 76c0-4.4 3.6-8 8-8l4 8 4-8h4l4 8 4-8h4l4 8 4-8h4l4 8 4-8h8V32c0-6.7-1.7-12.2-4.9-16.2l5.3-5.3c1.6 1 3.5 1.5 5.7 1.5 1.1 0 2-.9 2-2s-1-2-2.1-2zM24 84.4L12.1 78 24 71.4v13z"/></g></svg></div>
180
+ <div class="humanoids__human"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 108 92"><path fill="#FFFFFF" d="M14 62v30h68v-6H32c-5.5 0-10-4.5-10-10s4.5-10 10-10h50v-4c4 0 8-3.6 8-8s-4-8-8-8V32c0-14-7.9-22-22-22H36c-14.1 0-22 8-22 22v14c-4 0-8 3.6-8 8s4 8 8 8z"/><circle fill="#FFF" cx="36" cy="50" r="8"/><circle fill="#FFF" cx="60" cy="50" r="8"/><circle fill="#f64242" cx="36" cy="50" r="4"/><circle fill="#f64242" cx="60" cy="50" r="4"/><path fill="#f64242" d="M60 10H36c-14.1 0-22 8-22 22v2l4-4 6 6 6-6 6 6 6-6 6 6 6-6 6 6 6-6 6 6 6-6 4 4v-2c0-14-7.9-22-22-22z"/><g fill="#663F4C"><path d="M36 60c-5.5 0-10-4.5-10-10s4.5-10 10-10 10 4.5 10 10-4.5 10-10 10zm0-16c-3.3 0-6 2.7-6 6s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6zM60 60c-5.5 0-10-4.5-10-10s4.5-10 10-10 10 4.5 10 10-4.5 10-10 10zm0-16c-3.3 0-6 2.7-6 6s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6z"/><path d="M12 63.8V92h4V32c0-12.9 7.1-20 20-20h24c12.9 0 20 7.1 20 20v32H32c-6.6 0-12 5.4-12 12s5.4 12 12 12h48v4h4v-8H74v-1c0-1.7-1.3-3-3-3s-3 1.3-3 3v1h-6v-1c0-1.7-1.3-3-3-3s-3 1.3-3 3v1h-6v-1c0-1.7-1.3-3-3-3s-3 1.3-3 3v1h-6v-1c0-1.7-1.3-3-3-3s-3 1.3-3 3v1c-4 0-8-3.6-8-8s3.6-8 8-8h6v1c0 1.7 1.3 3 3 3s3-1.3 3-3v-1h6v1c0 1.7 1.3 3 3 3s3-1.3 3-3v-1h6v1c0 1.7 1.3 3 3 3s3-1.3 3-3v-1h6v1c0 1.7 1.3 3 3 3s3-1.3 3-3v-1h4v-4.2c5-.9 8-5 8-9.8s-3-8.9-8-9.8V32c0-15.3-8.7-24-24-24H36c-15.3 0-24 8.7-24 24v12.2c-5 .9-8 5-8 9.8s3 8.9 8 9.8zm72-15.5c2 .8 4 3 4 5.7s-2 4.8-4 5.7V48.3zm-72 0v11.3c-2-.8-4-3-4-5.7s2-4.7 4-5.6z"/></g></svg></div>
181
+ </div>
182
+ </a>
183
+ </header>
184
+ <div class="chart">
185
+ <form id="selector">
186
+ </form>
187
+ <div id="chart">
188
+ </div>
189
+ <div id="legend">
190
+ </div>
191
+ </div>
192
+ </div>
193
+
194
+ <script type="text/javascript" src="src/d3.v4.min.js"></script>
195
+
196
+ <script>
197
+ var report_data;
198
+ var width = 360;
199
+ var legendWidth = 240;
200
+ var height = 360;
201
+ var radius = Math.min(width, height) / 2;
202
+ var donutWidth = 75;
203
+ var legendRectSize = 18;
204
+ var legendSpacing = 4;
205
+
206
+ var color = d3.scaleOrdinal(d3.schemeCategory20);
207
+
208
+ function pad(n, size) {
209
+ if(size == void 0) size = 2;
210
+
211
+ var str = n.toString();
212
+
213
+ while(str.length < size){
214
+ str = "0" + str;
215
+ }
216
+
217
+ return str;
218
+ }
219
+
220
+ function duration(t) {
221
+ return pad(t / 60|0) + ':' + pad((t % 60) |0) + '.' + pad(((t * 1000) % 1000)|0, 3);
222
+ }
223
+
224
+ var svg = d3.select('#chart')
225
+ .append('svg')
226
+ .attr('width', width)
227
+ .attr('height', height)
228
+ .append('g')
229
+ .attr('transform', 'translate(' + (width / 2) +
230
+ ',' + (height / 2) + ')');
231
+
232
+ var arc = d3.arc()
233
+ .innerRadius(radius - donutWidth)
234
+ .outerRadius(radius);
235
+
236
+ var labelArc = d3.arc()
237
+ .outerRadius(radius - donutWidth)
238
+ .innerRadius(radius);
239
+
240
+ var pie = d3.pie()
241
+ .value(function(d) { return d.time; })
242
+ .sort(function(a, b) {
243
+ return b.time - a.time;
244
+ });
245
+
246
+ var tooltip = d3.select('#chart')
247
+ .append('div')
248
+ .attr('class', 'tooltip');
249
+
250
+ tooltip.append('div')
251
+ .attr('class', 'label');
252
+
253
+ tooltip.append('div')
254
+ .attr('class', 'stats');
255
+
256
+ // var report_data = JSON.parse('{/**REPORT-DATA**/}');
257
+ var report_data = {
258
+ tag: "type",
259
+ events: [
260
+ "sql.active_record"
261
+ ],
262
+ data: [
263
+ { value: "model", count: 123, time: 324.2, "sql.active_record": 21.54 },
264
+ { value: "controller", count: 44, time: 514, "sql.active_record": 121 },
265
+ { value: "job", count: 33, time: 122.2 },
266
+ { value: "__unknown__", count: 2, time: 44, "sql.active_record": 0 }
267
+ ]
268
+ };
269
+
270
+ var dataset = report_data.data;
271
+
272
+ dataset.sort(function(a, b){
273
+ return b.time - a.time;
274
+ });
275
+
276
+ var totals = {};
277
+
278
+ ["count", "time"].concat(report_data.events).forEach(function(event) {
279
+ totals[event] = d3.sum(dataset.map(function(d){
280
+ return d[event] || 0;
281
+ }));
282
+ });
283
+
284
+ dataset.forEach(function(d) {
285
+ d.labels = {};
286
+
287
+ if (d.value == "__unknown__") {
288
+ d.color = "#cccccc";
289
+ };
290
+
291
+ d.labels.count = {
292
+ value: d.count,
293
+ percent: Math.round(1000 * d.count / totals.count) / 10
294
+ };
295
+
296
+ ["time"].concat(report_data.events).forEach(function(event){
297
+ d.labels[event] = {
298
+ value: duration(d[event]),
299
+ percent: d[event] ? Math.round(1000 * d[event] / totals[event]) / 10 : 0
300
+ }
301
+ });
302
+ });
303
+
304
+ var path = svg.selectAll('path')
305
+ .data(pie(dataset))
306
+ .enter()
307
+ .append('path')
308
+ .attr('d', arc)
309
+ .attr('class', 'd3-donut-path')
310
+ .attr('fill', function(d, i) {
311
+ return d.data.color || color(d.data.value);
312
+ })
313
+ .each(function(d) { this._current = d; });
314
+
315
+ var currentMeasure = "time";
316
+
317
+ pie.value(function(d) {
318
+ return d[currentMeasure];
319
+ });
320
+
321
+ path.on('mouseover', function(d) {
322
+ tooltip.select('.label').html("<span class='value-label'>" + report_data.tag + ":</span><span class='value-data'>" + d.data.value + "</span>");
323
+
324
+ var str = "";
325
+
326
+ ["count", "time"].concat(report_data.events).forEach(function(event) {
327
+ str += "<div class='label " + (event == currentMeasure ? 'active' : '') + "'><span class='value-label'>" + event + ":</span><span class='value-data'>" + d.data.labels[event].value + " ("+d.data.labels[event].percent + "%)</span></div>";
328
+ });
329
+ tooltip.select('.stats').html(str);
330
+ tooltip.style('display', 'block');
331
+ });
332
+
333
+ path.on('mousemove', function(d) {
334
+ tooltip.style('top', (d3.event.layerY + 10) + 'px')
335
+ .style('left', (d3.event.layerX + 10) + 'px');
336
+ });
337
+
338
+ path.on('mouseout', function() {
339
+ tooltip.style('display', 'none');
340
+ });
341
+
342
+ if(report_data.events && report_data.events.length) {
343
+ var selectOptions = ["time"].concat(report_data.events);
344
+
345
+ var selector = d3.select("form").selectAll("label")
346
+ .data(selectOptions)
347
+ .enter().append("label");
348
+
349
+ selector.append("input")
350
+ .attr("type", "radio")
351
+ .attr("name", "measurement")
352
+ .attr("value", function(d) { return d; })
353
+ .property("checked", function(d, i) { return i === 0 });
354
+
355
+ d3.select("form").style('width', legendWidth + 'px');
356
+
357
+ d3.selectAll(("input[name='measurement']")).on("change", function(e) {
358
+ render(e);
359
+ });
360
+
361
+ selector.append("span")
362
+ .text(function(d) { return d; });
363
+ } else {
364
+ d3.select("form").remove();
365
+ }
366
+
367
+ var legendContainer = d3.select('#legend')
368
+ .append('svg')
369
+ .attr('height', height)
370
+ .attr('width', legendWidth);
371
+
372
+ var legend = legendContainer.selectAll('legend')
373
+ .data(color.domain())
374
+ .enter()
375
+ .append('g')
376
+ .attr('class', 'legend')
377
+ .attr('transform', function(d, i) {
378
+ var height = legendRectSize + legendSpacing;
379
+ var horz = 2 * legendRectSize;
380
+ var vert = i * height;
381
+ return 'translate(' + horz + ',' + vert + ')';
382
+ });
383
+
384
+ legend.append('rect')
385
+ .attr('width', legendRectSize)
386
+ .attr('height', legendRectSize)
387
+ .style('fill', color)
388
+ .style('stroke', color);
389
+
390
+ legend.append('text')
391
+ .attr('x', legendRectSize + legendSpacing)
392
+ .attr('y', legendRectSize - legendSpacing)
393
+ .text(function(d) { return d; });
394
+
395
+ var totalsContainer = svg.append('g')
396
+ .attr('class', 'totals')
397
+ .attr('transform', 'translate(-' + (radius - 100) +', -'+ (radius - 120) + ')');
398
+
399
+ var totalsArray = d3.keys(totals).map(function(key){
400
+ if(key == "count"){
401
+ return { key: key, value: totals[key] };
402
+ } else {
403
+ return { key: key, value: duration(totals[key]) };
404
+ }
405
+ });
406
+
407
+ totalsContainer.append('text')
408
+ .text("TOTALS")
409
+ .attr('x', 50);
410
+
411
+ var totalsLegend = totalsContainer.selectAll('total')
412
+ .data(totalsArray)
413
+ .enter()
414
+ .append('g')
415
+ .attr('class', 'total')
416
+ .attr('transform', function(d, i) {
417
+ var height = legendRectSize + legendSpacing;
418
+ var horz = 0;
419
+ var vert = i * height + 20;
420
+ return 'translate(' + horz + ',' + vert + ')';
421
+ });
422
+
423
+ totalsLegend.append('text')
424
+ .attr('x', 0)
425
+ .attr('y', legendRectSize - legendSpacing)
426
+ .text(function(d) {
427
+ return d.key + ": " + d.value;
428
+ });
429
+
430
+ var render = function(measurement) {
431
+ currentMeasure = measurement;
432
+
433
+ path = path.data(pie(dataset));
434
+
435
+ path.transition()
436
+ .duration(750)
437
+ .attrTween('d', function(d) {
438
+ var interpolate = d3.interpolate(this._current, d);
439
+ this._current = interpolate(0);
440
+ return function(t) {
441
+ return arc(interpolate(t));
442
+ };
443
+ });
444
+ }
445
+ </script>
446
+ </body>
447
+ </html>