
Ruby Methods
A method in Ruby is a set of expressions that return a value. These are similar to functions in other programming languages. Methods are defined using the def keyword, followed by the method name and then by optional parameters. The method body is enclosed between the preceding definition and the end keyword at the bottom:
def my_method
##method body
end
By convention, method names should begin with a lowercase letter, otherwise Ruby might consider it to be a constant while parsing. Also, names that have multiple words should be separated by an underscore. As in the preceding examples, the method name – my_method, has an underscore between two words.
Passing Arguments to a Method
We can pass arguments to the method on which a method has to operate. There is no limit in terms of the number of parameters that we can pass to a method. The following is a simple example of how we can create our own methods in Ruby:
def add_two_numbers a, b
puts "Sum of the number is #{a+b}"
end
add_two_numbers(10, 20)
The output will appear as follows:

Figure 2.58: Output for adding two numbers
As we can see from the preceding example, a method, add_two_numbers, is defined with the variables a and b. The method body comprises the display message, suggesting what method is used. The use of the parentheses ( and ) is optional. As a good practice, we should choose one approach in a project and follow it throughout.
Ruby Methods with Default Values
We can also define default values as parameters in the methods. In case the caller wants to use the default values, they may choose not to pass any parameter to the optional one. In case the caller wants to override it, they can always do that by passing that extra parameter.
Let's take this up with an example where we almost always want to calculate 50% of a particular value. In only 5% of the cases, this value goes to 70%:
def percent_of_value a, b=50
puts "Percent value is #{(a*b)/100}"
end
percent_of_value 10
percent_of_value 10, 70
The output will be as follows:

Figure 2.59: Output for percentage values
As we can see in the preceding example, in the first call, we do not provide the value of b. Since b is an optional value, the default value is taken, and the answer turns out to be 5. However, in the second call, we are explicitly passing the value of b. This value overrides the default value and, hence, the output is evaluated to 7.
However, these default values should always be the last parameter. Currently, there is no way where a value on the left can be a default and, when we pass just one parameter, as in the preceding case, it takes that value as a mandatory field. So, we have a default value that is stated by a=50 and a mandatory argument, which is the b.
Now, let's make our problem a little more difficult. Reverse the optional and required values and see the output:
def print_values a = 50, b
puts "value of a is #{a}"
puts "value of b is #{b}"
end
print_values 1
The output will be as follows:

Figure 2.60: Output after reversing the optional and required values
As we can see from the preceding example, the Ruby program was smart enough to decide which value should be given to which parameter. But actually, it shows the internal logic of Ruby; if we have an argument with a default value, it is skipped from being assigned an argument value until there are arguments without default values that can be mapped to actual arguments passed. Let's now make our problem more complex, and throw in another optional parameter:
def print_values a = 50, b = 100, c
puts "value of a is #{a}"
puts "value of b is #{b}"
puts "value of c is #{c}"
end
print_values 1, 2
The output will be printed as follows:

Figure 2.61: Output with three parameters
The preceding example is very interesting since our Ruby program skipped the assigning of the middle parameter. We can first deduce from the preceding example the value of the non-optional element that is committed (from right to left), and then the optional parameters from left to right.
However, we cannot mix and match the way in which we use optional and required parameters.
Let's now add one more parameter to the code and see what output is generated:
def print_values a = 50, b = 100, c, d=100
puts "value of a is #{a}"
puts "value of b is #{b}"
puts "value of c is #{c}"
end
print_values 1, 2
The output will be as follows:

Figure 2.62: Syntax error
Along similar lines, if we try to pass an incorrect number of arguments, we'll get the ArgumentError exception. Consider the following example:
def no_parameters
puts "I do not take any parameters"
end
no_parameters "Hello"
The output will be as follows:

Figure 2.63: The ArgumentError exception with no parameters
Look at another example with two parameters:
def add_two_numbers a, b
puts "The sum is #{a + b}"
end
add_two_numbers 10
The output will be as follows:

Figure 2.64: The ArgumentError exception with two parameters
Ruby also allows us to send an unknown number of parameters to the method. This is done with the help of a splat argument represented by an asterisk, *. Splatting means that whatever we pass as an argument is going to be treated as an array. Each value or argument passed will be stored in an array called my_params. Let's look at how this works in the following example:
def any_parameters(*my_params)
puts my_params.inspect
end
any_parameters "any", "number", "of", "parameters"
The output will be as follows:

Figure 2.65: Using a splat operator
The .inspect function is a little helper that converts the input object to a string temporarily, enabling us to print all the arguments that were passed.
If we look at the output, we can see that it is an array. So, what Ruby does internally is convert all the parameters that we are passing to a method and converting the parameters into an array. Once this is done, the method can access this array and take decisions based on accordingly.
This splat operator is very handy in many situations. Let's take a look at the following examples:
first_element, *rest_of_them = ["a", "b", "c", "d"]
puts first_element
puts rest_of_them
The output will be as follows:

Figure 2.66: Output using a splat operator
There are two things to note here. The first is that the first_element variable gets the first element and it is not an array. The second is that the rest_of_them variable is an array because of the splat operator. We can also use this splat operator to force variables into an array. Consider the following example:
a = "Hi".to_a
The output will show up an error as follows:

Figure 2.67: Undefined method error
Now, we use a splat operator as shown in the following code:
a = *"Hello"
The output will be as follows:

Figure 2.68: Using a splat operator
This is how the splat operator will force elements into an array.
Return Value(s) from Methods
Methods return the output of the last statement that is executed. Look at the following example:
def welcome_to_city
city = "New York"
"Welcome to " + city
end
puts welcome_to_city
The output will be as follows:

Figure 2.69: Output for the return statement
If we want to compare this with other languages where the return statement is required, the program would look like this:
def welcome_to_city
city = "New York"
result = "Welcome to " + city
return result
end
puts welcome_to_city
The output will be as follows:

Figure 2.70: Output for the return statement
The preceding code is valid Ruby code as well. As with everything in Ruby, it is developer-friendly and minimizes the typing effort required.
An explicit return statement before the end of the function definition can also be used to return from the function. This is useful to terminate from a loop or return from a function if a condition is satisfied. Consider the following example:
def welcome_to_city city
return city if city == "Garbage"
"Welcome to " + city
end
puts welcome_to_city "Garbage"
puts welcome_to_city "New York"
The output will now be as follows:

Figure 2.71: Using the explicit return statement
The next obvious question that comes to mind is how we can return more than one value from a method. The answer to that question is that we can trick the system by returning an object, which can contain multiple values.
The most common and obvious way to do this is to return an array containing all the values that we need. The following example shows how to return an array with all the requisite values:
def return_multiple_values a, b
result_array = []
result_array << a + b
result_array << b - a
end
puts return_multiple_values 10, 20
The output will be as follows:

Figure 2.72: Returning multiple required values in an array
Let's take a look at this example and try to understand what is happening. When the last statement of the method is executed, it is returning result_array, which happens to contain two values. We can have multiple values in that array and then return that object. Instead of an array, we can use hash or even a custom object that we want to return and have values associated with it.
Naming Conventions for Methods
Methods that act as queries and return a Boolean value should be named with a trailing ?. This convention is followed within Ruby as well. For example, look at the include? method. When we are using this method, we do not have to worry about the return type as we know it's going to be of the Boolean type.
The following is an example of how we use this. Note that this is not a strict rule and the program will not fail if we do not use ? at the end of the method that returns a Boolean value. It is a convention that makes our code more readable and helps our fellow developers:
def check_presence?
["New York", "Abu Dhabi"].include? "New Delhi"
end
puts check_presence?
#false
Another convention is the bang(!) method. What this means is that if a method ends with (!), it will modify the object on which it is working. The following is an example to reflect this:
def find_unique! test_array
test_array.uniq!
end
test_array = [1, 2, 3, 1, 2, 3]
puts find_unique! test_array
#[1,2,3]
Since our method is changing the value of the test_array array, it should end with (!).
Activity 2.01: Dice Roller Program
In this activity, we will simulate a dice roller program that has three objectives:
- Roll a die
- Roll a die with any number of sides
- Roll any number of dice
This activity will test you in terms of performing operations on arrays and using the .each iterator. Here are some steps that will help you to complete this activity:
- First, create a roller.rb file.
- Use the rand method to return a random number. Ruby has a built-in rand method, which returns a random number. It takes an argument as an integer and returns a random number.
- Type rand 2 and you'll notice that the numbers you get in response are either 0 or 1, never 2. This is what 0-indexing means.
- Create a method roll to simulate the rolling of a die.
- Next, add an argument to the roll to enable the die to roll.
- Then, add another argument along with the dice to roll any number of dice.
Upon successful completion of the activity, you should obtain a random number printed on the screen for two scenarios: rolling a five-sided die and rolling two six-sided dice.
The output should be similar to the following:

Figure 2.73: Output for dice roller
Note
The solution to this activity can be found on page 460.