rack-mini-profiler 0.9.9.2 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -1
- data/README.md +11 -1
- data/lib/mini_profiler/client_settings.rb +59 -8
- data/lib/mini_profiler/config.rb +23 -8
- data/lib/mini_profiler/context.rb +2 -1
- data/lib/mini_profiler/profiler.rb +34 -42
- data/lib/mini_profiler/profiling_methods.rb +2 -2
- data/lib/mini_profiler/storage/abstract_store.rb +12 -0
- data/lib/mini_profiler/storage/file_store.rb +31 -0
- data/lib/mini_profiler/storage/memcache_store.rb +37 -0
- data/lib/mini_profiler/storage/memory_store.rb +24 -0
- data/lib/mini_profiler/storage/redis_store.rb +46 -0
- data/lib/mini_profiler/timer_struct/request.rb +2 -2
- data/lib/mini_profiler/timer_struct/sql.rb +18 -2
- data/lib/mini_profiler/version.rb +1 -1
- data/lib/patches/db/activerecord.rb +13 -4
- data/lib/patches/sql_patches.rb +17 -26
- data/lib/rack-mini-profiler.rb +1 -0
- data/rack-mini-profiler.gemspec +4 -6
- metadata +14 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba607e06976028497914287036a4b96012892cc5
|
4
|
+
data.tar.gz: b4427269223c942280e544847be135d1fdd51943
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32f96b3ba1bb3667417e0255130aee336780167f28b06209b026ae5ccbe87542e5c100bd3297f178ead24a90deb27b460cdf90f409b63da2ba705cc7bb2fabfa
|
7
|
+
data.tar.gz: c62db46c5de3423581d3f0f2ca466f338b4d390d6bdad9f3bc0e8cb7a3f210ef24295ff0dfcd85925608c6bc05f85b0f5abcccd7a17ae0e053bb658faafd01be
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -196,6 +196,15 @@ window.MiniProfiler.pageTransition();
|
|
196
196
|
|
197
197
|
This method will remove profiling information that was related to previous page and clear aggregate statistics.
|
198
198
|
|
199
|
+
#### MiniProfiler's speed badge on pages that are not generated via Rails
|
200
|
+
You need to inject the following in your SPA to load MiniProfiler's speed badge ([extra details surrounding this script](https://github.com/MiniProfiler/rack-mini-profiler/issues/139#issuecomment-192880706)):
|
201
|
+
|
202
|
+
```html
|
203
|
+
<script async type="text/javascript" id="mini-profiler" src="/mini-profiler-resources/includes.js?v=12b4b45a3c42e6e15503d7a03810ff33" data-version="12b4b45a3c42e6e15503d7a03810ff33" data-path="/mini-profiler-resources/" data-current-id="redo66j4g1077kto8uh3" data-ids="redo66j4g1077kto8uh3" data-position="left" data-trivial="false" data-children="false" data-max-traces="10" data-controls="false" data-authorized="true" data-toggle-shortcut="Alt+P" data-start-hidden="false" data-collapse-results="true"></script>
|
204
|
+
```
|
205
|
+
|
206
|
+
_Note:_ The GUID (`data-version` and the `?v=` parameter on the `src`) will change with each release of `rack_mini_profiler`. The MiniProfiler's speed badge will continue to work, although you will have to change the GUID to expire the script to fetch the most recent version.
|
207
|
+
|
199
208
|
### Configuration Options
|
200
209
|
|
201
210
|
You can set configuration options using the configuration accessor on `Rack::MiniProfiler`.
|
@@ -220,10 +229,11 @@ backtrace_remove|rails: `Rails.root`<br>Rack: `nil`|A string or regex to remove
|
|
220
229
|
toggle_shortcut|Alt+P|Keyboard shortcut to toggle the mini_profiler's visibility. See [jquery.hotkeys](https://github.com/jeresig/jquery.hotkeys).
|
221
230
|
start_hidden|`false`|`false` to make mini_profiler visible on page load.
|
222
231
|
backtrace_threshold_ms|`0`|Minimum SQL query elapsed time before a backtrace is recorded. Backtrace recording can take a couple of milliseconds on rubies earlier than 2.0, impacting performance for very small queries.
|
223
|
-
flamegraph_sample_rate|`0.
|
232
|
+
flamegraph_sample_rate|`0.5`|How often to capture stack traces for flamegraphs in milliseconds.
|
224
233
|
disable_env_dump|`false`|`true` disables `?pp=env`, which prevents sending ENV vars over HTTP.
|
225
234
|
base_url_path|`'/mini-profiler-resources/'`|Path for assets; added as a prefix when naming assets and sought when responding to requests.
|
226
235
|
collapse_results|`true`|If multiple timing results exist in a single page, collapse them till clicked.
|
236
|
+
max_traces_to_show|20|Maximum number of mini profiler timing blocks to show on one page
|
227
237
|
|
228
238
|
### Custom middleware ordering (required if using `Rack::Deflate` with Rails)
|
229
239
|
|
@@ -12,39 +12,90 @@ module Rack
|
|
12
12
|
attr_accessor :backtrace_level
|
13
13
|
|
14
14
|
|
15
|
-
def initialize(env)
|
15
|
+
def initialize(env, store, start)
|
16
16
|
request = ::Rack::Request.new(env)
|
17
17
|
@cookie = request.cookies[COOKIE_NAME]
|
18
|
+
@store = store
|
19
|
+
@start = start
|
20
|
+
|
21
|
+
@allowed_tokens, @orig_auth_tokens = nil
|
22
|
+
|
18
23
|
if @cookie
|
19
24
|
@cookie.split(",").map{|pair| pair.split("=")}.each do |k,v|
|
20
25
|
@orig_disable_profiling = @disable_profiling = (v=='t') if k == "dp"
|
21
26
|
@backtrace_level = v.to_i if k == "bt"
|
27
|
+
@orig_auth_tokens = v.to_s.split("|") if k == "a"
|
22
28
|
end
|
23
29
|
end
|
24
30
|
|
25
|
-
|
31
|
+
if !@backtrace_level.nil? && (@backtrace_level == 0 || @backtrace_level > BACKTRACE_NONE)
|
32
|
+
@backtrace_level = nil
|
33
|
+
end
|
34
|
+
|
26
35
|
@orig_backtrace_level = @backtrace_level
|
27
36
|
|
28
37
|
end
|
29
38
|
|
39
|
+
def handle_cookie(result)
|
40
|
+
status,headers,_body = result
|
41
|
+
|
42
|
+
if (MiniProfiler.config.authorization_mode == :whitelist && !MiniProfiler.request_authorized?)
|
43
|
+
# this is non-obvious, don't kill the profiling cookie on errors or short requests
|
44
|
+
# this ensures that stuff that never reaches the rails stack does not kill profiling
|
45
|
+
if status.to_i >= 200 && status.to_i < 300 && ((Time.now - @start) > 0.1)
|
46
|
+
discard_cookie!(headers)
|
47
|
+
end
|
48
|
+
else
|
49
|
+
write!(headers)
|
50
|
+
end
|
51
|
+
|
52
|
+
result
|
53
|
+
end
|
54
|
+
|
30
55
|
def write!(headers)
|
31
|
-
|
56
|
+
|
57
|
+
tokens_changed = false
|
58
|
+
|
59
|
+
if MiniProfiler.request_authorized? && MiniProfiler.config.authorization_mode == :whitelist
|
60
|
+
@allowed_tokens ||= @store.allowed_tokens
|
61
|
+
tokens_changed = !@orig_auth_tokens || ((@allowed_tokens - @orig_auth_tokens).length > 0)
|
62
|
+
end
|
63
|
+
|
64
|
+
if @orig_disable_profiling != @disable_profiling ||
|
65
|
+
@orig_backtrace_level != @backtrace_level ||
|
66
|
+
@cookie.nil? ||
|
67
|
+
tokens_changed
|
68
|
+
|
32
69
|
settings = {"p" => "t" }
|
33
|
-
settings["dp"] = "t"
|
34
|
-
settings["bt"] = @backtrace_level
|
70
|
+
settings["dp"] = "t" if @disable_profiling
|
71
|
+
settings["bt"] = @backtrace_level if @backtrace_level
|
72
|
+
settings["a"] = @allowed_tokens.join("|") if @allowed_tokens && MiniProfiler.request_authorized?
|
73
|
+
|
35
74
|
settings_string = settings.map{|k,v| "#{k}=#{v}"}.join(",")
|
36
75
|
Rack::Utils.set_cookie_header!(headers, COOKIE_NAME, :value => settings_string, :path => '/')
|
37
76
|
end
|
38
77
|
end
|
39
78
|
|
40
79
|
def discard_cookie!(headers)
|
41
|
-
|
80
|
+
if @cookie
|
81
|
+
Rack::Utils.delete_cookie_header!(headers, COOKIE_NAME, :path => '/')
|
82
|
+
end
|
42
83
|
end
|
43
84
|
|
44
|
-
def
|
45
|
-
!@cookie.nil?
|
85
|
+
def has_valid_cookie?
|
86
|
+
valid_cookie = !@cookie.nil?
|
87
|
+
|
88
|
+
if (MiniProfiler.config.authorization_mode == :whitelist)
|
89
|
+
@allowed_tokens ||= @store.allowed_tokens
|
90
|
+
|
91
|
+
valid_cookie = (Array === @orig_auth_tokens) &&
|
92
|
+
((@allowed_tokens & @orig_auth_tokens).length > 0)
|
93
|
+
end
|
94
|
+
|
95
|
+
valid_cookie
|
46
96
|
end
|
47
97
|
|
98
|
+
|
48
99
|
def disable_profiling?
|
49
100
|
@disable_profiling
|
50
101
|
end
|
data/lib/mini_profiler/config.rb
CHANGED
@@ -15,10 +15,15 @@ module Rack
|
|
15
15
|
attr_accessor :authorization_mode, :auto_inject, :backtrace_ignores,
|
16
16
|
:backtrace_includes, :backtrace_remove, :backtrace_threshold_ms,
|
17
17
|
:base_url_path, :disable_caching, :disable_env_dump, :enabled,
|
18
|
-
:flamegraph_sample_rate, :logger, :
|
19
|
-
:
|
20
|
-
:
|
21
|
-
|
18
|
+
:flamegraph_sample_rate, :logger, :pre_authorize_cb, :skip_paths,
|
19
|
+
:skip_schema_queries, :storage, :storage_failure, :storage_instance,
|
20
|
+
:storage_options, :user_provider
|
21
|
+
attr_accessor :skip_sql_param_names, :max_sql_param_length
|
22
|
+
|
23
|
+
# ui accessors
|
24
|
+
attr_accessor :collapse_results, :max_traces_to_show, :position,
|
25
|
+
:show_children, :show_controls, :show_trivial, :start_hidden,
|
26
|
+
:toggle_shortcut
|
22
27
|
|
23
28
|
# Deprecated options
|
24
29
|
attr_accessor :use_existing_jquery
|
@@ -32,13 +37,10 @@ module Rack
|
|
32
37
|
@pre_authorize_cb = lambda {|env| true}
|
33
38
|
|
34
39
|
# called after rack chain, to ensure we are REALLY allowed to profile
|
35
|
-
@position = 'left' # Where it is displayed
|
36
40
|
@skip_schema_queries = false
|
37
41
|
@storage = MiniProfiler::MemoryStore
|
38
42
|
@user_provider = Proc.new{|env| Rack::Request.new(env).ip}
|
39
43
|
@authorization_mode = :allow_all
|
40
|
-
@toggle_shortcut = 'Alt+P'
|
41
|
-
@start_hidden = false
|
42
44
|
@backtrace_threshold_ms = 0
|
43
45
|
@flamegraph_sample_rate = 0.5
|
44
46
|
@storage_failure = Proc.new do |exception|
|
@@ -48,7 +50,20 @@ module Rack
|
|
48
50
|
end
|
49
51
|
@enabled = true
|
50
52
|
@disable_env_dump = false
|
51
|
-
@
|
53
|
+
@max_sql_param_length = 0 # disable sql parameter collection by default
|
54
|
+
@skip_sql_param_names = /password/ # skips parameters with the name password by default
|
55
|
+
|
56
|
+
# ui parameters
|
57
|
+
@autorized = true
|
58
|
+
@collapse_results = true
|
59
|
+
@max_traces_to_show = 20
|
60
|
+
@position = 'left' # Where it is displayed
|
61
|
+
@show_children = false
|
62
|
+
@show_controls = false
|
63
|
+
@show_trivial = false
|
64
|
+
@start_hidden = false
|
65
|
+
@toggle_shortcut = 'Alt+P'
|
66
|
+
|
52
67
|
self
|
53
68
|
}
|
54
69
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
class Rack::MiniProfiler::Context
|
2
|
-
attr_accessor :inject_js,:current_timer,:page_struct,:skip_backtrace
|
2
|
+
attr_accessor :inject_js,:current_timer,:page_struct,:skip_backtrace,
|
3
|
+
:full_backtrace,:discard, :mpt_init, :measure
|
3
4
|
|
4
5
|
def initialize(opts = {})
|
5
6
|
opts["measure"] = true unless opts.key? "measure"
|
@@ -120,7 +120,8 @@ module Rack
|
|
120
120
|
end
|
121
121
|
|
122
122
|
def serve_html(env)
|
123
|
-
|
123
|
+
path = env['PATH_INFO'].sub('//', '/')
|
124
|
+
file_name = path.sub(@config.base_url_path, '')
|
124
125
|
|
125
126
|
return serve_results(env) if file_name.eql?('results')
|
126
127
|
|
@@ -148,11 +149,13 @@ module Rack
|
|
148
149
|
|
149
150
|
def call(env)
|
150
151
|
|
151
|
-
|
152
|
+
start = Time.now
|
153
|
+
client_settings = ClientSettings.new(env, @storage, start)
|
154
|
+
MiniProfiler.deauthorize_request if @config.authorization_mode == :whitelist
|
152
155
|
|
153
156
|
status = headers = body = nil
|
154
157
|
query_string = env['QUERY_STRING']
|
155
|
-
path = env['PATH_INFO']
|
158
|
+
path = env['PATH_INFO'].sub('//', '/')
|
156
159
|
|
157
160
|
# Someone (e.g. Rails engine) could change the SCRIPT_NAME so we save it
|
158
161
|
env['RACK_MINI_PROFILER_ORIGINAL_SCRIPT_NAME'] = env['SCRIPT_NAME']
|
@@ -161,18 +164,15 @@ module Rack
|
|
161
164
|
(@config.skip_paths && @config.skip_paths.any?{ |p| path.start_with?(p) }) ||
|
162
165
|
query_string =~ /pp=skip/
|
163
166
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
client_settings.write!(headers)
|
170
|
-
end
|
171
|
-
return [status,headers,body]
|
167
|
+
if skip_it || (
|
168
|
+
@config.authorization_mode == :whitelist &&
|
169
|
+
!client_settings.has_valid_cookie?
|
170
|
+
)
|
171
|
+
return client_settings.handle_cookie(@app.call(env))
|
172
172
|
end
|
173
173
|
|
174
174
|
# handle all /mini-profiler requests here
|
175
|
-
return serve_html(env) if path.start_with? @config.base_url_path
|
175
|
+
return client_settings.handle_cookie(serve_html(env)) if path.start_with? @config.base_url_path
|
176
176
|
|
177
177
|
has_disable_cookie = client_settings.disable_profiling?
|
178
178
|
# manual session disable / enable
|
@@ -180,7 +180,7 @@ module Rack
|
|
180
180
|
skip_it = true
|
181
181
|
end
|
182
182
|
|
183
|
-
if query_string =~ /pp=enable/
|
183
|
+
if query_string =~ /pp=enable/
|
184
184
|
skip_it = false
|
185
185
|
config.enabled = true
|
186
186
|
end
|
@@ -188,8 +188,7 @@ module Rack
|
|
188
188
|
if skip_it || !config.enabled
|
189
189
|
status,headers,body = @app.call(env)
|
190
190
|
client_settings.disable_profiling = true
|
191
|
-
client_settings.
|
192
|
-
return [status,headers,body]
|
191
|
+
return client_settings.handle_cookie([status,headers,body])
|
193
192
|
else
|
194
193
|
client_settings.disable_profiling = false
|
195
194
|
end
|
@@ -197,7 +196,7 @@ module Rack
|
|
197
196
|
# profile gc
|
198
197
|
if query_string =~ /pp=profile-gc/
|
199
198
|
current.measure = false if current
|
200
|
-
return Rack::MiniProfiler::GCProfiler.new.profile_gc(@app, env)
|
199
|
+
return client_settings.handle_cookie(Rack::MiniProfiler::GCProfiler.new.profile_gc(@app, env))
|
201
200
|
end
|
202
201
|
|
203
202
|
# profile memory
|
@@ -214,11 +213,10 @@ module Rack
|
|
214
213
|
body.close if body.respond_to? :close
|
215
214
|
end
|
216
215
|
report.pretty_print(result)
|
217
|
-
return text_result(result.string)
|
216
|
+
return client_settings.handle_cookie(text_result(result.string))
|
218
217
|
end
|
219
218
|
|
220
219
|
MiniProfiler.create_current(env, @config)
|
221
|
-
MiniProfiler.deauthorize_request if @config.authorization_mode == :whitelist
|
222
220
|
|
223
221
|
if query_string =~ /pp=normal-backtrace/
|
224
222
|
client_settings.backtrace_level = ClientSettings::BACKTRACE_DEFAULT
|
@@ -237,7 +235,6 @@ module Rack
|
|
237
235
|
trace_exceptions = query_string =~ /pp=trace-exceptions/ && defined? TracePoint
|
238
236
|
status, headers, body, exceptions,trace = nil
|
239
237
|
|
240
|
-
start = Time.now
|
241
238
|
|
242
239
|
if trace_exceptions
|
243
240
|
exceptions = []
|
@@ -280,7 +277,6 @@ module Rack
|
|
280
277
|
else
|
281
278
|
status,headers,body = @app.call(env)
|
282
279
|
end
|
283
|
-
client_settings.write!(headers)
|
284
280
|
ensure
|
285
281
|
trace.disable if trace
|
286
282
|
end
|
@@ -288,35 +284,30 @@ module Rack
|
|
288
284
|
skip_it = current.discard
|
289
285
|
|
290
286
|
if (config.authorization_mode == :whitelist && !MiniProfiler.request_authorized?)
|
291
|
-
# this is non-obvious, don't kill the profiling cookie on errors or short requests
|
292
|
-
# this ensures that stuff that never reaches the rails stack does not kill profiling
|
293
|
-
if status.to_i >= 200 && status.to_i < 300 && ((Time.now - start) > 0.1)
|
294
|
-
client_settings.discard_cookie!(headers)
|
295
|
-
end
|
296
287
|
skip_it = true
|
297
288
|
end
|
298
289
|
|
299
|
-
return [status,headers,body] if skip_it
|
290
|
+
return client_settings.handle_cookie([status,headers,body]) if skip_it
|
300
291
|
|
301
292
|
# we must do this here, otherwise current[:discard] is not being properly treated
|
302
293
|
if trace_exceptions
|
303
294
|
body.close if body.respond_to? :close
|
304
|
-
return dump_exceptions exceptions
|
295
|
+
return client_settings.handle_cookie(dump_exceptions exceptions)
|
305
296
|
end
|
306
297
|
|
307
298
|
if query_string =~ /pp=env/ && !config.disable_env_dump
|
308
299
|
body.close if body.respond_to? :close
|
309
|
-
return dump_env env
|
300
|
+
return client_settings.handle_cookie(dump_env env)
|
310
301
|
end
|
311
302
|
|
312
303
|
if query_string =~ /pp=analyze-memory/
|
313
304
|
body.close if body.respond_to? :close
|
314
|
-
return analyze_memory
|
305
|
+
return client_settings.handle_cookie(analyze_memory)
|
315
306
|
end
|
316
307
|
|
317
308
|
if query_string =~ /pp=help/
|
318
309
|
body.close if body.respond_to? :close
|
319
|
-
return help(client_settings, env)
|
310
|
+
return client_settings.handle_cookie(help(client_settings, env))
|
320
311
|
end
|
321
312
|
|
322
313
|
page_struct = current.page_struct
|
@@ -325,7 +316,7 @@ module Rack
|
|
325
316
|
|
326
317
|
if flamegraph
|
327
318
|
body.close if body.respond_to? :close
|
328
|
-
return self.flamegraph(flamegraph)
|
319
|
+
return client_settings.handle_cookie(self.flamegraph(flamegraph))
|
329
320
|
end
|
330
321
|
|
331
322
|
|
@@ -336,9 +327,8 @@ module Rack
|
|
336
327
|
|
337
328
|
# inject headers, script
|
338
329
|
if status >= 200 && status < 300
|
339
|
-
client_settings.write!(headers)
|
340
330
|
result = inject_profiler(env,status,headers,body)
|
341
|
-
return result if result
|
331
|
+
return client_settings.handle_cookie(result) if result
|
342
332
|
end
|
343
333
|
rescue Exception => e
|
344
334
|
if @config.storage_failure != nil
|
@@ -346,8 +336,7 @@ module Rack
|
|
346
336
|
end
|
347
337
|
end
|
348
338
|
|
349
|
-
client_settings.
|
350
|
-
[status, headers, body]
|
339
|
+
client_settings.handle_cookie([status, headers, body])
|
351
340
|
|
352
341
|
ensure
|
353
342
|
# Make sure this always happens
|
@@ -542,7 +531,6 @@ Append the following to your query string:
|
|
542
531
|
</html>
|
543
532
|
"
|
544
533
|
|
545
|
-
client_settings.write!(headers)
|
546
534
|
[200, headers, [body]]
|
547
535
|
end
|
548
536
|
|
@@ -552,8 +540,12 @@ Append the following to your query string:
|
|
552
540
|
end
|
553
541
|
|
554
542
|
def ids(env)
|
555
|
-
|
556
|
-
|
543
|
+
all = ([current.page_struct[:id]] + (@storage.get_unviewed_ids(user(env)) || [])).uniq
|
544
|
+
if all.size > @config.max_traces_to_show
|
545
|
+
all = all[0...@config.max_traces_to_show]
|
546
|
+
@storage.set_all_unviewed(user(env), all)
|
547
|
+
end
|
548
|
+
all
|
557
549
|
end
|
558
550
|
|
559
551
|
def ids_json(env)
|
@@ -587,10 +579,10 @@ Append the following to your query string:
|
|
587
579
|
:path => path,
|
588
580
|
:version => MiniProfiler::ASSET_VERSION,
|
589
581
|
:position => @config.position,
|
590
|
-
:showTrivial =>
|
591
|
-
:showChildren =>
|
592
|
-
:maxTracesToShow =>
|
593
|
-
:showControls =>
|
582
|
+
:showTrivial => @config.show_trivial,
|
583
|
+
:showChildren => @config.show_children,
|
584
|
+
:maxTracesToShow => @config.max_traces_to_show,
|
585
|
+
:showControls => @config.show_controls,
|
594
586
|
:authorized => true,
|
595
587
|
:toggleShortcut => @config.toggle_shortcut,
|
596
588
|
:startHidden => @config.start_hidden,
|
@@ -2,10 +2,10 @@ module Rack
|
|
2
2
|
class MiniProfiler
|
3
3
|
module ProfilingMethods
|
4
4
|
|
5
|
-
def record_sql(query, elapsed_ms)
|
5
|
+
def record_sql(query, elapsed_ms, params = nil)
|
6
6
|
return unless current && current.current_timer
|
7
7
|
c = current
|
8
|
-
c.current_timer.add_sql(query, elapsed_ms, c.page_struct, c.skip_backtrace, c.full_backtrace)
|
8
|
+
c.current_timer.add_sql(query, elapsed_ms, c.page_struct, params, c.skip_backtrace, c.full_backtrace)
|
9
9
|
end
|
10
10
|
|
11
11
|
def start_step(name)
|
@@ -2,6 +2,9 @@ module Rack
|
|
2
2
|
class MiniProfiler
|
3
3
|
class AbstractStore
|
4
4
|
|
5
|
+
# maximum age of allowed tokens before cycling in seconds
|
6
|
+
MAX_TOKEN_AGE = 1800
|
7
|
+
|
5
8
|
def save(page_struct)
|
6
9
|
raise NotImplementedError.new("save is not implemented")
|
7
10
|
end
|
@@ -18,6 +21,10 @@ module Rack
|
|
18
21
|
raise NotImplementedError.new("set_viewed is not implemented")
|
19
22
|
end
|
20
23
|
|
24
|
+
def set_all_unviewed(user, ids)
|
25
|
+
raise NotImplementedError.new("set_all_unviewed is not implemented")
|
26
|
+
end
|
27
|
+
|
21
28
|
def get_unviewed_ids(user)
|
22
29
|
raise NotImplementedError.new("get_unviewed_ids is not implemented")
|
23
30
|
end
|
@@ -27,6 +34,11 @@ module Rack
|
|
27
34
|
""
|
28
35
|
end
|
29
36
|
|
37
|
+
# a list of tokens that are permitted to access profiler in whitelist mode
|
38
|
+
def allowed_tokens
|
39
|
+
raise NotImplementedError.new("allowed_tokens is not implemented")
|
40
|
+
end
|
41
|
+
|
30
42
|
end
|
31
43
|
end
|
32
44
|
end
|
@@ -51,6 +51,9 @@ module Rack
|
|
51
51
|
@user_view_cache = FileCache.new(@path, "mp_views")
|
52
52
|
@user_view_lock = Mutex.new
|
53
53
|
|
54
|
+
@auth_token_cache = FileCache.new(@path, "tokens")
|
55
|
+
@auth_token_lock = Mutex.new
|
56
|
+
|
54
57
|
me = self
|
55
58
|
t = CacheCleanupThread.new do
|
56
59
|
interval = 10
|
@@ -114,12 +117,40 @@ module Rack
|
|
114
117
|
}
|
115
118
|
end
|
116
119
|
|
120
|
+
def set_all_unviewed(user, ids)
|
121
|
+
@user_view_lock.synchronize {
|
122
|
+
@user_view_cache[user] = ids.uniq
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
117
126
|
def get_unviewed_ids(user)
|
118
127
|
@user_view_lock.synchronize {
|
119
128
|
@user_view_cache[user]
|
120
129
|
}
|
121
130
|
end
|
122
131
|
|
132
|
+
def flush_tokens
|
133
|
+
@auth_token_lock.synchronize {
|
134
|
+
@auth_token_cache[""] = nil
|
135
|
+
}
|
136
|
+
end
|
137
|
+
|
138
|
+
def allowed_tokens
|
139
|
+
@auth_token_lock.synchronize {
|
140
|
+
token1, token2, cycle_at = @auth_token_cache[""]
|
141
|
+
|
142
|
+
unless cycle_at && (Time === cycle_at) && (cycle_at > Time.now)
|
143
|
+
token2 = token1
|
144
|
+
token1 = SecureRandom.hex
|
145
|
+
cycle_at = Time.now + Rack::MiniProfiler::AbstractStore::MAX_TOKEN_AGE
|
146
|
+
end
|
147
|
+
|
148
|
+
@auth_token_cache[""] = [token1, token2, cycle_at]
|
149
|
+
|
150
|
+
[token1, token2].compact
|
151
|
+
}
|
152
|
+
end
|
153
|
+
|
123
154
|
def cleanup_cache
|
124
155
|
files = Dir.entries(@path)
|
125
156
|
@timer_struct_lock.synchronize {
|
@@ -43,10 +43,47 @@ module Rack
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
+
def set_all_unviewed(user, ids)
|
47
|
+
@client.set("#{@prefix}-#{user}-v", ids, @expires_in_seconds)
|
48
|
+
end
|
49
|
+
|
46
50
|
def get_unviewed_ids(user)
|
47
51
|
@client.get("#{@prefix}-#{user}-v") || []
|
48
52
|
end
|
49
53
|
|
54
|
+
def flush_tokens
|
55
|
+
@client.set("#{@prefix}-tokens", nil)
|
56
|
+
end
|
57
|
+
|
58
|
+
def allowed_tokens
|
59
|
+
|
60
|
+
token_info = @client.get("#{@prefix}-tokens")
|
61
|
+
key1, key2, cycle_at = nil
|
62
|
+
|
63
|
+
|
64
|
+
if token_info
|
65
|
+
key1, key2, cycle_at = Marshal::load(token_info)
|
66
|
+
|
67
|
+
key1 = nil unless key1 && key1.length == 32
|
68
|
+
key2 = nil unless key2 && key2.length == 32
|
69
|
+
|
70
|
+
if key1 && cycle_at && (cycle_at > Time.now)
|
71
|
+
return [key1,key2].compact
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
timeout = Rack::MiniProfiler::AbstractStore::MAX_TOKEN_AGE
|
76
|
+
|
77
|
+
# cycle keys
|
78
|
+
key2 = key1
|
79
|
+
key1 = SecureRandom.hex
|
80
|
+
cycle_at = Time.now + timeout
|
81
|
+
|
82
|
+
@client.set("#{@prefix}-tokens", Marshal::dump([key1, key2, cycle_at]))
|
83
|
+
|
84
|
+
[key1, key2].compact
|
85
|
+
end
|
86
|
+
|
50
87
|
end
|
51
88
|
end
|
52
89
|
end
|
@@ -49,11 +49,15 @@ module Rack
|
|
49
49
|
def initialize(args = nil)
|
50
50
|
args ||= {}
|
51
51
|
@expires_in_seconds = args.fetch(:expires_in) { EXPIRES_IN_SECONDS }
|
52
|
+
|
53
|
+
@token1, @token2, @cycle_tokens_at = nil
|
54
|
+
|
52
55
|
initialize_locks
|
53
56
|
initialize_cleanup_thread(args)
|
54
57
|
end
|
55
58
|
|
56
59
|
def initialize_locks
|
60
|
+
@token_lock = Mutex.new
|
57
61
|
@timer_struct_lock = Mutex.new
|
58
62
|
@user_view_lock = Mutex.new
|
59
63
|
@timer_struct_cache = {}
|
@@ -98,6 +102,12 @@ module Rack
|
|
98
102
|
}
|
99
103
|
end
|
100
104
|
|
105
|
+
def set_all_unviewed(user, ids)
|
106
|
+
@user_view_lock.synchronize {
|
107
|
+
@user_view_cache[user] = ids
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
101
111
|
def get_unviewed_ids(user)
|
102
112
|
@user_view_lock.synchronize {
|
103
113
|
@user_view_cache[user]
|
@@ -110,6 +120,20 @@ module Rack
|
|
110
120
|
@timer_struct_cache.delete_if { |k, v| v[:started] < expire_older_than }
|
111
121
|
}
|
112
122
|
end
|
123
|
+
|
124
|
+
def allowed_tokens
|
125
|
+
@token_lock.synchronize do
|
126
|
+
|
127
|
+
unless @cycle_at && (@cycle_at > Time.now)
|
128
|
+
@token2 = @token1
|
129
|
+
@token1 = SecureRandom.hex
|
130
|
+
@cycle_at = Time.now + Rack::MiniProfiler::AbstractStore::MAX_TOKEN_AGE
|
131
|
+
end
|
132
|
+
|
133
|
+
[@token1, @token2].compact
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
113
137
|
end
|
114
138
|
end
|
115
139
|
end
|
@@ -2,6 +2,8 @@ module Rack
|
|
2
2
|
class MiniProfiler
|
3
3
|
class RedisStore < AbstractStore
|
4
4
|
|
5
|
+
attr_reader :prefix
|
6
|
+
|
5
7
|
EXPIRES_IN_SECONDS = 60 * 60 * 24
|
6
8
|
|
7
9
|
def initialize(args = nil)
|
@@ -33,6 +35,13 @@ module Rack
|
|
33
35
|
redis.expire key, @expires_in_seconds
|
34
36
|
end
|
35
37
|
|
38
|
+
def set_all_unviewed(user, ids)
|
39
|
+
key = "#{@prefix}-#{user}-v"
|
40
|
+
redis.del key
|
41
|
+
ids.each { |id| redis.sadd(key, id) }
|
42
|
+
redis.expire key, @expires_in_seconds
|
43
|
+
end
|
44
|
+
|
36
45
|
def set_viewed(user, id)
|
37
46
|
redis.srem "#{@prefix}-#{user}-v", id
|
38
47
|
end
|
@@ -48,6 +57,43 @@ unviewed_ids: #{get_unviewed_ids(user)}
|
|
48
57
|
"
|
49
58
|
end
|
50
59
|
|
60
|
+
def flush_tokens
|
61
|
+
redis.del("#{@prefix}-key1", "#{@prefix}-key1_old", "#{@prefix}-key2")
|
62
|
+
end
|
63
|
+
|
64
|
+
# Only used for testing
|
65
|
+
def simulate_expire
|
66
|
+
redis.del("#{@prefix}-key1")
|
67
|
+
end
|
68
|
+
|
69
|
+
def allowed_tokens
|
70
|
+
key1, key1_old, key2 = redis.mget("#{@prefix}-key1", "#{@prefix}-key1_old", "#{@prefix}-key2")
|
71
|
+
|
72
|
+
if key1 && (key1.length == 32)
|
73
|
+
return [key1, key2].compact
|
74
|
+
end
|
75
|
+
|
76
|
+
timeout = Rack::MiniProfiler::AbstractStore::MAX_TOKEN_AGE
|
77
|
+
|
78
|
+
# TODO this could be moved to lua to correct a concurrency flaw
|
79
|
+
# it is not critical cause worse case some requests will miss profiling info
|
80
|
+
|
81
|
+
# no key so go ahead and set it
|
82
|
+
key1 = SecureRandom.hex
|
83
|
+
|
84
|
+
if key1_old && (key1_old.length == 32)
|
85
|
+
key2 = key1_old
|
86
|
+
redis.setex "#{@prefix}-key2", timeout, key2
|
87
|
+
else
|
88
|
+
key2 = nil
|
89
|
+
end
|
90
|
+
|
91
|
+
redis.setex "#{@prefix}-key1", timeout, key1
|
92
|
+
redis.setex "#{@prefix}-key1_old", timeout*2, key1
|
93
|
+
|
94
|
+
[key1, key2].compact
|
95
|
+
end
|
96
|
+
|
51
97
|
private
|
52
98
|
|
53
99
|
def redis
|
@@ -89,8 +89,8 @@ module Rack
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
-
def add_sql(query, elapsed_ms, page, skip_backtrace = false, full_backtrace = false)
|
93
|
-
TimerStruct::Sql.new(query, elapsed_ms, page, self , skip_backtrace, full_backtrace).tap do |timer|
|
92
|
+
def add_sql(query, elapsed_ms, page, params = nil, skip_backtrace = false, full_backtrace = false)
|
93
|
+
TimerStruct::Sql.new(query, elapsed_ms, page, self, params, skip_backtrace, full_backtrace).tap do |timer|
|
94
94
|
self[:sql_timings].push(timer)
|
95
95
|
timer[:parent_timing_id] = self[:id]
|
96
96
|
self[:has_sql_timings] = true
|
@@ -4,7 +4,7 @@ module Rack
|
|
4
4
|
# Timing system for a SQL query
|
5
5
|
module TimerStruct
|
6
6
|
class Sql < TimerStruct::Base
|
7
|
-
def initialize(query, duration_ms, page, parent, skip_backtrace = false, full_backtrace = false)
|
7
|
+
def initialize(query, duration_ms, page, parent, params = nil, skip_backtrace = false, full_backtrace = false)
|
8
8
|
|
9
9
|
stack_trace = nil
|
10
10
|
unless skip_backtrace || duration_ms < Rack::MiniProfiler.config.backtrace_threshold_ms
|
@@ -39,7 +39,7 @@ module Rack
|
|
39
39
|
:start_milliseconds => start_millis,
|
40
40
|
:duration_milliseconds => duration_ms,
|
41
41
|
:first_fetch_duration_milliseconds => duration_ms,
|
42
|
-
:parameters =>
|
42
|
+
:parameters => trim_binds(params),
|
43
43
|
:parent_timing_id => nil,
|
44
44
|
:is_duplicate => false
|
45
45
|
)
|
@@ -53,6 +53,22 @@ module Rack
|
|
53
53
|
@page[:duration_milliseconds_in_sql] += elapsed_ms
|
54
54
|
end
|
55
55
|
|
56
|
+
def trim_binds(binds)
|
57
|
+
max_len = Rack::MiniProfiler.config.max_sql_param_length
|
58
|
+
return if binds.nil? || max_len == 0
|
59
|
+
return binds if max_len.nil?
|
60
|
+
binds.map do |(name, val)|
|
61
|
+
val ||= name
|
62
|
+
if val.nil? || val == true || val == false || val.kind_of?(Numeric)
|
63
|
+
# keep these parameters as is
|
64
|
+
elsif val.kind_of?(String)
|
65
|
+
val = val[0...max_len] if max_len
|
66
|
+
else
|
67
|
+
val = val.class.name
|
68
|
+
end
|
69
|
+
[name, val]
|
70
|
+
end
|
71
|
+
end
|
56
72
|
end
|
57
73
|
end
|
58
74
|
end
|
@@ -13,20 +13,29 @@ module Rack
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
def binds_to_params(binds)
|
17
|
+
return if binds.nil? || Rack::MiniProfiler.config.max_sql_param_length == 0
|
18
|
+
# map ActiveRecord::Relation::QueryAttribute to [name, value]
|
19
|
+
params = binds.map { |c| c.kind_of?(Array) ? [c.first, c.last] : [c.name, c.value] }
|
20
|
+
if (skip = Rack::MiniProfiler.config.skip_sql_param_names)
|
21
|
+
params.map { |(n,v)| n =~ skip ? [n, nil] : [n, v] }
|
22
|
+
else
|
23
|
+
params
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
16
27
|
def log_with_miniprofiler(*args, &block)
|
17
28
|
return log_without_miniprofiler(*args, &block) unless SqlPatches.should_measure?
|
18
29
|
|
19
|
-
sql, name,
|
30
|
+
sql, name, binds = args
|
20
31
|
start = Time.now
|
21
32
|
rval = log_without_miniprofiler(*args, &block)
|
22
33
|
|
23
34
|
# Don't log schema queries if the option is set
|
24
|
-
# return rval unless sql =~ /\"vms\"/
|
25
|
-
# return rval unless sql =~ /\"(vms|ext_management_systems)\"/
|
26
35
|
return rval if Rack::MiniProfiler.config.skip_schema_queries and name =~ /SCHEMA/
|
27
36
|
|
28
37
|
elapsed_time = SqlPatches.elapsed_time(start)
|
29
|
-
Rack::MiniProfiler.record_sql(sql, elapsed_time)
|
38
|
+
Rack::MiniProfiler.record_sql(sql, elapsed_time, binds_to_params(binds))
|
30
39
|
rval
|
31
40
|
end
|
32
41
|
end
|
data/lib/patches/sql_patches.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
class SqlPatches
|
2
|
+
def self.unpatched?
|
3
|
+
!patched?
|
4
|
+
end
|
2
5
|
|
3
6
|
def self.patched?
|
4
7
|
@patched
|
@@ -8,28 +11,16 @@ class SqlPatches
|
|
8
11
|
@patched = val
|
9
12
|
end
|
10
13
|
|
11
|
-
def self.class_exists?(name)
|
12
|
-
eval(name + ".class").to_s.eql?('Class')
|
13
|
-
rescue NameError
|
14
|
-
false
|
15
|
-
end
|
16
|
-
|
17
14
|
def self.correct_version?(required_version, klass)
|
18
15
|
Gem::Dependency.new('', required_version).match?('', klass::VERSION)
|
19
16
|
rescue NameError
|
20
17
|
false
|
21
18
|
end
|
22
19
|
|
23
|
-
def self.
|
24
|
-
eval(name + ".class").to_s.eql?('Module')
|
25
|
-
rescue NameError
|
26
|
-
false
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.record_sql(statement, &block)
|
20
|
+
def self.record_sql(statement, parameters = nil, &block)
|
30
21
|
start = Time.now
|
31
22
|
result = yield
|
32
|
-
record = ::Rack::MiniProfiler.record_sql(
|
23
|
+
record = ::Rack::MiniProfiler.record_sql(statement, elapsed_time(start), parameters)
|
33
24
|
return result, record
|
34
25
|
end
|
35
26
|
|
@@ -43,15 +34,15 @@ class SqlPatches
|
|
43
34
|
end
|
44
35
|
end
|
45
36
|
|
46
|
-
require 'patches/db/mysql2' if defined?(Mysql2::Client) &&
|
47
|
-
require 'patches/db/pg' if defined?(PG::Result) &&
|
48
|
-
require 'patches/db/oracle_enhanced' if defined?(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter) &&
|
49
|
-
require 'patches/db/mongo' if defined?(Mongo) &&
|
50
|
-
require 'patches/db/moped' if defined?(Moped::Node) &&
|
51
|
-
require 'patches/db/plucky' if defined?(Plucky::Query) &&
|
52
|
-
require 'patches/db/rsolr' if defined?(RSolr::Connection) &&
|
53
|
-
require 'patches/db/sequel' if defined?(Sequel::Database) &&
|
54
|
-
require 'patches/db/activerecord' if
|
55
|
-
require 'patches/db/nobrainer' if defined?(NoBrainer) &&
|
56
|
-
require 'patches/db/riak' if defined?(Riak) &&
|
57
|
-
require 'patches/db/neo4j' if defined?(Neo4j::Core) &&
|
37
|
+
require 'patches/db/mysql2' if defined?(Mysql2::Client) && Mysql2::Client.class == Class
|
38
|
+
require 'patches/db/pg' if defined?(PG::Result) && PG::Result.class == Class
|
39
|
+
require 'patches/db/oracle_enhanced' if defined?(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter) && ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class == Class && SqlPatches.correct_version?('~> 1.5.0', ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter)
|
40
|
+
require 'patches/db/mongo' if defined?(Mongo::Server::Connection) && Mongo.class == Module
|
41
|
+
require 'patches/db/moped' if defined?(Moped::Node) && Moped::Node.class == Class
|
42
|
+
require 'patches/db/plucky' if defined?(Plucky::Query) && Plucky::Query.class == Class
|
43
|
+
require 'patches/db/rsolr' if defined?(RSolr::Connection) && RSolr::Connection.class == Class && RSolr::VERSION[0] != "0"
|
44
|
+
require 'patches/db/sequel' if SqlPatches.unpatched? && defined?(Sequel::Database) && Sequel::Database.class == Class
|
45
|
+
require 'patches/db/activerecord' if SqlPatches.unpatched? && defined?(ActiveRecord) && ActiveRecord.class == Module
|
46
|
+
require 'patches/db/nobrainer' if defined?(NoBrainer) && NoBrainer.class == Module
|
47
|
+
require 'patches/db/riak' if defined?(Riak) && Riak.class == Module
|
48
|
+
require 'patches/db/neo4j' if defined?(Neo4j::Core) && Neo4j::Core::Query.class == Class
|
data/lib/rack-mini-profiler.rb
CHANGED
data/rack-mini-profiler.gemspec
CHANGED
@@ -19,21 +19,19 @@ Gem::Specification.new do |s|
|
|
19
19
|
"CHANGELOG.md"
|
20
20
|
]
|
21
21
|
s.add_runtime_dependency 'rack', '>= 1.2.0'
|
22
|
-
|
23
|
-
s.add_runtime_dependency 'json', '>= 1.6'
|
24
|
-
end
|
22
|
+
s.required_ruby_version = '>= 1.9.3'
|
25
23
|
|
26
24
|
s.add_development_dependency 'rake'
|
27
25
|
s.add_development_dependency 'rack-test'
|
28
26
|
s.add_development_dependency 'activerecord', '~> 3.0'
|
29
27
|
s.add_development_dependency 'dalli'
|
30
|
-
s.add_development_dependency 'rspec'
|
31
|
-
s.add_development_dependency 'ZenTest'
|
32
|
-
s.add_development_dependency 'autotest'
|
28
|
+
s.add_development_dependency 'rspec', '~> 2.14.1'
|
33
29
|
s.add_development_dependency 'redis'
|
34
30
|
s.add_development_dependency 'therubyracer'
|
35
31
|
s.add_development_dependency 'less'
|
36
32
|
s.add_development_dependency 'flamegraph'
|
33
|
+
s.add_development_dependency 'guard'
|
34
|
+
s.add_development_dependency 'guard-rspec'
|
37
35
|
|
38
36
|
s.require_paths = ["lib"]
|
39
37
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-mini-profiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2016-
|
13
|
+
date: 2016-05-18 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rack
|
@@ -86,18 +86,18 @@ dependencies:
|
|
86
86
|
name: rspec
|
87
87
|
requirement: !ruby/object:Gem::Requirement
|
88
88
|
requirements:
|
89
|
-
- - "
|
89
|
+
- - "~>"
|
90
90
|
- !ruby/object:Gem::Version
|
91
|
-
version:
|
91
|
+
version: 2.14.1
|
92
92
|
type: :development
|
93
93
|
prerelease: false
|
94
94
|
version_requirements: !ruby/object:Gem::Requirement
|
95
95
|
requirements:
|
96
|
-
- - "
|
96
|
+
- - "~>"
|
97
97
|
- !ruby/object:Gem::Version
|
98
|
-
version:
|
98
|
+
version: 2.14.1
|
99
99
|
- !ruby/object:Gem::Dependency
|
100
|
-
name:
|
100
|
+
name: redis
|
101
101
|
requirement: !ruby/object:Gem::Requirement
|
102
102
|
requirements:
|
103
103
|
- - ">="
|
@@ -111,7 +111,7 @@ dependencies:
|
|
111
111
|
- !ruby/object:Gem::Version
|
112
112
|
version: '0'
|
113
113
|
- !ruby/object:Gem::Dependency
|
114
|
-
name:
|
114
|
+
name: therubyracer
|
115
115
|
requirement: !ruby/object:Gem::Requirement
|
116
116
|
requirements:
|
117
117
|
- - ">="
|
@@ -125,7 +125,7 @@ dependencies:
|
|
125
125
|
- !ruby/object:Gem::Version
|
126
126
|
version: '0'
|
127
127
|
- !ruby/object:Gem::Dependency
|
128
|
-
name:
|
128
|
+
name: less
|
129
129
|
requirement: !ruby/object:Gem::Requirement
|
130
130
|
requirements:
|
131
131
|
- - ">="
|
@@ -139,7 +139,7 @@ dependencies:
|
|
139
139
|
- !ruby/object:Gem::Version
|
140
140
|
version: '0'
|
141
141
|
- !ruby/object:Gem::Dependency
|
142
|
-
name:
|
142
|
+
name: flamegraph
|
143
143
|
requirement: !ruby/object:Gem::Requirement
|
144
144
|
requirements:
|
145
145
|
- - ">="
|
@@ -153,7 +153,7 @@ dependencies:
|
|
153
153
|
- !ruby/object:Gem::Version
|
154
154
|
version: '0'
|
155
155
|
- !ruby/object:Gem::Dependency
|
156
|
-
name:
|
156
|
+
name: guard
|
157
157
|
requirement: !ruby/object:Gem::Requirement
|
158
158
|
requirements:
|
159
159
|
- - ">="
|
@@ -167,7 +167,7 @@ dependencies:
|
|
167
167
|
- !ruby/object:Gem::Version
|
168
168
|
version: '0'
|
169
169
|
- !ruby/object:Gem::Dependency
|
170
|
-
name:
|
170
|
+
name: guard-rspec
|
171
171
|
requirement: !ruby/object:Gem::Requirement
|
172
172
|
requirements:
|
173
173
|
- - ">="
|
@@ -250,7 +250,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
250
250
|
requirements:
|
251
251
|
- - ">="
|
252
252
|
- !ruby/object:Gem::Version
|
253
|
-
version:
|
253
|
+
version: 1.9.3
|
254
254
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
255
255
|
requirements:
|
256
256
|
- - ">="
|
@@ -258,7 +258,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
258
258
|
version: '0'
|
259
259
|
requirements: []
|
260
260
|
rubyforge_project:
|
261
|
-
rubygems_version: 2.
|
261
|
+
rubygems_version: 2.5.1
|
262
262
|
signing_key:
|
263
263
|
specification_version: 4
|
264
264
|
summary: Profiles loading speed for rack applications.
|