views:

213

answers:

1

Here's the situation:

I have a User model, and two modules for authentication: Oauth and Openid. Both of them override ActiveRecord#save, and have a fair share of implementation logic.

Given that I can tell when the user is trying to login via Oauth vs. Openid, but that both of them have overridden save, how do "finally" override save such that I can conditionally call one of the modules' implementations of it?

Here is the base structure of what I'm describing:

module UsesOauth

  def self.included(base)
    base.class_eval do
      def save
        puts "Saving with Oauth!"
      end

      def save_with_oauth
        save
      end
    end
  end

end

module UsesOpenid

  def self.included(base)
    base.class_eval do
      def save
        puts "Saving with OpenID!"
      end

      def save_with_openid
        save
      end
    end
  end

end

module Sequencer

  def save
    if using_oauth?
      save_with_oauth
    elsif using_openid?
      save_with_openid
    else
      super
    end
  end

end

class User < ActiveRecord::Base
  include UsesOauth
  include UsesOpenid
  include Sequencer
end

I was thinking about using alias_method like so, but that got too complicated, because I might have 1 or 2 more similar modules. I also tried using those save_with_oauth methods (shown above), which almost works. The only thing that's missing is that I also need to call ActiveRecord::Base#save (the super method), so something like this:

def save_with_oauth
  # do this and that
  super.save
  # the rest
end

But I'm not allowed to do that in ruby.

Any ideas for a clever solution to this?

Is that what alias_method_chain would do? I've avoided that because people seemed to say it was a bad idea.

(Finding things as I go):

+2  A: 

Yes alias method chain would help you in this situation.

But consider using delegate pattern. Original save method would trigger a callback on special delegate object (which can be as well nil) and it would do whatever needs to be done when saving user.

Also there is simliar pattern supported directly by actve record called Observer, try to read somethng about it maybe that's a good solution too.

I'm not saying this chaining methods is wrong, but there are cleaner ways to achieve what you want.

Vojto