rorvswild 1.6.3 → 1.6.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +14 -263
- data/lib/rorvswild/client.rb +1 -1
- data/lib/rorvswild/installer.rb +11 -1
- data/lib/rorvswild/local/javascript/local.js +13 -1
- data/lib/rorvswild/local/local.html.erb +7 -2
- data/lib/rorvswild/local/middleware.rb +4 -0
- data/lib/rorvswild/local/stylesheet/local.css +88 -131
- data/lib/rorvswild/local/stylesheet/vendor/prism.css +10 -9
- data/lib/rorvswild/section.rb +4 -2
- data/lib/rorvswild/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd38e8268715a48ce8f49f648317660e41a512d80bb445d3f1ae7800ff13c7a8
|
4
|
+
data.tar.gz: 310aac7f6ac148f387e03f690eb7b54bfe8d1a25a0f450ccfdb2ec21245b7d28
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4975dcbaaf1df79282ca4e46277c201bbb21443ab3f59a1c44f3bd96573ca8ed56a25c748f5bd29e38dbe154c11cdae475768229603c9da6e16c2b505bf972c0
|
7
|
+
data.tar.gz: ff0593aa35f2e9352e66f87e9c0a8a6e2d99803b8740325755790b84eb5fc681646fa50f19935595d4534f513dc5113424326f4b180707d288467ff7d3be8293
|
data/README.md
CHANGED
@@ -4,28 +4,29 @@
|
|
4
4
|
[![Gem Version](https://badge.fury.io/rb/rorvswild.svg)](https://badge.fury.io/rb/rorvswild)
|
5
5
|
[![Maintainability](https://api.codeclimate.com/v1/badges/2c4805cf658d7af794fe/maintainability)](https://codeclimate.com/github/BaseSecrete/rorvswild/maintainability)
|
6
6
|
|
7
|
-
<img align="right" src="./images/rorvswild_logo.jpg">
|
7
|
+
<img align="right" width="120px" src="./images/rorvswild_logo.jpg">
|
8
8
|
|
9
9
|
*RoRvsWild* is a ruby gem to monitor performances and exceptions in Ruby on Rails applications.
|
10
10
|
|
11
|
-
This gem has a double mode, development and production
|
12
|
-
It can be used without an account to monitor your requests performances in your development environment.
|
11
|
+
This gem has a double mode, **development** and **production**.
|
12
|
+
It can be used without an account to monitor your requests performances in your development environment.
|
13
13
|
It can also be used in your production and staging environments with an account on https://rorvswild.com. With such an account you also get extra benefits such as 30 day trace, background jobs monitoring, exceptions monitoring and notifications.
|
14
14
|
|
15
|
+
## Development mode
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
#### Install the gem
|
17
|
+
### Install the gem
|
19
18
|
|
20
19
|
* Add in your Gemfile `gem "rorvswild"`
|
21
20
|
* Run `bundle install` in you terminal
|
22
21
|
* Restart your local server and you’ll see a small button in the bottom left corner of your page.
|
23
22
|
|
24
|
-
|
23
|
+
<img width="166px" src="./images/rorvswild_local_button.png">
|
24
|
+
|
25
|
+
Click on the button, or navigate to http://localhost:3000/rorvswild to see the details panel:
|
25
26
|
|
26
|
-
|
27
|
+
![RoRvsWild Local](./images/rorvswild_local.png)
|
27
28
|
|
28
|
-
|
29
|
+
## Production mode
|
29
30
|
|
30
31
|
**To monitor your production or staging environment, you need an API key.**
|
31
32
|
Signup on https://www.rorvswild.com and create an app to get one.
|
@@ -36,260 +37,10 @@ Signup on https://www.rorvswild.com and create an app to get one.
|
|
36
37
|
* Deploy/Restart your app
|
37
38
|
* Make a few requests and refresh your app page on rorvswild.com to view the dashboard.
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
If you prefer to use an initializer, you can do the following:
|
42
|
-
|
43
|
-
```ruby
|
44
|
-
# config/initializers/rorvswild.rb
|
45
|
-
RorVsWild.start(api_key: API_KEY)
|
46
|
-
```
|
47
|
-
|
48
|
-
You can create unlimited apps on *rorvswild.com*. If you want to monitor your staging environment, create a new app and edit your rorvswild.yml to add the API key.
|
49
|
-
|
50
|
-
In case there is no data in the dashboard, you can run in a rails console : `RorVsWild.check`.
|
51
|
-
|
52
|
-
## Development mode: *RoRvsWild Local*
|
53
|
-
|
54
|
-
![RoRvsWild Local](./images/rorvswild_local.jpg)
|
55
|
-
|
56
|
-
*RorVsWild Local* monitors the performances of requests in development environment.
|
57
|
-
It shows most of the requests performances insights *RoRvsWild.com* displays. **A big difference is everything works locally and no data is sent and recorded on our servers**. You don’t even need an account to use it.
|
58
|
-
|
59
|
-
*RoRvsWild Local* renders a small button in the bottom left corner of your page showing the runtime of the current request. If you click on it, you get all the profiled sections ordered by impact, which is depending on the sections average runtime and the calls count. As on RoRvsWild.com, the bottleneck is always on the top of the list.
|
60
|
-
|
61
|
-
You may want to hide or change the widget position like in the example below with the `widget` option :
|
62
|
-
|
63
|
-
```yaml
|
64
|
-
# config/rorvswild.yml
|
65
|
-
|
66
|
-
development:
|
67
|
-
widget: top-right
|
68
|
-
|
69
|
-
#accepted values : top-left, top-right, bottom-right, bottom-left (default), hidden
|
70
|
-
```
|
71
|
-
|
72
|
-
You can still access the profiler at http://localhost:3000/rorvswild if you choose to hide the widget.
|
73
|
-
|
74
|
-
Be aware that the performances on your development machine may vary from the production server. Obviously because of the different hardware and database size. Also, Rails is reloading all the code in development environment and this takes quite a lot of time.
|
75
|
-
To prevent this behaviour and better match the production, turn on cache_classes in your config/environments/development.rb:
|
76
|
-
|
77
|
-
```
|
78
|
-
Rails.application.configure do
|
79
|
-
config.cache_classes = true
|
80
|
-
end
|
81
|
-
```
|
82
|
-
|
83
|
-
If you are using `Rack::Deflater` middleware you won't see the small button in the corner. Because of the compression it is not possible to inject some JavaScript into the page. In that case visit http://localhost:3000/rorvswild to see the profiler.
|
84
|
-
|
85
|
-
## Production mode: *RoRvsWild.com*
|
86
|
-
|
87
|
-
![RoRvsWild.com](./images/rorvswild_prod.jpg)
|
88
|
-
|
89
|
-
*RoRvsWild.com* makes it easy to monitor requests, background jobs and errors in your production and staging environment.
|
90
|
-
It also comes with some extra options listed below.
|
91
|
-
|
92
|
-
#### Measure any section of code
|
93
|
-
|
94
|
-
RorVsWild measures a lot of events such as SQL queries. But it might not be enough for you. There is a solution to measure any section of code to help you find the most hidden bottlenecks.
|
95
|
-
|
96
|
-
```ruby
|
97
|
-
# Measure a code given as a string
|
98
|
-
RorVsWild.measure("bubble_sort(array)")
|
99
|
-
|
100
|
-
# Measure a code given as a block
|
101
|
-
RorVsWild.measure { bubble_sort(array) }
|
102
|
-
|
103
|
-
# Measure a code given as a block with an optional description
|
104
|
-
RorVsWild.measure("Optional description") { bubble_sort(array) }
|
105
|
-
```
|
106
|
-
|
107
|
-
For each custom measure, a section is added with the file name and line number where it has been called.
|
108
|
-
|
109
|
-
#### Send errors manually
|
110
|
-
|
111
|
-
When you already have a begin / rescue block, this manner suits well:
|
112
|
-
|
113
|
-
```ruby
|
114
|
-
begin
|
115
|
-
# Your code ...
|
116
|
-
rescue => exception
|
117
|
-
RorVsWild.record_error(exception)
|
118
|
-
end
|
119
|
-
```
|
120
|
-
|
121
|
-
If you prefer to be concise, just run the code from a block:
|
122
|
-
|
123
|
-
```ruby
|
124
|
-
RorVsWild.catch_error { 1 / 0 } # => #<ZeroDivisionError: divided by 0>
|
125
|
-
```
|
126
|
-
|
127
|
-
Moreover, you can provide extra details when capturing errors:
|
128
|
-
|
129
|
-
```ruby
|
130
|
-
RorVsWild.record_error(exception, {something: "important"})
|
131
|
-
```
|
132
|
-
|
133
|
-
```ruby
|
134
|
-
RorVsWild.catch_error(something: "important") { 1 / 0 }
|
135
|
-
```
|
136
|
-
|
137
|
-
It is also possible to pre-fill this context data at the begining of each request or job :
|
138
|
-
|
139
|
-
```ruby
|
140
|
-
class ApplicationController < ActionController::Base
|
141
|
-
before_action :prefill_error_context
|
142
|
-
|
143
|
-
def prefill_error_context
|
144
|
-
RorVsWild.merge_error_context(something: "important")
|
145
|
-
end
|
146
|
-
end
|
147
|
-
```
|
148
|
-
|
149
|
-
#### Ignore requests, jobs, exceptions and plugins
|
150
|
-
|
151
|
-
From the configuration file, you can tell RorVsWild to skip monitoring some requests, jobs, exceptions and plugins.
|
152
|
-
|
153
|
-
```yaml
|
154
|
-
# config/rorvswild.yml
|
155
|
-
production:
|
156
|
-
api_key: API_KEY
|
157
|
-
ignore_requests:
|
158
|
-
- HeartbeatController#show
|
159
|
-
- !ruby/regexp /SecretController/ # Ignore the entire controller
|
160
|
-
ignore_jobs:
|
161
|
-
- SecretJob
|
162
|
-
- !ruby/regexp /Secret::/ # Ignore the entire Secret namespace
|
163
|
-
ignore_exceptions:
|
164
|
-
- ActionController::RoutingError # Ignore by default any 404
|
165
|
-
- ZeroDivisionError
|
166
|
-
- !ruby/regexp /Secret::/ # Ignore all secret errors
|
167
|
-
ignore_plugins:
|
168
|
-
- Sidekiq # If you don't want to monitor your Sidekiq jobs
|
169
|
-
```
|
170
|
-
|
171
|
-
Here is the equivalent if you prefer initialising RorVsWild manually.
|
172
|
-
|
173
|
-
```ruby
|
174
|
-
# config/initializers/rorvswild.rb
|
175
|
-
RorVsWild.start(
|
176
|
-
api_key: "API_KEY",
|
177
|
-
ignore_requests: ["ApplicationController#heartbeat", /SecretController/],
|
178
|
-
ignore_jobs: ["SecretJob", /Secret::/],
|
179
|
-
ignore_exceptions: ["ActionController::RoutingError", "ZeroDivisionError", /Secret::/],
|
180
|
-
ignore_plugins: ["Sidekiq"])
|
181
|
-
```
|
182
|
-
|
183
|
-
Finally here is the list of all plugins you can ignore :
|
184
|
-
|
185
|
-
- ActionController
|
186
|
-
- ActionMailer
|
187
|
-
- ActionView
|
188
|
-
- ActiveJob
|
189
|
-
- ActiveRecord
|
190
|
-
- DelayedJob
|
191
|
-
- Elasticsearch
|
192
|
-
- Mongo
|
193
|
-
- NetHttp
|
194
|
-
- Redis
|
195
|
-
- Resque
|
196
|
-
- Sidekiq
|
197
|
-
|
198
|
-
#### Change logger
|
199
|
-
|
200
|
-
By default RorVsWild uses `Rails.logger` or standard output. However in some cases you want to isolate RorVsWild's logs.
|
201
|
-
To do that, you have to specifiy the log destination via the `logger` option :
|
202
|
-
|
203
|
-
```yaml
|
204
|
-
# config/rorvswild.yml
|
205
|
-
production:
|
206
|
-
api_key: API_KEY
|
207
|
-
logger: log/rorvswild.yml
|
208
|
-
```
|
209
|
-
|
210
|
-
Here is the equivalent if you prefer initialising RorVsWild manually :
|
211
|
-
|
212
|
-
```ruby
|
213
|
-
# config/initializers/rorvswild.rb
|
214
|
-
RorVsWild.start(api_key: "API_KEY", logger: "log/rorvswild.log")
|
215
|
-
```
|
216
|
-
|
217
|
-
In the case you want a custom logger such as Syslog, you can only do it by initialising it manually :
|
218
|
-
|
219
|
-
```ruby
|
220
|
-
# config/initializers/rorvswild.rb
|
221
|
-
RorVsWild.start(api_key: "API_KEY", logger: Logger::Syslog.new)
|
222
|
-
```
|
223
|
-
|
224
|
-
### Deployment tracking
|
225
|
-
|
226
|
-
Since version 1.6.0, RorVsWild compares performances between each deployment.
|
227
|
-
That is convenient to detect quickly a performance deterioration.
|
228
|
-
|
229
|
-
It is working without any actions from your part if the application is :
|
230
|
-
|
231
|
-
- Deployed via Capistrano
|
232
|
-
- Inside a Git repositoriy
|
233
|
-
- Hosted on Heroku if [Dyno metadata](https://devcenter.heroku.com/articles/dyno-metadata) is enabled
|
234
|
-
- Hosted on Scalingo
|
235
|
-
|
236
|
-
Because we are not aware of all cloud hosting providers, there is a generic method to provide these data via the configuration :
|
237
|
-
|
238
|
-
```yaml
|
239
|
-
# config/rorvswild.yml
|
240
|
-
production:
|
241
|
-
api_key: API_KEY
|
242
|
-
deployment:
|
243
|
-
revision: <%= "Anything that will return the deployment version" %> # Mandatory
|
244
|
-
description: <%= "Eventually if you have a description such as a Git message" %>
|
245
|
-
author: <%= "Author's name of the deployment" %>
|
246
|
-
email: <%= "emailOf@theAuthor.com" %>
|
247
|
-
```
|
248
|
-
|
249
|
-
Here is the equivalent if you prefer initialising RorVsWild manually :
|
250
|
-
|
251
|
-
```ruby
|
252
|
-
# config/initializers/rorvswild.rb
|
253
|
-
RorVsWild.start(api_key: "API_KEY", deployment: {
|
254
|
-
revision: "Unique version number such as Git commit ID",
|
255
|
-
description: "Message such as in Git",
|
256
|
-
author: "Deployer's name",
|
257
|
-
email: "Deployer's email"
|
258
|
-
})
|
259
|
-
```
|
260
|
-
|
261
|
-
Only the revision is mandatory, but it's better if you are able to provide more information.
|
262
|
-
|
263
|
-
|
264
|
-
#### Server metrics monitoring
|
265
|
-
|
266
|
-
Since version 1.6.0 RorVsWild monitors server metrics such as load average, CPU, memory, swap and disk space.
|
267
|
-
For now, only Linux is supported.
|
268
|
-
The data are available in a server tab beside requests and jobs.
|
269
|
-
|
270
|
-
Metrics are grouped by hostnames.
|
271
|
-
Cloud providers give random hostnames which change on every deployment.
|
272
|
-
You can manually define them:
|
273
|
-
|
274
|
-
```yaml
|
275
|
-
# config/rorvswild.yml
|
276
|
-
production:
|
277
|
-
api_key: API_KEY
|
278
|
-
server:
|
279
|
-
name: <%= "Some code that return a relevant hostname" %>
|
280
|
-
```
|
281
|
-
|
282
|
-
Here is the equivalent if you prefer initialising RorVsWild manually :
|
40
|
+
![RoRvsWild Production](./images/rorvswild_prod.png)
|
283
41
|
|
284
|
-
|
285
|
-
# config/initializers/rorvswild.rb
|
286
|
-
RorVsWild.start(api_key: "API_KEY", server: {name: "host.name"})
|
287
|
-
```
|
42
|
+
## Full documentation
|
288
43
|
|
289
|
-
|
44
|
+
- [Installation](https://www.rorvswild.com/docs/get-started/installation)
|
45
|
+
- [Configuration](https://www.rorvswild.com/docs/get-started/configuration)
|
290
46
|
|
291
|
-
1. Fork it ( https://github.com/[my-github-username]/rorvswild/fork )
|
292
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
293
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
294
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
295
|
-
5. Create a new Pull Request
|
data/lib/rorvswild/client.rb
CHANGED
data/lib/rorvswild/installer.rb
CHANGED
@@ -17,6 +17,16 @@ module RorVsWild
|
|
17
17
|
|
18
18
|
def self.template(api_key)
|
19
19
|
<<YAML
|
20
|
+
development:
|
21
|
+
# Widget position
|
22
|
+
# widget: top-left, top-right, bottom-left, bottom-right or hidden
|
23
|
+
|
24
|
+
# Open files in your text editor by clicking from the local widget.
|
25
|
+
# VSCode: vscode://file${path}:${line}
|
26
|
+
# Sublime: subl://${path}:${line}
|
27
|
+
# It should be set with an env variable when developers are not using the same editor.
|
28
|
+
editor_url: <%= ENV.fetch("RORVSWILD_EDITOR_URL", "vscode://file${path}:${line}") %>
|
29
|
+
|
20
30
|
production:
|
21
31
|
api_key: #{api_key}
|
22
32
|
# ignore_requests: # Do not monitor the following actions
|
@@ -46,7 +56,7 @@ production:
|
|
46
56
|
# revision: <%= "Anything that will return the deployment version" %> # Mandatory
|
47
57
|
# description: <%= "Eventually if you have a description such as a Git message" %>
|
48
58
|
# author: <%= "Author's name of the deployment" %>
|
49
|
-
|
59
|
+
# email: <%= "emailOf@theAuthor.example" %>
|
50
60
|
YAML
|
51
61
|
end
|
52
62
|
end
|
@@ -3,6 +3,7 @@ var RorVsWild = this.RorVsWild = {};
|
|
3
3
|
RorVsWild.Local = function(container) {
|
4
4
|
this.root = container
|
5
5
|
this.embedded = !(this.active = location.pathname == "/rorvswild")
|
6
|
+
RorVsWild.Local.editorUrl = container.getAttribute("data-editor-url")
|
6
7
|
this.fetchData()
|
7
8
|
}
|
8
9
|
|
@@ -44,6 +45,7 @@ RorVsWild.Local.prototype.expand = function() {
|
|
44
45
|
this.currentRequest = this.requests[0]
|
45
46
|
this.active = true
|
46
47
|
this.render()
|
48
|
+
window.addEventListener("keydown", this.keydown.bind(this))
|
47
49
|
}
|
48
50
|
|
49
51
|
RorVsWild.Local.prototype.collapse = function() {
|
@@ -51,6 +53,11 @@ RorVsWild.Local.prototype.collapse = function() {
|
|
51
53
|
this.render()
|
52
54
|
}
|
53
55
|
|
56
|
+
RorVsWild.Local.prototype.keydown = function(event) {
|
57
|
+
if (event.key == "Escape")
|
58
|
+
this.collapse()
|
59
|
+
}
|
60
|
+
|
54
61
|
RorVsWild.Local.formatRuntime = function(runtime) {
|
55
62
|
return runtime > 0 && runtime < 1 ? "<1" : Math.round(runtime)
|
56
63
|
}
|
@@ -112,7 +119,7 @@ RorVsWild.Local.Request.prototype.runtime = function() {
|
|
112
119
|
RorVsWild.Local.Request.prototype.sections = function() {
|
113
120
|
return this.data.sections.map(function(section) {
|
114
121
|
var runtime = (section.total_runtime - section.children_runtime)
|
115
|
-
|
122
|
+
var object = {
|
116
123
|
impact: RorVsWild.Local.formatImpact(runtime * 100 / this.data.runtime),
|
117
124
|
averageRuntime: RorVsWild.Local.formatRuntime(runtime / section.calls),
|
118
125
|
command: section.kind != "view" ? section.command : null,
|
@@ -123,5 +130,10 @@ RorVsWild.Local.Request.prototype.sections = function() {
|
|
123
130
|
runtime: runtime,
|
124
131
|
language: RorVsWild.Local.kindToLanguage(section.kind),
|
125
132
|
}
|
133
|
+
if (RorVsWild.Local.editorUrl) {
|
134
|
+
var path = section.file[0] == "/" ? section.file : this.data.environment.cwd + "/" + section.file
|
135
|
+
object.url = RorVsWild.Local.editorUrl.replace("${path}", path).replace("${line}", section.line)
|
136
|
+
}
|
137
|
+
return object
|
126
138
|
}.bind(this)).sort(function(a, b) { return b.runtime - a.runtime })
|
127
139
|
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<div id="RorVsWild.Local" class="<%= widget_css %>">
|
2
|
-
<div data-barber="RorVsWild.Local">
|
2
|
+
<div data-barber="RorVsWild.Local" data-editor-url="<%= editor_url %>">
|
3
3
|
</div>
|
4
4
|
</div>
|
5
5
|
|
@@ -101,7 +101,12 @@
|
|
101
101
|
<div class="rorvswild-local-panel__request-details__section">
|
102
102
|
<div class="rorvswild-local-panel__request-details__section__main">
|
103
103
|
<span class="rorvswild-local-panel__request-details__section__file">
|
104
|
-
{{
|
104
|
+
{{#url}}
|
105
|
+
<a href="{{url}}" class="rorvswild-local-panel__request-details__section__file__name" title="Open in your editor">{{file}}<span>:{{line}}</span></a>
|
106
|
+
{{/url}}
|
107
|
+
{{^url}}
|
108
|
+
<span class="rorvswild-local-panel__request-details__section__file__name">{{file}}<span>:{{line}}</span></span>
|
109
|
+
{{/url}}
|
105
110
|
<div class="rorvswild-local-panel__request-details__section__code">
|
106
111
|
<span class="rorvswild-local-panel__request-details__section__kind">{{kind}}</span>
|
107
112
|
<span class="rorvswild-local-panel__request-details__section__command">
|
@@ -61,6 +61,10 @@ module RorVsWild
|
|
61
61
|
config && config[:widget] && "is-#{config[:widget]}"
|
62
62
|
end
|
63
63
|
|
64
|
+
def editor_url
|
65
|
+
RorVsWild.agent.config[:editor_url]
|
66
|
+
end
|
67
|
+
|
64
68
|
def inject_into(html)
|
65
69
|
if index = html.index("</body>")
|
66
70
|
markup = File.read(File.join(LOCAL_FOLDER, "local.html.erb"))
|
@@ -1,10 +1,7 @@
|
|
1
|
-
|
2
|
-
/************* TOGGLER **********************/
|
3
|
-
/********************************************/
|
4
|
-
|
1
|
+
/* TOGGLER */
|
5
2
|
.rorvswild-local-toggler {
|
6
|
-
background: rgb(
|
7
|
-
color: rgb(
|
3
|
+
background: rgb(17 20 27) !important;
|
4
|
+
color: rgb(182 202 255) !important;
|
8
5
|
font-weight: 700 !important;
|
9
6
|
box-shadow: 0 0 0 1px rgba(69,74,84,1) inset, 0 1px 4px 0px rgba(14,18,26,0.96) !important;
|
10
7
|
border-radius: 2px !important;
|
@@ -16,19 +13,18 @@
|
|
16
13
|
left: 12px !important;
|
17
14
|
padding: 0 12px !important;
|
18
15
|
cursor: pointer !important;
|
19
|
-
z-index:
|
16
|
+
z-index: 99999999999999999 !important;
|
20
17
|
}
|
21
18
|
|
22
19
|
.rorvswild-local-toggler small {
|
23
|
-
color: rgb(
|
20
|
+
color: rgb(146 149 159) !important;
|
24
21
|
font-weight: 400 !important;
|
25
22
|
font-size: 10px !important;
|
26
23
|
margin-left: 4px !important;
|
27
24
|
display: inline-block !important;
|
28
25
|
}
|
29
26
|
|
30
|
-
|
31
|
-
|
27
|
+
/* TOGGLER POSITION OPTION (default bottom-left) */
|
32
28
|
.is-top-right .rorvswild-local-toggler {
|
33
29
|
bottom: auto !important;
|
34
30
|
left: auto !important;
|
@@ -47,39 +43,29 @@
|
|
47
43
|
right: 12px !important;
|
48
44
|
}
|
49
45
|
|
50
|
-
.is-hidden .rorvswild-local-toggler {
|
51
|
-
display: none;
|
52
|
-
}
|
53
|
-
|
54
|
-
/********************************************/
|
55
|
-
/*************** PANEL **********************/
|
56
|
-
/********************************************/
|
46
|
+
.is-hidden .rorvswild-local-toggler { display: none; }
|
57
47
|
|
48
|
+
/* PANEL */
|
58
49
|
.rorvswild-local-panel {
|
59
|
-
background: rgb(
|
60
|
-
|
61
|
-
0 1px 16px 2px rgba(14,18,26,0.96) !important;
|
62
|
-
color: rgb(168,174,186) !important;
|
50
|
+
background: rgb(17 20 27) !important;
|
51
|
+
color: rgb(199 203 214) !important;
|
63
52
|
font-family: SF Mono, Menlo, Consolas, DejaVu Sans Mono, monospace !important;
|
64
53
|
font-size: 15px !important;
|
65
54
|
line-height: 24px !important;
|
66
55
|
position: fixed !important;
|
67
|
-
top:
|
68
|
-
left:
|
69
|
-
right:
|
70
|
-
bottom:
|
56
|
+
top: 0 !important;
|
57
|
+
left: 0 !important;
|
58
|
+
right: 0 !important;
|
59
|
+
bottom: 0 !important;
|
71
60
|
z-index: 100000000000000000 !important;
|
72
|
-
overflow: hidden !important;
|
73
|
-
display: -webkit-box !important;
|
74
|
-
display: -ms-flexbox !important;
|
75
61
|
display: flex !important;
|
76
|
-
-
|
77
|
-
|
78
|
-
|
79
|
-
flex-direction: column !important;
|
62
|
+
flex-direction: column !important;
|
63
|
+
overflow: hidden;
|
64
|
+
height: 100dvh;
|
80
65
|
}
|
81
66
|
|
82
|
-
.rorvswild-local-panel.is-hidden
|
67
|
+
.rorvswild-local-panel.is-hidden,
|
68
|
+
.rorvswild-local-panel:not(.is-hidden) + .rorvswild-local-toggler {
|
83
69
|
display: none !important;
|
84
70
|
}
|
85
71
|
|
@@ -91,17 +77,17 @@
|
|
91
77
|
}
|
92
78
|
|
93
79
|
.rorvswild-local-panel ::-moz-selection {
|
94
|
-
background: rgb(
|
95
|
-
color: rgb(
|
80
|
+
background: rgb(71 75 83) !important;
|
81
|
+
color: rgb(224 228 239) !important;
|
96
82
|
}
|
97
83
|
|
98
84
|
.rorvswild-local-panel ::selection {
|
99
|
-
background: rgb(
|
100
|
-
color: rgb(
|
85
|
+
background: rgb(71 75 83) !important;
|
86
|
+
color: rgb(224 228 239) !important;
|
101
87
|
}
|
102
88
|
|
103
89
|
.rorvswild-local-panel small {
|
104
|
-
color: rgb(
|
90
|
+
color: rgb(146 149 159) !important;
|
105
91
|
font-size: 10px !important;
|
106
92
|
font-weight: 400 !important;
|
107
93
|
margin-left: 2px !important;
|
@@ -109,24 +95,19 @@
|
|
109
95
|
|
110
96
|
/* Panel Header */
|
111
97
|
.rorvswild-local-panel__header {
|
112
|
-
background: rgb(
|
113
|
-
z-index: 100000000000000005 !important;
|
98
|
+
background: rgb(26 29 36) !important;
|
114
99
|
padding: 12px !important;
|
115
100
|
box-shadow: 0 1px 2px 0 rgba(14,18,26, .8) !important;
|
116
|
-
display: -webkit-box;
|
117
|
-
display: -ms-flexbox;
|
118
101
|
display: flex;
|
119
102
|
}
|
120
103
|
|
121
104
|
.rorvswild-local-panel__header__title {
|
122
|
-
color: rgb(
|
105
|
+
color: rgb(199 203 214) !important;
|
123
106
|
text-transform: uppercase !important;
|
124
107
|
font-size: 12px !important;
|
125
108
|
letter-spacing: 2px !important;
|
126
109
|
text-align: center !important;
|
127
|
-
|
128
|
-
-ms-flex: 1;
|
129
|
-
flex: 1;
|
110
|
+
flex: 1;
|
130
111
|
}
|
131
112
|
|
132
113
|
.rorvswild-local-panel svg {
|
@@ -135,26 +116,18 @@
|
|
135
116
|
stroke-width: 2 !important;
|
136
117
|
fill: none !important;
|
137
118
|
stroke-linecap: square !important;
|
138
|
-
stroke: rgb(
|
119
|
+
stroke: rgb(146 149 159) !important;
|
139
120
|
stroke-miterlimit: 10 !important;
|
140
121
|
vertical-align: middle !important;
|
141
122
|
}
|
142
123
|
|
143
|
-
.rorvswild-local-panel svg:hover {
|
144
|
-
stroke: rgb(193,204,214) !important;
|
145
|
-
}
|
124
|
+
.rorvswild-local-panel svg:hover { stroke: rgb(199 203 214) !important; }
|
146
125
|
|
147
|
-
.rorvswild-local-panel__logo {
|
148
|
-
width: 60px !important;
|
149
|
-
}
|
126
|
+
.rorvswild-local-panel__logo { width: 60px !important; }
|
150
127
|
|
151
128
|
.rorvswild-local-panel__header__icons {
|
152
129
|
width: 60px !important;
|
153
|
-
display: -webkit-box !important;
|
154
|
-
display: -ms-flexbox !important;
|
155
130
|
display: flex !important;
|
156
|
-
-webkit-box-pack: end !important;
|
157
|
-
-ms-flex-pack: end !important;
|
158
131
|
justify-content: flex-end !important;
|
159
132
|
}
|
160
133
|
|
@@ -165,22 +138,20 @@
|
|
165
138
|
|
166
139
|
.rorvswild-local-panel__github svg {
|
167
140
|
stroke: none !important;
|
168
|
-
fill: rgb(
|
141
|
+
fill: rgb(146 149 159) !important;
|
169
142
|
}
|
170
143
|
|
171
144
|
.rorvswild-local-panel__github svg:hover {
|
172
145
|
stroke: none !important;
|
173
|
-
fill: rgb(
|
146
|
+
fill: rgb(199 203 214) !important;
|
174
147
|
}
|
175
148
|
|
176
149
|
/* Panel Content */
|
177
|
-
|
178
150
|
.rorvswild-local-panel__content {
|
179
|
-
-webkit-box-flex: 1 !important;
|
180
|
-
-ms-flex: 1 !important;
|
181
151
|
flex: 1 !important;
|
182
152
|
height: calc(100% - 48px) !important;
|
183
|
-
overflow-
|
153
|
+
overflow-y: auto;
|
154
|
+
overscroll-behavior: contain;
|
184
155
|
}
|
185
156
|
|
186
157
|
.rorvswild-local-panel__content::-webkit-scrollbar { width: 4px !important; }
|
@@ -193,50 +164,46 @@
|
|
193
164
|
}
|
194
165
|
|
195
166
|
.rorvswild-local-panel__content::-webkit-scrollbar-thumb {
|
196
|
-
background-color: rgb(
|
197
|
-
outline: 1px solid rgb(
|
167
|
+
background-color: rgb(146 149 159) !important;
|
168
|
+
outline: 1px solid rgb(146 149 159) !important;
|
198
169
|
}
|
199
170
|
|
200
171
|
/* Panel request summary */
|
201
|
-
|
202
172
|
.rorvswild-local-panel__request {
|
203
|
-
display: -webkit-box !important;
|
204
|
-
display: -ms-flexbox !important;
|
205
173
|
display: flex !important;
|
206
174
|
padding: 12px 12px 11px !important;
|
207
|
-
border-bottom: 1px solid rgb(
|
175
|
+
border-bottom: 1px solid rgb(26 29 36) !important;
|
208
176
|
cursor: pointer !important;
|
209
|
-
-ms-flex-wrap: wrap !important;
|
210
177
|
flex-wrap: wrap !important;
|
211
178
|
}
|
212
179
|
|
213
|
-
.rorvswild-local-panel__request:hover { background: rgb(
|
180
|
+
.rorvswild-local-panel__request:hover { background: rgb(26 29 36) !important; }
|
214
181
|
|
215
182
|
.rorvswild-local-panel__request__name {
|
216
183
|
word-break: break-all !important;
|
217
184
|
overflow-wrap: break-word !important;
|
218
185
|
width: 100% !important;
|
219
|
-
color: rgb(
|
186
|
+
color: rgb(199 203 214) !important;
|
220
187
|
font-weight: 700;
|
221
188
|
}
|
222
189
|
|
223
190
|
.rorvswild-local-panel__request__path {
|
224
|
-
color: rgb(
|
191
|
+
color: rgb(146 149 159) !important;
|
225
192
|
font-weight: 400;
|
226
193
|
display: block !important;
|
227
194
|
}
|
228
195
|
|
229
196
|
.rorvswild-local-panel__request__started-at {
|
230
|
-
color: rgb(
|
197
|
+
color: rgb(146 149 159) !important;
|
231
198
|
margin-left: 12px !important;
|
232
199
|
}
|
200
|
+
|
233
201
|
.rorvswild-local-panel__request__runtime {
|
234
|
-
color: rgb(
|
235
|
-
font-weight:
|
202
|
+
color: rgb(182 202 255) !important;
|
203
|
+
font-weight: 700 !important;
|
236
204
|
}
|
237
205
|
|
238
206
|
/* Panel request details */
|
239
|
-
|
240
207
|
.rorvswild-local-panel__back-button {
|
241
208
|
margin: 24px 12px 18px !important;
|
242
209
|
cursor: pointer !important;
|
@@ -244,12 +211,9 @@
|
|
244
211
|
}
|
245
212
|
|
246
213
|
.rorvswild-local-panel__request-details__request {
|
247
|
-
display: -webkit-box !important;
|
248
|
-
display: -ms-flexbox !important;
|
249
214
|
display: flex !important;
|
250
215
|
margin-bottom: 48px !important;
|
251
216
|
padding: 12px 12px 0 !important;
|
252
|
-
-ms-flex-wrap: wrap !important;
|
253
217
|
flex-wrap: wrap !important;
|
254
218
|
}
|
255
219
|
|
@@ -264,26 +228,21 @@ h2.rorvswild-local-panel__request__name__title {
|
|
264
228
|
}
|
265
229
|
|
266
230
|
/* details sections */
|
267
|
-
|
268
231
|
.rorvswild-local-panel__request-details__section {
|
269
232
|
padding: 12px 12px 11px !important;
|
270
|
-
border-bottom: 1px solid rgb(
|
233
|
+
border-bottom: 1px solid rgb(26 29 36) !important;
|
271
234
|
display: block !important;
|
272
235
|
}
|
273
236
|
|
274
237
|
.rorvswild-local-panel__request-details__section:hover {
|
275
|
-
background: rgb(
|
276
|
-
-webkit-transition: all .3s !important;
|
238
|
+
background: rgb(26 29 36) !important;
|
277
239
|
transition: all .3s !important;
|
278
240
|
}
|
279
241
|
|
280
242
|
.rorvswild-local-panel__request-details__section__main {
|
281
|
-
display: -webkit-box !important;
|
282
|
-
display: -ms-flexbox !important;
|
283
243
|
display: flex !important;
|
284
244
|
width: 100% !important;
|
285
|
-
|
286
|
-
flex-wrap: wrap !important;
|
245
|
+
flex-wrap: wrap !important;
|
287
246
|
}
|
288
247
|
|
289
248
|
.rorvswild-local-panel__request-details__section__file {
|
@@ -291,39 +250,62 @@ h2.rorvswild-local-panel__request__name__title {
|
|
291
250
|
word-break: break-all !important;
|
292
251
|
overflow-wrap: break-word !important;
|
293
252
|
min-width: 0 !important;
|
294
|
-
color: rgb(
|
253
|
+
color: rgb(199 203 214) !important;
|
254
|
+
}
|
255
|
+
|
256
|
+
a.rorvswild-local-panel__request-details__section__file__name {
|
257
|
+
text-decoration-color: rgb(146 149 159);
|
258
|
+
font-weight: 700;
|
259
|
+
}
|
260
|
+
|
261
|
+
a.rorvswild-local-panel__request-details__section__file__name:hover {
|
262
|
+
text-decoration-color: rgb(199 203 214);
|
263
|
+
}
|
264
|
+
|
265
|
+
a.rorvswild-local-panel__request-details__section__file__name::after {
|
266
|
+
content: '↗';
|
267
|
+
text-decoration: none;
|
268
|
+
display: inline-block;
|
269
|
+
margin-left: 4px;
|
270
|
+
opacity: 0.5;
|
271
|
+
}
|
272
|
+
|
273
|
+
a.rorvswild-local-panel__request-details__section__file__name:hover::after { opacity: 1; }
|
274
|
+
|
275
|
+
.rorvswild-local-panel__request-details__section__file__name span {
|
276
|
+
background: rgb(38 41 49);
|
277
|
+
text-decoration: none;
|
278
|
+
display: inline-block;
|
295
279
|
}
|
296
280
|
|
297
281
|
.rorvswild-local-panel__request-details__section__average {
|
298
|
-
color: rgb(
|
282
|
+
color: rgb(182 202 255) !important;
|
299
283
|
font-weight: 700 !important;
|
300
284
|
width: 48px !important;
|
301
285
|
margin:0 12px 0 0 !important;
|
302
286
|
}
|
303
287
|
|
304
288
|
.rorvswild-local-panel__request-details__section__calls {
|
305
|
-
color: rgb(
|
289
|
+
color: rgb(199 203 214) !important;
|
306
290
|
width: 48px !important;
|
307
291
|
margin: 0 12px 0 0 !important;
|
308
292
|
}
|
309
293
|
|
310
294
|
.rorvswild-local-panel__request-details__section__impact {
|
311
|
-
color: rgb(
|
295
|
+
color: rgb(199 203 214) !important;
|
312
296
|
width: 48px !important;
|
313
297
|
font-weight: 700 !important;
|
314
298
|
margin: 0 12px 0 0 !important;
|
315
299
|
}
|
316
300
|
|
317
301
|
.rorvswild-local-panel__request-details__section__code {
|
318
|
-
display: -webkit-box !important;
|
319
|
-
display: -ms-flexbox !important;
|
320
302
|
display: flex !important;
|
321
|
-
color: rgb(
|
303
|
+
color: rgb(146 149 159) !important;
|
322
304
|
margin-top: 8px !important;
|
323
305
|
}
|
324
306
|
|
325
307
|
.rorvswild-local-panel__request-details__section__kind {
|
326
|
-
background: rgb(
|
308
|
+
background: rgb(38 41 49) !important;
|
327
309
|
font-size: 10px !important;
|
328
310
|
text-transform: uppercase !important;
|
329
311
|
text-align: center !important;
|
@@ -334,9 +316,7 @@ h2.rorvswild-local-panel__request__name__title {
|
|
334
316
|
}
|
335
317
|
|
336
318
|
.rorvswild-local-panel__request-details__section__command {
|
337
|
-
color: rgb(
|
338
|
-
-webkit-box-flex: 1 !important;
|
339
|
-
-ms-flex: 1 !important;
|
319
|
+
color: rgb(146 149 159) !important;
|
340
320
|
flex: 1 !important;
|
341
321
|
margin-left: 12px !important;
|
342
322
|
opacity: 1;
|
@@ -344,55 +324,32 @@ h2.rorvswild-local-panel__request__name__title {
|
|
344
324
|
}
|
345
325
|
|
346
326
|
/* Panel Footer */
|
347
|
-
|
348
327
|
.rorvswild-local-panel__footer {
|
349
328
|
width: 100% !important;
|
350
|
-
background: rgb(
|
351
|
-
color: rgb(
|
329
|
+
background: rgb(38 41 49) !important;
|
330
|
+
color: rgb(182 202 255) !important;
|
352
331
|
padding: 12px !important;
|
353
332
|
text-align: center !important;
|
354
333
|
}
|
355
334
|
|
356
335
|
a.rorvswild-local-panel__footer__link {
|
357
|
-
color: rgb(
|
336
|
+
color: rgb(199 203 214) !important;
|
358
337
|
text-decoration: underline !important;
|
359
338
|
line-height: inherit !important;
|
360
339
|
}
|
361
340
|
|
362
|
-
/* MEDIA QUERIES */
|
363
|
-
|
364
341
|
@media screen and (min-width: 720px) {
|
365
|
-
.rorvswild-local-panel__request-details__section__main {
|
366
|
-
-ms-flex-wrap: nowrap !important;
|
367
|
-
flex-wrap: nowrap !important;
|
368
|
-
}
|
342
|
+
.rorvswild-local-panel__request-details__section__main { flex-wrap: nowrap !important; }
|
369
343
|
|
370
|
-
.rorvswild-local-panel__request__name
|
371
|
-
|
372
|
-
-ms-flex: 1 !important;
|
373
|
-
flex: 1 !important;
|
374
|
-
}
|
344
|
+
.rorvswild-local-panel__request__name,
|
345
|
+
.rorvswild-local-panel__request-details__section__file { flex: 1 !important; }
|
375
346
|
|
376
347
|
.rorvswild-local-panel__request__runtime { margin-left: 12px !important; }
|
377
348
|
|
378
|
-
.rorvswild-local-panel__request-
|
379
|
-
|
380
|
-
-ms-flex: 1 !important;
|
381
|
-
flex: 1 !important;
|
382
|
-
}
|
383
|
-
|
384
|
-
.rorvswild-local-panel__request-details__section__average {
|
385
|
-
text-align: right !important;
|
386
|
-
margin: 0 0 0 12px !important;
|
387
|
-
}
|
388
|
-
|
389
|
-
.rorvswild-local-panel__request-details__section__calls {
|
390
|
-
text-align: right !important;
|
391
|
-
margin: 0 0 0 12px !important;
|
392
|
-
}
|
393
|
-
|
349
|
+
.rorvswild-local-panel__request-details__section__average,
|
350
|
+
.rorvswild-local-panel__request-details__section__calls,
|
394
351
|
.rorvswild-local-panel__request-details__section__impact {
|
395
352
|
text-align: right !important;
|
396
353
|
margin: 0 0 0 12px !important;
|
397
354
|
}
|
398
|
-
}
|
355
|
+
}
|
@@ -7,15 +7,16 @@ http://prismjs.com/download.html?themes=prism-twilight&languages=markup+css+clik
|
|
7
7
|
*/
|
8
8
|
.rorvswild-local-panel code[class*="language-"],
|
9
9
|
.rorvswild-local-panel pre[class*="language-"] {
|
10
|
-
color: rgb(
|
10
|
+
color: rgb(199 203 214) !important;
|
11
11
|
background: none !important;
|
12
12
|
font-family: SF Mono, Menlo, Consolas, DejaVu Sans Mono, monospace !important;
|
13
13
|
text-align: left !important;
|
14
14
|
text-shadow: 0 0 0 !important;
|
15
15
|
word-spacing: normal !important;
|
16
16
|
|
17
|
-
font-size:
|
17
|
+
font-size: 14px !important;
|
18
18
|
line-height: 24px !important;
|
19
|
+
font-weight: 300 !important;
|
19
20
|
|
20
21
|
white-space: pre-line !important;
|
21
22
|
word-wrap: break-word !important;
|
@@ -60,7 +61,7 @@ http://prismjs.com/download.html?themes=prism-twilight&languages=markup+css+clik
|
|
60
61
|
.rorvswild-local-panel .token.prolog,
|
61
62
|
.rorvswild-local-panel .token.doctype,
|
62
63
|
.rorvswild-local-panel .token.cdata {
|
63
|
-
color: rgb(
|
64
|
+
color: rgb(146 149 159) !important;
|
64
65
|
font-style: italic !important;
|
65
66
|
opacity: 0.7 !important;
|
66
67
|
}
|
@@ -77,7 +78,7 @@ http://prismjs.com/download.html?themes=prism-twilight&languages=markup+css+clik
|
|
77
78
|
.rorvswild-local-panel .token.boolean,
|
78
79
|
.rorvswild-local-panel .token.number,
|
79
80
|
.rorvswild-local-panel .token.deleted {
|
80
|
-
color: rgb(
|
81
|
+
color: rgb(255 185 173) !important;
|
81
82
|
}
|
82
83
|
|
83
84
|
.rorvswild-local-panel .token.property,
|
@@ -85,7 +86,7 @@ http://prismjs.com/download.html?themes=prism-twilight&languages=markup+css+clik
|
|
85
86
|
.rorvswild-local-panel .token.symbol,
|
86
87
|
.rorvswild-local-panel .token.builtin,
|
87
88
|
.rorvswild-local-panel .token.string {
|
88
|
-
color: rgb(
|
89
|
+
color: rgb(182 202 255) !important;
|
89
90
|
}
|
90
91
|
|
91
92
|
.rorvswild-local-panel .token.attr-name,
|
@@ -98,17 +99,17 @@ http://prismjs.com/download.html?themes=prism-twilight&languages=markup+css+clik
|
|
98
99
|
.rorvswild-local-panel .token.atrule,
|
99
100
|
.rorvswild-local-panel .token.attr-value,
|
100
101
|
.rorvswild-local-panel .token.keyword {
|
101
|
-
color: rgb(
|
102
|
+
color: rgb(248 185 227) !important;
|
102
103
|
}
|
103
104
|
|
104
105
|
.rorvswild-local-panel .token.constant,
|
105
106
|
.rorvswild-local-panel .token.regex,
|
106
107
|
.rorvswild-local-panel .token.important {
|
107
|
-
color: rgb(
|
108
|
+
color: rgb(227 201 134) !important;
|
108
109
|
}
|
109
110
|
|
110
111
|
.rorvswild-local-panel .token.function {
|
111
|
-
color: rgb(
|
112
|
+
color: rgb(124 218 210) !important;
|
112
113
|
}
|
113
114
|
|
114
115
|
.rorvswild-local-panel .token.important,
|
@@ -132,5 +133,5 @@ http://prismjs.com/download.html?themes=prism-twilight&languages=markup+css+clik
|
|
132
133
|
.rorvswild-local-panel .language-markup .token.tag,
|
133
134
|
.rorvswild-local-panel .language-markup .token.attr-name,
|
134
135
|
.rorvswild-local-panel .language-markup .token.punctuation {
|
135
|
-
color: rgb(
|
136
|
+
color: rgb(146 149 159) !important;
|
136
137
|
}
|
data/lib/rorvswild/section.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RorVsWild
|
2
4
|
class Section
|
3
5
|
attr_reader :started_at
|
@@ -30,7 +32,7 @@ module RorVsWild
|
|
30
32
|
@calls = 1
|
31
33
|
@total_runtime = 0
|
32
34
|
@children_runtime = 0
|
33
|
-
@kind = "code"
|
35
|
+
@kind = "code"
|
34
36
|
@started_at = RorVsWild.clock_milliseconds
|
35
37
|
location = RorVsWild.agent.locator.find_most_relevant_location(caller_locations)
|
36
38
|
@file = RorVsWild.agent.locator.relative_path(location.path)
|
@@ -61,7 +63,7 @@ module RorVsWild
|
|
61
63
|
total_runtime - children_runtime
|
62
64
|
end
|
63
65
|
|
64
|
-
COMMAND_MAX_SIZE =
|
66
|
+
COMMAND_MAX_SIZE = 5_000
|
65
67
|
|
66
68
|
def command=(value)
|
67
69
|
@command = value && value.size > COMMAND_MAX_SIZE ? value[0, COMMAND_MAX_SIZE] + " [TRUNCATED]" : value
|
data/lib/rorvswild/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rorvswild
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.6.
|
4
|
+
version: 1.6.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexis Bernard
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2024-01-12 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Performances and errors insights for rails developers.
|
15
15
|
email:
|