How to write manage created_on and created_by via mixin?


#1

Hi!

Definite newbie here - I’m working through this like-hate relationship
with
Ruby and Rails right now… sometimes things work really well (like
blobs
seem to rock) but other times, I dunno! :slight_smile:

Anyway, I’d like put in create and update user timestamps on all my
primary
domain tables and allow them to be updated automatically by
ActiveRecord.
Coming from Hibernate, I just place an interceptor that dynamically
discovers the column names and sets it up. In Ruby, I’m totally lost.
However, I hold out hope - I just don’t know the terminology for what
I’m
looking for since what I’m doing has to be relatively common.

So, I’ve created a mixin called UserTimestamp.rb:
module UserTimestamp
def mark_created
self.created_on = Time.new
self.created_by = @user.user_id
end

def mark_updated
self.updated_on = Time.new
self.updated_by = @user.user_id
end
end

And, then I include it and setup the before_create and after_create
macros:
require ‘lib/UserTimestamp.rb’

class Video < ActiveRecord::Base
include UserTimestamp
before_create :mark_created
before_update :mark_updated

… etc…

end

Of course, this doesn’t work, but that seemed logical. Neither @user or
user
is present, nor do I have any way to get the user_id. User is identified
in
my ApplicationController by a filter which is available in all my
controllers. However, I have absolutely no way of locating the User
object
from an ActiveRecord class… or do I? Do I need to pass it in on create
or
something weird like that? (Or, yuck, manually invoke it??)

Is there a better way to do this? This does work, but I wonder if
there’s a
more appropriate manner.

Thanks for any feedback!
-Rob


#2

Heh… you’re gonna laugh… or cry…

http://wiki.rubyonrails.com/rails/pages/MagicFieldNames

(also pg 277 in AWDWR)

It’s all done for ya baby… I’m finding that any time I think of a
cool thing to do in
rails, it’s already been done.

b


#3

Hey, excellent! I’m actually close to going down the right path?! (Ok,
well
the timestamps will take care of themself…)

Anyway, I chased down the user-level information to this post:

http://livsey.org/2005/07/16/adding_created_by_and_updated_by_to_rails/

The current_user is simply stored in a class-level attribute on User
which,
in turn, is set by a before_filter on the application controller. Neat
an
nifty… except - that’s not thread safe, right?? If two people hit the
site at the same time, User.current_user holds the last one, and all
record
updates will be attributed to that person. (Maybe I’m thinking too much
like
a Java person here…)

-Rob


#4

rails is process-based, not thread based. There are no issues with
multi-threading because each process handles 1 request.


#5

Rob Greene wrote:
[…]

Anyway, I chased down the user-level information to this post:

http://livsey.org/2005/07/16/adding_created_by_and_updated_by_to_rails/

The current_user is simply stored in a class-level attribute on User
which, in turn, is set by a before_filter on the application controller.
Neat an nifty… except - that’s not thread safe, right?? If two people
hit the site at the same time, User.current_user holds the last one, and
all record updates will be attributed to that person. (Maybe I’m
thinking too much like a Java person here…)

Yes, you would be right if Rails worked like Java servlets.

Rails is single-threaded (at least for the moment), so the approach of
using a model class variable is safe. In Rails running under Webrick,
only one request is processed at a time.

In production envvironments there may be multiple FastCGI (or SCGI)
processes, but each process only handles one request at a time. It
would be quite possible for multiple users’ requests to be handled
concurrently, but in separate processes, each with its own User class
and current_user class variable.

regards

Justin


#6

Wow, what a concept! That opens up a few doors I wouldn’t otherwise
consider…

Thanks!
-Rob