Forum: Ruby on Rails Automating user-specific stamps on records

4a9cd711ccb80f1ab3ce17450c2857e4?d=identicon&s=25 Greg Willits (-gw-)
on 2008-05-31 11:00
(Received via mailing list)
So, given the opinion of Rails-core regarding the it's-a-feature-not-
a-bug lack of access of the session to models, what has been the
"rails way" of dealing with these cases:

-- stamping records with created_by and updated_by fields with user
name or id

-- writing breadcrumb logs of user actions where user-specific
details must be written per record

-- I have a third case, less common, that essentially puts records on
"hold" -- in an attempt to cleanup after users who abandon these
holds prior to completing the work for which the hold is originally
secured, I maintain a list of what records are on hold and call a
cleanup function when the user visits a page that clearly is an act
of abandonment (otherwise the time experation eventually results in a
release). I have always used the session to store the list (which
requires that models be able to store to sessions).Pushing storage
off to a database doesn't solve it -- the user & data still has to be
correlated, and you're right back into a scenario just like the above
two.

-- I have a pessimistic locking system that has worked very well for
me that's very similar in behavior to the "hold" system


All I can see are fairly lame workarounds:

-- subclass ActiveRecord so I can bury the manual passing of the
session into the model a layer away from the app code

-- extend ActiveRecord to modify save and update methods (basically
same approach as above)

-- pass session or user details into abstracted save/update methods
that have to be written/included for every model


All these are just monkey patches IMO that fake the appearance that
"the framework" is handling those details like it does for created_at
and updated_at.

Sure, allowing models to read/write to a session can be abused, but
so can erb in a view, or every other aspect of programming. So what.
IMO, a session is a system wide bucket for dealing with state. Why
there's such a rabid opinion that it is solely the domain of the
controller seems rather odd. Especially when it's exactly this kind
of widely used pattern (connected record transactions to users) that
I would expect a framework to embrace in enabling in the name of
system-level integration for the greater good which trumps MVC
extremism.

created_at is handled, so why not created_by? Because of implentation
details? Well, if we had this thing that stored arbitrary data about
the current user that each model in a given system could deal with on
its own, we could... oh, wait. We do. Sessions.

However, I recognize that debate has long lost its luster in these
halls, so, enlighten a poor, ignorant soul as to god's way of
handling these scenarios.

Am I really supposed to pass these details along with every model
modification call, or abstract my own layer to fake appearances?

I've never considered a way other than allowing a model to access a
session as a system-wide resource, so seriously... what option am I
not seeing (or just not accepting as the "correct" way in an MVC
extremist world).

-- gw
E3ba60e3dcb813f8abcd7732350e74cf?d=identicon&s=25 Phillip Koebbe (pkoebbe)
on 2008-05-31 14:27
Greg Willits wrote:
> So, given the opinion of Rails-core regarding the it's-a-feature-not-
> a-bug lack of access of the session to models, what has been the
> "rails way" of dealing with these cases:
>
> -- stamping records with created_by and updated_by fields with user
> name or id
>

Hi Greg,

Have you looked into UserStamp or acts_as_audited? I was doing a little
reading on this the other day and came across these two.  If you google
the two terms "rails" and "created_by", you'll get some decent links.

For my current project, I need only to automatically stamp created_by
and updated_by, so I chose to use the simplest approach.  I inherited
from AR::Base and created default before_create and before_update
methods.  In my get_user method in the base controller, I included

Thread.current[:person_id] = session[:person_id]

so I can use that in the AR callbacks. Using Thread is not actually
necessary, according to the articles, but it's the shortest line between
points A and B, and that's what I'm concerned about right now.

Hope that helps at least a little.

Peace,
Phillip
4a9cd711ccb80f1ab3ce17450c2857e4?d=identicon&s=25 Greg Willits (-gw-)
on 2008-06-01 01:45
(Received via mailing list)
On May 31, 2008, at 5:27 AM, Phillip Koebbe wrote:
> Greg Willits wrote:
>> So, given the opinion of Rails-core regarding the it's-a-feature-not-
>> a-bug lack of access of the session to models, what has been the
>> "rails way" of dealing with these cases:
>>
>> -- stamping records with created_by and updated_by fields with user
>> name or id
>>
>
> Have you looked into UserStamp or acts_as_audited?

Looked. Yeesh, far too much gymnastics for something that should be
very simple.

> points A and B, and that's what I'm concerned about right now.
That's what I have adopted for now as well. The clue to look at
Thread helped. Thanks.

I think I can weasel Thread.current to solve my "hold" examples too
by simply passing the pertinent session data into the thread.

It's a wonky hack IMO. Session should just be an app-wide resource,
but at least this isn't a whole plugin worth of bloat.

Thanks, Phillip.

-- gw
747b0480986bd8ac5d91aafd74bea043?d=identicon&s=25 Sheldon Hearn (Guest)
on 2008-06-01 16:38
(Received via mailing list)
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On Sunday 01 June 2008 01:44:04 Greg Willits wrote:
> > Have you looked into UserStamp or acts_as_audited?
>
> Looked. Yeesh, far too much gymnastics for something that should be  
> very simple.

If all you want really _is_ who last touched the record, then yes.

But think into the future.  You may find that this isn't enough.  Once
management have the answer to that question, then next thing they ask
is typically "What did she change?"  If they could have the answer to
that, the next thing they'd come up with is "No, she didn't clear the
account balance, go back one more change in time please."

So be careful about calling auditing "something that should be very
simple".  Yes, "last changed" stamps are very simple.  But they're
often not enough.  They're just the answer to the question management
can't have answered today.  Give them the answer, and the question will
change. :-)

Ciao,
Sheldon.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQFIQrQ1pGJX8XSgas0RAsJNAJ9ykITWFtbTRhnk4jQB0xFRx226aACbBgB2
42tIuEQ6eKKtSL0twMFoUR8=
=TTlf
-----END PGP SIGNATURE-----
4a9cd711ccb80f1ab3ce17450c2857e4?d=identicon&s=25 Greg Willits (-gw-)
on 2008-06-01 20:52
(Received via mailing list)
On Jun 1, 2008, at 7:37 AM, Sheldon Hearn wrote:

> is typically "What did she change?"  If they could have the answer to
> that, the next thing they'd come up with is "No, she didn't clear the
> account balance, go back one more change in time please."
>
> So be careful about calling auditing "something that should be very
> simple".  Yes, "last changed" stamps are very simple.  But they're
> often not enough.  They're just the answer to the question management
> can't have answered today.  Give them the answer, and the question
> will
> change. :-)

Very simple = the part of collecting data on who. It should be as
simple as getting info out of the session. Not allowed. Thread is
next "simplest" thing AFAICT.

As for auditing, yep, been there, done that. Now that I have who, I
can build my own audit trail.

I'm transcribing processes I've written in another platform. So,
usually I'm just trying to grope my way around the Ruby / Rails
syntax and idioms.

-- gw
747b0480986bd8ac5d91aafd74bea043?d=identicon&s=25 Sheldon Hearn (Guest)
on 2008-06-01 22:50
(Received via mailing list)
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On Sunday 01 June 2008 20:52:33 Greg Willits wrote:
> I'm transcribing processes I've written in another platform. So,  
> usually I'm just trying to grope my way around the Ruby / Rails  
> syntax and idioms.

Ah, I misunderstood what it was that you're balking at.

Yeah, in classic MVC, models shouldn't really know _anything_ about the
controller.  Rails is pretty fanatical about the layering, so there's
no special affordance for having your models know what's going on in
your controllers.

When you start to consider how you'll deal with the "who" of it all
outside of HTTP request context (for example, in runner scripts), it
becomes clear why the separation of concerns is important.

And at that point, you'll be pretty glad that you bothered with
something like the threadlocal identity.

For example, in a document management portal I developed, a number of
automated jobs are expected to upload documents from cron jobs.  The
customer didn't want this going through web services.  Tying down
the "who" of the uploads was as simple as forging "script" as the
username for the current process.  Something more complex was possible,
to be sure, but that was sufficient for them.

Ciao,
Sheldon.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQFIQwtJpGJX8XSgas0RAk/4AJ4vZ18nyQU/DcQFCh+W324iPxlnYQCeNLGc
2/yze6Rb0uKBnzbx4UeJ0iM=
=plYv
-----END PGP SIGNATURE-----
4a9cd711ccb80f1ab3ce17450c2857e4?d=identicon&s=25 Greg Willits (-gw-)
on 2008-06-02 08:46
(Received via mailing list)
On Jun 1, 2008, at 1:49 PM, Sheldon Hearn wrote:

> no special affordance for having your models know what's going on in
> your controllers.

(not an attempt to belabor or underscore a point, but an attempt to
reason out my position)

My core observation is that a session shouldn't even be chained to a
controller in the first place.

It's not controller-state, it is application-state that I am
interested in preserving. Unfortunately, in web apps we only have one
way to do that--the session. I think the Rails interpretation of the
session is flawed, but it takes some expaining as to how I arrive at
that opinion.

The purpose of a framework is to provide generic services that
(nearly) everyone would otherwise just end up writing for themselves
(efficiency, reliability, bla bla bla) -- _and_, IMO, to isolate
those cases, in a well prescribed way, where breaking the normal
rules for boundaries or layers provides greater benefit than adhering
to the rules.

The normal channel for communication between a controller and model
is of course the sending of commands from controller to model
(separation, tell don't ask, etc). There are cases where you find
yourself sending some data in every message. That's the type of thing
you look to push down into the framework to handle automatically to
relieve the app dev the tedium (efficiency) and subjectivity to error
(reliability). This is where allowing models to ask controllers might
be allowed as an underlayer service of the framework -- it breaks the
rules, but has an overall advantageous benefit all around. So of
course you look to do that in a well-described way.

If we take the example of stamping who created/last-modified a model,
it is not a specialized task between two specialized components. It's
a rather generic one. Still, some models may support it, some models
may not. Devs are going to have to constantly reference docs to see
which models do vs don't. Requiring that a user or data object be
passed to models becomes a rather tedious and inefficient
requirement. "Can't the system just do it?"

IMO, yes, the system should just do it. We have a task that is such a
pervasive pattern across the whole of the application that it should
become a service available to any part of the application to relieve
the developer from the tedium. So, in a stateful environment like a
desktop application, I would abstract current user data as
standardized component that we would allow models to voluntarily
consult as needed. If the model needs to support user stamping it can
request/retrieve the info on its own.

I would expect the possibility to create a number of similar objects
a in a complex application where certain components raise above the
norm of an application level object into being part of a framework or
service layer. In a stateful desktop or client-server application, we
have a few patterns we can pick to implement this object, and we have
the luxury of being able to create many independent objects to retain
isolated elements of the application state where we blur the lines of
normal separation. In a desktop app, if I need to retain state of
some controllers, and some models, and some views, I can create
separate buckets for each of these to keep them clean.

The problem we face with web apps, of course, is sustaining the state
of that user object. We have one tool--the session. Be it implemented
as cookie or database record, we have one bucket to put things in to
sustain a state. Arguably with cookies, we could have multiple
buckets, but with the classic web session (and in particular a Rails
session, we have one). The purposes and uses for that tool are many,
and it should therefore have some flexibility.

In our immediate use case of user stamping, the limitation of the
Rails session means that if we're to relieve the developer of the
tedium described above, everyone has to come up with a work around.

That work-around turns out to be "store the user in the session, then
store the user again in a Thread." Hmm.

DRY?
-- storing the same data in two places
-- writing code twice that copies the same data to storage bucket
-- many developers all writing the same work-around code

Nope.

Why?
Rails interprets a session to be the exclusive territory of the
controller.

Even though the reasons vary, there are several use cases for
allowing model access to the web app session that when combined, I
believe we find justification that a session should not be limited by
Rails' current interpretation.

No, a session should not be a dumping ground for every piece of data
that needs to be moved about (for all the usual design and practical
reasons typically cited), but there are some fairly clear uses cases
IMO where the session proves to be an efficient and effective place
to store data, or the required pointer to data (in table data,
memcache, cookies, files, etc), that is stateful -- and the purpose
of that state is not always limited to passing data from controller
to controller.

By making a session an object independent of the Rails application
controller and therefore reachable by any "object" in the framework
hierarchy, we improve DRYness, and we open possibilities for web apps
to solve API problems more like we can in desktop apps.

-- gw
81b61875e41eaa58887543635d556fca?d=identicon&s=25 Frederick Cheung (Guest)
on 2008-06-02 09:11
(Received via mailing list)
On Jun 2, 7:44 am, Greg Willits <li...@gregwillits.ws> wrote:
> > the
> It's not controller-state, it is application-state that I am  
> interested in preserving. Unfortunately, in web apps we only have one  
> way to do that--the session. I think the Rails interpretation of the  
> session is flawed, but it takes some expaining as to how I arrive at  
> that opinion.
>

Well there's always Pratik's (somewhat tongue in cheek) solution:
http://m.onkey.org/2007/10/17/how-to-access-sessio...
It's not a very nice workaround, nor are any of the other ones, which
reflects the general position that you shouldn't be trying to do this.
More generally, Rails' position is that the very concept of
application state is something that is not the concern of the model.

Fred
4a9cd711ccb80f1ab3ce17450c2857e4?d=identicon&s=25 Greg Willits (-gw-)
on 2008-06-02 12:26
(Received via mailing list)
On Jun 2, 2008, at 12:09 AM, Frederick Cheung wrote:

>>> Yeah, in classic MVC, models shouldn't really know _anything_ about
>> controller in the first place.
> request-in-model
Indeed. The source of my original post's reference to "rabid
opinion." (and I might say that post goes too far in enabling the
wrong things for the wrong reasons--which I understand is his point,
and agree with 97.42%)

> It's not a very nice workaround, nor are any of the other ones, which
> reflects the general position that you shouldn't be trying to do this.

It's either this or a lot of duplicated code, or work-around code
that just let's it happen anyway.

> More generally, Rails' position is that the very concept of
> application state is something that is not the concern of the model.

Of which I say there are valid exceptions to the rule, (wrong things
for the right reasons), and thus we arrive at the start of our
circle :-)

-- gw
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.