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?
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
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:
Persist in database, store the session only in a cookie.