2

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?

sawa
  • 165,429
  • 45
  • 277
  • 381
delpha
  • 931
  • 1
  • 8
  • 23
  • 2
    Not clear what you mean by "these types of functions". What is special about your method? – sawa Oct 03 '15 at 11:24
  • I wan to call a method on any number anywhere in the application by just writing the variable name then dot then the name of the function. I do not want to pass the variable name as a parameter to the function – delpha Oct 03 '15 at 11:44
  • 1
    That is how you normally call methods. Nothing special. – sawa Oct 03 '15 at 11:46
  • ok, I thought there is a special way of doing that. Thanks a lot – delpha Oct 03 '15 at 11:52

3 Answers3

6

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.

Class? What 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.

Modifying the Float class

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

Modifying several related classes at once

You could of course now also define the function of Fixnum. But there are numbers that are neither Fixnums nor Floats. 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.

Community
  • 1
  • 1
das-g
  • 9,718
  • 4
  • 38
  • 80
1
class Float
  def my_format_number
    ...
  end
end
sawa
  • 165,429
  • 45
  • 277
  • 381
1

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
stevec
  • 41,291
  • 27
  • 223
  • 311