rubocop-rails 2.5.0 → 2.7.1
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 +2 -2
- data/config/default.yml +156 -9
- data/lib/rubocop/cop/mixin/active_record_helper.rb +22 -0
- data/lib/rubocop/cop/mixin/index_method.rb +8 -1
- data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +145 -0
- data/lib/rubocop/cop/rails/content_tag.rb +69 -0
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +1 -3
- data/lib/rubocop/cop/rails/default_scope.rb +54 -0
- data/lib/rubocop/cop/rails/delegate.rb +2 -4
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +40 -15
- data/lib/rubocop/cop/rails/exit.rb +2 -2
- data/lib/rubocop/cop/rails/file_path.rb +2 -1
- data/lib/rubocop/cop/rails/find_by_id.rb +103 -0
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +1 -1
- data/lib/rubocop/cop/rails/http_status.rb +2 -0
- data/lib/rubocop/cop/rails/inquiry.rb +34 -0
- data/lib/rubocop/cop/rails/inverse_of.rb +0 -4
- data/lib/rubocop/cop/rails/link_to_blank.rb +3 -3
- data/lib/rubocop/cop/rails/mailer_name.rb +80 -0
- data/lib/rubocop/cop/rails/match_route.rb +119 -0
- data/lib/rubocop/cop/rails/negate_include.rb +39 -0
- data/lib/rubocop/cop/rails/pick.rb +55 -0
- data/lib/rubocop/cop/rails/pluck.rb +59 -0
- data/lib/rubocop/cop/rails/pluck_id.rb +58 -0
- data/lib/rubocop/cop/rails/pluck_in_where.rb +36 -0
- data/lib/rubocop/cop/rails/presence.rb +2 -6
- data/lib/rubocop/cop/rails/rake_environment.rb +17 -0
- 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/render_inline.rb +40 -0
- data/lib/rubocop/cop/rails/render_plain_text.rb +76 -0
- data/lib/rubocop/cop/rails/safe_navigation.rb +1 -1
- data/lib/rubocop/cop/rails/save_bang.rb +6 -7
- data/lib/rubocop/cop/rails/short_i18n.rb +76 -0
- data/lib/rubocop/cop/rails/skips_model_validations.rb +46 -8
- data/lib/rubocop/cop/rails/time_zone.rb +1 -3
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +12 -12
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +28 -6
- data/lib/rubocop/cop/rails/unknown_env.rb +18 -6
- data/lib/rubocop/cop/rails/where_exists.rb +68 -0
- data/lib/rubocop/cop/rails_cops.rb +17 -0
- data/lib/rubocop/rails/schema_loader.rb +10 -10
- data/lib/rubocop/rails/schema_loader/schema.rb +48 -14
- data/lib/rubocop/rails/version.rb +1 -1
- metadata +27 -10
@@ -0,0 +1,69 @@
|
|
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
|
+
range = correction_range(node)
|
44
|
+
|
45
|
+
rest_args = node.arguments.drop(1)
|
46
|
+
replacement = "tag.#{node.first_argument.value}(#{rest_args.map(&:source).join(', ')})"
|
47
|
+
|
48
|
+
corrector.replace(range, replacement)
|
49
|
+
else
|
50
|
+
corrector.replace(node.loc.selector, 'tag')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def method_name?(node)
|
58
|
+
return false unless node.str_type? || node.sym_type?
|
59
|
+
|
60
|
+
/^[a-zA-Z_][a-zA-Z_0-9]*$/.match?(node.value)
|
61
|
+
end
|
62
|
+
|
63
|
+
def correction_range(node)
|
64
|
+
range_between(node.loc.selector.begin_pos, node.loc.expression.end_pos)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
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
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop looks for uses of `default_scope`.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# default_scope -> { where(hidden: false) }
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# scope :published, -> { where(hidden: false) }
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# def self.default_scope
|
17
|
+
# where(hidden: false)
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# def self.published
|
22
|
+
# where(hidden: false)
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
class DefaultScope < Cop
|
26
|
+
MSG = 'Avoid use of `default_scope`. It is better to use explicitly named scopes.'
|
27
|
+
|
28
|
+
def_node_matcher :method_call?, <<~PATTERN
|
29
|
+
(send nil? :default_scope ...)
|
30
|
+
PATTERN
|
31
|
+
|
32
|
+
def_node_matcher :class_method_definition?, <<~PATTERN
|
33
|
+
(defs _ :default_scope args ...)
|
34
|
+
PATTERN
|
35
|
+
|
36
|
+
def_node_matcher :eigenclass_method_definition?, <<~PATTERN
|
37
|
+
(sclass (self) $(def :default_scope args ...))
|
38
|
+
PATTERN
|
39
|
+
|
40
|
+
def on_send(node)
|
41
|
+
add_offense(node, location: :selector) if method_call?(node)
|
42
|
+
end
|
43
|
+
|
44
|
+
def on_defs(node)
|
45
|
+
add_offense(node, location: :name) if class_method_definition?(node)
|
46
|
+
end
|
47
|
+
|
48
|
+
def on_sclass(node)
|
49
|
+
eigenclass_method_definition?(node) { |default_scope| add_offense(default_scope, location: :name) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -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(', '))
|
@@ -124,7 +122,7 @@ module RuboCop
|
|
124
122
|
end
|
125
123
|
|
126
124
|
def private_or_protected_inline(line)
|
127
|
-
processed_source[line - 1].strip
|
125
|
+
processed_source[line - 1].strip.match?(/\A(private )|(protected )/)
|
128
126
|
end
|
129
127
|
end
|
130
128
|
end
|
@@ -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}: " }
|
@@ -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
|
#
|
@@ -48,6 +48,7 @@ module RuboCop
|
|
48
48
|
|
49
49
|
def on_dstr(node)
|
50
50
|
return unless rails_root_nodes?(node)
|
51
|
+
return unless node.children.last.str_type?
|
51
52
|
return unless node.children.last.source.start_with?('.') ||
|
52
53
|
node.children.last.source.include?(File::SEPARATOR)
|
53
54
|
|
@@ -89,7 +90,7 @@ module RuboCop
|
|
89
90
|
end
|
90
91
|
|
91
92
|
def string_with_slash?(node)
|
92
|
-
node.str_type? && node.source
|
93
|
+
node.str_type? && node.source.match?(%r{/})
|
93
94
|
end
|
94
95
|
|
95
96
|
def register_offense(node)
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop enforces that `ActiveRecord#find` is used instead of
|
7
|
+
# `where.take!`, `find_by!`, and `find_by_id!` to retrieve a single record
|
8
|
+
# by primary key when you expect it to be found.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# # bad
|
12
|
+
# User.where(id: id).take!
|
13
|
+
# User.find_by_id!(id)
|
14
|
+
# User.find_by!(id: id)
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# User.find(id)
|
18
|
+
#
|
19
|
+
class FindById < Cop
|
20
|
+
include RangeHelp
|
21
|
+
|
22
|
+
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
23
|
+
|
24
|
+
def_node_matcher :where_take?, <<~PATTERN
|
25
|
+
(send
|
26
|
+
$(send _ :where
|
27
|
+
(hash
|
28
|
+
(pair (sym :id) $_))) :take!)
|
29
|
+
PATTERN
|
30
|
+
|
31
|
+
def_node_matcher :find_by?, <<~PATTERN
|
32
|
+
{
|
33
|
+
(send _ :find_by_id! $_)
|
34
|
+
(send _ :find_by! (hash (pair (sym :id) $_)))
|
35
|
+
}
|
36
|
+
PATTERN
|
37
|
+
|
38
|
+
def on_send(node)
|
39
|
+
where_take?(node) do |where, id_value|
|
40
|
+
range = where_take_offense_range(node, where)
|
41
|
+
|
42
|
+
good_method = build_good_method(id_value)
|
43
|
+
bad_method = build_where_take_bad_method(id_value)
|
44
|
+
message = format(MSG, good_method: good_method, bad_method: bad_method)
|
45
|
+
|
46
|
+
add_offense(node, location: range, message: message)
|
47
|
+
end
|
48
|
+
|
49
|
+
find_by?(node) do |id_value|
|
50
|
+
range = find_by_offense_range(node)
|
51
|
+
|
52
|
+
good_method = build_good_method(id_value)
|
53
|
+
bad_method = build_find_by_bad_method(node, id_value)
|
54
|
+
message = format(MSG, good_method: good_method, bad_method: bad_method)
|
55
|
+
|
56
|
+
add_offense(node, location: range, message: message)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def autocorrect(node)
|
61
|
+
if (matches = where_take?(node))
|
62
|
+
where, id_value = *matches
|
63
|
+
range = where_take_offense_range(node, where)
|
64
|
+
elsif (id_value = find_by?(node))
|
65
|
+
range = find_by_offense_range(node)
|
66
|
+
end
|
67
|
+
|
68
|
+
lambda do |corrector|
|
69
|
+
replacement = build_good_method(id_value)
|
70
|
+
corrector.replace(range, replacement)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def where_take_offense_range(node, where)
|
77
|
+
range_between(where.loc.selector.begin_pos, node.loc.expression.end_pos)
|
78
|
+
end
|
79
|
+
|
80
|
+
def find_by_offense_range(node)
|
81
|
+
range_between(node.loc.selector.begin_pos, node.loc.expression.end_pos)
|
82
|
+
end
|
83
|
+
|
84
|
+
def build_good_method(id_value)
|
85
|
+
"find(#{id_value.source})"
|
86
|
+
end
|
87
|
+
|
88
|
+
def build_where_take_bad_method(id_value)
|
89
|
+
"where(id: #{id_value.source}).take!"
|
90
|
+
end
|
91
|
+
|
92
|
+
def build_find_by_bad_method(node, id_value)
|
93
|
+
case node.method_name
|
94
|
+
when :find_by_id!
|
95
|
+
"find_by_id!(#{id_value.source})"
|
96
|
+
when :find_by!
|
97
|
+
"find_by!(id: #{id_value.source})"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -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
|
@@ -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,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks that Active Support's `inquiry` method is not used.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad - String#inquiry
|
10
|
+
# ruby = 'two'.inquiry
|
11
|
+
# ruby.two?
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# ruby = 'two'
|
15
|
+
# ruby == 'two'
|
16
|
+
#
|
17
|
+
# # bad - Array#inquiry
|
18
|
+
# pets = %w(cat dog).inquiry
|
19
|
+
# pets.gopher?
|
20
|
+
#
|
21
|
+
# # good
|
22
|
+
# pets = %w(cat dog)
|
23
|
+
# pets.include? 'cat'
|
24
|
+
#
|
25
|
+
class Inquiry < Cop
|
26
|
+
MSG = "Prefer Ruby's comparison operators over Active Support's `inquiry`."
|
27
|
+
|
28
|
+
def on_send(node)
|
29
|
+
add_offense(node, location: :selector) if node.method?(:inquiry) && node.arguments.empty?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|