Dmitry Dmitriev
Dmitry Dmitriev

Reputation: 1059

Ruby different behavior depend of block type

Good day. I have different behavior of the same chunk of code depends on type of block syntax curly braces or do/end. The block with do/end just skipped without any error notification:

Block with curly brackets just implements and p prints one Ruby is a COOL language!:

p "rubyisacoollanguage".gsub(/(ruby)(is)(a)(cool)(language)/) {
   "one " + $1.capitalize + " %s %s %s %s!" % [$2,$3,$4.upcase,$5]
}

The 'same' snippet of code in with do/end just skipped, and p show me Enumerator <Enumerator: "rubyisacoollanguage":gsub(/(ruby)(is)(a)(cool)(language)/)>:

p "rubyisacoollanguage".gsub(/(ruby)(is)(a)(cool)(language)/) do
    "two " + $1.capitalize + " %s %s %s %s!" % [$2,$3,$4.upcase,$5]
end

I think it happens because of p in second case it's eliminates block. Things become clear when I add p inside blocks. Data from first block print 2 times, when data from second did not prited at all.

p "rubyisacoollanguage".gsub(/(ruby)(is)(a)(cool)(language)/) {
   p "one " + $1.capitalize + " %s %s %s %s!" % [$2,$3,$4.upcase,$5]
}
p "rubyisacoollanguage".gsub(/(ruby)(is)(a)(cool)(language)/) do
    p "two " + $1.capitalize + " %s %s %s %s!" % [$2,$3,$4.upcase,$5]
end

It was quite strange and unpredictable behavior for me. No error, just skip part of the code. Why it's happens?

Upvotes: 1

Views: 52

Answers (2)

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230306

Why does it happen?

Because {} and do/end have different precedence. {} is "stronger". As in "is associated to the nearest method call". So this

p foo {
  something
}

Is seen like this.

p (foo {
  something
})

And do/end is like this

p(foo) do
  something
end

No error, just skip part of the code

Yes, due to another feature of ruby. Which is "you can pass a block to ANY method. It is then that method's responsibility to use or ignore it." Here p does not expect a block and simply ignores it.

Upvotes: 5

tadman
tadman

Reputation: 211560

The problem with Ruby's loose syntax rules is that sometimes it gets tripped up on who the block belongs to. Due to ambiguity both p and gsub could "own" that block. The do method gets assigned to p, while the { ... } approach gets pinned on gsub.

This is probably due to some operator precedence type rule though as the block is not an operator per-se, I'm not sure I can find a reference for that particular behaviour right now.

Where there's ambiguity it's best to avoid depending on how it gets interpreted and instead be more specific:

rv = "rubyisacoollanguage".gsub(/(ruby)(is)(a)(cool)(language)/) do
  "two" + $1.capitalize + " %s %s %s %s!" % [$2,$3,$4.upcase,$5]
end

p rv

Here rv = can't take a block so it doesn't end up grabbing it inadvertently.

You can also do this:

p(
  "rubyisacoollanguage".gsub(/(ruby)(is)(a)(cool)(language)/) do
    "two" + $1.capitalize + " %s %s %s %s!" % [$2,$3,$4.upcase,$5]
  end
)

Where you're making it abundantly clear who gets what.

Upvotes: 2

Related Questions