do..end vs curly braces for blocks in Ruby

2019-01-02 17:03发布

I have a coworker who is actively trying to convince me that I should not use do..end and instead use curly braces for defining multiline blocks in Ruby.

I'm firmly in the camp of only using curly braces for short one-liners and do..end for everything else. But I thought I would reach out to the greater community to get some resolution.

So which is it, and why? (Example of some shoulda code)

context do
  setup { do_some_setup() }
  should "do somthing" do
    # some more code...
  end
end

or

context {
  setup { do_some_setup() }
  should("do somthing") {
    # some more code...
  }
}

Personally, just looking at the above answers the question for me, but I wanted to open this up to the greater community.

13条回答
余生请多指教
2楼-- · 2019-01-02 17:17

Comes down to personal bias, I prefer curly braces over a do/end block as its more understandable to a higher number of developers due to a majority of background languages use them over the do/end convention. With that being said the real key is to come to an agreement within your shop, if do/end is used by 6/10 developers than EVERYONE should be using them, if 6/10 use curly braces, then stick to that paradigm.

Its all about making a pattern so that the team as a whole can identify the code structures quicker.

查看更多
皆成旧梦
3楼-- · 2019-01-02 17:22

I'm voting for do / end


The convention is do .. end for multiline and { ... } for one-liners.

But I like do .. end better, so when I have a one liner, I use do .. end anyway but format it as usual for do/end in three lines. This makes everyone happy.

  10.times do 
    puts ...
  end

One problem with { } is that it is poetry-mode-hostile (because they bind tightly to the last parameter and not the entire method call, so you must include method parens) and they just, to my mind, don't look as nice. They are not statement-groups and they clash with hash constants for readability.

Plus, I've seen enough of { } in C programs. Ruby's way, as usual, is better. There is exactly one type of if block, and you never have to go back and convert a statement into a compound-statement.

查看更多
与风俱净
4楼-- · 2019-01-02 17:22

A couple influential rubyists suggest to use braces when you use the return value, and do/end when you don't.

http://talklikeaduck.denhaven2.com/2007/10/02/ruby-blocks-do-or-brace (on archive.org)

http://onestepback.org/index.cgi/Tech/Ruby/BraceVsDoEnd.rdoc (on archive.org)

This seems like a good practice in general.

I'd modify this principle a bit to say that you should avoid using do/end on a single line because it's harder to read.

You do have to be more careful using braces because it'll bind to a method's final param instead of the whole method call. Just add parentheses to avoid that.

查看更多
有味是清欢
5楼-- · 2019-01-02 17:22

My personal style is to emphasize readability over rigid rules of {...} vs do...end choice, when such choice is possible. My idea of readability is as follows:

[ 1, 2, 3 ].map { |e| e + 1 }      # preferred
[ 1, 2, 3 ].map do |e| e + 1 end   # acceptable

[ 1, 2, 3 ].each_with_object [] do |e, o| o << e + 1 end # preferred, reads like a sentence
[ 1, 2, 3 ].each_with_object( [] ) { |e, o| o << e + 1 } # parens make it less readable

Foo = Module.new do     # preferred for a multiline block, other things being equal
  include Comparable
end

Foo = Module.new {      # less preferred
  include Comparable
}

Foo = Module.new { include Comparable }      # preferred for a oneliner
Foo = module.new do include Comparable end   # imo less readable for a oneliner

[ [ 1 ], [ 1, 2 ] ].map { |e| e.map do |e| e + 1 end }  # slightly better
[ [ 1 ], [ 1, 2 ] ].map { |e| e.map { |e| e + 1 } }     # slightly worse

In more complex syntax, such as multiline nested blocks, I try to intersperse {...} and do...end delimiters for most natural result, eg.

Foo = Module.new { 
  if true then
    Bar = Module.new {                          # I intersperse {} and keyword delimiters
      def quux
        "quux".tap do |string|                  # I choose not to intersperse here, because
          puts "(#{string.size} characters)"    # for multiline tap, do ... end in this
        end                                     # case still loks more readable to me.
      end
    }
  end
}

Although the lack of rigid rules might produce sligtly different choices for different programmers, I believe that case-by-case optimization for readability, although subjective, is a net gain over adherence to rigid rules.

查看更多
有味是清欢
6楼-- · 2019-01-02 17:23

From Programming Ruby:

Braces have a high precedence; do has a low precedence. If the method invocation has parameters that are not enclosed in parentheses, the brace form of a block will bind to the last parameter, not to the overall invocation. The do form will bind to the invocation.

So the code

f param {do_something()}

Binds the block to the param variable while the code

f param do do_something() end

Binds the block to the function f.

However this is a non-issue if you enclose function arguments in parenthesis.

查看更多
与风俱净
7楼-- · 2019-01-02 17:26

The most common rule I've seen (most recently in Eloquent Ruby) is:

  • If it's a multi-line block, use do/end
  • If it's a single line block, use {}
查看更多
登录 后发表回答