Question

I published an article on disabling ActiveModel callbacks, but I’m not completely sure this is the prettiest way to do something like this.

Mongoid::Timestamps adds a before save callback that updates the updated_at field. Let's say I don't want that in some cases and I disable the callback like this:

class User
  # I'm using Mongoid, but this should work for anything based on 
  # ActiveModel.
  include Mongoid::Document
  include Mongoid::Timestamps

  def sneaky_update(attributes)
    User.skip_callback(:save, :before, :set_updated_at)
    User.update_attributes(attributes)
    User.set_callback(:save, :before, :set_updated_at)
  end

end

Is calling skip_callback followed by set_callback to set the removed callback again a bad idea? How would you do this? :)

Was it helpful?

Solution

How about this?

module Mongoid
  module Timestamps
    attr_accessor :skip_updated_at

    def set_updated_at_new
      unless self.skip_updated_at
        set_updated_at_org
      end
    end

    alias set_updated_at_org set_updated_at
    alias set_updated_at set_updated_at_new
  end
end

class User
  # I'm using Mongoid, but this should work for anything based on 
  # ActiveModel.
  include Mongoid::Document
  include Mongoid::Timestamps

  def sneaky_update(attributes)
    self.skip_updated_at = true
    User.update_attributes(attributes)
    self.skip_updated_at = false
  end

end

OTHER TIPS

You can skip before save callbacks and validations by using send, for example

user = User.new(:name=>'test')
user.send(:create_without_callbacks)

Your current solution seems "dangerous" in that if the update raises an exception then the callbacks don't get put back into place which could seriously break any request after it. Self modifying code is a really bad idea if it can have persistent side effects for other threads, it's a bit like using globals.

But I have the seem problem and I have been looking all around for a solution and yours has been the best I could find until now. I think Rails may need to add a more elegant way to do this to the Callback module.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top