Best way to add "created_by" entries to all tables?

Hi,

I want to automate somewhat the addition of created_by/updated_by fields
on my models (i.e. to include username). What’s the best/recommended
way to do this? Would it be to:

a) Find out how to extend the ActiveRecord::Base itself (I haven’t
extended a class before yet)

b) Use a plugin type approach where you have to add a tag manually to
each model file which then calls the ruby code which adds extract
methods into the model class (i.e. dynamically adding methods)

c) Use a model “before_create” type callback to an appropriate method

I’m assuming that, assuming the DB table has the columns (e.g.
created_by) in place, that you just need to effectively get the model
class to have a “self.created_by=xxx” piece of ruby code run to set it
prior to the save (e.g. use of a before_save appraoch perhaps)

Any ideas/guidance welcomed

Tks

I want to automate somewhat the addition of created_by/updated_by fields
on my models (i.e. to include username). What’s the best/recommended
way to do this? Would it be to:

A simple way would be to add records via your user models. Take a
users/articles relationship. Article belongs_to User, User has_many
Articles.

When you create the article, if you do it like this:

@user.articles.create(params)

then your articles are automatically linked to the creating user.

The problem with using before filters in the models to do this is that
you would some how need to get hold of the user’s name - likely to be
via the session like this:

user = User.find(params[:user_id])

But including that in the model would be breaking the whole MVC thing.

If you have a small amount of models, it’s probably easier to just
manually specify the users:

article.created_by = @user.name

or

article.updated_by = @user.name

As you would only be updating/creating in one place, it’s not a huge
amount of code.

Any other thoughts on this?

Steve

I could take this approach Steve (of adding “article.created_by =
@user.name
to each model) and then extend this via customising my generator for
model
creation perhaps. This way whenever I generate a model it would have
this
added automatically. I guess it’s a slightly different way of adhering
to
DRY (don’t repeat yourself).

I would still be interesting in following some sort of “extend the base
class” approach to be really DRY compliant :). Anyone have any
comments on
this option, i.e. this would mean somehow extending “ActiveRecord::Base”
no
doubt?

Tks

Hello Greg,
Urm, okay, stupid question, but, why not take the -exact- same route
as the ‘timestamp.rb’ file contained in the activerecord gem ?
Basically, it allows you to switch on timestamping (and thus populates
the created_at field) or switch it off on a per model basis. I dare say
that you could look at the 60* or so lines, and get it to do what you
want to do. This way would definitely be more DRY, and if you do it
right, you could include it for inclusion into Rails or at least make it
a nice little ‘plugin’ or some such :wink:

Jst a thought.
Regards
Stef

(* and out of those 60 lines, only half or less are actually -code- :wink:

I would still be interesting in following some sort of “extend the base
class” approach to be really DRY compliant :). Anyone have any
comments on this option, i.e. this would mean somehow extending
“ActiveRecord::Base” no doubt?
It’s easy to extend activerecord but I can’t really see how this will
help your situation. You’ll still need to know who the user is - that
information will either need to be passed during the method call or
obtained from the session (which would break MVC).

How DRY the method I mentioned is depends on how many models you have to
update with this information.

If you’re interested in how you extend classes, have a look at

for details on how to write plugins.

Steve

Actually, what about this:

Option 1

a) create a “MyActiveRecordBase” class which extends
“ActiveRecord:Base”.
Place all the custom things you want in here.
b) for all your own model classes extend from “MyActiveRecordBase”
c) modify your model generators to align with this for ease

or

Option 2

I thought I read it was possible to just define your own version of a
class
(e.g. “ActiveRecord:Base”) and put the methods you want to override. In
this case it would be more adding in class calls like “before_create
:generic_fields” however, so I’m not sure if this would work?

or

Option 3

Use of the “extend” method as per the plugin tutorial that Stephen
suggested:

Any coments on the pro’s/con’s of the above? Or are all just as good as
each other?

Cheers

thanks Steve - I’ll look this up. BTW - I already have a process for
getting hold of the userid (I looked up how some of the plugins did it)

Any coments on the pro’s/con’s of the above? Or are all just as good as
each other?
I’ll reply again :0)

Option 1 - Not cool because it’s not really using the power of ruby to
dynamically extend classes. It also makes it difficult to add this
functionality to existing models.

Option 2 - This is true - Ruby classes are never ‘closed’ so you can
just happily open them up and start adding more methods.

However, this is effectively the same as:

Option 3 - which will allow you add and override methods dynamically
across specified models. You create the plugin that loads when the
application starts. In your models, you simply add the call to use this
plugin:

class MyModel < ActiveRecord::Base
track_users
end

Yeah, you have to add one line of code to models that require tracking,
but that’s a good thing - I wouldn’t have thought that you really want
all of your models tracked.

The other benefit is that the functionality is nice and encapsulated,
ready to use in other apps and share with your friends!

The actual extension itself should be pretty easy:

def before_create
created_by = however_youre_doing_this
super
end

Hope that helps

Steve

thanks Steve :slight_smile:

Just briefly:

a) with the “before_create” example method you just posted, do you
actually
need to call super at the end? I haven’t tested, however I would have
thought if rails provides a “before_create” method you wouldn’t have to
do
this?

b) re my option 2 - what I meant to imply was an option where you don’t
touch the rails class itself, but put your own copy of with the same
file
name into your application - I thought I saw somewhere you could do this
and
your local copy would override the real copy with any methods from your
local copy - am I dreaming this up? Was it just rails “Engines” that
supports this and not rails itself?

Tks

a) with the “before_create” example method you just posted, do you
actually need to call super at the end? I haven’t tested, however I
would have thought if rails provides a “before_create” method you
wouldn’t have to do this?
super just calls the same method but in the super class. As you’re
overriding the super’s method, you may need to call any additionally
defined hooks.

b) re my option 2 - what I meant to imply was an option where you don’t
touch the rails class itself, but put your own copy of with the same
file name into your application - I thought I saw somewhere you could do
this and your local copy would override the real copy with any methods
from your local copy - am I dreaming this up? Was it just rails
“Engines” that supports this and not rails itself?
Yeah that’s a ruby feature. You can just drop a file in lib/, include
it in your environment.rb and just open up any existing class:

for example, string.rb:

class String
def i_say
"Steve says: " << self
end
end

Then you would be able to do this:

my_string = “Hello”
my_string.i_say -> “Steve says: Hello”

Excuse the weird example, but I hope it explains it :0)

You can do this just as easily with the rails classes like activerecord,
but it’s not as neat a solution as using the existing plugins system.

Steve

thanks Steve for clearing that up for me :slight_smile:

Hey, if you’re keen to reply to one more of my posts, I’m stuck on one
aspect of the filters. My post is at the URL below…still no replies
for
the moment…Tks again :slight_smile:

http://www.ruby-forum.com/topic/80742#new

Greg H. wrote:

Hi,

I want to automate somewhat the addition of created_by/updated_by fields
on my models (i.e. to include username). What’s the best/recommended
way to do this? Would it be to:

Why not use the userstamp plugin? It extends AR the same way
ActiveRecord::Timestamp does. Works for me.

script/plugin install
http://www.delynnberry.com/svn/code/rails/plugins/userstamp

Steven

Why not use the userstamp plugin? It extends AR the same way
ActiveRecord::Timestamp does. Works for me.

script/plugin install
http://www.delynnberry.com/svn/code/rails/plugins/userstamp

Im also using this, it work pretty good for me, except I don’t like
how you have to define this in every model:

belongs_to :updated_by, :class_name => "User", :foreign_key =>

“updated_by”
belongs_to :created_by, :class_name => “User”, :foreign_key =>
“created_by”

It does work pretty reliably for me tho. If there were a way to just
have it work like timestamping with no configuration beyond if the
database field existed, I always thought that seemed like the most
ideal solution.
Good luck. I’m curious to see what you decide in the end.
~michael

Tks - Just had a look at the userstamp plugin. What I still don’t quite
understand is that, given the plugin is already extending the
ActiveRecord:Base class, why does it then in addition to this invoke
ruby
dynamic language concepts to perform aliasing etc (noting that, at least
for
me, the code is less straight forward to understand).

That is, why not just put all the required extensions in the
ActiveRecord:Base extension, and eliminate the need for ruby dynamic
language class/object changing routines? Perhaps I’m missing something?

Tks