Search code examples
ruby-on-railsrubyrspecfactory-bot

Ruby, Rails and aliasing - stack too deep problem


My problem is with calling old aliased method in Test environment. Probably only with Factory Girl

app/models/user.rb

class User < ActiveRecord::Base
  ...
  include Authentication
  include Authentication::ByPassword
  ...

  attr_accessor :skip_password_validations

  alias old_password_required? password_required?
  # alias_method :old_password_required?, :password_required?
  def password_required?
    return false if !!skip_password_validations
    old_password_required?
  end

  # def password_required_with_skip_validations?
  #   return false if !!skip_password_validations
  #   password_required_without_skip_validations?
  # end
  # alias_method_chain :password_required?, :skip_validations  
end

vendor/plugins/../by_password.rb

module Authentication
  module ByPassword
    # Stuff directives into including module
    def self.included(recipient)
      recipient.class_eval do
        include ModelInstanceMethods
        attr_accessor :password
        validates_presence_of :password, :if => :password_required?
      end
    end # #included directives

    module ModelInstanceMethods 
      def password_required?
        crypted_password.blank? || !password.blank?
      end
    end # instance methods
  end
end

spec/models/user_spec.rb

describe User do
    it 'test' do
      @user = Factory(:user)
      @user.should be_valid
    end
 end

spec/factories/user_factories.rb

FactoryGirl.define do
  factory :user do |u|
    u.sequence(:email) { |n| "login#{n}@example.com" }
    u.password 'qwertz123'
    u.password_confirmation 'qwertz123'
  end
end

Result is

1) User test
     Failure/Error: Unable to find matching line from backtrace
     SystemStackError:
       stack level too deep
     # /Users/schovi/.rvm/gems/ruby-1.9.2-p180/gems/activerecord-3.1.0.rc4/lib/active_record/connection_adapters/abstract/database_statements.rb:197

When i debug it with debugger gem, I found that method old_password_required? point to the new method password_required? in user.rb and not in vendor/plugins/../by_password.rb

Get same result with alias, alias_method or alias_method_chain

Any idea?


Solution

  • My opinion is: Using alias in Ruby is often a pain in the ass. So how about moving your password_required? into it's own Module and the calling super inside it, to call password_required? from Authentication::ByPassword::ModelInstanceMethods like so:

    class User < ActiveRecord::Base
      module PasswordRequired
        def password_required?
          return false if !!skip_password_validations
          super # Is going to call Authentication's password_required?
        end
      end
    
      include Authentication
      include Authentication::ByPassword
      include PasswordRequired
    
      ...
    
    end