How to override Ruby + method? - ruby

Just for learning purpose I am trying to override the Ruby + method, but I am not getting the desired output.
class Integer
def +(my_num)
"Plus method overridden"
end
end
puts 5.+(9)
Please let me know what I am doing wrong here.

It seems you use ruby < 2.4. If so you want to patch Fixnum and not Integer. Be careful as the system itself uses numbers as well.
class Fixnum
alias_method :add, :+
def +(other)
puts 'plus method overridden'
add(other)
end
end
puts 5 + 9

The Ruby Language Specification allows Integer to have implementation-specific subclasses. See Section 15.2.8.1, lines 27-33.
It looks like your implementation does have such subclasses. In that case, the + method may be overridden in a subclass.
My best guess is that you have an implementation which distinguishes between Fixnums and Bignums, and that our Integer#+ gets overridden by Fixnum#+.
By the way, even if what you were trying to do were working, it wouldn't be overriding, it would be overwriting.
Also note that if what you were trying to do were working, you would have most likely broken your Ruby process, since Integers are fundamental and widely-used all over the place in Ruby.

Related

Ruby Monk - Understanding Inheritance - Alternate solution not accepted?

I have been coding for three or four months (started in Python) and I am just getting into Ruby on account of Rails popularity.
To help build my understanding of the Ruby language, I have been going through the problems on Ruby Monk. The Ruby Primer: Ascent 1.1 - Understanding Inheritance course has the following problem:
Write a method that takes a class and a subclass as arguments and returns a boolean regarding whether or not the subclass is an ancestor of the class.
Here is what I came up with (note: Ruby Monk decided to go with spelling "klass" for "class"):
def is_ancestor?(klass, subclass)
subclass.ancestors.map{ |ancestor| ancestor.to_s }.include? klass.to_s
end
This code passes all tests except for a special one that states doesn't use any other methods to solve the problem (yes, there's a shortcut :)).
I was really vexed as to how to solve this without using other methods, and so I looked at the proposed solution. Here is what Ruby Monk says that answer should be.
def is_ancestor?(klass, subclass)
current_class = subclass
while !current_class.superclass.nil? && current_class != klass
current_class = current_class.superclass
end
current_class == klass
end
I understand this code. What I don't understand is why this code passes the test requirement of not using methods while my code doesn't. After all, the Ruby Monk proposed answer does use methods (see !current_class.superclass.nil).
Am I missing something here? Perhaps I don't really understand what a method is. Perhaps my code does work and is only failing because Ruby Monk is performing tests that match code 1:1.
Maybe they don't want you to use map and include? since they are not methods of classes. They are methods of arrays.
I guess this shall pass the tests.
def is_ancestor?(klass, subclass)
subclass <= klass
end
Well, strictly speaking, <= is also a method.
BTW, if you can compare classes directly, don't compare their names. You original code can be optimized as
def is_ancestor?(klass, subclass)
subclass.ancestors.include? klass
end

NoMethodError: undefined method `type' for 8:Fixnum

Working through an intro Ruby exercise with this code:
num = 8
7.times do
print num.type, " ", num, "\n"
num *= num
end
I keep getting:
NoMethodError: undefined method `type' for 8:Fixnum
Do i have to define type? I thought this was a method that ruby recognized
The type method used to return an object's class but was deprecated a long time ago (back in the 1.8 days) and subsequently removed.
You can use the class method instead, however if you are following a tutorial or something similar this is a sign that it is very old - possibly 10 years old!
By the type you probably meant class, so change type to class:
num = 8
7.times do
print num.class, " ", num, "\n"
num *= num
end
I had the same problem with method 'type' while working through Programming Ruby: The Pragmatic Programmer's Guide. The purpose of this exercise was to show that integers are stored in objects of classes Fixnum and Bignum, both subclasses of class Integer(Bignum for bigger numbers). Also, to show that Ruby automatically manages the conversion back and forth.
But since Feature #12005 in Ruby 2.4, Fixnum and Bignum have been unified into Integer. With them gone, the Object#type method, too, was gone. Note that Object#class method, will not show the distinction between Fixnum and Bignum in this exercise(recognizes both as Integer). So, yeah, the only thing this exercise will teach us now is a bit of history about Ruby.
If you want to know more about these two classes, check the first exercise on 'Standard Types' from the book.

Is there a short way to write `{|x| x}`?

We often shorten a block using the & notation on a symbol like this:
some_array.group_by(&:foo)
Is there a similar way to shorten expressions like {|x| x}?
some_array.group_by{|x| x}
If there were a method Object#self that returns self, then we can do
some_array.group_by(&:self)
but unfortunately, there is no such method. In terms of the number of characters, it may be longer, but readability improves.
Yes. #itself was implemented in Ruby 2.2.0.
You can access the Ruby core team discussion about this feature here.
As an interesting analogue, the #ergo method has been proposed, which would yield the receiver to a given block.
If you haven't yet upgraded to Ruby 2.2.0, you may wish to backport #itself and/or define #ergo as follows:
class Object
def itself; self end
def ergo
fail ArgumentError, "Block expected!" unless block_given?
yield self
end
end
And then:
some_array.group_by &:itself
Well, there's no built-in as far as I know, but you can make a reusable identity block:
id = Proc.new {|x| x}
some_array.group_by(&id)
And then if you really wish this were a language feature:
class Object
def it
Proc.new {|x| x}
end
end
And then you can do:
some_array.group_by(&it)
wherever you like. This may void your warranty.
Yes! The method Kernel#itself was added in Ruby 2.2.0. This method simply returns the object it was called on, so you can write:
some_array.group_by(&:itself)
You can see the extensive discussion of this feature here: https://bugs.ruby-lang.org/issues/6373. The patch was submitted by Rafael França in message #53. You can see it in the official Ruby source by looking in object.c.
If you are using a version of Ruby older than 2.2.0, you can easily add Kernel#itself into your project by putting this code somewhere in your project and making sure it gets required:
module Kernel
def itself
self
end
end if !Kernel.instance_methods.include?(:itself)
However, monkey-patching a part of the Ruby core like that can be dangerous and I would not recommend it if you are making reusable code, like a gem. Instead I would recommend just making your own identity function, as suggested by user2246674:
module MyLibrary
IDENT = Proc.new { |x| x }
array.group_by(&IDENT)
end

How to use Ruby Module?

The following code works fine:
class Float
def round
self.format.to_f
end
def format
"%.2f" % self
end
end
However, it seems bad practice to monkeypatch a class like Float because other people are doing the same thing and it causes problems.
Then I installed simplecov and the problem started: simplecov monkeypatches the same methods.
So I created a module and mixed it in to Float.
module MyModule
def round
self.format.to_f
end
def format
"%.2f" % self
end
end
Which I guess works as well. But the problem is that simplecov seems to be overwriting the mixed-in method above.
So, what is the proper way to extend built-in classes so that they do not conflict with other people's code?
Ruby 1.9.3
Why not use just argument on the round call?
13.6657.round(2) # => 13.67
But if you are sure you need module (to possibly adjust the format for all Floats out there, I'd propose you just define format method as such:
module MyModule
def format
("%.2f" % self).to_f
end
end
And mix this in to Float.
And later in code you call the format method instead of round:
13.6657.format # => 13.67
This way it does not hurt the core functionality (as your initial code dropped the argument from the round definition).
Even better - if you want (can) pinpoint the monkey-patching, simply extend specific instance:
a = 13.6657
a.extend MyModule
a.format # => 13.67
This way it wont mess with other Floats, but you can still adjust the format without finding all calls to a.round(2) in your code.

Extending Array hides initializer?

I'm using ruby 1.8.7
my_array = Array.new(5, "A")
works fine. However if I extend the Array class like this:
class Array
def my_function
self
end
end
then the initializer no longer works and returns
wrong number of arguments (2 for 0)
Why is this and how do I fix it?
Are you doing this inside a Module? If so, you are defining a separate Array class, rather than extending Array from the standard library.
Make sure your extension is at the top level, or else write:
class ::Array
(By the way, is there a good reason why you want to use Ruby 1.8.7? If not, I would recommend using an up-to-date version -- Ruby has improved over the years.)

Resources