I want to call a function named my_format_number
like:
num = 5.5
formated_num = num.my_format_number
How can I do that in Ruby? And what is the name of these types of functions?
I want to call a function named my_format_number
like:
num = 5.5
formated_num = num.my_format_number
How can I do that in Ruby? And what is the name of these types of functions?
what is the name of these types of functions?
In object oriented programming (independent of programming language), a function that is bound to an object (often through the object's class) is called a 'method'. Some Ruby documentation calls all functions 'methods', though. For the sake of distinction, I'll use 'method' only to mean bound functions and keep referring to functions in general as 'functions'.
A function not bound to an object is usually called a 'free function'.
The object to which a method is bound is called the method's 'receiver'.
How can I do that in Ruby?
In Ruby, methods are always instance methods: They are defined on the class, but act on instances of the class. (I.e., the receiver must be an instance.) Other than in C/C++ or Java, all values in Ruby are objects (instances of classes), so every value has a class.
A class is the type of a value/object/instance. It determines (to some degree) the value's semantic, as well as the methods available on that value.
What is the class of 5.5
? Let's find out:
(5.5).class()
# => Float
(The shorter 5.5.class
would have worked, too, but is a bit harder to understand.)
Float
is Ruby's built-in class for floating point numbers.
Float
classClass definitions in Ruby can be split up and distributed. Each chunk will have effect after having been interpreted, additional to the chunks already in effect, which means you can modify classes at runtime. This is known as 'monkey patching' or 're-opening a class'.
Let's re-open the Float
class and add the my_format_number
method. I decided it shall return a string:
class Float
def my_format_number
return "The number is #{self} and #{self} is the number."
end
end
(The return
in front of the string could be skipped, as Ruby functions implicitly return the last expression they evaluate.)
Now you can call your new method on all Float
values:
num = 5.5
formatted_num = num.my_format_number()
puts(formatted_num)
# The number is 5.5 and 5.5 is the number.
We don't even need variables and can invoke the method directly on value literals:
puts 7.8.my_format_number
# The number is 7.8 and 7.8 is the number.
It won't work for all numbers, though:
100.my_format_number
# NoMethodError: undefined method `my_format_number' for 100:Fixnum
# from (irb):15
# from :0
That's because we defined the number of Float
and 100
is (as the error message tells us) not a Float
but a Fixnum
(a special kind of Integer
).
You could of course now also define the function of Fixnum
. But there are numbers that are neither Fixnum
s nor Float
s. So it would make sense to have the function at some central place.
We could monkey patch Ruby's master class, Object
. But that would allow
"foobar".my_format_number
to return "The number is foobar and foobar is the number."
, which doesn't make sense, as foobar
is not a number. I'd only want our method to format numbers.
Let's see how the classes of Fixnum
are structured:
Fixnum.superclass()
# => Integer
Fixnum.superclass().superclass() # equivalent to `Integer.superclass`
# => Numeric
Fixnum.superclass().superclass().superclass() # equivalent to `Numeric.superclass`
# => Object
The superclass
method only gives us one direct ancestor. Using Ruby modules, multiple inheritance is possible, thus we can get some more results like so:
Fixnum.ancestors
# => [Fixnum, Integer, Precision, Numeric, Comparable, Object, Kernel]
Let's see what the overlap is with
Float.ancestors()
# => [Float, Precision, Numeric, Comparable, Object, Kernel]
Comparable
is probably more general than just numbers, while Precision
might be too specific or even somewhat unrelated. (It's probably a module.) Let's modify Numeric
thus, which (guessing from it's name) sounds like the right level of abstraction. Indeed, the documentation calls it
The top-level number class.
Therefore, very similar to before,
class Numeric
def my_format_number
return "The number is #{self} and #{self} is the number."
end
end
And now
100.my_format_number
# => "The number is 100 and 100 is the number."
If you want to know all the classes to which we've added the method in this second monkey-patching, have a look at Look up all descendants of a class in Ruby.
I thought I'd add a simple example that demonstrates how to make and call a method using the .
def plus_two
self + 2
end
2.plus_two
=> 4