Ruby Block

Let's discuss blocks in Ruby!

A block in Ruby is a piece of code that you can store in a variable or pass to a method. Blocks can take parameters and they are always enclosed within braces {} or do..end.

1. Using a Block with a Method

One of the most common ways to use a block is with the .each method on an array:

[1, 2, 3, 4, 5].each { |num| puts num }

In this example, { |num| puts num } is the block. It's a piece of code that takes one parameter, num, and then prints that parameter. The .each method runs the block once for each element in the array, passing each element to the block in turn.

You could also write the same block using do..end:

[1, 2, 3, 4, 5].each do |num|
  puts num
end

2. Block Syntax and Parameters

A block can take multiple parameters, which are listed between pipe | characters at the start of the block. For example:

{ |param1, param2, ... | ... }

3. Yielding to a Block in Your Own Method

You can also write your own methods that accept a block. To do this, you use the yield keyword:

def my_method
  yield if block_given?
end

my_method { puts "Hello, block!" } # outputs "Hello, block!"

In this example, my_method is defined to yield to a block if one is given. When my_method { puts "Hello, block!" } is called, it yields to the block { puts "Hello, block!" }, causing "Hello, block!" to be printed.

4. Block Local Variables

Variables that are defined outside of a block are accessible inside the block. However, any variables that are defined inside a block are local to that block:

x = 10
5.times do |x|
  puts x  # x inside the block is a different variable
end
puts x  # outputs 10, because the original x is not affected by the block

In this example, x inside the block is a different variable from x outside the block.

5. Blocks, Procs, and Lambdas

Blocks are closely related to Procs and Lambdas in Ruby. You can convert a block to a Proc by passing it as an argument to a method with the & symbol, and you can call a Proc or a Lambda like a block with the yield keyword.

  1. Creating and using blocks in Ruby:

    • Description: Blocks are chunks of code enclosed within {} or do...end. They are used to encapsulate behavior and are often passed to methods.
    • Code example:
      3.times { puts "Hello, Ruby!" }
      
  2. Passing blocks to methods in Ruby:

    • Description: Methods can accept blocks as arguments, allowing dynamic behavior to be injected into the method.
    • Code example:
      def greet
        puts "Hello,"
        yield if block_given?
      end
      
      greet { puts "Ruby!" }
      
  3. Block parameters and yield in Ruby:

    • Description: Blocks can accept parameters, and the yield keyword is used to execute the block.
    • Code example:
      def greet(name)
        puts "Hello, #{name}"
        yield if block_given?
      end
      
      greet("Alice") { puts "Nice to meet you!" }
      
  4. Closures and scope in Ruby blocks:

    • Description: Blocks form closures, capturing the surrounding scope's variables. They can access and modify variables from the outer scope.
    • Code example:
      x = 10
      3.times { puts x; x += 1 }
      
  5. Procs and lambdas in Ruby blocks:

    • Description: Procs and lambdas are objects that encapsulate blocks. They can be stored in variables and reused.
    • Code example:
      my_proc = Proc.new { puts "I'm a Proc!" }
      my_lambda = -> { puts "I'm a Lambda!" }
      
      my_proc.call
      my_lambda.call
      
  6. Common use cases for Ruby blocks:

    • Description: Blocks are used for iteration, filtering, customizing behavior in methods, and more. They make Ruby code expressive and flexible.
    • Code example:
      numbers = [1, 2, 3, 4, 5]
      squared_numbers = numbers.map { |num| num**2 }