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)
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.
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?
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
Jst a thought.
Regards
Stef
(* and out of those 60 lines, only half or less are actually -code-
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
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?
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
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?
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
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
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.
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
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.