rubocop-rails 2.4.1

Sign up to get free protection for your applications and to get access to all the features.
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