Methods, Blocks, and Procs
## Learning Objectives
- Define and call methods
- Understand method parameters and return values
- Work with blocks and yield
- Master procs and lambdas
- Understand closures and scope
## Methods
### Basic Syntax
```ruby
def greet
"Hello!"
end
puts greet # "Hello!"
```
### Method with Parameters
```ruby
def greet(name)
"Hello, #{name}!"
end
puts greet("Alice") # "Hello, Alice!"
```
### Default Parameters
```ruby
def greet(name = "World")
"Hello, #{name}!"
end
puts greet # "Hello, World!"
puts greet("Bob") # "Hello, Bob!"
```
### Multiple Parameters
```ruby
def add(a, b)
a + b
end
puts add(2, 3) # 5
```
### Splat Operator
```ruby
def sum(*numbers)
numbers.reduce(0, :+)
end
puts sum(1, 2, 3, 4, 5) # 15
```
### Keyword Arguments (Ruby 2.0+)
```ruby
def create_user(name:, email:, age: 18)
{ name: name, email: email, age: age }
end
create_user(name: "Alice", email: "alice@example.com")
create_user(name: "Bob", email: "bob@example.com", age: 25)
```
### Double Splat for Keyword Args
```ruby
def config(**options)
options
end
config(server: "localhost", port: 3000, debug: true)
# => { server: "localhost", port: 3000, debug: true }
```
## Return Values
```ruby
# Explicit return
def find_first_even(numbers)
numbers.each { |n| return n if n.even? }
nil
end
# Implicit return (last expression)
def add(a, b)
a + b
end
```
## Method Naming
```ruby
# Question methods (?) - return boolean
def empty?(str)
str.length == 0
end
# Bang methods (!) - potentially dangerous
def sort!(array)
array.sort!
end
def sort(array)
array.sort
end
# Setter methods
def name=(value)
@name = value
end
# Predicates
def between?(a, b)
self >= a && self <= b
end
```
## Variable Arguments
```ruby
def logger(level, *messages)
messages.each { |msg| puts "[#{level.upcase}] #{msg}" }
end
logger("INFO", "Server started", "User logged in", "Request processed")
```
## Blocks
### Block Syntax
```ruby
# Do-end block
[1, 2, 3].each do |n|
puts n
end
# Brace block
[1, 2, 3].each { |n| puts n }
```
### Yield
```ruby
def repeat(times)
times.times { yield }
end
repeat(3) { puts "Hello" }
# Output: Hello three times
```
### Yield with Arguments
```ruby
def calculate(a, b)
yield(a, b)
end
result = calculate(5, 3) { |x, y| x + y }
puts result # 8
result = calculate(5, 3) { |x, y| x * y }
puts result # 15
```
### Block_given?
```ruby
def maybe_repeat(times)
if block_given?
times.times { yield }
else
puts "No block provided"
end
end
maybe_repeat(3) { puts "Hello" }
maybe_repeat(3)
```
## Procs
### Creating Procs
```ruby
# Proc.new
greet = Proc.new { |name| "Hello, #{name}!" }
# proc method
shout = proc { |text| text.upcase }
# lambda keyword (stabby lambda)
welcome = lambda { |name| "Welcome, #{name}!" }
# Stabby lambda shorthand (Ruby 1.9+)
farewell = ->(name) { "Goodbye, #{name}!" }
```
### Calling Procs
```ruby
greet = proc { |name| "Hello, #{name}!" }
puts greet.call("Alice")
puts greet["Bob"] # Alternative syntax
puts greet.("Charlie") # Alternative syntax
```
### Proc vs Lambda
```ruby
# Proc - doesn't check argument count
p = proc { |a, b| "#{a}, #{b}" }
p.call(1, 2) # "1, 2"
p.call(1) # "1, " (nil for missing)
p.call(1, 2, 3) # "1, 2" (extra ignored)
# Lambda - strict argument count
l = lambda { |a, b| "#{a}, #{b}" }
l.call(1, 2) # "1, 2"
l.call(1) # ArgumentError!
l.call(1, 2, 3) # ArgumentError!
```
### Lambda_check
```ruby
p = proc { |x| x }
l = lambda { |x| x }
puts p.lambda? # false
puts l.lambda? # true
```
## Lambdas
### Stabby Lambda Syntax
```ruby
add = ->(a, b) { a + b }
puts add.call(2, 3) # 5
square = ->(n) { n ** 2 }
puts square.call(5) # 25
```
### Lambda as Method Argument
```ruby
numbers = [1, 2, 3, 4, 5]
# Instead of block
doubled = numbers.map(&:to_i) # Symbol to proc
doubled = numbers.map { |n| n * 2 }
# With lambda
numbers.each(&->(n) { puts "Number: #{n}" })
```
## Blocks to Procs
### Ampersand Operator
```ruby
def method_that_yields
yield if block_given?
end
proc = -> { puts "Hello" }
method_that_yields(&proc)
# Symbol to proc
names = ["alice", "bob"]
upcase_names = names.map(&:upcase)
```
## Closures
```ruby
# Blocks capture local variables
def multiplier(factor)
->(number) { number * factor }
end
double = multiplier(2)
triple = multiplier(3)
puts double.call(5) # 10
puts triple.call(5) # 15
```
## Method Arguments Summary
| Syntax | Description |
|--------|-------------|
| `def foo(a)` | Positional argument |
| `def foo(a=1)` | Default argument |
| `def foo(*a)` | Variable positional |
| `def foo(a:)` | Keyword argument |
| `def foo(a: 1)` | Keyword with default |
| `def foo(**a)` | Keyword splat |
| `def foo(&a)` | Block as proc |
## Summary
- Methods: `def...end`, implicit/explicit return
- Blocks: `do...end` or `{ }`, passed to methods
- `yield`: executes the block
- Procs: `Proc.new`, `proc`, stored as objects
- Lambdas: `lambda { }` or `->()`, strict argument checking
- `&` operator: converts block to proc or proc to block
- Closures: blocks capture surrounding scope
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →