rails-webprofiler 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0d5d3d3fec249c55feee97e07f90502654799248
4
+ data.tar.gz: 9add6f25af392583b805641dab95d3b5d040f761
5
+ SHA512:
6
+ metadata.gz: c0d62734e506066c976b4cf9aa4cab9998f294b1a2a3e164c89042a5afb7ba7ec11eaee105556212b71e5bc7deab2b8aadaea9ac8f306a6ca5f2a4eb2523e3da
7
+ data.tar.gz: 29d2f4fbb9512d3fae65f52b9cfeedaa73e39c46559aa25983374468449c92037359897069777573db087c09c20392de3fc347a0dac5ea49b728190d3d662fcf
data/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ ## Unplanned
4
+
5
+ ### Features
6
+
7
+ * Create a collector for ActionMailer.
8
+ * Create a collector to list jobs created with ActiveJob.
9
+ * Create a collector to show the Rails logger informations.
10
+
11
+
12
+ ## v0.1.0
13
+
14
+ ### Features
15
+
16
+ * Friendly UI.
17
+ * ActiveRecord, ActionView, Rails, Request collectors.
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at pro@nicolas-brousse.fr. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Nicolas Brousse
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # Rails WebProfiler
2
+
3
+ A web profiler for Rails applications based on
4
+ [`rack-webprofiler`](https://github.com/rack-webprofiler/rack-webprofiler/).
5
+
6
+ [![Version ](http://img.shields.io/gem/v/rails-webprofiler.svg) ](https://rubygems.org/gems/rails-webprofiler)
7
+ [![Travis CI ](http://img.shields.io/travis/rack-webprofiler/rails-webprofiler/master.svg) ](https://travis-ci.org/rack-webprofiler/rails-webprofiler)
8
+ [![Gitter ](https://img.shields.io/gitter/room/rack-webprofiler/rails-webprofiler.svg) ](https://gitter.im/rack-webprofiler/rails-webprofiler)
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem "rails-webprofiler"
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ Simply add the gem to your Gemfile.
21
+
22
+ ## Development
23
+
24
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
25
+
26
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
27
+
28
+ ## Contributing
29
+
30
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rails-webprofiler. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
31
+
32
+
33
+ ## License
34
+
35
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
36
+
@@ -0,0 +1,16 @@
1
+ require "rack/webprofiler"
2
+
3
+ class Rails::WebProfiler
4
+
5
+ autoload :VERSION, "rails/web_profiler/version"
6
+ autoload :NotificationHandler, "rails/web_profiler/notification_handler"
7
+
8
+ module Collectors
9
+ autoload :ActionViewCollector, "rails/web_profiler/collectors/action_view_collector"
10
+ autoload :ActiveRecordCollector, "rails/web_profiler/collectors/active_record_collector"
11
+ autoload :RailsCollector, "rails/web_profiler/collectors/rails_collector"
12
+ autoload :RequestCollector, "rails/web_profiler/collectors/request_collector"
13
+ end
14
+ end
15
+
16
+ require "rails/web_profiler/railtie" if defined? Rails
@@ -0,0 +1,84 @@
1
+ module Rails
2
+ class WebProfiler::Collectors::ActionViewCollector
3
+ include ::Rack::WebProfiler::Collector::DSL
4
+
5
+ icon <<-'ICON'
6
+ data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjIwcHgiIHZpZXdCb3g9IjAgMCAxMjAgMTIwIiB3aWR0aD0iMjBweCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+ICA8cGF0aCBkPSJNNTYuNywyMS4ySDEyLjh2MTA3LjJoNzcuOFY1NS4xaC0zNFYyMS4yTDU2LjcsMjEuMnogTTY0LjgsMjEuMlY0N2gyNS45TDY0LjgsMjEuMkw2NC44LDIxLjJ6IiBmaWxsPSIjNTg1NDczIi8+PC9zdmc+
7
+ ICON
8
+
9
+ identifier "rails.action_view"
10
+ label "ActionView"
11
+ position 9
12
+
13
+ collect do |_request, _response|
14
+ data = ::Rack::WebProfiler.data("rails.events")
15
+
16
+ events = []
17
+ events = data.by_event_names(%w(render_template.action_view render_partial.action_view)) unless data.nil?
18
+
19
+ total_duration = events.inject(0) { |sum, e| sum + e.duration }
20
+
21
+ store :root_path, Rails.root.to_s
22
+ store :events, events
23
+ store :total_duration, total_duration
24
+ end
25
+
26
+ template __FILE__, type: :DATA
27
+
28
+ is_enabled? -> { defined? ::ActiveRecord }
29
+ end
30
+ end
31
+
32
+ __END__
33
+ <% tab_content do %>
34
+ <%=h data(:total_duration).round(2) %> ms
35
+
36
+ <div class="details">
37
+ <div class="wrapper">
38
+ <dl>
39
+ <dt>Time</dt>
40
+ <dd><%=h data(:total_duration).round(2) %> ms</dd>
41
+ <dt>Views</dt>
42
+ <dd><%=h data(:events).length %></dd>
43
+ </dl>
44
+ </div>
45
+ </div>
46
+ <% end %>
47
+
48
+ <% panel_content do %>
49
+ <div class="block">
50
+ <h3>ActionView</h3>
51
+
52
+ <% unless data(:events).empty? %>
53
+ <table>
54
+ <thead>
55
+ <tr>
56
+ <th width="140">#</th>
57
+ <th>View</th>
58
+ <th width="50">Time</th>
59
+ </tr>
60
+ </thead>
61
+ <tbody>
62
+ <% data(:events).each do |e| %>
63
+ <tr<% if e.duration > 500 %> class="warning"<% end %>>
64
+ <td class="code">
65
+ <%=h e.transaction_id %>
66
+ </td>
67
+ <td>
68
+ <code><%=h e.payload[:identifier].sub!(data(:root_path), '') %></code>
69
+ <% unless e.payload[:layout].nil? %>
70
+ within <code><%=h e.payload[:layout] %></code>
71
+ <% end %>
72
+ </td>
73
+ <td>
74
+ <%=h e.duration.round(2) %> ms
75
+ </td>
76
+ </tr>
77
+ <% end %>
78
+ </tbody>
79
+ </table>
80
+ <% else %>
81
+ <p><span class="text__no-value">No views loaded.</span></p>
82
+ <% end %>
83
+ </div>
84
+ <% end %>
@@ -0,0 +1,88 @@
1
+ module Rails
2
+ class WebProfiler::Collectors::ActiveRecordCollector
3
+ include ::Rack::WebProfiler::Collector::DSL
4
+
5
+ icon <<-'ICON'
6
+ data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB3aWR0aD0iMjBweCIgaGVpZ2h0PSIyMHB4IiB2aWV3Qm94PSIwIDAgMjAgMjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+ICAgICAgICA8dGl0bGU+UmVjdGFuZ2xlIDQ8L3RpdGxlPiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4gICAgPGRlZnM+PC9kZWZzPiAgICA8ZyBpZD0iUGFnZS0xIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4gICAgICAgIDxnIGlkPSJEZXNrdG9wLTIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xMS4wMDAwMDAsIC0yNTAuMDAwMDAwKSIgZmlsbD0iIzU4NTQ3MyI+ICAgICAgICAgICAgPGcgaWQ9IkRhdGFiYXNlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLjAwMDAwMCwgMjQwLjAwMDAwMCkiPiAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEsMTAgTDMxLDEwIEwzMSwxNiBMMTEsMTYgTDExLDEwIFogTTExLDE3IEwzMSwxNyBMMzEsMjMgTDExLDIzIEwxMSwxNyBaIE0xMSwyNCBMMzEsMjQgTDMxLDMwIEwxMSwzMCBMMTEsMjQgWiIgaWQ9IlJlY3RhbmdsZS00Ij48L3BhdGg+ICAgICAgICAgICAgPC9nPiAgICAgICAgPC9nPiAgICA8L2c+PC9zdmc+
7
+ ICON
8
+
9
+ identifier "rails.active_record"
10
+ label "ActiveRecord"
11
+ position 10
12
+
13
+ collect do |_request, _response|
14
+ data = ::Rack::WebProfiler.data("rails.events")
15
+
16
+ events = []
17
+ events = data.by_event_name("sql.active_record") unless data.nil?
18
+
19
+ total_duration = events.inject(0) { |sum, e| sum + e.duration }
20
+
21
+ store :events, events
22
+ store :total_duration, total_duration
23
+ end
24
+
25
+ template __FILE__, type: :DATA
26
+
27
+ is_enabled? -> { defined? ::ActiveRecord }
28
+ end
29
+ end
30
+
31
+ __END__
32
+ <% tab_content do %>
33
+ <%=h data(:events).length %>
34
+
35
+ <div class="details">
36
+ <div class="wrapper">
37
+ <dl>
38
+ <dt>Total</dt>
39
+ <dd><%=h data(:events).length %></dd>
40
+ </dl>
41
+ </div>
42
+ </div>
43
+ <% end %>
44
+
45
+ <% panel_content do %>
46
+ <div class="block">
47
+ <h3>Informations</h3>
48
+
49
+ <p>Queries: <%=h data(:events).length %></p>
50
+ <p>Total time: <%=h data(:total_duration).round(2) %> ms</p>
51
+ </div>
52
+
53
+ <div class="block">
54
+ <h3>SQL Queries</h3>
55
+
56
+ <% unless data(:events).empty? %>
57
+ <table>
58
+ <thead>
59
+ <tr>
60
+ <th width="140">#</th>
61
+ <th>SQL</th>
62
+ <th width="50">Time</th>
63
+ </tr>
64
+ </thead>
65
+ <tbody>
66
+ <% data(:events).each do |e| %>
67
+ <tr<% if e.duration > 500 %> class="warning"<% end %>>
68
+ <td class="code">
69
+ <%=h e.transaction_id %>
70
+ </td>
71
+ <td>
72
+ <%=highlight language: :sql, code: e.payload[:sql] %>
73
+ <% if e.payload[:binds].length > 0 %>
74
+ <strong>params: </strong> <code>[<%=h e.payload[:binds].map { |b| "[\"#{b.name}\", #{b.value_for_database.inspect}]" }.join(", ") %>]</code>
75
+ <% end %>
76
+ </td>
77
+ <td>
78
+ <%=h e.duration.round(2) %> ms
79
+ </td>
80
+ </tr>
81
+ <% end %>
82
+ </tbody>
83
+ </table>
84
+ <% else %>
85
+ <p><span class="text__no-value">No queries executed.</span></p>
86
+ <% end %>
87
+ </div>
88
+ <% end %>
@@ -0,0 +1,103 @@
1
+ module Rails
2
+ # @todo extends Rack::WebProfiler::Collectors::RackCollector
3
+ class WebProfiler::Collectors::RailsCollector
4
+ include ::Rack::WebProfiler::Collector::DSL
5
+
6
+ icon <<-'ICON'
7
+ data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDAgMTk5LjgiIHdpZHRoPSIyMHB4IiBoZWlnaHQ9IjIwcHgiID4gIDxnIGZpbGw9IiM1ODU0NzMiPiAgICA8cGF0aCBkPSJNMjMuMSwxOTdoOTUuOHMtMjEuMy00OC4zLTE1LjktOTIuNiw0MC4zLTY0LjUsNjAuMi02Ni42LDMwLjYsMTAuMSwzMC42LDEwLjFsNC4zLTYuNXMtMjguNS0yOC44LTY0LjUtMjUuNi02MC45LDI3LTc2LjQsNTUuOC0yNC41LDQ5LTMwLjYsNzkuM1MyMy4xLDE5NywyMy4xLDE5N1oiLz4gICAgPHBhdGggZD0iTTQsMTQ4LjdsMTguNywxLjQtMy4yLDE4LjdMMS44LDE2Ni43WiIvPiAgICA8cGF0aCBkPSJNMzMuNSwxMDkuMWw1LTE0TDIyLDg4LjZsLTUuNCwxNS4xWiIvPiAgICA8cGF0aCBkPSJNNTcsNjAuOGw5LjctMTEuNUw1NC4xLDQxLDQ0LDUyLjJaIi8+ICAgIDxwYXRoIGQ9Ik04Mi41LDE2LjFsOC42LDEwLjQsMTEuOS02LjhMOTQuNCwxMFoiLz4gICAgPHBhdGggZD0iTTEyNC43LDQuM2wyLjIsMTAuNCwxNC44LS40TDE0MC4yLDVaIi8+ICAgIDxwYXRoIGQ9Ik0xNzQuNywxNS40bC0uNCw2LjUsMTAuMSw1LjQsMi4yLTMuMloiLz4gICAgPHBhdGggZD0iTTE3Mi42LDQzLjJ2NS40bDkuNywxLjFWNDUuM1oiLz4gICAgPHBhdGggZD0iTTE0MS42LDQ3LjhsNC43LDguNiw2LjgtNS40LTEuNC01LjhaIi8+ICAgIDxwYXRoIGQ9Ik0xMjYuOCw1OSwxMzQsNjkuOGwtNC4zLDcuNkwxMTkuMyw2NS45WiIvPiAgICA8cGF0aCBkPSJNMTEyLjQsODguOWwtNC43LDkuNEwxMjAsMTA4LjdsMi45LTExLjlaIi8+ICAgIDxwYXRoIGQ9Ik0xMDcuNCwxMjEuN2wtLjcsMTIuNiwxNS4xLDYuNS0uNy0xMS45WiIvPiAgICA8cGF0aCBkPSJNMTExLjcsMTY2LjRsMy42LDEzLDE5LjEsMS4xLTYuOC0xNFoiLz4gIDwvZz4gPC9zdmc+
8
+ ICON
9
+
10
+ identifier "rails.rails"
11
+ label "Rails"
12
+ position 1
13
+
14
+ collect do |request, _response|
15
+ store :rack_version, ::Rack.release
16
+
17
+ store :rails_version, Rails.version
18
+ store :rails_env, Rails.env
19
+ store :rails_doc_url, "http://api.rubyonrails.org/v#{Rails.version}/"
20
+ store :env, hash_stringify_values(request.env)
21
+ end
22
+
23
+ template __FILE__, type: :DATA
24
+
25
+ is_enabled? -> { defined? ::Rails }
26
+
27
+ class << self
28
+ def hash_stringify_values(hash)
29
+ return {} unless hash.kind_of?(Hash)
30
+ hash.collect do |k,v|
31
+ v = v.inspect unless v.kind_of?(String)
32
+ [k, v]
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ __END__
40
+ <% tab_content do %>
41
+ <%=h data(:rails_version) %> | <%=h data(:rails_env) %>
42
+ <% end %>
43
+
44
+ <% panel_content do %>
45
+ <div class="block">
46
+ <h3>Rails informations</h3>
47
+
48
+ <table>
49
+ <tbody>
50
+ <tr>
51
+ <th>Version</th>
52
+ <td><%=h data(:rails_version) %></td>
53
+ </tr>
54
+ <tr>
55
+ <th>Environment</th>
56
+ <td><%=h data(:rails_env) %></td>
57
+ </tr>
58
+ <tr>
59
+ <th>Documentation</th>
60
+ <td><a href="<%=h data(:rails_doc_url) %>"><%=h data(:rails_doc_url) %></td>
61
+ </tr>
62
+ </tbody>
63
+ </table>
64
+ </div>
65
+
66
+ <div class="block">
67
+ <h3>Rack informations</h3>
68
+
69
+ <table>
70
+ <tbody>
71
+ <tr>
72
+ <th>Version</th>
73
+ <td><%=h data(:rack_version) %></td>
74
+ </tr>
75
+ </tbody>
76
+ </table>
77
+ </div>
78
+
79
+ <div class="block">
80
+ <h3>Env</h3>
81
+
82
+ <% if data(:env) && !data(:env).empty? %>
83
+ <table>
84
+ <thead>
85
+ <tr>
86
+ <th>Key</th>
87
+ <th>Value</th>
88
+ </tr>
89
+ <thead>
90
+ <tbody>
91
+ <% data(:env).sort.each do |k, v| %>
92
+ <tr>
93
+ <th><%=h k %></th>
94
+ <td class="code"><%=h v %></td>
95
+ </tr>
96
+ <% end %>
97
+ </tbody>
98
+ </table>
99
+ <% else %>
100
+ <p><span class="text__no-value">No rack env datas</span></p>
101
+ <% end %>
102
+ </div>
103
+ <% end %>
@@ -0,0 +1,257 @@
1
+ module Rails
2
+ # @todo extends Rack::WebProfiler::Collectors::RequestCollector
3
+ class WebProfiler::Collectors::RequestCollector
4
+ include ::Rack::WebProfiler::Collector::DSL
5
+
6
+ icon <<-'ICON'
7
+ data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB3aWR0aD0iMjBweCIgaGVpZ2h0PSIyMHB4IiB2aWV3Qm94PSIwIDAgMjAgMjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+ICAgICAgICA8dGl0bGU+T3ZhbCA3IENvcHkgMjwvdGl0bGU+ICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPiAgICA8ZGVmcz48L2RlZnM+ICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPiAgICAgICAgPGcgaWQ9IkRlc2t0b3AtMiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEwLjAwMDAwMCwgLTEzMC4wMDAwMDApIiBmaWxsPSIjNTg1NDczIj4gICAgICAgICAgICA8ZyBpZD0iUmVxdWVzdCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDEyMC4wMDAwMDApIj4gICAgICAgICAgICAgICAgPHBhdGggZD0iTTE3LjY3OTE5MTIsMTMuNDA2NDMyMiBDMTguMTAzNzAxMiwxMy4yNTUzNzMzIDE4LjU0NzA4OSwxMy4xNDQ1ODY0IDE5LjAwNDgzMTcsMTMuMDc4NjI3OCBMMTkuMDA0ODMxNywxMCBMMjAuOTk1MTY4MywxMCBMMjAuOTk1MTY4MywxMy4wNzg2Mjc4IEMyMS40NTI5MTEsMTMuMTQ0NTg2NCAyMS44OTYyOTg4LDEzLjI1NTM3MzMgMjIuMzIwODA4OCwxMy40MDY0MzIyIEwyMy43NjY1Mzg2LDEwLjY4NzQxIEwyNS41MjM5MDE1LDExLjYyMTgxNjUgTDI0LjA4MDM0MDcsMTQuMzM2NzU5NiBDMjQuNDU0NDUyLDE0LjYwOTU2NTUgMjQuODAwNTkwMywxNC45MTg5MDYzIDI1LjExMzU5ODIsMTUuMjU5NTg2NCBMMjcuNjI0NzkyLDEzLjUwMTIyOTYgTDI4Ljc2NjQwMjIsMTUuMTMxNjE3OSBMMjYuMjM5NDcyLDE2LjkwMDk5MzUgQzI2LjQ0Mjk1ODQsMTcuMzEyNjE1IDI2LjYwNzQ4OTQsMTcuNzQ3MjEzIDI2LjcyODIwMjgsMTguMTk5ODg5MyBMMjkuNzc1NjgxNCwxNy44MjU3MDU5IEwzMC4wMTgyNDI0LDE5LjgwMTIwNjkgTDI2Ljk2NDU5ODcsMjAuMTc2MTQ3MiBDMjYuOTU0Njg2MiwyMC42NDkzNjI3IDI2Ljg5ODI3NTUsMjEuMTExMDU1NiAyNi43OTk3ODQxLDIxLjU1Njc3NTkgTDI5LjY3NTIwODQsMjIuNjYwNTQ3OSBMMjguOTYxOTM1NiwyNC41MTg2ODcyIEwyNi4wOTg1NTE3LDIzLjQxOTUzNzEgQzI1Ljg3MzU5ODcsMjMuODI4ODI2IDI1LjYwOTIyNDgsMjQuMjEzMDczNSAyNS4zMTA1OTI0LDI0LjU2NzA3OTEgTDI3LjM2NTk4NjQsMjYuODQ5ODI1NCBMMjUuODg2ODc4LDI4LjE4MTYyMDYgTDIzLjgyNDc3MTQsMjUuODkxNDE5MSBDMjMuNDQ1Mzk3NywyNi4xNDI5NDcyIDIzLjA0MDE3MDgsMjYuMzU4MTMyMyAyMi42MTM5NzI3LDI2LjUzMjA1NjQgTDIzLjM1ODEzMTgsMjkuNTE2NzE1NCBMMjEuNDI2OTE2NiwyOS45OTgyMjE0IEwyMC42ODE3ODQ0LDI3LjAwOTY1OTQgQzIwLjQ1NzQ2NzgsMjcuMDMxNjA5MyAyMC4yMzAwMzYsMjcuMDQyODQxNiAyMCwyNy4wNDI4NDE2IEMxOS43Njk5NjQsMjcuMDQyODQxNiAxOS41NDI1MzIyLDI3LjAzMTYwOTMgMTkuMzE4MjE1NiwyNy4wMDk2NTk0IEwxOC41NzMwODM0LDI5Ljk5ODIyMTQgTDE2LjY0MTg2ODIsMjkuNTE2NzE1NCBMMTcuMzg2MDI3MywyNi41MzIwNTY0IEMxNi45NTk4MjkyLDI2LjM1ODEzMjMgMTYuNTU0NjAyMywyNi4xNDI5NDcyIDE2LjE3NTIyODYsMjUuODkxNDE5MSBMMTQuMTEzMTIyLDI4LjE4MTYyMDYgTDEyLjYzNDAxMzYsMjYuODQ5ODI1NCBMMTQuNjg5NDA3NiwyNC41NjcwNzkxIEMxNC4zOTA3NzUyLDI0LjIxMzA3MzUgMTQuMTI2NDAxMywyMy44Mjg4MjYgMTMuOTAxNDQ4MywyMy40MTk1MzcxIEwxMS4wMzgwNjQ0LDI0LjUxODY4NzIgTDEwLjMyNDc5MTYsMjIuNjYwNTQ3OSBMMTMuMjAwMjE1OSwyMS41NTY3NzU5IEMxMy4xMDE3MjQ1LDIxLjExMTA1NTYgMTMuMDQ1MzEzOCwyMC42NDkzNjI4IDEzLjAzNTQwMTMsMjAuMTc2MTQ3MyBMOS45ODE3NTc1OSwxOS44MDEyMDY5IEwxMC4yMjQzMTg2LDE3LjgyNTcwNTkgTDEzLjI3MTc5NzIsMTguMTk5ODg5MyBDMTMuMzkyNTEwNiwxNy43NDcyMTMgMTMuNTU3MDQxNiwxNy4zMTI2MTUgMTMuNzYwNTI4LDE2LjkwMDk5MzUgTDExLjIzMzU5NzgsMTUuMTMxNjE3OSBMMTIuMzc1MjA4LDEzLjUwMTIyOTYgTDE0Ljg4NjQwMTgsMTUuMjU5NTg2NCBDMTUuMTk5NDA5NywxNC45MTg5MDYzIDE1LjU0NTU0OCwxNC42MDk1NjU1IDE1LjkxOTY1OTMsMTQuMzM2NzU5NiBMMTQuNDc2MDk4NSwxMS42MjE4MTY1IEwxNi4yMzM0NjE0LDEwLjY4NzQxIEwxNy42NzkxOTEyLDEzLjQwNjQzMjIgWiBNMjAsMjMuMDMyNzYxMiBDMjEuNjQ4ODQ4OSwyMy4wMzI3NjEyIDIyLjk4NTUwNSwyMS42ODYyMzA2IDIyLjk4NTUwNSwyMC4wMjUyMDA5IEMyMi45ODU1MDUsMTguMzY0MTcxMyAyMS42NDg4NDg5LDE3LjAxNzY0MDYgMjAsMTcuMDE3NjQwNiBDMTguMzUxMTUxMSwxNy4wMTc2NDA2IDE3LjAxNDQ5NSwxOC4zNjQxNzEzIDE3LjAxNDQ5NSwyMC4wMjUyMDA5IEMxNy4wMTQ0OTUsMjEuNjg2MjMwNiAxOC4zNTExNTExLDIzLjAzMjc2MTIgMjAsMjMuMDMyNzYxMiBaIiBpZD0iT3ZhbC03LUNvcHktMiI+PC9wYXRoPiAgICAgICAgICAgIDwvZz4gICAgICAgIDwvZz4gICAgPC9nPjwvc3ZnPg==
8
+ ICON
9
+
10
+ identifier "rails.request"
11
+ label "Request"
12
+ position 2
13
+
14
+ collect do |request, response|
15
+ route, _matches, _request_params = find_route(request)
16
+
17
+ store :request_headers, request.http_headers
18
+ store :request_fullpath, request.fullpath
19
+ store :request_method, request.request_method
20
+ store :request_cookies, request.cookies
21
+ store :request_get, request.GET
22
+ store :request_post, request_post(request)
23
+ store :request_session, hash_stringify_values(request.session)
24
+ store :request_cookies, request.cookies
25
+ store :request_body, request.body_string
26
+ store :request_mediatype, request.media_type
27
+ store :request_raw, request.raw
28
+
29
+ store :response_status, response.status
30
+ store :response_headers, response.headers
31
+ store :response_raw, response.raw
32
+
33
+ store :route_name, route.nil? ? nil : route.name
34
+ store :path_parameters, request.env["action_dispatch.request.path_parameters"]
35
+
36
+ if response.successful?
37
+ status :success
38
+ elsif response.redirection?
39
+ status :warning
40
+ else
41
+ status :danger
42
+ end
43
+ end
44
+
45
+ template __FILE__, type: :DATA
46
+
47
+ is_enabled? -> { defined? ::Rails }
48
+
49
+ class << self
50
+ def find_route(request)
51
+ request = ::ActionDispatch::Request.new(request.env)
52
+
53
+ Rails.application.routes.router.recognize(request) do |route, matches, params|
54
+ return [route, matches, params]
55
+ end
56
+ end
57
+
58
+ def request_post(request)
59
+ request.POST if request.POST && !request.POST.empty?
60
+ rescue Exception
61
+ nil
62
+ end
63
+
64
+ def hash_stringify_values(hash)
65
+ return {} unless hash.kind_of?(Hash)
66
+ hash.collect {|k,v| [k, v.to_s]}
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ __END__
73
+ <% tab_content do %>
74
+ <%=h data(:response_status) %>
75
+
76
+ <div class="details">
77
+ <div class="wrapper">
78
+ <dl>
79
+ <dt>Status</dt>
80
+ <dd><%=h data(:response_status) %> - <%=h Rack::Utils::HTTP_STATUS_CODES[data(:response_status).to_i] %></dd>
81
+ <dt>Path</dt>
82
+ <dd><%=h data(:request_method) %> <%=h data(:request_fullpath) %></dd>
83
+ <dt>Route</dt>
84
+ <dd><%=h data(:route_name) %></dd>
85
+ <% if !data(:path_parameters)[:controller].nil? && !data(:path_parameters)[:action].nil? %>
86
+ <dt>Action</dt>
87
+ <dd><%=h "#{data(:path_parameters)[:controller]}##{data(:path_parameters)[:action]}" %></dd>
88
+ <% end %>
89
+ </dl>
90
+ </div>
91
+ </div>
92
+ <% end %>
93
+
94
+ <% panel_content do %>
95
+ <div class="block">
96
+ <h3>GET</h3>
97
+ <% unless data(:request_get).empty? %>
98
+ <table>
99
+ <thead>
100
+ <tr>
101
+ <th>Key</th>
102
+ <th>Value</th>
103
+ </tr>
104
+ <thead>
105
+ <tbody>
106
+ <% data(:request_get).each do |k, v| %>
107
+ <tr>
108
+ <th><%=h k %></th>
109
+ <td class="code"><%=h v %></td>
110
+ </tr>
111
+ <% end %>
112
+ </tbody>
113
+ </table>
114
+ <% else %>
115
+ <p><span class="text__no-value">No GET parameters</span></p>
116
+ <% end %>
117
+ </div>
118
+
119
+ <div class="block">
120
+ <h3>POST</h3>
121
+ <% if data(:request_post) && !data(:request_post).empty? %>
122
+ <table>
123
+ <thead>
124
+ <tr>
125
+ <th>Key</th>
126
+ <th>Value</th>
127
+ </tr>
128
+ <thead>
129
+ <tbody>
130
+ <% data(:request_post).each do |k, v| %>
131
+ <tr>
132
+ <th><%=h k %></th>
133
+ <td class="code"><%=h v %></td>
134
+ </tr>
135
+ <% end %>
136
+ </tbody>
137
+ </table>
138
+ <% else %>
139
+ <p><span class="text__no-value">No POST parameters</span></p>
140
+ <% end %>
141
+ </div>
142
+
143
+ <div class="block">
144
+ <h3>Request headers</h3>
145
+ <% unless data(:request_headers).empty? %>
146
+ <table>
147
+ <thead>
148
+ <tr>
149
+ <th>Name</th>
150
+ <th>Version</th>
151
+ </tr>
152
+ <thead>
153
+ <tbody>
154
+ <% data(:request_headers).sort.each do |k, v| %>
155
+ <tr>
156
+ <th><%=h k %></th>
157
+ <td class="code"><%=h v %></td>
158
+ </tr>
159
+ <% end %>
160
+ </tbody>
161
+ </table>
162
+ <% else %>
163
+ <p><span class="text__no-value">No request headers</span></p>
164
+ <% end %>
165
+ </div>
166
+
167
+ <div class="block">
168
+ <h3>Request content</h3>
169
+ <% unless data(:request_body).empty? %>
170
+ <%=highlight mimetype: data(:request_mediatype), code: data(:request_body) %>
171
+ <% else %>
172
+ <p><span class="text__no-value">No request content</span></p>
173
+ <% end %>
174
+ </div>
175
+ </div>
176
+
177
+ <div class="block">
178
+ <h3>Request raw</h3>
179
+ <% unless data(:request_raw).nil? %>
180
+ <%=highlight language: :http, code: data(:request_raw) %>
181
+ <% else %>
182
+ <p><span class="text__no-value">No request raw</span></p>
183
+ <% end %>
184
+ </div>
185
+
186
+ <div class="block">
187
+ <h3>Response headers</h3>
188
+ <% unless data(:response_headers).empty? %>
189
+ <table>
190
+ <thead>
191
+ <tr>
192
+ <th>Name</th>
193
+ <th>Version</th>
194
+ </tr>
195
+ <thead>
196
+ <tbody>
197
+ <% data(:response_headers).sort.each do |k, v| %>
198
+ <tr>
199
+ <th><%= k %></th>
200
+ <td class="code"><%= v %></td>
201
+ </tr>
202
+ <% end %>
203
+ </tbody>
204
+ </table>
205
+ <% else %>
206
+ <p><span class="text__no-value">No response headers</span></p>
207
+ <% end %>
208
+ </div>
209
+
210
+ <div class="block">
211
+ <h3>Session</h3>
212
+ <% unless data(:request_session).empty? %>
213
+ <table>
214
+ <thead>
215
+ <tr>
216
+ <th>Name</th>
217
+ <th>Version</th>
218
+ </tr>
219
+ <thead>
220
+ <tbody>
221
+ <% data(:request_session).each do |k, v| %>
222
+ <tr>
223
+ <th><%= k %></th>
224
+ <td class="code"><%= v %></td>
225
+ </tr>
226
+ <% end %>
227
+ </tbody>
228
+ </table>
229
+ <% else %>
230
+ <p><span class="text__no-value">No session data</span></p>
231
+ <% end %>
232
+ </div>
233
+
234
+ <div class="block">
235
+ <h3>Cookies</h3>
236
+ <% unless data(:request_cookies).empty? %>
237
+ <table>
238
+ <thead>
239
+ <tr>
240
+ <th>Name</th>
241
+ <th>Version</th>
242
+ </tr>
243
+ <thead>
244
+ <tbody>
245
+ <% data(:request_cookies).each do |k, v| %>
246
+ <tr>
247
+ <th><%=h k %></th>
248
+ <td class="code"><%=h v %></td>
249
+ </tr>
250
+ <% end %>
251
+ </tbody>
252
+ </table>
253
+ <% else %>
254
+ <p><span class="text__no-value">No cookies data</span></p>
255
+ <% end %>
256
+ </div>
257
+ <% end %>
@@ -0,0 +1,26 @@
1
+ class Rails::WebProfiler::NotificationHandler
2
+ def call(*args)
3
+ event = ActiveSupport::Notifications::Event.new(*args)
4
+
5
+ events = ::Rack::WebProfiler.data("rails.events")
6
+ events ||= Events.new
7
+ events << event
8
+ ::Rack::WebProfiler.data("rails.events", events)
9
+ end
10
+
11
+ class Events < Array
12
+ def by_event_name(event_name)
13
+ select do |e|
14
+ if event_name.is_a?(Array)
15
+ event_name.include?(e.name)
16
+ elsif event_name.is_a?(Regexp)
17
+ event_name.scan(event_name).length > 0
18
+ else
19
+ e.name === event_name
20
+ end
21
+ end
22
+ end
23
+
24
+ alias by_event_names by_event_name
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ require "rails"
2
+
3
+ module Rails
4
+ # WebProfiler::Railtie
5
+ class WebProfiler::Railtie < ::Rails::Railtie # :nodoc:
6
+ initializer "rails-webprofiler.configure_rails_initialization" do |app|
7
+ app.middleware.use ::Rack::WebProfiler do |c|
8
+ c.tmp_dir = ::File.expand_path(::File.join(Rails.root, "tmp"), __FILE__)
9
+ end
10
+
11
+ ::Rack::WebProfiler.unregister_collector [
12
+ # ::Rack::WebProfiler::Collectors::ExceptionCollector,
13
+ ::Rack::WebProfiler::Collectors::RackCollector,
14
+ ::Rack::WebProfiler::Collectors::RequestCollector,
15
+ ]
16
+
17
+ ::Rack::WebProfiler.register_collector [
18
+ Rails::WebProfiler::Collectors::ActionViewCollector,
19
+ Rails::WebProfiler::Collectors::ActiveRecordCollector,
20
+ Rails::WebProfiler::Collectors::RailsCollector,
21
+ Rails::WebProfiler::Collectors::RequestCollector,
22
+ ]
23
+
24
+ # Subscrine all Rails notifications.
25
+ handler = Rails::WebProfiler::NotificationHandler.new
26
+ ActiveSupport::Notifications.subscribe(/.+/, handler)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,5 @@
1
+ module Rails
2
+ class WebProfiler
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ require "rails/web_profiler"
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rails/web_profiler/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rails-webprofiler"
8
+ spec.version = Rails::WebProfiler::VERSION
9
+ spec.authors = ["Nicolas Brousse"]
10
+ spec.email = ["pro@nicolas-brousse.fr"]
11
+
12
+ spec.summary = %q{A web profiler for Rails applications.}
13
+ spec.description = %q{A web profiler for Rails applications.}
14
+ spec.homepage = "http://github.com/rack-webprofiler/rails-webprofiler"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
+ else
22
+ # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
26
+ f.match(%r{^(bin|test|spec|features|examples)/}) \
27
+ || f.match(%r{^(Guardfile|Gemfile|Rakefile)$}) \
28
+ || f.match(%r{^(\..*)$})
29
+ end
30
+
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.add_dependency "rails", ">= 5.0.0"
34
+ spec.add_dependency "rack-webprofiler", "~> 0.1.0"
35
+
36
+ spec.add_development_dependency "bundler", "~> 1.12"
37
+ spec.add_development_dependency "rake", "~> 10.0"
38
+ spec.add_development_dependency "rspec", "~> 3.0"
39
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails-webprofiler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nicolas Brousse
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-12-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 5.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rack-webprofiler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ description: A web profiler for Rails applications.
84
+ email:
85
+ - pro@nicolas-brousse.fr
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - CHANGELOG.md
91
+ - CODE_OF_CONDUCT.md
92
+ - LICENSE
93
+ - README.md
94
+ - lib/rails/web_profiler.rb
95
+ - lib/rails/web_profiler/collectors/action_view_collector.rb
96
+ - lib/rails/web_profiler/collectors/active_record_collector.rb
97
+ - lib/rails/web_profiler/collectors/rails_collector.rb
98
+ - lib/rails/web_profiler/collectors/request_collector.rb
99
+ - lib/rails/web_profiler/notification_handler.rb
100
+ - lib/rails/web_profiler/railtie.rb
101
+ - lib/rails/web_profiler/version.rb
102
+ - lib/rails/webprofiler.rb
103
+ - rails-webprofiler.gemspec
104
+ homepage: http://github.com/rack-webprofiler/rails-webprofiler
105
+ licenses:
106
+ - MIT
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.5.2
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: A web profiler for Rails applications.
128
+ test_files: []