Skip to content

Commit

Permalink
Add always_braces to Style/BlockDelimiters
Browse files Browse the repository at this point in the history
This style always prefers braces over do...end. Similar to double
quotes, some developers don't want to bother with changing the style
when adding or removing a line or debate, whether a blocks like

```ruby
date = Timecop.freeze(1.year.ago) { format_date(Time.now) }
customer = Timecop.freeze(1.year.ago) { create(:customer) }
```

are functional or procedual.

I didn't add a always_do_end style, as I don't think that someone wants
to enforce this in all situation (chaining, ...).

Sponsored by @BillFront
  • Loading branch information
iGEL committed Apr 28, 2019
1 parent b1510dd commit 6413cbc
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@

### New features

* [#6973](https://github.com/rubocop-hq/rubocop/pull/6973): Add `always_braces` to `Style/BlockDelimiter`. ([@iGEL][])
* [#6841](https://github.com/rubocop-hq/rubocop/issues/6841): Node patterns can now match children in any order using `<>`. ([@marcandre][])
* [#6928](https://github.com/rubocop-hq/rubocop/pull/6928): Add `--init` option for generate `.rubocop.yml` file in the current directory. ([@koic][])
* Add new `Layout/HeredocArgumentClosingParenthesis` cop. ([@maxh][])
Expand Down
2 changes: 2 additions & 0 deletions config/default.yml
Expand Up @@ -2663,6 +2663,8 @@ Style/BlockDelimiters:
# return value is being chained with another method (in which case braces
# are enforced).
- braces_for_chaining
# The `always_braces` style always enforces braces.
- always_braces
ProceduralMethods:
# Methods that are known to be procedural in nature but look functional from
# their usage, e.g.
Expand Down
20 changes: 20 additions & 0 deletions lib/rubocop/cop/style/block_delimiters.rb
Expand Up @@ -95,10 +95,24 @@ module Style
# word.flip.flop
# }.join("-")
#
# @example EnforcedStyle: always_braces
# # bad
# words.each do |word|
# word.flip.flop
# end
#
# # good
# words.each { |word|
# word.flip.flop
# }
#
class BlockDelimiters < Cop
include ConfigurableEnforcedStyle
include IgnoredMethods

ALWAYS_BRACES_MESSAGE = 'Prefer `{...}` over `do...end` for blocks.'
.freeze

def on_send(node)
return unless node.arguments?
return if node.parenthesized? || node.operator_method?
Expand Down Expand Up @@ -166,6 +180,7 @@ def message(node)
when :line_count_based then line_count_based_message(node)
when :semantic then semantic_message(node)
when :braces_for_chaining then braces_for_chaining_message(node)
when :always_braces then ALWAYS_BRACES_MESSAGE
end
end

Expand Down Expand Up @@ -229,6 +244,7 @@ def proper_block_style?(node)
when :line_count_based then line_count_based_block_style?(node)
when :semantic then semantic_block_style?(node)
when :braces_for_chaining then braces_for_chaining_style?(node)
when :always_braces then braces_style?(node)
end
end

Expand Down Expand Up @@ -257,6 +273,10 @@ def braces_for_chaining_style?(node)
end
end

def braces_style?(node)
node.loc.begin.source == '{'
end

def return_value_chaining?(node)
node.parent && node.parent.send_type? && node.parent.dot?
end
Expand Down
15 changes: 14 additions & 1 deletion manual/cops_style.md
Expand Up @@ -460,12 +460,25 @@ words.each { |word|
word.flip.flop
}.join("-")
```
#### EnforcedStyle: always_braces

```ruby
# bad
words.each do |word|
word.flip.flop
end

# good
words.each { |word|
word.flip.flop
}
```

### Configurable attributes

Name | Default value | Configurable values
--- | --- | ---
EnforcedStyle | `line_count_based` | `line_count_based`, `semantic`, `braces_for_chaining`
EnforcedStyle | `line_count_based` | `line_count_based`, `semantic`, `braces_for_chaining`, `always_braces`
ProceduralMethods | `benchmark`, `bm`, `bmbm`, `create`, `each_with_object`, `measure`, `new`, `realtime`, `tap`, `with_object` | Array
FunctionalMethods | `let`, `let!`, `subject`, `watch` | Array
IgnoredMethods | `lambda`, `proc`, `it` | Array
Expand Down
93 changes: 93 additions & 0 deletions spec/rubocop/cop/style/block_delimiters_spec.rb
Expand Up @@ -507,4 +507,97 @@
end
end
end

context 'always braces' do
cop_config = {
'EnforcedStyle' => 'always_braces',
'IgnoredMethods' => %w[proc]
}

let(:cop_config) { cop_config }

it 'registers an offense for a single line block with do-end' do
expect_offense(<<-RUBY.strip_indent)
each do |x| end
^^ Prefer `{...}` over `do...end` for blocks.
RUBY
end

it 'accepts a single line block with braces' do
expect_no_offenses('each { |x| }')
end

it 'registers an offence for a multi-line block with do-end' do
expect_offense(<<-RUBY.strip_indent)
each do |x|
^^ Prefer `{...}` over `do...end` for blocks.
end
RUBY
end

it 'auto-corrects do and end for single line blocks to { and }' do
new_source = autocorrect_source('block do |x| end')
expect(new_source).to eq('block { |x| }')
end

it 'does not auto-correct do-end if {} would change the meaning' do
src = "s.subspec 'Subspec' do |sp| end"
new_source = autocorrect_source(src)
expect(new_source).to eq(src)
end

it 'accepts a multi-line block that needs braces to be valid ruby' do
expect_no_offenses(<<-RUBY.strip_indent)
puts [1, 2, 3].map { |n|
n * n
}, 1
RUBY
end

it 'registers an offense for multi-line chained do-end blocks' do
expect_offense(<<-RUBY.strip_indent)
each do |x|
^^ Prefer `{...}` over `do...end` for blocks.
end.map(&:to_s)
RUBY
end

it 'auto-corrects do-end for chained blocks' do
src = <<-RUBY.strip_indent
each do |x|
end.map(&:to_s)
RUBY
new_source = autocorrect_source(src)
expect(new_source).to eq(<<-RUBY.strip_indent)
each { |x|
}.map(&:to_s)
RUBY
end

it 'accepts a multi-line functional block with do-end if it is ' \
'an ignored method' do
expect_no_offenses(<<-RUBY.strip_indent)
foo = proc do
puts 42
end
RUBY
end

context 'when there are braces around a multi-line block' do
it 'registers an offense in the simple case' do
expect_offense(<<-RUBY.strip_indent)
each do |x|
^^ Prefer `{...}` over `do...end` for blocks.
end
RUBY
end

it 'allows when the block is being chained' do
expect_no_offenses(<<-RUBY.strip_indent)
each { |x|
}.map(&:to_sym)
RUBY
end
end
end
end

0 comments on commit 6413cbc

Please sign in to comment.