rubocop-obsession 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +91 -0
- data/config/default.yml +163 -0
- data/lib/rubocop/cop/mixin/files/verbs.txt +8507 -0
- data/lib/rubocop/cop/mixin/helpers.rb +27 -0
- data/lib/rubocop/cop/obsession/graphql/mutation_name.rb +40 -0
- data/lib/rubocop/cop/obsession/method_order.rb +244 -0
- data/lib/rubocop/cop/obsession/no_break_or_next.rb +94 -0
- data/lib/rubocop/cop/obsession/no_paragraphs.rb +62 -0
- data/lib/rubocop/cop/obsession/no_todos.rb +26 -0
- data/lib/rubocop/cop/obsession/rails/callback_one_method.rb +35 -0
- data/lib/rubocop/cop/obsession/rails/fully_defined_json_field.rb +71 -0
- data/lib/rubocop/cop/obsession/rails/migration_belongs_to.rb +44 -0
- data/lib/rubocop/cop/obsession/rails/no_callback_conditions.rb +60 -0
- data/lib/rubocop/cop/obsession/rails/private_callback.rb +59 -0
- data/lib/rubocop/cop/obsession/rails/safety_assured_comment.rb +37 -0
- data/lib/rubocop/cop/obsession/rails/service_name.rb +82 -0
- data/lib/rubocop/cop/obsession/rails/service_perform_method.rb +57 -0
- data/lib/rubocop/cop/obsession/rails/short_after_commit.rb +90 -0
- data/lib/rubocop/cop/obsession/rails/short_validate.rb +36 -0
- data/lib/rubocop/cop/obsession/rails/validate_one_field.rb +33 -0
- data/lib/rubocop/cop/obsession/rails/validation_method_name.rb +32 -0
- data/lib/rubocop/cop/obsession/rspec/describe_public_method.rb +125 -0
- data/lib/rubocop/cop/obsession/rspec/empty_line_after_final_let.rb +36 -0
- data/lib/rubocop/cop/obsession/too_many_paragraphs.rb +56 -0
- data/lib/rubocop/obsession/version.rb +5 -0
- data/lib/rubocop/obsession.rb +9 -0
- metadata +100 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Obsession
|
6
|
+
module Rails
|
7
|
+
# This cop checks for model callbacks with conditions.
|
8
|
+
#
|
9
|
+
# Model callback with conditions should be avoided because they can
|
10
|
+
# quickly degenerate into multiple conditions that pollute the macro
|
11
|
+
# definition section, even more so if lambdas are involved. Instead, move
|
12
|
+
# the condition inside the callback method.
|
13
|
+
#
|
14
|
+
# Note: conditions are allowed for `validates :field` callbacks, as it is
|
15
|
+
# sometimes not easy to translate them into `validate :validate_field`
|
16
|
+
# custom validation callbacks.
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
#
|
20
|
+
# # bad
|
21
|
+
# after_update_commit :crawl_rss, if: :rss_changed?
|
22
|
+
# def crawl_rss
|
23
|
+
# ...
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# # good
|
27
|
+
# after_update_commit :crawl_rss
|
28
|
+
# def crawl_rss
|
29
|
+
# return if !rss_changed?
|
30
|
+
# ...
|
31
|
+
# end
|
32
|
+
class NoCallbackConditions < Cop
|
33
|
+
MSG =
|
34
|
+
'Avoid condition in callback declaration, move it inside the callback method instead.'
|
35
|
+
|
36
|
+
def_node_matcher :callback_with_condition?, <<~PATTERN
|
37
|
+
(
|
38
|
+
send nil? _
|
39
|
+
(sym _)
|
40
|
+
(hash
|
41
|
+
...
|
42
|
+
(pair (sym {:if :unless}) ...)
|
43
|
+
...
|
44
|
+
)
|
45
|
+
)
|
46
|
+
PATTERN
|
47
|
+
|
48
|
+
def on_send(node)
|
49
|
+
return if !callback_with_condition?(node)
|
50
|
+
callback = node.children[1]
|
51
|
+
return if callback == :validates
|
52
|
+
return if callback.to_s.include?('around')
|
53
|
+
|
54
|
+
add_offense(node)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Obsession
|
6
|
+
module Rails
|
7
|
+
# This cop checks for callbacks methods that are not private.
|
8
|
+
#
|
9
|
+
# Callback methods are usually never called outside of the class, so
|
10
|
+
# there is no reason to declare them in the public section. They should
|
11
|
+
# be private.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# before_action :load_blog_post
|
17
|
+
#
|
18
|
+
# def load_blog_post
|
19
|
+
# ...
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# before_action :load_blog_post
|
24
|
+
#
|
25
|
+
# private
|
26
|
+
#
|
27
|
+
# def load_blog_post
|
28
|
+
# ...
|
29
|
+
# end
|
30
|
+
class PrivateCallback < Cop
|
31
|
+
include VisibilityHelp
|
32
|
+
include Helpers
|
33
|
+
|
34
|
+
MSG = 'Make callback method private'
|
35
|
+
|
36
|
+
def_node_matcher :on_callback, <<~PATTERN
|
37
|
+
(send nil? $_ (sym $_) ...)
|
38
|
+
PATTERN
|
39
|
+
|
40
|
+
def on_new_investigation
|
41
|
+
@callbacks = Set.new
|
42
|
+
end
|
43
|
+
|
44
|
+
def on_send(node)
|
45
|
+
on_callback(node) do |callback, method_name|
|
46
|
+
@callbacks << method_name if rails_callback?(callback.to_s)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def on_def(node)
|
51
|
+
if @callbacks.include?(node.method_name) && node_visibility(node) == :public
|
52
|
+
add_offense(node, message: MSG)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Obsession
|
6
|
+
module Rails
|
7
|
+
# This cop checks uses of strong_migrations' `safety_assured { ... }`
|
8
|
+
# without a valid reason.
|
9
|
+
#
|
10
|
+
# `safety_assured { ... }` should only be used after *carefully*
|
11
|
+
# following the instructions from the strong_migrations gem. Always add a
|
12
|
+
# `# Safe because <reason>` comment explaining how you assured the safety
|
13
|
+
# of the DB migration. The reason should be detailed and reviewed by a
|
14
|
+
# knowledgeable PR reviewer. Failure to follow instructions may bring your
|
15
|
+
# app down.
|
16
|
+
class SafetyAssuredComment < Cop
|
17
|
+
MSG =
|
18
|
+
'Add `# Safe because <reason>` comment above safety_assured. ' \
|
19
|
+
'An invalid reason may bring the site down.'
|
20
|
+
|
21
|
+
def_node_matcher :safety_assured_block?, <<~PATTERN
|
22
|
+
(block (send nil? :safety_assured) ...)
|
23
|
+
PATTERN
|
24
|
+
|
25
|
+
def on_block(node)
|
26
|
+
return if !safety_assured_block?(node)
|
27
|
+
previous_comment = processed_source.comments_before_line(node.first_line)&.first
|
28
|
+
|
29
|
+
if previous_comment.nil? || !previous_comment.text.match?(/^# Safe because( [^ ]+){4}/)
|
30
|
+
add_offense(node)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Obsession
|
6
|
+
module Rails
|
7
|
+
# This cop checks for services and jobs whose name do not start with a verb.
|
8
|
+
#
|
9
|
+
# Services and jobs with only one public method should have a name that
|
10
|
+
# starts with a verb, because these classes are essentially performing
|
11
|
+
# one action, and the best way to describe an action is with a verb.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# class Company
|
17
|
+
# def perform
|
18
|
+
# ...
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# class ImportCompany
|
24
|
+
# def perform
|
25
|
+
# ...
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# # bad
|
30
|
+
# class BlogPostPopularityJob < ApplicationJob
|
31
|
+
# def perform
|
32
|
+
# ...
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# # good
|
37
|
+
# class UpdateBlogPostPopularityJob < ApplicationJob
|
38
|
+
# def perform
|
39
|
+
# ...
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
class ServiceName < Base
|
43
|
+
include Helpers
|
44
|
+
|
45
|
+
MSG = 'Service or Job name should start with a verb.'
|
46
|
+
IGNORED_PUBLIC_METHODS = %i[initialize lock_period].freeze
|
47
|
+
|
48
|
+
def_node_matcher :private_section?, <<~PATTERN
|
49
|
+
(send nil? {:private :protected})
|
50
|
+
PATTERN
|
51
|
+
|
52
|
+
def on_class(class_node)
|
53
|
+
return if public_methods(class_node).length != 1
|
54
|
+
class_name = class_node.identifier.source
|
55
|
+
class_name_first_word = class_name.underscore.split('_').first
|
56
|
+
|
57
|
+
add_offense(class_node) if !verb?(class_name_first_word)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def public_methods(class_node)
|
63
|
+
return [] if !class_node.body
|
64
|
+
public_methods = []
|
65
|
+
|
66
|
+
case class_node.body.type
|
67
|
+
when :def
|
68
|
+
public_methods << class_node.body
|
69
|
+
when :begin
|
70
|
+
class_node.body.children.each do |child|
|
71
|
+
public_methods << child if child.type == :def
|
72
|
+
break if private_section?(child)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
public_methods.reject { |method| IGNORED_PUBLIC_METHODS.include?(method.method_name) }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Obsession
|
6
|
+
module Rails
|
7
|
+
# This cop checks for services whose single public method is not named
|
8
|
+
# `perform`.
|
9
|
+
#
|
10
|
+
# Services and jobs with only one public method should have their method
|
11
|
+
# named `perform` for consistency. The choice of `perform` as a name is
|
12
|
+
# inspired from ActiveJob and makes it easier to make services and jobs
|
13
|
+
# interchangeable.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
#
|
17
|
+
# # bad
|
18
|
+
# class ImportCompany
|
19
|
+
# def import
|
20
|
+
# ...
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# # bad
|
25
|
+
# class ImportCompany
|
26
|
+
# def execute
|
27
|
+
# ...
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# # good
|
32
|
+
# class ImportCompany
|
33
|
+
# def perform
|
34
|
+
# ...
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
class ServicePerformMethod < ServiceName
|
38
|
+
extend AutoCorrector
|
39
|
+
|
40
|
+
MSG = 'Single public method of Service should be called `perform`'
|
41
|
+
|
42
|
+
def on_class(class_node)
|
43
|
+
public_methods = public_methods(class_node)
|
44
|
+
return if public_methods.length != 1
|
45
|
+
method = public_methods.first
|
46
|
+
|
47
|
+
if method.method_name != :perform
|
48
|
+
add_offense(method) do |corrector|
|
49
|
+
corrector.replace(method, method.source.sub(/#{method.method_name}/, 'perform'))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Obsession
|
6
|
+
module Rails
|
7
|
+
# This cop checks for `after_commit` declarations that could be shorter.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# # bad
|
12
|
+
# after_commit :send_email, on: :create
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# after_create_commit :send_email
|
16
|
+
class ShortAfterCommit < Cop
|
17
|
+
MSG = 'Use shorter %<prefer>s'
|
18
|
+
|
19
|
+
def_node_matcher :after_commit?, '(send nil? :after_commit ...)'
|
20
|
+
|
21
|
+
def_node_matcher :after_commit_create?, <<~PATTERN
|
22
|
+
(
|
23
|
+
send nil? :after_commit
|
24
|
+
(sym _)
|
25
|
+
(hash
|
26
|
+
(pair (sym :on) {(sym :create)|(array (sym :create))})
|
27
|
+
)
|
28
|
+
)
|
29
|
+
PATTERN
|
30
|
+
|
31
|
+
def_node_matcher :after_commit_update?, <<~PATTERN
|
32
|
+
(
|
33
|
+
send nil? :after_commit
|
34
|
+
(sym _)
|
35
|
+
(hash
|
36
|
+
(pair (sym :on) {(sym :update)|(array (sym :update))})
|
37
|
+
)
|
38
|
+
)
|
39
|
+
PATTERN
|
40
|
+
|
41
|
+
def_node_matcher :after_commit_destroy?, <<~PATTERN
|
42
|
+
(
|
43
|
+
send nil? :after_commit
|
44
|
+
(sym _)
|
45
|
+
(hash
|
46
|
+
(pair (sym :on) {(sym :destroy)|(array (sym :destroy))})
|
47
|
+
)
|
48
|
+
)
|
49
|
+
PATTERN
|
50
|
+
|
51
|
+
def_node_matcher :after_commit_create_update?, <<~PATTERN
|
52
|
+
(
|
53
|
+
send nil? :after_commit
|
54
|
+
(sym _)
|
55
|
+
(hash
|
56
|
+
(pair (sym :on) (array <(sym :create) (sym :update)>))
|
57
|
+
)
|
58
|
+
)
|
59
|
+
PATTERN
|
60
|
+
|
61
|
+
def_node_matcher :after_commit_all_events?, <<~PATTERN
|
62
|
+
(
|
63
|
+
send nil? :after_commit
|
64
|
+
(sym _)
|
65
|
+
(hash
|
66
|
+
(pair (sym :on) (array <(sym :create) (sym :update) (sym :destroy)>))
|
67
|
+
)
|
68
|
+
)
|
69
|
+
PATTERN
|
70
|
+
|
71
|
+
def on_send(node)
|
72
|
+
return if !after_commit?(node)
|
73
|
+
|
74
|
+
if after_commit_create?(node)
|
75
|
+
add_offense(node, message: format(MSG, prefer: 'after_create_commit'))
|
76
|
+
elsif after_commit_update?(node)
|
77
|
+
add_offense(node, message: format(MSG, prefer: 'after_update_commit'))
|
78
|
+
elsif after_commit_destroy?(node)
|
79
|
+
add_offense(node, message: format(MSG, prefer: 'after_destroy_commit'))
|
80
|
+
elsif after_commit_create_update?(node)
|
81
|
+
add_offense(node, message: format(MSG, prefer: 'after_save_commit'))
|
82
|
+
elsif after_commit_all_events?(node)
|
83
|
+
add_offense(node, message: format(MSG, prefer: 'after_commit with no `on:` argument'))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Obsession
|
6
|
+
module Rails
|
7
|
+
# This cop checks for `validate` declarations that could be shorter.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# # bad
|
12
|
+
# validate :validate_url, on: %i(create update)
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# validate :validate_url
|
16
|
+
class ShortValidate < Cop
|
17
|
+
MSG = 'The `on:` argument is not needed in this validate.'
|
18
|
+
|
19
|
+
def_node_matcher :validate_with_unneeded_on?, <<~PATTERN
|
20
|
+
(
|
21
|
+
send nil? :validate
|
22
|
+
(sym _)
|
23
|
+
(hash
|
24
|
+
(pair (sym :on) (array <(sym :create) (sym :update)>))
|
25
|
+
)
|
26
|
+
)
|
27
|
+
PATTERN
|
28
|
+
|
29
|
+
def on_send(node)
|
30
|
+
add_offense(node) if validate_with_unneeded_on?(node)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Obsession
|
6
|
+
module Rails
|
7
|
+
# This cop checks for `validates` callbacks with multiple fields.
|
8
|
+
#
|
9
|
+
# One field per `validates` makes the validation extra clear.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
#
|
13
|
+
# # bad
|
14
|
+
# validates :name, :status, presence: true
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# validates :name, presence: true
|
18
|
+
# validates :status, presence: true
|
19
|
+
class ValidateOneField < Cop
|
20
|
+
MSG = 'Validate only one field per line.'
|
21
|
+
|
22
|
+
def_node_matcher :validates_with_more_than_one_field?, <<~PATTERN
|
23
|
+
(send nil? :validates (sym _) (sym _) ...)
|
24
|
+
PATTERN
|
25
|
+
|
26
|
+
def on_send(node)
|
27
|
+
add_offense(node) if validates_with_more_than_one_field?(node)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Obsession
|
6
|
+
module Rails
|
7
|
+
# This cop checks for validation methods that do not start with `validate_`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# # bad
|
12
|
+
# validate :at_least_one_admin
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# validate :validate_at_least_one_admin
|
16
|
+
class ValidationMethodName < Cop
|
17
|
+
MSG = 'Prefix custom validation method with validate_'
|
18
|
+
|
19
|
+
def_node_matcher :on_validate_callback, <<~PATTERN
|
20
|
+
(send nil? :validate (sym $_) ...)
|
21
|
+
PATTERN
|
22
|
+
|
23
|
+
def on_send(node)
|
24
|
+
on_validate_callback(node) do |method_name|
|
25
|
+
add_offense(node) if !method_name.start_with?('validate_')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Obsession
|
6
|
+
module Rspec
|
7
|
+
# This cop checks for `describe` blocks that test private methods.
|
8
|
+
#
|
9
|
+
# If you are doing black box unit testing, it means that you are only
|
10
|
+
# interested in testing external behavior, a.k.a public interface,
|
11
|
+
# a.k.a public methods. Private methods are considered implementation
|
12
|
+
# details and are not directly tested.
|
13
|
+
#
|
14
|
+
# If you need to test a Rails callback, test it indirectly through its
|
15
|
+
# corresponding Rails public method, e.g. #create, #save, etc.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
#
|
19
|
+
# class Comment < ApplicationRecord
|
20
|
+
# after_create_commit :notify_users
|
21
|
+
#
|
22
|
+
# private
|
23
|
+
#
|
24
|
+
# def notify_users
|
25
|
+
# ...
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# # bad
|
30
|
+
# describe '#notify_users' do
|
31
|
+
# ...
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# # good
|
35
|
+
# describe '#create' do
|
36
|
+
# it 'notifies users' do
|
37
|
+
# ...
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
class DescribePublicMethod < Cop
|
41
|
+
MSG = 'Only test public methods.'
|
42
|
+
|
43
|
+
def_node_matcher :on_context_method, <<-PATTERN
|
44
|
+
(block (send nil? :describe (str $#method_name?)) ...)
|
45
|
+
PATTERN
|
46
|
+
|
47
|
+
def_node_search :class_nodes, <<~PATTERN
|
48
|
+
(class ...)
|
49
|
+
PATTERN
|
50
|
+
|
51
|
+
def_node_matcher :private_section?, <<~PATTERN
|
52
|
+
(send nil? {:private :protected})
|
53
|
+
PATTERN
|
54
|
+
|
55
|
+
def on_block(node)
|
56
|
+
on_context_method(node) do |method_name|
|
57
|
+
method_name = method_name.sub('#', '').to_sym
|
58
|
+
add_offense(node) if private_methods.include?(method_name)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def method_name?(description)
|
65
|
+
description.start_with?('#')
|
66
|
+
end
|
67
|
+
|
68
|
+
def private_methods
|
69
|
+
return @private_methods if @private_methods
|
70
|
+
@private_methods = []
|
71
|
+
|
72
|
+
if File.exist?(tested_file_path)
|
73
|
+
node = parse_file(tested_file_path)
|
74
|
+
@private_methods = find_private_methods(class_nodes(node).first)
|
75
|
+
end
|
76
|
+
|
77
|
+
@private_methods
|
78
|
+
end
|
79
|
+
|
80
|
+
def tested_file_path
|
81
|
+
return @tested_file_path if @tested_file_path
|
82
|
+
|
83
|
+
spec_path = processed_source.file_path.sub(Dir.pwd, '')
|
84
|
+
file_path =
|
85
|
+
if spec_path.include?('/lib/')
|
86
|
+
spec_path.sub('/spec/lib/', '/lib/')
|
87
|
+
else
|
88
|
+
spec_path.sub('/spec/', '/app/')
|
89
|
+
end
|
90
|
+
file_path = file_path.sub('_spec.rb', '.rb')
|
91
|
+
file_path = File.join(Dir.pwd, file_path)
|
92
|
+
|
93
|
+
@tested_file_path = file_path
|
94
|
+
end
|
95
|
+
|
96
|
+
def parse_file(file_path)
|
97
|
+
parser_class = ::Parser.const_get(:"Ruby#{target_ruby_version.to_s.sub('.', '')}")
|
98
|
+
parser = parser_class.new(RuboCop::AST::Builder.new)
|
99
|
+
|
100
|
+
buffer = Parser::Source::Buffer.new(file_path, 1)
|
101
|
+
buffer.source = File.read(file_path)
|
102
|
+
|
103
|
+
parser.parse(buffer)
|
104
|
+
end
|
105
|
+
|
106
|
+
def find_private_methods(class_node)
|
107
|
+
return [] if class_node&.body&.type != :begin
|
108
|
+
private_methods = []
|
109
|
+
private_section_found = false
|
110
|
+
|
111
|
+
class_node.body.children.each do |child|
|
112
|
+
if private_section?(child)
|
113
|
+
private_section_found = true
|
114
|
+
elsif child.type == :def && private_section_found
|
115
|
+
private_methods << child.method_name
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
private_methods
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
if defined?(RuboCop::RSpec)
|
2
|
+
module RuboCop
|
3
|
+
module Cop
|
4
|
+
module Obsession
|
5
|
+
module Rspec
|
6
|
+
# Same as RSpec/EmptyLineAfterFinalLet, but allows `let` to be followed
|
7
|
+
# by `it` with no new line, to allow for this style of spec:
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# describe '#domain' do
|
12
|
+
# context do
|
13
|
+
# let(:url) { Url.new('http://www.some-site.com/some-page') }
|
14
|
+
# it { expect(url.domain).to eq 'some-site.com' }
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# context do
|
18
|
+
# let(:url) { Url.new('some-site.com') }
|
19
|
+
# it { expect(url.domain).to eq 'some-site.com' }
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
class EmptyLineAfterFinalLet < RSpec::EmptyLineAfterFinalLet
|
23
|
+
def missing_separating_line(node)
|
24
|
+
line = final_end_location(node).line
|
25
|
+
line += 1 while comment_line?(processed_source[line])
|
26
|
+
return if processed_source[line].blank?
|
27
|
+
return if processed_source[line].match?(/\s*it /)
|
28
|
+
|
29
|
+
yield offending_loc(line)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|