rack-profiler 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 10111d8a7faa32f82444b864868a3364a4c557b3
4
- data.tar.gz: b77adab1e71c584c5075da95b8eb60f737c84d79
3
+ metadata.gz: d84a8f5ef46d358a0da6c75940fdb9c5faf28d2c
4
+ data.tar.gz: 80a1d8da3c99c037717710177999f98a90d2e6d6
5
5
  SHA512:
6
- metadata.gz: 3306764da5aa9f681a1025ce5a0c43ea7cd08d210b8813e0a8e9ff7ab93ab7551ffdf11305479a28f7889e2641a0207170e32ed21e84b6c27117580529494514
7
- data.tar.gz: 0647b6ba8a86a09f3d45e2d71c3c71164d494637364cdfdb8af90865ab1a488affc1fe115c9d2530f39f18af3ae53e638bf9b15f3a3e950a594a9d4d8d290fde
6
+ metadata.gz: 875f0998cdf03527b766fa2b9d83fa946673683fdd4842e737fa69fe8c82fb5a60fcf8767fccd0b1249f5d9354206189c364b96015fde59f4a25dcc945f73da6
7
+ data.tar.gz: 3c71402f03f05b104d2d5a2294ff17e4533e24b9925a75e2bc436ac1704f4be750bc7bdb04b597a581de362ddc36c5044d1870f7d6ca56bddc22bdd0579334e9
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in rack-profiler.gemspec
4
4
  gemspec
5
+
6
+ gem 'codeclimate-test-reporter', group: :test, require: nil
data/README.md CHANGED
@@ -1,22 +1,34 @@
1
1
  # Rack::Profiler
2
2
 
3
+ [![Build Status](https://travis-ci.org/dawanda/rack-profiler.svg)](https://travis-ci.org/dawanda/rack-profiler) [![Code Climate](https://codeclimate.com/github/dawanda/rack-profiler/badges/gpa.svg)](https://codeclimate.com/github/dawanda/rack-profiler) [![Test Coverage](https://codeclimate.com/github/dawanda/rack-profiler/badges/coverage.svg)](https://codeclimate.com/github/dawanda/rack-profiler)
4
+
3
5
  Simple profiler for Rack applications (Sinatra and Ruby on Rails for example).
6
+ It helps providing an answer to common questions like:
7
+
8
+ - Where is time spent in requests to my app?
9
+ - Which SQL queries are executed by `ActiveRecord`?
10
+ - Which are the parts of my app's request flow that need optimization?
11
+
12
+ And more.
4
13
 
5
- `Rack::Profiler` uses the [Active Support Instrumentation API](http://guides.rubyonrails.org/active_support_instrumentation.html)
6
- and subscribes to the following hooks:
14
+ `Rack::Profiler` uses the [Active Support Instrumentation
15
+ API](http://guides.rubyonrails.org/active_support_instrumentation.html) and
16
+ subscribes by default to the following hooks:
7
17
 
8
18
  * [sql.active_record](http://guides.rubyonrails.org/active_support_instrumentation.html#sql-active-record)
9
19
  * [render_template.action_view](http://guides.rubyonrails.org/active_support_instrumentation.html#render_template.action_view)
10
20
  * [render_partial.action_view](http://guides.rubyonrails.org/active_support_instrumentation.html#render_partial.action_view)
11
21
  * [process_action.action_controller](http://guides.rubyonrails.org/active_support_instrumentation.html#process_action.action_controller)
12
22
 
13
- You can also define your own events, by wrapping your code with the
14
- [`Rack::Profiler.step`](#custom-steps).
23
+ On top of this, you can also define your own events, by wrapping your code with
24
+ the [`Rack::Profiler.step`](#custom-steps).
15
25
 
16
26
  `Rack::Profiler` is easy to integrate in any Rack application and it produces a
17
27
  JSON response with the results. It also exposes a simple web dashboard to directly
18
28
  issue HTTP requests to your application and see the results of the profiling.
19
29
 
30
+ ![Rack::Profiler Web Dashboard](http://i.imgur.com/tcUSYle.png?1)
31
+
20
32
  ## Installation
21
33
 
22
34
  Add this line to your application's Gemfile:
@@ -25,8 +37,6 @@ Add this line to your application's Gemfile:
25
37
  gem 'rack-profiler'
26
38
  ```
27
39
 
28
- *You might want to put the gem in the development group if you are using Rails.*
29
-
30
40
  And then execute:
31
41
 
32
42
  $ bundle
@@ -56,6 +66,7 @@ module YourApp
56
66
  class Application < Rails::Application
57
67
 
58
68
  # ...
69
+
59
70
  config.middleware.insert_before Rack::Runtime, Rack::Profiler
60
71
 
61
72
  end
@@ -64,28 +75,19 @@ end
64
75
 
65
76
  ## Configuration
66
77
 
67
- You can configure `Rack::Profiler` to subscribe to more notifications:
78
+ You can configure `Rack::Profiler` passing a block to `use`. In the block you can subscribe to more notifications and change some defaults:
68
79
 
69
80
  ```ruby
70
- Rack::Profiler.configure do |config|
81
+ use Rack::Profiler do |profiler|
71
82
  # Subscribe to email delivery in a Rails app
72
- config.subscribe('deliver.action_mailer')
73
- end
74
- ```
75
-
76
- You can also specify a backtrace filter to exclude lines that are not
77
- interesting:
83
+ profiler.subscribe('deliver.action_mailer')
78
84
 
79
- ```ruby
80
- Rack::Profiler.configure do |config|
81
- # Exclude gems from the backtrace
82
- config.filter_backtrace { |line| !line.include? '/gems/' }
85
+ # You can also exclude lines that are not interesting from the backtrace
86
+ # For example, exclude gems from the backtrace:
87
+ profiler.filter_backtrace { |line| !line.include? '/gems/' }
83
88
  end
84
89
  ```
85
90
 
86
- You can put these configurations in your `config.ru` for a Rack/Sinatra application
87
- or in an initializer `config/rack_profiler.rb` for Rails apps.
88
-
89
91
  ## Usage
90
92
 
91
93
  ### Custom steps
data/lib/rack/profiler.rb CHANGED
@@ -5,134 +5,107 @@ require "active_support/notifications"
5
5
 
6
6
  module Rack
7
7
  class Profiler
8
- class DummyError < StandardError; end
9
-
10
- module ClassMethods
11
- def configure(&block)
12
- block.call(config)
13
- end
14
8
 
15
- def config
16
- @config ||= Configuration.new
17
- end
9
+ attr_reader :events, :backtrace_filter, :subscriptions
10
+ attr_accessor :dashboard_path
18
11
 
19
- def step(name, payload = {})
20
- ActiveSupport::Notifications.instrument('rack-profiler.step', payload.merge(step_name: name)) do
21
- yield
22
- end
23
- end
24
- end
12
+ DEFAULT_SUBSCRIPTIONS = ['sql.active_record',
13
+ 'render_template.action_view',
14
+ 'render_partial.action_view',
15
+ 'process_action.action_controller',
16
+ 'rack-profiler.total_time',
17
+ 'rack-profiler.step']
25
18
 
26
- class Configuration
27
- attr_reader :subscriptions, :backtrace_filter
28
- attr_accessor :dashboard_path
29
-
30
- DEFAULT_SUBSCRIPTIONS = ['sql.active_record',
31
- 'render_template.action_view',
32
- 'render_partial.action_view',
33
- 'process_action.action_controller',
34
- 'rack-profiler.total_time',
35
- 'rack-profiler.step']
36
-
37
- def initialize
38
- @subscriptions = DEFAULT_SUBSCRIPTIONS.clone
39
- @dashboard_path = '/rack-profiler'
40
- end
41
-
42
- def subscribe(*names)
43
- names.each { |name| @subscriptions << name }
44
- @subscriptions.uniq!
45
- end
19
+ class DummyError < StandardError; end
46
20
 
47
- def filter_backtrace(&block)
48
- @backtrace_filter = block
21
+ def self.step(name, payload = {})
22
+ ActiveSupport::Notifications.instrument('rack-profiler.step', payload.merge(step_name: name)) do
23
+ yield
49
24
  end
50
25
  end
51
26
 
52
- extend ClassMethods
53
-
54
- attr_reader :events
55
-
56
- def initialize(app)
57
- subscribe_to_all
58
- @events = []
59
- @app = app
27
+ def initialize(app, &block)
28
+ @events = []
29
+ @subscriptions = []
30
+ @dashboard_path = '/rack-profiler'
31
+ @app = app
32
+ subscribe_to_default
33
+ block.call(self) if block_given?
60
34
  end
61
35
 
62
36
  def call(env)
63
37
  @events = []
64
- status, headers, body = [nil, nil, nil]
65
38
  req = Rack::Request.new(env)
66
39
 
67
- if req.path == config.dashboard_path
40
+ if req.path == dashboard_path
68
41
  render_dashboard
69
42
  elsif req.params.has_key?('rack-profiler')
70
- ActiveSupport::Notifications.instrument('rack-profiler.total_time') do
71
- status, headers, body = @app.call(env)
72
- end
73
- [ 200,
74
- { 'Content-Type' => 'application/json' },
75
- [ { events: events.sort_by { |event| event[:start] },
76
- response: {
77
- status: status,
78
- headers: headers,
79
- body: stringify_body(body)
80
- }
81
- }.to_json ]
82
- ]
43
+ render_profiler_results(env)
83
44
  else
84
- @status, @headers, @body = @app.call(env)
45
+ @app.call(env)
85
46
  end
86
47
  end
87
48
 
88
- def subscribe(event_name)
89
- ActiveSupport::Notifications.subscribe(event_name) do |name, start, finish, id, payload|
90
- backtrace = filtered_backtrace(tap_backtrace)
91
- evt = {
92
- id: id,
93
- name: name,
94
- start: start.to_f,
95
- finish: finish.to_f,
96
- duration: (finish - start) * 1000,
97
- payload: payload,
98
- backtrace: backtrace
99
- }
100
- (@events ||= []) << evt
49
+ def subscribe(*events)
50
+ events.each do |event_name|
51
+ next if @subscriptions.include?(event_name)
52
+ ActiveSupport::Notifications.subscribe(event_name) do |name, start, finish, id, payload|
53
+ evt = {
54
+ id: id,
55
+ name: name,
56
+ start: start.to_f,
57
+ finish: finish.to_f,
58
+ duration: (finish - start) * 1000,
59
+ payload: payload,
60
+ backtrace: filtered_backtrace(caller(1))
61
+ }
62
+ (@events ||= []) << evt
63
+ end
64
+ @subscriptions << event_name
101
65
  end
102
66
  end
103
67
 
104
- def config
105
- self.class.config
68
+ def filter_backtrace(&block)
69
+ @backtrace_filter = block
106
70
  end
107
71
 
108
72
  private
109
73
 
74
+ def render_profiler_results(env)
75
+ status, headers, body = [nil, nil, nil]
76
+ ActiveSupport::Notifications.instrument('rack-profiler.total_time') do
77
+ status, headers, body = @app.call(env)
78
+ end
79
+ [ 200,
80
+ { 'Content-Type' => 'application/json' },
81
+ [ { events: events.sort_by { |event| event[:start] },
82
+ response: {
83
+ status: status,
84
+ headers: headers,
85
+ body: stringify_body(body)
86
+ }
87
+ }.to_json ]
88
+ ]
89
+ end
90
+
110
91
  def render_dashboard
111
- dashboard = ::File.expand_path( '../../public/rack-profiler.html', ::File.dirname( __FILE__ ) )
92
+ dashboard = ::File.expand_path( '../../public/rack-profiler.html',
93
+ ::File.dirname( __FILE__ ) )
112
94
  body = ::File.open(dashboard, ::File::RDONLY)
113
95
  [200, { 'Content-Type' => 'text/html' }, body]
114
96
  end
115
97
 
116
- def subscribe_to_all
117
- # Subscribe to interesting events
118
- config.subscriptions.each do |event|
98
+ def subscribe_to_default
99
+ DEFAULT_SUBSCRIPTIONS.each do |event|
119
100
  subscribe(event)
120
101
  end
121
102
  end
122
103
 
123
- def tap_backtrace
124
- begin
125
- raise DummyError.new
126
- rescue DummyError => e
127
- e.backtrace
128
- end
129
- end
130
-
131
104
  def filtered_backtrace(backtrace)
132
- if config.backtrace_filter.nil?
105
+ if backtrace_filter.nil?
133
106
  backtrace
134
107
  else
135
- backtrace.select(&config.backtrace_filter)
108
+ backtrace.select(&backtrace_filter)
136
109
  end
137
110
  end
138
111
 
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class Profiler
3
- VERSION = "0.0.4"
3
+ VERSION = "0.0.5"
4
4
  end
5
5
  end
@@ -96,7 +96,7 @@
96
96
  <input type="text" id="req-path" class="form-control" placeholder="Path">
97
97
  </div>
98
98
  <div class="col-md-6">
99
- <input type="text" id="req-data" class="form-control" placeholder="Data">
99
+ <input type="text" id="req-data" class="form-control" placeholder="Data (as query string/form data)">
100
100
  </div>
101
101
  <div class="col-md-1">
102
102
  <button type="submit" class="btn btn-success">Profile</button>
@@ -241,7 +241,7 @@
241
241
  $(".pr-queries tbody").html('')
242
242
  $(".pr-steps tbody").html('')
243
243
 
244
- $.ajax( path, { type: type, data: $.extend(data, { "rack-profiler": 1 }) } )
244
+ $.ajax( path, { type: type, data: ["rack-profiler", data].join("&") } )
245
245
  .done(function( data, status, xhr ) {
246
246
  $(".stats").show()
247
247
 
@@ -16,14 +16,28 @@ describe Rack::Profiler do
16
16
 
17
17
  describe :initialize do
18
18
  it "subscribes to all subscriptions" do
19
- config = Rack::Profiler::Configuration.new
20
- config.subscribe("bar")
21
- allow(Rack::Profiler).to receive(:config).and_return(config)
22
-
23
- profiler = Rack::Profiler.new(nil)
19
+ profiler = Rack::Profiler.new(nil) do |config|
20
+ config.subscribe("bar")
21
+ end
24
22
  trigger_dummy_event("bar")
25
23
  expect(profiler.events.last[:name]).to eq("bar")
26
24
  end
25
+
26
+ it "executes the given block passing self" do
27
+ block_arg = nil
28
+ profiler = Rack::Profiler.new(nil) do |p|
29
+ block_arg = p
30
+ end
31
+ expect(block_arg).to be(profiler)
32
+ end
33
+
34
+ it "sets the correct defaults" do
35
+ expect(profiler.dashboard_path).to eq('/rack-profiler')
36
+ expect(profiler.backtrace_filter).to be_nil
37
+ expect(profiler.subscriptions).to include(
38
+ *Rack::Profiler::DEFAULT_SUBSCRIPTIONS
39
+ )
40
+ end
27
41
  end
28
42
 
29
43
  describe :subscribe do
@@ -52,7 +66,7 @@ describe Rack::Profiler do
52
66
  end
53
67
 
54
68
  it "filters the backtrace if a filter was provided" do
55
- Rack::Profiler.config.filter_backtrace do |line|
69
+ profiler.filter_backtrace do |line|
56
70
  !line.include?("trigger_dummy_event")
57
71
  end
58
72
  trigger_dummy_event("foo")
@@ -70,6 +84,23 @@ describe Rack::Profiler do
70
84
  event = profiler.events.last
71
85
  expect(event[:payload]).to eq(bar: "baz")
72
86
  end
87
+
88
+ it "accepts more than one subscription" do
89
+ profiler.subscribe('baz', 'qux')
90
+ trigger_dummy_event('baz')
91
+ event = profiler.events.last
92
+ expect(event[:name]).to eq('baz')
93
+ trigger_dummy_event('qux')
94
+ event = profiler.events.last
95
+ expect(event[:name]).to eq('qux')
96
+ end
97
+
98
+ it "prevents duplicate subscriptions" do
99
+ profiler.subscribe('baz')
100
+ profiler.subscribe('baz')
101
+ trigger_dummy_event('baz')
102
+ expect(profiler.events.size).to eq(1)
103
+ end
73
104
  end
74
105
 
75
106
  describe :call do
@@ -117,11 +148,14 @@ describe Rack::Profiler do
117
148
 
118
149
  context "when the path is config.dashboard_path" do
119
150
  it "renders dashboard" do
120
- expect(profiler).to receive(:render_dashboard)
121
- profiler.call env.merge(
122
- "PATH_INFO" => profiler.config.dashboard_path,
123
- "REQUEST_PATH" => profiler.config.dashboard_path
151
+ path = ::File.expand_path('../../public/rack-profiler.html',
152
+ ::File.dirname( __FILE__ ) )
153
+ status, header, body = profiler.call env.merge(
154
+ "PATH_INFO" => profiler.dashboard_path,
155
+ "REQUEST_PATH" => profiler.dashboard_path
124
156
  )
157
+ expect(body).to be_a(File)
158
+ expect(body.path).to eq(path)
125
159
  end
126
160
  end
127
161
 
@@ -178,63 +212,12 @@ describe Rack::Profiler do
178
212
  "do stuff"
179
213
  end
180
214
  end
181
- end
182
-
183
- describe ".configure" do
184
- it "executes the given block passing the configuration object" do
185
- config_inside_block = nil
186
- Rack::Profiler.configure do |c|
187
- config_inside_block = c
188
- end
189
- expect(config_inside_block).to be(Rack::Profiler.config)
190
- end
191
- end
192
-
193
- describe ".config" do
194
- it "instantiates a Configuration object if there is none" do
195
- Rack::Profiler.send(:instance_variable_set, :@config, nil)
196
- expect(Rack::Profiler.config).to be_a(Rack::Profiler::Configuration)
197
- end
198
- end
199
- end
200
-
201
- describe Rack::Profiler::Configuration do
202
- let(:config) { Rack::Profiler::Configuration.new }
203
-
204
- it "has the correct defaults" do
205
- expect(config.dashboard_path).to eq('/rack-profiler')
206
- expect(config.backtrace_filter).to be_nil
207
- expect(config.subscriptions).to include(
208
- *Rack::Profiler::Configuration::DEFAULT_SUBSCRIPTIONS
209
- )
210
- end
211
-
212
- describe :subscribe do
213
- it "adds entries to subscriptions" do
214
- config.subscribe('bar')
215
- expect(config.subscriptions).to include('bar')
216
- end
217
-
218
- it "accepts more than one subscription" do
219
- config.subscribe('bar', 'baz')
220
- expect(config.subscriptions).to include('bar', 'baz')
221
- end
222
215
 
223
- it "does not add duplicates" do
224
- config.subscribe('bar', 'bar')
225
- expect(
226
- config.subscriptions.count { |s| s == 'bar' }
227
- ).to eq(1)
228
- end
229
- end
230
-
231
- describe :filter_backtrace do
232
- it "sets the backtrace_filter" do
233
- config.filter_backtrace do |line|
234
- line.include? 'foo'
216
+ it "returns whatever is returned by the block" do
217
+ returned = Rack::Profiler.step("xxx") do
218
+ "do stuff"
235
219
  end
236
- filtered = ['foo', 'foobar', 'baz'].select(&config.backtrace_filter)
237
- expect(filtered).to eq(['foo', 'foobar'])
220
+ expect(returned).to eq("do stuff")
238
221
  end
239
222
  end
240
223
  end
data/spec/spec_helper.rb CHANGED
@@ -1,2 +1,5 @@
1
+ require 'codeclimate-test-reporter'
2
+ CodeClimate::TestReporter.start
3
+
1
4
  $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
5
  require 'rack/profiler'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-profiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Ongaro
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-01-27 00:00:00.000000000 Z
12
+ date: 2015-01-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack