Storing/Retrieving values in a Rails cookie

2020-07-23 06:55发布

I created a SessionHelper module which looks more or less like this:

module SessionHelper

  def create_cookie
      cookies.signed[:token] = {
      :value => [],
      :expires => 1.hour.from_now
      }
  end

  def cookie_values
      cookies.signed[:token]
  end

  def add_value_to_cookie(value)
      cookies[:token] << value
  end

  def cookie_exists?
      cookies[:token].present?
  end

end

It then gets included to the Application controller and gets fired in a filter, before every request:

create_cookie unless cookie_exists?

My site does not use authentication, I would like to display the recent items that were created by a guest. So, in an Item controller I have something like this:

def create
  @item=Item.new(params[:item])
  if @item.save 
     add_value_to_cookie(@item.id)
     redirect_to @item
  else
     render :new
  end
end

The problem is as follows:

  • The before filter creates an empty cookie, I can confirm it's initial value is set to [] (OK)
  • After a user creates an item, I would expect the cookie Array to have an additional value, but instead it gets turned into a Nil, and nothing is present (BAD). It doesn't even throw an exception.

Where do I made an error?. How should I add new values to the cookie? Additionally, what is the prefered way to check the existance of a cookie? It seems that the #has_key? method is not available.

Some side questions:

Since I want to display links to the guest's items (stored in the cookie), which would have the form of:

link_to @item.title, item_path(@item.id)

Should I then just make the links in the #create action and store them in the cookie? Or should I keep just the IDs as I do know, to generate them? I am concerned that a cookie might not be able to store such links (thus I initially thought of storing just IDs)

Thanks in advance

EDIT: Seems like it is not possible to use a cookie as an array, and the workaround is to use some form of serialization. I settled on JSON

module SessionHelper

  def set_user_cookie
    cookies.signed[:token] = {
        :value => [].to_json,
        :expires => 1.hour.from_now
    }
  end

  def user_cookie_values
    load_user_cookie_data
  end


  def user_cookie_exists?
        cookies[:token].present?
  end

  def add_to_user_cookie(value)
    current = load_user_cookie_data
    current << value
    cookies.signed[:token] = current.to_json
  end

  def remove_from_user_cookie(value)
    current = load_user_cookie_data
    current.delete(value)
    cookies.signed[:token] = current.to_json
  end

  private

  def load_user_cookie_data
    JSON.load(cookies.signed[:token])
  end

end

Although I'd still be interested in hearing what is the preferred way of storing content in the cookie. Pure IDs or Links?

2条回答
萌系小妹纸
2楼-- · 2020-07-23 07:07

I recently ran into a similar issue and solved it by creating a CookieCollection class that hides all the cookie hash interaction and allows you to add and list from the collection like you would with an Array. It's exposed through a controller method and helper.

http://www.swards.net/2013/08/cookie-collection-objects.html

查看更多
冷血范
3楼-- · 2020-07-23 07:13

If the Item model is non-trivial, do not load it into a cookie. There's a cookie limit at about 2k and serialization of data is going to eat into that pretty quick. In any case, persistence should be done to a database (or something) and only the minimal amount of session state needs to go into a cookie. Items get created into a database. If you want to load items client side, use javascript.

Even if you have guest items, just load it into a database and mark it somehow. IE: put into a guest table, or have a column setting guest=1 or have a foreign_key to guest_items table or something. Really (even with anonymous auth), a session just has the current user ID (in your case, nothing). I assume you're trying to persist permanent cookies for "remember me" type functionality, that's fine. The abstraction layer is a little weird. The preferred way in a simple system is:

# in controller, bad idea
cookies['items'] << Item.new("whatever")

Persist in database, store the session only in a cookie.

查看更多
登录 后发表回答