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
How to Call Shell Commands from Ruby
What's the Difference Between Equal, Eql, ===, and ==
Difference Between \A \Z and ^ $ in Ruby Regular Expressions
What Are the Ruby Gotchas a Newbie Should Be Warned About
Why Does Ruby Have Both Private and Protected Methods
How to Count Duplicate Elements in a Ruby Array
How to Search Within an Array of Hashes by Hash Values in Ruby
Accessing Elements of Nested Hashes in Ruby
Creating Microsoft Word (.Docx) Documents in Ruby
Difference Between Attr_Accessor and Attr_Accessible
How to Make --No-Ri --No-Rdoc the Default For Gem Install
Ruby Operator Precedence Table
How to Install Postgresql'S Pg Gem on Ubuntu
Error to Install Nokogiri on Osx 10.9 Maverick
Aws S3: the Bucket You Are Attempting to Access Must Be Addressed Using the Specified Endpoint