method_profiler 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/README.md +80 -12
- data/lib/method_profiler.rb +15 -101
- data/lib/method_profiler/hirb.rb +14 -0
- data/lib/method_profiler/profiler.rb +105 -0
- data/lib/method_profiler/report.rb +89 -0
- data/method_profiler.gemspec +2 -1
- data/spec/method_profiler/profiler_spec.rb +19 -0
- data/spec/method_profiler/report_spec.rb +91 -0
- data/spec/method_profiler_spec.rb +3 -54
- data/spec/spec_helper.rb +9 -4
- data/spec/support/petition.rb +18 -0
- metadata +27 -6
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,19 +1,87 @@
|
|
1
1
|
# MethodProfiler
|
2
2
|
|
3
|
-
**MethodProfiler** collects performance information about the methods in your objects and creates reports to help you identify slow methods.
|
3
|
+
**MethodProfiler** collects performance information about the methods in your objects and creates reports to help you identify slow methods. The collected data can be sorted in various ways, converted into an array, or pretty printed as a table.
|
4
|
+
|
5
|
+
## Basic usage
|
6
|
+
|
7
|
+
Create a new profiler by passing the object you want to profile to `MethodProfiler.observe`. All future class and instance methods called on your object will be recorded by the profiler. To see the results of the profiling as a table, simply print out the report returned by `#report` on the profiler object.
|
4
8
|
|
5
9
|
```ruby
|
6
|
-
profiler = MethodProfiler.
|
7
|
-
|
10
|
+
profiler = MethodProfiler.observe(MyClass)
|
11
|
+
|
12
|
+
MyClass.nisi_inventore
|
13
|
+
MyClass.nisi_inventore
|
14
|
+
|
15
|
+
my_obj = MyClass.new
|
16
|
+
|
17
|
+
my_obj.accusamus_est
|
18
|
+
my_obj.accusamus_est
|
19
|
+
my_obj.accusamus_est
|
20
|
+
|
8
21
|
puts profiler.report
|
22
|
+
```
|
9
23
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
#
|
24
|
+
The resulting chart includes each method, the minimum time it took to run, the maximum time, the average across all calls, and the total number of times it was called. Class methods are prefixed by a `.` and instance methods are prefixed with a `#`.
|
25
|
+
|
26
|
+
```
|
27
|
+
MethodProfiler results for: MyClass
|
28
|
+
+------------------------+-----------+------------+--------------+-------------+
|
29
|
+
| Method | Min Time | Max Time | Average Time | Total Calls |
|
30
|
+
+------------------------+-----------+------------+--------------+-------------+
|
31
|
+
| #accusamus_est | 32.086 ms | 324.140 ms | 160.252 ms | 3 |
|
32
|
+
| #autem_iste! | 29.607 ms | 318.592 ms | 156.221 ms | 3 |
|
33
|
+
| #distinctio_eos | 29.477 ms | 318.471 ms | 156.097 ms | 3 |
|
34
|
+
| #laborum_fugit | 18.388 ms | 291.900 ms | 140.580 ms | 3 |
|
35
|
+
| #suscipit_architecto | 12.036 ms | 272.279 ms | 130.247 ms | 3 |
|
36
|
+
| #et_fugit | 2.794 ms | 11.658 ms | 6.185 ms | 3 |
|
37
|
+
| #porro_rerum | 0.097 ms | 12.096 ms | 1.031 ms | 43 |
|
38
|
+
| #provident_molestiae | 0.005 ms | 3.997 ms | 0.871 ms | 6 |
|
39
|
+
| .nisi_inventore | 0.368 ms | 1.329 ms | 0.849 ms | 2 |
|
40
|
+
| #quis_temporibus | 0.104 ms | 6.696 ms | 0.713 ms | 24 |
|
41
|
+
| #labore_voluptatum | 0.004 ms | 8.834 ms | 0.447 ms | 86 |
|
42
|
+
| #quia_est | 0.004 ms | 3.667 ms | 0.327 ms | 24 |
|
43
|
+
| #ut_reiciendis | 0.004 ms | 2.842 ms | 0.250 ms | 24 |
|
44
|
+
| #sint_quasi | 0.066 ms | 1.836 ms | 0.166 ms | 24 |
|
45
|
+
| #sed_at | 0.067 ms | 0.119 ms | 0.078 ms | 24 |
|
46
|
+
| #repellendus_suscipit | 0.054 ms | 0.128 ms | 0.067 ms | 24 |
|
47
|
+
| #quas_nesciunt | 0.024 ms | 0.026 ms | 0.025 ms | 3 |
|
48
|
+
| #iure_quis | 0.006 ms | 0.232 ms | 0.014 ms | 48 |
|
49
|
+
| #dicta_ipsam | 0.005 ms | 0.068 ms | 0.013 ms | 24 |
|
50
|
+
| #perspiciatis_aut | 0.006 ms | 0.006 ms | 0.006 ms | 3 |
|
51
|
+
| #aperiam_laborum | 0.004 ms | 0.009 ms | 0.005 ms | 34 |
|
52
|
+
+------------------------+-----------+------------+--------------+-------------+
|
19
53
|
```
|
54
|
+
|
55
|
+
## Reporting
|
56
|
+
|
57
|
+
`MethodProfiler::Profiler#report` actually returns a report object which can be used to sort and display the data in various ways. A report has chainable `#sort_by` and `#order` methods to control the sorting of the report when it is ultimately displayed. The report can be turned into an array by calling `#to_a` and the table shown above by calling `#to_s`.
|
58
|
+
|
59
|
+
*Example of sorting by the number of total calls, ascending:*
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
puts profiler.report.sort_by(:total_calls).order(:ascending)
|
63
|
+
```
|
64
|
+
|
65
|
+
`#sort_by` accepts a symbol or string with the name of any of the columns in the table: `:method`, `:min`, `:max`, `:average`, or `:total_calls`.
|
66
|
+
|
67
|
+
`#order` accepts a symbol or string of `:ascending` or `:descending`. These can also be abbreviated with `:asc` and `:desc`.
|
68
|
+
|
69
|
+
## Documentation
|
70
|
+
|
71
|
+
The public API is fully documented using [YARD](http://yardoc.org/) and can be viewed on [RubyDoc.info](http://rubydoc.info/).
|
72
|
+
|
73
|
+
## Tests
|
74
|
+
|
75
|
+
All code is tested with [RSpec](https://github.com/rspec/rspec). To run the specs, clone the repository, install the dependencies with `bundle install`, and then run `rake`.
|
76
|
+
|
77
|
+
## Issues
|
78
|
+
|
79
|
+
If you have any problems or suggestions for the project, please open a GitHub issue.
|
80
|
+
|
81
|
+
## License
|
82
|
+
|
83
|
+
MethodProfiler is available under the included MIT license.
|
84
|
+
|
85
|
+
## Acknowledgements
|
86
|
+
|
87
|
+
Thank you to [Change.org](http://www.change.org/) for sponsoring the project and to my coworker [Alain Bloch](https://github.com/alainbloch) for the inspiration.
|
data/lib/method_profiler.rb
CHANGED
@@ -1,103 +1,17 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def
|
15
|
-
|
16
|
-
benchmark = Benchmark.measure { result = block.call }
|
17
|
-
elapsed_time = benchmark.to_s.match(/\(\s*([^\)]+)\)/)[1].to_f
|
18
|
-
@data[method.to_sym] << elapsed_time
|
19
|
-
result
|
20
|
-
end
|
21
|
-
|
22
|
-
def report
|
23
|
-
[
|
24
|
-
"MethodProfiler results for: #{@obj}",
|
25
|
-
Hirb::Helpers::Table.render(
|
26
|
-
final_data,
|
27
|
-
headers: {
|
28
|
-
method: "Method",
|
29
|
-
min: "Min Time",
|
30
|
-
max: "Max Time",
|
31
|
-
average: "Average Time",
|
32
|
-
total_calls: "Total Calls"
|
33
|
-
},
|
34
|
-
fields: [:method, :min, :max, :average, :total_calls],
|
35
|
-
description: false
|
36
|
-
)
|
37
|
-
].join("\n")
|
38
|
-
end
|
39
|
-
|
40
|
-
def reset!
|
41
|
-
initialize_data
|
42
|
-
@final_data = nil
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
def initialize_data
|
48
|
-
@data = Hash.new { |h, k| h[k] = [] }
|
49
|
-
end
|
50
|
-
|
51
|
-
def find_obj_methods
|
52
|
-
@observed_methods ||= begin
|
53
|
-
methods = @obj.instance_methods
|
54
|
-
@obj.ancestors.each do |a|
|
55
|
-
next if a == @obj
|
56
|
-
methods -= a.instance_methods
|
57
|
-
end
|
58
|
-
methods
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def wrap_methods_with_profiling
|
63
|
-
profiler = self
|
64
|
-
observed = observed_methods
|
65
|
-
|
66
|
-
@obj.module_eval do
|
67
|
-
observed.each do |method|
|
68
|
-
define_method("#{method}_with_profiling") do |*args|
|
69
|
-
profiler.profile(method) { send("#{method}_without_profiling", *args) }
|
70
|
-
end
|
71
|
-
|
72
|
-
alias_method "#{method}_without_profiling", method
|
73
|
-
alias_method method, "#{method}_with_profiling"
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def final_data
|
79
|
-
@final_data ||= begin
|
80
|
-
final_data = []
|
81
|
-
data.each do |method, records|
|
82
|
-
total_calls = records.size
|
83
|
-
average = records.reduce(:+) / total_calls
|
84
|
-
final_data << {
|
85
|
-
method: method,
|
86
|
-
min: records.min,
|
87
|
-
max: records.max,
|
88
|
-
average: average,
|
89
|
-
total_calls: total_calls
|
90
|
-
}
|
91
|
-
end
|
92
|
-
final_data.sort! { |a, b| b[:average] <=> a[:average] }
|
93
|
-
final_data.each do |record|
|
94
|
-
[:min, :max, :average].each { |k| record[k] = to_ms(record[k]) }
|
95
|
-
end
|
96
|
-
final_data
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def to_ms(seconds)
|
101
|
-
"%.3f ms" % (seconds * 1000)
|
1
|
+
require 'method_profiler/profiler'
|
2
|
+
|
3
|
+
# {MethodProfiler} collects performance information about the methods
|
4
|
+
# in your objects and creates reports to help you identify slow methods.
|
5
|
+
#
|
6
|
+
module MethodProfiler
|
7
|
+
# Create a new {MethodProfiler::Profiler} which will observe all method calls
|
8
|
+
# on the given object. This is a convenience method and has the same effect
|
9
|
+
# as {MethodProfiler::Profiler#initialize}.
|
10
|
+
#
|
11
|
+
# @param [Object] obj The object to observe.
|
12
|
+
# @return [MethodProfiler::Profiler] A new profiler.
|
13
|
+
#
|
14
|
+
def self.observe(obj)
|
15
|
+
Profiler.new(obj)
|
102
16
|
end
|
103
17
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'hirb'
|
2
|
+
|
3
|
+
# All methods in this module can be used as filters in Hirb.
|
4
|
+
#
|
5
|
+
module Hirb::Helpers::Table::Filters
|
6
|
+
# Converts seconds to milliseconds. Used to format times in
|
7
|
+
# the output of {MethodProfiler::Report}.
|
8
|
+
#
|
9
|
+
# @param [Float] seconds The duration to convert.
|
10
|
+
# @return [String] The duration in milliseconds with units displayed. Rounded to 3 decimal places.
|
11
|
+
def to_milliseconds(seconds)
|
12
|
+
"%.3f ms" % (seconds * 1000)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'method_profiler/report'
|
2
|
+
|
3
|
+
require 'benchmark'
|
4
|
+
|
5
|
+
module MethodProfiler
|
6
|
+
# Observes an object, keeping track of all its method calls and the wall clock
|
7
|
+
# time spent executing them.
|
8
|
+
#
|
9
|
+
class Profiler
|
10
|
+
# Initializes a new {Profiler}. Wraps all methods in the object and its singleton
|
11
|
+
# class with profiling code.
|
12
|
+
#
|
13
|
+
# @param [Object] obj The object to observe.
|
14
|
+
#
|
15
|
+
def initialize(obj)
|
16
|
+
@obj = obj
|
17
|
+
@data = Hash.new { |h, k| h[k] = [] }
|
18
|
+
|
19
|
+
wrap_methods_with_profiling
|
20
|
+
end
|
21
|
+
|
22
|
+
# Generates a report object with all the data collected so far bay the profiler. This report
|
23
|
+
# can be displayed in various ways. See {Report}.
|
24
|
+
#
|
25
|
+
# @return [Report] A new report with all the data the profiler has collected.
|
26
|
+
#
|
27
|
+
def report
|
28
|
+
Report.new(final_data)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def wrap_methods_with_profiling
|
34
|
+
profiler = self
|
35
|
+
singleton_methods_to_wrap = find_singleton_methods
|
36
|
+
instance_methods_to_wrap = find_instance_methods
|
37
|
+
|
38
|
+
@obj.singleton_class.module_eval do
|
39
|
+
singleton_methods_to_wrap.each do |method|
|
40
|
+
define_method("#{method}_with_profiling") do |*args|
|
41
|
+
profiler.send(:profile, method, true) { send("#{method}_without_profiling", *args) }
|
42
|
+
end
|
43
|
+
|
44
|
+
alias_method "#{method}_without_profiling", method
|
45
|
+
alias_method method, "#{method}_with_profiling"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
@obj.module_eval do
|
50
|
+
instance_methods_to_wrap.each do |method|
|
51
|
+
define_method("#{method}_with_profiling") do |*args|
|
52
|
+
profiler.send(:profile, method) { send("#{method}_without_profiling", *args) }
|
53
|
+
end
|
54
|
+
|
55
|
+
alias_method "#{method}_without_profiling", method
|
56
|
+
alias_method method, "#{method}_with_profiling"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def find_singleton_methods
|
62
|
+
@obj.singleton_class.instance_methods - @obj.singleton_class.ancestors.map do |a|
|
63
|
+
a == @obj ? [] : a.instance_methods
|
64
|
+
end.flatten
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_instance_methods
|
68
|
+
@obj.instance_methods - @obj.ancestors.map do |a|
|
69
|
+
a == @obj ? [] : a.instance_methods
|
70
|
+
end.flatten
|
71
|
+
end
|
72
|
+
|
73
|
+
def profile(method, singleton = false, &block)
|
74
|
+
method_name = singleton ? ".#{method}" : "##{method}"
|
75
|
+
result = nil
|
76
|
+
elapsed_time = benchmark(result, &block).to_s.match(/\(\s*([^\)]+)\)/)[1].to_f
|
77
|
+
@data[method_name] << elapsed_time
|
78
|
+
result
|
79
|
+
end
|
80
|
+
|
81
|
+
def final_data
|
82
|
+
results = []
|
83
|
+
|
84
|
+
@data.each do |method, records|
|
85
|
+
total_calls = records.size
|
86
|
+
average = records.reduce(:+) / total_calls
|
87
|
+
results << {
|
88
|
+
method: method,
|
89
|
+
min: records.min,
|
90
|
+
max: records.max,
|
91
|
+
average: average,
|
92
|
+
total_calls: total_calls
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
results
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def benchmark(result, &block)
|
102
|
+
Benchmark.measure { result = block.call }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'hirb'
|
2
|
+
require 'method_profiler/hirb'
|
3
|
+
|
4
|
+
module MethodProfiler
|
5
|
+
# Sorts and displays data collected by a {Profiler}.
|
6
|
+
#
|
7
|
+
class Report
|
8
|
+
# Fields that can be passed to {#sort_by}.
|
9
|
+
FIELDS = [:method, :min, :max, :average, :total_calls]
|
10
|
+
|
11
|
+
# Directions that can be passed to {#order}.
|
12
|
+
DIRECTIONS = [:asc, :ascending, :desc, :descending]
|
13
|
+
|
14
|
+
# Initializes a new {Report}. Used to sort and display data collected by a {Profiler}.
|
15
|
+
#
|
16
|
+
# @param [Array] data Data collected by a {Profiler}.
|
17
|
+
#
|
18
|
+
def initialize(data)
|
19
|
+
@data = data
|
20
|
+
@sort_by = :average
|
21
|
+
@order = :descending
|
22
|
+
end
|
23
|
+
|
24
|
+
# Sorts the report by the given field. Defaults to `:average`. Chainable with {#order}.
|
25
|
+
#
|
26
|
+
# @param [Symbol, String] field Any field from {FIELDS} to sort by.
|
27
|
+
# @return [Report] The {Report} object, suitable for chaining or display.
|
28
|
+
#
|
29
|
+
def sort_by(field)
|
30
|
+
field = field.to_sym
|
31
|
+
field = :average unless FIELDS.include?(field)
|
32
|
+
@sort_by = field
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
# Changes the direction of the sort. Defaults to `:descending`. Chainable with {#sort_by}.
|
37
|
+
#
|
38
|
+
# @param [Symbol, String] direction Any direction from {DIRECTIONS} to direct the sort.
|
39
|
+
# @return [Report] The {Report} object, suitable for chaining or display.
|
40
|
+
#
|
41
|
+
def order(direction)
|
42
|
+
direction = direction.to_sym
|
43
|
+
direction = :descending unless DIRECTIONS.include?(direction)
|
44
|
+
direction = :descending if direction == :desc
|
45
|
+
direction = :ascending if direction == :asc
|
46
|
+
@order = direction
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
# Sorts the data by the currently set criteria and returns an array of profiling results.
|
51
|
+
#
|
52
|
+
# @return [Array] An array of profiling results.
|
53
|
+
#
|
54
|
+
def to_a
|
55
|
+
if @order == :ascending
|
56
|
+
@data.sort { |a, b| a[@sort_by] <=> b[@sort_by] }
|
57
|
+
else
|
58
|
+
@data.sort { |a, b| b[@sort_by] <=> a[@sort_by] }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Sorts the data by the currently set criteria and returns a pretty printed table as a string.
|
63
|
+
#
|
64
|
+
# @return [String] A table of profiling results.
|
65
|
+
#
|
66
|
+
def to_s
|
67
|
+
[
|
68
|
+
"MethodProfiler results for: #{@obj}",
|
69
|
+
Hirb::Helpers::Table.render(
|
70
|
+
to_a,
|
71
|
+
headers: {
|
72
|
+
method: "Method",
|
73
|
+
min: "Min Time",
|
74
|
+
max: "Max Time",
|
75
|
+
average: "Average Time",
|
76
|
+
total_calls: "Total Calls"
|
77
|
+
},
|
78
|
+
fields: [:method, :min, :max, :average, :total_calls],
|
79
|
+
filters: {
|
80
|
+
min: :to_milliseconds,
|
81
|
+
max: :to_milliseconds,
|
82
|
+
average: :to_milliseconds
|
83
|
+
},
|
84
|
+
description: false
|
85
|
+
)
|
86
|
+
].join("\n")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/method_profiler.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "method_profiler"
|
5
|
-
s.version = "
|
5
|
+
s.version = "1.0.0"
|
6
6
|
s.authors = ["Jimmy Cuadra"]
|
7
7
|
s.email = ["jimmy@jimmycuadra.com"]
|
8
8
|
s.homepage = "https://github.com/change/method_profiler"
|
@@ -16,4 +16,5 @@ Gem::Specification.new do |s|
|
|
16
16
|
|
17
17
|
s.add_runtime_dependency "hirb", ">= 0.6.0"
|
18
18
|
s.add_development_dependency "rspec"
|
19
|
+
s.add_development_dependency "simplecov"
|
19
20
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MethodProfiler::Profiler do
|
4
|
+
let!(:profiler) { described_class.new(Petition) }
|
5
|
+
let(:petition) { Petition.new }
|
6
|
+
|
7
|
+
it "creates wrapper methods for the object's methods" do
|
8
|
+
petition.should respond_to(:foo)
|
9
|
+
petition.should respond_to(:foo_with_profiling)
|
10
|
+
petition.should respond_to(:foo_without_profiling)
|
11
|
+
petition.should_not respond_to(:foo_with_profiling_with_profiling)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#report" do
|
15
|
+
it "returns a new Report object" do
|
16
|
+
profiler.report.should be_an_instance_of MethodProfiler::Report
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MethodProfiler::Report do
|
4
|
+
before do
|
5
|
+
profiler = MethodProfiler::Profiler.new(Petition)
|
6
|
+
|
7
|
+
# Fake the timings for testing purposes
|
8
|
+
profiler.stub(:benchmark) do |result, block|
|
9
|
+
result = block.call
|
10
|
+
"(#{rand})"
|
11
|
+
end
|
12
|
+
|
13
|
+
petition = Petition.new
|
14
|
+
|
15
|
+
[:hay, :hay, :guys].each { |m| petition.class.send(m) }
|
16
|
+
[:foo, :bar, :baz].each { |m| petition.send(m) }
|
17
|
+
|
18
|
+
@report = profiler.report
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#to_a" do
|
22
|
+
it "returns an array of records" do
|
23
|
+
results = @report.to_a
|
24
|
+
results.should be_an Array
|
25
|
+
results.size.should == 5
|
26
|
+
end
|
27
|
+
|
28
|
+
it "sorts by average time (descending) by default" do
|
29
|
+
results = @report.to_a
|
30
|
+
average_times = results.map { |r| r[:average] }
|
31
|
+
average_times.should == average_times.sort.reverse
|
32
|
+
end
|
33
|
+
|
34
|
+
it "sorts by another type if it's been changed" do
|
35
|
+
results = @report.sort_by(:min).to_a
|
36
|
+
min_times = results.map { |r| r[:min] }
|
37
|
+
min_times.should == min_times.sort.reverse
|
38
|
+
end
|
39
|
+
|
40
|
+
it "sorts in a different direction if it's been changed" do
|
41
|
+
results = @report.order(:ascending).to_a
|
42
|
+
average_times = results.map { |r| r[:average] }
|
43
|
+
average_times.should == average_times.sort
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#sort_by" do
|
48
|
+
it "sets the sort type" do
|
49
|
+
@report.sort_by(:total_calls)
|
50
|
+
@report.instance_variable_get("@sort_by").should == :total_calls
|
51
|
+
end
|
52
|
+
|
53
|
+
it "defaults to average if an invalid sort type is passed" do
|
54
|
+
@report.sort_by(:foo)
|
55
|
+
@report.instance_variable_get("@sort_by").should == :average
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#order" do
|
60
|
+
it "sets the sort direction" do
|
61
|
+
@report.order(:ascending)
|
62
|
+
@report.instance_variable_get("@order").should == :ascending
|
63
|
+
end
|
64
|
+
|
65
|
+
it "defaults to descending if an invalid direction is passed" do
|
66
|
+
@report.order(:foo)
|
67
|
+
@report.instance_variable_get("@order").should == :descending
|
68
|
+
end
|
69
|
+
|
70
|
+
it "allows normalizes :asc and :desc aliases" do
|
71
|
+
@report.order(:asc)
|
72
|
+
@report.instance_variable_get("@order").should == :ascending
|
73
|
+
|
74
|
+
@report.order(:desc)
|
75
|
+
@report.instance_variable_get("@order").should == :descending
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#to_s" do
|
80
|
+
it "outputs one line for each method that was called" do
|
81
|
+
output = @report.to_s
|
82
|
+
|
83
|
+
output.should be_a String
|
84
|
+
output.scan(/\.hay/).size.should == 1
|
85
|
+
output.scan(/\.guys/).size.should == 1
|
86
|
+
output.scan(/#foo/).size.should == 1
|
87
|
+
output.scan(/#bar/).size.should == 1
|
88
|
+
output.scan(/#baz/).size.should == 1
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -1,60 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
profiler = MethodProfiler.new(Petition)
|
4
|
-
|
5
3
|
describe MethodProfiler do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
it "can be instantiated with an object to observe" do
|
10
|
-
profiler.should be_true
|
11
|
-
end
|
12
|
-
|
13
|
-
it "finds all the object's instance methods" do
|
14
|
-
profiler.observed_methods.sort.should == [:foo, :bar, :baz].sort
|
15
|
-
end
|
16
|
-
|
17
|
-
it "creates wrapper methods for each method in the object" do
|
18
|
-
@petition.should respond_to(:foo)
|
19
|
-
@petition.should respond_to(:foo_with_profiling)
|
20
|
-
@petition.should respond_to(:foo_without_profiling)
|
21
|
-
@petition.should_not respond_to(:foo_with_profiling_with_profiling)
|
22
|
-
end
|
23
|
-
|
24
|
-
describe "#profile" do
|
25
|
-
it "adds a new record for the method call" do
|
26
|
-
@petition.foo
|
27
|
-
profiler.data[:foo].size.should == 1
|
28
|
-
end
|
29
|
-
|
30
|
-
it "calls the real method" do
|
31
|
-
@petition.should_receive(:foo_without_profiling)
|
32
|
-
@petition.foo
|
33
|
-
end
|
34
|
-
|
35
|
-
it "returns the value of the real method" do
|
36
|
-
@petition.baz.should == "blah"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
describe "#report" do
|
41
|
-
it "outputs a string of report data" do
|
42
|
-
profiler.report.should be_an_instance_of(String)
|
43
|
-
end
|
44
|
-
|
45
|
-
it "outputs one line for each method that was called" do
|
46
|
-
@petition.foo
|
47
|
-
@petition.bar
|
48
|
-
@petition.baz
|
49
|
-
profiler.report.scan(/foo/).size.should == 1
|
50
|
-
profiler.report.scan(/bar/).size.should == 1
|
51
|
-
profiler.report.scan(/baz/).size.should == 1
|
52
|
-
end
|
53
|
-
|
54
|
-
it "combines multiple calls to the same method into one line" do
|
55
|
-
@petition.foo
|
56
|
-
@petition.foo
|
57
|
-
profiler.report.scan(/foo/).size.should == 1
|
4
|
+
describe ".observe" do
|
5
|
+
it "returns a new Profiler instance" do
|
6
|
+
described_class.observe(Petition).should be_an_instance_of described_class::Profiler
|
58
7
|
end
|
59
8
|
end
|
60
9
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
+
require "simplecov"
|
2
|
+
SimpleCov.start
|
3
|
+
|
1
4
|
$:.unshift(File.expand_path("../../lib", __FILE__))
|
2
5
|
require "method_profiler"
|
3
6
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.around do |example|
|
9
|
+
load File.expand_path("../support/petition.rb", __FILE__)
|
10
|
+
example.call
|
11
|
+
Object.send(:remove_const, :Petition)
|
12
|
+
end
|
8
13
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: method_profiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-03-
|
12
|
+
date: 2012-03-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: hirb
|
16
|
-
requirement: &
|
16
|
+
requirement: &70181816945620 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 0.6.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70181816945620
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &70181816945220 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,18 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70181816945220
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: simplecov
|
38
|
+
requirement: &70181816944760 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70181816944760
|
36
47
|
description: MethodProfiler observes your code and generates reports about the methods
|
37
48
|
that were run and how long they took.
|
38
49
|
email:
|
@@ -47,9 +58,15 @@ files:
|
|
47
58
|
- README.md
|
48
59
|
- Rakefile
|
49
60
|
- lib/method_profiler.rb
|
61
|
+
- lib/method_profiler/hirb.rb
|
62
|
+
- lib/method_profiler/profiler.rb
|
63
|
+
- lib/method_profiler/report.rb
|
50
64
|
- method_profiler.gemspec
|
65
|
+
- spec/method_profiler/profiler_spec.rb
|
66
|
+
- spec/method_profiler/report_spec.rb
|
51
67
|
- spec/method_profiler_spec.rb
|
52
68
|
- spec/spec_helper.rb
|
69
|
+
- spec/support/petition.rb
|
53
70
|
homepage: https://github.com/change/method_profiler
|
54
71
|
licenses: []
|
55
72
|
post_install_message:
|
@@ -75,5 +92,9 @@ signing_key:
|
|
75
92
|
specification_version: 3
|
76
93
|
summary: Find slow methods in your program.
|
77
94
|
test_files:
|
95
|
+
- spec/method_profiler/profiler_spec.rb
|
96
|
+
- spec/method_profiler/report_spec.rb
|
78
97
|
- spec/method_profiler_spec.rb
|
79
98
|
- spec/spec_helper.rb
|
99
|
+
- spec/support/petition.rb
|
100
|
+
has_rdoc:
|