How do I resolve the deprecation warning "Method to_hash is deprecated and will be removed in Rails 5.1"
Asked Answered
S

1

42

I'm trying to update to Rails 5, I'm getting the following deprecation warning:

DEPRECATION WARNING: Method to_hash is deprecated and will be removed in Rails 5.1, as ActionController::Parameters no longer inherits from hash. Using this deprecated behavior exposes potential security problems. If you continue to use this method you may be creating a security vulnerability in your app that can be exploited. Instead, consider using one of these documented methods which are not deprecated: http://api.rubyonrails.org/v5.0.0/classes/ActionController/Parameters.html (called from column_header at /Data/Projects/portal/trunk/app/helpers/application_helper.rb:114)

The line the warning is on looks like this:

    link_to(name,
            {
              action: action_name,
              params: params.merge({ order: key, page: nil })
            },
            {
              title: "Sort by this field",
            }) +

As you can see, I'm not calling to_hash. Maybe Rails is. Maybe some other gem is. I have no way to tell, because they didn't think it was worth providing a stack trace. (Pro tip - it usually is worth providing a stack trace!)

So anyway, I followed the link, planning to find a replacement, and the merge method does not appear to be deprecated, but maybe they simply forgot to document deprecated status, so I can't really be sure.

So what am I supposed to do to clear this?

Stereoscopy answered 2/8, 2016 at 2:31 Comment(8)
Is it params.merge or link_to that is calling to_hash?Actium
I had a look at params.merge at least, and it doesn't. Maybe link_to does... in which case I could potentially call this Rails' fault? I don't really know. (This is exactly why a stack trace would be useful - I would rather not have to dig through library sources to figure out who broke something)Stereoscopy
Debugger suggests that link_to calls it but I stepped in to investigate and it goes pretty deep. I managed to verify that url_for calls it, but can't figure out where. So should my view be that they shouldn't be doing that in their own library? I mean, I have passed a Parameters object into something that generates a link from parameters. It seems like it should be OK to do this.Stereoscopy
Are you sure you should specify params to url_for through a param named params, and not directly by passing the params at the top level, as in: link_to(name, {action: action_name, order: key, page: nil}.merge(params)?Huneycutt
If you really need to pass {params: params}, try {params: params.to_h}.Huneycutt
Then I'd be calling the method they're telling me not to call. I guess I can use to_unsafe_h though...Stereoscopy
Issue about this: github.com/rails/rails/issues/26415. It was closed recently (in Sep 2016) using a to_h conversion internally.Reborn
If you want to know where a method is being called, one hacky way is to temporarily monkey patch the method to log caller to a file of your choosing and then call super().Gymnasiarch
G
78

Use .to_h

You can call .to_h to get a safe hash, according to a comment on the Rails PR.

There are now three methods for converting parameters to a hash.

  • .to_h means "if I haven't called .permit, assume nothing is allowed."
  • .to_unsafe_h means "if I haven't called .permit, assume everything is allowed."
  • .to_hash is now ambiguous. Rails treats it like .to_unsafe_h, but prints a warning because you haven't explicitly said which of the two options above you wanted.

First, let's see what happens if you haven't called .permit. In a Rails 5.0 console:

> params = ActionController::Parameters.new({yes: "y", no: "n"})

> params.to_h
{} # empty hash because nothing has been permitted

> params.to_unsafe_h
{"yes"=>"y", "no"=>"n"} # raw values with no warning; you asked for it

> params.to_hash
# (puts deprecation warning - if you want unsafe values, say so)
{"yes"=>"y", "no"=>"n"} # returns raw values

However, if you call .permit first, there will be no way to get the non-permitted values.

> params = ActionController::Parameters.new({yes: "y", no: "n"})

> params = params.permit(:yes)
# (puts warning about unpermitted parameter :no)

> params.to_h
{"yes"=>"y"} # permitted values only

> params.to_unsafe_h
{"yes"=>"y"} # permitted values only

> params.to_hash
# (puts deprecation warning, but still safe)
{"yes"=>"y"} # permitted values only

So:

  1. Always use .permit to whitelist the values you expect
  2. Use .to_h to ensure that if you forgot step 1, nothing will get through
  3. If you really want the raw values, don't call .permit and call .to_unsafe_hash
  4. Don't call .to_hash because that's now ambiguous
Gymnasiarch answered 9/8, 2016 at 20:17 Comment(6)
I think with this, I will have to use to_unsafe_hash - to_h would, as you say, only return what's permitted, but I'm in library code where I don't know what params are permitted. So anything I do to use permit would require iterating all keys anyway, which seems about as bad as to_unsafe_hash. Plus, all I'm doing is generating a URL which will ultimately become params again. It's too bad that Parameters itself can't be used for url_for, but oh well.Stereoscopy
Hmm. If you're in library code, where are you getting the params? If an application is calling your library, they should call .permit before handing you the params, right? You can check that with params.permitted?. I'd be nervous about building a URL with arbitrary params, especially in library code where you don't know where they came from.Gymnasiarch
The params come from a controller, but the hash in question is used for filtering and sorting, and I guess I mustn't have been keen on duplicating code to permit the full list of all filters. I'll have to come up with some way to pull the list programmatically somehow. :/Stereoscopy
I don't understand why this answer was voted up since despite providing a nice explanation it doesn't answer the question at all (OP wanted to know - as I do - who/where called .to_hash in the first placeComte
@Comte The question says "I'm getting the following deprecation warning... (details) So what am I supposed to do to clear this?"Gymnasiarch
This is a very helpful, clear, and concise answer to this question. God bless you.Forfeit

© 2022 - 2024 — McMap. All rights reserved.