rubocop-obsession 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|