4

I have to wrap some behavior around an external gem in a elegant and isolated manner. Given the abstraction below, everything runs smoothly, but 'bar' is never printed. Could someone tell me why?

My code:

module RefineGem
  refine GemMainModule::GemClass do
    def self.foo
      p 'bar'
      super
    end
  end
end

module Test
  using RefineGem

  def test
    GemMainModule::GemClass.foo
  end
end

class Testing
  include Test
end

Testing.new.test

Gem code:

module GemMainModule
  class Base
    include GemMainModule::Fooable
  end

  class GemClass < Base
  end
end

module GemMainModule
  module Fooable
    extend ActiveSupport::Concern

    class_methods do
      def foo
        p 'zoo'
      end
    end
  end
end
fabriciofreitag
  • 2,843
  • 22
  • 26

1 Answers1

2

I doubt refinements work for class methods. You might refine the singleton_class though:

module RefineGem
  refine GemMainModule::GemClass.singleton_class do
    def foo
      p 'bar'
      super
    end
  end
end

I personally prefer to use Module#prepend to achieve the same functionality:

GemMainModule::GemClass.singleton_class.prepend(Module.new do
  def foo
    p 'bar'
    super
  end
end)
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
  • I like this and it works, if I may be annoying and raise just one missing point for my situation: can I control the scope of usage? (like refinements do with the "using" keyword), I want the behavior to be overridden in a particular place and nowhere else. – fabriciofreitag Nov 10 '17 at 13:36
  • No, with `Module#prepend` you can not do that. Honestly, I never ever felt a necessity to deal with usage scopes. Even more: it sounds counterintuitive and more error-prone to me, that here `foo` works this way and there it works that way. – Aleksei Matiushkin Nov 10 '17 at 13:39