-1

I've got a gem with a class that dynamically includes modules. I need to override all methods in those modules to insert some additional logic (for metrics tracking)

Example class:

module MyAPI
  class Client
    # This dynamically requires a bunch of modules  
    Dir[File.dirname(__FILE__) + '/client/*.rb'].each do |file|
      require file
      include const_get(File.basename(file).gsub('.rb','').split('_').map(&:capitalize).join('').to_s)
    end
  end
end

One of these modules might look like this:

module MyAPI
  class Client
    module Companies

      def get_company(company_id, params = {})
        puts "original method"
      end

    end
  end
end

I need to monkey patch this in my application. I have added a new file /config/initializers/monkey_patches/my_api.rb

module MyAPI
  class Client
    module MonkeyPatchCompanies
      def get_company(company_id, params = {})
        puts 'Monkey Patched'
        super
      end
    end
    module Companies
      include MonkeyPatchCompanies
    end
  end
end

I have tried all sorts of things like the above to add some functionality to all of the methods in the included modules.

Nothing I have tried has been successful. I get something like:

NoMethodError (undefined method `include?' for nil:NilClass)

What is the best way to monkey patch something like this?

EDIT:

I was able to solve this by first aliasing the the original method, then by calling the aliased original in my overridden method.

module_eval do
alias_method :get_company_old, :get_company
define_method :get_company do |*args|
  # My new code here
  send :get_company_old, *args
end

The problem is that super in this context does not work. This solved the issue for me.

Sean
  • 1,078
  • 14
  • 25
  • 2
    That error message is 100% unrelated to any of the code you posted. At no point in your code do you define nor call a method named `include?`, so the code you posted *cannot possibly* have anything to do with your problem. – Jörg W Mittag Oct 10 '19 at 02:29
  • ok... Calling MyAPI::Client.get_company throws this error with no stacktrace. You are suggesting that include? cannot possibly be used by ruby when you call "include MonkeyPatchCompanies" in a module? – Sean Oct 10 '19 at 15:05
  • First off, if you had a problem with the `include` method, the error message would be talking about the `include` method instead of the `include?` method. Secondly, if you call a method without an explicit receiver, then the implicit receiver is `self`, which inside the module is the module itself. However, the receiver in the error message is `nil`. The only way this can happen is if you have a call somewhere that looks like `foo.include?`, where `foo` is an expression that evaluates to `nil`. Since you don't have that in your code, the error message is not caused by the code. – Jörg W Mittag Oct 10 '19 at 15:50
  • Thanks for replying. I guess the point was instead of focusing on the error I was receiving (which may or may not have anything to do with my new code), I was simply looking for an answer to this question: "What is the best way to monkey patch something like this?" – Sean Oct 10 '19 at 15:58

1 Answers1

0

I was able to solve this by first aliasing the the original method, then by calling the aliased original in my overridden method.

module_eval do
alias_method :get_company_old, :get_company
define_method :get_company do |*args|
  # My new code here
  send :get_company_old, *args
end

The problem is that super in this context does not work. This solved the issue for me.

Sean
  • 1,078
  • 14
  • 25