Ben
Ben

Reputation: 862

How do I extend a Ruby class inside a module?

I'm trying to extend the File class inside a module. Here's my (abbreviated) code:

module Wireshark

   # Extend the file class to write a header comment
   class File
     def write_header
        self.puts '/* ' + Wireshark::AUTOGEN_LABEL + ' */'
        self.puts '/* ' + Wireshark::timestamp + ' */'
     end
   end

   # Read a file
   def read
     begin
        file = File.read 'file'
     rescue IOError
        STDERR.puts 'Error reading file.'
        return
     end
   end
end

When I run my code, I'm getting

undefined method `read' for Wireshark::File:Class (NoMethodError)

when I try to run file.read. I tried getting rid of the module encapsulation, but I'd like to only extend the File class inside my module, not in the rest of my program.

Upvotes: 1

Views: 105

Answers (2)

Chris Heald
Chris Heald

Reputation: 62638

You're close.

module Wireshark
   module File
     def write_header
        self.puts '/* ' + Wireshark::AUTOGEN_LABEL + ' */'
        self.puts '/* ' + Wireshark::timestamp + ' */'
     end
   end

   # Extend File with the methods in the Wireshark::File module
   ::File.send :include, Wireshark::File

   # Read a file
   def read
     begin
        file = ::File.read 'file'
     rescue IOError
        STDERR.puts 'Error reading file.'
        return
     end
   end
end

The general idea here is that we define a Wireshark::File module that holds the methods you want to include on the File class, then you can just include them on File directly.

You'll also notice that in the read method, I changed File to ::File. Ruby will walk up the tree to try to find the nearest matching value for a given constant, so since you are in the Wireshark module, and there is a constant named File in that scope, using just File gets you Wireshark::File. Specifying ::File means "The File constant at the top-level namespace".

Upvotes: 1

tadman
tadman

Reputation: 211560

In the current version of Ruby it's not possible to do this, but it is a popular proposed extension called "refinements".

If you need to patch the core File class, you'll need to do that as a monkey patch, and this affects all instances of File anywhere in your Ruby process.

Normally you can do it this way:

# Define the instance methods you want to overide
module MyFileHacksInstanceMethods
  def read
    # ... (reimplementation) ...
  end
end

# Force load these in the File class
class File
  include MyFileHacksInstanceMethods
end

What you've declared in your example here is an independent class called Wireshark::File because it's within that module's namespace. ::File is the main class (:: being a prefix to force absolute name, sort of like / for filesystems).

Upvotes: 0

Related Questions