I want to create a custom method on Object class and Array class. It works perfectly with array of Integer, Strings, etc. but not for File. Weird!
class Object
def foo
puts("Object#foo on #{self.inspect}")
end
end
class Array
def foo
puts("Array#foo on #{self.inspect}")
self.each do |e|
e.foo
end
end
end
Then,
a = [1,2]
a.foo
Displays:
Array#foo on [1, 2]
Object#foo on 1
Object#foo on 2
But,
b = [File.new('f1','w'),File.new('f2','w')]
b.foo
Only displays:
Array#foo on [#<File:f1>, #<File:f2>]
Your issue is not Object#foo
, instead it is the fact that you are assuming puts
is Kernel#puts
; however, in the context of File
it is actually IO#puts
.
puts(obj, ...) → nil
Writes the given object(s) to ios. Writes a newline after any that do not already end with a newline sequence. Returns
nil
.The stream must be opened for writing. If called with an array argument, writes each element on a new line. Each given object that isn’t a string or array will be converted by calling its
to_s
method. If called without arguments, outputs a single newline.
So instead of outputting to STDOUT
as you are expecting it is writing that puts statement to the file (which is the ios in this case).
To solve this you can call Kernel#puts
explicitly:
class Object
def foo
Kernel.puts("Object#foo on #{self.inspect}")
end
end
Then
b = [File.new('f1','w'),File.new('f2','w')]
b.foo
# Array#foo on [#<File:f1>, #<File:f2>]
# Object#foo on #<File:f1>
# Object#foo on #<File:f2>
# => [#<File:f1>, #<File:f2>]
Pedantic implementation detail
Technically speaking Kernel#puts
is $stdout.puts
, which is IO#puts
where $stdout
is the ios but when you call puts
(without a receiver) in the general context of Object
(including main
) it is the Kernel#puts
method you are calling because of the Kernel
module's inclusion in Object
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With