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

PostgreSQL for Rails on Apple OS X

This could be a short tutorial to follow along, how to setup PostgreSQL for your development environment. Basically it’s going to be a sharing my experience about, when I tried to understand more of it from my Rails Meetup Study Group. I... Continue →