Ruby block

I’ve been trying to figure out how it works. This morning I wanted to write it down my thinking process today, before I forget.

block_given?

block_given? is a method of Kernel module. In other words, it’s one of the same kind of puts, gets, sleep, etc. They are at the top-level, so to speak.

The Kernel module is included by class Object, so its methods are available in every Ruby object.

And block_given? verifies if block is given when you call a method.

What do you mean by “block is given”? In normal cases (when block is not given) the codes should be like this:

object_name.method_name(parameters)
object_name.method_name

Here you call methods with parameters, or without parameters. But when you say “block is given” (However this is also normal, nothing special) that means you called it with a block. A lot like a parameter.

object_name.method_name(parameter_names) { chunks of code }
object_name.method_name { chunks of code }

Let’s try to do some meaningful examples, define methods without blocks, and then with blocks.

def hello(param_name)
  puts "Hello, #{param_name}"
end

hello("Jimmy")
# "Hello, Jimmy"

Everything looks still fine, but only difference will be, you put a & sign there and call the Proc#call method from the block.

def hello(param_name, &block_name)
  block_name.call(param_name)
end

hello("Jimmy") { |name| puts "Hello, #{name} on the block" }
# "Hello, Jimmy on the block"

Let’s see if we can use the keyword block_given? and make a block optional. Now hello method should be useful with a block or without a block. By the way, it’s a convention to use “block” in place of block_name, as a block name.

def hello(param_name, &block)
  if block_given?
    block.call(param_name)
  else
    puts "Hello, #{param_name}"
  end
end

hello("Jimmy")
# "Hello, Jimmy"
hello("Jimmy") { |name| puts "Hello, #{name} on the block" }
# "Hello, Jimmy on the block"

Usually we do it with some value in the object (some state of the object) as follows.

class Friend
  def initialize(name)
    @name = name
  end

  def hello(&block)
    if block_given?
      block.call(@name)
    else
      "Hello, #{@name}"
    end
  end
end

friend = Friend.new("Jimmy")
puts friend.hello
# "Hello, Jimmy"
puts friend.hello { |name| "Hello, #{name} on the block" }
# "Hello, Jimmy on the block"

For the last thing, but not least, there’s yield. (Probably I guess this is the most important thing in this post)

Yield #

You don’t need to name a block. There’s yield instead of &block or block.call. In addition, yield runs faster than block.call, they say.

Many people prefer block.call because it’s clear.

If one method makes your code clearer at the expense of a millionth of a second, use it anyway.

Check out yourself if it’s still equivalent when they changed the code with the yield keyword. Since we didn’t make it optional or use block_given? here, it’s also a good chance to experience their errors.

def bye(&block)
  block.call
end

bye { "Bye-bye" }
# "Bye-bye"
bye
# NoMethodError
def bye
  yield
end

bye { "Bye-bye-bye" }
# "Bye-bye-bye"
bye
# LocalJumpError

I’m not sure which error is better. They are anyways expecting a block, so you might need block_given?.

 
1
Kudos
 
1
Kudos

Now read this

Git Merge your next Change

Sometimes when you want to merge changes made in another branch (in some remote) to your master branch. (or any branch supposed to be working) When you trust and it works fine for sure, then it would be quite simple: git merge And also... Continue →