4.1. Defining Methods

Every object and class exposes several methods. Class methods are methods that can be invoked directly on classes, for example ActiveRecord's finders:

author = Author.find_by_last_name("Ginsberg")

Similarly, there are instance methods that can be invoked on any object of a given class, like the downcase method of a String, or to keep in line with ActiveRecord's example:

books = author.books

Then there are the so-called singleton methods, which are a special type of method that can be defined, and which exist only for a specific instance of a class:

my_string.my_method    # my_string has a singleton method
my_string2.my_method   # Raises a NoMethodError

Ruby doesn't really differentiate between the three of them when you call a given method on a class or an object. The interpreter only cares about determining whether or not the receiver exposes that method. When you use the syntax receiver.method, Ruby is actually sending a message with the name of the method, and its arguments, to the receiver object (be it an object, class, or module). As a matter of fact, the following two are equivalent, and the dot notation is just sugar syntax for the developer:

"antonio".capitalize          # "Antonio"
"antonio".send(:capitalize)   # "Antonio"

You can see the advantages of the dot notation when chaining multiple method calls together as shown here:

puts "$32.90".sub('$','Đ')                          # Prints Đ32.90
Kernel.send(:puts, "$32.90".send(:sub, '$', 'Đ'))   # Prints Đ32.90

As a reminder, the values placed in the inline comments can either refer to the output of an expression or the value it evaluates to. If a method like puts, p, or print is used, the output is shown in the comment (as opposed to the return value). If you were to run either of the two lines above in irb, you would see Đ32.90 printed, as well as a => nil to notify you of the much less interesting return value.

The method puts that you've used so far (as though it were a function rather than a method) is in reality a Kernel method. Kernel is a module included in Object, and as such its methods are available everywhere within Ruby programs. Remember, Ruby is purely object-oriented and as such every function is a method and it's defined, implicitly or explicitly, within an object, a class, or a module.

Using existing methods won't get you too far, so you can move on to creating methods of your own.

You define methods through the def statement and terminate the method definition with end:

def hello_antonio
  puts "Antonio"
end

hello_antonio   # Prints Antonio

You can create an alias method by writing alias :new_method_name :old_method_name outside of the method definition. Oddly enough, Ruby provides you with undef as well, so you could theoretically define a method, use it and then "undefine" it (for example, undef hello_antonio).

Methods can also accept one or more arguments as shown here:

def hello(name)
  puts name
end

def sum(x, y)
  x + y
end

hello("Antonio")    # Prints Antonio
puts sum(5, 3)      # Prints 8

Notice how there is no need to declare variable types and, as such, the hello method is able to print any type of argument that you pass to it. Similarly, the sum method works with any parameters whose class implements the + method/operator, be it numeric, string, array, or what have you. Note also how the sum method did not use the return keyword. This is optional in Ruby, given that by default the last evaluated expression in the method body is returned. This also means that the hello method defined in the preceding code returns nil, because that's the returning value of the puts method (which is the last, and only, expression that's evaluated in the body of the method). At times it's still useful to use return to make the returning value stand out or to insert multiple exit points within the method.

Ruby methods also support default arguments:

def greet(user = "guest")
  puts "Welcome #{user}!"
end

greet                # Prints Welcome guest!
greet("Matz")        # Prints Welcome Matz!

Methods can only have one default argument, and this has to be the last argument:

def power(x, y = 2)
  return x**y
end

puts power(5)        # Prints 25
puts power(5, 3)     # Prints 125

Unlike C# and VB, Ruby does not support method overload. This is to say that Ruby won't accept two identical identifiers with the same scope, which accepts different arguments. The polymorphic nature of Ruby's methods ensures that you don't have to create copies of the same method to handle integers, floating-point numbers, and so on, as you would in most compiled languages. The issue of variable arguments passed to the method still exists though, and you may want your method to accept one, two, or even three arguments. In Ruby you can use the splat operator (*) to define a method that accepts a variable number of arguments that are stored in an array:

def my_print(*list)
  p list
end

my_print                         # Prints []
my_print("a string")             # Prints ["a string"]
my_print(2, 5, 9)                # Prints [2, 5, 9]
my_print("user", "pass", true)   # Prints ["user", "pass", true]

This can also be used to define a list of mandatory arguments along with optional ones:

def collect_data(name, last_name, *info)
  # code...
end

You learn much more about methods later on in this chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.234.188