I believe the distinction is that block matchers check side effects, while value matchers - returned value.
But I believe it's confusing in terms of telling a story how to create a block matcher. Frankly, I can't create a usable composable block matcher using those docs.
To add to the confusion, there are actually two ways of creating matchers. The first one is with `RSpec::Matchers.define`, and another one is by writing a regular class.
I usually start with `define`, get quickly frustrated and switch to a class.
But it's not checking the returned value along the way.
So I quickly came up with this:
```
$foo = 0
class Bar
include RSpec::Matchers::Composable
def initialize(expected_return_value)
@expected_return_value = expected_return_value
end
def matches?(block)
@actual_return_value = block.call
values_match?(@expected_return_value, @actual_return_value)
end
def failure_message(*args)
"Expected: #{@expected_return_value}, got: #{@actual_return_value}"
end
def supports_block_expectations?
true
end
end
module RSpec
module Matchers # reopen for simplicity. you can `config.include MyModule` in spec_helper
def bar(expected_return_value)
Bar.new(expected_return_value)
end
end
end
RSpec.describe "a custom block matcher" do
specify do
expect { $foo = 2 }
.to bar(2)
.and change { $foo }.by(2)
end
end
```
and it works!
However, if you change the order:
```
expect { $foo = 2 }
.to change { $foo }.by(2)
.and bar(2)
```
it starts complaining that `@actual_return_value` (the result of the block being called) is `true`, not `2`.
It seems to me that only the innermost matcher can get the return value of the block, and it's never passed over to composed matchers.
Can you work with that? This would imply that the matcher that checks the return value is the always the first one in the chain.
It feels doable to pass the returned value of the block along.
We've restricted value matchers from working with block expectations in RSpec 4, i.e.
```
expect { 1 }.to eq(1)
```
will raise an error.
But I have no certainty if this feature will not be confusing. Even though it might be usable in some cases, e.g.:
```
expect { post :delete }
.to be_ok
.and change { User.count }.by(-1)
```
instead of
```
expect { post :delete }
.to change { User.count }.by(-1)
expect(response).to be_ok
```
Let me know what you think.
> happy holidays
Happy holidays to you too, Nick!
- Phil