rspec/rspec-expectations — pull request #107 by josevalim (2012-01-11 15:56:56+00:00)
Title: Compile matchers to classes

This is a proposal that compiles DSL matchers to classes. The main goal here is to improve the performance of DSL matchers. A consequence of this proposal is that the implementation for RSpec::Matchers::Matcher is more sane (we are able to get rid of instance_exec, making_defined_methods_public and the global variable). You can see the benchmark results after my patch at the end.

However, this proposal is backwards incompatible. At the moment we compile blocks to methods, we lose the binding from the block. A practical example would be this one:

RSpec::Matchers.define :be_one do
one = 1
match do |actual|
actual == one
end
end

The setup above will no longer work. Another backward incompatible change is that we no longer pass arguments to the block in `define`. For instance, a matcher like this:

RSpec::Matchers.define :eq_using_dsl do |value|
match do |actual|
actual == value
end
end

Should be rewritten to:

RSpec::Matchers.define :eq_using_dsl do
match do |actual|
actual == expected
end
end

Another option to reduce the rewrite is to have a method called arguments in the DSL that specifies which arguments we expect:

RSpec::Matchers.define :eq_using_dsl do
arguments :value

match do |actual|
actual == value
end
end

The value will always be accessible as `expected`. This pull request leaves 23 failures in the test suite, all related to the error above. I have halted work since I realized the backwards incompatible changes.

## Benchmark results

3 runs of 1000 times for each example running ruby/1.9.3
passing examples: 5.should eq(5)
* using the DSL
0.010000 0.000000 0.010000 ( 0.002245)
0.000000 0.000000 0.000000 ( 0.002228)
0.000000 0.000000 0.000000 ( 0.002162)

* using a class
0.000000 0.000000 0.000000 ( 0.001121)
0.000000 0.000000 0.000000 ( 0.001097)
0.000000 0.000000 0.000000 ( 0.001098)

failing examples: 5.should eq(3)
* using the DSL
0.010000 0.000000 0.010000 ( 0.002129)
0.000000 0.000000 0.000000 ( 0.002209)
0.000000 0.000000 0.000000 ( 0.002146)

* using a class
0.060000 0.000000 0.060000 ( 0.057473)
0.040000 0.000000 0.040000 ( 0.049150)
0.040000 0.010000 0.050000 ( 0.040319)