Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ruby include method name collision

Tags:

ruby

If a class includes many modules, and the methods gained from the include call private methods from their originating modules, and private methods exist in other modules with the same name, it should use the definition from its own module. In code:

module C
  def c
    puts collision
  end
  private
  def collision
    'c'
  end
end
module B
  def b
    puts collision
  end
  private
  def collision
    'b'
  end
end
class A
  include B
  include C
end
A.new.b
# => 'c'

I expected A.new.b to give 'b' and A.new.c to give 'c' but there is collision because they use the same private method name. In large projects with many includes, how is this avoided?

like image 280
xxjjnn Avatar asked Sep 17 '17 14:09

xxjjnn


2 Answers

I assume the real question is: how do I ensure that the my modules are not using other modules methods, which (which accidentally have the same name).

It's best if you can make sure that your module methods are not state dependent and define them as module method and use them this way. So you explicitly use any helping methods like this:

module C
  def c
    puts C.collision
  end
  def self.collision
    'c'
  end
end

module B
  def b
    puts B.collision
  end
  def self.collision
    'b'
  end
end

class A
  include B
  include C
end

A.new.b # b
A.new.c # c

If you want to make them private, there's some extra code to put there, you can read a clear article about it: https://6ftdan.com/allyourdev/2015/05/02/private-module-methods-in-ruby/

As stated in this answer there's not magic you can turn on to make it work. Include mechanism is quite simple and (IMO) it should stay that way. As you are aware of this collision problem - you can now avoid it.

In case you don't like this approach, you can also encapsulate the real work in a class and use module just for inclusion:

  module C
    def c
      MyLibStuff::ClassDoingRealWork.no_collision_here_anymore(any, param, if_needed)
    end
  end
like image 131
Grzegorz Avatar answered Oct 16 '22 05:10

Grzegorz


You expect some magic, which is not there.

include is a plain old good mixin. Once called, it just “inserts” all the methods declared in the included module into the module that calls include.

Hence you end up with something like:

class A
public
  def c; ... end
private
  def collision; ... end
public
  def b; ... end
private
  def collision; ... end
end

And the latter private method collision obviously overrides the former one due to ruby open classes concept.

like image 44
Aleksei Matiushkin Avatar answered Oct 16 '22 06:10

Aleksei Matiushkin