Search code examples
rubyincluderequire

'include'-ing a module, but still can't call the method


Why does the following code give the error below?

require 'open3'

module Hosts
  def read
    include Open3
    popen3("cat /etc/hosts") do |i,o,e,w|
      puts o.read
    end
  end
end

Hosts.read
#=> undefined method `popen3' for Hosts:Class (NoMethodError)

It works if I call popen3 using full path ie Open3::popen3. But I've include-ed it, so thought I wouldn't need the Open3:: bit?

Thanks


Solution

  • You have defined an instance method, but are trying to use it as singleton method. To make what you want possible, you also have to extend the Open3, not include:

    module Hosts
      extend Open3
    
      def read
        popen3("cat /etc/hosts") do |i,o,e,w|
          puts o.read
        end
      end
      module_function :read # makes it available for Hosts
    end
    

    Now:

    Hosts.read
    ##
    # Host Database
    #
    # localhost is used to configure the loopback interface
    # when the system is booting.  Do not change this entry.
    ##
    127.0.0.1 localhost
    255.255.255.255 broadcasthost
    ::1             localhost
    => nil
    

    Reading about the following concepts in Ruby will make things much clearer for you:

    • context

    • self

    • include vs extend

    Instead of module_fuction you could also achieve the same result with either of below:

    module Hosts
      extend Open3
      extend self
    
      def read
        popen3("cat /etc/hosts") do |i,o,e,w|
          puts o.read
        end
      end
    end
    

    And

    module Hosts
      extend Open3
    
      def self.read
        popen3("cat /etc/hosts") do |i,o,e,w|
          puts o.read
        end
      end
    end