1

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.

2 Answers 2

1

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".

Sign up to request clarification or add additional context in comments.

6 Comments

Could I do something like includeing ::File in the Wireshark::File module so I don't have to use file = ::File in the read method?
That likely won't work. You could alias ::File to another constant (RealFile = ::File) then RealFile.open` or whatever, but that would be unconventional and is not recommended. ::File is self-descriptive and standard Ruby, so it will be easily maintained by other programmers.
@burwell: You have two classes, both named File, both with similar responsibilities, both used in the same context. That just reeks of incredibly bad design. The fact that you have to jump through hoops to work with that design is not an inconvenience that you need to work around, it's trying to tell you something, namely that you have an extraordinarily convoluted design that needs fixing.
@Chris, thanks for your reply. I'll just stick with ::File. @Jörg, I'm not sure that you're understanding the situation. I don't want to have two classes; I'd like to just extend Ruby's File class. The practice of "extending" classes to create more specific sub-types is extremely common and nothing revolutionary. I am just new to Ruby, and don't know the "ruby way" to accomplish this.
@burwell: I think I understand now, you are looking for something like scoped monkeypatching? I think "extremely common and nothing revolutionary" is a gross mischaracterization of that. Scoped monkeypatching is still very much an open research problem, if you have found a way to make that "extremely common", that's a PhD right there. The Smalltalk community is researching selector namespaces, the Java community classboxes, the Ruby community has toyed with both of those and is now experimenting with refinements, but none of those is even close to production-ready.
|
0

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).

1 Comment

You could 'fix' your code by using this line: class ::File, but as @tadman says, this will patch File everywhere.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.