Instead of using
describe Foo do
# ...
end
use
RSpec.describe Foo, type: :model do
# ...
end
instead. This is not exactly official, as the former style still works,
but the latter style is highly suggested in RSpec documentation and the
like, so this is what people are used to.
[ci skip]
`should` is not a valid method inside a `should` block, so we have to use
`assert_accepts` instead. This lets us use any variables from the setup block,
as opposed to a bare `should` which requires us to know all parameters
up front.
Why:
* There were architectural issues with how the permit matcher kept track
of params instances on which doubles had been placed. Previously we
were starting off by taking the ActionController::Parameters class and
stubbing the #permit and #require instance method on it -- in other
words, we were stubbing #require for all instances of
ActionController::Parameters -- then we would stub #permit on a
particular instance of ActionController::Parameters that #require
returned. What this means is that if for some reason the #permit stub
on an individual instance isn't working properly, then the #permit
stub on ActionController::Parameters will respond to the invocation.
This is exactly what happened for the issue we recently fixed --
if the stubbing were done a different way we wouldn't have run into
that issue.
* Also, there's no reason to have both ParametersDoubles and
SliceOfParametersDoubles classes around. While it's nice that we have
a simpler option to use if we don't need the more complex one, we
actually don't need a distinction here, and we can afford one class
that does both.
To satisfy the above:
* When stubbing #permit or #require, always do so on an instance of
ActionController::Parameters and not the whole class. This way we know
exactly which methods are being doubled and it's easier to debug things
in the future.
* This means that we now stub ActionController::Parameters.new and then
place stubs on the returned instance.
* Refactor ParametersDoubles and SliceOfParametersDoubles: combine them
into a ParametersDoubleRegistry class, but extract the code that stubs
ActionController::Parameters.new into
a CompositeParametersDoubleRegistry class.
* Since this broke one of the tests, modify DoubleCollection so that a
method cannot be doubled more than once -- if the method is already
doubled then `register_stub` or `register_proxy` does nothing and
returns the original Double.
This commit simply fixes the failure messages for #permit so that
instead of looking like this:
Expected POST #create to restrict parameters for :personto :name, :age, :city, and :country, but ...
they look like this:
Expected POST #create to restrict parameters on :person to :name, :age, :city, and :country, but ...
Also, flesh out more of the tests for the failure messages.
Previously we were taking ActionController::Parameters and completely
overriding #require, forcing it to return `self`, i.e, the entire
ActionController::Parameters object. This meant that we broke its
functionality, which is to return a slice of the params hash instead.
The consequence of this is that attempting to call #permit on a slice of
the params hash obtained via #require would not work:
``` ruby
params = ActionController::Parameters.new(
{ "course" => { "foo" => "bar" } }
)
params.require(:course)
params.require(:course).permit(:foo)
```
This commit fixes the permit matcher so that #require is proxied
instead, retaining the existing behavior.
This commit also adds a qualifier, #on, for asserting that your action
places a restriction on a slice of the params hash. The `permit` matcher
will properly track calls on child `params` instances. For example:
``` ruby
class UsersController < ActionController::Base
def create
User.create!(user_params)
...
end
private
def user_params
params.require(:user).permit(:name, :age)
end
end
describe UsersController do
it { should permit(:name, :age).for(:create).on(:user) }
end
```
If this fails, you'll get the following error message:
```
Expected POST #create to restrict parameters for :user to :name and :age,
but restricted parameters were :first_name and :last_name.
```
MethodCall is used to represent a method call made on a double. It
stores the object the call was made on, the name of the method, any
arguments or block sent to the method, and the double itself. There were
quite a few places where we were using an (args, block) or (object,
args, block) tuple. We also already had a MethodCall class, and
additionally a MethodCallWithName class, and the two were basically the
same. These usages have all been collapsed.