rubocop-rails 2.0.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 +20 -0
- data/README.md +73 -0
- data/bin/setup +7 -0
- data/config/default.yml +466 -0
- data/lib/rubocop-rails.rb +12 -0
- data/lib/rubocop/cop/mixin/target_rails_version.rb +16 -0
- data/lib/rubocop/cop/rails/action_filter.rb +117 -0
- data/lib/rubocop/cop/rails/active_record_aliases.rb +48 -0
- data/lib/rubocop/cop/rails/active_record_override.rb +82 -0
- data/lib/rubocop/cop/rails/active_support_aliases.rb +69 -0
- data/lib/rubocop/cop/rails/application_job.rb +40 -0
- data/lib/rubocop/cop/rails/application_record.rb +40 -0
- data/lib/rubocop/cop/rails/assert_not.rb +44 -0
- data/lib/rubocop/cop/rails/belongs_to.rb +102 -0
- data/lib/rubocop/cop/rails/blank.rb +164 -0
- data/lib/rubocop/cop/rails/bulk_change_table.rb +289 -0
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +91 -0
- data/lib/rubocop/cop/rails/date.rb +161 -0
- data/lib/rubocop/cop/rails/delegate.rb +132 -0
- data/lib/rubocop/cop/rails/delegate_allow_blank.rb +37 -0
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +91 -0
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +45 -0
- data/lib/rubocop/cop/rails/environment_comparison.rb +68 -0
- data/lib/rubocop/cop/rails/exit.rb +67 -0
- data/lib/rubocop/cop/rails/file_path.rb +108 -0
- data/lib/rubocop/cop/rails/find_by.rb +55 -0
- data/lib/rubocop/cop/rails/find_each.rb +51 -0
- data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +25 -0
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +106 -0
- data/lib/rubocop/cop/rails/helper_instance_variable.rb +39 -0
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +117 -0
- data/lib/rubocop/cop/rails/http_status.rb +160 -0
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +94 -0
- data/lib/rubocop/cop/rails/inverse_of.rb +246 -0
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +175 -0
- data/lib/rubocop/cop/rails/link_to_blank.rb +98 -0
- data/lib/rubocop/cop/rails/not_null_column.rb +67 -0
- data/lib/rubocop/cop/rails/output.rb +49 -0
- data/lib/rubocop/cop/rails/output_safety.rb +99 -0
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +107 -0
- data/lib/rubocop/cop/rails/presence.rb +124 -0
- data/lib/rubocop/cop/rails/present.rb +153 -0
- data/lib/rubocop/cop/rails/read_write_attribute.rb +74 -0
- data/lib/rubocop/cop/rails/redundant_allow_nil.rb +111 -0
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +136 -0
- data/lib/rubocop/cop/rails/reflection_class_name.rb +37 -0
- data/lib/rubocop/cop/rails/refute_methods.rb +76 -0
- data/lib/rubocop/cop/rails/relative_date_constant.rb +93 -0
- data/lib/rubocop/cop/rails/request_referer.rb +56 -0
- data/lib/rubocop/cop/rails/reversible_migration.rb +286 -0
- data/lib/rubocop/cop/rails/safe_navigation.rb +87 -0
- data/lib/rubocop/cop/rails/save_bang.rb +316 -0
- data/lib/rubocop/cop/rails/scope_args.rb +29 -0
- data/lib/rubocop/cop/rails/skips_model_validations.rb +87 -0
- data/lib/rubocop/cop/rails/time_zone.rb +238 -0
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +105 -0
- data/lib/rubocop/cop/rails/unknown_env.rb +63 -0
- data/lib/rubocop/cop/rails/validation.rb +109 -0
- data/lib/rubocop/cop/rails_cops.rb +64 -0
- data/lib/rubocop/rails.rb +12 -0
- data/lib/rubocop/rails/inject.rb +18 -0
- data/lib/rubocop/rails/version.rb +10 -0
- metadata +143 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Checks Rails model validations for a redundant `allow_nil` when
|
7
|
+
# `allow_blank` is present.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# validates :x, length: { is: 5 }, allow_nil: true, allow_blank: true
|
12
|
+
#
|
13
|
+
# # bad
|
14
|
+
# validates :x, length: { is: 5 }, allow_nil: false, allow_blank: true
|
15
|
+
#
|
16
|
+
# # bad
|
17
|
+
# validates :x, length: { is: 5 }, allow_nil: false, allow_blank: false
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# validates :x, length: { is: 5 }, allow_blank: true
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# validates :x, length: { is: 5 }, allow_blank: false
|
24
|
+
#
|
25
|
+
# # good
|
26
|
+
# # Here, `nil` is valid but `''` is not
|
27
|
+
# validates :x, length: { is: 5 }, allow_nil: true, allow_blank: false
|
28
|
+
#
|
29
|
+
class RedundantAllowNil < Cop
|
30
|
+
include RangeHelp
|
31
|
+
|
32
|
+
MSG_SAME =
|
33
|
+
'`allow_nil` is redundant when `allow_blank` has the same value.'
|
34
|
+
|
35
|
+
MSG_ALLOW_NIL_FALSE =
|
36
|
+
'`allow_nil: false` is redundant when `allow_blank` is true.'
|
37
|
+
|
38
|
+
def on_send(node)
|
39
|
+
return unless node.method_name == :validates
|
40
|
+
|
41
|
+
allow_nil, allow_blank = find_allow_nil_and_allow_blank(node)
|
42
|
+
return unless allow_nil && allow_blank
|
43
|
+
|
44
|
+
allow_nil_val = allow_nil.children.last
|
45
|
+
allow_blank_val = allow_blank.children.last
|
46
|
+
|
47
|
+
offense(allow_nil_val, allow_blank_val, allow_nil)
|
48
|
+
end
|
49
|
+
|
50
|
+
def autocorrect(node)
|
51
|
+
prv_sib = previous_sibling(node)
|
52
|
+
nxt_sib = next_sibling(node)
|
53
|
+
|
54
|
+
lambda do |corrector|
|
55
|
+
if nxt_sib
|
56
|
+
corrector.remove(range_between(node_beg(node), node_beg(nxt_sib)))
|
57
|
+
elsif prv_sib
|
58
|
+
corrector.remove(range_between(node_end(prv_sib), node_end(node)))
|
59
|
+
else
|
60
|
+
corrector.remove(node.loc.expression)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def offense(allow_nil_val, allow_blank_val, allow_nil)
|
68
|
+
if allow_nil_val.type == allow_blank_val.type
|
69
|
+
add_offense(allow_nil, message: MSG_SAME)
|
70
|
+
elsif allow_nil_val.false_type? && allow_blank_val.true_type?
|
71
|
+
add_offense(allow_nil, message: MSG_ALLOW_NIL_FALSE)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def find_allow_nil_and_allow_blank(node)
|
76
|
+
allow_nil = nil
|
77
|
+
allow_blank = nil
|
78
|
+
|
79
|
+
node.each_descendant do |descendant|
|
80
|
+
next unless descendant.pair_type?
|
81
|
+
|
82
|
+
key = descendant.children.first.source
|
83
|
+
|
84
|
+
allow_nil = descendant if key == 'allow_nil'
|
85
|
+
allow_blank = descendant if key == 'allow_blank'
|
86
|
+
|
87
|
+
break if allow_nil && allow_blank
|
88
|
+
end
|
89
|
+
|
90
|
+
[allow_nil, allow_blank]
|
91
|
+
end
|
92
|
+
|
93
|
+
def previous_sibling(node)
|
94
|
+
node.parent.children[node.sibling_index - 1]
|
95
|
+
end
|
96
|
+
|
97
|
+
def next_sibling(node)
|
98
|
+
node.parent.children[node.sibling_index + 1]
|
99
|
+
end
|
100
|
+
|
101
|
+
def node_beg(node)
|
102
|
+
node.loc.expression.begin_pos
|
103
|
+
end
|
104
|
+
|
105
|
+
def node_end(node)
|
106
|
+
node.loc.expression.end_pos
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks for redundant receiver in `with_options`.
|
7
|
+
# Receiver is implicit from Rails 4.2 or higher.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# class Account < ApplicationRecord
|
12
|
+
# with_options dependent: :destroy do |assoc|
|
13
|
+
# assoc.has_many :customers
|
14
|
+
# assoc.has_many :products
|
15
|
+
# assoc.has_many :invoices
|
16
|
+
# assoc.has_many :expenses
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# class Account < ApplicationRecord
|
22
|
+
# with_options dependent: :destroy do
|
23
|
+
# has_many :customers
|
24
|
+
# has_many :products
|
25
|
+
# has_many :invoices
|
26
|
+
# has_many :expenses
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# # bad
|
32
|
+
# with_options options: false do |merger|
|
33
|
+
# merger.invoke(merger.something)
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# # good
|
37
|
+
# with_options options: false do
|
38
|
+
# invoke(something)
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# # good
|
42
|
+
# client = Client.new
|
43
|
+
# with_options options: false do |merger|
|
44
|
+
# client.invoke(merger.something, something)
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# # ok
|
48
|
+
# # When `with_options` includes a block, all scoping scenarios
|
49
|
+
# # cannot be evaluated. Thus, it is ok to include the explicit
|
50
|
+
# # receiver.
|
51
|
+
# with_options options: false do |merger|
|
52
|
+
# merger.invoke
|
53
|
+
# with_another_method do |another_receiver|
|
54
|
+
# merger.invoke(another_receiver)
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
class RedundantReceiverInWithOptions < Cop
|
58
|
+
extend TargetRailsVersion
|
59
|
+
include RangeHelp
|
60
|
+
|
61
|
+
minimum_target_rails_version 4.2
|
62
|
+
|
63
|
+
MSG = 'Redundant receiver in `with_options`.'
|
64
|
+
|
65
|
+
def_node_matcher :with_options?, <<-PATTERN
|
66
|
+
(block
|
67
|
+
(send nil? :with_options
|
68
|
+
(...))
|
69
|
+
(args
|
70
|
+
$_arg)
|
71
|
+
$_body)
|
72
|
+
PATTERN
|
73
|
+
|
74
|
+
def_node_search :all_block_nodes_in, <<-PATTERN
|
75
|
+
(block ...)
|
76
|
+
PATTERN
|
77
|
+
|
78
|
+
def_node_search :all_send_nodes_in, <<-PATTERN
|
79
|
+
(send ...)
|
80
|
+
PATTERN
|
81
|
+
|
82
|
+
def on_block(node)
|
83
|
+
with_options?(node) do |arg, body|
|
84
|
+
return if body.nil?
|
85
|
+
return unless all_block_nodes_in(body).count.zero?
|
86
|
+
|
87
|
+
send_nodes = all_send_nodes_in(body)
|
88
|
+
|
89
|
+
if send_nodes.all? { |n| same_value?(arg, n.receiver) }
|
90
|
+
send_nodes.each do |send_node|
|
91
|
+
receiver = send_node.receiver
|
92
|
+
add_offense(send_node, location: receiver.source_range)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def autocorrect(node)
|
99
|
+
lambda do |corrector|
|
100
|
+
corrector.remove(node.receiver.source_range)
|
101
|
+
corrector.remove(node.loc.dot)
|
102
|
+
corrector.remove(block_argument_range(node))
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def block_argument_range(node)
|
109
|
+
block_node = node.each_ancestor(:block).first
|
110
|
+
block_argument = block_node.children[1].source_range
|
111
|
+
|
112
|
+
range_between(
|
113
|
+
search_begin_pos_of_space_before_block_argument(
|
114
|
+
block_argument.begin_pos
|
115
|
+
),
|
116
|
+
block_argument.end_pos
|
117
|
+
)
|
118
|
+
end
|
119
|
+
|
120
|
+
def search_begin_pos_of_space_before_block_argument(begin_pos)
|
121
|
+
position = begin_pos - 1
|
122
|
+
|
123
|
+
if processed_source.raw_source[position] == ' '
|
124
|
+
search_begin_pos_of_space_before_block_argument(position)
|
125
|
+
else
|
126
|
+
begin_pos
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def same_value?(arg_node, recv_node)
|
131
|
+
recv_node && recv_node.children[0] == arg_node.children[0]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks if the value of the option `class_name`, in
|
7
|
+
# the definition of a reflection is a string.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# has_many :accounts, class_name: Account
|
12
|
+
# has_many :accounts, class_name: Account.name
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# has_many :accounts, class_name: 'Account'
|
16
|
+
class ReflectionClassName < Cop
|
17
|
+
MSG = 'Use a string value for `class_name`.'
|
18
|
+
|
19
|
+
def_node_matcher :association_with_reflection, <<-PATTERN
|
20
|
+
(send nil? {:has_many :has_one :belongs_to} _
|
21
|
+
(hash <$#reflection_class_name ...>)
|
22
|
+
)
|
23
|
+
PATTERN
|
24
|
+
|
25
|
+
def_node_matcher :reflection_class_name, <<-PATTERN
|
26
|
+
(pair (sym :class_name) [!dstr !str !sym])
|
27
|
+
PATTERN
|
28
|
+
|
29
|
+
def on_send(node)
|
30
|
+
association_with_reflection(node) do |reflection_class_name|
|
31
|
+
add_offense(node, location: reflection_class_name.loc.expression)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
#
|
7
|
+
# Use `assert_not` methods instead of `refute` methods.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# refute false
|
12
|
+
# refute_empty [1, 2, 3]
|
13
|
+
# refute_equal true, false
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# assert_not false
|
17
|
+
# assert_not_empty [1, 2, 3]
|
18
|
+
# assert_not_equal true, false
|
19
|
+
#
|
20
|
+
class RefuteMethods < Cop
|
21
|
+
MSG = 'Prefer `%<assert_method>s` over `%<refute_method>s`.'
|
22
|
+
|
23
|
+
CORRECTIONS = {
|
24
|
+
refute: 'assert_not',
|
25
|
+
refute_empty: 'assert_not_empty',
|
26
|
+
refute_equal: 'assert_not_equal',
|
27
|
+
refute_in_delta: 'assert_not_in_delta',
|
28
|
+
refute_in_epsilon: 'assert_not_in_epsilon',
|
29
|
+
refute_includes: 'assert_not_includes',
|
30
|
+
refute_instance_of: 'assert_not_instance_of',
|
31
|
+
refute_kind_of: 'assert_not_kind_of',
|
32
|
+
refute_nil: 'assert_not_nil',
|
33
|
+
refute_operator: 'assert_not_operator',
|
34
|
+
refute_predicate: 'assert_not_predicate',
|
35
|
+
refute_respond_to: 'assert_not_respond_to',
|
36
|
+
refute_same: 'assert_not_same',
|
37
|
+
refute_match: 'assert_no_match'
|
38
|
+
}.freeze
|
39
|
+
|
40
|
+
OFFENSIVE_METHODS = CORRECTIONS.keys.freeze
|
41
|
+
|
42
|
+
def_node_matcher :offensive?, '(send nil? #refute_method? ...)'
|
43
|
+
|
44
|
+
def on_send(node)
|
45
|
+
return unless offensive?(node)
|
46
|
+
|
47
|
+
message = offense_message(node.method_name)
|
48
|
+
add_offense(node, location: :selector, message: message)
|
49
|
+
end
|
50
|
+
|
51
|
+
def autocorrect(node)
|
52
|
+
lambda do |corrector|
|
53
|
+
corrector.replace(
|
54
|
+
node.loc.selector,
|
55
|
+
CORRECTIONS[node.method_name]
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def refute_method?(method_name)
|
63
|
+
OFFENSIVE_METHODS.include?(method_name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def offense_message(method_name)
|
67
|
+
format(
|
68
|
+
MSG,
|
69
|
+
refute_method: method_name,
|
70
|
+
assert_method: CORRECTIONS[method_name]
|
71
|
+
)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks whether constant value isn't relative date.
|
7
|
+
# Because the relative date will be evaluated only once.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# class SomeClass
|
12
|
+
# EXPIRED_AT = 1.week.since
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# class SomeClass
|
17
|
+
# def self.expired_at
|
18
|
+
# 1.week.since
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
class RelativeDateConstant < Cop
|
22
|
+
include RangeHelp
|
23
|
+
|
24
|
+
MSG = 'Do not assign %<method_name>s to constants as it ' \
|
25
|
+
'will be evaluated only once.'
|
26
|
+
|
27
|
+
def on_casgn(node)
|
28
|
+
relative_date_assignment?(node) do |method_name|
|
29
|
+
add_offense(node, message: format(MSG, method_name: method_name))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_masgn(node)
|
34
|
+
lhs, rhs = *node
|
35
|
+
|
36
|
+
return unless rhs&.array_type?
|
37
|
+
|
38
|
+
lhs.children.zip(rhs.children).each do |(name, value)|
|
39
|
+
next unless name.casgn_type?
|
40
|
+
|
41
|
+
relative_date?(value) do |method_name|
|
42
|
+
add_offense(node,
|
43
|
+
location: range_between(name.loc.expression.begin_pos,
|
44
|
+
value.loc.expression.end_pos),
|
45
|
+
message: format(MSG, method_name: method_name))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def on_or_asgn(node)
|
51
|
+
relative_date_or_assignment?(node) do |method_name|
|
52
|
+
add_offense(node, message: format(MSG, method_name: method_name))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def autocorrect(node)
|
57
|
+
return unless node.casgn_type?
|
58
|
+
|
59
|
+
scope, const_name, value = *node
|
60
|
+
return unless scope.nil?
|
61
|
+
|
62
|
+
indent = ' ' * node.loc.column
|
63
|
+
new_code = ["def self.#{const_name.downcase}",
|
64
|
+
"#{indent}#{value.source}",
|
65
|
+
'end'].join("\n#{indent}")
|
66
|
+
->(corrector) { corrector.replace(node.source_range, new_code) }
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def_node_matcher :relative_date_assignment?, <<-PATTERN
|
72
|
+
{
|
73
|
+
(casgn _ _ (send _ ${:since :from_now :after :ago :until :before}))
|
74
|
+
(casgn _ _ ({erange irange} _ (send _ ${:since :from_now :after :ago :until :before})))
|
75
|
+
(casgn _ _ ({erange irange} (send _ ${:since :from_now :after :ago :until :before}) _))
|
76
|
+
}
|
77
|
+
PATTERN
|
78
|
+
|
79
|
+
def_node_matcher :relative_date_or_assignment?, <<-PATTERN
|
80
|
+
(:or_asgn (casgn _ _) (send _ ${:since :from_now :after :ago :until :before}))
|
81
|
+
PATTERN
|
82
|
+
|
83
|
+
def_node_matcher :relative_date?, <<-PATTERN
|
84
|
+
{
|
85
|
+
({erange irange} _ (send _ ${:since :from_now :after :ago :until :before}))
|
86
|
+
({erange irange} (send _ ${:since :from_now :after :ago :until :before}) _)
|
87
|
+
(send _ ${:since :from_now :after :ago :until :before})
|
88
|
+
}
|
89
|
+
PATTERN
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|