rubocop-rails 2.4.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.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.md +92 -0
  4. data/bin/setup +7 -0
  5. data/config/default.yml +510 -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 +111 -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_controller.rb +36 -0
  13. data/lib/rubocop/cop/rails/application_job.rb +40 -0
  14. data/lib/rubocop/cop/rails/application_mailer.rb +40 -0
  15. data/lib/rubocop/cop/rails/application_record.rb +40 -0
  16. data/lib/rubocop/cop/rails/assert_not.rb +44 -0
  17. data/lib/rubocop/cop/rails/belongs_to.rb +102 -0
  18. data/lib/rubocop/cop/rails/blank.rb +164 -0
  19. data/lib/rubocop/cop/rails/bulk_change_table.rb +293 -0
  20. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +91 -0
  21. data/lib/rubocop/cop/rails/date.rb +161 -0
  22. data/lib/rubocop/cop/rails/delegate.rb +132 -0
  23. data/lib/rubocop/cop/rails/delegate_allow_blank.rb +37 -0
  24. data/lib/rubocop/cop/rails/dynamic_find_by.rb +91 -0
  25. data/lib/rubocop/cop/rails/enum_hash.rb +75 -0
  26. data/lib/rubocop/cop/rails/enum_uniqueness.rb +65 -0
  27. data/lib/rubocop/cop/rails/environment_comparison.rb +68 -0
  28. data/lib/rubocop/cop/rails/exit.rb +67 -0
  29. data/lib/rubocop/cop/rails/file_path.rb +108 -0
  30. data/lib/rubocop/cop/rails/find_by.rb +55 -0
  31. data/lib/rubocop/cop/rails/find_each.rb +51 -0
  32. data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +25 -0
  33. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +106 -0
  34. data/lib/rubocop/cop/rails/helper_instance_variable.rb +39 -0
  35. data/lib/rubocop/cop/rails/http_positional_arguments.rb +117 -0
  36. data/lib/rubocop/cop/rails/http_status.rb +160 -0
  37. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +94 -0
  38. data/lib/rubocop/cop/rails/inverse_of.rb +246 -0
  39. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +175 -0
  40. data/lib/rubocop/cop/rails/link_to_blank.rb +98 -0
  41. data/lib/rubocop/cop/rails/not_null_column.rb +67 -0
  42. data/lib/rubocop/cop/rails/output.rb +49 -0
  43. data/lib/rubocop/cop/rails/output_safety.rb +99 -0
  44. data/lib/rubocop/cop/rails/pluralization_grammar.rb +107 -0
  45. data/lib/rubocop/cop/rails/presence.rb +148 -0
  46. data/lib/rubocop/cop/rails/present.rb +153 -0
  47. data/lib/rubocop/cop/rails/rake_environment.rb +91 -0
  48. data/lib/rubocop/cop/rails/read_write_attribute.rb +74 -0
  49. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +111 -0
  50. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +136 -0
  51. data/lib/rubocop/cop/rails/reflection_class_name.rb +37 -0
  52. data/lib/rubocop/cop/rails/refute_methods.rb +76 -0
  53. data/lib/rubocop/cop/rails/relative_date_constant.rb +102 -0
  54. data/lib/rubocop/cop/rails/request_referer.rb +56 -0
  55. data/lib/rubocop/cop/rails/reversible_migration.rb +284 -0
  56. data/lib/rubocop/cop/rails/safe_navigation.rb +85 -0
  57. data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +48 -0
  58. data/lib/rubocop/cop/rails/save_bang.rb +331 -0
  59. data/lib/rubocop/cop/rails/scope_args.rb +29 -0
  60. data/lib/rubocop/cop/rails/skips_model_validations.rb +87 -0
  61. data/lib/rubocop/cop/rails/time_zone.rb +249 -0
  62. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +105 -0
  63. data/lib/rubocop/cop/rails/unknown_env.rb +84 -0
  64. data/lib/rubocop/cop/rails/validation.rb +147 -0
  65. data/lib/rubocop/cop/rails_cops.rb +61 -0
  66. data/lib/rubocop/rails.rb +12 -0
  67. data/lib/rubocop/rails/inject.rb +18 -0
  68. data/lib/rubocop/rails/version.rb +10 -0
  69. metadata +148 -0
@@ -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,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks for Rake tasks without the `:environment` task
7
+ # dependency. The `:environment` task loads application code for other
8
+ # Rake tasks. Without it, tasks cannot make use of application code like
9
+ # models.
10
+ #
11
+ # You can ignore the offense if the task satisfies at least one of the
12
+ # following conditions:
13
+ #
14
+ # * The task does not need application code.
15
+ # * The task invokes the `:environment` task.
16
+ #
17
+ # @example
18
+ # # bad
19
+ # task :foo do
20
+ # do_something
21
+ # end
22
+ #
23
+ # # good
24
+ # task foo: :environment do
25
+ # do_something
26
+ # end
27
+ #
28
+ class RakeEnvironment < Cop
29
+ MSG = 'Include `:environment` task as a dependency for all Rake tasks.'
30
+
31
+ def_node_matcher :task_definition?, <<~PATTERN
32
+ (send nil? :task ...)
33
+ PATTERN
34
+
35
+ def on_send(node)
36
+ return unless task_definition?(node)
37
+ return if task_name(node) == :default
38
+ return if with_dependencies?(node)
39
+
40
+ add_offense(node)
41
+ end
42
+
43
+ private
44
+
45
+ def task_name(node)
46
+ first_arg = node.arguments[0]
47
+ case first_arg&.type
48
+ when :sym, :str
49
+ first_arg.value.to_sym
50
+ when :hash
51
+ return nil if first_arg.children.size != 1
52
+
53
+ pair = first_arg.children.first
54
+ key = pair.children.first
55
+ case key.type
56
+ when :sym, :str
57
+ key.value.to_sym
58
+ end
59
+ end
60
+ end
61
+
62
+ def with_dependencies?(node)
63
+ first_arg = node.arguments[0]
64
+ return false unless first_arg
65
+
66
+ if first_arg.hash_type?
67
+ with_hash_style_dependencies?(first_arg)
68
+ else
69
+ task_args = node.arguments[1]
70
+ return false unless task_args
71
+ return false unless task_args.hash_type?
72
+
73
+ with_hash_style_dependencies?(task_args)
74
+ end
75
+ end
76
+
77
+ def with_hash_style_dependencies?(hash_node)
78
+ deps = hash_node.pairs.first&.value
79
+ return false unless deps
80
+
81
+ case deps.type
82
+ when :array
83
+ !deps.values.empty?
84
+ else
85
+ true
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ 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
@@ -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?(: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