Custom Methods
Methods
A method is a simple way to group steps so you can reuse them easily.
- Sequence of steps that gives consistent results
- Accepts inputs and returns a result
- Runs only when invoked, can be reused many times
We can define a method using the def and end keywords. Everything between def and end is the method body. This body contains the steps Ruby will run each time the method is called.
def jurassic_park
puts "Welcome to Jurassic Park"
puts "Dinosaurs are alive again"
puts "Please stay on the tour path"
end
To call the method, write the method name:
jurassic_park
Output:
Welcome to Jurassic Park
Dinosaurs are alive again
Please stay on the tour path
Methods can also be called multiple times without rewriting logic.
jurassic_park
jurassic_park
jurassic_park
Output:
Welcome to Jurassic Park
Dinosaurs are alive again
Please stay on the tour path
Welcome to Jurassic Park
Dinosaurs are alive again
Please stay on the tour path
Welcome to Jurassic Park
Dinosaurs are alive again
Please stay on the tour path
Parameters
Parameters allow a method to receive data and adjust its behavior. They exist only while the method is running, which means they cannot be used outside the method.
A parameter is created when the method starts and is removed when the method finishes.
-
Without parameters:
def say_hello
puts "Nice to see you!"
end
# Calling the method
say_helloOutput:
Nice to see you! -
With parameters:
def say_hello(message)
puts message
end
# Calling the method
say_hello("How is your day?")Output:
How is your day? -
Multiple Parameters
first_num = 4
second_num = 3
def multiply(first_num, second_num)
return first_num.to_f * second_num.to_f
end
puts "The product of #{first_num} and #{second_num} is #{multiply(first_num, second_num)}"Output:
The product of 4 and 3 is 12.0
Using Arguments Inside a Method
Arguments behave like variables inside a method and can be used directly in its logic. Once values are passed in, the method can work with them just like normal variables.
- Strings can call string methods
- Numbers can be used in calculations
- Behavior depends on the input type
Example:
def describe_character(name, age)
puts name.upcase
puts age + 5
end
describe_character("Luna", 25)
describe_character("Milo", 30)
Output:
LUNA
30
MILO
35
Dynamic Typing and Runtime Errors
Ruby does not require data type declarations and does not enforce types ahead of time.
This means that errors will occur at runtime if the inputs do not support the methods being used.
Example:
def describe_character(name, age)
puts name.upcase
puts age + 5
end
describe_character(10, 25)
Output:
NoMethodError: undefined method `upcase` for 10:Integer
Ruby assumes the inputs are valid, so incorrect values cause errors when the method runs.
Local Variables
A local variable is a variable declared inside a method and exists only while that method runs. It cannot be used outside the method because it is limited to the method's execution environment.
For example, we can define a method to store a movie star's name:
def film_movie
action_star = "Arnold Schwarzenegger"
puts action_star
end
Here, action_star is a local variable. If you try to access it outside the method, Ruby will raise an error because its scope is limited to the film_movie method.
puts action_star
Output:
undefined local variable or method 'action_star' for main (NameError)
You can also have a variable with the same name at the top level of your file:
action_star = "Jean-Claude Van Damme"
puts action_star
film_movie
puts action_star
Output:
Jean-Claude Van Damme
Arnold Schwarzenegger
Jean-Claude Van Damme
This shows that variables with the same name in different scopes are completely separate. The top-level action_star exists throughout the file, while the one inside the method is temporary and isolated.
Return Values
A return value is the final output that a method gives back after it runs. Unlike printing with puts, which just shows something on the screen, a return value can be stored and used elsewhere in your program.
For example, calling upcase on a string produces a new string:
word = "maximilian"
all_caps = word.upcase
puts all_caps
Output:
MAXIMILIAN
Here, upcase returns the string in all caps, which we store in all_caps.
Method Return Values
We can also return values from our own methods. For example:
def add_two_numbers(num_one, num_two)
puts "Solving the math problem"
return num_one + num_two
end
result = add_two_numbers(1, 5)
puts result
Output:
Solving the math problem
6
Note: Every method always produces a return value, even if it seems like it does nothing. If a method has no explicit return statement, Ruby automatically returns nil by default. This ensures that every method produces some kind of object.
Multiple return
Consider the following example:
def add_two_numbers(num_one, num_two)
return "Solving the math problem"
return num_one + num_two
return num_one * num_two
return num_one / num_two
end
result = add_two_numbers(1, 5)
puts result
As soon as a return runs, the method stops immediately and nothing after it is executed. If a method has multiple return statements, Ruby executes only the first one it encounters and exits the method.
Output:
Solving the math problem
Implicit Returns in Methods
If a method does not have an explicit return, Ruby returns the result of the last evaluated expression automatically.
def add_two_numbers(num_one, num_two)
num_one + num_two
end
result = add_two_numbers(3, 4)
puts result
Output:
7
Here, the sum 7 is returned implicitly because it is the last evaluation.
Using return is optional if you want the last line as the return value. It's mainly helpful when you need to stop the method early or return a value from a conditional branch.
def check_number(num)
return "Too small" if num < 5
"Just right"
end
puts check_number(3)
puts check_number(7)
Output:
Too small
Just right
This shows that return can be used to exit a method early. Otherwise, Ruby will automatically return the last evaluated expression.
Optional Parameters and Default Arguments
Optional parameters allow a method to be called without giving a value for every parameter.
Default arguments set a fallback value for optional parameters when no value is provided.
- Optional parameters can be skipped when calling a method
- Default arguments are used if no value is given
- Required parameters must be listed before optional ones
Condier the method title_assigner:
def title_assigner(name, suffix="The Great")
"#{name} #{suffix}"
end
puts title_assigner("Henry", "The Navigator")
puts title_assigner("Alexander")
Output:
Henry The Navigator
Alexander The Great
In this example, name is required, while suffix is optional. If we do not provide a value for suffix, Ruby uses the default "The Great".
Splat Arguments
Ruby allows methods to accept any number of arguments using a special syntax called the splat or sponge operator (*).
- The
*before a parameter collects all extra arguments into an array - You can have required parameters before or after the splat parameter
- This lets you pass zero, one, or many arguments
Example:
-
Simple adder method:
def adder(*numbers)
sum = 0
numbers.each { |num| sum += num }
sum
end
puts adder(1) # Output: 1
puts adder(1, 2, 3) # Output: 6
puts adder() # Output: 0Here,
*numberscaptures all arguments in an array. We iterate over it to calculate the sum. If no arguments are given, it’s an empty array. -
With required parameters before the splat:
def adder(a, b, *numbers)
sum = numbers.sum
sum
end
puts adder(1, 2) # Output: 0
puts adder(1, 2, 3, 4, 5) # Output: 12 (3+4+5)aandbare required. Any extra arguments go intonumbers. Ruby matches arguments to parameters in order. -
With required parameters after the splat (less common):
def adder(a, b, *numbers, c, d)
sum = numbers.sum
sum
end
puts adder(1, 2, 3, 4, 5, 6) # Output: 7 (3+4)Here, the first two arguments go to
aandb, the last two tocandd, and any in the middle go intonumbers.
Object Mutation in Methods
When you pass an object to a method, you pass the original object, not a copy. This means the method can change the object, which can create side effects if you're not careful.
- Methods receive a reference to the original object
- Modifying the object inside the method changes the original
- Overwriting the parameter does not change the original object
Examples:
-
Mutating an array:
def append_five(elements)
elements << 5
end
values = [1, 2, 3, 4]
append_five(values)
puts values.inspectOutput:
[1, 2, 3, 4, 5]The
append_fivemethod adds 5 to the original arrayvalues. The method does not create a copy, so the original array is changed. -
Mutating a string:
def uppercase(text)
text.upcase!
end
name = "John"
uppercase(name)
puts nameOutput:
JOHNThe
uppercasemethod changes the original stringnamebecause the parametertextrefers to the same object in memory. -
Overwriting a parameter:
def replace_elements(elements)
elements = []
end
values = [1, 2, 3, 4]
replace_elements(values)
puts values.inspectOutput:
[1, 2, 3, 4]Assigning a new array to
elementsdoes not affectvalues. The parameter is just a temporary name for the object.
Keyword Arguments
Keyword arguments let you explicitly assign values to specific parameters when calling a method, so the order of arguments doesn't matter.
In the example below, a: and b: are keyword parameters. You must provide values for both when calling the sum method:
def sum(a:, b:)
a + b
end
puts sum(a: 2, b: 3) # Output: 5
puts sum(b: 3, a: 2) # Output: 5
Keyword arguments can also have default values, so you don’t need to provide every keyword when calling a method.
def sum(a: 1, b: 2)
a + b
end
# Will use the defaults
puts sum
# "b" defaults to 2
puts sum(a: 5)
# "a" defaults to 1
puts sum(b: 3)
# Both values provided
puts sum(a: 2, b: 8)
puts sum(b: 8, a: 2)
Output:
3
7
4
10
10
In this case, we set a: 1 and b: 2 as default values, which is useful when no value or a value is missing for a keyword argument.
You can also provide a default just for only one keyword (like a:), but then the other keyword (b) must always be given when calling the method.
def sum(a: 1, b: )
a + b
end
# "a" defaults to 1
puts sum(b: 3)
# Both values provided
puts sum(a: 2, b: 8)
puts sum(b: 8, a: 2)
Output:
4
10
10