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,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop looks for enums written with array syntax.
7
+ #
8
+ # When using array syntax, adding an element in a
9
+ # position other than the last causes all previous
10
+ # definitions to shift. Explicitly specifying the
11
+ # value for each key prevents this from happening.
12
+ #
13
+ # @example
14
+ # # bad
15
+ # enum status: [:active, :archived]
16
+ #
17
+ # # good
18
+ # enum status: { active: 0, archived: 1 }
19
+ #
20
+ class EnumHash < Cop
21
+ MSG = 'Enum defined as an array found in `%<enum>s` enum declaration. '\
22
+ 'Use hash syntax instead.'
23
+
24
+ def_node_matcher :enum?, <<~PATTERN
25
+ (send nil? :enum (hash $...))
26
+ PATTERN
27
+
28
+ def_node_matcher :array_pair?, <<~PATTERN
29
+ (pair $_ $array)
30
+ PATTERN
31
+
32
+ def on_send(node)
33
+ enum?(node) do |pairs|
34
+ pairs.each do |pair|
35
+ key, array = array_pair?(pair)
36
+ next unless key
37
+
38
+ add_offense(array, message: format(MSG, enum: enum_name(key)))
39
+ end
40
+ end
41
+ end
42
+
43
+ def autocorrect(node)
44
+ hash = node.children.each_with_index.map do |elem, index|
45
+ "#{source(elem)} => #{index}"
46
+ end.join(', ')
47
+
48
+ ->(corrector) { corrector.replace(node.loc.expression, "{#{hash}}") }
49
+ end
50
+
51
+ private
52
+
53
+ def enum_name(key)
54
+ case key.type
55
+ when :sym, :str
56
+ key.value
57
+ else
58
+ key.source
59
+ end
60
+ end
61
+
62
+ def source(elem)
63
+ case elem.type
64
+ when :str
65
+ elem.value.dump
66
+ when :sym
67
+ elem.value.inspect
68
+ else
69
+ elem.source
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop looks for duplicate values in enum declarations.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # enum status: { active: 0, archived: 0 }
11
+ #
12
+ # # good
13
+ # enum status: { active: 0, archived: 1 }
14
+ #
15
+ # # bad
16
+ # enum status: [:active, :archived, :active]
17
+ #
18
+ # # good
19
+ # enum status: [:active, :archived]
20
+ class EnumUniqueness < Cop
21
+ include Duplication
22
+
23
+ MSG = 'Duplicate value `%<value>s` found in `%<enum>s` ' \
24
+ 'enum declaration.'
25
+
26
+ def_node_matcher :enum?, <<~PATTERN
27
+ (send nil? :enum (hash $...))
28
+ PATTERN
29
+
30
+ def_node_matcher :enum_values, <<~PATTERN
31
+ (pair $_ ${array hash})
32
+ PATTERN
33
+
34
+ def on_send(node)
35
+ enum?(node) do |pairs|
36
+ pairs.each do |pair|
37
+ enum_values(pair) do |key, args|
38
+ items = args.values
39
+
40
+ next unless duplicates?(items)
41
+
42
+ consecutive_duplicates(items).each do |item|
43
+ add_offense(item, message: format(
44
+ MSG, value: item.source, enum: enum_name(key)
45
+ ))
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def enum_name(key)
55
+ case key.type
56
+ when :sym, :str
57
+ key.value
58
+ else
59
+ key.source
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks that Rails.env is compared using `.production?`-like
7
+ # methods instead of equality against a string or symbol.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # Rails.env == 'production'
12
+ #
13
+ # # bad, always returns false
14
+ # Rails.env == :test
15
+ #
16
+ # # good
17
+ # Rails.env.production?
18
+ class EnvironmentComparison < Cop
19
+ MSG = "Favor `Rails.env.%<env>s?` over `Rails.env == '%<env>s'`."
20
+
21
+ SYM_MSG = 'Do not compare `Rails.env` with a symbol, it will always ' \
22
+ 'evaluate to `false`.'
23
+
24
+ def_node_matcher :environment_str_comparison?, <<~PATTERN
25
+ (send
26
+ (send (const {nil? cbase} :Rails) :env)
27
+ :==
28
+ $str
29
+ )
30
+ PATTERN
31
+
32
+ def_node_matcher :environment_sym_comparison?, <<~PATTERN
33
+ (send
34
+ (send (const {nil? cbase} :Rails) :env)
35
+ :==
36
+ $sym
37
+ )
38
+ PATTERN
39
+
40
+ def on_send(node)
41
+ environment_str_comparison?(node) do |env_node|
42
+ env, = *env_node
43
+ add_offense(node, message: format(MSG, env: env))
44
+ end
45
+ environment_sym_comparison?(node) do |_|
46
+ add_offense(node, message: SYM_MSG)
47
+ end
48
+ end
49
+
50
+ def autocorrect(node)
51
+ lambda do |corrector|
52
+ corrector.replace(node.source_range, replacement(node))
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def replacement(node)
59
+ "#{node.receiver.source}.#{content(node.first_argument)}?"
60
+ end
61
+
62
+ def_node_matcher :content, <<~PATTERN
63
+ ({str sym} $_)
64
+ PATTERN
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop enforces that `exit` calls are not used within a rails app.
7
+ # Valid options are instead to raise an error, break, return, or some
8
+ # other form of stopping execution of current request.
9
+ #
10
+ # There are two obvious cases where `exit` is particularly harmful:
11
+ #
12
+ # - Usage in library code for your application. Even though Rails will
13
+ # rescue from a `SystemExit` and continue on, unit testing that library
14
+ # code will result in specs exiting (potentially silently if `exit(0)`
15
+ # is used.)
16
+ # - Usage in application code outside of the web process could result in
17
+ # the program exiting, which could result in the code failing to run and
18
+ # do its job.
19
+ #
20
+ # @example
21
+ # # bad
22
+ # exit(0)
23
+ #
24
+ # # good
25
+ # raise 'a bad error has happened'
26
+ class Exit < Cop
27
+ include ConfigurableEnforcedStyle
28
+
29
+ MSG = 'Do not use `exit` in Rails applications.'
30
+ TARGET_METHODS = %i[exit exit!].freeze
31
+ EXPLICIT_RECEIVERS = %i[Kernel Process].freeze
32
+
33
+ def on_send(node)
34
+ add_offense(node, location: :selector) if offending_node?(node)
35
+ end
36
+
37
+ private
38
+
39
+ def offending_node?(node)
40
+ right_method_name?(node.method_name) &&
41
+ right_argument_count?(node.arguments) &&
42
+ right_receiver?(node.receiver)
43
+ end
44
+
45
+ def right_method_name?(method_name)
46
+ TARGET_METHODS.include?(method_name)
47
+ end
48
+
49
+ # More than 1 argument likely means it is a different
50
+ # `exit` implementation than the one we are preventing.
51
+ def right_argument_count?(arg_nodes)
52
+ arg_nodes.size <= 1
53
+ end
54
+
55
+ # Only register if exit is being called explicitly on `Kernel`,
56
+ # `Process`, or if receiver node is nil for plain `exit` calls.
57
+ def right_receiver?(receiver_node)
58
+ return true unless receiver_node
59
+
60
+ _a, receiver_node_class, _c = *receiver_node
61
+
62
+ EXPLICIT_RECEIVERS.include?(receiver_node_class)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop is used to identify usages of file path joining process
7
+ # to use `Rails.root.join` clause. It is used to add uniformity when
8
+ # joining paths.
9
+ #
10
+ # @example EnforcedStyle: arguments
11
+ # # bad
12
+ # Rails.root.join('app/models/goober')
13
+ # File.join(Rails.root, 'app/models/goober')
14
+ # "#{Rails.root}/app/models/goober"
15
+ #
16
+ # # good
17
+ # Rails.root.join('app', 'models', 'goober')
18
+ #
19
+ # @example EnforcedStyle: slashes (default)
20
+ # # bad
21
+ # Rails.root.join('app', 'models', 'goober')
22
+ # File.join(Rails.root, 'app/models/goober')
23
+ # "#{Rails.root}/app/models/goober"
24
+ #
25
+ # # good
26
+ # Rails.root.join('app/models/goober')
27
+ #
28
+ class FilePath < Cop
29
+ include ConfigurableEnforcedStyle
30
+ include RangeHelp
31
+
32
+ MSG_SLASHES = 'Please use `Rails.root.join(\'path/to\')` ' \
33
+ 'instead.'
34
+ MSG_ARGUMENTS = 'Please use `Rails.root.join(\'path\', \'to\')` ' \
35
+ 'instead.'
36
+
37
+ def_node_matcher :file_join_nodes?, <<~PATTERN
38
+ (send (const nil? :File) :join ...)
39
+ PATTERN
40
+
41
+ def_node_search :rails_root_nodes?, <<~PATTERN
42
+ (send (const nil? :Rails) :root)
43
+ PATTERN
44
+
45
+ def_node_matcher :rails_root_join_nodes?, <<~PATTERN
46
+ (send (send (const nil? :Rails) :root) :join ...)
47
+ PATTERN
48
+
49
+ def on_dstr(node)
50
+ return unless rails_root_nodes?(node)
51
+ return unless node.children.last.source.start_with?('.') ||
52
+ node.children.last.source.include?(File::SEPARATOR)
53
+
54
+ register_offense(node)
55
+ end
56
+
57
+ def on_send(node)
58
+ check_for_file_join_with_rails_root(node)
59
+ check_for_rails_root_join_with_slash_separated_path(node)
60
+ check_for_rails_root_join_with_string_arguments(node)
61
+ end
62
+
63
+ private
64
+
65
+ def check_for_file_join_with_rails_root(node)
66
+ return unless file_join_nodes?(node)
67
+ return unless node.arguments.any? { |e| rails_root_nodes?(e) }
68
+
69
+ register_offense(node)
70
+ end
71
+
72
+ def check_for_rails_root_join_with_string_arguments(node)
73
+ return unless style == :slashes
74
+ return unless rails_root_nodes?(node)
75
+ return unless rails_root_join_nodes?(node)
76
+ return unless node.arguments.size > 1
77
+ return unless node.arguments.all?(&:str_type?)
78
+
79
+ register_offense(node)
80
+ end
81
+
82
+ def check_for_rails_root_join_with_slash_separated_path(node)
83
+ return unless style == :arguments
84
+ return unless rails_root_nodes?(node)
85
+ return unless rails_root_join_nodes?(node)
86
+ return unless node.arguments.any? { |arg| string_with_slash?(arg) }
87
+
88
+ register_offense(node)
89
+ end
90
+
91
+ def string_with_slash?(node)
92
+ node.str_type? && node.source =~ %r{/}
93
+ end
94
+
95
+ def register_offense(node)
96
+ line_range = node.loc.column...node.loc.last_column
97
+ source_range = source_range(processed_source.buffer, node.first_line,
98
+ line_range)
99
+ add_offense(node, location: source_range)
100
+ end
101
+
102
+ def message(_node)
103
+ format(style == :arguments ? MSG_ARGUMENTS : MSG_SLASHES)
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop is used to identify usages of `where.first` and
7
+ # change them to use `find_by` instead.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # User.where(name: 'Bruce').first
12
+ # User.where(name: 'Bruce').take
13
+ #
14
+ # # good
15
+ # User.find_by(name: 'Bruce')
16
+ class FindBy < Cop
17
+ include RangeHelp
18
+
19
+ MSG = 'Use `find_by` instead of `where.%<method>s`.'
20
+ TARGET_SELECTORS = %i[first take].freeze
21
+
22
+ def_node_matcher :where_first?, <<~PATTERN
23
+ (send ({send csend} _ :where ...) {:first :take})
24
+ PATTERN
25
+
26
+ def on_send(node)
27
+ return unless where_first?(node)
28
+
29
+ range = range_between(node.receiver.loc.selector.begin_pos,
30
+ node.loc.selector.end_pos)
31
+
32
+ add_offense(node, location: range,
33
+ message: format(MSG, method: node.method_name))
34
+ end
35
+ alias on_csend on_send
36
+
37
+ def autocorrect(node)
38
+ # Don't autocorrect where(...).first, because it can return different
39
+ # results from find_by. (They order records differently, so the
40
+ # 'first' record can be different.)
41
+ return if node.method?(:first)
42
+
43
+ where_loc = node.receiver.loc.selector
44
+ first_loc = range_between(node.loc.dot.begin_pos,
45
+ node.loc.selector.end_pos)
46
+
47
+ lambda do |corrector|
48
+ corrector.replace(where_loc, 'find_by')
49
+ corrector.replace(first_loc, '')
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop is used to identify usages of `all.each` and
7
+ # change them to use `all.find_each` instead.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # User.all.each
12
+ #
13
+ # # good
14
+ # User.all.find_each
15
+ class FindEach < Cop
16
+ MSG = 'Use `find_each` instead of `each`.'
17
+
18
+ SCOPE_METHODS = %i[
19
+ all eager_load includes joins left_joins left_outer_joins not preload
20
+ references unscoped where
21
+ ].freeze
22
+ IGNORED_METHODS = %i[order limit select].freeze
23
+
24
+ def on_send(node)
25
+ return unless node.receiver&.send_type? &&
26
+ node.method?(:each)
27
+
28
+ return unless SCOPE_METHODS.include?(node.receiver.method_name)
29
+ return if method_chain(node).any? { |m| ignored_by_find_each?(m) }
30
+
31
+ add_offense(node, location: :selector)
32
+ end
33
+
34
+ def autocorrect(node)
35
+ ->(corrector) { corrector.replace(node.loc.selector, 'find_each') }
36
+ end
37
+
38
+ private
39
+
40
+ def method_chain(node)
41
+ node.each_node(:send).map(&:method_name)
42
+ end
43
+
44
+ def ignored_by_find_each?(relation_method)
45
+ # Active Record's #find_each ignores various extra parameters
46
+ IGNORED_METHODS.include?(relation_method)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end