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,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks for correct grammar when using ActiveSupport's
|
7
|
+
# core extensions to the numeric classes.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# 3.day.ago
|
12
|
+
# 1.months.ago
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# 3.days.ago
|
16
|
+
# 1.month.ago
|
17
|
+
class PluralizationGrammar < Cop
|
18
|
+
SINGULAR_DURATION_METHODS = { second: :seconds,
|
19
|
+
minute: :minutes,
|
20
|
+
hour: :hours,
|
21
|
+
day: :days,
|
22
|
+
week: :weeks,
|
23
|
+
fortnight: :fortnights,
|
24
|
+
month: :months,
|
25
|
+
year: :years }.freeze
|
26
|
+
|
27
|
+
PLURAL_DURATION_METHODS = SINGULAR_DURATION_METHODS.invert.freeze
|
28
|
+
|
29
|
+
MSG = 'Prefer `%<number>s.%<correct>s`.'
|
30
|
+
|
31
|
+
def on_send(node)
|
32
|
+
return unless duration_method?(node.method_name)
|
33
|
+
return unless literal_number?(node.receiver)
|
34
|
+
|
35
|
+
return unless offense?(node)
|
36
|
+
|
37
|
+
add_offense(node)
|
38
|
+
end
|
39
|
+
|
40
|
+
def autocorrect(node)
|
41
|
+
lambda do |corrector|
|
42
|
+
method_name = node.loc.selector.source
|
43
|
+
|
44
|
+
corrector.replace(node.loc.selector, correct_method(method_name))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def message(node)
|
51
|
+
number, = *node.receiver
|
52
|
+
|
53
|
+
format(MSG, number: number,
|
54
|
+
correct: correct_method(node.method_name.to_s))
|
55
|
+
end
|
56
|
+
|
57
|
+
def correct_method(method_name)
|
58
|
+
if plural_method?(method_name)
|
59
|
+
singularize(method_name)
|
60
|
+
else
|
61
|
+
pluralize(method_name)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def offense?(node)
|
66
|
+
number, = *node.receiver
|
67
|
+
|
68
|
+
singular_receiver?(number) && plural_method?(node.method_name) ||
|
69
|
+
plural_receiver?(number) && singular_method?(node.method_name)
|
70
|
+
end
|
71
|
+
|
72
|
+
def plural_method?(method_name)
|
73
|
+
method_name.to_s.end_with?('s')
|
74
|
+
end
|
75
|
+
|
76
|
+
def singular_method?(method_name)
|
77
|
+
!plural_method?(method_name)
|
78
|
+
end
|
79
|
+
|
80
|
+
def singular_receiver?(number)
|
81
|
+
number.abs == 1
|
82
|
+
end
|
83
|
+
|
84
|
+
def plural_receiver?(number)
|
85
|
+
!singular_receiver?(number)
|
86
|
+
end
|
87
|
+
|
88
|
+
def literal_number?(node)
|
89
|
+
node && (node.int_type? || node.float_type?)
|
90
|
+
end
|
91
|
+
|
92
|
+
def pluralize(method_name)
|
93
|
+
SINGULAR_DURATION_METHODS.fetch(method_name.to_sym).to_s
|
94
|
+
end
|
95
|
+
|
96
|
+
def singularize(method_name)
|
97
|
+
PLURAL_DURATION_METHODS.fetch(method_name.to_sym).to_s
|
98
|
+
end
|
99
|
+
|
100
|
+
def duration_method?(method_name)
|
101
|
+
SINGULAR_DURATION_METHODS.key?(method_name) ||
|
102
|
+
PLURAL_DURATION_METHODS.key?(method_name)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks code that can be written more easily using
|
7
|
+
# `Object#presence` defined by Active Support.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# a.present? ? a : nil
|
12
|
+
#
|
13
|
+
# # bad
|
14
|
+
# !a.present? ? nil : a
|
15
|
+
#
|
16
|
+
# # bad
|
17
|
+
# a.blank? ? nil : a
|
18
|
+
#
|
19
|
+
# # bad
|
20
|
+
# !a.blank? ? a : nil
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# a.presence
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# # bad
|
27
|
+
# a.present? ? a : b
|
28
|
+
#
|
29
|
+
# # bad
|
30
|
+
# !a.present? ? b : a
|
31
|
+
#
|
32
|
+
# # bad
|
33
|
+
# a.blank? ? b : a
|
34
|
+
#
|
35
|
+
# # bad
|
36
|
+
# !a.blank? ? a : b
|
37
|
+
#
|
38
|
+
# # good
|
39
|
+
# a.presence || b
|
40
|
+
class Presence < Cop
|
41
|
+
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
42
|
+
|
43
|
+
def_node_matcher :redundant_receiver_and_other, <<-PATTERN
|
44
|
+
{
|
45
|
+
(if
|
46
|
+
(send $_recv :present?)
|
47
|
+
_recv
|
48
|
+
$!begin
|
49
|
+
)
|
50
|
+
(if
|
51
|
+
(send $_recv :blank?)
|
52
|
+
$!begin
|
53
|
+
_recv
|
54
|
+
)
|
55
|
+
}
|
56
|
+
PATTERN
|
57
|
+
|
58
|
+
def_node_matcher :redundant_negative_receiver_and_other, <<-PATTERN
|
59
|
+
{
|
60
|
+
(if
|
61
|
+
(send (send $_recv :present?) :!)
|
62
|
+
$!begin
|
63
|
+
_recv
|
64
|
+
)
|
65
|
+
(if
|
66
|
+
(send (send $_recv :blank?) :!)
|
67
|
+
_recv
|
68
|
+
$!begin
|
69
|
+
)
|
70
|
+
}
|
71
|
+
PATTERN
|
72
|
+
|
73
|
+
def on_if(node)
|
74
|
+
return if ignore_if_node?(node)
|
75
|
+
|
76
|
+
redundant_receiver_and_other(node) do |receiver, other|
|
77
|
+
unless ignore_other_node?(other) || receiver.nil?
|
78
|
+
add_offense(node, message: message(node, receiver, other))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
redundant_negative_receiver_and_other(node) do |receiver, other|
|
83
|
+
unless ignore_other_node?(other) || receiver.nil?
|
84
|
+
add_offense(node, message: message(node, receiver, other))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def autocorrect(node)
|
90
|
+
lambda do |corrector|
|
91
|
+
redundant_receiver_and_other(node) do |receiver, other|
|
92
|
+
corrector.replace(node.source_range, replacement(receiver, other))
|
93
|
+
end
|
94
|
+
|
95
|
+
redundant_negative_receiver_and_other(node) do |receiver, other|
|
96
|
+
corrector.replace(node.source_range, replacement(receiver, other))
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def ignore_if_node?(node)
|
104
|
+
node.elsif?
|
105
|
+
end
|
106
|
+
|
107
|
+
def ignore_other_node?(node)
|
108
|
+
node && (node.if_type? || node.rescue_type? || node.while_type?)
|
109
|
+
end
|
110
|
+
|
111
|
+
def message(node, receiver, other)
|
112
|
+
format(MSG,
|
113
|
+
prefer: replacement(receiver, other),
|
114
|
+
current: node.source)
|
115
|
+
end
|
116
|
+
|
117
|
+
def replacement(receiver, other)
|
118
|
+
or_source = other.nil? || other.nil_type? ? '' : " || #{other.source}"
|
119
|
+
"#{receiver.source}.presence" + or_source
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks for code that can be written with simpler conditionals
|
7
|
+
# using `Object#present?` defined by Active Support.
|
8
|
+
#
|
9
|
+
# Interaction with `Style/UnlessElse`:
|
10
|
+
# The configuration of `NotBlank` will not produce an offense in the
|
11
|
+
# context of `unless else` if `Style/UnlessElse` is inabled. This is
|
12
|
+
# to prevent interference between the auto-correction of the two cops.
|
13
|
+
#
|
14
|
+
# @example NotNilAndNotEmpty: true (default)
|
15
|
+
# # Converts usages of `!nil? && !empty?` to `present?`
|
16
|
+
#
|
17
|
+
# # bad
|
18
|
+
# !foo.nil? && !foo.empty?
|
19
|
+
#
|
20
|
+
# # bad
|
21
|
+
# foo != nil && !foo.empty?
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# foo.present?
|
25
|
+
#
|
26
|
+
# @example NotBlank: true (default)
|
27
|
+
# # Converts usages of `!blank?` to `present?`
|
28
|
+
#
|
29
|
+
# # bad
|
30
|
+
# !foo.blank?
|
31
|
+
#
|
32
|
+
# # bad
|
33
|
+
# not foo.blank?
|
34
|
+
#
|
35
|
+
# # good
|
36
|
+
# foo.present?
|
37
|
+
#
|
38
|
+
# @example UnlessBlank: true (default)
|
39
|
+
# # Converts usages of `unless blank?` to `if present?`
|
40
|
+
#
|
41
|
+
# # bad
|
42
|
+
# something unless foo.blank?
|
43
|
+
#
|
44
|
+
# # good
|
45
|
+
# something if foo.present?
|
46
|
+
class Present < Cop
|
47
|
+
MSG_NOT_BLANK = 'Use `%<prefer>s` instead of `%<current>s`.'
|
48
|
+
MSG_EXISTS_AND_NOT_EMPTY = 'Use `%<prefer>s` instead of ' \
|
49
|
+
'`%<current>s`.'
|
50
|
+
MSG_UNLESS_BLANK = 'Use `if %<prefer>s` instead of ' \
|
51
|
+
'`%<current>s`.'
|
52
|
+
|
53
|
+
def_node_matcher :exists_and_not_empty?, <<-PATTERN
|
54
|
+
(and
|
55
|
+
{
|
56
|
+
(send (send $_ :nil?) :!)
|
57
|
+
(send (send $_ :!) :!)
|
58
|
+
(send $_ :!= nil)
|
59
|
+
$_
|
60
|
+
}
|
61
|
+
{
|
62
|
+
(send (send $_ :empty?) :!)
|
63
|
+
}
|
64
|
+
)
|
65
|
+
PATTERN
|
66
|
+
|
67
|
+
def_node_matcher :not_blank?, '(send (send $_ :blank?) :!)'
|
68
|
+
|
69
|
+
def_node_matcher :unless_blank?, <<-PATTERN
|
70
|
+
(:if $(send $_ :blank?) {nil? (...)} ...)
|
71
|
+
PATTERN
|
72
|
+
|
73
|
+
def on_send(node)
|
74
|
+
return unless cop_config['NotBlank']
|
75
|
+
|
76
|
+
not_blank?(node) do |receiver|
|
77
|
+
add_offense(node,
|
78
|
+
message: format(MSG_NOT_BLANK,
|
79
|
+
prefer: replacement(receiver),
|
80
|
+
current: node.source))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def on_and(node)
|
85
|
+
return unless cop_config['NotNilAndNotEmpty']
|
86
|
+
|
87
|
+
exists_and_not_empty?(node) do |var1, var2|
|
88
|
+
return unless var1 == var2
|
89
|
+
|
90
|
+
add_offense(node,
|
91
|
+
message: format(MSG_EXISTS_AND_NOT_EMPTY,
|
92
|
+
prefer: replacement(var1),
|
93
|
+
current: node.source))
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def on_or(node)
|
98
|
+
return unless cop_config['NilOrEmpty']
|
99
|
+
|
100
|
+
exists_and_not_empty?(node) do |var1, var2|
|
101
|
+
return unless var1 == var2
|
102
|
+
|
103
|
+
add_offense(node, message: MSG_EXISTS_AND_NOT_EMPTY)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def on_if(node)
|
108
|
+
return unless cop_config['UnlessBlank']
|
109
|
+
return unless node.unless?
|
110
|
+
return if node.else? && config.for_cop('Style/UnlessElse')['Enabled']
|
111
|
+
|
112
|
+
unless_blank?(node) do |method_call, receiver|
|
113
|
+
range = unless_condition(node, method_call)
|
114
|
+
msg = format(MSG_UNLESS_BLANK, prefer: replacement(receiver),
|
115
|
+
current: range.source)
|
116
|
+
add_offense(node, location: range, message: msg)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def autocorrect(node)
|
121
|
+
lambda do |corrector|
|
122
|
+
method_call, variable1 = unless_blank?(node)
|
123
|
+
|
124
|
+
if method_call
|
125
|
+
corrector.replace(node.loc.keyword, 'if')
|
126
|
+
range = method_call.loc.expression
|
127
|
+
else
|
128
|
+
variable1, _variable2 =
|
129
|
+
exists_and_not_empty?(node) || not_blank?(node)
|
130
|
+
range = node.loc.expression
|
131
|
+
end
|
132
|
+
|
133
|
+
corrector.replace(range, replacement(variable1))
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
def unless_condition(node, method_call)
|
140
|
+
if node.modifier_form?
|
141
|
+
node.loc.keyword.join(node.loc.expression.end)
|
142
|
+
else
|
143
|
+
node.loc.expression.begin.join(method_call.loc.expression)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def replacement(node)
|
148
|
+
node.respond_to?(:source) ? "#{node.source}.present?" : 'present?'
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks for the use of the `read_attribute` or `write_attribute`
|
7
|
+
# methods and recommends square brackets instead.
|
8
|
+
#
|
9
|
+
# If an attribute is missing from the instance (for example, when
|
10
|
+
# initialized by a partial `select`) then `read_attribute`
|
11
|
+
# will return nil, but square brackets will raise
|
12
|
+
# an `ActiveModel::MissingAttributeError`.
|
13
|
+
#
|
14
|
+
# Explicitly raising an error in this situation is preferable, and that
|
15
|
+
# is why rubocop recommends using square brackets.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
#
|
19
|
+
# # bad
|
20
|
+
# x = read_attribute(:attr)
|
21
|
+
# write_attribute(:attr, val)
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# x = self[:attr]
|
25
|
+
# self[:attr] = val
|
26
|
+
class ReadWriteAttribute < Cop
|
27
|
+
MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
|
28
|
+
|
29
|
+
def_node_matcher :read_write_attribute?, <<-PATTERN
|
30
|
+
{
|
31
|
+
(send nil? :read_attribute _)
|
32
|
+
(send nil? :write_attribute _ _)
|
33
|
+
}
|
34
|
+
PATTERN
|
35
|
+
|
36
|
+
def on_send(node)
|
37
|
+
return unless read_write_attribute?(node)
|
38
|
+
|
39
|
+
add_offense(node, location: :selector)
|
40
|
+
end
|
41
|
+
|
42
|
+
def autocorrect(node)
|
43
|
+
case node.method_name
|
44
|
+
when :read_attribute
|
45
|
+
replacement = read_attribute_replacement(node)
|
46
|
+
when :write_attribute
|
47
|
+
replacement = write_attribute_replacement(node)
|
48
|
+
end
|
49
|
+
|
50
|
+
->(corrector) { corrector.replace(node.source_range, replacement) }
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def message(node)
|
56
|
+
if node.method?(:read_attribute)
|
57
|
+
format(MSG, prefer: 'self[:attr]', current: 'read_attribute(:attr)')
|
58
|
+
else
|
59
|
+
format(MSG, prefer: 'self[:attr] = val',
|
60
|
+
current: 'write_attribute(:attr, val)')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def read_attribute_replacement(node)
|
65
|
+
"self[#{node.first_argument.source}]"
|
66
|
+
end
|
67
|
+
|
68
|
+
def write_attribute_replacement(node)
|
69
|
+
"self[#{node.first_argument.source}] = #{node.last_argument.source}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|