5

Recently I've been learning Ruby.I hit a problem while I'm writing a subclass of File.

class MyFile < File

end

file_path = "text_file"

file = MyFile.open(file_path) do | file |
    file.each_line do | line |
        puts line
    end
    file.close
end

result:

line 1
line 2
line 3

If I want output by calling a method:

class MyFile < File
    def foo
        self.each_line do | line |
            puts line
        end
    end
end

file_path = "text_file"

my_file = MyFile.open(file_path) do | file |
    file.foo
    file.close
end

Result:

/Users/veightz/Developer/RubyCode/io_error.rb:4:in `write': not opened for writing (IOError)
    from /Users/veightz/Developer/RubyCode/io_error.rb:4:in `puts'
    from /Users/veightz/Developer/RubyCode/io_error.rb:4:in `block in foo'
    from /Users/veightz/Developer/RubyCode/io_error.rb:3:in `each_line'
    from /Users/veightz/Developer/RubyCode/io_error.rb:3:in `foo'
    from /Users/veightz/Developer/RubyCode/io_error.rb:20:in `block in <main>'
    from /Users/veightz/Developer/RubyCode/io_error.rb:19:in `open'
    from /Users/veightz/Developer/RubyCode/io_error.rb:19:in `<main>'

Then I add new method bar

class MyFile < File
    def foo
        self.each_line do | line |
            puts line
        end
    end

    def bar
        self.each_line do | line |
            p line
        end
    end
end

my_file = MyFile.open(file_path) do | file |
    file.bar
    file.close
end

Result:

"line 1\n"
"line 2\n"
"line 3\n"

So, I'm so confused about IO in Ruby.Why puts line in foo can't work well.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
Veight Zhou
  • 969
  • 1
  • 7
  • 8

2 Answers2

6

There is a puts method in IO and IO is a superclass of File. That means that this:

puts line

is actually self.puts rather than Kernel#puts as it is pretty much everywhere else that you use puts. Hence the "not opened for writing" error message.

You need an explicit receiver to get the same effect as Kernel#puts; Kernel#puts is equivalent to $stdout.puts so you want:

file.each_line do | line |
  $stdout.puts line
end

Your p line version works fine because there is no IO#p or File#p method, p is Kernel#p just like it is everywhere else.

Keep in mind that we don't have functions in Ruby the way other languages have global functions. Methods that you use like a function are almost always methods in Kernel.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
0

You can also use $> "the default output stream" to redirect output of methods such as Kernel#puts... Which is the sole job of $> .

def foo
     self.each_line do | line |
     $>.puts line
     end
end

I added one more method showing how you can also borrow "require" other gems (e.g pretty-printed gem) and the use of class << slef....Hence , you said "Recently I've been learning Ruby" :|

The below works as intended.

#!/usr/bin/env ruby

require 'pp'

class MyFile < File
class << self

    def foo
        each_line do | line |
           $>.puts line
        end
    end



    def bar
        each_line do | line |
            p line
        end
    end


    def bam
     each_line do | line |
            pp line
        end
    end

end

file_path = "/Users/path/ofdirectory_to/somefile.txt"

my_file = MyFile.open(file_path) do | file |

    file.bam
    file.foo
    file.bar
    File.close
end

Note: the use of NameOfClass << self ... wana read more SO question

What seems btter :

non-pretty-printed output by p is:

  #<PP:0x81fedf0 @genspace=#<Proc:0x81feda0>, @group_queue=#<PrettyPrint::GroupQueue:0x81fed3c @queue=[[#<PrettyPrint::Group:0x81fed78 @breakables=[], @depth=0, @break=false>], []]>, @buffer=[], @newline="\n", @group_stack=[#<PrettyPrint::Group:0x81fed78 @breakables=[], @depth=0, @break=false>], @buffer_width=0, @indent=0, @maxwidth=79, @output_width=2, @output=#<IO:0x8114ee4>>

pretty-printed output by pp is:

 #<PP:0x81fedf0
 @buffer=[],
 @buffer_width=0,
 @genspace=#<Proc:0x81feda0>,
 @group_queue=
  #<PrettyPrint::GroupQueue:0x81fed3c
   @queue=
    [[#<PrettyPrint::Group:0x81fed78 @break=false, @breakables=[], @depth=0>],
     []]>,
 @group_stack=
  [#<PrettyPrint::Group:0x81fed78 @break=false, @breakables=[], @depth=0>],
 @indent=0,
 @maxwidth=79,
 @newline="\n",
 @output=#<IO:0x8114ee4>,
 @output_width=2>

For more about pp go here Ruby-Doc

Community
  • 1
  • 1
z atef
  • 7,138
  • 3
  • 55
  • 50