简体   繁体   中英

Why is my ruby object being converted to a Hash after saving it in a session and how do I retain it's status as an object?

I have an instance of a Board class that I am saving into a session. After I submit a form and try to retrieve the Board object from the session, the session[:board] is now a hash. It is probably easier to demonstrate then explain so here is my code.

the new method in my controller:

def new
    session[:player] = "X"
    session[:board] = Board.new
    @board = session[:board]
end

Here is the view:

<div id="board">
  <%= simple_format(@board.render) %>

</div>


<h2>Choose the column you would like to drop a piece in</h2>


  <%= form_tag( drop_piece_path, :method => "post" ) do %>

    <% 7.times do |col| %>
      <%= label_tag col %>
      <% unless @board.column_full?(col) %>
        <%= radio_button_tag(:column, col) %>
      <% end %>
    <% end %>


    <%= submit_tag("Enter move")%>
  <% end %>

<%= @board.class %>

At this point @board.class is a Board like I expect it to be.

After I submit the form to the following drop_piece method:

def drop_piece
    @board = session[:board]
    @board.drop_piece(params[:column].to_i, session[:player])
    save_session(@board)

end

I get this error:

undefined method `drop_piece' for #<Hash:0x007fe40bd070e0>

And @board.class and session[:board].class both result in Hash. And drop_piece is a method of my Board class so it makes sense that I can't call it on a Hash.

So somewhere along the way my Board object is being converted to a hash. Maybe I am just misusing sessions, but I tried googling the issue already and was not able to come up with anything. Thanks for the help.

You're not meant to stuff random objects into a Rails session. Sessions are essentially a key-value store of strings, so what you're seeing is automatic flattening of objects into a form that the session can manage.

You're better off converting your object to JSON before passing it to the session and decoding it after retrieving it. In your situation, you could also just create a constructor that takes all necessary fields of your Board object as parameters and reconstruct the Board within the receiver method or use Ruby's Marshal module to get what you want.

You can also store your Board in something more persistent (cache or even database) and just pass an ID to your session. The receiver can then retrieve the row based on ID and reconstruct the object.

When you use the cookie session store (the default), the default behaviour since rails 4.1 is to serialize session data using JSON. Earlier versions of rails used Marshal, which did allow storing of arbitrary ruby classes, but a number of ruby security vulnerabilities involving marshal made people wary of using this. You are also of risk of bugs when a newer version of an application loads data created by an older version.

You can revert to the old behaviour by setting

Rails.application.config.action_dispatch.cookies_serializer = :marshal

Or by using a different session store. However I would recommend that you instead stick to storing simple data in the session, or at the very least be explicit about what is stored rather than letting this conversion happen implicitly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM