The Simplest Guide on Ruby Methods Arguments
Tags: ruby, rails, programming, beginners
Published: 2023-12-20 12:39:46

Creating functions is fundamental in our developer profession. With Ruby, we are fortunate to have a language that is very flexible in composing the definition of these methods.

In this article, we will explore all the different types of arguments a function can receive.

## Table of Contents

- [Positional Arguments](#positional-arguments)
- [Keyword Arguments](#keyword-arguments)
- [When to use Positional / Keyword?](#when-to-use-positional-keyword)
- [Named Rest Arguments](#named-rest-arguments)
- [Anonymous Rest Arguments](#anonymous-rest-arguments)
- [Nil Splat Operator Arguments](#nil-splat-operator-arguments)
- [Block Argument](#block-argument)


### Positional Arguments

Positional Arguments are the most commonly used and are defined in the order they appear. They are directly related to the order of method parameters. Let's look at an example:

```ruby
def add(a, b, c)
  puts a + b + c
end

add(1, 2, 3) # => 6
```

You can also assign them a default value with this syntax:

```ruby
def add(a, b = 2, c = 3)
  puts a + b + c
end

add(1) # => 6
```

⚠️ Note: It is impossible to wrap a Positional Argument around two Positional Arguments with a default value.

```ruby
def add(a = 1, b, c = 3) # => SyntaxError
  puts a + b + c
end
```

---

### Keyword Arguments

Keyword arguments allow you to explicitly specify which variable each value is intended for. This makes the method call more readable and flexible. Here's how it works:

```ruby
def greet(name:, age:)
  puts "Hello #{name}! You are #{age} years old."
end

greet(name: "Alice", age: 30) # => Hello Alice! You are 30 years old.
```

You can mix Positional and Keyword arguments:

```ruby
def greet(name, age:)
  puts "Hello #{name}! You are #{age} years old."
end

greet("Alice", age: 30) # => Hello Alice! You are 30 years old.
```

⚠️ Note: It is impossible to place a Positional argument after a Keyword Argument.

```ruby
def greet(name:, age) # => SyntaxError
  puts "Hello #{name}! You are #{age} years old."
end
```

The great advantage of using Keywords is that it does not allocate memory for the Hash sent to it. I'll explain this with an example:

```ruby
def greet(options = {}) # will allocate memory if no args
  puts "Hello #{options.fetch(:name)}! You are #{options.fetch(:age)} years old."
end

greet({name: "Alicia", age: 33}) # you just created a hash!

def greet(name:, age:) # won't allocate memory if no args
  puts "Hello #{name}! You are #{age} years old."
end

greet(name: "Alicia", age: 33) # no memory allocation
```

---

Positional and Keywords are the two most commonly used types of arguments. So far, we haven't seen the true power of Ruby. Before we go further, let's take a moment to recall some rules regarding the use of Positional and Keywords arguments.

### When to use Positional / Keyword?

For this, I have a simple rule:

1. If my method is public → Keyword Argument
Examples of methods called outside the context of my class, the `initialize` method, the `call` method for Service Objects.
2. If my method is private → Positional Argument
Methods internal to a class are there to unload actions performed in public methods. Since we have total control over callers and callees, we can afford to use only Positional Arguments.

---

### Named Rest Arguments

The Splat Operator (`*`) and the Double Splat Operator (`**`) help us manage a variable number of arguments to receive in our function.

Let's look at an example:

```ruby
def display_info(*args, **kwargs)
  puts "Positional Arguments: #{args}"
  puts "Keyword Arguments: #{kwargs}"
end

display_info(1, 2, {fake: :love}, name: "Bob", age: 25)
# => Positional Arguments: [1, 2, {:fake=>:love}]
# => Keyword Arguments: {:name=>"Bob", :age=>25}
```

- `*args` means: Take all Positional Arguments and put them into the `args` variable as a list.
- `**kwargs` means: Take all Keyword Arguments and put them into the `kwargs` variable as a Hash.

`args` and `kwargs` are named as such by convention; you are free to rename them as you see fit.

---

### Anonymous Rest Arguments

In the previous case, we saw what happens when we name our use of Splat Operators in our method definition.

In reality, there is a second use for splat operators when we don't assign them to anything:

```ruby
def process_data(*, processing_method: "default")
  puts "Processing with the method: #{processing_method}"
end

process_data("Hello", "World", processing_method: "custom")
# => Processing with the method: custom
```

In the example above, you saw that nothing was done with the sent Positional Arguments.

The behavior is the same with the Double Splat Operator:

```ruby
def process_data(card, method, **)
  puts "#{card} was debited with #{method} method"
end

process_data("Hello", "World", processing_method: "custom")
# => Hello was debited with World method
```

But then you must be wondering, what's the use?

1. Delegate to underlying functions by ignoring the content of the arguments.
2. Ignore "extra" arguments and avoid raising an exception.

Let's look at an example that explains these two use cases:

```ruby
def greet(**)
  greet_kindly(**)
  greet_wickedly(**)
end

def greet_kindly(name:, **)
  puts "Hi #{name}, nice to see you!"
end

def greet_wickedly(name:, surname:, **)
  puts "Hi #{name}, what a stupid surname #{surname}..."
end

greet(name: "Hugo", surname: "The V")
# => Hi Hugo, nice to see you!
# => Hi Hugo, what a stupid surname The V...
```

The `greet_kindly` method received `name` and `surname` but extracted `name` and sent `surname` into the void thanks to the `**` notation. Without `**`, an `ArgumentError` would be raised.

There is another method for delegating arguments; it's called Argument Forwarding, with the `...` notation:

```ruby
def greet(...)
  greet_kindly(...)
  greet_wickedly(..)
end
[...]
```

The only difference is that `*` and `**` only look at one type of argument (Positional / Keyword), while `...` considers all types of arguments.

---

### Nil Splat Operator Arguments

If we combine the Double Splat Operator and `nil`, what do you think happens?

```ruby
def greet(name, **nil)
  puts "Hello #{name}!"
end

greet("Charlie", other: "argument?") # => ???
```

I'll give you the answer: an exception `no keywords accepted (ArgumentError)` is raised.

How is this useful?

Well, you will be surprised to learn that Keyword Arguments have a very interesting property. If the method expects a Positional Argument, and a Keyword Argument is sent, the Keyword Argument is cast into a Hash and promoted to the rank of Position Argument.

```ruby
def greet(name)
  puts "Hello #{name}!"
end

greet(name: "Hugo") # => Hello {:name=>"Hugo"}
```

If you want to prevent this behavior in your methods, you need to add `**nil` at the end of your parameter declaration!

---

### Block Argument

This section could be an entire article. Managing Blocks is what makes Ruby so appreciated.

The promise of the Block Argument is to be able to delegate a part of the internal body of your method externally. This offers incredible flexibility. The best part? You can directly send information to the Block:

```ruby
def do_action
  yield(1, 2)
end

do_action do |a, b|
  pp "What does #{a} + #{b} ? #{a+b}"
end

```

In this example, I used my Block anonymously, but you can also name it as follows:

```ruby
def do_action(&block)
  yield block
end

do_action do
  "Hello World!"
end
# => Hello World!

```

These are very simple examples, but keep in mind that Block Arguments open up a huge range of possibilities in your applications.

---

## TL;DR

Here's a concise summary of each type of argument covered today!

- **Positional Arguments** `(a, b, c)`
- **Keyword Arguments** `(name:, age:)`
- **Named Rest Arguments `(*args, **kwargs)`**
- **Anonymous Rest Arguments** `(*, **)`
- **Argument Forwarding** `(...)`
- **nil kwargs** `(**nil)`
- **Block Argument** `(&block)`

---

That's it for today!

Feel free to experiment with these different types of arguments in your own Ruby projects and share your discoveries. By mastering these subtleties, you will be better equipped to design functions that are more robust and flexible, truly tailored to the needs of your applications.