ruby-on-railsrubycomposite-primary-key

Extract symbols from an Array that has been converted to a String?


I have the following string:

s = "[:test1, :test2]"

I want it to end up as an Array of symbols again:

[:test1, :test2]

I found two similar questions:

Ruby: Parsing a string representation of nested arrays into an Array?

Ruby convert string array to array object

But neither the JSON.parse or YAML.load work:

irb(main):043> test
=> "[:test1, :test2]"
irb(main):044> JSON.parse test
/Users/[...]/.rbenv/versions/3.2.2/lib/ruby/3.2.0/json/common.rb:216:in `parse':
unexpected token at ':test1, :test2]' (JSON::ParserError)
irb(main):045>
irb(main):045> YAML.load test
/Users/[...]/.rbenv/versions/3.2.2/gemsets/rails_test/gems/psych-5.1.1.1/lib/psych/parser.rb:62:in `_native_parse': (<unknown>):
did not find expected node content while parsing a flow node at line 1 column 2 (Psych::SyntaxError)

eval does work but is generally not recommended due to injection concerns.

irb(main):047> eval(test)
=> [:test1, :test2]

The only way I can find to do it is some basic string manipulation, but I was wondering if there was a better way built into Ruby that does not involve creating a custom function.

irb(main):046> test.delete("[]:").split(", ").map(&:to_sym)
=> [:test1, :test2]

Ruby 3.2.2 for reference.


Solution

  • You can clean up your example, but it's not guaranteed to work on anything that's not a flat array of symbols stored inside a string.

    s = "[:test1, :test2]"
    
    s = s.delete(":[]").split(?,).map { _1.strip.to_sym }
    => [:test1, :test2]
    

    However, it's entirely unclear how you're ending up with a String instead of an Array of Symbols, and nothing in the Rails Guide about composite keys explains how you're ending up in that state. I'm forced to assume you're getting data from form input or URI parameters (both of which are represented as strings) and not parsing the elements as separate column names so you can properly form the composite key from the two String values.

    The section on composite keys in forms says that the keys must be delimited by an underscore for the Rails magic to work, and you may also need to implement params.extract_value in your controller to access them properly. You didn't show any controller, view, or model code, so it seems like you're trying to patch up the improperly-formatted end result instead of focusing on the source of the incorrect data.

    Even if that's not the case, this is ultimately solving the wrong problem. You need to figure out why s contains invalid JSON, or why you aren't getting two separate keys as String objects (not Symbols) that you can then use to form your composite key properly.

    Solve for those things. The post facto String munging is addressing the symptom, not the cause.