Skip to content

Commit 87a6ec4

Browse files
iGELbbatsov
authored andcommitted
Add always_braces to Style/BlockDelimiters
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
1 parent b1510dd commit 87a6ec4

File tree

5 files changed

+130
-1
lines changed

5 files changed

+130
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### New features
66

7+
* [#6973](https://github.com/rubocop-hq/rubocop/pull/6973): Add `always_braces` to `Style/BlockDelimiter`. ([@iGEL][])
78
* [#6841](https://github.com/rubocop-hq/rubocop/issues/6841): Node patterns can now match children in any order using `<>`. ([@marcandre][])
89
* [#6928](https://github.com/rubocop-hq/rubocop/pull/6928): Add `--init` option for generate `.rubocop.yml` file in the current directory. ([@koic][])
910
* Add new `Layout/HeredocArgumentClosingParenthesis` cop. ([@maxh][])

config/default.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2663,6 +2663,8 @@ Style/BlockDelimiters:
26632663
# return value is being chained with another method (in which case braces
26642664
# are enforced).
26652665
- braces_for_chaining
2666+
# The `always_braces` style always enforces braces.
2667+
- always_braces
26662668
ProceduralMethods:
26672669
# Methods that are known to be procedural in nature but look functional from
26682670
# their usage, e.g.

lib/rubocop/cop/style/block_delimiters.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,24 @@ module Style
9595
# word.flip.flop
9696
# }.join("-")
9797
#
98+
# @example EnforcedStyle: always_braces
99+
# # bad
100+
# words.each do |word|
101+
# word.flip.flop
102+
# end
103+
#
104+
# # good
105+
# words.each { |word|
106+
# word.flip.flop
107+
# }
108+
#
98109
class BlockDelimiters < Cop
99110
include ConfigurableEnforcedStyle
100111
include IgnoredMethods
101112

113+
ALWAYS_BRACES_MESSAGE = 'Prefer `{...}` over `do...end` for blocks.'
114+
.freeze
115+
102116
def on_send(node)
103117
return unless node.arguments?
104118
return if node.parenthesized? || node.operator_method?
@@ -166,6 +180,7 @@ def message(node)
166180
when :line_count_based then line_count_based_message(node)
167181
when :semantic then semantic_message(node)
168182
when :braces_for_chaining then braces_for_chaining_message(node)
183+
when :always_braces then ALWAYS_BRACES_MESSAGE
169184
end
170185
end
171186

@@ -229,6 +244,7 @@ def proper_block_style?(node)
229244
when :line_count_based then line_count_based_block_style?(node)
230245
when :semantic then semantic_block_style?(node)
231246
when :braces_for_chaining then braces_for_chaining_style?(node)
247+
when :always_braces then braces_style?(node)
232248
end
233249
end
234250

@@ -257,6 +273,10 @@ def braces_for_chaining_style?(node)
257273
end
258274
end
259275

276+
def braces_style?(node)
277+
node.loc.begin.source == '{'
278+
end
279+
260280
def return_value_chaining?(node)
261281
node.parent && node.parent.send_type? && node.parent.dot?
262282
end

manual/cops_style.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,12 +460,25 @@ words.each { |word|
460460
word.flip.flop
461461
}.join("-")
462462
```
463+
#### EnforcedStyle: always_braces
464+
465+
```ruby
466+
# bad
467+
words.each do |word|
468+
word.flip.flop
469+
end
470+
471+
# good
472+
words.each { |word|
473+
word.flip.flop
474+
}
475+
```
463476

464477
### Configurable attributes
465478

466479
Name | Default value | Configurable values
467480
--- | --- | ---
468-
EnforcedStyle | `line_count_based` | `line_count_based`, `semantic`, `braces_for_chaining`
481+
EnforcedStyle | `line_count_based` | `line_count_based`, `semantic`, `braces_for_chaining`, `always_braces`
469482
ProceduralMethods | `benchmark`, `bm`, `bmbm`, `create`, `each_with_object`, `measure`, `new`, `realtime`, `tap`, `with_object` | Array
470483
FunctionalMethods | `let`, `let!`, `subject`, `watch` | Array
471484
IgnoredMethods | `lambda`, `proc`, `it` | Array

spec/rubocop/cop/style/block_delimiters_spec.rb

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,4 +507,97 @@
507507
end
508508
end
509509
end
510+
511+
context 'always braces' do
512+
cop_config = {
513+
'EnforcedStyle' => 'always_braces',
514+
'IgnoredMethods' => %w[proc]
515+
}
516+
517+
let(:cop_config) { cop_config }
518+
519+
it 'registers an offense for a single line block with do-end' do
520+
expect_offense(<<-RUBY.strip_indent)
521+
each do |x| end
522+
^^ Prefer `{...}` over `do...end` for blocks.
523+
RUBY
524+
end
525+
526+
it 'accepts a single line block with braces' do
527+
expect_no_offenses('each { |x| }')
528+
end
529+
530+
it 'registers an offence for a multi-line block with do-end' do
531+
expect_offense(<<-RUBY.strip_indent)
532+
each do |x|
533+
^^ Prefer `{...}` over `do...end` for blocks.
534+
end
535+
RUBY
536+
end
537+
538+
it 'auto-corrects do and end for single line blocks to { and }' do
539+
new_source = autocorrect_source('block do |x| end')
540+
expect(new_source).to eq('block { |x| }')
541+
end
542+
543+
it 'does not auto-correct do-end if {} would change the meaning' do
544+
src = "s.subspec 'Subspec' do |sp| end"
545+
new_source = autocorrect_source(src)
546+
expect(new_source).to eq(src)
547+
end
548+
549+
it 'accepts a multi-line block that needs braces to be valid ruby' do
550+
expect_no_offenses(<<-RUBY.strip_indent)
551+
puts [1, 2, 3].map { |n|
552+
n * n
553+
}, 1
554+
RUBY
555+
end
556+
557+
it 'registers an offense for multi-line chained do-end blocks' do
558+
expect_offense(<<-RUBY.strip_indent)
559+
each do |x|
560+
^^ Prefer `{...}` over `do...end` for blocks.
561+
end.map(&:to_s)
562+
RUBY
563+
end
564+
565+
it 'auto-corrects do-end for chained blocks' do
566+
src = <<-RUBY.strip_indent
567+
each do |x|
568+
end.map(&:to_s)
569+
RUBY
570+
new_source = autocorrect_source(src)
571+
expect(new_source).to eq(<<-RUBY.strip_indent)
572+
each { |x|
573+
}.map(&:to_s)
574+
RUBY
575+
end
576+
577+
it 'accepts a multi-line functional block with do-end if it is ' \
578+
'an ignored method' do
579+
expect_no_offenses(<<-RUBY.strip_indent)
580+
foo = proc do
581+
puts 42
582+
end
583+
RUBY
584+
end
585+
586+
context 'when there are braces around a multi-line block' do
587+
it 'registers an offense in the simple case' do
588+
expect_offense(<<-RUBY.strip_indent)
589+
each do |x|
590+
^^ Prefer `{...}` over `do...end` for blocks.
591+
end
592+
RUBY
593+
end
594+
595+
it 'allows when the block is being chained' do
596+
expect_no_offenses(<<-RUBY.strip_indent)
597+
each { |x|
598+
}.map(&:to_sym)
599+
RUBY
600+
end
601+
end
602+
end
510603
end

0 commit comments

Comments
 (0)