Aaron Renoir
Aaron Renoir

Reputation: 4381

How do I prevent hash from escaping strings?

I have the following code that generates a hash used for testing the authentication of a internal Restful API.

The problem is the generated hash is returning escaped values.

The json_headers method should return something like this:

{"Authorization"=>"Basic ZnBfZXJwOllrdGh4aHRSZTZWM1d1d09oeVlmcW0xTg==", "Accept"=>"application/json"} 

But is returning:

{\"Authorization\"=>\"Basic ZnBfZXJwOllrdGh4aHRSZTZWM1d1d09oeVlmcW0xTg==\\n\", \"Accept\"=>\"application/json\"}

Does anyone know an easy way to return raw key/values from a Ruby hash?

Here is the code:

module ERP
  class CustomersMock

    def json_headers
      authorization_hash.merge("Accept"=>"application/json")
    end

    def authorization_hash 
      {"Authorization" => "Basic #{base64_user_pass}"}
    end

    def user_pass
       # user:password
       [ ERP_CONF['user'], ERP_CONF['password'] ].join(':')
    end

    def base64_user_pass
      Base64.encode64(user_pass)
    end

    def add(customer)
      customers << customer
    end        

    def customers
      []
    end         

    def run
      ActiveResource::HttpMock.respond_to do |mock|
        customers.each do |customer|
          mock.get "/customers/#{customer.id}.json", json_headers, customer.to_json
        end
      end
    end
  end
end

For some reason, if I hard-code the headers hash in place, they work, but if I call the json_headers method, the respond_to block converts them to a string and escapes the values.

Here is the rspec failure:

Could not find a response recorded for <GET: /api/customers/1011.json [{"Authorization"=>"Basic ZnBfZXJwOllrdGh4aHRSZTZWM1d1d09oeVlmcW0xTg==", "Accept"=>"application/json"}] ()> - Responses recorded are: ["<GET: /api/customers/1011.json [{\"Authorization\"=>\"Basic ZnBfZXJwOllrdGh4aHRSZTZWM1d1d09oeVlmcW0xTg==\\n\", \"Accept\"=>\"application/json\"}] ()>"]

Upvotes: 3

Views: 4390

Answers (2)

Aaron Renoir
Aaron Renoir

Reputation: 4381

This was not a escaping problem. I thought it was based on the rspec failing test output:

Could not find a response recorded for 
<GET: /api/customers/1011.json [{"Authorization"=>"Basic ZnBfZXJwOllrdGh4aHRSZTZWM1d1d09oeVlmcW0xTg==", "Accept"=>"application/json"}] ()> 

- Responses recorded are: [
"<GET: /api/customers/1011.json [{\"Authorization\"=>\"Basic ZnBfZXJwOllrdGh4aHRSZTZWM1d1d09oeVlmcW0xTg==\\n\", \"Accept\"=>\"application/json\"}] ()>"
]

It looks like the only difference is the headers hash is escaped in one and not in the other.

However, it is not escaping the line ending at the end of the "Authorization" value, so the fix is to strip the value:

def base64_user_pass
  Base64.encode64(user_pass).strip
end

Upvotes: 0

Patrick Oscity
Patrick Oscity

Reputation: 54684

Escaped quotes are a bit confusing at first. I guess you were testing this from a REPL such as IRB or Pry. Your method actually contains normal double quotes – they are only escaped for output because double quotes are already used to denote the beginning and end of a string. That means that when you actually want to store the double quote character " in a string, you need to type "\"" because if you were typing """ you would actually end the string with the second ". You can see what a string "really" contains by actually printing it with puts. Let me demonstrate this with an example:

require 'json'

h = {a: 123, b: 456}

h.to_json
#=> "{\"a\":123,\"b\":456}"       # note the escaped double quotes

puts h.to_json
# {"a":123,"b":456}               # when printed, quotes are no longer escaped

double_quote_1 = '"'              # another way to store " in a string
double_quote_2 = "\""
double_quote_1 == double_quote_2  # both have the same contents, a single "
#=> true

json1 = '{"a":123,"b":456}'       # less confusing way of writing the json from above
json2 = "{\"a\":123,\"b\":456}"
json1 == json2
#=> true

Upvotes: 3

Related Questions