Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby 2 Keyword Arguments and ActionController::Parameters

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?

like image 604
Kyle Decot Avatar asked Jan 27 '14 23:01

Kyle Decot


People also ask

How do you pass keyword arguments in Ruby?

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 is keyword arguments in Ruby?

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.

What are strong Parameters in Ruby?

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.

What is params permit in Ruby on Rails?

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.


2 Answers

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)

like image 129
morgoth Avatar answered Oct 03 '22 04:10

morgoth


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
like image 30
derrek Avatar answered Oct 03 '22 04:10

derrek