Ruby Multiline Block Without Do End

Ruby multiline block without do end

There is a subtle difference between the two syntaxes. { } are higher precedence than do ... end. Thus, the following will pass bar and a block to method foo:

foo bar do ... end

while the following will pass a block to bar, and the result of that to foo:

foo bar { ... }

So your examples will act the same. However, if you left the parentheses off:

> 3.upto 9 { 
puts "Hi"
}
SyntaxError: compile error
(irb):82: syntax error, unexpected '{', expecting $end
3.upto 9 {
^
from (irb):82
from :0

> 3.upto 9 do
puts "Hi"
end
Hi
Hi
Hi
Hi
Hi
Hi
Hi
=> 3

So, { } are more likely to catch you up if you leave off parentheses in Ruby, which is fairly common; for this reason, and because Ruby conditionals and other control constructs all use end as a delimiter, people usually use do ... end for multi-line code blocks that come at the end of a statement.

However, { } is frequently use in places where do ... end would be cumbersome, for instance, if you are chaining several methods together which take blocks. This can allow you to write short, one line little blocks which can be used as part of a method chain.

> [1,2,3].sort{|x,y| y<=>x}.map{|x| x+1}
=> [4, 3, 2]

Here's an example to illustrate this difference:

def foo arg
if block_given?
puts "Block given to foo"
yield arg
else
puts "No block given to foo"
arg
end
end


def bar
if block_given?
puts "Block given to bar"
yield "Yielded from bar"
else
puts "No block given to bar"
end
"Returned from bar"
end

irb(main):077:0> foo bar { |arg| puts arg }
Block given to bar
Yielded from bar
No block given to foo
=> "Returned from bar"
irb(main):078:0> foo bar do |arg| puts arg end
No block given to bar
Block given to foo
Returned from bar
=> nil

How to use if (without else) statement for multiple lines of code?

This is very basic ruby syntax. All the ruby control structures can be used in inline way, or in multi-line/block way, closed with end keyword.

def self.foo(a)
if a == true
# mutiple lines of code
end
end

For more informations about syntax and ruby best practices, you can refer to : this ruby style guide

Multiline strings with no indent

In the RubyTapas Episode 249, Avdi Grimm describes a technique to strip leading whitespace from a multi-line string:

def unindent(s)
s.gsub(/^#{s.scan(/^[ \t]+(?=\S)/).min}/, '')
end

It is behavior compatible to other existing solutions to this problem, e.g. String#strip_heredoc in ActiveSupport / Rails or the standalone unindent gem.

You can use this method with a heredoc which is special syntax in ruby (and many other languages) to write multi-line strings.

module Something
def unindent(s)
s.gsub(/^#{s.scan(/^[ \t]+(?=\S)/).min}/, '')
end

def welcome
unindent(<<-TEXT)
Hello

This is an example. This multiline string works
- even with deeper nestings...
All is OK here :)
TEXT
end
end

Ruby style practice for multi-line arguments followed by a block

For such a complicated method call, I would build the arguments separately and splat them:

collection_args = [
:available_surveys,
{
exec_context: :decorator,
class: Survey,
skip_render: lambda {|object, opts| opts[:show_all_surveys] != true }
}
]
collection *collection_args do
property :name, as: :survey_name
property :id
end

Block not called in Ruby

Add parentheses to puts

puts(m do
x = 2
y = 3
x * y
end)

The output is 6.

Your code is equivalent to

puts(m) do 
x = 2
y = 3
x * y
end

Rubocop rule: Never use 'do' with multi-line 'while

while is a keyword,so you don't need to pass a block. Without do..end it will work fine. The below is fine

  def colour_random!
while true
col, row = rand(columns), rand(rows)
cell = self[row,col]
if cell.empty? then
cell.should_be_filled? ? cell.colour!(1) : cell.colour!(0)
break
end
end
end

while is a keyword, and if you pass a block to it, like do..end, it still works as you asked it to do, by not throwing any error, rather just a warning. But it could be dangerous if you try to pass a Proc or Method object to it, and dynamically try to convert it to a block using & keyword, as we do generally. That means

# below code will work as expected just throwing an warning.
x = 2
while x < 2 do
#code
end

But if you try to do by mistake like below

while &block # booom!! error

The reason is while is a keyword, which don't support any to_proc method to satisfy your need. So it can be dangerous.

Ruby style guide also suggested that Never use while/until condition do for multi-line while/until

I think the reason is as Nobuyoshi Nakada said in the mailing list

loop is a kernel method which takes a block. A block introduces new local variable scope.

  loop do
a = 1
break
end
p a #=> causes NameError

while doesn't.

  while 1
a = 1
break
end
p a #=> 1

Proper Ruby style for multi-line method chaining containing blocks

I would use do ... end blocks for multi-line and curly braces { ... } for one-line code. I never use do ... end for chaining methods, except when preceding for a multi-line block. I also chain methods in the same line as longer as the code is readable and the line is no longer than 80 chars.

So your example I will code it as below:

def example1 (arr)
arr.sort do |a, b|
a_pieces = a.value.split ' '
b_pieces = b.value.split ' '
(a_pieces[1] + a_pieces[5]) <=> (b_pieces[1] + b_pieces[5])
end.last[:some_value].select { |a| a == 'something' }
end

Although I probably will split it in two methods (actually three with the example1 one):

def sort_array(arr)
arr.sort do |a, b|
a_pieces = a.value.split ' '
b_pieces = b.value.split ' '
(a_pieces[1] + a_pieces[5]) <=> (b_pieces[1] + b_pieces[5])
end
end

def select_item(arr)
arr.last[:some_value].select { |a| a == 'something' }
end

def example1(arr)
select_item(sort_array(arr))
end

How efficiently end nested blocks in Ruby?

I'm not aware of any language where this is a feature. It would, in my opinion, make for a terrible language design.

It sounds like you're coming from a python background, which is (unlike ruby) whitespace-sensitive. Arguing for that as a desirable language feature has some merit, but is not something that will ever be built into ruby at this point.


Interestingly, you're not the first person to come up with this idea. Here's an amusing (joke/rejected) ruby feature request from a few years ago:

module MyModule
class MyClass
def my_method
10.times do
if rand < 0.5
p :small
ennnnnd

Or:

 module MyModule
class MyClass
def my_method
10.times do
if rand < 0.5
p :small
Ruby multiline block without do end How to use if (without else) statement for multiple lines of code? Multiline strings with no indent Ruby style practice for multi-liend
^^^ <- same place of original "end"!

Or:

module MyModule
class MyClass
def my_method
10.times do
if rand < 0.5
p :small
endmodule

Or:

module MyModule
class MyClass
def my_method
10.times do
if rand < 0.5
p :small
end!

Or perhaps instead this should be "fold up" syntax:

module MyModule
class MyClass
def my_method
10.times do
if rand < 0.5
p :small
fuuuuu


Related Topics



Leave a reply



Submit