Storing User Data in Session - Standard Practice - ruby-on-rails

I've seen some information on SO and Google regarding storing user data for sessions, but most have been for PHP and not Rails.
Additionally, I've watched Dangers of Model in Session on RailsCasts.
Let's say I have a user that logs in, and I want to access some basic preferences from the user like: zip code, height, weight, and perhaps, 10 other things that I would like to access later.
Should I?
Store those 10 things in a hashed session variable?
e.g., session[:user_prefs] = User.find(:first)
Just store the user's ID as a sesion variable, and then run queries later to access the zip code, height, weight, etc?
e.g., session[:user_id] = User.find(:first).id
Or, is there something entirely different that I should be doing?
I'm not sure what the standard coding practice or best practice would be for this scenario.
Any help would be great.

Option 2 is the way to go.
Option 1 is "No" because your session data will be out of sync with the database as soon as the user information gets updated. Say for example you store those ten fields in the session upon user login, and later in the application the user updates one of those ten fields, now the session data is out of sync with the database. You could define a function that updates the session data when one of the attributes changes but I think this adds unnecessary extra complexity to the application.
Option 3, I cannot think of anything that replaces the session for this requirement. There are other ways you could implement the session logic but they would just be your version of already provided(by Rails) implementation of session.

Related

Rails: working on temporary instance between requests and then commit changes to database

I have already read Rails - How do I temporarily store a rails model instance? and similar questions but I cannot find a successful answer.
Imagine I have the model Customer, which may contain a huge amount of information attached (simple attributes, data in other tables through has_many relation, etc...). I want the application's user to access all data in a single page with a single Save button on it. As the user makes changes in the data (i.e. he changes simple attributes, adds or deletes has_many items,...) I want the application to update the model, but without committing changes to the database. Only when the user clicks on Save, the model must be committed.
For achieving this I need the model to be kept by Rails between HTTP requests. Furthermore, two different users may be changing the model's data at the same time, so these temporary instances should be bound to the Rails session.
Is there any way to achieve this? Is it actually a good idea? And, if not, how can one design a web application in which changes in a model cannot be retained in the browser but in the server until the user wants to commit them?
EDIT
Based on user smallbutton.com's proposal, I wonder if serializing the model instance to a temporary file (whose path would be stored in the session hash), and then reloading it each time a new request arrives, would do the trick. Would it work in all cases? Is there any piece of information that would be lost during serialization/deserialization?
As HTTP requests are stateless you need some kind of storeage between requests. The session is the easiest way to store data between requests. As for you the session will not be enough because you need it to be accessed by multiple users.
I see two ways to achive your goal:
1) Get some fast external data storage like a key-value server (redis, or anything you prefer http://nosql-database.org/) where you put your objects via serializing/deserializing (eg. JSON).
This may be fast depending on your design choices and data model but this is the harder approach.
2) Just store your Objects in the DB as you would regularly do and get them versioned: (https://github.com/airblade/paper_trail). Then you can just store a timestamp when people hit the save-button and you can always go back to this state. This would be the easier approach i guess but may be a bit slower depending on the size of your data model changes ( but I think it'll do )
EDIT: If you need real-time collaboration between users you should probably have a look at something like Firebase
EDIT2: Anwer to your second question, whether you can put the data into a file:
Sure you can do that. But you would need some kind of locking to prevent data loss if more than one person is editing. You will need that aswell if you go for 1) but tools like redis already include locks to achive your goal (eg. redis-semaphore). Depending on your data you may need to build some logic for merging different changes of different users.
3) Another aproach that came to my mind would be doing all editing with Javascript and save it in one db-transaction. This would go well with synchronization tools like firebase (or your own synchronization via Rails streaming API)

Using tags for user-set UX details

I'm using acts_as_taggable_on for tagging items across my system so that they're easily searchable.
Now I have a UX problem: I'm noticing lots of places where users choose certain minor states (for example, closing a one-time help box or moving to the next javascript-run step in a given page). We have here situations that are both too minor/numerous/dynamic/fast-changing to be put into a database table (imagine having to migrate with every UX change!), and that there is a need to persist some of these choices beyond the session.
In this case, is there anything wrong with using tags to store these simple decisions? For example, user.set_tags_on(:ui, "closed_index_help") or user.set_tags_on(:ui, "tutorial_1_done"), then showing/hiding these elements in the future by looking at the user's ui_list.
Are there drawbacks to this I'm not considering or is this a prudent way to go?
Another way might be to store the information in the SESSION. You will of course have to migrate the session information to be stored in the DB rather than the cookie, but at least that way - you only have to retrieve the session once.

How do I see real-time activity of my users in Rails 3?

What I would like to do is have my admin user be able to see - in real time (via some AJAX/jQuery niceness) - what my user's are doing.
How do I go about doing that ?
I assume it has something to do with session activity - and I have started saving the session to the db, rather than the cookie.
But generally speaking, how do I take that info and parse it in real time ?
I looked at my session table and aside from the ids (id and session_id), I see a 'data' field. That data field stores a hash - which I can't make any sense of (looks like an md5 hash).
How would I use that to see that User A just clicked on Link B, and right after that User B clicked on link A, etc. ?
Is there a gem - aside from rackamole - that might be able to help me?
You might want to check out Mixpanel. They are easy to setup and have some of what you are asking for.
The session data only contains the values stored in the session[]-hash from the user. It doesn't store which action/controller was called, so you don't know which "link was clicked".
Get the activity of your users:
Besides rackamole you have two options IMHO.
Use a before_filter in your ApplicationController to store the relevant info you are interested in. (Name of controller, action or URI, additional parameters and id of the logged in user for example).
Use an AJAX-call at the bottom of each page which posts back the info you are interested in (URI, id of logged in user, etc.) to your server. This allows faster response times from the server, as the info is stored after the page has already been delivered. Plus, you don't have to use a Rails-request to store it. The AJAX-request could also be calling a simple PHP-script writing the data to disk. This is much faster.
Storing this activity:
Store this data/info either in the database or in a logfile. The database will give your more flexibility like showing all actions from one user, or all visitors for one page, etc. The logfile solution will give you better performance.
Realtime vs. Oldschool:
As for pulling out your collected data in realtime, you have to build your own solution. To do this elegantly (without querying your server once a second to look if new data has arrived) you'll need another server process. Search for AJAX Push for more info.
Depending on your application I'd ask myself if realtime notifications for this are really necessary (because of all the hassles of setting this up).
To monitor the activity on your site, it should be enough to have a page listing the latest actions and manually refresh it (or refresh it automatically every ten seconds).
Maybe you can test https://github.com/raid5/acts_as_scribe#readme
It works with Rails 3 too.

Is there a way to load and set a specific session from the database in ruby on rails?

My Scenario:
Sessions are being stored in the database using ActiveRecord::SessionStore
User is on site at 'http://domain.com '
they go to secure checkout at 'https://domain.secure.com' (our app has lots of domains, but want to send everyone through secure checkout on the same domain to save SSL headaches).
We append session_id and a hash to authenticate and identify the user at 'https://domain.secure.com'
Load that users session data by doing ActiveRecord::SessionStore::Session.find_by_session_id(session_id)
The above all works fine. BUT how can I actually set the same session_id to the user once they are on 'https://domain.secure.com'? I can loop through the data and assign it to the user's session, but Rails automatically gives the user a new session_id on 'https://domain.secure.com'. I want the users session to be stored in the same database row regardless of whether they are on 'http://domain.com' or 'https://domain.secure.com'
Why do I want to do this? For ease of expiring sessions. I can just remove that row from the sessions table.
Otherwise I'm thinking I'll have to add a user_id column to the sessions table and then when i want to expire the session, I delete all rows for that user. But this sounds more complicated to me. I'd rather just have one row in the sessions table.
Any suggestions?
A couple ways to approach this.
One, you could look at a higher level of abstraction for authentication that lets you manage cross server. For example, Devise http://github.com/plataformatec/devise
Second, you can override the whole session controller and write your own session manager. A bit more work, but not that complicated if you ultimately need a bunch of custom functionality. I remember correctly, start with this in your environment:
CGI::Session::ActiveRecordStore.session_class = MySessionClass
and go from there..

Storing "favorites" in a Rails cookie?

I'm trying to run my site off the lazy idea of not having user registrations.
Anyway, I want a user to be able to "favorite" items on the site when they click "favorite" off of an "item"
I'm assuming I need to use cookies for this but I don't really know the next step. Could anyone point me in the right direction?
Thanks!
Not rails specific, but I dislike the idea of storing too much stuff in a cookie. Instead I store it on a database on my web site, and just put a primary key value in the cookie. That way you can store as much or as little as you like without transmitting too much over the net. The disadvantage of the "no registrations" approach is that they lose all their stored data if they change to a new computer, or even a new browser on the same computer.
What I do in my app is to store a cookie with a "session id". Then I have several tables, one stores one-off data like the last date that session id was seen, and others store multiple items per session id. I have a "session_states(session_id, state)" table, for instance, that stores one record for each state a person chooses from a list of US states.
One reason for storing the last date is that I purge any session ids from the database that haven't been seen in two years (because I give an expiration of +2 years when I create the cookie).
Maybe you could learn more about cookies on Rails. This link has a lot of information about this topic. I think that you will appreciate it!
But take some care to not store too much information using cookies.

Resources