Skip to content

Instantly share code, notes, and snippets.

@DonSchado
Last active December 22, 2015 20:18
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save DonSchado/6525126 to your computer and use it in GitHub Desktop.
Save DonSchado/6525126 to your computer and use it in GitHub Desktop.
Thoughtbot retracted their initial implementation of strong parameters matchers in v2.0.0 of shoulda-matchers, so we decided to build our own until new official ones are released. The following is a small matcher for testing what params should be permitted in controllers. The matcher's syntax is based on validation matchers. If you're not follow…
# assuming your subject is the UsersController with a method user_params
describe UsersController do
describe "params" do
# per default the matcher extracts the subject and params method
it { should permit_params(:email, :name, :role) }
# to overwrite the params method use explicit .params_method()
it { should permit_params(:first_name, :last_name).params_method(:other_user_params) }
# to overwrite the class use explicit .for_class()
it { should permit_params(:email, :name).for_class(SpecialUser) }
# overwrite both
it { should permit_params(:name).params_method(:my_params_method).for_class(SuperUser) }
# or use the expect syntax if you like
it { expect(permit_params(:email, :name)).to be_true }
# and since it's RSpec you can also use specify if you like
specify { expect(permit_params(:role).for_class(User).to be_false }
end
end
module StrongParameterMatcher
class PermitMatcher
attr_reader :permitted_params, :errors_protected, :errors_permitted
def initialize(permitted_params)
@permitted_params = permitted_params
@errors_protected = []
@errors_permitted = []
end
def for_class(klass)
@klass_name = klass.name
@model = klass
self
end
def params_method(name)
@params_method = name
self
end
def matches?(controller)
default_klass_name(controller)
default_model
default_params_method
protected_params.each do |param|
controller.params = action_controller_parameters(param)
errors_protected << param if controller.send(@params_method)[param.to_sym]
end
permitted_params.each do |param|
controller.params = action_controller_parameters(param)
errors_permitted << param unless controller.send(@params_method).has_key?(param)
end
return errors_protected.empty? && errors_permitted.empty?
end
def failure_message
msg = "expected "
if !errors_protected.empty?
msg << "#{errors_protected} to be protected but is permitted"
else
msg << "#{errors_permitted} to be permitted but is not"
end
end
def description
"permit #{permitted_params} through :#{@params_method}"
end
private
def protected_params
@model.column_names - permitted_params.map(&:to_s)
end
def action_controller_parameters(param)
ActionController::Parameters.new(@klass_name.underscore.to_sym => {param => "random"})
end
def default_klass_name(controller)
@klass_name ||= controller.class.name.gsub("Controller", "").classify
end
def default_model
@model ||= @klass_name.constantize
end
def default_params_method
@params_method ||= "#{@klass_name.underscore}_params".to_sym
end
end
def permit_params(*keys)
PermitMatcher.new(keys)
end
end
RSpec.configure do |config|
config.include(StrongParameterMatcher)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment