browser-prof 1.2.3
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.
- data/.gitignore +2 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +41 -0
- data/Rakefile +8 -0
- data/browser-prof.gemspec +30 -0
- data/init.rb +7 -0
- data/lib/browser-prof.rb +37 -0
- data/lib/ruby-prof/graph_html_printer_enhanced.rb +123 -0
- data/lib/ruby-prof/template.rhtml +156 -0
- data/pkg/browser_profiler-1.2.0.gem +0 -0
- data/tasks/github-gem.rake +365 -0
- data/test/functional/slow_controller_test.rb +34 -0
- data/test/test_helper.rb +34 -0
- metadata +129 -0
data/.gitignore
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2007 Revolution Health Group LLC. All rights reserved.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
= Browser-prof
|
2
|
+
|
3
|
+
This is a simple tool to profile single web requests in Rails using the power or ruby-prof.
|
4
|
+
This gem is a based on the discontinued Browser Profiler (http://rubyforge.org/projects/browser-prof).
|
5
|
+
|
6
|
+
* Easy walking through complex changes
|
7
|
+
* Low overhead in development mode
|
8
|
+
* MIT licensed
|
9
|
+
|
10
|
+
== Installation & basic usage
|
11
|
+
|
12
|
+
Installing browser-prof as a Ruby gem is currently broken, so you need to use it as a plugin:
|
13
|
+
|
14
|
+
$ ./script/plugin install http://github.com/barttenbrinke/browser-prof.git
|
15
|
+
|
16
|
+
== Profiling
|
17
|
+
|
18
|
+
Add ruby-prof to your Gemfile, run bundle install and start op your server environment.
|
19
|
+
To profile, just open any url:
|
20
|
+
|
21
|
+
$ http://0.0.0.0:3000/some/action?browser_profile!
|
22
|
+
|
23
|
+
On the botton of your page a html ruby-prof trace will be appended.
|
24
|
+
Please note that the action will take lot longer, as it will be passed through ruby-prof.
|
25
|
+
|
26
|
+
If you want to profile PDF, Json or post actions, just run the following command:
|
27
|
+
$ http://0.0.0.0:3000/some/action.js?file_profile!
|
28
|
+
|
29
|
+
This will write the results to /log/. You can take a look at this through any browser.
|
30
|
+
$ open log/profile_out.html
|
31
|
+
|
32
|
+
== Additional information
|
33
|
+
|
34
|
+
Browser-profile was originally designed and built by Aaron Batalion (aaron@hungrymachine.com)
|
35
|
+
and is now maintained by Bart ten Brinke.
|
36
|
+
|
37
|
+
Do you have a rails application that is not performing as it should? If you need
|
38
|
+
an expert to analyze your application, feel free to contact either Willem van
|
39
|
+
Bergen (willem@railsdoctors.com) or Bart ten Brinke (bart@railsdoctors.com).
|
40
|
+
|
41
|
+
* railsdoctors homepage: http://railsdoctors.com
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "browser-prof"
|
3
|
+
|
4
|
+
# Do not set the version and date field manually, this is done by the release script
|
5
|
+
s.version = "1.2.3"
|
6
|
+
s.date = "2011-02-17"
|
7
|
+
|
8
|
+
s.summary = "Rails tool to to profile single requests using ruby-prof."
|
9
|
+
s.description = <<-eos
|
10
|
+
This is a simple tool to profile single web requests in Rails using the power or ruby-prof.
|
11
|
+
eos
|
12
|
+
|
13
|
+
s.rdoc_options << '--title' << s.name << '--main' << 'README.rdoc' << '--line-numbers' << '--inline-source'
|
14
|
+
s.extra_rdoc_files = ['README.rdoc']
|
15
|
+
|
16
|
+
s.requirements << "ruby-prof gem is required."
|
17
|
+
|
18
|
+
s.add_development_dependency('rake')
|
19
|
+
s.add_development_dependency('rspec', '~> 2.0')
|
20
|
+
s.add_development_dependency('ruby-prof')
|
21
|
+
|
22
|
+
s.authors = ['Willem van Bergen', 'Bart ten Brinke']
|
23
|
+
s.email = ['willem@railsdoctors.com', 'bart@railsdoctors.com']
|
24
|
+
s.homepage = 'http://railsdoctors.com'
|
25
|
+
|
26
|
+
# The files and test_files directives are set automatically by the release script.
|
27
|
+
# Do not change them by hand, but make sure to add the files to the git repository.
|
28
|
+
s.files = %w(.gitignore MIT-LICENSE README.rdoc Rakefile browser-prof.gemspec init.rb lib/browser-prof.rb lib/ruby-prof/graph_html_printer_enhanced.rb lib/ruby-prof/template.rhtml pkg/browser_profiler-1.2.0.gem tasks/github-gem.rake test/functional/slow_controller_test.rb test/test_helper.rb)
|
29
|
+
s.test_files = %w(test/functional/slow_controller_test.rb)
|
30
|
+
end
|
data/init.rb
ADDED
data/lib/browser-prof.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module ActionController
|
2
|
+
class Base
|
3
|
+
def process_with_browser_profiling(request, response, method = :perform_action, *arguments)
|
4
|
+
browser_output = request.parameters.key?('browser_profile!') || request.parameters[:params].key?('browser_profile!') || ENV["BROWSER_PROFILE"]
|
5
|
+
file_output = request.parameters.key?('file_profile') || request.parameters[:params].key?('file_profile!') || ENV["FILE_PROFILE"]
|
6
|
+
|
7
|
+
if (browser_output or file_output)
|
8
|
+
# Only require these files in needed
|
9
|
+
require 'ruby-prof'
|
10
|
+
require 'ruby-prof/graph_html_printer_enhanced'
|
11
|
+
|
12
|
+
#run the process through a profile block
|
13
|
+
profile_results = RubyProf.profile {
|
14
|
+
response = process_without_browser_profiling(request,response, method, *arguments);
|
15
|
+
}
|
16
|
+
|
17
|
+
#Use the enhanced html printer to get better results
|
18
|
+
printer = RubyProf::GraphHtmlPrinterEnhanced.new(profile_results)
|
19
|
+
|
20
|
+
#determine output location
|
21
|
+
if file_output
|
22
|
+
printer.print(File.new("#{RAILS_ROOT}/log/profile_out.html","w"))
|
23
|
+
else
|
24
|
+
response.body << printer.print("",0)
|
25
|
+
end
|
26
|
+
|
27
|
+
#reset the content length so the profiling data is included in the response
|
28
|
+
response.send("set_content_length!")
|
29
|
+
|
30
|
+
response
|
31
|
+
else
|
32
|
+
process_without_browser_profiling(request, response, method, *arguments)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
alias_method_chain :process, :browser_profiling
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# This is a modified version from the rubyprof project rubyprof.rubyforge.org
|
2
|
+
require 'ruby-prof/abstract_printer'
|
3
|
+
require "erb"
|
4
|
+
|
5
|
+
module RubyProf
|
6
|
+
# Generates graph[link:files/examples/graph_html.html] profile reports as html.
|
7
|
+
# To use the grap html printer:
|
8
|
+
#
|
9
|
+
# result = RubyProf.profile do
|
10
|
+
# [code to profile]
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# printer = RubyProf::GraphHtmlPrinter.new(result, 5)
|
14
|
+
# printer.print(STDOUT, 0)
|
15
|
+
#
|
16
|
+
# The constructor takes two arguments. The first is
|
17
|
+
# a RubyProf::Result object generated from a profiling
|
18
|
+
# run. The second is the minimum %total (the methods
|
19
|
+
# total time divided by the overall total time) that
|
20
|
+
# a method must take for it to be printed out in
|
21
|
+
# the report. Use this parameter to eliminate methods
|
22
|
+
# that are not important to the overall profiling results.
|
23
|
+
#
|
24
|
+
# This is mostly from ruby_forge, with some optimization changes.
|
25
|
+
|
26
|
+
class GraphHtmlPrinterEnhanced < AbstractPrinter
|
27
|
+
include ERB::Util
|
28
|
+
|
29
|
+
MIN_TIME = 0.01
|
30
|
+
MIN_THREAD_TIME = 0.0
|
31
|
+
PERCENTAGE_WIDTH = 8
|
32
|
+
TIME_WIDTH = 10
|
33
|
+
CALL_WIDTH = 20
|
34
|
+
|
35
|
+
# Create a GraphPrinter. Result is a RubyProf::Result
|
36
|
+
# object generated from a profiling run.
|
37
|
+
def initialize(result)
|
38
|
+
super(result)
|
39
|
+
@thread_times = Hash.new
|
40
|
+
calculate_thread_times
|
41
|
+
end
|
42
|
+
|
43
|
+
# Print a graph html report to the provided output.
|
44
|
+
#
|
45
|
+
# output - Any IO oject, including STDOUT or a file.
|
46
|
+
# The default value is STDOUT.
|
47
|
+
#
|
48
|
+
# options - Hash of print options. See #setup_options
|
49
|
+
# for more information.
|
50
|
+
#
|
51
|
+
def print(output = STDOUT, options = {})
|
52
|
+
@output = output
|
53
|
+
setup_options(options)
|
54
|
+
|
55
|
+
_erbout = @output
|
56
|
+
erb = ERB.new(template, nil, nil)
|
57
|
+
@output << erb.result(binding)
|
58
|
+
end
|
59
|
+
|
60
|
+
# These methods should be private but then ERB doesn't
|
61
|
+
# work. Turn off RDOC though
|
62
|
+
#--
|
63
|
+
def calculate_thread_times
|
64
|
+
# Cache thread times since this is an expensive
|
65
|
+
# operation with the required sorting
|
66
|
+
@result.threads.each do |thread_id, methods|
|
67
|
+
top = methods.sort.last
|
68
|
+
|
69
|
+
thread_time = 0.01
|
70
|
+
thread_time = top.total_time if top.total_time > 0
|
71
|
+
|
72
|
+
@thread_times[thread_id] = thread_time
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def thread_time(thread_id)
|
77
|
+
@thread_times[thread_id]
|
78
|
+
end
|
79
|
+
|
80
|
+
def select_methods(methods)
|
81
|
+
return [] unless methods
|
82
|
+
methods.select {|method| method.total_time >= MIN_TIME }
|
83
|
+
end
|
84
|
+
|
85
|
+
def select_threads(threads)
|
86
|
+
threads.select {|thread_id, methods| thread_time(thread_id) >= MIN_THREAD_TIME }
|
87
|
+
end
|
88
|
+
|
89
|
+
def total_percent(thread_id, method)
|
90
|
+
overall_time = self.thread_time(thread_id)
|
91
|
+
(method.total_time/overall_time) * 100
|
92
|
+
end
|
93
|
+
|
94
|
+
def self_percent(method)
|
95
|
+
overall_time = self.thread_time(method.thread_id)
|
96
|
+
(method.self_time/overall_time) * 100
|
97
|
+
end
|
98
|
+
|
99
|
+
# Creates a link to a method. Note that we do not create
|
100
|
+
# links to methods which are under the min_perecent
|
101
|
+
# specified by the user, since they will not be
|
102
|
+
# printed out.
|
103
|
+
def create_link(thread_id, method)
|
104
|
+
if self.total_percent(thread_id, method) < min_percent
|
105
|
+
# Just return name
|
106
|
+
h method.full_name
|
107
|
+
else
|
108
|
+
href = '#' + method_href(thread_id, method)
|
109
|
+
"<a href=\"#{href}\">#{h method.full_name}</a>"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def method_href(thread_id, method)
|
114
|
+
h(method.full_name.gsub(/[><#\.\?=:]/,"_") + "_" + thread_id.to_s)
|
115
|
+
end
|
116
|
+
|
117
|
+
def template
|
118
|
+
return IO.read(File.dirname(__FILE__) + "/template.rhtml")
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
@@ -0,0 +1,156 @@
|
|
1
|
+
<style media="all" type="text/css">
|
2
|
+
table {
|
3
|
+
border-collapse: collapse;
|
4
|
+
border: 1px solid #CCC;
|
5
|
+
font-family: Verdana, Arial, Helvetica, sans-serif;
|
6
|
+
font-size: 9pt;
|
7
|
+
line-height: normal;
|
8
|
+
width: 900px;
|
9
|
+
}
|
10
|
+
|
11
|
+
th {
|
12
|
+
text-align: center;
|
13
|
+
border-top: 1px solid #FB7A31;
|
14
|
+
border-bottom: 1px solid #FB7A31;
|
15
|
+
background: #FFC;
|
16
|
+
padding: 0.3em;
|
17
|
+
border-left: 1px solid silver;
|
18
|
+
}
|
19
|
+
|
20
|
+
tr.break td {
|
21
|
+
border: 0;
|
22
|
+
border-top: 1px solid #FB7A31;
|
23
|
+
padding: 0;
|
24
|
+
margin: 0;
|
25
|
+
}
|
26
|
+
|
27
|
+
tr.method td {
|
28
|
+
font-weight: bold;
|
29
|
+
}
|
30
|
+
td.method_name {
|
31
|
+
width: 200px;
|
32
|
+
}
|
33
|
+
|
34
|
+
td {
|
35
|
+
padding: 0.3em;
|
36
|
+
}
|
37
|
+
|
38
|
+
td:first-child {
|
39
|
+
width: 190px;
|
40
|
+
}
|
41
|
+
|
42
|
+
td {
|
43
|
+
border-left: 1px solid #CCC;
|
44
|
+
text-align: center;
|
45
|
+
}
|
46
|
+
#browser_profile {
|
47
|
+
width: 920px;
|
48
|
+
margin: 0 auto;
|
49
|
+
}
|
50
|
+
|
51
|
+
</style>
|
52
|
+
<div id="browser_profile">
|
53
|
+
<h1>Profile Report</h1>
|
54
|
+
<br/><br/>
|
55
|
+
<table>
|
56
|
+
<tr><th>Column</th><th>Description</th></tr>
|
57
|
+
<tr><td>%self</td><td>The percentage of time spent in this method, derived from self_time/total_time</td></tr>
|
58
|
+
<tr><td>cumulative</td><td>The sum of the time spent in this method and all the methods listed above it.</td></tr>
|
59
|
+
<tr><td>total</td><td>The time spent in this method and its children.</td></tr>
|
60
|
+
<tr><td>self</td><td>The time spent in this method.</td></tr>
|
61
|
+
<tr><td>children</td><td>The time spent in this method's children.</td></tr>
|
62
|
+
<tr><td>calls</td><td>The number of times this method was called.</td></tr>
|
63
|
+
<tr><td>self/call</td><td>The average time spent per call in this method.</td></tr>
|
64
|
+
<tr><td>total/call</td><td>The average time spent per call in this method and its children.</td></tr>
|
65
|
+
<tr><td>name</td><td>The name of the method.</td></tr>
|
66
|
+
</table>
|
67
|
+
<br/><br/>
|
68
|
+
<!-- Threads Table -->
|
69
|
+
<table>
|
70
|
+
<tr>
|
71
|
+
<th>Thread ID</th>
|
72
|
+
<th>Total Time</th>
|
73
|
+
</tr>
|
74
|
+
<% for thread_id, methods in @result.threads %>
|
75
|
+
<tr>
|
76
|
+
<td><a href="#<%= thread_id %>"><%= thread_id %></a></td>
|
77
|
+
<td><%= thread_time(thread_id) %></td>
|
78
|
+
</tr>
|
79
|
+
<% end %>
|
80
|
+
</table>
|
81
|
+
|
82
|
+
<!-- Methods Tables -->
|
83
|
+
<% for thread_id, methods in @result.threads
|
84
|
+
total_time = thread_time(thread_id) %>
|
85
|
+
<h2><a name="<%= thread_id %>">Thread <%= thread_id %></a></h2>
|
86
|
+
|
87
|
+
<table>
|
88
|
+
<tr>
|
89
|
+
<th><%= sprintf("%#{PERCENTAGE_WIDTH}s", "%Total") %></th>
|
90
|
+
<th><%= sprintf("%#{PERCENTAGE_WIDTH}s", "%Self") %></th>
|
91
|
+
<th><%= sprintf("%#{TIME_WIDTH}s", "Total") %></th>
|
92
|
+
<th><%= sprintf("%#{TIME_WIDTH}s", "Self") %></th>
|
93
|
+
<th><%= sprintf("%#{TIME_WIDTH}s", "Wait") %></th>
|
94
|
+
<th><%= sprintf("%#{TIME_WIDTH+2}s", "Child") %></th>
|
95
|
+
<th><%= sprintf("%#{CALL_WIDTH}s", "Calls") %></th>
|
96
|
+
<th>Name</th>
|
97
|
+
<th>Line</th>
|
98
|
+
</tr>
|
99
|
+
|
100
|
+
<% select_methods(methods).sort.reverse_each do |method|
|
101
|
+
total_percentage = (method.total_time/total_time) * 100
|
102
|
+
next if total_percentage < min_percent
|
103
|
+
self_percentage = (method.self_time/total_time) * 100
|
104
|
+
%>
|
105
|
+
|
106
|
+
<!-- Parents -->
|
107
|
+
<% for caller in select_methods(method.aggregate_parents) %>
|
108
|
+
<tr>
|
109
|
+
<td> </td>
|
110
|
+
<td> </td>
|
111
|
+
<td><%= sprintf("%#{TIME_WIDTH}.2f", caller.total_time) %></td>
|
112
|
+
<td><%= sprintf("%#{TIME_WIDTH}.2f", caller.self_time) %></td>
|
113
|
+
<td><%= sprintf("%#{TIME_WIDTH}.2f", caller.wait_time) %></td>
|
114
|
+
<td><%= sprintf("%#{TIME_WIDTH}.2f", caller.children_time) %></td>
|
115
|
+
<% called = "#{caller.called}/#{method.called}" %>
|
116
|
+
<td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
|
117
|
+
<td><%= create_link(thread_id, caller.target) %></td>
|
118
|
+
<td><a href="file://<%= File.expand_path(caller.target.source_file) %>#line=<%= caller.line %>"><%= caller.line %></a></td>
|
119
|
+
</tr>
|
120
|
+
<% end %>
|
121
|
+
|
122
|
+
<tr class="method">
|
123
|
+
<td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", total_percentage) %></td>
|
124
|
+
<td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", self_percentage) %></td>
|
125
|
+
<td><%= sprintf("%#{TIME_WIDTH}.2f", method.total_time) %></td>
|
126
|
+
<td><%= sprintf("%#{TIME_WIDTH}.2f", method.self_time) %></td>
|
127
|
+
<td><%= sprintf("%#{TIME_WIDTH}.2f", method.wait_time) %></td>
|
128
|
+
<td><%= sprintf("%#{TIME_WIDTH}.2f", method.children_time) %></td>
|
129
|
+
<td><%= sprintf("%#{CALL_WIDTH}i", method.called) %></td>
|
130
|
+
<td><a name="<%= method_href(thread_id, method) %>"><%= h method.full_name %></a></td>
|
131
|
+
<td><a href="file://<%= File.expand_path(method.source_file) %>#line=<%= method.line %>"><%= method.line %></a></td>
|
132
|
+
</tr>
|
133
|
+
|
134
|
+
<!-- Children -->
|
135
|
+
<% for callee in select_methods(method.children) %>
|
136
|
+
<tr>
|
137
|
+
<td> </td>
|
138
|
+
<td> </td>
|
139
|
+
<td><%= sprintf("%#{TIME_WIDTH}.2f", callee.total_time) %></td>
|
140
|
+
<td><%= sprintf("%#{TIME_WIDTH}.2f", callee.self_time) %></td>
|
141
|
+
<td><%= sprintf("%#{TIME_WIDTH}.2f", callee.wait_time) %></td>
|
142
|
+
<td><%= sprintf("%#{TIME_WIDTH}.2f", callee.children_time) %></td>
|
143
|
+
<% called = "#{callee.called}/#{callee.target.called}" %>
|
144
|
+
<td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
|
145
|
+
<td><%= create_link(thread_id, callee.target) %></td>
|
146
|
+
<td><a href="file://<%= File.expand_path(method.source_file) %>#line=<%= callee.line %>"><%= callee.line %></a></td>
|
147
|
+
</tr>
|
148
|
+
<% end %>
|
149
|
+
<!-- Create divider row -->
|
150
|
+
<tr class="break"><td colspan="8"></td></tr>
|
151
|
+
<% end %>
|
152
|
+
</table>
|
153
|
+
|
154
|
+
<% end %>
|
155
|
+
</div>
|
156
|
+
|
Binary file
|
@@ -0,0 +1,365 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/tasklib'
|
4
|
+
require 'date'
|
5
|
+
require 'set'
|
6
|
+
|
7
|
+
module GithubGem
|
8
|
+
|
9
|
+
# Detects the gemspc file of this project using heuristics.
|
10
|
+
def self.detect_gemspec_file
|
11
|
+
FileList['*.gemspec'].first
|
12
|
+
end
|
13
|
+
|
14
|
+
# Detects the main include file of this project using heuristics
|
15
|
+
def self.detect_main_include
|
16
|
+
if detect_gemspec_file =~ /^(\.*)\.gemspec$/ && File.exist?("lib/#{$1}.rb")
|
17
|
+
"lib/#{$1}.rb"
|
18
|
+
elsif FileList['lib/*.rb'].length == 1
|
19
|
+
FileList['lib/*.rb'].first
|
20
|
+
else
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class RakeTasks
|
26
|
+
|
27
|
+
attr_reader :gemspec, :modified_files
|
28
|
+
attr_accessor :gemspec_file, :task_namespace, :main_include, :root_dir, :spec_pattern, :test_pattern, :remote, :remote_branch, :local_branch
|
29
|
+
|
30
|
+
# Initializes the settings, yields itself for configuration
|
31
|
+
# and defines the rake tasks based on the gemspec file.
|
32
|
+
def initialize(task_namespace = :gem)
|
33
|
+
@gemspec_file = GithubGem.detect_gemspec_file
|
34
|
+
@task_namespace = task_namespace
|
35
|
+
@main_include = GithubGem.detect_main_include
|
36
|
+
@modified_files = Set.new
|
37
|
+
@root_dir = Dir.pwd
|
38
|
+
@test_pattern = 'test/**/*_test.rb'
|
39
|
+
@spec_pattern = 'spec/**/*_spec.rb'
|
40
|
+
@local_branch = 'master'
|
41
|
+
@remote = 'origin'
|
42
|
+
@remote_branch = 'master'
|
43
|
+
|
44
|
+
yield(self) if block_given?
|
45
|
+
|
46
|
+
load_gemspec!
|
47
|
+
define_tasks!
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
|
52
|
+
def git
|
53
|
+
@git ||= ENV['GIT'] || 'git'
|
54
|
+
end
|
55
|
+
|
56
|
+
# Define Unit test tasks
|
57
|
+
def define_test_tasks!
|
58
|
+
require 'rake/testtask'
|
59
|
+
|
60
|
+
namespace(:test) do
|
61
|
+
Rake::TestTask.new(:basic) do |t|
|
62
|
+
t.pattern = test_pattern
|
63
|
+
t.verbose = true
|
64
|
+
t.libs << 'test'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
desc "Run all unit tests for #{gemspec.name}"
|
69
|
+
task(:test => ['test:basic'])
|
70
|
+
end
|
71
|
+
|
72
|
+
# Defines RSpec tasks
|
73
|
+
def define_rspec_tasks!
|
74
|
+
require 'rspec/core/rake_task'
|
75
|
+
|
76
|
+
namespace(:spec) do
|
77
|
+
desc "Verify all RSpec examples for #{gemspec.name}"
|
78
|
+
RSpec::Core::RakeTask.new(:basic) do |t|
|
79
|
+
t.pattern = spec_pattern
|
80
|
+
end
|
81
|
+
|
82
|
+
desc "Verify all RSpec examples for #{gemspec.name} and output specdoc"
|
83
|
+
RSpec::Core::RakeTask.new(:specdoc) do |t|
|
84
|
+
t.pattern = spec_pattern
|
85
|
+
t.rspec_opts = ['--format', 'documentation', '--color']
|
86
|
+
end
|
87
|
+
|
88
|
+
desc "Run RCov on specs for #{gemspec.name}"
|
89
|
+
RSpec::Core::RakeTask.new(:rcov) do |t|
|
90
|
+
t.pattern = spec_pattern
|
91
|
+
t.rcov = true
|
92
|
+
t.rcov_opts = ['--exclude', '"spec/*,gems/*"', '--rails']
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
desc "Verify all RSpec examples for #{gemspec.name} and output specdoc"
|
97
|
+
task(:spec => ['spec:specdoc'])
|
98
|
+
end
|
99
|
+
|
100
|
+
# Defines the rake tasks
|
101
|
+
def define_tasks!
|
102
|
+
|
103
|
+
define_test_tasks! if has_tests?
|
104
|
+
define_rspec_tasks! if has_specs?
|
105
|
+
|
106
|
+
namespace(@task_namespace) do
|
107
|
+
desc "Updates the filelist in the gemspec file"
|
108
|
+
task(:manifest) { manifest_task }
|
109
|
+
|
110
|
+
desc "Builds the .gem package"
|
111
|
+
task(:build => :manifest) { build_task }
|
112
|
+
|
113
|
+
desc "Sets the version of the gem in the gemspec"
|
114
|
+
task(:set_version => [:check_version, :check_current_branch]) { version_task }
|
115
|
+
task(:check_version => :fetch_origin) { check_version_task }
|
116
|
+
|
117
|
+
task(:fetch_origin) { fetch_origin_task }
|
118
|
+
task(:check_current_branch) { check_current_branch_task }
|
119
|
+
task(:check_clean_status) { check_clean_status_task }
|
120
|
+
task(:check_not_diverged => :fetch_origin) { check_not_diverged_task }
|
121
|
+
|
122
|
+
checks = [:check_current_branch, :check_clean_status, :check_not_diverged, :check_version]
|
123
|
+
checks.unshift('spec:basic') if has_specs?
|
124
|
+
checks.unshift('test:basic') if has_tests?
|
125
|
+
# checks.push << [:check_rubyforge] if gemspec.rubyforge_project
|
126
|
+
|
127
|
+
desc "Perform all checks that would occur before a release"
|
128
|
+
task(:release_checks => checks)
|
129
|
+
|
130
|
+
release_tasks = [:release_checks, :set_version, :build, :github_release, :gemcutter_release]
|
131
|
+
# release_tasks << [:rubyforge_release] if gemspec.rubyforge_project
|
132
|
+
|
133
|
+
desc "Release a new version of the gem using the VERSION environment variable"
|
134
|
+
task(:release => release_tasks) { release_task }
|
135
|
+
|
136
|
+
namespace(:release) do
|
137
|
+
desc "Release the next version of the gem, by incrementing the last version segment by 1"
|
138
|
+
task(:next => [:next_version] + release_tasks) { release_task }
|
139
|
+
|
140
|
+
desc "Release the next version of the gem, using a patch increment (0.0.1)"
|
141
|
+
task(:patch => [:next_patch_version] + release_tasks) { release_task }
|
142
|
+
|
143
|
+
desc "Release the next version of the gem, using a minor increment (0.1.0)"
|
144
|
+
task(:minor => [:next_minor_version] + release_tasks) { release_task }
|
145
|
+
|
146
|
+
desc "Release the next version of the gem, using a major increment (1.0.0)"
|
147
|
+
task(:major => [:next_major_version] + release_tasks) { release_task }
|
148
|
+
end
|
149
|
+
|
150
|
+
# task(:check_rubyforge) { check_rubyforge_task }
|
151
|
+
# task(:rubyforge_release) { rubyforge_release_task }
|
152
|
+
task(:gemcutter_release) { gemcutter_release_task }
|
153
|
+
task(:github_release => [:commit_modified_files, :tag_version]) { github_release_task }
|
154
|
+
task(:tag_version) { tag_version_task }
|
155
|
+
task(:commit_modified_files) { commit_modified_files_task }
|
156
|
+
|
157
|
+
task(:next_version) { next_version_task }
|
158
|
+
task(:next_patch_version) { next_version_task(:patch) }
|
159
|
+
task(:next_minor_version) { next_version_task(:minor) }
|
160
|
+
task(:next_major_version) { next_version_task(:major) }
|
161
|
+
|
162
|
+
desc "Updates the gem release tasks with the latest version on Github"
|
163
|
+
task(:update_tasks) { update_tasks_task }
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Updates the files list and test_files list in the gemspec file using the list of files
|
168
|
+
# in the repository and the spec/test file pattern.
|
169
|
+
def manifest_task
|
170
|
+
# Load all the gem's files using "git ls-files"
|
171
|
+
repository_files = `#{git} ls-files`.split("\n")
|
172
|
+
test_files = Dir[test_pattern] + Dir[spec_pattern]
|
173
|
+
|
174
|
+
update_gemspec(:files, repository_files)
|
175
|
+
update_gemspec(:test_files, repository_files & test_files)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Builds the gem
|
179
|
+
def build_task
|
180
|
+
sh "gem build -q #{gemspec_file}"
|
181
|
+
Dir.mkdir('pkg') unless File.exist?('pkg')
|
182
|
+
sh "mv #{gemspec.name}-#{gemspec.version}.gem pkg/#{gemspec.name}-#{gemspec.version}.gem"
|
183
|
+
end
|
184
|
+
|
185
|
+
def newest_version
|
186
|
+
`#{git} tag`.split("\n").map { |tag| tag.split('-').last }.compact.map { |v| Gem::Version.new(v) }.max || Gem::Version.new('0.0.0')
|
187
|
+
end
|
188
|
+
|
189
|
+
def next_version(increment = nil)
|
190
|
+
next_version = newest_version.segments
|
191
|
+
increment_index = case increment
|
192
|
+
when :micro then 3
|
193
|
+
when :patch then 2
|
194
|
+
when :minor then 1
|
195
|
+
when :major then 0
|
196
|
+
else next_version.length - 1
|
197
|
+
end
|
198
|
+
|
199
|
+
next_version[increment_index] ||= 0
|
200
|
+
next_version[increment_index] = next_version[increment_index].succ
|
201
|
+
((increment_index + 1)...next_version.length).each { |i| next_version[i] = 0 }
|
202
|
+
|
203
|
+
Gem::Version.new(next_version.join('.'))
|
204
|
+
end
|
205
|
+
|
206
|
+
def next_version_task(increment = nil)
|
207
|
+
ENV['VERSION'] = next_version(increment).version
|
208
|
+
puts "Releasing version #{ENV['VERSION']}..."
|
209
|
+
end
|
210
|
+
|
211
|
+
# Updates the version number in the gemspec file, the VERSION constant in the main
|
212
|
+
# include file and the contents of the VERSION file.
|
213
|
+
def version_task
|
214
|
+
update_gemspec(:version, ENV['VERSION']) if ENV['VERSION']
|
215
|
+
update_gemspec(:date, Date.today)
|
216
|
+
|
217
|
+
update_version_file(gemspec.version)
|
218
|
+
update_version_constant(gemspec.version)
|
219
|
+
end
|
220
|
+
|
221
|
+
def check_version_task
|
222
|
+
raise "#{ENV['VERSION']} is not a valid version number!" if ENV['VERSION'] && !Gem::Version.correct?(ENV['VERSION'])
|
223
|
+
proposed_version = Gem::Version.new((ENV['VERSION'] || gemspec.version).dup)
|
224
|
+
raise "This version (#{proposed_version}) is not higher than the highest tagged version (#{newest_version})" if newest_version >= proposed_version
|
225
|
+
end
|
226
|
+
|
227
|
+
# Checks whether the current branch is not diverged from the remote branch
|
228
|
+
def check_not_diverged_task
|
229
|
+
raise "The current branch is diverged from the remote branch!" if `#{git} rev-list HEAD..#{remote}/#{remote_branch}`.split("\n").any?
|
230
|
+
end
|
231
|
+
|
232
|
+
# Checks whether the repository status ic clean
|
233
|
+
def check_clean_status_task
|
234
|
+
raise "The current working copy contains modifications" if `#{git} ls-files -m`.split("\n").any?
|
235
|
+
end
|
236
|
+
|
237
|
+
# Checks whether the current branch is correct
|
238
|
+
def check_current_branch_task
|
239
|
+
raise "Currently not on #{local_branch} branch!" unless `#{git} branch`.split("\n").detect { |b| /^\* / =~ b } == "* #{local_branch}"
|
240
|
+
end
|
241
|
+
|
242
|
+
# Fetches the latest updates from Github
|
243
|
+
def fetch_origin_task
|
244
|
+
sh git, 'fetch', remote
|
245
|
+
end
|
246
|
+
|
247
|
+
# Commits every file that has been changed by the release task.
|
248
|
+
def commit_modified_files_task
|
249
|
+
really_modified = `#{git} ls-files -m #{modified_files.entries.join(' ')}`.split("\n")
|
250
|
+
if really_modified.any?
|
251
|
+
really_modified.each { |file| sh git, 'add', file }
|
252
|
+
sh git, 'commit', '-m', "Released #{gemspec.name} gem version #{gemspec.version}."
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# Adds a tag for the released version
|
257
|
+
def tag_version_task
|
258
|
+
sh git, 'tag', '-a', "#{gemspec.name}-#{gemspec.version}", '-m', "Released #{gemspec.name} gem version #{gemspec.version}."
|
259
|
+
end
|
260
|
+
|
261
|
+
# Pushes the changes and tag to github
|
262
|
+
def github_release_task
|
263
|
+
sh git, 'push', '--tags', remote, remote_branch
|
264
|
+
end
|
265
|
+
|
266
|
+
def gemcutter_release_task
|
267
|
+
sh "gem", 'push', "pkg/#{gemspec.name}-#{gemspec.version}.gem"
|
268
|
+
end
|
269
|
+
|
270
|
+
# Gem release task.
|
271
|
+
# All work is done by the task's dependencies, so just display a release completed message.
|
272
|
+
def release_task
|
273
|
+
puts
|
274
|
+
puts "Release successful."
|
275
|
+
end
|
276
|
+
|
277
|
+
private
|
278
|
+
|
279
|
+
# Checks whether this project has any RSpec files
|
280
|
+
def has_specs?
|
281
|
+
FileList[spec_pattern].any?
|
282
|
+
end
|
283
|
+
|
284
|
+
# Checks whether this project has any unit test files
|
285
|
+
def has_tests?
|
286
|
+
FileList[test_pattern].any?
|
287
|
+
end
|
288
|
+
|
289
|
+
# Loads the gemspec file
|
290
|
+
def load_gemspec!
|
291
|
+
@gemspec = eval(File.read(@gemspec_file))
|
292
|
+
end
|
293
|
+
|
294
|
+
# Updates the VERSION file with the new version
|
295
|
+
def update_version_file(version)
|
296
|
+
if File.exists?('VERSION')
|
297
|
+
File.open('VERSION', 'w') { |f| f << version.to_s }
|
298
|
+
modified_files << 'VERSION'
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
# Updates the VERSION constant in the main include file if it exists
|
303
|
+
def update_version_constant(version)
|
304
|
+
if main_include && File.exist?(main_include)
|
305
|
+
file_contents = File.read(main_include)
|
306
|
+
if file_contents.sub!(/^(\s+VERSION\s*=\s*)[^\s].*$/) { $1 + version.to_s.inspect }
|
307
|
+
File.open(main_include, 'w') { |f| f << file_contents }
|
308
|
+
modified_files << main_include
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
# Updates an attribute of the gemspec file.
|
314
|
+
# This function will open the file, and search/replace the attribute using a regular expression.
|
315
|
+
def update_gemspec(attribute, new_value, literal = false)
|
316
|
+
|
317
|
+
unless literal
|
318
|
+
new_value = case new_value
|
319
|
+
when Array then "%w(#{new_value.join(' ')})"
|
320
|
+
when Hash, String then new_value.inspect
|
321
|
+
when Date then new_value.strftime('%Y-%m-%d').inspect
|
322
|
+
else raise "Cannot write value #{new_value.inspect} to gemspec file!"
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
spec = File.read(gemspec_file)
|
327
|
+
regexp = Regexp.new('^(\s+\w+\.' + Regexp.quote(attribute.to_s) + '\s*=\s*)[^\s].*$')
|
328
|
+
if spec.sub!(regexp) { $1 + new_value }
|
329
|
+
File.open(gemspec_file, 'w') { |f| f << spec }
|
330
|
+
modified_files << gemspec_file
|
331
|
+
|
332
|
+
# Reload the gemspec so the changes are incorporated
|
333
|
+
load_gemspec!
|
334
|
+
|
335
|
+
# Also mark the Gemfile.lock file as changed because of the new version.
|
336
|
+
modified_files << 'Gemfile.lock' if File.exist?(File.join(root_dir, 'Gemfile.lock'))
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
# Updates the tasks file using the latest file found on Github
|
341
|
+
def update_tasks_task
|
342
|
+
require 'net/https'
|
343
|
+
require 'uri'
|
344
|
+
|
345
|
+
uri = URI.parse('https://github.com/wvanbergen/github-gem/raw/master/tasks/github-gem.rake')
|
346
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
347
|
+
http.use_ssl = true
|
348
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
349
|
+
response = http.request(Net::HTTP::Get.new(uri.path))
|
350
|
+
|
351
|
+
if Net::HTTPSuccess === response
|
352
|
+
open(__FILE__, "w") { |file| file.write(response.body) }
|
353
|
+
relative_file = File.expand_path(__FILE__).sub(%r[^#{@root_dir}/], '')
|
354
|
+
if `#{git} ls-files -m #{relative_file}`.split("\n").any?
|
355
|
+
sh git, 'add', relative_file
|
356
|
+
sh git, 'commit', '-m', "Updated to latest gem release management tasks."
|
357
|
+
else
|
358
|
+
puts "Release managament tasks already are at the latest version."
|
359
|
+
end
|
360
|
+
else
|
361
|
+
raise "Download failed with HTTP status #{response.code}!"
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class SlowControllerTest < ActionController::TestCase
|
4
|
+
test "should profile" do
|
5
|
+
get :profile_me, :params => {}
|
6
|
+
assert_response :success
|
7
|
+
assert_equal "slow action", @response.body
|
8
|
+
end
|
9
|
+
|
10
|
+
test "should have mixin" do
|
11
|
+
assert @controller.respond_to?(:process_with_browser_profiling)
|
12
|
+
end
|
13
|
+
|
14
|
+
test "should profile param adds profile" do
|
15
|
+
get :profile_me, :params => {"browser_profile!" => ""}
|
16
|
+
assert_response :success
|
17
|
+
|
18
|
+
assert profiled?(@response.body)
|
19
|
+
end
|
20
|
+
|
21
|
+
test "should output to file" do
|
22
|
+
clean_outfile
|
23
|
+
|
24
|
+
get :profile_me, :params => {"file_profile!" => ""}
|
25
|
+
assert_response :success
|
26
|
+
|
27
|
+
assert ! profiled?(@response.body)
|
28
|
+
assert File.exists?(profile_out_file)
|
29
|
+
assert profiled?(File.read(profile_out_file))
|
30
|
+
|
31
|
+
clean_outfile
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Bootstrap rails
|
2
|
+
ENV["RAILS_ENV"] = "test"
|
3
|
+
rails_path = File.expand_path(File.dirname(__FILE__) + "/../rails_test/config/environment.rb")
|
4
|
+
|
5
|
+
unless File.exists?(rails_path)
|
6
|
+
p 'Rails not found, please run:'
|
7
|
+
p '$ rails rails_test'
|
8
|
+
exit
|
9
|
+
end
|
10
|
+
require rails_path
|
11
|
+
|
12
|
+
require 'test_help'
|
13
|
+
require 'pp'
|
14
|
+
require File.expand_path(File.dirname(__FILE__) +'/../init.rb') # Load browser=prof into rails_env
|
15
|
+
|
16
|
+
class SlowController < ActionController::Base
|
17
|
+
def profile_me
|
18
|
+
sleep 1
|
19
|
+
render :text => "slow action"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Helpers
|
24
|
+
def clean_outfile
|
25
|
+
FileUtils.rm_rf(profile_out_file)
|
26
|
+
end
|
27
|
+
|
28
|
+
def profiled?(body)
|
29
|
+
(body =~ /browser_profile/) && (body =~ /Profile Report/)
|
30
|
+
end
|
31
|
+
|
32
|
+
def profile_out_file
|
33
|
+
"#{RAILS_ROOT}/log/profile_out.html"
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: browser-prof
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 2
|
9
|
+
- 3
|
10
|
+
version: 1.2.3
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Willem van Bergen
|
14
|
+
- Bart ten Brinke
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2011-02-17 00:00:00 +01:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: rake
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 3
|
31
|
+
segments:
|
32
|
+
- 0
|
33
|
+
version: "0"
|
34
|
+
type: :development
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
45
|
+
segments:
|
46
|
+
- 2
|
47
|
+
- 0
|
48
|
+
version: "2.0"
|
49
|
+
type: :development
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: ruby-prof
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
type: :development
|
64
|
+
version_requirements: *id003
|
65
|
+
description: " This is a simple tool to profile single web requests in Rails using the power or ruby-prof.\n"
|
66
|
+
email:
|
67
|
+
- willem@railsdoctors.com
|
68
|
+
- bart@railsdoctors.com
|
69
|
+
executables: []
|
70
|
+
|
71
|
+
extensions: []
|
72
|
+
|
73
|
+
extra_rdoc_files:
|
74
|
+
- README.rdoc
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- MIT-LICENSE
|
78
|
+
- README.rdoc
|
79
|
+
- Rakefile
|
80
|
+
- browser-prof.gemspec
|
81
|
+
- init.rb
|
82
|
+
- lib/browser-prof.rb
|
83
|
+
- lib/ruby-prof/graph_html_printer_enhanced.rb
|
84
|
+
- lib/ruby-prof/template.rhtml
|
85
|
+
- pkg/browser_profiler-1.2.0.gem
|
86
|
+
- tasks/github-gem.rake
|
87
|
+
- test/functional/slow_controller_test.rb
|
88
|
+
- test/test_helper.rb
|
89
|
+
has_rdoc: true
|
90
|
+
homepage: http://railsdoctors.com
|
91
|
+
licenses: []
|
92
|
+
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options:
|
95
|
+
- --title
|
96
|
+
- browser-prof
|
97
|
+
- --main
|
98
|
+
- README.rdoc
|
99
|
+
- --line-numbers
|
100
|
+
- --inline-source
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
hash: 3
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
version: "0"
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 3
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
requirements:
|
122
|
+
- ruby-prof gem is required.
|
123
|
+
rubyforge_project:
|
124
|
+
rubygems_version: 1.3.7
|
125
|
+
signing_key:
|
126
|
+
specification_version: 3
|
127
|
+
summary: Rails tool to to profile single requests using ruby-prof.
|
128
|
+
test_files:
|
129
|
+
- test/functional/slow_controller_test.rb
|