чур я хедкраб!
Is there someway you could set the session to expire a particular comment_id in 15 mins?
Thanks for re-pointing out that you can (and should) still have classes that are not part of Active Record. Sometimes I forget that. A Shopping Cart could be another good example.
@taelor, I agree. Maybe this should be tagged under 'Models' instead of or in addition to 'Active-record'.
@Morten it's coming soon. :)
@taelor, not sure what you mean by expiring a particular comment_id. Could you give an example?
@Karmen, fixed.
This is a pretty off-scope, but:
I have added a login_at-column to my User-model to keep track of the user-logins. However, I feel that there must be a better solution than updating the database on each and every action.
I guess sessions won't work as they are stored client side, but isn't it possible to cache the time of the last login?
However, I'm still trying to figure out how to write the changes from the cache back to database, as soon as it expires.
Maybe someone can give me a hint on this.
Excellent stuff!
Maybe you've already touched on this before (forgive me if so), but I'm curious to know what are some other scenarios where non-Active Record models are helpful?
Also, how does Rails know how to find that user_session model? Does it just load any .rb files inside the models directory?
Usually, if i use a kind of non-database-stored class, i don't put it into app/model. I prefer putting it into lib, but that's just my preference.
@Jonathan, non Active Record models are usually good when you have business logic that doesn't really fit anywhere else. This varies from app to app.
One thing to watch out for is RESTful controllers which don't map directly to an existing model. Instead of filling the controller up with the logic, make a custom model.
Edit: I forgot to mention. The model directory is included in the load paths, so that's how it can find the UserSession class.
@Rainer, that'll work. I prefer to blur the lines a bit between Active Record models and other models. What concerns me more is the type of logic which they contain - and in that sense they are very similar.
I usually keep the lib directory for logic which is not specific to my application - it has no knowledge of existing models and is usually more generic. Usually these are good candidates to move into a plugin or gem.
Since the session store is now saved in a cookie, wouldn't this permit any user to edit any comment added to the system if they typed the id of any comment added within the last 15 minutes into their cookie?
MySQL seems to start auto incrementing ids at 1 and increments by 1. So, it'd be fairly trivial to guess the id of the latest comment by just looking at the URL of a create comment URL.
Why not just key the comment_ids off the Rails _session_id from a Rails.cache rather than risk a privilege escalation attempt? Or, recommend switching the session store to memcached or the old PStore.
@xhan the RSS feeds are working for me. Which link specifically doesn't work?
@Tony, the cookie session store is encoded and doesn't allow users to edit it. I'm not entirely sure how it works, but I know it's secure.
One thing it doesn't do is hide the contents from the owner of the session, but as long as you don't mind them seeing what's inside the session then you don't have to worry about it. Here we don't care if they see the comment ids so a session works fine.
My bad, I never bothered to look at how the cookie store actually saved the data in the browser. Since it seems to be encrypted, my privilege escalation point was totally wrong. Thanks for clearing this up, and keep up the good work.
surely it would be cleverer to find out if there have been any comments on the post since, and if not then let them edit it... that would work far better than 15 mins IMHO. I don't know how easy that would be to do though
great podcasts though, i really love them
@Lenary, yep that would work if you like that logic better. You would just have to check if another comment has been created after the given comment with that same article_id.
What you have to be careful of is that if the user is currently editing a comment and in the meantime another user posts one. Then they can't submit their edit. Using 15 minutes tells the editor they have that amount of time guaranteed to make the change.
Its funny you should post this now. I had to come in last weekend to solve this issue for my intranet site. I ended up implementing a version of the method layed out here http://www.zorched.net/2007/05/29/making-session-data-available-to-models-in-ruby-on-rails/ which uses a user info module that stores info in the mongrel thread. Can you think of any advantages / disadvantages to doing it that way? Perhaps you could compare and contrast for extra credit? :p
@Isaac, functionally it sounds like that solution works, however I'd prefer to use a sweeper instead of an observer if you need access to request specific info on a callback (such as the session).
Generally I like to keep my models (and observers) very dumb about the request details. This UserSession model is pushing that limit a bit, but since I'm not passing it directly to other models I feel it's okay.
This is a great video, and I think it's good to avoid requiring user logins whenever possible.
- Depending on the number of comments stored in the session, it might be worth replacing the Array of comment ids with a Set.
- While it looks cleaner, moving the initialization of session into 'initialize' rather than on-demand will end up giving every visitor to your site a session, most of which will be empty.
- Could you simply make a helper module that is included in the controller and exposed to the view? It has direct access to session, and could privately define UserSession to avoid the HTTP/Model layering debate.
I didn't know you could have non-ActiveRecord based models.
Could you please do a screencast on how to make plugin/gemplugins?
Ryan,
I'm still trying to get my head around the advantages of storing the session in a cookie instead of on the server... I guess it allows to have multiple servers running with no state at all (since the state is completely stored on the client)... what other reason you'd see for using the cookie session store?
@Matthew, great points.
1. Yeah, a Set could be better since the order isn't important. I was also considering storing just the last comment id.
2. I was wondering about this. Isn't a session automatically created anyway (for the flash message)? An empty array would be very small but I suppose could build up.
3. A helper module could work and may be better for this simple example. But if things get more complex I think having its own class will scale better. This way it can have instance variables outside of what is in the session, and you can have private methods, etc and not have to worry about conflicting with existing controller methods.
@Bryce, check out episode #33. It's pretty old, but most of it still applies. I hope to do a new one on gems sometime in the future.
@Jean-Marc, as you mentioned having shared state across multiple servers is one of the things it solves. Another reason is that sessions are a pain to manage. You need to set up a separate cron job or something to remove old sessions. I think storing sessions in Memcachd is probably preferred, but not everyone has that setup.
I noticed some really unimportant detail while watching this brilliant episode: How comes your example post has the post id 53582982? That must be a really vast example application ;)
@klickverbot:
Still on the "noticed unimportant details", its funny how the screencast jumps through time. It goes to show that either:
a) Ryan's clock moves faster than ours.
b) Ryan's editing work is very good (I never notice it)
If you look at the timestamp on his comments on the screencast, they are spread over more than 1 hour, and yet the screencast is a little over 13min.
Ryan,
I looked at the actual cookie... call me stupid if you want but I could not make heads or tails of the encoding used to get the info in there.
having access to the actual data of the session would allow some interesting behaviors on the client site (like displaying a different set of links when a user_id is available)
(forget the heads and tails.... I just found it... before the "-" the information is encoded in BASE64... should be easier to parse at that point)
@klickverbot, I generated the models from fixtures, which if you don't specify an id, generates one based on the name of the fixture record. That's what you're seeing there.
@Felipe, heh, I record the show in very tiny segments (usually less than a minute each) and edit it all together. Recording a full screencast in one go is very difficult and usually ends up being more work than editing.
Awesome screen cast, thanks!
I'm curious: why didn't you create/edit/update the comments through the user model association:
@user.comments.build
@user.comments.find(params[:id])
Seems like this might have simplified some of the logic in you UserSession model.
@Steven, there isn't a User model in this app. We have a UserSession model, but that's not based on Active Record so you can't build associations through it.
Dude, Ryan Bates. You are the man. That was AWESOME. I learned so much in that action-packed video. Thanks so much for the free Rails University courses. You are the man! I hope Obama nominates you for the position of vice-president, so that the entire nation will learn how to bitch-slap rails code like you!!
IMO the comment should know if it can be edited ( http://pragmatig.wordpress.com/2008/06/29/separate-rights-management-from-controllers/ ) and then link_to_edit(comment) ( http://pragmatig.wordpress.com/2008/07/09/generic-smart-link_to_s-link_to_edit-link_to_destroy/ )
Hi Ryan,
I can't get my head around something. When providing a new object to your session... why not just override the session method on the controller? This way no matter if you wrote the code or not you could make sure you are controlling the usage of the session data.
Any thoughts on that?
Jean-Marc




