rack-mini-profiler 0.9.9.2 → 0.10.1
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|