2014-04-16 08:58:05 +00:00
|
|
|
require 'delegate'
|
|
|
|
|
2014-01-24 21:14:49 +00:00
|
|
|
begin
|
|
|
|
require 'strong_parameters'
|
|
|
|
rescue LoadError
|
|
|
|
end
|
|
|
|
|
2014-04-16 08:58:05 +00:00
|
|
|
require 'active_support/hash_with_indifferent_access'
|
|
|
|
|
2014-01-24 21:14:49 +00:00
|
|
|
module Shoulda
|
|
|
|
module Matchers
|
|
|
|
module ActionController
|
2014-01-23 18:07:36 +00:00
|
|
|
# The `permit` matcher tests that an action in your controller receives a
|
|
|
|
# whitelist of parameters using Rails' Strong Parameters feature
|
|
|
|
# (specifically that `permit` was called with the correct arguments).
|
|
|
|
#
|
|
|
|
# Here's an example:
|
|
|
|
#
|
|
|
|
# class UsersController < ApplicationController
|
|
|
|
# def create
|
|
|
|
# user = User.create(user_params)
|
|
|
|
# # ...
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# private
|
|
|
|
#
|
|
|
|
# def user_params
|
|
|
|
# params.require(:user).permit(
|
|
|
|
# :first_name,
|
|
|
|
# :last_name,
|
|
|
|
# :email,
|
|
|
|
# :password
|
|
|
|
# )
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe UsersController do
|
|
|
|
# it do
|
2015-06-10 22:38:49 +00:00
|
|
|
# params = {
|
|
|
|
# user: {
|
|
|
|
# first_name: 'John',
|
|
|
|
# last_name: 'Doe',
|
|
|
|
# email: 'johndoe@example.com',
|
|
|
|
# password: 'password'
|
|
|
|
# }
|
|
|
|
# }
|
2014-01-23 18:07:36 +00:00
|
|
|
# should permit(:first_name, :last_name, :email, :password).
|
2015-06-10 22:38:49 +00:00
|
|
|
# for(:create, params: params).
|
Permit matcher now supports subparameters
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.
```
2015-02-19 18:35:45 +00:00
|
|
|
# on(:user)
|
2014-01-23 18:07:36 +00:00
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # Test::Unit
|
|
|
|
# class UsersControllerTest < ActionController::TestCase
|
2015-06-10 22:38:49 +00:00
|
|
|
# should "(for POST #create) restrict parameters on :user to first_name, last_name, email, and password" do
|
|
|
|
# params = {
|
|
|
|
# user: {
|
|
|
|
# first_name: 'John',
|
|
|
|
# last_name: 'Doe',
|
|
|
|
# email: 'johndoe@example.com',
|
|
|
|
# password: 'password'
|
|
|
|
# }
|
|
|
|
# }
|
|
|
|
# should permit(:first_name, :last_name, :email, :password).
|
|
|
|
# for(:create, params: params).
|
|
|
|
# on(:user)
|
|
|
|
# end
|
2014-01-23 18:07:36 +00:00
|
|
|
# end
|
|
|
|
#
|
|
|
|
# If your action requires query parameters in order to work, then you'll
|
|
|
|
# need to supply them:
|
|
|
|
#
|
|
|
|
# class UsersController < ApplicationController
|
|
|
|
# def update
|
|
|
|
# user = User.find(params[:id])
|
|
|
|
#
|
|
|
|
# if user.update_attributes(user_params)
|
|
|
|
# # ...
|
|
|
|
# else
|
|
|
|
# # ...
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# private
|
|
|
|
#
|
|
|
|
# def user_params
|
|
|
|
# params.require(:user).permit(
|
|
|
|
# :first_name,
|
|
|
|
# :last_name,
|
|
|
|
# :email,
|
|
|
|
# :password
|
|
|
|
# )
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe UsersController do
|
|
|
|
# before do
|
|
|
|
# create(:user, id: 1)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# it do
|
2015-06-10 22:38:49 +00:00
|
|
|
# params = {
|
|
|
|
# id: 1,
|
|
|
|
# user: {
|
|
|
|
# first_name: 'Jon',
|
|
|
|
# last_name: 'Doe',
|
|
|
|
# email: 'jondoe@example.com',
|
|
|
|
# password: 'password'
|
|
|
|
# }
|
|
|
|
# }
|
2014-01-23 18:07:36 +00:00
|
|
|
# should permit(:first_name, :last_name, :email, :password).
|
2015-06-10 22:38:49 +00:00
|
|
|
# for(:update, params: params).
|
Permit matcher now supports subparameters
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.
```
2015-02-19 18:35:45 +00:00
|
|
|
# on(:user)
|
2014-01-23 18:07:36 +00:00
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # Test::Unit
|
|
|
|
# class UsersControllerTest < ActionController::TestCase
|
|
|
|
# setup do
|
|
|
|
# create(:user, id: 1)
|
|
|
|
# end
|
|
|
|
#
|
2015-06-10 22:38:49 +00:00
|
|
|
# should "(for PATCH #update) restrict parameters on :user to :first_name, :last_name, :email, and :password" do
|
|
|
|
# params = {
|
|
|
|
# id: 1,
|
|
|
|
# user: {
|
|
|
|
# first_name: 'Jon',
|
|
|
|
# last_name: 'Doe',
|
|
|
|
# email: 'jondoe@example.com',
|
|
|
|
# password: 'password'
|
|
|
|
# }
|
|
|
|
# }
|
|
|
|
# should permit(:first_name, :last_name, :email, :password).
|
|
|
|
# for(:update, params: params).
|
|
|
|
# on(:user)
|
|
|
|
# end
|
2014-01-23 18:07:36 +00:00
|
|
|
# end
|
|
|
|
#
|
|
|
|
# Finally, if you have an action that isn't one of the seven resourceful
|
|
|
|
# actions, then you'll need to provide the HTTP verb that it responds to:
|
|
|
|
#
|
|
|
|
# Rails.application.routes.draw do
|
|
|
|
# resources :users do
|
|
|
|
# member do
|
|
|
|
# put :toggle
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# class UsersController < ApplicationController
|
|
|
|
# def toggle
|
|
|
|
# user = User.find(params[:id])
|
|
|
|
#
|
|
|
|
# if user.update_attributes(user_params)
|
|
|
|
# # ...
|
|
|
|
# else
|
|
|
|
# # ...
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# private
|
|
|
|
#
|
|
|
|
# def user_params
|
|
|
|
# params.require(:user).permit(:activated)
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe UsersController do
|
|
|
|
# before do
|
|
|
|
# create(:user, id: 1)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# it do
|
2015-06-10 22:38:49 +00:00
|
|
|
# params = { id: 1, user: { activated: true } }
|
Permit matcher now supports subparameters
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.
```
2015-02-19 18:35:45 +00:00
|
|
|
# should permit(:activated).
|
2015-06-10 22:38:49 +00:00
|
|
|
# for(:toggle, params: params, verb: :put).
|
Permit matcher now supports subparameters
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.
```
2015-02-19 18:35:45 +00:00
|
|
|
# on(:user)
|
2014-01-23 18:07:36 +00:00
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # Test::Unit
|
|
|
|
# class UsersControllerTest < ActionController::TestCase
|
|
|
|
# setup do
|
|
|
|
# create(:user, id: 1)
|
|
|
|
# end
|
|
|
|
#
|
2015-06-10 22:38:49 +00:00
|
|
|
# should "(for PUT #toggle) restrict parameters on :user to :activated" do
|
|
|
|
# params = { id: 1, user: { activated: true } }
|
|
|
|
# should permit(:activated).
|
|
|
|
# for(:toggle, params: params, verb: :put).
|
|
|
|
# on(:user)
|
|
|
|
# end
|
2014-01-23 18:07:36 +00:00
|
|
|
# end
|
|
|
|
#
|
2015-02-19 17:35:20 +00:00
|
|
|
# @return [PermitMatcher]
|
2014-01-23 18:07:36 +00:00
|
|
|
#
|
2014-04-20 02:48:43 +00:00
|
|
|
def permit(*params)
|
2015-02-19 17:35:20 +00:00
|
|
|
PermitMatcher.new(params).in_context(self)
|
2014-01-24 21:14:49 +00:00
|
|
|
end
|
|
|
|
|
2014-01-23 18:07:36 +00:00
|
|
|
# @private
|
2015-02-19 17:35:20 +00:00
|
|
|
class PermitMatcher
|
2014-04-16 08:58:05 +00:00
|
|
|
attr_writer :stubbed_params
|
2014-01-24 21:17:46 +00:00
|
|
|
|
2014-04-20 02:48:43 +00:00
|
|
|
def initialize(expected_permitted_params)
|
Permit matcher now supports subparameters
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.
```
2015-02-19 18:35:45 +00:00
|
|
|
@expected_permitted_params = expected_permitted_params
|
2014-04-20 02:48:43 +00:00
|
|
|
@action = nil
|
|
|
|
@verb = nil
|
|
|
|
@request_params = {}
|
Permit matcher now supports subparameters
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.
```
2015-02-19 18:35:45 +00:00
|
|
|
@subparameter = nil
|
|
|
|
@parameters_doubles = ParametersDoubles.new
|
2014-01-24 21:14:49 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def for(action, options = {})
|
|
|
|
@action = action
|
2014-04-20 02:48:43 +00:00
|
|
|
@verb = options.fetch(:verb, default_verb)
|
|
|
|
@request_params = options.fetch(:params, {})
|
2014-01-24 21:14:49 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
Permit matcher now supports subparameters
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.
```
2015-02-19 18:35:45 +00:00
|
|
|
def add_params(params)
|
|
|
|
request_params.merge!(params)
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def on(subparameter)
|
|
|
|
@subparameter = subparameter
|
|
|
|
@parameters_doubles = SliceOfParametersDoubles.new(subparameter)
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2014-01-24 21:14:49 +00:00
|
|
|
def in_context(context)
|
|
|
|
@context = context
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2014-04-04 18:03:22 +00:00
|
|
|
def description
|
2015-09-29 16:48:48 +00:00
|
|
|
"(for #{verb.upcase} ##{action}) " + expectation
|
2014-04-04 18:03:22 +00:00
|
|
|
end
|
|
|
|
|
2014-04-16 08:58:05 +00:00
|
|
|
def matches?(controller)
|
|
|
|
@controller = controller
|
2014-04-20 02:48:43 +00:00
|
|
|
ensure_action_and_verb_present!
|
2014-01-24 21:14:49 +00:00
|
|
|
|
Permit matcher now supports subparameters
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.
```
2015-02-19 18:35:45 +00:00
|
|
|
parameters_doubles.register
|
|
|
|
|
2014-04-20 02:48:43 +00:00
|
|
|
Doublespeak.with_doubles_activated do
|
|
|
|
context.__send__(verb, action, request_params)
|
|
|
|
end
|
|
|
|
|
|
|
|
unpermitted_params.empty?
|
2014-01-24 21:14:49 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def failure_message
|
Permit matcher now supports subparameters
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.
```
2015-02-19 18:35:45 +00:00
|
|
|
"Expected #{verb.upcase} ##{action} to #{expectation},\nbut #{reality}."
|
2014-01-24 21:14:49 +00:00
|
|
|
end
|
|
|
|
|
2014-01-24 21:17:46 +00:00
|
|
|
def failure_message_when_negated
|
Permit matcher now supports subparameters
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.
```
2015-02-19 18:35:45 +00:00
|
|
|
"Expected #{verb.upcase} ##{action} not to #{expectation},\nbut it did."
|
2014-01-24 21:14:49 +00:00
|
|
|
end
|
|
|
|
|
2014-06-24 15:57:52 +00:00
|
|
|
protected
|
2014-01-24 21:17:46 +00:00
|
|
|
|
Permit matcher now supports subparameters
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.
```
2015-02-19 18:35:45 +00:00
|
|
|
attr_reader :controller, :double_collections_by_param, :action, :verb,
|
|
|
|
:request_params, :expected_permitted_params, :context, :subparameter,
|
|
|
|
:parameters_doubles
|
|
|
|
|
|
|
|
def expectation
|
|
|
|
message = 'restrict parameters '
|
|
|
|
|
|
|
|
if subparameter
|
2015-09-29 16:48:48 +00:00
|
|
|
message << "on #{subparameter.inspect} "
|
Permit matcher now supports subparameters
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.
```
2015-02-19 18:35:45 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
message << 'to ' + format_param_names(expected_permitted_params)
|
2014-01-24 21:14:49 +00:00
|
|
|
|
Permit matcher now supports subparameters
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.
```
2015-02-19 18:35:45 +00:00
|
|
|
message
|
|
|
|
end
|
2014-01-24 21:14:49 +00:00
|
|
|
|
Permit matcher now supports subparameters
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.
```
2015-02-19 18:35:45 +00:00
|
|
|
def reality
|
|
|
|
if actual_permitted_params.empty?
|
|
|
|
'it did not restrict any parameters'
|
|
|
|
else
|
|
|
|
'the restricted parameters were ' +
|
|
|
|
format_param_names(actual_permitted_params) +
|
|
|
|
' instead'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def format_param_names(param_names)
|
|
|
|
param_names.map(&:inspect).to_sentence
|
2014-01-24 21:14:49 +00:00
|
|
|
end
|
|
|
|
|
2014-04-20 02:48:43 +00:00
|
|
|
def actual_permitted_params
|
Permit matcher now supports subparameters
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.
```
2015-02-19 18:35:45 +00:00
|
|
|
parameters_doubles.permitted_params
|
2014-01-24 21:14:49 +00:00
|
|
|
end
|
|
|
|
|
2014-04-20 02:48:43 +00:00
|
|
|
def permit_called?
|
|
|
|
actual_permitted_params.any?
|
2014-01-24 21:14:49 +00:00
|
|
|
end
|
|
|
|
|
2014-04-20 02:48:43 +00:00
|
|
|
def unpermitted_params
|
|
|
|
expected_permitted_params - actual_permitted_params
|
2014-01-24 21:17:46 +00:00
|
|
|
end
|
2014-01-24 21:14:49 +00:00
|
|
|
|
2014-04-20 02:48:43 +00:00
|
|
|
def verified_permitted_params
|
|
|
|
expected_permitted_params & actual_permitted_params
|
2014-04-06 15:18:12 +00:00
|
|
|
end
|
|
|
|
|
2014-01-24 21:14:49 +00:00
|
|
|
def ensure_action_and_verb_present!
|
|
|
|
if action.blank?
|
|
|
|
raise ActionNotDefinedError
|
|
|
|
end
|
2014-04-20 02:48:43 +00:00
|
|
|
|
2014-01-24 21:14:49 +00:00
|
|
|
if verb.blank?
|
|
|
|
raise VerbNotDefinedError
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-04-20 02:48:43 +00:00
|
|
|
def default_verb
|
|
|
|
case action
|
|
|
|
when :create then :post
|
|
|
|
when :update then RailsShim.verb_for_update
|
2014-04-16 08:58:05 +00:00
|
|
|
end
|
2014-01-24 21:17:46 +00:00
|
|
|
end
|
|
|
|
|
2014-04-20 02:48:43 +00:00
|
|
|
def param_names_as_sentence
|
|
|
|
expected_permitted_params.map(&:inspect).to_sentence
|
2014-01-24 21:17:46 +00:00
|
|
|
end
|
|
|
|
|
Permit matcher now supports subparameters
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.
```
2015-02-19 18:35:45 +00:00
|
|
|
# @private
|
|
|
|
class ParametersDoubles
|
|
|
|
def self.permitted_params_within(double_collection)
|
|
|
|
double_collection.calls_to(:permit).map(&:args).flatten
|
|
|
|
end
|
|
|
|
|
|
|
|
def initialize
|
|
|
|
klass = ::ActionController::Parameters
|
|
|
|
@double_collection = Doublespeak.double_collection_for(klass)
|
|
|
|
end
|
|
|
|
|
|
|
|
def register
|
|
|
|
double_collection.register_proxy(:permit)
|
|
|
|
end
|
|
|
|
|
|
|
|
def permitted_params
|
|
|
|
ParametersDoubles.permitted_params_within(double_collection)
|
|
|
|
end
|
|
|
|
|
|
|
|
protected
|
|
|
|
|
|
|
|
attr_reader :double_collection
|
|
|
|
end
|
|
|
|
|
|
|
|
# @private
|
|
|
|
class SliceOfParametersDoubles
|
|
|
|
TOP_LEVEL = Object.new
|
|
|
|
|
|
|
|
def initialize(subparameter)
|
|
|
|
klass = ::ActionController::Parameters
|
|
|
|
|
|
|
|
@subparameter = subparameter
|
|
|
|
@double_collections_by_param = {
|
|
|
|
TOP_LEVEL => Doublespeak.double_collection_for(klass)
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def register
|
|
|
|
top_level_collection = double_collections_by_param[TOP_LEVEL]
|
|
|
|
double_permit_on(top_level_collection)
|
|
|
|
double_require_on(top_level_collection)
|
|
|
|
end
|
|
|
|
|
|
|
|
def permitted_params
|
|
|
|
if double_collections_by_param.key?(subparameter)
|
|
|
|
ParametersDoubles.permitted_params_within(
|
|
|
|
double_collections_by_param[subparameter]
|
|
|
|
)
|
|
|
|
else
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
protected
|
|
|
|
|
|
|
|
attr_reader :subparameter, :double_collections_by_param
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def double_permit_on(double_collection)
|
|
|
|
double_collection.register_proxy(:permit)
|
|
|
|
end
|
|
|
|
|
|
|
|
def double_require_on(double_collection)
|
|
|
|
double_collections_by_param = @double_collections_by_param
|
|
|
|
require_double = double_collection.register_proxy(:require)
|
|
|
|
|
|
|
|
require_double.to_return do |call|
|
|
|
|
param_name = call.args.first
|
|
|
|
params = call.return_value
|
|
|
|
double_collections_by_param[param_name] ||=
|
|
|
|
double_permit_against(params)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def double_permit_against(params)
|
|
|
|
klass = params.singleton_class
|
|
|
|
|
|
|
|
Doublespeak.double_collection_for(klass).tap do |double_collection|
|
|
|
|
double_permit_on(double_collection)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-01-23 18:07:36 +00:00
|
|
|
# @private
|
2014-04-16 08:58:05 +00:00
|
|
|
class ActionNotDefinedError < StandardError
|
|
|
|
def message
|
|
|
|
'You must specify the controller action using the #for method.'
|
|
|
|
end
|
2014-01-24 21:14:49 +00:00
|
|
|
end
|
|
|
|
|
2014-01-23 18:07:36 +00:00
|
|
|
# @private
|
2014-04-16 08:58:05 +00:00
|
|
|
class VerbNotDefinedError < StandardError
|
|
|
|
def message
|
2014-04-20 02:48:43 +00:00
|
|
|
'You must specify an HTTP verb when using a non-RESTful action. For example: for(:authorize, verb: :post)'
|
2014-04-16 08:58:05 +00:00
|
|
|
end
|
2014-01-24 21:14:49 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|