14

If I have this params for adding to the URL

params = { name: 'John Key' }

and use the method to_param:

params.to_param
 => "name=John+Key"

The point is that '+' is not correctly read by the service used and is needed '%20' instead name=John%20Key: When to encode space to plus (+) or %20?

Is there a way to return the params with '%20' without using gsub?

Community
  • 1
  • 1
AlexLarra
  • 841
  • 5
  • 18
  • 1
    You misread the answers to the question linked. In _query_ plus signs are perfectly valid, treated as spaces and therefore there is no such redundant Rails helper existing. – Aleksei Matiushkin Jul 13 '16 at 14:06
  • 1
    I need the params for a url of typeform. It does not treat well the '+', but it does with '%20' – AlexLarra Jul 13 '16 at 14:09

6 Answers6

8

I would recommend just sticking to the use of a gsub, perhaps with a comment to explain the need for such behaviour.

While you could solve the problem by use of URI.escape, it is supposedly deprecated, as it does not fully conform to RFC specs. See here for a great write-up on it.

Hash#to_param is an alias of Hash#to_query, which calls Object#to_query. The simplest example to demonstrate this + vs %20 issue is:

'John Key'.to_query(:name) # => "name=John+Key"

The implementation of Object#to_query is:

def to_query(key)
  "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
end

And so, we find that:

CGI.escape("John Key") # => "John+Key"

Hence, this is why I have referenced the differences between CGI.escape and URI.escape.

Community
  • 1
  • 1
Tom Lord
  • 27,404
  • 4
  • 50
  • 77
3

How about

URI.encode 'John Smith'
# => John%20Smith
OneChillDude
  • 7,856
  • 10
  • 40
  • 79
3

not really.

Suggest using: your_params_hash.to_query.gsub("+", "%20")

Blair Anderson
  • 19,463
  • 8
  • 77
  • 114
2

Have you tried using uri? e.g.

require 'uri' URI.escape('John Smith')

The result in this example should be John%20Smith

Realized from other post that URI.escape is technically deprecated. Where h stands for hash, and k is the key in the hash, try this:

params.keys.inject({}) {|h, k| h[k] = ERB::Util.url_encode(params[k]); h }

Julie
  • 1,941
  • 3
  • 17
  • 30
  • Yes, it works for the value of a param. But in this case I want to get a group of params with '%20'. Like to_param method but not returning the whitespaces with '+' ```name=John%20Key``` – AlexLarra Jul 13 '16 at 14:35
1

Expanding on Julie's answer to achieve the string of encoded values rather than a hash.

With the depreciation of URI.encode it is possible to achieve this with ERB::Util.url_encode, however you need to encode just the key and value pairs, not the full parameter, then join them together.


params = { name: 'John Key' }

# not what we want
ERB::Util.url_encode(params) # => "%7B%3Aname%3D%3E%22John%20Key%22%7D"

Here is a (pretty long) command that will work:

# Construct a new object (a string here) and build our params into it correctly.
# k = key of hash. 
# v = value of hash. 
# a = array created in each_with_object.

params.each_with_object([]) { |(k, v), a| a << [ERB::Util.url_encode(k), ERB::Util.url_encode(v)].join('=') }.join('&')

# => "name=John%20Key"

With more params:

params = { name: 'John Key', occupation: 'Web Developer!', 'Bad Key' => 'but works' }

params.each_with_object([]) { |(k, v), a| a << [ERB::Util.url_encode(k), ERB::Util.url_encode(v)].join('=') }.join('&')

# => "name=John%20Key&occupation=Web%20Developer%21&Bad%20Key=but%20works"

compared to Hash.to_query

params.to_query

# => "Bad+Key=but+works&name=John+Key&occupation=Web+Developer%21"
Kyle Ratliff
  • 558
  • 6
  • 14
0

If you want a literal percent encoding of a string without context (e.g. for RFC 5987 encoding of something like a Content-Disposition HTTP header's filename*=... value) then use of URI.escape / URI.encode (its alias) would have been correct. The core library documentation's suggested replacements ARE WRONG in this case, as they are not equivalent:

  • is converted to +, not %20
  • + is converted to %2B, not left as +

Some posts about this suggest a gsub to hack the string back towards the original, but this is far simpler:

...toggle source to see how it works - it's just a blunt encoder without context. That is exactly what you sometimes want when you know what you're doing, so let's hope it doesn't get removed from, or deprecated within the standard library.

If I get the time I intend to submit a patch to the core library docs that lists the three (not two) possible alternatives and gives more information on when you would use each of them. Right now, it's clear from the very large number of Google hits about this, and large number of at-best misleading answers, that the community is dangerously confused by the status quo.

Community
  • 1
  • 1
Andrew Hodgkinson
  • 4,379
  • 3
  • 33
  • 43