In the Rails Recipes book the recipe "Keeping track of who did what" explains how to do decoupled observing of models. In my application I''d like to do a similar thing but watching the controllers. For example, when somebody hits the login method of the Security controller, I''d like to make a note of it. In this case I could observe the User model and watch for authenticate?, but in the case of some other actions in the application (e.g. logging out) I cannot see how to observe the model and get the same results. Is it possible to observe the controllers in any way? Thanks. -- Posted via http://www.ruby-forum.com/.
> Is it possible to observe the controllers in any way?Use a filter/observer monster. See the cache sweeper implementation for example. It''s basically an observer with filter callbacks. In your case, you could store the controller/action names in an intance variable during the after_filter, then use before_save to set stuff in the record.
On 2006-08-01, at 09:49 , Caio Chassot wrote:> In your case, you could store the controller/action names in an > intance variable during the after_filterThat would be a before_filter, or else it''ll not be set by the time the record is saved.
Caio Chassot wrote:>> Is it possible to observe the controllers in any way? > > Use a filter/observer monster. See the cache sweeper implementation > for example. > > It''s basically an observer with filter callbacks. > > In your case, you could store the controller/action names in an > intance variable during the after_filter, then use before_save to set > stuff in the record.This is fine, but what about the case where I want to record (for example) the user choosing "logout". In this case, there is no model to observe with before_* or after_*. The cache_sweeper example I have seen observes one model and only certain events, using the before and after filters. I would like to observe multiple controllers in my application. Is it better to just put the logging statements in the code, or can this be done in a decoupled way? -- Posted via http://www.ruby-forum.com/.
On 2006-08-01, at 11:04 , David wrote:> This is fine, but what about the case where I want to record (for > example) the user choosing "logout". In this case, there is no > model to > observe with before_* or after_*. > > The cache_sweeper example I have seen observes one model and only > certain events, using the before and after filters. I would like to > observe multiple controllers in my application. > > Is it better to just put the logging statements in the code, or can > this > be done in a decoupled way?True. So you just need a filter. I assume you keep track of the current user in the session. So: after_filter :log_action def log_action User.find(sesson[:user_id]).actions.create( :controller => controller_path, :action => action_name) end
Caio Chassot wrote:> On 2006-08-01, at 11:04 , David wrote: >> this >> be done in a decoupled way? > > True. So you just need a filter. > > I assume you keep track of the current user in the session. So: > > after_filter :log_action > > def log_action > User.find(sesson[:user_id]).actions.create( > :controller => controller_path, > :action => action_name) > endThat''s great, thank you. I guess I can use that in the application controller then filter out only the actions I am interested in saving information about. Should I add the after_filter to the application controller and then observe it from the cache sweeper, or just add both to the controller? Do you have any advice on how to manage the mapping of which actions I wish to save audit information about? A hash of their names perhaps? -- Posted via http://www.ruby-forum.com/.
On 2006-08-01, at 12:18 , David wrote:> That''s great, thank you. I guess I can use that in the application > controller then filter out only the actions I am interested in saving > information about.Yea, I intended it to be in the application controller.> Should I add the after_filter to the application controller and then > observe it from the cache sweeper, or just add both to the controller?You don''t need to observe anything, do you? I think you can handle it all from the controller. I messed up in my previous suggestion, for a while I thought you asked about something else: I''d use that observer/filter pattern to set a user_id field whenever a record is saved.> Do you have any advice on how to manage the mapping of which actions I > wish to save audit information about? A hash of their names perhaps?A hash is fine. You can go fancy with a DSL class method, so: In application.rb: def self.audit_actions *actions write_inheritable_array :audit_actions, [actions].flatten.compact end Elsewhere: class SecurityController < ... audit_actions :login, :logout, :change_password And my log_action, modified: def log_action return unless read_inheritable_attribute( :audit_actions).include?(action_name) User.find(sesson[:user_id]).actions.create( :controller => controller_path, :action => action_name) end
Caio Chassot wrote:> A hash is fine. You can go fancy with a DSL class method, so:I really like this idea, thank you. It is very neat.> In application.rb: > > def self.audit_actions *actions > write_inheritable_array :audit_actions, [actions].flatten.compact > endNo errors here.> And my log_action, modified: > > def log_action > return unless read_inheritable_attribute( > :audit_actions).include?(action_name) > User.find(sesson[:user_id]).actions.create( > :controller => controller_path, > :action => action_name) > endHere I''m getting: undefined method `read_inheritable_attribute'' for <SecurityController:0xb742fe68> I''ve googled around and read_inheritable_attribute seems to have been in rails for ages, so my web application should definitely have it (and obviously write_inheritable_array seems to be working). Am I missing something here? The code is: def log_action return unless read_inheritable_attribute(:audit_actions).include?(action_name) logger.info "THIS ACTION SHOULD BE LOGGED" end For the moment I''m just using logger.info, I''ll obviously put it in the DB long term. Thanks. -- Posted via http://www.ruby-forum.com/.
> -----Original Message----- > From: rails-bounces@lists.rubyonrails.org [mailto:rails- > bounces@lists.rubyonrails.org] On Behalf Of David > Sent: Tuesday, August 01, 2006 5:27 AM > To: rails@lists.rubyonrails.org > Subject: [Rails] Decoupled observers for controllers? > > In the Rails Recipes book the recipe "Keeping track of who did what" > explains how to do decoupled observing of models. > > In my application I''d like to do a similar thing but watching the > controllers. For example, when somebody hits the login method of the > Security controller, I''d like to make a note of it. In this case I > could observe the User model and watch for authenticate?, but in the > case of some other actions in the application (e.g. logging out) I > cannot see how to observe the model and get the same results. > > Is it possible to observe the controllers in any way?I use a before filter. I set up a model for activities, which keeps the user, an activity (some text) and a created_on date. /app/models/activity.rb class Activity < ActiveRecord::Base belongs_to :user end /app/controllers/application.rb, # Log requests to the activity log protected def activity_log(activity_text=request.env[''REQUEST_URI'']) if logged_in? u = current_user u.activities.create( :description => activity_text) end true end Then, in any controller I want to log before_filter :login_required before_filter :activity_log In the special case of the login controller, in order to catch the login event, I have: after_filter :activity_log Note that I only care about activities of logged in users, and my code accounts for that. I suppose one could log all activities to a ''guest'' account should they so desire. Regards, Rich