Syntactic Fructose
Syntactic Fructose

Reputation: 20076

Rails 5 form for submitting array passed as string

I have a serialized Array field in my model called :cords, and part of my form looks like so:

<%= form_for @group do |f| %>
    ....
    <p>
        <%= f.label :cords %><br>
        <%= f.text_field :cords, name: "group[cords][]" %>
    </p>
    ....

Then inside my controller, I attempt to use it like so

    @group = Group.new(params.require(:group).permit(:name, :members, cords: [] ))

    if @group.save
        redirect_to @group
    else
        render 'new'
    end

This doesn't seem to work though, because when I type in some array like [[1,2],[3,4]] I see the SQL insert is

INSERT INTO "groups" ("cords", "name", "members", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?)  [["cords", "---\n- \"[[1,2],[3,4],[5.5,6]]\"\n"], ["name", "GG"], ["members", 55], ["created_at", "2017-06-12 02:13:37.462355"], ["updated_at", "2017-06-12 02:13:37.462355"]]

Why is cords submitted as ["cords", "---\n- \"[[1,2],[3,4],[5.5,6]]\"\n"]? I believe I'm doing something wrong with my actual form setup

Upvotes: 3

Views: 1633

Answers (1)

wyde19
wyde19

Reputation: 418

Explanation of problem:

Rails serialized column indeed allow us to store complex data types such as array, but they do so in YAML format by default. What you do is pass a text as group[cords][] param. So, what you should get in your params is {group: {cords: [ "[[1,2],[3,4],[5.5,6]]" ]}}. Take notice, that cords is array of one element, which is string you pass to it: "[[1,2],[3,4],[5.5,6]]"

That array gets serialized in compliance with YAML scalar syntax: "---\n- \"[[1,2],[3,4],[5.5,6]]\"\n". Let's look at it up close, first substituting newlines:

---
- "[[1,2],[3,4],[5.5,6]]"
#here also newline

Three hyphens indicate start of YAML document.

Array is packed like this:

- "first_element"
- "second_element"
- "third_element"

Is equal to ["first_element", "second_element", "third_element"]

Backslash before quotation marks just escape those in SQL denoting that quotes are part of the string.

So, as you can see, rails do just what it is supposed / you told it to do: it gets single string as one element of array and it stores one element of array, containing that string.

Solution:

What you should do if you still want to get user input as array is transform that manually.

  • Drop the [] in text_field: f.text_field :cords, name: "group[cords]"
  • In controller, transform that text into ruby array, for example like this:
def cords_to_a
  # get the params[:group][:cords], if it's empty use empty array string
  cords = params.dig(:group, :cords).presence || "[]"
  # parse it, possibly you also want to handle parse exceptions
  # and transform parsed array as something else
  # also, user will be able to save any json object
  # to your serialized field, and it will be parsed
  # accordingly: either to array or to hash
  JSON.parse cords
end

def group_params
  params.require(:group).permit(:name, :members).merge({cords: cords_to_a})
end

and after that use Group.new(group_params)

Take notice: you still will get a "strange" string in your INSERT statement, because that will contain YAML-encoded array: essentially, a string.

Further reading:

Upvotes: 6

Related Questions