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,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