I'm still a bit confused about the issue of storing "complex" objects
in session state rather than storing simple objects.
It's my understanding that everything is an object in Ruby. If my
understanding is correct, then storing a string or an integer in
session means storing an object.
It would seem then that the problem of storing objects in session is a
matter of how "complex" those objects are, and that problems may arise
due if much work is required by the runtime to unmarshal larger
amounts of structured data.
With this is mind, how "bad" would it be to store the following in
session:
Struct.new("User", :name, :role).new("jdoe", "admin")
I would prefer to keep the data pertaining to "user" (in this case)
encapsulated in a single struct and to store that struct in session.
Is this categorically a bad thing to do?
and if your users are in the db, you'd be far better off just sticking
the id in the session.
Whatever you do, you'll want to create some kind of current_user and
current_user= methods to encapsulate the access. That way you can
change it around all you want.
Smaller I grok. The simpler part depends on what's involved in
cleaning session state for the number of state items supporting a
completed task flow that requires multiple requests.
If a given task flow's session state is stored as a single item, it's
much easier to avoid what amounts to orphaned global memory variables.
Clearing an instance of a LoanApplication object (not a model object)
from session state is easier to code, easier to test, and easier to
assure than clearing 10, 20, 50, or what ever number of individual
session state slots.
My notion here is that one session state slot per interaction is a
cleaner pattern - regardless of the platofrm.. be it Rails, ASP, JSP,
etc.
If there are serious technical limitations to storing a state value
object in session state in Rails, I'd love to hear them. If we're
talking about a culturally-embedded reticence to this pattern, it
might be valuable to crack that nut open in community dialog...
What if you are storing a model object in the session that could
potentially be modified from more than one process? Say you have your
User object in the session and an Admin disables that User. If you are
just referencing the User object in the session, the change in status
won't be picked up by already existing sessions.
If you just have the user id in the session and load the object on each
request cycle this problem is avoided.
User is a bit of an edge case since it has the special concurrency
considerations that you're talking about.
But for the sake of argument, if I have a threat model for the app
that suggests that instantaneous restrictions placed on a user when an
admin deactivates the user are not necessary, then I don't need to hit
the database with each request to get the user's permissions. The
permissions and roles can live in session state along with the
username.
In situations like this, I've also stored user roles in an encrypted
authentication ticket with an expiration time different than the
session timeout. I wouldn't necessarily assume a need to have the
freshest possible access control information for a user unless the
requirements called for it.
I don't think that this is an entirely realistic solution for every
app, but I'm putting it out there to suggest that there are threat
models that allow for some latency when a user is deactivated.
Ultimately, my question still remains about using a single state value
object for task flow state for a task flow that requires multiple
requests to fulfill.
I'd like to feel good about using state value objects rather than
using a myriad of individual session slots. I'm still not clear on
the technical limitations of doing this in Rails.
Smaller I grok. The simpler part depends on what's involved in
cleaning session state for the number of state items supporting a
completed task flow that requires multiple requests.
If a given task flow's session state is stored as a single item, it's
much easier to avoid what amounts to orphaned global memory variables.
Clearing an instance of a LoanApplication object (not a model object)
from session state is easier to code, easier to test, and easier to
assure than clearing 10, 20, 50, or what ever number of individual
session state slots.
My notion here is that one session state slot per interaction is a
cleaner pattern - regardless of the platofrm.. be it Rails, ASP, JSP,
etc.
If there are serious technical limitations to storing a state value
object in session state in Rails, I'd love to hear them. If we're
talking about a culturally-embedded reticence to this pattern, it
might be valuable to crack that nut open in community dialog...
One argument against storing larger things in the session could be that the session is unmarshalled on each request thus requiring a potentially severe overhead for requests not related to the thing you are storing in the session. Actually I haven't read the source so I don't know if the session object is lazily initialized so thight might me a non-issue.
Another concern is in regard to changes to code. If you decide to change the structure of the object old sessions will turn stale when you update the code. That may or may not be an issue depending on the use case.
I do agree that having a ton of session variables is also a bad thing. It's all to easy to forget about some of these. I prefer putting stuff in the database and keeping the amount of information in the session to a bare minimum. I have a hunch the ideas behind REST may be helpful in this regard.
Sure, there are definitely ways to handle the issues around storing
objects in the session. I was just pointing out one of the potential
issues with it.
There is nothing inherently bad about storing things in the session. The
session exists so we can fake out having stateful applications using a
stateless protocol. In AWD they say of the session, "You can store
anything you want, subject to a few restrictions and caveats." So, it is
up to the developer to be aware of the caveats and decide what is
acceptable to store in the session given the situation they are coding for.
I also agree that if you are going to store a set of related data in the
session it is better to have it contained in an Object of some sort
referenced by a single key ( e.g. session[:my_object] = MyObject.new(
some_stuff ) ) as opposed to session[:user_name] = 'foo',
session[user_role] = 'bar' & etc.
The main reason for not putting complex objects in the session,
complex typically being defined as Model (ActiveRecord) objects, is
that the marshalling code is so smart it's dumb. If it encounters a
model attribute that doesn't exist anymore, it'll typically blow up,
and unless you want all your users sessions invalidated by your rescue
code, you don't want this happening. I've seen some good discussions
on this "out there", just look around, you'll probably find some.
That being said, let's address your "keeping it encapsulated"
desire...
Models don't have to be ActiveRecord based, and they don't have to be
stored as objects in the session. Create yourself a CurrentUser model
that manipulates a session[:current_user] key in the session and have
that be a hash, like session[:current_user][:id] and
session[:current_user][:user_name]. Have that CurrentUser model be a
singleton or a normal object that is instantiated by a before_filter,
either way, both work just as well but the singleton design applies
better in my mind to the problem at hand. A little meta-programming
gives you the ability to define the attribute that'll be stored in the
session with something similar to attr_accessor. There, it's all nice
and encapsulated.
Cleanup, easy, session.delete(:current_user) or whatever you're usage
requires.
Just remember one very important thing, a Model is what you need it to
be, it encapsulates things that would be ugly, unclean, and just plain
wrong to put elsewhere into very nice, compact packages ... of your
design.