Skip to content

Commit 20e37fb

Browse files
committed
RSpec/SubjectStub. Refactor and decrease complexity
1 parent 97e8ebb commit 20e37fb

File tree

1 file changed

+35
-46
lines changed

1 file changed

+35
-46
lines changed

lib/rubocop/cop/rspec/subject_stub.rb

Lines changed: 35 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# frozen_string_literal: true
22

3+
require 'set'
4+
35
module RuboCop
46
module Cop
57
module RSpec
@@ -74,71 +76,58 @@ class SubjectStub < Cop
7476
PATTERN
7577

7678
def on_block(node)
79+
# process only describe/context/... example groups
7780
return unless example_group?(node)
7881

79-
find_subject_stub(node) do |stub|
82+
# skip already processed example group
83+
# it's processed if is nested in one of the processed example groups
84+
return unless (processed_example_groups & node.ancestors).empty?
85+
86+
# add example group to already processed
87+
processed_example_groups << node
88+
89+
# find all custom subjects e.g. subject(:foo) { ... }
90+
@named_subjects = find_all_named_subjects(node)
91+
92+
# look for method expectation matcher
93+
find_subject_expectations(node) do |stub|
8094
add_offense(stub)
8195
end
8296
end
8397

8498
private
8599

86-
# Find subjects within tree and then find (send) nodes for that subject
87-
#
88-
# @param node [RuboCop::Node] example group
89-
#
90-
# @yield [RuboCop::Node] message expectations for subject
91-
def find_subject_stub(node, &block)
92-
find_subject(node) do |subject_name, context|
93-
find_subject_expectation(context, subject_name, &block)
94-
end
100+
def processed_example_groups
101+
@processed_example_groups ||= Set[]
95102
end
96103

97-
# Find a subject message expectation
98-
#
99-
# @param node [RuboCop::Node]
100-
# @param subject_name [Symbol] name of subject
101-
#
102-
# @yield [RuboCop::Node] message expectation
103-
def find_subject_expectation(node, subject_name, &block)
104-
# Do not search node if it is an example group with its own subject.
105-
return if example_group?(node) && redefines_subject?(node)
106-
107-
# Yield the current node if it is a message expectation.
108-
yield(node) if message_expectation?(node, subject_name)
104+
def find_all_named_subjects(node)
105+
named_subjects = {}
109106

110-
# Recurse through node's children looking for a message expectation.
111-
node.each_child_node do |child|
112-
find_subject_expectation(child, subject_name, &block)
107+
node.each_descendant(:block) do |child|
108+
name = subject(child)
109+
named_subjects[child.parent.parent] = name if name
113110
end
111+
112+
named_subjects
114113
end
115114

116-
# Check if node's children contain a subject definition
117-
#
118-
# @param node [RuboCop::Node]
119-
#
120-
# @return [Boolean]
121-
def redefines_subject?(node)
122-
node.each_child_node.any? do |child|
123-
subject(child) || redefines_subject?(child)
115+
def find_subject_expectations(node, subject_name = nil, &block)
116+
# if it's a new example group - check whether new named subject is
117+
# defined there
118+
if example_group?(node)
119+
subject_name = @named_subjects[node] || subject_name
124120
end
125-
end
126121

127-
# Find a subject definition
128-
#
129-
# @param node [RuboCop::Node]
130-
# @param parent [RuboCop::Node,nil]
131-
#
132-
# @yieldparam subject_name [Symbol] name of subject being defined
133-
# @yieldparam parent [RuboCop::Node] parent of subject definition
134-
def find_subject(node, parent: nil, &block)
135-
# An implicit subject is defined by RSpec when no subject is declared
136-
subject_name = subject(node) || :subject
122+
# check default :subject and then named one (if it's present)
123+
expectation_detected = message_expectation?(node, :subject) || \
124+
(subject_name && message_expectation?(node, subject_name))
137125

138-
yield(subject_name, parent) if parent
126+
return yield(node) if expectation_detected
139127

128+
# Recurse through node's children looking for a message expectation.
140129
node.each_child_node do |child|
141-
find_subject(child, parent: node, &block)
130+
find_subject_expectations(child, subject_name, &block)
142131
end
143132
end
144133
end

0 commit comments

Comments
 (0)