1

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

Andrey Deineko
  • 51,333
  • 10
  • 112
  • 145
spoovy
  • 311
  • 1
  • 3
  • 13

1 Answers1

0

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
Andrey Deineko
  • 51,333
  • 10
  • 112
  • 145
  • ah, i was thinking along those lines but coming up short. I shall read up on 'extend'. And 'module_function'! Thanks very much. – spoovy Apr 21 '17 at 09:21
  • @spoovy N/p :) You could also achieve the same effect with few more options. Will edit in a moment (just in case you're interested :)) – Andrey Deineko Apr 21 '17 at 09:30