# Methods & Blocks in Ruby

## Methods

Methods in Ruby are set of expressions that 1) always return a value, 2) are always associated with some Ruby object.

Ruby methods are not straightforward objects but can be obtained as objects using an `Object.method`

method. Such a method object might be 1) called, 2) used as a block or 3) unbound from its associated object and bound to another.

```
class Mathematician
def sum(a, b)
a + b
end
end
engineer = Mathematician.new
engineer.sum(2, 2) # => 4
engineer.sum.class # => ArgumentError: wrong number of arguments (given 0, expected 2)
object_method = engineer.method(:sum)
object_method.class # => Method
object_method.call(3, 3) # => 6
```

## Method Arguments

Arguments can be provided to Ruby methods through: 1) standard arguments, 2) keyword arguments, and 3) variable length arguments.

```
# Standard arguments:
def sum(first_number, second_number)
first_number + second_number
end
sum(2, 2) #=> 4
sum(2) #=> ArgumentError: wrong number of arguments (given 1, expected 2)
# Keyword arguments:
def sum(first_number:, second_number:)
first_number + second_number
end
sum(first_number: 2, second_number: 2) # => 4
sum(second_number: 2, first_number: 2) # => 4
sum(first_number: 2) # => missing keyword: second_number
# Variable length arguments:
def sum_all(*args)
args.inject(0, :+)
end
sum_all(1, 2, 3) # => 6
sum_all(1, 2, 3, 4, 5) # => 15
```

With both - standard arguments and keyword arguments - it is possible to use default values making the arguments with default values optional.

```
def sum(first_number, second_number = 2)
first_number + second_number
end
sum(2, 3) #=> 5
sum(2) #=> 4
def sum(first_number:, second_number: 2)
first_number + second_number
end
sum(first_number: 2, second_number: 3) # => 5
sum(first_number: 2) # => 4
```

## Blocks

It might be said that an inspiration for blocks were higher-order functions.

Blocks in Ruby are set of expressions accepting arguments that 1) do not return a value by itself, 2) are not associated with any Ruby object. Blocks are not objects but can be turned to objects with `&`

notation signalled in a given method's arguments.

Blocks are signalled by the `do ... end`

notation or the `{ ... }`

notation and their arguments are enclosed in `|...|`

notation.

Blocks can be 1) called from within a method or 2) yielded to.

## Calling Blocks

Blocks can be turned into objects (namely procs) and called when its underlying method uses `&`

in its arguments.

```
def sum(&my_block)
my_block.call(2, 2)
end
sum { |a, b| a + b } # => 4
sum { |a, b| a - b } # => 0
sum { |a, b| a + a + b + b } # => 8
```

## Yielding to Blocks

The same results as with calling blocks can be achieved with yielding to blocks with a `yield`

keyword.

```
def sum
yield(3, 4)
end
sum { |a, b| a + b } # => 7
sum { |a, b| a - b } # => -1
sum { |a, b| a + a + b + b } # => 14
```