Search code examples
rubymethodsmoduleinclude

Include module into method ruby


I have used module to extend class functionality. But suddenly I wonder if it is ok if I include a module directly into a method of the class instead of into the class like this:

Original use:

model:

class Baby
  include CommunicationSkills

  def initialize(name)
    @name = name
  end
end

module:

module CommunicationSkills
  def greet
    "Hi"
  end
end

so we can:

ivan = Baby.new('Iván')
ivan.greet
=> "Hi"

but what if I include the method inside a method directly:

class Baby

  def initialize(name)
    @name = name
  end

  def greet(language)
    extend GreetLanguages
    send(language)
  end
end

module:

module GreetLanguages
  def spanish
    "Hola"
  end

  def english
     "Hi"
  end
end

so:

ivan = Baby.new('Iván')
ivan.greet('spanish')
=> "Hola"
ivan.greet('english')
=> "Hi"

I know that this is posible, but conceptually it is right?


Solution

  • The module contains two instance methods. You want to send them to an instance, not to the class, so you need to include the module (using Module#include), not extendit. To invoke include from within an instance method (self being the instance), you must send include to self.class. You therefore need to write the following.

    class Baby
      def initialize(name)
        @name = name
      end
      def greet(language)
        self.class.include GreetLanguages
        send(language)
      end
    end
    
    ivan = Baby.new('Iván')
    
    ivan.greet('spanish')
      #=> "Hola"
    Baby.instance_methods.include?(:english) &&
      Baby.instance_methods.include?(:spanish)
      #=> true
    ivan.greet('english')
      #=> "Hi"
    

    You can alternatively write ivan.greet(:spanish).

    You would use Object#extend if you wanted to convert the instance methods in the module to class methods when you bring them into the class.

    class Baby
      def initialize(name)
        @name = name
      end
      def extend_mod
        self.class.extend GreetLanguages
      end
    end
    
    Baby.new('Iván').extend_mod
    
    Baby.methods.include?(:spanish) &&
      Baby.methods.include?(:english)
      #=> true  
    Baby.spanish
      #=> "Hola"
    Baby.english
      #=> "Hi"
    

    Here self.class.extend GreetLanguages is equivalent to self.class.singleton_class.include GreetLanguages.