0
[1] pry(main)> o.items
=> [{"78"=>"{\"size\"=>\"96\", \"side\"=>[]}"}]

[2] pry(main)> o.items[0]
=> {"78"=>"{\"size\"=>\"96\", \"side\"=>[]}"}

[3] pry(main)> o.items[0]['78']
=> "{\"size\"=>\"96\", \"side\"=>[]}"

[4] pry(main)> o.items[0]['78']["side"]
=> "side"

Isn't line 4 supposed to return an empty array? How come its returning "side"?

EDIT

I found this happens after saving the array of hashes (it is an array of hstore on postgresql).

E.g This returns a hash as intended.

o.items << {78 => {"size" => 1, "side" => []}}
o.items
=> [{"78"=> {"size"=>"1", "side"=>[]}}]  

But after saving it

o.save
o.items
=> [{"78"=>"{\"size\"=>\"96\", \"side\"=>[]}"}]

I ended up doing eval(o.items[0]['78']) to convert the string back into a hash before making any changes to the hash and making updates. This seems very unnecessary, are there better options?

6
  • 4
    Look closely. o.items[0]['78'] is a string, not a hash. Commented Oct 6, 2016 at 23:28
  • @Aetherus awesome, thanks Commented Oct 6, 2016 at 23:31
  • @Aetherus That seems to answer the question, you should probably write it up as an answer so it's more visible. Commented Oct 6, 2016 at 23:33
  • How did it end up this way? Commented Oct 7, 2016 at 0:29
  • "foobar"["oba"] is "oba", as is "foobar"[/o.a/]. You searched for "side" in a string, and it was found. Commented Oct 7, 2016 at 0:58

2 Answers 2

2

As the commenters pointed out, the problem lies in the data type. You thought that the value was a hash when it fact it was a string. The escaped double quotes are the clues here.

"{\"size\"=>\"96\", \"side\"=>[]}"

vs

{"size"=>"96", "side"=>[]}

See the difference?

When you have a string and access it using the String#[] method, it returns that string when it is present. The documentation says:

If a match_str is given, that string is returned if it occurs in the string.

Check this link for more details: http://ruby-doc.org/core-2.3.0/String.html#method-i-5B-5D

Sign up to request clarification or add additional context in comments.

1 Comment

I've added more info about how this happened in my question. It seems like the inside of the hash was converted into a string after saving to my postgresql database. Is the only way to go about this to convert the string back to a hash to make updates to it?
1

If this is being saved into a Rails property then you need to handle that sort of data properly. This is best done using one of the following methods:

  • Using a JSON column type to store arbitrary structures.
  • Flagging the column with serialize to handle arbitrary data.

MySQL 5.7 and Postgres 9.3 or better support JSON as a column type so I'd steer in that direction if possible. If that's not an option, use a regular TEXT column and the serialize option.

That will save your structure in a format that ActiveRecord will reconstitute back in to its original form. You won't have to deal with the stringified hash.

What you were inadvertently tripping here was the String#[] method which can take a variety of options, but one of which is a substring to match. In your case that Ruby hash-as-string contains the phrase you're looking for, just as this works:

string = "ruby has strings"

string["ruby"]
# => "ruby"

string["test"]
# => nil

The [] method is used for all sorts of things as a matter of convenience, though this particular usage is often overlooked.

WARNING: Using eval to reconstitute a hash from a string is really a last resort option and should be avoided if there's any other way, no matter how awkward or inconvenient it is. Evaluating arbitrary strings is a massive liability as if anything should get into that which could be evaluated as code it's very easy to compromise your system.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.