Reputation: 1059
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
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
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