Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ RSpec/ExcessiveDocstringSpacing:
Enabled: true
RSpec/IdenticalEqualityAssertion:
Enabled: true
RSpec/MemoizedHelperBlockDelimiter:
Enabled: true
RSpec/NoExpectationExample:
Enabled: true
RSpec/SortMetadata:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* Update `config/default.yml` removing deprecated option to make the config correctable by users. ([@ignaciovillaverde][])
* Do not attempt to auto-correct example groups with `include_examples` in `RSpec/LetBeforeExamples`. ([@pirj][])
* Add new `RSpec/SortMetadata` cop. ([@leoarnold][])
* Add `RSpec/MemoizedHelperBlockDelimiter` cop. ([@r7kamura][])

## 2.13.2 (2022-09-23)

Expand Down
10 changes: 10 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,16 @@ RSpec/LetSetup:
VersionAdded: '1.7'
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/LetSetup

RSpec/MemoizedHelperBlockDelimiter:
Description: Use consistent style block delimiters for memoized helpers.
Enabled: pending
EnforcedStyle: do_end
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree with the choice of the default style.

do_end doesn't seem to be a frequent choice in the wild, according to a run against real-world-rspec:

57497 files inspected, 104170 offenses detected, 104170 offenses autocorrectable

as opposed to braces:

57497 files inspected, 17121 offenses detected

And, I suspect some part of do/end usages (offences to braces style) are due to the line length limitation:

    let!(:conference) do
      WimbaConference.create!(title: "my conference", user: @user, duration: 60, context: course_factory)
    end

or because of being multi-line e.g. /Users/pirj/source/real-world-rspec/spree/emails/spec/mailers/spree/order_mailer_spec.rb:25:22::

  let(:second_order) do
    order = create(:completed_order_with_totals, email: 'test2@example.com')
    product = create(:product, name: %{The "BESTEST" product})
    variant = create(:variant, product: product)
    price = create(:price, variant: variant, amount: 15.00)
    store = second_store
    line_item = create(:line_item, variant: variant, order: order, quantity: 1, price: 4.99)
    allow(product).to receive_messages(default_variant: variant)
    allow(variant).to receive_messages(default_price: price)
    allow(order).to receive_messages(line_items: [line_item])
    allow(order).to receive_messages(store: store)
    order
  end

SupportedStyles:
- braces
- do_end
VersionAdded: "<<next>>"
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MemoizedHelperBlockDelimiter

RSpec/MessageChain:
Description: Check that chains of messages are not being stubbed.
Enabled: true
Expand Down
1 change: 1 addition & 0 deletions docs/modules/ROOT/pages/cops.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
* xref:cops_rspec.adoc#rspecleakyconstantdeclaration[RSpec/LeakyConstantDeclaration]
* xref:cops_rspec.adoc#rspecletbeforeexamples[RSpec/LetBeforeExamples]
* xref:cops_rspec.adoc#rspecletsetup[RSpec/LetSetup]
* xref:cops_rspec.adoc#rspecmemoizedhelperblockdelimiter[RSpec/MemoizedHelperBlockDelimiter]
* xref:cops_rspec.adoc#rspecmessagechain[RSpec/MessageChain]
* xref:cops_rspec.adoc#rspecmessageexpectation[RSpec/MessageExpectation]
* xref:cops_rspec.adoc#rspecmessagespies[RSpec/MessageSpies]
Expand Down
72 changes: 72 additions & 0 deletions docs/modules/ROOT/pages/cops_rspec.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2800,6 +2800,78 @@ end

* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/LetSetup

== RSpec/MemoizedHelperBlockDelimiter

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Pending
| Yes
| Yes
| <<next>>
| -
|===

Use consistent style block delimiters for memoized helpers.

=== Examples

==== EnforcedStyle: do_end (default)

[source,ruby]
----
# bad
let(:foo) { 'bar' }

# good
let(:foo) do
'bar'
end

# bad
subject(:foo) { 'bar' }

# good
subject(:foo) do
'bar'
end
----

==== EnforcedStyle: braces

[source,ruby]
----
# bad
let(:foo) do
'bar'
end

# good
let(:foo) { 'bar' }

# bad
subject(:foo) do
'bar'
end

# good
subject(:foo) { 'bar' }
----

=== Configurable attributes

|===
| Name | Default value | Configurable values

| EnforcedStyle
| `do_end`
| `braces`, `do_end`
|===

=== References

* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MemoizedHelperBlockDelimiter

== RSpec/MessageChain

|===
Expand Down
135 changes: 135 additions & 0 deletions lib/rubocop/cop/rspec/memoized_helper_block_delimiter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# frozen_string_literal: true

module RuboCop
module Cop
module RSpec
# Use consistent style block delimiters for memoized helpers.
#
# @example EnforcedStyle: do_end (default)
# # bad
# let(:foo) { 'bar' }
#
# # good
# let(:foo) do
# 'bar'
# end
#
# # bad
# subject(:foo) { 'bar' }
#
# # good
# subject(:foo) do
# 'bar'
# end
#
# @example EnforcedStyle: braces
# # bad
# let(:foo) do
# 'bar'
# end
#
# # good
# let(:foo) { 'bar' }
#
# # bad
# subject(:foo) do
# 'bar'
# end
#
# # good
# subject(:foo) { 'bar' }
class MemoizedHelperBlockDelimiter < Base
extend AutoCorrector

include ConfigurableEnforcedStyle
include RangeHelp
include RuboCop::RSpec::Language

# @param node [RuboCop::AST::BlockNode]
# @return [void]
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
return unless bad?(node)

add_offense(
node.location.begin.with(
end_pos: node.location.end.end_pos
),
message: "Use #{style} style block delimiters."
) do |corrector|
autocorrect(corrector, node)
end
end

private

# @param corrector [RuboCop::Cop::Corrector]
# @param node [RuboCop::AST::BlockNode]
# @return [void]
def autocorrect(corrector, node)
case style
when :braces
# Not supported because it conflicts with Style/BlockDelimiters.
when :do_end
autocorrect_braces(corrector, node)
end
end

# @param corrector [RuboCop::Cop::Corrector]
# @param node [RuboCop::AST::BlockNode]
# @return [void]
def autocorrect_braces(corrector, node)
unless whitespace_before?(node.location.begin)
corrector.insert_before(node.location.begin, ' ')
end
corrector.replace(node.location.begin, 'do')
corrector.replace(node.location.end, 'end')
wrap_in_newlines(corrector, node) if node.single_line?
end

# @param node [RuboCop::AST::BlockNode]
# @return [Boolean]
def bad?(node)
memoized_helper_method?(node) &&
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line doesn't semantically belong to "bad", it detects if it's a match.
I suggest moving it to on_block:

memoized_helper_method(node) do |memoized_helper|
  next if preferred_block_delimiter?(memoized_helper)
  add_offense(...)

!preferred_block_delimiter?(node)
end

# @param node [RuboCop::AST::BlockNode]
# @return [Boolean]
def memoized_helper_method?(node)
let?(node) || subject?(node)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will also match blockpass, should it?

Comment on lines +98 to +99
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest turning this into a node pattern:

        {
          #{block_pattern('#Helpers.all')}
          #{block_pattern('#Subjects.all')}
        }

end

# @param node [RuboCop::AST::BlockNode]
# @return [Boolean]
def preferred_block_delimiter?(node)
case style
when :braces
node.braces?
when :do_end
!node.braces?
end
end

# @param range [Parser::Source::Range]
# @return [Boolean]
def whitespace_before?(range)
range.source_buffer.source[range.begin_pos - 1].match?(/\s/)
end

# @param corrector [RuboCop::Cop::Corrector]
# @param node [RuboCop::AST::BlockNode]
# @return [void]
def wrap_in_newlines(corrector, node)
corrector.wrap(
node.location.begin.with(
begin_pos: node.location.begin.end_pos,
end_pos: node.location.end.begin_pos
),
"\n",
"\n"
)
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/rspec_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
require_relative 'rspec/leaky_constant_declaration'
require_relative 'rspec/let_before_examples'
require_relative 'rspec/let_setup'
require_relative 'rspec/memoized_helper_block_delimiter'
require_relative 'rspec/message_chain'
require_relative 'rspec/message_expectation'
require_relative 'rspec/message_spies'
Expand Down
12 changes: 9 additions & 3 deletions spec/project/changelog_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# frozen_string_literal: true

RSpec.describe 'CHANGELOG.md' do
subject(:changelog) { SpecHelper::ROOT.join('CHANGELOG.md').read }
subject(:changelog) do
SpecHelper::ROOT.join('CHANGELOG.md').read
end

it 'has link definitions for all implicit links' do
implicit_link_names = changelog.scan(/\[([^\]]+)\]\[\]/).flatten.uniq
Expand Down Expand Up @@ -32,9 +34,13 @@
end

describe 'entry' do
subject(:entries) { lines.grep(/^\*/).map(&:chomp) }
subject(:entries) do
lines.grep(/^\*/).map(&:chomp)
end

let(:lines) { changelog.each_line }
let(:lines) do
changelog.each_line
end

it 'has a whitespace between the * and the body' do
expect(entries).to all(match(/^\* \S/))
Expand Down
4 changes: 3 additions & 1 deletion spec/rubocop/cli/autocorrect_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# frozen_string_literal: true

RSpec.describe 'RuboCop::CLI --autocorrect' do # rubocop:disable RSpec/DescribeClass
subject(:cli) { RuboCop::CLI.new }
subject(:cli) do
RuboCop::CLI.new
end

include_context 'isolated environment'

Expand Down
12 changes: 9 additions & 3 deletions spec/rubocop/cop/rspec/base_spec.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::RSpec::Base do
let(:cop_class) { RuboCop::RSpec::FakeCop }
let(:cop_config) { { 'Exclude' => %w[bar_spec.rb] } }
let(:cop_class) do
RuboCop::RSpec::FakeCop
end
let(:cop_config) do
{ 'Exclude' => %w[bar_spec.rb] }
end

before do
stub_const('RuboCop::RSpec::FakeCop',
Expand Down Expand Up @@ -89,7 +93,9 @@ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
end)
end

let(:cop_class) { RuboCop::RSpec::ExampleGroupHaterCop }
let(:cop_class) do
RuboCop::RSpec::ExampleGroupHaterCop
end

shared_examples_for 'it detects `describe`' do
it 'detects `describe` as an example group' do
Expand Down
8 changes: 6 additions & 2 deletions spec/rubocop/cop/rspec/be_nil_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
end

context 'with EnforcedStyle `be_nil`' do
let(:enforced_style) { 'be_nil' }
let(:enforced_style) do
'be_nil'
end

it 'registers an offense when using `#be` for `nil` value' do
expect_offense(<<~RUBY)
Expand Down Expand Up @@ -37,7 +39,9 @@
end

context 'with EnforcedStyle `be`' do
let(:enforced_style) { 'be' }
let(:enforced_style) do
'be'
end

it 'does not register an offense when using `#be` for `nil` value' do
expect_no_offenses(<<~RUBY)
Expand Down
4 changes: 3 additions & 1 deletion spec/rubocop/cop/rspec/capybara/feature_methods_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@
end

context 'with configured `EnabledMethods`' do
let(:cop_config) { { 'EnabledMethods' => %w[feature] } }
let(:cop_config) do
{ 'EnabledMethods' => %w[feature] }
end

it 'ignores usage of the enabled method' do
expect_no_offenses(<<-RUBY)
Expand Down
12 changes: 9 additions & 3 deletions spec/rubocop/cop/rspec/capybara/negation_matcher_spec.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::RSpec::Capybara::NegationMatcher, :config do
let(:cop_config) { { 'EnforcedStyle' => enforced_style } }
let(:cop_config) do
{ 'EnforcedStyle' => enforced_style }
end

context 'with EnforcedStyle `have_no`' do
let(:enforced_style) { 'have_no' }
let(:enforced_style) do
'have_no'
end

%i[selector css xpath text title current_path link button
field checked_field unchecked_field select table
Expand Down Expand Up @@ -50,7 +54,9 @@
end

context 'with EnforcedStyle `not_to`' do
let(:enforced_style) { 'not_to' }
let(:enforced_style) do
'not_to'
end

%i[selector css xpath text title current_path link button
field checked_field unchecked_field select table
Expand Down
Loading