views:

64

answers:

2

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? :)

+1  A: 

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

user = User.new(:name=>'test')
user.send(:create_without_callbacks)
Joshua
Could you show me the `:create_without_callbacks` too? I suspect that's where the magic happens. :)
jkreeftmeijer
My understanding is that this calls a private method on the base class. Undocumented, but does work.
Joshua
A: 

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
Shinya Miyazaki
That's a solution, but I'd prefer disabling the callback instead of altering the method it calls. :)
jkreeftmeijer