rubycut-metriks 0.9.9.4
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/Gemfile +14 -0
- data/LICENSE +21 -0
- data/README.md +405 -0
- data/Rakefile +150 -0
- data/benchmark/samplers.rb +92 -0
- data/lib/metriks/counter.rb +44 -0
- data/lib/metriks/ewma.rb +63 -0
- data/lib/metriks/exponentially_decaying_sample.rb +102 -0
- data/lib/metriks/histogram.rb +112 -0
- data/lib/metriks/meter.rb +85 -0
- data/lib/metriks/registry.rb +207 -0
- data/lib/metriks/reporter/graphite.rb +119 -0
- data/lib/metriks/reporter/librato_metrics.rb +185 -0
- data/lib/metriks/reporter/logger.rb +129 -0
- data/lib/metriks/reporter/proc_title.rb +65 -0
- data/lib/metriks/reporter/riemann.rb +119 -0
- data/lib/metriks/simple_moving_average.rb +60 -0
- data/lib/metriks/snapshot.rb +59 -0
- data/lib/metriks/time_tracker.rb +26 -0
- data/lib/metriks/timer.rb +101 -0
- data/lib/metriks/uniform_sample.rb +40 -0
- data/lib/metriks/utilization_timer.rb +43 -0
- data/lib/metriks.rb +35 -0
- data/metriks.gemspec +100 -0
- data/test/counter_test.rb +39 -0
- data/test/graphite_reporter_test.rb +41 -0
- data/test/histogram_test.rb +199 -0
- data/test/librato_metrics_reporter_test.rb +35 -0
- data/test/logger_reporter_test.rb +49 -0
- data/test/meter_test.rb +38 -0
- data/test/metriks_test.rb +31 -0
- data/test/proc_title_reporter_test.rb +25 -0
- data/test/registry_test.rb +49 -0
- data/test/riemann_reporter_test.rb +88 -0
- data/test/test_helper.rb +33 -0
- data/test/thread_error_handling_tests.rb +20 -0
- data/test/timer_test.rb +32 -0
- data/test/utilization_timer_test.rb +25 -0
- metadata +161 -0
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2012 Eric Lindvall
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,405 @@
|
|
1
|
+
# Metriks Client
|
2
|
+
|
3
|
+
This is an experiment in making a threadsafe, low impact library to measure
|
4
|
+
aspects of your ruby.
|
5
|
+
|
6
|
+
The library is very much a work-in-progress. It is being developed as
|
7
|
+
I find needs while developing [Papertrail](https://papertrailapp.com/).
|
8
|
+
|
9
|
+
|
10
|
+
# Installing
|
11
|
+
|
12
|
+
The API is still in flux, but you can add this to your project by installing
|
13
|
+
the gem.
|
14
|
+
|
15
|
+
To install, add this to your `Gemfile`:
|
16
|
+
|
17
|
+
``` ruby
|
18
|
+
gem 'metriks'
|
19
|
+
```
|
20
|
+
|
21
|
+
and re-run `bundle`.
|
22
|
+
|
23
|
+
|
24
|
+
# Metric API Overview
|
25
|
+
|
26
|
+
## Counters
|
27
|
+
|
28
|
+
Basic atomic counter. Used as an underlying metric for many of the other
|
29
|
+
more advanced metrics.
|
30
|
+
|
31
|
+
|
32
|
+
### increment(incr = 1)
|
33
|
+
|
34
|
+
Increment the counter. Without an argument it will increment by `1`.
|
35
|
+
|
36
|
+
``` ruby
|
37
|
+
counter = Metriks.counter('calls')
|
38
|
+
counter.increment
|
39
|
+
```
|
40
|
+
|
41
|
+
### decrement(decr = 1)
|
42
|
+
|
43
|
+
Decrement the counter. Without an argument it will decrement by `1`.
|
44
|
+
|
45
|
+
``` ruby
|
46
|
+
counter = Metriks.counter('calls')
|
47
|
+
counter.decrement
|
48
|
+
```
|
49
|
+
|
50
|
+
#### count()
|
51
|
+
|
52
|
+
Return the current value of the counter.
|
53
|
+
|
54
|
+
``` ruby
|
55
|
+
counter = Metriks.counter('calls')
|
56
|
+
puts "counter: #{counter.count}"
|
57
|
+
```
|
58
|
+
|
59
|
+
## Gauges
|
60
|
+
|
61
|
+
A gauge is an instantaneous measurement of a value.
|
62
|
+
|
63
|
+
It takes a callback to measure the value in form of a block or a callable
|
64
|
+
object.
|
65
|
+
|
66
|
+
**WARNING:** The code in the callback is executed every time the `#value`
|
67
|
+
method is called on the gauge. Most of the time this will be done by a
|
68
|
+
metriks reporter that is running in a separate thread.
|
69
|
+
|
70
|
+
``` ruby
|
71
|
+
# Callback as block
|
72
|
+
gauge = Metriks.gauge('queue.size') { queue.size }
|
73
|
+
|
74
|
+
# Callback as object responding to #call
|
75
|
+
callable = proc { queue.size }
|
76
|
+
gauge = Metriks.gauge('queue.size', callable)
|
77
|
+
```
|
78
|
+
|
79
|
+
### set(val)
|
80
|
+
|
81
|
+
Set the current value.
|
82
|
+
|
83
|
+
``` ruby
|
84
|
+
gauge = Metriks.gauge('queue_size')
|
85
|
+
gauge.set(queue.size)
|
86
|
+
```
|
87
|
+
|
88
|
+
### value()
|
89
|
+
|
90
|
+
Returns the value returned by the callback (if one is defined), returns the
|
91
|
+
value set via `#set` (or the default of 0) otherwise.
|
92
|
+
|
93
|
+
``` ruby
|
94
|
+
gauge = Metriks.gauge('queue_size')
|
95
|
+
puts "queue size: #{gauge.value}"
|
96
|
+
```
|
97
|
+
|
98
|
+
## Meters
|
99
|
+
|
100
|
+
A meter that measures the mean throughput and the one-, five-, and
|
101
|
+
fifteen-minute exponentially-weighted moving average throughputs.
|
102
|
+
|
103
|
+
### mark(val = 1)
|
104
|
+
|
105
|
+
Record an event with the meter. Without an argument it will record one event.
|
106
|
+
|
107
|
+
``` ruby
|
108
|
+
meter = Metriks.meter('requests')
|
109
|
+
meter.mark
|
110
|
+
```
|
111
|
+
|
112
|
+
### count()
|
113
|
+
|
114
|
+
Returns the total number of events that have been recorded.
|
115
|
+
|
116
|
+
``` ruby
|
117
|
+
meter = Metriks.meter('requests')
|
118
|
+
puts "total: #{meter.count}"
|
119
|
+
```
|
120
|
+
|
121
|
+
### one_minute_rate()
|
122
|
+
|
123
|
+
Returns the one-minute average rate.
|
124
|
+
|
125
|
+
``` ruby
|
126
|
+
meter = Metriks.meter('requests')
|
127
|
+
puts "rate: #{meter.one_minute_rate}/sec"
|
128
|
+
```
|
129
|
+
|
130
|
+
### five_minute_rate()
|
131
|
+
|
132
|
+
Returns the five-minute average rate.
|
133
|
+
|
134
|
+
``` ruby
|
135
|
+
meter = Metriks.meter('requests')
|
136
|
+
puts "rate: #{meter.five_minute_rate}/sec"
|
137
|
+
```
|
138
|
+
|
139
|
+
### fifteen_minute_rate()
|
140
|
+
|
141
|
+
Returns the fifteen-minute average rate.
|
142
|
+
|
143
|
+
``` ruby
|
144
|
+
meter = Metriks.meter('requests')
|
145
|
+
puts "rate: #{meter.fifteen_minute_rate}/sec"
|
146
|
+
```
|
147
|
+
|
148
|
+
### mean_rate()
|
149
|
+
|
150
|
+
Returns the mean (average) rate of the events since the start of the process.
|
151
|
+
|
152
|
+
``` ruby
|
153
|
+
meter = Metriks.meter('requests')
|
154
|
+
puts "rate: #{meter.mean_rate}/sec"
|
155
|
+
```
|
156
|
+
|
157
|
+
## Timers
|
158
|
+
|
159
|
+
A timer that measures the average time as well as throughput metrics via
|
160
|
+
a meter.
|
161
|
+
|
162
|
+
### update(duration)
|
163
|
+
|
164
|
+
Records the duration of an operation. This normally wouldn't need to be
|
165
|
+
called — the `#time` method is provided to simplify recording a duration.
|
166
|
+
|
167
|
+
``` ruby
|
168
|
+
timer = Metriks.timer('requests')
|
169
|
+
t0 = Time.now
|
170
|
+
work
|
171
|
+
timer.update(Time.now - t0)
|
172
|
+
```
|
173
|
+
|
174
|
+
### time(callable = nil, &block)
|
175
|
+
|
176
|
+
Measure the amount of time a proc takes to execute. Takes either a block
|
177
|
+
or an object responding to `#call` (normally a `proc` or `lambda`).
|
178
|
+
|
179
|
+
``` ruby
|
180
|
+
timer = Metriks.timer('requests')
|
181
|
+
timer.time do
|
182
|
+
work
|
183
|
+
end
|
184
|
+
```
|
185
|
+
|
186
|
+
If neither a block or an object is passed to the method, an object that
|
187
|
+
responds to `#stop` will be returned. When `#stop` is called, the time
|
188
|
+
will be recorded.
|
189
|
+
|
190
|
+
``` ruby
|
191
|
+
timer = Metriks.timer('requests')
|
192
|
+
t = timer.time
|
193
|
+
work
|
194
|
+
t.stop
|
195
|
+
```
|
196
|
+
|
197
|
+
### count()
|
198
|
+
|
199
|
+
Returns the number of measurements that have been made.
|
200
|
+
|
201
|
+
``` ruby
|
202
|
+
timer = Metriks.timer('requests')
|
203
|
+
puts "calls: #{timer.count}"
|
204
|
+
```
|
205
|
+
|
206
|
+
### one_minute_rate()
|
207
|
+
|
208
|
+
Returns the one-minute average rate.
|
209
|
+
|
210
|
+
``` ruby
|
211
|
+
meter = Metriks.timer('requests')
|
212
|
+
puts "rate: #{meter.one_minute_rate}/sec"
|
213
|
+
```
|
214
|
+
|
215
|
+
### five_minute_rate()
|
216
|
+
|
217
|
+
Returns the five-minute average rate.
|
218
|
+
|
219
|
+
``` ruby
|
220
|
+
meter = Metriks.timer('requests')
|
221
|
+
puts "rate: #{meter.five_minute_rate}/sec"
|
222
|
+
```
|
223
|
+
|
224
|
+
### fifteen_minute_rate()
|
225
|
+
|
226
|
+
Returns the fifteen-minute average rate.
|
227
|
+
|
228
|
+
``` ruby
|
229
|
+
meter = Metriks.timer('requests')
|
230
|
+
puts "rate: #{meter.fifteen_minute_rate}/sec"
|
231
|
+
```
|
232
|
+
|
233
|
+
### mean_rate()
|
234
|
+
|
235
|
+
Returns the mean (average) rate of the events since the start of the process.
|
236
|
+
|
237
|
+
``` ruby
|
238
|
+
meter = Metriks.timer('requests')
|
239
|
+
puts "rate: #{meter.mean_rate}/sec"
|
240
|
+
```
|
241
|
+
|
242
|
+
### min()
|
243
|
+
|
244
|
+
Returns the minimum amount of time spent in the operation.
|
245
|
+
|
246
|
+
``` ruby
|
247
|
+
meter = Metriks.timer('requests')
|
248
|
+
puts "time: #{meter.min} seconds"
|
249
|
+
```
|
250
|
+
|
251
|
+
### max()
|
252
|
+
|
253
|
+
Returns the maximum time spent in the operation.
|
254
|
+
|
255
|
+
``` ruby
|
256
|
+
meter = Metriks.timer('requests')
|
257
|
+
puts "time: #{meter.max} seconds"
|
258
|
+
```
|
259
|
+
|
260
|
+
### mean()
|
261
|
+
|
262
|
+
Returns the mean (average) time spent in the operation.
|
263
|
+
|
264
|
+
``` ruby
|
265
|
+
meter = Metriks.timer('requests')
|
266
|
+
puts "time: #{meter.mean} seconds"
|
267
|
+
```
|
268
|
+
|
269
|
+
### stddev()
|
270
|
+
|
271
|
+
Returns the standard deviation of the mean spent in the operation.
|
272
|
+
|
273
|
+
``` ruby
|
274
|
+
meter = Metriks.timer('requests')
|
275
|
+
puts "time: #{meter.stddev} seconds"
|
276
|
+
```
|
277
|
+
|
278
|
+
|
279
|
+
## Utilization Timer
|
280
|
+
|
281
|
+
A specialized `Timer` that calculates the percentage (between `0.0` and `1.0`) of
|
282
|
+
wall-clock time that was spent. It includes all of the methods of `Timer`.
|
283
|
+
|
284
|
+
|
285
|
+
### one_minute_utilization()
|
286
|
+
|
287
|
+
Returns the one-minute average utilization as a percentage between `0.0` and `1.0`.
|
288
|
+
|
289
|
+
``` ruby
|
290
|
+
meter = Metriks.utilization_timer('requests')
|
291
|
+
puts "utilization: #{meter.one_minute_utilization * 100}%"
|
292
|
+
```
|
293
|
+
|
294
|
+
### five_minute_utilization()
|
295
|
+
|
296
|
+
Returns the five-minute average utilization as a percentage between `0.0` and `1.0`.
|
297
|
+
|
298
|
+
``` ruby
|
299
|
+
meter = Metriks.utilization_timer('requests')
|
300
|
+
puts "utilization: #{meter.five_minute_utilization * 100}%"
|
301
|
+
```
|
302
|
+
|
303
|
+
### fifteen_minute_utilization()
|
304
|
+
|
305
|
+
Returns the fifteen-minute average utilization as a percentage between `0.0` and `1.0`.
|
306
|
+
|
307
|
+
``` ruby
|
308
|
+
meter = Metriks.utilization_timer('requests')
|
309
|
+
puts "utilization: #{meter.fifteen_minute_utilization * 100}%"
|
310
|
+
```
|
311
|
+
|
312
|
+
### mean_utilization()
|
313
|
+
|
314
|
+
Returns the mean (average) utilization as a percentage between `0.0` and `1.0`
|
315
|
+
since the process started.
|
316
|
+
|
317
|
+
``` ruby
|
318
|
+
meter = Metriks.utilization_timer('requests')
|
319
|
+
puts "utilization: #{meter.mean_utilization * 100}%"
|
320
|
+
```
|
321
|
+
|
322
|
+
|
323
|
+
# Reporter Overview
|
324
|
+
|
325
|
+
How to get metrics out of the process.
|
326
|
+
|
327
|
+
## Graphite Reporter
|
328
|
+
|
329
|
+
Sends metrics to Graphite every 60 seconds.
|
330
|
+
|
331
|
+
``` ruby
|
332
|
+
reporter = Metriks::Reporter::Graphite.new 'localhost', 3004
|
333
|
+
reporter.start
|
334
|
+
```
|
335
|
+
|
336
|
+
|
337
|
+
## Logger Reporter
|
338
|
+
|
339
|
+
Send metrics to a logger every 60 seconds.
|
340
|
+
|
341
|
+
``` ruby
|
342
|
+
reporter = Metriks::Reporter::Logger.new(:logger => Logger.new('log/metrics.log'))
|
343
|
+
reporter.start
|
344
|
+
```
|
345
|
+
|
346
|
+
|
347
|
+
## Librato Metrics Reporter
|
348
|
+
|
349
|
+
Send metrics to Librato Metrics every 60 seconds.
|
350
|
+
|
351
|
+
``` ruby
|
352
|
+
reporter = Metriks::Reporter::LibratoMetrics.new('email', 'token')
|
353
|
+
reporter.start
|
354
|
+
```
|
355
|
+
|
356
|
+
|
357
|
+
## Proc Title Reporter
|
358
|
+
|
359
|
+
Provides a simple way to get up-to-date statistics from a process by
|
360
|
+
updating the proctitle every 5 seconds (default).
|
361
|
+
|
362
|
+
``` ruby
|
363
|
+
reporter = Metriks::Reporter::ProcTitle.new :interval => 5
|
364
|
+
reporter.add 'reqs', 'sec' do
|
365
|
+
Metriks.meter('rack.requests').one_minute_rate
|
366
|
+
end
|
367
|
+
reporter.start
|
368
|
+
```
|
369
|
+
|
370
|
+
will display:
|
371
|
+
|
372
|
+
```
|
373
|
+
501 17015 26.0 1.9 416976 246956 ? Ss 18:54 11:43 thin reqs: 273.3/sec
|
374
|
+
```
|
375
|
+
|
376
|
+
# Application Server Configuration
|
377
|
+
|
378
|
+
Depending on how your application server operates, you may need to configure how reporters are created. Please look at [Troubleshooting](https://github.com/eric/metriks/wiki/Troubleshooting) for more information.
|
379
|
+
|
380
|
+
# Plans
|
381
|
+
|
382
|
+
An incomplete list of things I would like to see added:
|
383
|
+
|
384
|
+
* Rack middleware to measure utilization, throughput and worker time
|
385
|
+
* Basic reporters:
|
386
|
+
* Rack endpoint returning JSON
|
387
|
+
* [Statsd](https://github.com/etsy/statsd) reporter
|
388
|
+
* Metaprogramming instrumentation hooks like [Shopify's statsd-instrument](https://github.com/Shopify/statsd-instrument)
|
389
|
+
|
390
|
+
|
391
|
+
# Credits
|
392
|
+
|
393
|
+
Most of the inspiration for this project comes from Coda Hale's amazing
|
394
|
+
[Metrics, Metrics Everywhere][metrics-talk] talk at CodeConf and his sweet
|
395
|
+
[Metrics][metrics] Java Library.
|
396
|
+
|
397
|
+
[metrics-talk]: http://pivotallabs.com/talks/139-metrics-metrics-everywhere
|
398
|
+
[metrics]: https://github.com/codahale/metrics
|
399
|
+
|
400
|
+
|
401
|
+
# License
|
402
|
+
|
403
|
+
Copyright (c) 2012 Eric Lindvall
|
404
|
+
|
405
|
+
Published under the MIT License, see LICENSE
|
data/Rakefile
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
#############################################################################
|
6
|
+
#
|
7
|
+
# Helper functions
|
8
|
+
#
|
9
|
+
#############################################################################
|
10
|
+
|
11
|
+
def name
|
12
|
+
@name ||= Dir['*.gemspec'].first.split('.').first
|
13
|
+
end
|
14
|
+
|
15
|
+
def version
|
16
|
+
line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
|
17
|
+
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
18
|
+
end
|
19
|
+
|
20
|
+
def date
|
21
|
+
Date.today.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def rubyforge_project
|
25
|
+
name
|
26
|
+
end
|
27
|
+
|
28
|
+
def gemspec_file
|
29
|
+
"#{name}.gemspec"
|
30
|
+
end
|
31
|
+
|
32
|
+
def gem_file
|
33
|
+
"#{name}-#{version}.gem"
|
34
|
+
end
|
35
|
+
|
36
|
+
def replace_header(head, header_name)
|
37
|
+
head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
|
38
|
+
end
|
39
|
+
|
40
|
+
#############################################################################
|
41
|
+
#
|
42
|
+
# Standard tasks
|
43
|
+
#
|
44
|
+
#############################################################################
|
45
|
+
|
46
|
+
task :default => :test
|
47
|
+
|
48
|
+
require 'rake/testtask'
|
49
|
+
Rake::TestTask.new(:test) do |test|
|
50
|
+
test.libs << 'lib' << 'test'
|
51
|
+
test.pattern = 'test/**/*_test.rb'
|
52
|
+
test.verbose = true
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "Generate RCov test coverage and open in your browser"
|
56
|
+
task :coverage do
|
57
|
+
require 'rcov'
|
58
|
+
sh "rm -fr coverage"
|
59
|
+
sh "rcov test/*_test.rb"
|
60
|
+
sh "open coverage/index.html"
|
61
|
+
end
|
62
|
+
|
63
|
+
# require 'rake/rdoctask'
|
64
|
+
# Rake::RDocTask.new do |rdoc|
|
65
|
+
# rdoc.rdoc_dir = 'rdoc'
|
66
|
+
# rdoc.title = "#{name} #{version}"
|
67
|
+
# rdoc.rdoc_files.include('README*')
|
68
|
+
# rdoc.rdoc_files.include('lib/**/*.rb')
|
69
|
+
# end
|
70
|
+
|
71
|
+
desc "Open an irb session preloaded with this library"
|
72
|
+
task :console do
|
73
|
+
sh "irb -rubygems -r ./lib/#{name}.rb"
|
74
|
+
end
|
75
|
+
|
76
|
+
#############################################################################
|
77
|
+
#
|
78
|
+
# Custom tasks (add your own tasks here)
|
79
|
+
#
|
80
|
+
#############################################################################
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
#############################################################################
|
85
|
+
#
|
86
|
+
# Packaging tasks
|
87
|
+
#
|
88
|
+
#############################################################################
|
89
|
+
|
90
|
+
desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
|
91
|
+
task :release => :build do
|
92
|
+
unless `git branch` =~ /^\* master$/
|
93
|
+
puts "You must be on the master branch to release!"
|
94
|
+
exit!
|
95
|
+
end
|
96
|
+
sh "git commit --allow-empty -a -m 'Release #{version}'"
|
97
|
+
sh "git tag v#{version}"
|
98
|
+
sh "git push origin master"
|
99
|
+
sh "git push origin v#{version}"
|
100
|
+
sh "gem push pkg/#{name}-#{version}.gem"
|
101
|
+
end
|
102
|
+
|
103
|
+
desc "Build #{gem_file} into the pkg directory"
|
104
|
+
task :build => :gemspec do
|
105
|
+
sh "mkdir -p pkg"
|
106
|
+
sh "gem build #{gemspec_file}"
|
107
|
+
sh "mv #{gem_file} pkg"
|
108
|
+
end
|
109
|
+
|
110
|
+
desc "Generate #{gemspec_file}"
|
111
|
+
task :gemspec => :validate do
|
112
|
+
# read spec file and split out manifest section
|
113
|
+
spec = File.read(gemspec_file)
|
114
|
+
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
115
|
+
|
116
|
+
# replace name version and date
|
117
|
+
replace_header(head, :name)
|
118
|
+
replace_header(head, :version)
|
119
|
+
replace_header(head, :date)
|
120
|
+
#comment this out if your rubyforge_project has a different name
|
121
|
+
replace_header(head, :rubyforge_project)
|
122
|
+
|
123
|
+
# determine file list from git ls-files
|
124
|
+
files = `git ls-files`.
|
125
|
+
split("\n").
|
126
|
+
sort.
|
127
|
+
reject { |file| file =~ /^\./ }.
|
128
|
+
reject { |file| file =~ /^(rdoc|pkg)/ }.
|
129
|
+
map { |file| " #{file}" }.
|
130
|
+
join("\n")
|
131
|
+
|
132
|
+
# piece file back together and write
|
133
|
+
manifest = " s.files = %w[\n#{files}\n ]\n"
|
134
|
+
spec = [head, manifest, tail].join(" # = MANIFEST =\n")
|
135
|
+
File.open(gemspec_file, 'w') { |io| io.write(spec) }
|
136
|
+
puts "Updated #{gemspec_file}"
|
137
|
+
end
|
138
|
+
|
139
|
+
desc "Validate #{gemspec_file}"
|
140
|
+
task :validate do
|
141
|
+
libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
|
142
|
+
unless libfiles.empty?
|
143
|
+
puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
|
144
|
+
exit!
|
145
|
+
end
|
146
|
+
unless Dir['VERSION*'].empty?
|
147
|
+
puts "A `VERSION` file at root level violates Gem best practices."
|
148
|
+
exit!
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'benchmark'
|
4
|
+
require 'metriks'
|
5
|
+
require 'rbtree'
|
6
|
+
require 'avl_tree'
|
7
|
+
require 'red_black_tree'
|
8
|
+
|
9
|
+
fib_times = ARGV[0] ? ARGV[0].to_i : 10
|
10
|
+
iter = ARGV[1] ? ARGV[1].to_i : 100000
|
11
|
+
|
12
|
+
|
13
|
+
class TimerBenchmarker
|
14
|
+
attr_reader :iter, :fib_times
|
15
|
+
|
16
|
+
def initialize(fib_times, iter)
|
17
|
+
@fib_times = fib_times
|
18
|
+
@iter = iter
|
19
|
+
@mapping = { :plain => nil }
|
20
|
+
end
|
21
|
+
|
22
|
+
def measure(key, value)
|
23
|
+
@mapping[key] = value
|
24
|
+
end
|
25
|
+
|
26
|
+
def run
|
27
|
+
@results = {}
|
28
|
+
@mapping.each do |key, timer|
|
29
|
+
@results[key] = Benchmark.realtime do
|
30
|
+
if timer
|
31
|
+
for i in 1..iter
|
32
|
+
timer.time do
|
33
|
+
fib(fib_times)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
else
|
37
|
+
for i in 1..iter
|
38
|
+
fib(fib_times)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
report
|
44
|
+
end
|
45
|
+
|
46
|
+
def report
|
47
|
+
results = @results.sort_by { |k,v| v }
|
48
|
+
results.each_with_index do |(name, time), idx|
|
49
|
+
puts "%23s: %f secs %f secs/call" % [
|
50
|
+
name, time, time / iter
|
51
|
+
]
|
52
|
+
|
53
|
+
if idx > 0
|
54
|
+
prev_name, prev_time = results[idx - 1]
|
55
|
+
puts "#{' ' * 25} - %.1f%% slower than %s (%f secs/call)" % [
|
56
|
+
(time - prev_time) / prev_time * 100, prev_name,
|
57
|
+
(time - prev_time) / iter
|
58
|
+
]
|
59
|
+
end
|
60
|
+
|
61
|
+
if idx > 1
|
62
|
+
plain_name, plain_time = results[0]
|
63
|
+
puts "#{' ' * 25} - %.1f%% slower than %s (%f secs/call)" % [
|
64
|
+
(time - plain_time) / plain_time * 100, plain_name,
|
65
|
+
(time - plain_time) / iter
|
66
|
+
]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def fib(n)
|
72
|
+
n < 2 ? n : fib(n-1) + fib(n-2)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
reporter = TimerBenchmarker.new(fib_times, iter)
|
77
|
+
|
78
|
+
reporter.measure :uniform, Metriks::Timer.new(Metriks::Histogram.new_uniform)
|
79
|
+
|
80
|
+
reporter.measure :exponential, Metriks::Timer.new(Metriks::ExponentiallyDecayingSample.new(
|
81
|
+
Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA, RBTree.new))
|
82
|
+
|
83
|
+
reporter.measure :exponential_avl, Metriks::Timer.new(Metriks::ExponentiallyDecayingSample.new(
|
84
|
+
Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA, AVLTree.new))
|
85
|
+
|
86
|
+
reporter.measure :exponential_red_black, Metriks::Timer.new(Metriks::ExponentiallyDecayingSample.new(
|
87
|
+
Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA, RedBlackTree.new))
|
88
|
+
|
89
|
+
puts "fib(#{fib_times}): #{iter} iterations"
|
90
|
+
puts "-" * 50
|
91
|
+
|
92
|
+
reporter.run
|