Warning
This code does not work in rails 2
If you are having trouble look at
http://dev.rubyonrails.org/changeset/7749
The instance_method_already_implemented? changes break this module.
Reverting to the previous version in green fixes the issue.
Much like how Timestamping callbacks are set up by default for columns named created_at/created_on and updated_at/updated_on, (See http://rails.rubyonrails.com/classes/ActiveRecord/Timestamp.html) this will automaticaly populate created_by and updated_by fields with the user who created/updated items.
Also see ExtendingActiveRecordExample for more examples on how to extend ActiveRecord to your needs.
This code is from this blog post
Place this in /lib/usermonitor.rb
module ActiveRecord
module UserMonitor
def self.included(base)
base.class_eval do
alias_method :create_without_user, :create
alias_method :create, :create_with_user
alias_method :update_without_user, :update
alias_method :update, :update_with_user
end
end
def create_with_user
user = user_model.current_user
self[:created_by] = user.id if respond_to?(:created_by) && created_by.nil?
self[:updated_by] = user.id if respond_to?(:updated_by)
create_without_user
end
def update_with_user
user = user_model.current_user
self[:updated_by] = user.id if respond_to?(:updated_by)
update_without_user
end
def created_by
begin
user_model.find(self[:created_by])
rescue ActiveRecord::RecordNotFound
nil
end
end
def updated_by
begin
user_model.find(self[:updated_by])
rescue ActiveRecord::RecordNotFound
nil
end
end
end
class Base
@@default_user_model = :users
cattr_accessor :user_model_name
def user_model_name
@@user_model_name || @@default_user_model
end
def self.relates_to_user_in(model)
self.user_model_name = model
end
def user_model
Object.const_get(user_model_name.to_s.singularize.humanize)
end
end
end
Then add the following to the bottom of /config/environment.rb
require 'usermonitor'
ActiveRecord::Base.class_eval do
include ActiveRecord::UserMonitor
end
Now if you add created_by or updated_by to your database tables, you will be able to access the user who created/updated the objects by calling object.created_by and object.updated_by respectively. Likewise you can set the user by assigning a user object to either one.
This defaults to assuming you have a user model called User, which can be overriden by calling relates_to_user_in in your model like so:
class WebPage < ActiveRecord::Base
relates_to_user_in :monkeys
end
The user model must contain “current_user” that references the currently logged in user.
An example of setting up a “current_user” method in your User model is as follows
In my model I have an accessor for current_user: ——user.rb—— cattr_accessor :current_user ————————- Then in my application controller I have a before_filter which grabs the user ID from the session (if there is one) and sets the current_user in the model. ——application.rb—— before_filter do |c| User.current_user = User.find(c.session[:user]) unless c.session[:user].nil? end ————————- So now you can call User.current_user to get the current logged in user.
——————————————
Note: an update to the blog post mentioned below says that thread safety is not actually an issue here …
I’m afraid that current_user method is not thread-safe, so I use Thread.current instead to retrieve currently logged-in user.
Find out more info at this blog post.
Here is the updated version with Thread.current
/app/controllers/application.rb
class ApplicationController < ActionController::Base
…. before_filter :set_current_user def set_current_user Thread.current‘user’ = session[:user] endend
/lib/usermonitor.rb
module ActiveRecord
module UserMonitor
def self.included(base)
base.class_eval do
alias_method_chain :create, :user
alias_method_chain :update, :user def current_user Thread.current‘user’ end end end def create_with_user user = current_user if !user.nil? self[:created_by] = user.id if respond_to?(:created_by) && created_by.nil? self[:updated_by] = user.id if respond_to?(:updated_by) end create_without_user end def update_with_user user = current_user self[:updated_by] = user.id if respond_to?(:updated_by) && !user.nil? update_without_user end def created_by begin current_user.class.find(self[:created_by]) if current_user rescue ActiveRecord::RecordNotFound nil end end def updated_by begin current_user.class.find(self[:updated_by]) if current_user rescue ActiveRecord::RecordNotFound nil end end endend
Note alias_method_chain is currently only available if you are using Rails on Edge.
If you are using the LoginGenerator, neither of the two beforementioned codes work! After working on a fix for several hours, I decided to post it here, so that others can possibly use this very usefull feature. I’ve merged code here and there from the two and am using the thread-safe Thread.current to store the current_user. There also was a typo (or maybe it works, but only on edge!) that prevented the current_user to be determined. The following code works with 1.1.6 and the newest LoginGenerator. As alias_method_chain is only supported on Edge I’m not using it.
application.rb (inside the class)
before_filter :set_current_user def set_current_user Thread.current‘user’ = @session‘user’ end
@environment.rb (insert before end of file)
require ‘usermonitor’
ActiveRecord::Base.class_eval do
include ActiveRecord::UserMonitor
end
/lib/usermonitor.rb
module ActiveRecord module UserMonitor def self.included(base) base.class_eval do alias_method :create_without_user, :create alias_method :create, :create_with_user alias_method :update_without_user, :update alias_method :update, :update_with_user def current_user Thread.current‘user’ end end end def create_with_user user = current_user if !user.nil? self[:created_by] = user.id if respond_to?(:created_by) && created_by.nil? self[:updated_by] = user.id if respond_to?(:updated_by) end create_without_user end def update_with_user user = current_user self[:updated_by] = user.id if respond_to?(:updated_by) && !user.nil? update_without_user end def created_by begin current_user.class.find(self[:created_by]) if current_user rescue ActiveRecord::RecordNotFound nil end end def updated_by begin current_user.class.find(self[:updated_by]) if current_user rescue ActiveRecord::RecordNotFound nil end end endend
That’s it!
category:Howto, Example
Warning
This code does not work in rails 2
If you are having trouble look at
http://dev.rubyonrails.org/changeset/7749
The instance_method_already_implemented? changes break this module.
Reverting to the previous version in green fixes the issue.
Much like how Timestamping callbacks are set up by default for columns named created_at/created_on and updated_at/updated_on, (See http://rails.rubyonrails.com/classes/ActiveRecord/Timestamp.html) this will automaticaly populate created_by and updated_by fields with the user who created/updated items.
Also see ExtendingActiveRecordExample for more examples on how to extend ActiveRecord to your needs.
This code is from this blog post
Place this in /lib/usermonitor.rb
module ActiveRecord
module UserMonitor
def self.included(base)
base.class_eval do
alias_method :create_without_user, :create
alias_method :create, :create_with_user
alias_method :update_without_user, :update
alias_method :update, :update_with_user
end
end
def create_with_user
user = user_model.current_user
self[:created_by] = user.id if respond_to?(:created_by) && created_by.nil?
self[:updated_by] = user.id if respond_to?(:updated_by)
create_without_user
end
def update_with_user
user = user_model.current_user
self[:updated_by] = user.id if respond_to?(:updated_by)
update_without_user
end
def created_by
begin
user_model.find(self[:created_by])
rescue ActiveRecord::RecordNotFound
nil
end
end
def updated_by
begin
user_model.find(self[:updated_by])
rescue ActiveRecord::RecordNotFound
nil
end
end
end
class Base
@@default_user_model = :users
cattr_accessor :user_model_name
def user_model_name
@@user_model_name || @@default_user_model
end
def self.relates_to_user_in(model)
self.user_model_name = model
end
def user_model
Object.const_get(user_model_name.to_s.singularize.humanize)
end
end
end
Then add the following to the bottom of /config/environment.rb
require 'usermonitor'
ActiveRecord::Base.class_eval do
include ActiveRecord::UserMonitor
end
Now if you add created_by or updated_by to your database tables, you will be able to access the user who created/updated the objects by calling object.created_by and object.updated_by respectively. Likewise you can set the user by assigning a user object to either one.
This defaults to assuming you have a user model called User, which can be overriden by calling relates_to_user_in in your model like so:
class WebPage < ActiveRecord::Base
relates_to_user_in :monkeys
end
The user model must contain “current_user” that references the currently logged in user.
An example of setting up a “current_user” method in your User model is as follows
In my model I have an accessor for current_user: ——user.rb—— cattr_accessor :current_user ————————- Then in my application controller I have a before_filter which grabs the user ID from the session (if there is one) and sets the current_user in the model. ——application.rb—— before_filter do |c| User.current_user = User.find(c.session[:user]) unless c.session[:user].nil? end ————————- So now you can call User.current_user to get the current logged in user.
——————————————
Note: an update to the blog post mentioned below says that thread safety is not actually an issue here …
I’m afraid that current_user method is not thread-safe, so I use Thread.current instead to retrieve currently logged-in user.
Find out more info at this blog post.
Here is the updated version with Thread.current
/app/controllers/application.rb
class ApplicationController < ActionController::Base
…. before_filter :set_current_user def set_current_user Thread.current‘user’ = session[:user] endend
/lib/usermonitor.rb
module ActiveRecord
module UserMonitor
def self.included(base)
base.class_eval do
alias_method_chain :create, :user
alias_method_chain :update, :user def current_user Thread.current‘user’ end end end def create_with_user user = current_user if !user.nil? self[:created_by] = user.id if respond_to?(:created_by) && created_by.nil? self[:updated_by] = user.id if respond_to?(:updated_by) end create_without_user end def update_with_user user = current_user self[:updated_by] = user.id if respond_to?(:updated_by) && !user.nil? update_without_user end def created_by begin current_user.class.find(self[:created_by]) if current_user rescue ActiveRecord::RecordNotFound nil end end def updated_by begin current_user.class.find(self[:updated_by]) if current_user rescue ActiveRecord::RecordNotFound nil end end endend
Note alias_method_chain is currently only available if you are using Rails on Edge.
If you are using the LoginGenerator, neither of the two beforementioned codes work! After working on a fix for several hours, I decided to post it here, so that others can possibly use this very usefull feature. I’ve merged code here and there from the two and am using the thread-safe Thread.current to store the current_user. There also was a typo (or maybe it works, but only on edge!) that prevented the current_user to be determined. The following code works with 1.1.6 and the newest LoginGenerator. As alias_method_chain is only supported on Edge I’m not using it.
application.rb (inside the class)
before_filter :set_current_user def set_current_user Thread.current‘user’ = @session‘user’ end
@environment.rb (insert before end of file)
require ‘usermonitor’
ActiveRecord::Base.class_eval do
include ActiveRecord::UserMonitor
end
/lib/usermonitor.rb
module ActiveRecord module UserMonitor def self.included(base) base.class_eval do alias_method :create_without_user, :create alias_method :create, :create_with_user alias_method :update_without_user, :update alias_method :update, :update_with_user def current_user Thread.current‘user’ end end end def create_with_user user = current_user if !user.nil? self[:created_by] = user.id if respond_to?(:created_by) && created_by.nil? self[:updated_by] = user.id if respond_to?(:updated_by) end create_without_user end def update_with_user user = current_user self[:updated_by] = user.id if respond_to?(:updated_by) && !user.nil? update_without_user end def created_by begin current_user.class.find(self[:created_by]) if current_user rescue ActiveRecord::RecordNotFound nil end end def updated_by begin current_user.class.find(self[:updated_by]) if current_user rescue ActiveRecord::RecordNotFound nil end end endend
That’s it!
category:Howto, Example