Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get Devise's current_user in ActiveRecord callback in Rails?

I'm using Devise and Rails 3.2.16. I want to automatically insert who created a record and who updated a record. So I have something like this in models:

before_create :insert_created_by
before_update :insert_updated_by

private
def insert_created_by
  self.created_by_id = current_user.id
end
def insert_updated_by
  self.updated_by_id = current_user.id
end

Problem is that I get the error undefined local variable or method 'current_user' because current_user is not visible in a callback. How can I automatically insert who created and updated this record?

If there's an easy way to do it in Rails 4.x I'll make the migration.

like image 601
at. Avatar asked Jan 02 '14 10:01

at.


2 Answers

Editing @HarsHarl's answer would probably have made more sense since this answer is very much similar.

With the Thread.current[:current_user] approach, you would have to make this call to set the User for every request. You've said that you don't like the idea of setting a variable for every single request that is only used so seldom; you could chose to use skip_before_filter to skip setting the User or instead of placing the before_filter in the ApplicationController set it in the controllers where you need the current_user.

A modular approach would be to move the setting of created_by_id and updated_by_id to a concern and include it in models you need to use.

Auditable module:

# app/models/concerns/auditable.rb

module Auditable
  extend ActiveSupport::Concern

  included do 
    # Assigns created_by_id and updated_by_id upon included Class initialization
    after_initialize :add_created_by_and_updated_by

    # Updates updated_by_id for the current instance
    after_save :update_updated_by
  end

  private

  def add_created_by_and_updated_by
    self.created_by_id ||= User.current.id if User.current
    self.updated_by_id ||= User.current.id if User.current
  end

  # Updates current instance's updated_by_id if current_user is not nil and is not destroyed.
  def update_updated_by
    self.updated_by_id = User.current.id if User.current and not destroyed?
  end
end

User Model:

#app/models/user.rb
class User < ActiveRecord::Base
  ...

  def self.current=(user)
    Thread.current[:current_user] = user
  end

  def self.current
    Thread.current[:current_user]
  end
  ...
end

Application Controller:

#app/controllers/application_controller

class ApplicationController < ActionController::Base
  ...
  before_filter :authenticate_user!, :set_current_user

  private

  def set_current_user
    User.current = current_user
  end
end

Example Usage: Include auditable module in one of the models:

# app/models/foo.rb
class Foo < ActiveRecord::Base
  include Auditable
  ...
end

Including Auditable concern in Foo model will assign created_by_id and updated_by_id to Foo's instance upon initialization so you have these attributes to use right after initialization, and they are persisted into the foos table on an after_save callback.

like image 163
vee Avatar answered Sep 22 '22 11:09

vee


another approach is this

class User
class << self
  def current_user=(user)
    Thread.current[:current_user] = user
  end

  def current_user
    Thread.current[:current_user]
  end
end
end

class ApplicationController
  before_filter :set_current_user

  def set_current_user
    User.current_user = current_user
  end
end
like image 31
HarsHarI Avatar answered Sep 21 '22 11:09

HarsHarI