-
-
Notifications
You must be signed in to change notification settings - Fork 269
/
multiple_memoized_helpers.rb
145 lines (133 loc) · 3.57 KB
/
multiple_memoized_helpers.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# frozen_string_literal: true
module RuboCop
module Cop
module RSpec
# Checks if example groups contain too many `let` and `subject` calls.
#
# This cop is configurable using the `Max` option and the `AllowSubject`
# which will configure the cop to only register offenses on calls to
# `let` and not calls to `subject`.
#
# @example
# # bad
# describe MyClass do
# let(:foo) { [] }
# let(:bar) { [] }
# let!(:baz) { [] }
# let(:qux) { [] }
# let(:quux) { [] }
# subject(:quuz) { {} }
# end
#
# describe MyClass do
# let(:foo) { [] }
# let(:bar) { [] }
# let!(:baz) { [] }
#
# context 'when stuff' do
# let(:qux) { [] }
# let(:quux) { [] }
# subject(:quuz) { {} }
# end
# end
#
# # good
# describe MyClass do
# let(:bar) { [] }
# let!(:baz) { [] }
# let(:qux) { [] }
# let(:quux) { [] }
# subject(:quuz) { {} }
# end
#
# describe MyClass do
# context 'when stuff' do
# let(:foo) { [] }
# let(:bar) { [] }
# let!(:booger) { [] }
# end
#
# context 'when other stuff' do
# let(:qux) { [] }
# let(:quux) { [] }
# subject(:quuz) { {} }
# end
# end
#
# @example when disabling AllowSubject configuration
#
# # rubocop.yml
# # RSpec/MultipleMemoizedHelpers:
# # AllowSubject: true
#
# # good
# describe MyClass do
# subject { {} }
# let(:foo) { [] }
# let(:bar) { [] }
# let!(:baz) { [] }
# let(:qux) { [] }
# let(:quux) { [] }
# end
#
# @example with Max configuration
#
# # rubocop.yml
# # RSpec/MultipleMemoizedHelpers:
# # Max: 0
#
# # bad
# describe MyClass do
# let(:foo) { [] }
# end
#
# # good
# describe MyClass do
# def foo; []; end
# end
#
class MultipleMemoizedHelpers < Base
include ConfigurableMax
include RuboCop::RSpec::Variable
MSG = 'Example group has too many memoized helpers [%<count>d/%<max>d]'
def on_block(node)
return unless spec_group?(node)
count = all_helpers(node).uniq.count
return if count <= max
self.max = count
add_offense(node, message: format(MSG, count: count, max: max))
end
private
def all_helpers(node)
[
*helpers(node),
*node.each_ancestor(:block).flat_map(&method(:helpers))
]
end
def helpers(node)
variable_nodes(node).map do |variable_node|
if variable_node.block_type?
variable_definition?(variable_node.send_node)
else # block-pass (`let(:foo, &bar)`)
variable_definition?(variable_node)
end
end
end
def variable_nodes(node)
example_group = RuboCop::RSpec::ExampleGroup.new(node)
if allow_subject?
example_group.lets
else
example_group.lets + example_group.subjects
end
end
def max
cop_config['Max']
end
def allow_subject?
cop_config['AllowSubject']
end
end
end
end
end