rack-profiler 0.0.1

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: dcf9692d850db32e476a603183617395fdb6bdc4
4
+ data.tar.gz: 75d12789c07ac795e3eb027762f4042d1bf243fe
5
+ SHA512:
6
+ metadata.gz: cc077a17c68d75b88ca9ba0bc4000ee35021bfdc4d209be40b1b31a976cf57b7615d769bb509e05bcb89c57c1b2c9393dd24fb31e651fac87e47c6810d601518
7
+ data.tar.gz: 54c806345ef491647fc97a85d1ba27086df6f325e747354180d823948d115081da8d0b6d3a9424b1ed50890b6aafd01dfea88a30222c25ddc425873113efcc33
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rack-profiler.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Luca Ongaro
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # Rack::Profiler
2
+
3
+ Simple profiler for Rack applications
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'rack-profiler'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install rack-profiler
20
+
21
+ ## Usage
22
+
23
+ In your `config.ru` use the `Rack::Profiler` middleware at the beginning of your
24
+ middleware stack:
25
+
26
+ ```ruby
27
+ require 'rack/profiler'
28
+ use Rack::Profiler
29
+ ```
30
+
31
+ By default `Rack::Profiler` will subscribe to `ActiveRecord` SQL queries and to
32
+ steps you define in your code with:
33
+
34
+ ```ruby
35
+ Rack::Profiler.step('your-step-name') do
36
+ # Do stuff. The profiler will tell you how long it took to perform this step
37
+ end
38
+ ```
39
+
40
+ You can also subscribe to more notifications:
41
+
42
+ ```ruby
43
+ Rack::Profiler.configure do |profiler|
44
+ # Subscribe to template rendering in a Rails app
45
+ profiler.subscribe('render_template.action_view')
46
+ end
47
+ ```
48
+
49
+ You can also specify a backtrace filter to exclude lines that are not
50
+ interesting:
51
+
52
+ ```ruby
53
+ Rack::Profiler.configure do |profiler|
54
+ # Exclude gems from the backtrace
55
+ profiler.filter_backtrace { |line| !line.include? '/gems/' }
56
+ end
57
+ ```
58
+
59
+ ## Contributing
60
+
61
+ 1. Fork it ( https://github.com/[my-github-username]/rack-profiler/fork )
62
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
63
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
64
+ 4. Push to the branch (`git push origin my-new-feature`)
65
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ class Profiler
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,138 @@
1
+ require "rack/profiler/version"
2
+ require "active_support/notifications"
3
+
4
+ module Rack
5
+ class Profiler
6
+ class DummyError < StandardError; end
7
+
8
+ module ClassMethods
9
+ attr_reader :backtrace_filter
10
+
11
+ def configure(&block)
12
+ instance_exec(self, &block)
13
+ end
14
+
15
+ def filter_backtrace(&block)
16
+ @backtrace_filter = block
17
+ end
18
+
19
+ def dashboard_path=(path)
20
+ @dashboard_path = path
21
+ end
22
+
23
+ def dashboard_path
24
+ @dashboard_path || '/rack-profiler'
25
+ end
26
+
27
+ def step(name, payload = {})
28
+ ActiveSupport::Notifications.instrument('rack-profiler.step', payload.merge(step_name: name)) do
29
+ yield
30
+ end
31
+ end
32
+
33
+ def render_dashboard
34
+ dashboard = ::File.expand_path( '../../public/rack-profiler.html', ::File.dirname( __FILE__ ) )
35
+ body = ::File.open(dashboard, ::File::RDONLY)
36
+ [200, { 'Content-Type' => 'text/html' }, body]
37
+ end
38
+
39
+ def subscribe(name)
40
+ (@subscriptions ||= []) << name
41
+ @subscriptions.uniq!
42
+ end
43
+
44
+ def subscriptions
45
+ @subscriptions || []
46
+ end
47
+ end
48
+
49
+ extend ClassMethods
50
+
51
+ # Subscribe to interesting events
52
+ subscribe('sql.active_record')
53
+ subscribe('render_template.action_view')
54
+ subscribe('render_partial.action_view')
55
+ subscribe('process_action.action_controller')
56
+ subscribe('rack-profiler.step')
57
+
58
+ attr_reader :events
59
+
60
+ def initialize(app)
61
+ subscribe_all_events
62
+ @app = app
63
+ end
64
+
65
+ def call(env)
66
+ @events = []
67
+ status, headers, body = [nil, nil, nil]
68
+ req = Rack::Request.new(env)
69
+
70
+ if req.path == Profiler.dashboard_path
71
+ Profiler.render_dashboard
72
+ elsif req.params.has_key?('rack-profiler')
73
+ Profiler.step('total_time') do
74
+ status, headers, body = @app.call(env)
75
+ end
76
+ [200, { 'Content-Type' => 'application/json' }, [{ events: nested_events, response: { status: status, headers: headers, body: body } }.to_json]]
77
+ else
78
+ @status, @headers, @body = @app.call(env)
79
+ end
80
+ end
81
+
82
+ def subscribe(event_name)
83
+ ActiveSupport::Notifications.subscribe(event_name) do |name, start, finish, id, payload|
84
+ backtrace = filtered_backtrace(tap_backtrace)
85
+ evt = {
86
+ id: id,
87
+ name: name,
88
+ start: start.to_f,
89
+ finish: finish.to_f,
90
+ duration: (finish - start) * 1000,
91
+ payload: payload,
92
+ backtrace: backtrace
93
+ }
94
+ (@events ||= []) << evt
95
+ end
96
+ end
97
+
98
+ def nested_events
99
+ events.sort_by { |evt| evt[:start] }.reduce([]) do |list, evt|
100
+ nest_event(list, list, evt)
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def subscribe_all_events
107
+ self.class.subscriptions.each do |event|
108
+ subscribe(event)
109
+ end
110
+ end
111
+
112
+ def nest_event(list, children, evt)
113
+ previous = children.last
114
+ if previous && evt[:finish] <= previous[:finish]
115
+ nest_event(list, previous[:children] ||= [], evt)
116
+ else
117
+ children << evt
118
+ end
119
+ list
120
+ end
121
+
122
+ def tap_backtrace
123
+ begin
124
+ raise DummyError.new
125
+ rescue DummyError => e
126
+ e.backtrace
127
+ end
128
+ end
129
+
130
+ def filtered_backtrace(backtrace)
131
+ if self.class.backtrace_filter.nil?
132
+ backtrace
133
+ else
134
+ backtrace.select(&self.class.backtrace_filter)
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,304 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <title>Rack::Profiler</title>
7
+ <meta name="description" content="">
8
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
9
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
10
+ <style type="text/css">
11
+ .stats details summary {
12
+ cursor: pointer;
13
+ outline: none;
14
+ }
15
+
16
+ .response-info {
17
+ margin-bottom: 1em;
18
+ }
19
+
20
+ .pr-steps td:first-child {
21
+ width: 800px;
22
+ }
23
+
24
+ .step-bar {
25
+ display: inline-block;
26
+ background: #5bc0de;
27
+ position: relative;
28
+ box-sizing: border-box;
29
+ margin-top: 0.2em;
30
+ border-radius: 2px;
31
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15)
32
+ }
33
+
34
+ .step-bar.step-type-sql {
35
+ background: #f0ad4e;
36
+ }
37
+
38
+ .step-bar.step-type-render-partial, .step-bar.step-type-render-template {
39
+ background: #5cb85c;
40
+ }
41
+
42
+ .step-bar.step-type-process-action {
43
+ background: #d9534f;
44
+ }
45
+
46
+ .key-value-list {
47
+ margin: 1em 0;
48
+ padding: 1em;
49
+ font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
50
+ font-size: 13px;
51
+ background-color: #f5f5f5;
52
+ border: 1px solid #ccc;
53
+ border-radius: 4px;
54
+ }
55
+
56
+ .key-value-list li {
57
+ list-style: none;
58
+ }
59
+
60
+ .pr-queries th {
61
+ white-space: nowrap;
62
+ }
63
+ .pr-queries pre {
64
+ max-width: 800px;
65
+ font-size: 12px;
66
+ }
67
+
68
+ </style>
69
+ </head>
70
+ <body>
71
+
72
+ <div class="container">
73
+
74
+ <h1 class="page-header">Rack::Profiler</h1>
75
+
76
+ <form class="pr-request-form">
77
+ <div class="row">
78
+ <div class="col-md-2">
79
+ <select id="req-method" class="form-control">
80
+ <option value="GET">GET</option>
81
+ <option value="POST">POST</option>
82
+ <option value="PUT">PUT</option>
83
+ <option value="PATCH">PATCH</option>
84
+ <option value="OPTIONS">OPTIONS</option>
85
+ </select>
86
+ </div>
87
+ <div class="col-md-3">
88
+ <input type="text" id="req-path" class="form-control" placeholder="Path">
89
+ </div>
90
+ <div class="col-md-6">
91
+ <input type="text" id="req-data" class="form-control" placeholder="Data">
92
+ </div>
93
+ <div class="col-md-1">
94
+ <button type="submit" class="btn btn-success">Profile</button>
95
+ </div>
96
+ </div>
97
+ </form>
98
+
99
+ <div class="stats" style="display:none;">
100
+
101
+ <hr>
102
+
103
+ <div id="response_panel" class="panel panel-default">
104
+ <div class="panel-heading">
105
+ <h3 class="panel-title">Response</h3>
106
+ </div>
107
+ <div id="response" class="panel-body"></div>
108
+ </div>
109
+
110
+ <div class="panel panel-default">
111
+ <div class="panel-heading">
112
+ <h3 class="panel-title">Steps</h3>
113
+ </div>
114
+ <div id="response" class="panel-body">
115
+ <table class="table pr-steps">
116
+ <thead>
117
+ <tr>
118
+ <th>Step</th>
119
+ <th>Duration</th>
120
+ <th>% of Total</th>
121
+ <th>From Start</th>
122
+ </tr>
123
+ </thead>
124
+ <tbody id="steps"></tbody>
125
+ </table>
126
+ </div>
127
+ </div>
128
+
129
+ <div class="panel panel-default">
130
+ <div class="panel-heading">
131
+ <h3 class="panel-title">SQL Queries <span id="query_count" class="badge"></span></h3>
132
+ </div>
133
+ <div id="response" class="panel-body">
134
+ <table class="table pr-queries">
135
+ <thead>
136
+ <tr>
137
+ <th>Query</th>
138
+ <th>Duration</th>
139
+ <th>% of Total</th>
140
+ <th>From Start</th>
141
+ </tr>
142
+ </thead>
143
+ <tbody id="queries"></tbody>
144
+ </table>
145
+ </div>
146
+ </div>
147
+ </div>
148
+
149
+ </div>
150
+
151
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
152
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.8.1/mustache.min.js"></script>
153
+ <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
154
+
155
+ <script type="text/template" id="response_tmpl">
156
+ <div class='response-info'>
157
+ <strong>Status:</strong> {{ status }}<br>
158
+ </div>
159
+ <div class='response-info'>
160
+ <strong>Headers:</strong>
161
+ <details>
162
+ <summary>Toggle</summary>
163
+ <ul class='key-value-list'>
164
+ {{ #headers }}
165
+ <li><strong>{{ key }}:</strong> {{ value }}</li>
166
+ {{ /headers }}
167
+ </ul>
168
+ </details>
169
+ </div>
170
+ <div class='response-info'>
171
+ <strong>Body:</strong>
172
+ <details><summary>Toggle</summary>
173
+ <pre>{{ body }}</pre></details>
174
+ </div>
175
+ </script>
176
+
177
+ <script type="text/template" id="step_tmpl">
178
+ <tr>
179
+ <td>
180
+ <details><summary>{{ name }}</summary>
181
+ <ul class='key-value-list'>
182
+ {{ #payload }}
183
+ {{ #value }}<li><strong>{{ key }}:</strong> {{ value }}</li>{{/value }}
184
+ {{ /payload }}
185
+ </ul>
186
+ </details>
187
+ <span class="step-name step-bar step-type-{{ type }}"
188
+ style="width: {{ percent }}%; left: {{ fromStartPercent }}%">
189
+ &nbsp;
190
+ </span>
191
+ </td>
192
+ <td>{{ duration }}ms</td>
193
+ <td>{{ percent }}%</td>
194
+ <td>+{{ fromStart }}ms</td>
195
+ </tr>
196
+ </script>
197
+
198
+ <script type="text/template" id="query_tmpl">
199
+ <tr>
200
+ <td>
201
+ <pre>{{ sql }}</pre>
202
+ <details><summary>Backtrace</summary>
203
+ <pre>{{ #backtrace }}{{.}}
204
+ {{ /backtrace }}</pre></details></td>
205
+ </td>
206
+ <td>{{ duration }}ms</td>
207
+ <td>{{ percent }}%</td>
208
+ <td>+{{ fromStart }}ms</td>
209
+ </tr>
210
+ </script>
211
+
212
+ <script type="text/javascript">
213
+ (function() {
214
+
215
+ var responseTmpl = $("#response_tmpl").html(),
216
+ queryTmpl = $("#query_tmpl").html(),
217
+ stepTmpl = $("#step_tmpl").html()
218
+
219
+ $(".pr-request-form").submit( function( evt ) {
220
+ evt.preventDefault()
221
+ $(".stats").hide()
222
+
223
+ var path = $("#req-path").val() || "/",
224
+ type = $("#req-method").val() || "GET",
225
+ data = $("#req-data").val()
226
+
227
+ var exposeKeyValues = function( obj ) {
228
+ return Object.keys( obj ).map( function( key ) {
229
+ return { key: key, value: JSON.stringify( obj[key], null, 1 ) }
230
+ })
231
+ }
232
+
233
+ $(".pr-queries tbody").html('')
234
+ $(".pr-steps tbody").html('')
235
+
236
+ $.ajax( path, { type: type, data: $.extend(data, { "rack-profiler": 1 }) } )
237
+ .done(function( data, status, xhr ) {
238
+ $(".stats").show()
239
+
240
+ var rootEvent = data.events[0],
241
+ queryCount = 0,
242
+ headersKeyVal = exposeKeyValues( data.response.headers ),
243
+ responseWrap = $.extend( {}, data.response, { headers: headersKeyVal } )
244
+
245
+ $('#response').html( Mustache.render( responseTmpl, responseWrap ) )
246
+ if (data.response.status < 400) {
247
+ $('#response_panel').removeClass('panel-danger').addClass('panel-success')
248
+ } else {
249
+ $('#response_panel').removeClass('panel-success').addClass('panel-danger')
250
+ }
251
+
252
+ var renderEvents = function( events ) {
253
+ events.map( function( event ) {
254
+ if ( event.name == "sql.active_record" ) {
255
+ var sqlEvent = {
256
+ sql: event.payload.sql,
257
+ duration: event.duration.toFixed(2),
258
+ percent: ( event.duration / rootEvent.duration * 100 ).toFixed(2),
259
+ fromStart: ( ( event.start - rootEvent.start ) * 1000 ).toFixed(2),
260
+ backtrace: event.backtrace
261
+ }
262
+ $(".pr-queries tbody").append( Mustache.render(queryTmpl, sqlEvent) )
263
+ queryCount++
264
+ }
265
+
266
+ var eventType = null
267
+ switch ( event.name ) {
268
+ case 'sql.active_record': eventType = 'sql'; break;
269
+ case 'render_partial.action_view': eventType = 'render-partial'; break;
270
+ case 'render_template.action_view': eventType = 'render-template'; break;
271
+ case 'process_action.action_controller': eventType = 'process-action'; break;
272
+ case 'rack-profiler.step': eventType = 'step'; break;
273
+ default: eventType = 'other'
274
+ }
275
+
276
+ var stepEvent = {
277
+ name: event.payload.step_name || event.name,
278
+ type: eventType,
279
+ duration: event.duration.toFixed(2),
280
+ percent: ( event.duration / rootEvent.duration * 100 ).toFixed(2),
281
+ fromStart: ( ( event.start - rootEvent.start ) * 1000 ).toFixed(2),
282
+ payload: exposeKeyValues( event.payload ),
283
+ fromStartPercent: ( ( event.start - rootEvent.start ) /
284
+ ( rootEvent.finish - rootEvent.start ) ) * 100
285
+ }
286
+ $(".pr-steps tbody").append( Mustache.render( stepTmpl, stepEvent ) )
287
+
288
+ // Recurse on children
289
+ if ( event.children ) {
290
+ renderEvents( event.children )
291
+ }
292
+ })
293
+ }
294
+
295
+ renderEvents( data.events )
296
+ $("#query_count").html( queryCount )
297
+ })
298
+ .fail(function( _, __, error ) { alert( "Request failed with: " + error ) })
299
+ })
300
+
301
+ })();
302
+ </script>
303
+ </body>
304
+ </html>
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rack/profiler/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rack-profiler"
8
+ spec.version = Rack::Profiler::VERSION
9
+ spec.authors = ["Luca Ongaro", "Luca Tironi"]
10
+ spec.email = ["lukeongaro@gmail.com"]
11
+ spec.summary = "A simple profiler for Rack applications"
12
+ spec.description = "A simple profiler for Rack applications, only depending on ActiveSupport::Notifications"
13
+ spec.homepage = "https://github.com/dawanda/rack-profiler"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "activesupport", ">= 3.0.0"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.7"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec"
26
+ end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rack::Profiler do
4
+ let(:profiler) { Rack::Profiler.new(nil) }
5
+
6
+ def trigger_dummy_event(name, payload = {})
7
+ ActiveSupport::Notifications.instrument(name, payload) do
8
+ "do stuff"
9
+ end
10
+ end
11
+
12
+ it 'has a version number' do
13
+ expect(Rack::Profiler::VERSION).not_to be nil
14
+ end
15
+
16
+ describe :nested_events do
17
+ it "returns events properly nested" do
18
+ evt1 = { start: 1, finish: 10 }
19
+ evt2 = { start: 2, finish: 3 }
20
+ evt3 = { start: 4, finish: 8 }
21
+ evt4 = { start: 5, finish: 8 }
22
+ evt5 = { start: 9, finish: 10 }
23
+ evts = [evt1, evt2, evt3, evt4, evt5]
24
+ allow(profiler).to receive(:events).and_return(evts)
25
+ nested = profiler.nested_events
26
+ expect(nested.first).to be(evt1)
27
+ expect(evt1[:children].first).to be(evt2)
28
+ expect(evt1[:children][1]).to be(evt3)
29
+ expect(evt3[:children].first).to be(evt4)
30
+ expect(evt1[:children].last).to be(evt5)
31
+ end
32
+ end
33
+
34
+ describe :subscribe do
35
+ before do
36
+ profiler.subscribe('foo')
37
+ end
38
+
39
+ it "subscribe to event populating the event list" do
40
+ trigger_dummy_event('foo')
41
+ event = profiler.events.last
42
+ expect(event[:name]).to eq('foo')
43
+ end
44
+
45
+ it "populate event with the proper keys" do
46
+ trigger_dummy_event('foo')
47
+ event = profiler.events.last
48
+ expect(event.keys).to include(:id, :name, :start, :finish, :duration, :payload, :backtrace)
49
+ end
50
+
51
+ it "adds the backtrace" do
52
+ trigger_dummy_event('foo')
53
+ event = profiler.events.last
54
+ expect(event[:backtrace].any? do |line|
55
+ line.include?("trigger_dummy_event")
56
+ end).to be(true)
57
+ end
58
+
59
+ it "filters the backtrace if a filter was provided" do
60
+ Rack::Profiler.filter_backtrace do |line|
61
+ !line.include?("trigger_dummy_event")
62
+ end
63
+ trigger_dummy_event('foo')
64
+ event = profiler.events.last
65
+ expect(event[:backtrace].any? do |line|
66
+ line.include?("trigger_dummy_event")
67
+ end).to be(false)
68
+ expect(event[:backtrace].any? do |line|
69
+ line.include?(__FILE__)
70
+ end).to be(true)
71
+ end
72
+
73
+ it "adds the payload" do
74
+ trigger_dummy_event('foo', bar: 'baz')
75
+ event = profiler.events.last
76
+ expect(event[:payload]).to eq(bar: 'baz')
77
+ end
78
+ end
79
+
80
+ describe ".subscribe" do
81
+ it "causes any newly created instance to subscribe to the given event" do
82
+ Rack::Profiler.subscribe('bar')
83
+ profiler = Rack::Profiler.new(nil)
84
+ trigger_dummy_event('bar')
85
+ event = profiler.events.last
86
+ expect(event[:name]).to eq('bar')
87
+ end
88
+ end
89
+
90
+ describe ".step" do
91
+ it "calls ActiveSupport::Notifications.instrument with the right arguments" do
92
+ expect(ActiveSupport::Notifications).to receive(:instrument).with(
93
+ 'rack-profiler.step', { step_name: 'xxx' })
94
+ Rack::Profiler.step('xxx') do
95
+ "do stuff"
96
+ end
97
+ end
98
+
99
+ it "mixes data in the payload if provided" do
100
+ expect(ActiveSupport::Notifications).to receive(:instrument).with(
101
+ 'rack-profiler.step', { step_name: 'xxx', foo: 'bar' })
102
+ Rack::Profiler.step('xxx', foo: 'bar') do
103
+ "do stuff"
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'rack/profiler'
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-profiler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Luca Ongaro
8
+ - Luca Tironi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-01-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 3.0.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 3.0.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: bundler
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.7'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.7'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '10.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '10.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rspec
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ description: A simple profiler for Rack applications, only depending on ActiveSupport::Notifications
71
+ email:
72
+ - lukeongaro@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".rspec"
79
+ - ".travis.yml"
80
+ - Gemfile
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - lib/rack/profiler.rb
85
+ - lib/rack/profiler/version.rb
86
+ - public/rack-profiler.html
87
+ - rack-profiler.gemspec
88
+ - spec/rack/profiler_spec.rb
89
+ - spec/spec_helper.rb
90
+ homepage: https://github.com/dawanda/rack-profiler
91
+ licenses:
92
+ - MIT
93
+ metadata: {}
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 2.2.2
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: A simple profiler for Rack applications
114
+ test_files:
115
+ - spec/rack/profiler_spec.rb
116
+ - spec/spec_helper.rb