I have a rails 4 application that is running on ruby 2.1. I have a User
model that looks something like
class User < ActiveModel::Base
def self.search(query: false, active: true, **extra)
# ...
end
end
As you can see in the search method I am attempting to use the new keyword arguments feature of ruby 2.
The problem is that when I call this code from in my controller all values get dumped into query
.
params
{"action"=>"search", "controller"=>"users", query: "foobar" }
Please note that this is a ActionController::Parameters object and not a hash as it looks
UsersController
def search
@users = User.search(params)
end
I feel that this is because params is a ActionController::Parameters
object and not a hash. However even calling to_h
on params when passing it in dumps everything into query
instead of the expected behavior. I think this is because the keys are now strings instead of symbols.
I know that I could build a new hash w/ symbols as the keys but this seems to be more trouble than it's worth. Ideas? Suggestions?
So when you want to pass keyword arguments, you should always use foo(k: expr) or foo(**expr) . If you want to accept keyword arguments, in principle you should always use def foo(k: default) or def foo(k:) or def foo(**kwargs) .
What are keyword arguments? Keyword arguments are a feature in Ruby 2.0 and higher. They're an alternative to positional arguments, and are really similar (conceptually) to passing a hash to a function, but with better and more explicit errors.
Strong Parameters, aka Strong Params, are used in many Rails applications to increase the security of data sent through forms. Strong Params allow developers to specify in the controller which parameters are accepted and used.
In Rails, strong params provide an interface for protecting attributes from the end-user assignment. We can specify required attributes and neglect unnecessary attributes to be used in the Active model mass assignment. However, in Rails 6, there was an issue with permitting nested hash with numeric keys.
Keywords arguments must be passed as hash with symbols, not strings:
class Something
def initialize(one: nil)
end
end
irb(main):019:0> Something.new("one" => 1)
ArgumentError: wrong number of arguments (1 for 0)
ActionController::Parameters
inherits from ActiveSupport::HashWithIndifferentAccess
which defaults to string keys:
a = HashWithIndifferentAccess.new(one: 1)
=> {"one"=>1}
To make it symbols you can call symbolize_keys
method. In your case: User.search(params.symbolize_keys)
I agree with Morgoth, however, with rails ~5 you will get a Deprecation Warning because ActionController::Parameters no longer inherits from hash. So instead you can do:
params.to_unsafe_hash.symbolize_keys
or if you have nested params as is often the case when building api endpoints:
params.to_unsafe_hash.deep_symbolize_keys
You might add a method to ApplicationController that looks something like this:
def unsafe_keyworded_params
@_unsafe_keyworded_params ||= params.to_unsafe_hash.deep_symbolized_keys
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With