contracts 0.7 → 0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/Gemfile.lock +16 -1
- data/README.md +12 -4
- data/TUTORIAL.md +67 -18
- data/benchmarks/bench.rb +20 -20
- data/benchmarks/invariants.rb +28 -18
- data/benchmarks/io.rb +62 -0
- data/benchmarks/wrap_test.rb +11 -13
- data/contracts.gemspec +1 -1
- data/lib/contracts.rb +167 -109
- data/lib/contracts/builtin_contracts.rb +132 -84
- data/lib/contracts/decorators.rb +41 -53
- data/lib/contracts/eigenclass.rb +3 -7
- data/lib/contracts/errors.rb +1 -1
- data/lib/contracts/formatters.rb +134 -0
- data/lib/contracts/invariants.rb +7 -8
- data/lib/contracts/method_reference.rb +33 -9
- data/lib/contracts/support.rb +9 -3
- data/lib/contracts/testable.rb +6 -6
- data/lib/contracts/version.rb +1 -1
- data/script/rubocop.rb +5 -0
- data/spec/builtin_contracts_spec.rb +57 -9
- data/spec/contracts_spec.rb +182 -53
- data/spec/fixtures/fixtures.rb +130 -9
- data/spec/invariants_spec.rb +0 -2
- data/spec/module_spec.rb +2 -2
- data/spec/ruby_version_specific/contracts_spec_1.9.rb +18 -0
- data/spec/ruby_version_specific/contracts_spec_2.0.rb +22 -0
- data/spec/ruby_version_specific/contracts_spec_2.1.rb +55 -0
- data/spec/spec_helper.rb +9 -2
- data/spec/support.rb +4 -0
- data/spec/support_spec.rb +21 -0
- metadata +11 -3
- data/lib/contracts/core_ext.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 124deda42f022fb31b53dc3389bd51ff85860d30
|
4
|
+
data.tar.gz: 8ea3f78006952432311338880c563cdbdf327f33
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 644753384168c11234cd6a77b6d64384cc0f6fca93f3614e4d71ae3629ec7d05d85b02bfbd0150b3db21f94e04fe980f784d0e4decee0e5dcfd912f66526b2fb
|
7
|
+
data.tar.gz: 8a92fd259e98d4320fe5a4a52cc0d2d6cd01e7362b0fc6ed809001ef4e4618a7d1fab4513b2cb2db5946028fdf7a1c745e13491bf9d1e4e897f2dfe39446aafe
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,15 +1,22 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
contracts (0.
|
4
|
+
contracts (0.7)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: http://rubygems.org/
|
8
8
|
specs:
|
9
|
+
ast (2.0.0)
|
10
|
+
astrolabe (1.3.0)
|
11
|
+
parser (>= 2.2.0.pre.3, < 3.0)
|
9
12
|
diff-lcs (1.2.5)
|
10
13
|
hirb (0.7.2)
|
11
14
|
method_profiler (2.0.1)
|
12
15
|
hirb (>= 0.6.0)
|
16
|
+
parser (2.2.0.3)
|
17
|
+
ast (>= 1.1, < 3.0)
|
18
|
+
powerpack (0.1.0)
|
19
|
+
rainbow (2.0.0)
|
13
20
|
rspec (3.1.0)
|
14
21
|
rspec-core (~> 3.1.0)
|
15
22
|
rspec-expectations (~> 3.1.0)
|
@@ -22,7 +29,14 @@ GEM
|
|
22
29
|
rspec-mocks (3.1.3)
|
23
30
|
rspec-support (~> 3.1.0)
|
24
31
|
rspec-support (3.1.2)
|
32
|
+
rubocop (0.29.1)
|
33
|
+
astrolabe (~> 1.3)
|
34
|
+
parser (>= 2.2.0.1, < 3.0)
|
35
|
+
powerpack (~> 0.1)
|
36
|
+
rainbow (>= 1.99.1, < 3.0)
|
37
|
+
ruby-progressbar (~> 1.4)
|
25
38
|
ruby-prof (0.15.2)
|
39
|
+
ruby-progressbar (1.7.5)
|
26
40
|
|
27
41
|
PLATFORMS
|
28
42
|
ruby
|
@@ -31,4 +45,5 @@ DEPENDENCIES
|
|
31
45
|
contracts!
|
32
46
|
method_profiler
|
33
47
|
rspec
|
48
|
+
rubocop (~> 0.29)
|
34
49
|
ruby-prof
|
data/README.md
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
# contracts.ruby
|
2
|
-
|
3
|
-
[![Build Status](https://travis-ci.org/egonSchiele/contracts.ruby.png?branch=master)](https://travis-ci.org/egonSchiele/contracts.ruby)
|
1
|
+
# contracts.ruby [![Build Status](https://travis-ci.org/egonSchiele/contracts.ruby.png?branch=master)](https://travis-ci.org/egonSchiele/contracts.ruby) [![Join the chat at https://gitter.im/egonSchiele/contracts.ruby](https://img.shields.io/badge/gitter-join%20chat-brightgreen.svg)](https://gitter.im/egonSchiele/contracts.ruby)
|
4
2
|
|
5
3
|
Contracts let you clearly – even beautifully – express how your code behaves, and free you from writing tons of boilerplate, defensive code.
|
6
4
|
|
@@ -65,11 +63,21 @@ Using contracts.ruby results in very little slowdown. Check out [this blog post]
|
|
65
63
|
|
66
64
|
If you're using the library, please [let me know](https://github.com/egonSchiele) what project you're using it on :)
|
67
65
|
|
66
|
+
## Testimonials
|
67
|
+
|
68
|
+
> Contracts literally saves us hours of pain at Snowplow every day
|
69
|
+
|
70
|
+
Alexander Dean, creator of [Snowplow](https://github.com/snowplow/snowplow)
|
71
|
+
|
72
|
+
> Contracts caught a bug that saved us several hundred dollars. It took less than 30 seconds to add the contract.
|
73
|
+
|
74
|
+
Michael Tomer
|
75
|
+
|
68
76
|
## Credits
|
69
77
|
|
70
78
|
Inspired by [contracts.coffee](http://disnetdev.com/contracts.coffee/).
|
71
79
|
|
72
|
-
Copyright 2012 [Aditya Bhargava](http://adit.io).
|
80
|
+
Copyright 2012-2015 [Aditya Bhargava](http://adit.io).
|
73
81
|
Major improvements by [Alexey Fedorov](https://github.com/waterlink).
|
74
82
|
|
75
83
|
BSD Licensed.
|
data/TUTORIAL.md
CHANGED
@@ -59,17 +59,33 @@ You can also see the contract for a function with the `functype` method:
|
|
59
59
|
functype(:add)
|
60
60
|
=> "add :: Num, Num => Num"
|
61
61
|
|
62
|
-
This can be useful if you're in a
|
63
|
-
|
64
|
-
##
|
65
|
-
|
66
|
-
`Num` is one of the
|
67
|
-
|
68
|
-
contracts.ruby comes with a lot of
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
62
|
+
This can be useful if you're in a REPL and want to figure out how a function should be used.
|
63
|
+
|
64
|
+
## Built-in Contracts
|
65
|
+
|
66
|
+
`Num` is one of the built-in contracts that contracts.ruby comes with. The built-in contracts are in the `Contracts` namespace. The easiest way to use them is to include the `Contracts` module in your class/module.
|
67
|
+
|
68
|
+
contracts.ruby comes with a lot of built-in contracts, including the following:
|
69
|
+
|
70
|
+
* [`Num`](http://www.rubydoc.info/gems/contracts/Contracts/Num) – checks that the argument is `Numeric`
|
71
|
+
* [`Pos`](http://www.rubydoc.info/gems/contracts/Contracts/Pos) – checks that the argument is a positive number
|
72
|
+
* [`Neg`](http://www.rubydoc.info/gems/contracts/Contracts/Neg) – checks that the argument is a negative number
|
73
|
+
* [`Nat`](http://www.rubydoc.info/gems/contracts/Contracts/Nat) – checks that the argument is a natural number
|
74
|
+
* [`Bool`](http://www.rubydoc.info/gems/contracts/Contracts/Bool) – checks that the argument is `true` or `false`
|
75
|
+
* [`Any`](http://www.rubydoc.info/gems/contracts/Contracts/Any) – Passes for any argument. Use when the argument has no constraints.
|
76
|
+
* [`None`](http://www.rubydoc.info/gems/contracts/Contracts/None) – Fails for any argument. Use when the method takes no arguments.
|
77
|
+
* [`Or`](http://www.rubydoc.info/gems/contracts/Contracts/Or) – passes if any of the given contracts pass, e.g. `Or[Fixnum, Float]`
|
78
|
+
* [`Xor`](http://www.rubydoc.info/gems/contracts/Contracts/Xor) – passes if exactly one of the given contracts pass, e.g. `Xor[Fixnum, Float]`
|
79
|
+
* [`And`](http://www.rubydoc.info/gems/contracts/Contracts/And) – passes if all contracts pass, e.g. `And[Fixnum, Float]`
|
80
|
+
* [`Not`](http://www.rubydoc.info/gems/contracts/Contracts/Not) – passes if all contracts fail for the given argument, e.g. `Not[nil]`
|
81
|
+
* [`ArrayOf`](http://www.rubydoc.info/gems/contracts/Contracts/ArrayOf) – checks that the argument is an array, and all elements pass the given contract, e.g. `ArrayOf[Num]`
|
82
|
+
* [`HashOf`](http://www.rubydoc.info/gems/contracts/Contracts/HashOf) – checks that the argument is a hash, and all keys and values pass the given contract, e.g. `HashOf[Symbol, String]`
|
83
|
+
* [`Maybe`](http://www.rubydoc.info/gems/contracts/Contracts/Maybe) – passes if the argument is `nil`, or if the given contract passes
|
84
|
+
* [`RespondTo`](http://www.rubydoc.info/gems/contracts/Contracts/RespondTo) – checks that the argument responds to all of the given methods, e.g. `RespondTo[:password, :credit_card]`
|
85
|
+
* [`Send`](http://www.rubydoc.info/gems/contracts/Contracts/Send) – checks that all named methods return true, e.g. `Send[:valid?]`
|
86
|
+
* [`Exactly`](http://www.rubydoc.info/gems/contracts/Contracts/Exactly) – checks that the argument has the given type, not accepting sub-classes, e.g. `Exactly[Numeric]`.
|
87
|
+
|
88
|
+
To see all the built-in contracts and their full descriptions, check out the [RDoc](http://rubydoc.info/gems/contracts/Contracts).
|
73
89
|
|
74
90
|
## More Examples
|
75
91
|
|
@@ -106,7 +122,7 @@ This introduces some new syntax. One of the valid values for a contract is an in
|
|
106
122
|
Contract Or.new(Fixnum, Float) => Or.new(Fixnum, Float)
|
107
123
|
```
|
108
124
|
|
109
|
-
All the
|
125
|
+
All the built-in contracts have overridden the square brackets (`[]`) to give the same functionality. So you could write
|
110
126
|
|
111
127
|
```ruby
|
112
128
|
Contract Or[Fixnum, Float] => Or[Fixnum, Float]
|
@@ -224,8 +240,26 @@ give_largest_value("a" => 1, 2 => 2, c: 3)
|
|
224
240
|
|
225
241
|
### Contracts On Functions
|
226
242
|
|
227
|
-
|
228
|
-
|
243
|
+
Lets say you are writing a simple map function:
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
def map(arr, func)
|
247
|
+
```
|
248
|
+
|
249
|
+
`map` takes an array, and a function. Suppose you want to add a contract to this function. You could try this:
|
250
|
+
|
251
|
+
```ruby
|
252
|
+
Contract ArrayOf[Any], Proc => ArrayOf[Any]
|
253
|
+
def map(arr, func)
|
254
|
+
```
|
255
|
+
|
256
|
+
This says that the second argument should be a `Proc`. You can call the function like so:
|
257
|
+
|
258
|
+
```ruby
|
259
|
+
p map([1, 2, 3], lambda { |x| x + 1 }) # works
|
260
|
+
```
|
261
|
+
|
262
|
+
But suppose you want to have a contract on the Proc too! Suppose you want to make sure that the Proc returns a number. Use the `Func` contract. `Func` takes a contract as it's argument, and uses that contract on the function that you pass in.
|
229
263
|
|
230
264
|
Here's a `map` function that requires an array of numbers, and a function that takes a number and returns a number:
|
231
265
|
|
@@ -240,13 +274,24 @@ def map(arr, func)
|
|
240
274
|
end
|
241
275
|
```
|
242
276
|
|
243
|
-
|
277
|
+
Earlier, we used `Proc`, which just says "make sure the second variable is a Proc". Now we are using `Func[Num => Num]`, which says "make sure the second variable is a Proc that takes a number and returns a number". Better!
|
278
|
+
|
279
|
+
Try this map function with these two examples:
|
244
280
|
|
245
281
|
```ruby
|
246
282
|
p map([1, 2, 3], lambda { |x| x + 1 }) # works
|
247
283
|
p map([1, 2, 3], lambda { |x| "oops" }) # fails, the lambda returns a string.
|
248
284
|
```
|
249
285
|
|
286
|
+
NOTE: This is not valid:
|
287
|
+
|
288
|
+
```ruby
|
289
|
+
Contract ArrayOf[Num], Func => ArrayOf[Num]
|
290
|
+
def map(arr, &func)
|
291
|
+
```
|
292
|
+
|
293
|
+
Here I am using `Func` without specifying a contract, like `Func[Num => Num]`. That's not a legal contract. If you just want to validate that the second argument is a proc, use `Proc`.
|
294
|
+
|
250
295
|
### Returning Multiple Values
|
251
296
|
Treat the return value as an array. For example, here's a function that returns two numbers:
|
252
297
|
|
@@ -491,8 +536,8 @@ class MyBirthday < Struct.new(:day, :month)
|
|
491
536
|
include Contracts
|
492
537
|
include Contracts::Invariants
|
493
538
|
|
494
|
-
|
495
|
-
|
539
|
+
invariant(:day) { 1 <= day && day <= 31 }
|
540
|
+
invariant(:month) { 1 <= month && month <= 12 }
|
496
541
|
|
497
542
|
Contract None => Fixnum
|
498
543
|
def silly_next_day!
|
@@ -514,7 +559,11 @@ If you run it, last line will generate invariant violation:
|
|
514
559
|
At: main.rb:9
|
515
560
|
```
|
516
561
|
|
517
|
-
Which means, that after `#silly_next_day!` all checks specified in `
|
562
|
+
Which means, that after `#silly_next_day!` all checks specified in `invariant` statement will be verified, and if at least one fail, then invariant violation error will be raised.
|
563
|
+
|
564
|
+
## Auto-generate documentation using contracts
|
565
|
+
|
566
|
+
If you are generating documentation for your code with [YARD](http://yardoc.org/), check out [yard-contracts](https://github.com/sfcgeorge/yard-contracts). It will automatically annotate your functions with contracts information. Instead of documenting each parameter for a function yourself, you can just add a contract and yard-contracts will generate the documentation for you!
|
518
567
|
|
519
568
|
## Misc
|
520
569
|
|
data/benchmarks/bench.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
require "./lib/contracts"
|
2
|
+
require "benchmark"
|
3
|
+
require "rubygems"
|
4
|
+
require "method_profiler"
|
5
|
+
require "ruby-prof"
|
6
6
|
|
7
7
|
include Contracts
|
8
8
|
|
@@ -16,22 +16,22 @@ def contracts_add a, b
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def explicit_add a, b
|
19
|
-
|
20
|
-
|
19
|
+
fail unless a.is_a?(Numeric)
|
20
|
+
fail unless b.is_a?(Numeric)
|
21
21
|
c = a + b
|
22
|
-
|
22
|
+
fail unless c.is_a?(Numeric)
|
23
23
|
c
|
24
24
|
end
|
25
25
|
|
26
26
|
def benchmark
|
27
27
|
Benchmark.bm 30 do |x|
|
28
|
-
x.report
|
29
|
-
|
28
|
+
x.report "testing add" do
|
29
|
+
1_000_000.times do |_|
|
30
30
|
add(rand(1000), rand(1000))
|
31
31
|
end
|
32
32
|
end
|
33
|
-
x.report
|
34
|
-
|
33
|
+
x.report "testing contracts add" do
|
34
|
+
1_000_000.times do |_|
|
35
35
|
contracts_add(rand(1000), rand(1000))
|
36
36
|
end
|
37
37
|
end
|
@@ -46,20 +46,20 @@ def profile
|
|
46
46
|
profilers << MethodProfiler.observe(Contracts::Decorator)
|
47
47
|
profilers << MethodProfiler.observe(Contracts::Support)
|
48
48
|
profilers << MethodProfiler.observe(UnboundMethod)
|
49
|
-
|
49
|
+
10_000.times do |_|
|
50
50
|
contracts_add(rand(1000), rand(1000))
|
51
51
|
end
|
52
52
|
profilers.each { |p| puts p.report }
|
53
53
|
end
|
54
54
|
|
55
55
|
def ruby_prof
|
56
|
-
RubyProf.start
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
result = RubyProf.stop
|
61
|
-
printer = RubyProf::FlatPrinter.new(result)
|
62
|
-
printer.print(STDOUT)
|
56
|
+
RubyProf.start
|
57
|
+
100_000.times do |_|
|
58
|
+
contracts_add(rand(1000), rand(1000))
|
59
|
+
end
|
60
|
+
result = RubyProf.stop
|
61
|
+
printer = RubyProf::FlatPrinter.new(result)
|
62
|
+
printer.print(STDOUT)
|
63
63
|
end
|
64
64
|
|
65
65
|
benchmark
|
data/benchmarks/invariants.rb
CHANGED
@@ -1,24 +1,34 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
require "./lib/contracts"
|
2
|
+
require "benchmark"
|
3
|
+
require "rubygems"
|
4
|
+
require "method_profiler"
|
5
|
+
require "ruby-prof"
|
6
6
|
|
7
|
-
class Obj
|
7
|
+
class Obj
|
8
8
|
include Contracts
|
9
9
|
|
10
|
+
attr_accessor :value
|
11
|
+
def initialize value
|
12
|
+
@value = value
|
13
|
+
end
|
14
|
+
|
10
15
|
Contract Num, Num => Num
|
11
16
|
def contracts_add a, b
|
12
17
|
a + b
|
13
18
|
end
|
14
19
|
end
|
15
20
|
|
16
|
-
class ObjWithInvariants
|
21
|
+
class ObjWithInvariants
|
17
22
|
include Contracts
|
18
23
|
include Contracts::Invariants
|
19
24
|
|
20
|
-
|
21
|
-
|
25
|
+
invariant(:value_not_nil) { value != nil }
|
26
|
+
invariant(:value_not_string) { !value.is_a?(String) }
|
27
|
+
|
28
|
+
attr_accessor :value
|
29
|
+
def initialize value
|
30
|
+
@value = value
|
31
|
+
end
|
22
32
|
|
23
33
|
Contract Num, Num => Num
|
24
34
|
def contracts_add a, b
|
@@ -31,16 +41,16 @@ def benchmark
|
|
31
41
|
obj_with_invariants = ObjWithInvariants.new(3)
|
32
42
|
|
33
43
|
Benchmark.bm 30 do |x|
|
34
|
-
x.report
|
35
|
-
|
44
|
+
x.report "testing contracts add" do
|
45
|
+
1_000_000.times do |_|
|
36
46
|
obj.contracts_add(rand(1000), rand(1000))
|
37
47
|
end
|
38
48
|
end
|
39
|
-
x.report
|
40
|
-
|
49
|
+
x.report "testing contracts add with invariants" do
|
50
|
+
1_000_000.times do |_|
|
41
51
|
obj_with_invariants.contracts_add(rand(1000), rand(1000))
|
42
52
|
end
|
43
|
-
end
|
53
|
+
end
|
44
54
|
end
|
45
55
|
end
|
46
56
|
|
@@ -55,19 +65,19 @@ def profile
|
|
55
65
|
profilers << MethodProfiler.observe(Contracts::Invariants::InvariantExtension)
|
56
66
|
profilers << MethodProfiler.observe(UnboundMethod)
|
57
67
|
|
58
|
-
|
68
|
+
10_000.times do |_|
|
59
69
|
obj_with_invariants.contracts_add(rand(1000), rand(1000))
|
60
|
-
end
|
70
|
+
end
|
61
71
|
|
62
72
|
profilers.each { |p| puts p.report }
|
63
73
|
end
|
64
74
|
|
65
75
|
def ruby_prof
|
66
|
-
RubyProf.start
|
76
|
+
RubyProf.start
|
67
77
|
|
68
78
|
obj_with_invariants = ObjWithInvariants.new(3)
|
69
79
|
|
70
|
-
|
80
|
+
100_000.times do |_|
|
71
81
|
obj_with_invariants.contracts_add(rand(1000), rand(1000))
|
72
82
|
end
|
73
83
|
|
data/benchmarks/io.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require "./lib/contracts"
|
2
|
+
require "benchmark"
|
3
|
+
require "rubygems"
|
4
|
+
require "method_profiler"
|
5
|
+
require "ruby-prof"
|
6
|
+
require "open-uri"
|
7
|
+
|
8
|
+
include Contracts
|
9
|
+
|
10
|
+
def download url
|
11
|
+
open("https://www.#{url}/").read
|
12
|
+
end
|
13
|
+
|
14
|
+
Contract String => String
|
15
|
+
def contracts_download url
|
16
|
+
open("https://www.#{url}").read
|
17
|
+
end
|
18
|
+
|
19
|
+
@urls = %w{facebook.com google.com bing.com}
|
20
|
+
|
21
|
+
def benchmark
|
22
|
+
Benchmark.bm 30 do |x|
|
23
|
+
x.report "testing download" do
|
24
|
+
100.times do |_|
|
25
|
+
download(@urls.sample)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
x.report "testing contracts download" do
|
29
|
+
100.times do |_|
|
30
|
+
contracts_download(@urls.sample)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def profile
|
37
|
+
profilers = []
|
38
|
+
profilers << MethodProfiler.observe(Contract)
|
39
|
+
profilers << MethodProfiler.observe(Object)
|
40
|
+
profilers << MethodProfiler.observe(Contracts::MethodDecorators)
|
41
|
+
profilers << MethodProfiler.observe(Contracts::Decorator)
|
42
|
+
profilers << MethodProfiler.observe(Contracts::Support)
|
43
|
+
profilers << MethodProfiler.observe(UnboundMethod)
|
44
|
+
10.times do |_|
|
45
|
+
contracts_download(@urls.sample)
|
46
|
+
end
|
47
|
+
profilers.each { |p| puts p.report }
|
48
|
+
end
|
49
|
+
|
50
|
+
def ruby_prof
|
51
|
+
RubyProf.start
|
52
|
+
10.times do |_|
|
53
|
+
contracts_download(@urls.sample)
|
54
|
+
end
|
55
|
+
result = RubyProf.stop
|
56
|
+
printer = RubyProf::FlatPrinter.new(result)
|
57
|
+
printer.print(STDOUT)
|
58
|
+
end
|
59
|
+
|
60
|
+
benchmark
|
61
|
+
profile
|
62
|
+
ruby_prof if ENV["FULL_BENCH"] # takes some time
|