rubocop-rails 2.4.1 → 2.6.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 +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +6 -2
- data/config/default.yml +71 -6
- data/lib/rubocop-rails.rb +3 -0
- data/lib/rubocop/cop/mixin/active_record_helper.rb +77 -0
- data/lib/rubocop/cop/mixin/index_method.rb +161 -0
- data/lib/rubocop/cop/rails/content_tag.rb +82 -0
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +1 -3
- data/lib/rubocop/cop/rails/delegate.rb +1 -3
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +40 -15
- data/lib/rubocop/cop/rails/environment_comparison.rb +60 -14
- data/lib/rubocop/cop/rails/exit.rb +2 -2
- data/lib/rubocop/cop/rails/file_path.rb +1 -0
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +2 -2
- data/lib/rubocop/cop/rails/http_status.rb +2 -0
- data/lib/rubocop/cop/rails/index_by.rb +56 -0
- data/lib/rubocop/cop/rails/index_with.rb +59 -0
- data/lib/rubocop/cop/rails/inverse_of.rb +0 -4
- data/lib/rubocop/cop/rails/link_to_blank.rb +1 -3
- data/lib/rubocop/cop/rails/pick.rb +51 -0
- data/lib/rubocop/cop/rails/presence.rb +2 -6
- data/lib/rubocop/cop/rails/rake_environment.rb +24 -6
- data/lib/rubocop/cop/rails/redundant_foreign_key.rb +80 -0
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +0 -3
- data/lib/rubocop/cop/rails/refute_methods.rb +52 -26
- data/lib/rubocop/cop/rails/reversible_migration.rb +6 -1
- data/lib/rubocop/cop/rails/save_bang.rb +16 -9
- data/lib/rubocop/cop/rails/skips_model_validations.rb +4 -1
- data/lib/rubocop/cop/rails/time_zone.rb +1 -3
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +16 -16
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +155 -0
- data/lib/rubocop/cop/rails/unknown_env.rb +7 -6
- data/lib/rubocop/cop/rails_cops.rb +8 -0
- data/lib/rubocop/rails/inject.rb +1 -1
- data/lib/rubocop/rails/schema_loader.rb +61 -0
- data/lib/rubocop/rails/schema_loader/schema.rb +190 -0
- data/lib/rubocop/rails/version.rb +1 -1
- metadata +32 -8
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks that `tag` is used instead of `content_tag`
|
7
|
+
# because `content_tag` is legacy syntax.
|
8
|
+
#
|
9
|
+
# NOTE: Allow `content_tag` when the first argument is a variable because
|
10
|
+
# `content_tag(name)` is simpler rather than `tag.public_send(name)`.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # bad
|
14
|
+
# content_tag(:p, 'Hello world!')
|
15
|
+
# content_tag(:br)
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# tag.p('Hello world!')
|
19
|
+
# tag.br
|
20
|
+
# content_tag(name, 'Hello world!')
|
21
|
+
class ContentTag < Cop
|
22
|
+
include RangeHelp
|
23
|
+
extend TargetRailsVersion
|
24
|
+
|
25
|
+
minimum_target_rails_version 5.1
|
26
|
+
|
27
|
+
MSG = 'Use `tag` instead of `content_tag`.'
|
28
|
+
|
29
|
+
def on_send(node)
|
30
|
+
return unless node.method?(:content_tag)
|
31
|
+
|
32
|
+
first_argument = node.first_argument
|
33
|
+
return unless first_argument
|
34
|
+
|
35
|
+
return if first_argument.variable? || first_argument.send_type? || first_argument.const_type?
|
36
|
+
|
37
|
+
add_offense(node)
|
38
|
+
end
|
39
|
+
|
40
|
+
def autocorrect(node)
|
41
|
+
lambda do |corrector|
|
42
|
+
if method_name?(node.first_argument)
|
43
|
+
replace_method_with_tag_method(corrector, node)
|
44
|
+
remove_first_argument(corrector, node)
|
45
|
+
else
|
46
|
+
corrector.replace(node.loc.selector, 'tag')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def method_name?(node)
|
54
|
+
return false unless node.str_type? || node.sym_type?
|
55
|
+
|
56
|
+
/^[a-zA-Z_][a-zA-Z_0-9]*$/.match?(node.value)
|
57
|
+
end
|
58
|
+
|
59
|
+
def replace_method_with_tag_method(corrector, node)
|
60
|
+
corrector.replace(
|
61
|
+
node.loc.selector,
|
62
|
+
"tag.#{node.first_argument.value}"
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
def remove_first_argument(corrector, node)
|
67
|
+
if node.arguments.length > 1
|
68
|
+
corrector.remove(
|
69
|
+
range_between(child_node_beg(node, 0), child_node_beg(node, 1))
|
70
|
+
)
|
71
|
+
elsif node.arguments.length == 1
|
72
|
+
corrector.remove(node.arguments[0].loc.expression)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def child_node_beg(node, index)
|
77
|
+
node.arguments[index].loc.expression.begin_pos
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -70,9 +70,7 @@ module RuboCop
|
|
70
70
|
parent = node.parent
|
71
71
|
|
72
72
|
if create_table_with_block?(parent)
|
73
|
-
if parent.body.nil? || !time_columns_included?(parent.body)
|
74
|
-
add_offense(parent)
|
75
|
-
end
|
73
|
+
add_offense(parent) if parent.body.nil? || !time_columns_included?(parent.body)
|
76
74
|
elsif create_table_with_timestamps_proc?(node)
|
77
75
|
# nothing to do
|
78
76
|
else
|
@@ -71,9 +71,7 @@ module RuboCop
|
|
71
71
|
delegation = ["delegate :#{node.body.method_name}",
|
72
72
|
"to: :#{node.body.receiver.method_name}"]
|
73
73
|
|
74
|
-
if node.method?(prefixed_method_name(node.body))
|
75
|
-
delegation << ['prefix: true']
|
76
|
-
end
|
74
|
+
delegation << ['prefix: true'] if node.method?(prefixed_method_name(node.body))
|
77
75
|
|
78
76
|
lambda do |corrector|
|
79
77
|
corrector.replace(node.source_range, delegation.join(', '))
|
@@ -10,37 +10,41 @@ module RuboCop
|
|
10
10
|
# @example
|
11
11
|
# # bad
|
12
12
|
# User.find_by_name(name)
|
13
|
-
#
|
14
|
-
# # bad
|
15
13
|
# User.find_by_name_and_email(name)
|
16
|
-
#
|
17
|
-
# # bad
|
18
14
|
# User.find_by_email!(name)
|
19
15
|
#
|
20
16
|
# # good
|
21
17
|
# User.find_by(name: name)
|
18
|
+
# User.find_by(name: name, email: email)
|
19
|
+
# User.find_by!(email: email)
|
20
|
+
#
|
21
|
+
# @example AllowedMethods: find_by_sql
|
22
|
+
# # bad
|
23
|
+
# User.find_by_query(users_query)
|
22
24
|
#
|
23
25
|
# # good
|
24
|
-
# User.
|
26
|
+
# User.find_by_sql(users_sql)
|
27
|
+
#
|
28
|
+
# @example AllowedReceivers: Gem::Specification
|
29
|
+
# # bad
|
30
|
+
# Specification.find_by_name('backend').gem_dir
|
25
31
|
#
|
26
32
|
# # good
|
27
|
-
#
|
33
|
+
# Gem::Specification.find_by_name('backend').gem_dir
|
28
34
|
class DynamicFindBy < Cop
|
29
35
|
MSG = 'Use `%<static_name>s` instead of dynamic `%<method>s`.'
|
30
36
|
METHOD_PATTERN = /^find_by_(.+?)(!)?$/.freeze
|
31
37
|
|
32
38
|
def on_send(node)
|
33
|
-
|
34
|
-
|
35
|
-
return if whitelist.include?(method_name)
|
39
|
+
return if allowed_invocation?(node)
|
36
40
|
|
41
|
+
method_name = node.method_name
|
37
42
|
static_name = static_method_name(method_name)
|
38
|
-
|
39
43
|
return unless static_name
|
40
44
|
|
41
45
|
add_offense(node,
|
42
46
|
message: format(MSG, static_name: static_name,
|
43
|
-
method:
|
47
|
+
method: method_name))
|
44
48
|
end
|
45
49
|
alias on_csend on_send
|
46
50
|
|
@@ -57,6 +61,31 @@ module RuboCop
|
|
57
61
|
|
58
62
|
private
|
59
63
|
|
64
|
+
def allowed_invocation?(node)
|
65
|
+
allowed_method?(node) || allowed_receiver?(node) ||
|
66
|
+
whitelisted?(node)
|
67
|
+
end
|
68
|
+
|
69
|
+
def allowed_method?(node)
|
70
|
+
return unless cop_config['AllowedMethods']
|
71
|
+
|
72
|
+
cop_config['AllowedMethods'].include?(node.method_name.to_s)
|
73
|
+
end
|
74
|
+
|
75
|
+
def allowed_receiver?(node)
|
76
|
+
return unless cop_config['AllowedReceivers'] && node.receiver
|
77
|
+
|
78
|
+
cop_config['AllowedReceivers'].include?(node.receiver.source)
|
79
|
+
end
|
80
|
+
|
81
|
+
# config option `WhiteList` will be deprecated soon
|
82
|
+
def whitelisted?(node)
|
83
|
+
whitelist_config = cop_config['Whitelist']
|
84
|
+
return unless whitelist_config
|
85
|
+
|
86
|
+
whitelist_config.include?(node.method_name.to_s)
|
87
|
+
end
|
88
|
+
|
60
89
|
def autocorrect_method_name(corrector, node)
|
61
90
|
corrector.replace(node.loc.selector,
|
62
91
|
static_method_name(node.method_name.to_s))
|
@@ -68,10 +97,6 @@ module RuboCop
|
|
68
97
|
end
|
69
98
|
end
|
70
99
|
|
71
|
-
def whitelist
|
72
|
-
cop_config['Whitelist']
|
73
|
-
end
|
74
|
-
|
75
100
|
def column_keywords(method)
|
76
101
|
keyword_string = method.to_s[METHOD_PATTERN, 1]
|
77
102
|
keyword_string.split('_and_').map { |keyword| "#{keyword}: " }
|
@@ -16,52 +16,98 @@ module RuboCop
|
|
16
16
|
# # good
|
17
17
|
# Rails.env.production?
|
18
18
|
class EnvironmentComparison < Cop
|
19
|
-
MSG =
|
19
|
+
MSG = 'Favor `%<bang>sRails.env.%<env>s?` over `%<source>s`.'
|
20
20
|
|
21
21
|
SYM_MSG = 'Do not compare `Rails.env` with a symbol, it will always ' \
|
22
22
|
'evaluate to `false`.'
|
23
23
|
|
24
|
-
def_node_matcher :
|
24
|
+
def_node_matcher :comparing_str_env_with_rails_env_on_lhs?, <<~PATTERN
|
25
25
|
(send
|
26
26
|
(send (const {nil? cbase} :Rails) :env)
|
27
|
-
:==
|
27
|
+
{:== :!=}
|
28
28
|
$str
|
29
29
|
)
|
30
30
|
PATTERN
|
31
31
|
|
32
|
-
def_node_matcher :
|
32
|
+
def_node_matcher :comparing_str_env_with_rails_env_on_rhs?, <<~PATTERN
|
33
|
+
(send
|
34
|
+
$str
|
35
|
+
{:== :!=}
|
36
|
+
(send (const {nil? cbase} :Rails) :env)
|
37
|
+
)
|
38
|
+
PATTERN
|
39
|
+
|
40
|
+
def_node_matcher :comparing_sym_env_with_rails_env_on_lhs?, <<~PATTERN
|
33
41
|
(send
|
34
42
|
(send (const {nil? cbase} :Rails) :env)
|
35
|
-
:==
|
43
|
+
{:== :!=}
|
36
44
|
$sym
|
37
45
|
)
|
38
46
|
PATTERN
|
39
47
|
|
48
|
+
def_node_matcher :comparing_sym_env_with_rails_env_on_rhs?, <<~PATTERN
|
49
|
+
(send
|
50
|
+
$sym
|
51
|
+
{:== :!=}
|
52
|
+
(send (const {nil? cbase} :Rails) :env)
|
53
|
+
)
|
54
|
+
PATTERN
|
55
|
+
|
56
|
+
def_node_matcher :content, <<~PATTERN
|
57
|
+
({str sym} $_)
|
58
|
+
PATTERN
|
59
|
+
|
40
60
|
def on_send(node)
|
41
|
-
|
61
|
+
if (env_node = comparing_str_env_with_rails_env_on_lhs?(node) ||
|
62
|
+
comparing_str_env_with_rails_env_on_rhs?(node))
|
42
63
|
env, = *env_node
|
43
|
-
|
64
|
+
bang = node.method?(:!=) ? '!' : ''
|
65
|
+
|
66
|
+
add_offense(node, message: format(
|
67
|
+
MSG, bang: bang, env: env, source: node.source
|
68
|
+
))
|
44
69
|
end
|
45
|
-
|
70
|
+
|
71
|
+
if comparing_sym_env_with_rails_env_on_lhs?(node) ||
|
72
|
+
comparing_sym_env_with_rails_env_on_rhs?(node)
|
46
73
|
add_offense(node, message: SYM_MSG)
|
47
74
|
end
|
48
75
|
end
|
49
76
|
|
50
77
|
def autocorrect(node)
|
51
78
|
lambda do |corrector|
|
52
|
-
|
79
|
+
replacement = build_predicate_method(node)
|
80
|
+
|
81
|
+
corrector.replace(node.source_range, replacement)
|
53
82
|
end
|
54
83
|
end
|
55
84
|
|
56
85
|
private
|
57
86
|
|
58
|
-
def
|
59
|
-
|
87
|
+
def build_predicate_method(node)
|
88
|
+
if rails_env_on_lhs?(node)
|
89
|
+
build_predicate_method_for_rails_env_on_lhs(node)
|
90
|
+
else
|
91
|
+
build_predicate_method_for_rails_env_on_rhs(node)
|
92
|
+
end
|
60
93
|
end
|
61
94
|
|
62
|
-
|
63
|
-
(
|
64
|
-
|
95
|
+
def rails_env_on_lhs?(node)
|
96
|
+
comparing_str_env_with_rails_env_on_lhs?(node) ||
|
97
|
+
comparing_sym_env_with_rails_env_on_lhs?(node)
|
98
|
+
end
|
99
|
+
|
100
|
+
def build_predicate_method_for_rails_env_on_lhs(node)
|
101
|
+
bang = node.method?(:!=) ? '!' : ''
|
102
|
+
|
103
|
+
"#{bang}#{node.receiver.source}.#{content(node.first_argument)}?"
|
104
|
+
end
|
105
|
+
|
106
|
+
def build_predicate_method_for_rails_env_on_rhs(node)
|
107
|
+
bang = node.method?(:!=) ? '!' : ''
|
108
|
+
|
109
|
+
"#{bang}#{node.first_argument.source}.#{content(node.receiver)}?"
|
110
|
+
end
|
65
111
|
end
|
66
112
|
end
|
67
113
|
end
|
@@ -9,11 +9,11 @@ module RuboCop
|
|
9
9
|
#
|
10
10
|
# There are two obvious cases where `exit` is particularly harmful:
|
11
11
|
#
|
12
|
-
#
|
12
|
+
# * Usage in library code for your application. Even though Rails will
|
13
13
|
# rescue from a `SystemExit` and continue on, unit testing that library
|
14
14
|
# code will result in specs exiting (potentially silently if `exit(0)`
|
15
15
|
# is used.)
|
16
|
-
#
|
16
|
+
# * Usage in application code outside of the web process could result in
|
17
17
|
# the program exiting, which could result in the code failing to run and
|
18
18
|
# do its job.
|
19
19
|
#
|
@@ -8,7 +8,7 @@ module RuboCop
|
|
8
8
|
# change them to use keyword args. This cop only applies to Rails >= 5.
|
9
9
|
# If you are running Rails < 5 you should disable the
|
10
10
|
# Rails/HttpPositionalArguments cop or set your TargetRailsVersion in your
|
11
|
-
# .rubocop.yml file to 4.
|
11
|
+
# .rubocop.yml file to 4.2.
|
12
12
|
#
|
13
13
|
# @example
|
14
14
|
# # bad
|
@@ -22,7 +22,7 @@ module RuboCop
|
|
22
22
|
MSG = 'Use keyword arguments instead of ' \
|
23
23
|
'positional arguments for http call: `%<verb>s`.'
|
24
24
|
KEYWORD_ARGS = %i[
|
25
|
-
method params session body flash xhr as headers env
|
25
|
+
method params session body flash xhr as headers env to
|
26
26
|
].freeze
|
27
27
|
HTTP_METHODS = %i[get post put patch delete head].freeze
|
28
28
|
|
@@ -83,6 +83,7 @@ module RuboCop
|
|
83
83
|
'to define HTTP status code.'
|
84
84
|
|
85
85
|
attr_reader :node
|
86
|
+
|
86
87
|
def initialize(node)
|
87
88
|
@node = node
|
88
89
|
end
|
@@ -124,6 +125,7 @@ module RuboCop
|
|
124
125
|
PERMITTED_STATUS = %i[error success missing redirect].freeze
|
125
126
|
|
126
127
|
attr_reader :node
|
128
|
+
|
127
129
|
def initialize(node)
|
128
130
|
@node = node
|
129
131
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop looks for uses of `each_with_object({}) { ... }`,
|
7
|
+
# `map { ... }.to_h`, and `Hash[map { ... }]` that are transforming
|
8
|
+
# an enumerable into a hash where the values are the original elements.
|
9
|
+
# Rails provides the `index_by` method for this purpose.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# # bad
|
13
|
+
# [1, 2, 3].each_with_object({}) { |el, h| h[foo(el)] = el }
|
14
|
+
# [1, 2, 3].map { |el| [foo(el), el] }.to_h
|
15
|
+
# Hash[[1, 2, 3].collect { |el| [foo(el), el] }]
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# [1, 2, 3].index_by { |el| foo(el) }
|
19
|
+
class IndexBy < Cop
|
20
|
+
include IndexMethod
|
21
|
+
|
22
|
+
def_node_matcher :on_bad_each_with_object, <<~PATTERN
|
23
|
+
(block
|
24
|
+
({send csend} _ :each_with_object (hash))
|
25
|
+
(args (arg $_el) (arg _memo))
|
26
|
+
({send csend} (lvar _memo) :[]= $_ (lvar _el)))
|
27
|
+
PATTERN
|
28
|
+
|
29
|
+
def_node_matcher :on_bad_map_to_h, <<~PATTERN
|
30
|
+
({send csend}
|
31
|
+
(block
|
32
|
+
({send csend} _ {:map :collect})
|
33
|
+
(args (arg $_el))
|
34
|
+
(array $_ (lvar _el)))
|
35
|
+
:to_h)
|
36
|
+
PATTERN
|
37
|
+
|
38
|
+
def_node_matcher :on_bad_hash_brackets_map, <<~PATTERN
|
39
|
+
(send
|
40
|
+
(const _ :Hash)
|
41
|
+
:[]
|
42
|
+
(block
|
43
|
+
({send csend} _ {:map :collect})
|
44
|
+
(args (arg $_el))
|
45
|
+
(array $_ (lvar _el))))
|
46
|
+
PATTERN
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def new_method_name
|
51
|
+
'index_by'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop looks for uses of `each_with_object({}) { ... }`,
|
7
|
+
# `map { ... }.to_h`, and `Hash[map { ... }]` that are transforming
|
8
|
+
# an enumerable into a hash where the keys are the original elements.
|
9
|
+
# Rails provides the `index_with` method for this purpose.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# # bad
|
13
|
+
# [1, 2, 3].each_with_object({}) { |el, h| h[el] = foo(el) }
|
14
|
+
# [1, 2, 3].map { |el| [el, foo(el)] }.to_h
|
15
|
+
# Hash[[1, 2, 3].collect { |el| [el, foo(el)] }]
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# [1, 2, 3].index_with { |el| foo(el) }
|
19
|
+
class IndexWith < Cop
|
20
|
+
extend TargetRailsVersion
|
21
|
+
include IndexMethod
|
22
|
+
|
23
|
+
minimum_target_rails_version 6.0
|
24
|
+
|
25
|
+
def_node_matcher :on_bad_each_with_object, <<~PATTERN
|
26
|
+
(block
|
27
|
+
({send csend} _ :each_with_object (hash))
|
28
|
+
(args (arg $_el) (arg _memo))
|
29
|
+
({send csend} (lvar _memo) :[]= (lvar _el) $_))
|
30
|
+
PATTERN
|
31
|
+
|
32
|
+
def_node_matcher :on_bad_map_to_h, <<~PATTERN
|
33
|
+
({send csend}
|
34
|
+
(block
|
35
|
+
({send csend} _ {:map :collect})
|
36
|
+
(args (arg $_el))
|
37
|
+
(array (lvar _el) $_))
|
38
|
+
:to_h)
|
39
|
+
PATTERN
|
40
|
+
|
41
|
+
def_node_matcher :on_bad_hash_brackets_map, <<~PATTERN
|
42
|
+
(send
|
43
|
+
(const _ :Hash)
|
44
|
+
:[]
|
45
|
+
(block
|
46
|
+
({send csend} _ {:map :collect})
|
47
|
+
(args (arg $_el))
|
48
|
+
(array (lvar _el) $_)))
|
49
|
+
PATTERN
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def new_method_name
|
54
|
+
'index_with'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|