Refinements: can't build up mixin in 'using' context

54 Views Asked by At

Trying to extend functionality to ease usage/integration of IceCube in a Rails context, ended up using Refinements.

After encountering some limitations and reading some documentation, I am facing a last challenge that I have not seen documented anywhere. How comes the example below does not work.

The target class to extend functionality with some generic Utils is Foo:

class Foo; end

module Utils
  def bar; "bar"; end
end

This is done with a refinement called RefFoo, where some other refinements are meant to use Utils (i.e. call_bar):

module RefFoo
  refine Foo.singleton_class do
    include Utils
    def call_bar; bar; end
  end
end
  • Refining the class methods as explained in this post.

Let's say we are using the refinement within the context of a module called RefinedFoo (in the real case, a Rails service class):

module RefinedFoo
  using RefFoo

  class << self
    def bar;      Foo.bar;       end
    def call_bar; Foo.call_bar;  end
  end
end

Although the refinement is active, as shown by the result of the class method Foo::bar (via RefinedFoo::bar), invoking Foo::call_bar (via RefinedFoo::call_bar) throws a NameError:

RefinedFoo.bar
#=> "bar"
RefinedFoo.call_bar
#=> NameError (undefined local variable or method `bar' for Foo:Class)
  • Foo::call_bar exists, however, for Foo::call_bar the method Foo::bar does NOT exist, regardless include was called.

There seems not to be a way around it, if we rewrite RefFoo to include an already mixed module, the error persists:

module RefFoo
  module Mixin
    include Utils
    def call_bar; bar; end
  end
  refine Foo.singleton_class do
    include Mixin
  end
end

# define RefinedFoo the same way

RefinedFoo.call_bar
#=> NameError (undefined local variable or method `bar' for Foo:Class)

And yet, if you extend RefFoo in a module, the error won't show up:

def Bar
  extend RefFoo::Mixin
end

Bar.call_bar
#=> "bar"

Obviously, I could just create a new class called MyFoo that inherits and extends Foo (i.e. MyIceCube) and work from there. Before I do, it would be great if I could get a bit more of context. Among other readings, went through these:

Still unsure how the code above fits in any of the posts and articles. Are refinements just a method to implement very small and well localized (self-contained) patches? (i.e. can't include libraries of helpers to extend classes) and are NOT meant to be used for extending classes just in specific scopes of your app? Or I have just landed to this problem because I am trying to refine at class methods level?

0

There are 0 best solutions below