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.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.md +73 -0
  4. data/bin/setup +7 -0
  5. data/config/default.yml +466 -0
  6. data/lib/rubocop-rails.rb +12 -0
  7. data/lib/rubocop/cop/mixin/target_rails_version.rb +16 -0
  8. data/lib/rubocop/cop/rails/action_filter.rb +117 -0
  9. data/lib/rubocop/cop/rails/active_record_aliases.rb +48 -0
  10. data/lib/rubocop/cop/rails/active_record_override.rb +82 -0
  11. data/lib/rubocop/cop/rails/active_support_aliases.rb +69 -0
  12. data/lib/rubocop/cop/rails/application_job.rb +40 -0
  13. data/lib/rubocop/cop/rails/application_record.rb +40 -0
  14. data/lib/rubocop/cop/rails/assert_not.rb +44 -0
  15. data/lib/rubocop/cop/rails/belongs_to.rb +102 -0
  16. data/lib/rubocop/cop/rails/blank.rb +164 -0
  17. data/lib/rubocop/cop/rails/bulk_change_table.rb +289 -0
  18. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +91 -0
  19. data/lib/rubocop/cop/rails/date.rb +161 -0
  20. data/lib/rubocop/cop/rails/delegate.rb +132 -0
  21. data/lib/rubocop/cop/rails/delegate_allow_blank.rb +37 -0
  22. data/lib/rubocop/cop/rails/dynamic_find_by.rb +91 -0
  23. data/lib/rubocop/cop/rails/enum_uniqueness.rb +45 -0
  24. data/lib/rubocop/cop/rails/environment_comparison.rb +68 -0
  25. data/lib/rubocop/cop/rails/exit.rb +67 -0
  26. data/lib/rubocop/cop/rails/file_path.rb +108 -0
  27. data/lib/rubocop/cop/rails/find_by.rb +55 -0
  28. data/lib/rubocop/cop/rails/find_each.rb +51 -0
  29. data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +25 -0
  30. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +106 -0
  31. data/lib/rubocop/cop/rails/helper_instance_variable.rb +39 -0
  32. data/lib/rubocop/cop/rails/http_positional_arguments.rb +117 -0
  33. data/lib/rubocop/cop/rails/http_status.rb +160 -0
  34. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +94 -0
  35. data/lib/rubocop/cop/rails/inverse_of.rb +246 -0
  36. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +175 -0
  37. data/lib/rubocop/cop/rails/link_to_blank.rb +98 -0
  38. data/lib/rubocop/cop/rails/not_null_column.rb +67 -0
  39. data/lib/rubocop/cop/rails/output.rb +49 -0
  40. data/lib/rubocop/cop/rails/output_safety.rb +99 -0
  41. data/lib/rubocop/cop/rails/pluralization_grammar.rb +107 -0
  42. data/lib/rubocop/cop/rails/presence.rb +124 -0
  43. data/lib/rubocop/cop/rails/present.rb +153 -0
  44. data/lib/rubocop/cop/rails/read_write_attribute.rb +74 -0
  45. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +111 -0
  46. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +136 -0
  47. data/lib/rubocop/cop/rails/reflection_class_name.rb +37 -0
  48. data/lib/rubocop/cop/rails/refute_methods.rb +76 -0
  49. data/lib/rubocop/cop/rails/relative_date_constant.rb +93 -0
  50. data/lib/rubocop/cop/rails/request_referer.rb +56 -0
  51. data/lib/rubocop/cop/rails/reversible_migration.rb +286 -0
  52. data/lib/rubocop/cop/rails/safe_navigation.rb +87 -0
  53. data/lib/rubocop/cop/rails/save_bang.rb +316 -0
  54. data/lib/rubocop/cop/rails/scope_args.rb +29 -0
  55. data/lib/rubocop/cop/rails/skips_model_validations.rb +87 -0
  56. data/lib/rubocop/cop/rails/time_zone.rb +238 -0
  57. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +105 -0
  58. data/lib/rubocop/cop/rails/unknown_env.rb +63 -0
  59. data/lib/rubocop/cop/rails/validation.rb +109 -0
  60. data/lib/rubocop/cop/rails_cops.rb +64 -0
  61. data/lib/rubocop/rails.rb +12 -0
  62. data/lib/rubocop/rails/inject.rb +18 -0
  63. data/lib/rubocop/rails/version.rb +10 -0
  64. 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