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,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks the migration for which timestamps are not included
7
+ # when creating a new table.
8
+ # In many cases, timestamps are useful information and should be added.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # create_table :users
13
+ #
14
+ # # bad
15
+ # create_table :users do |t|
16
+ # t.string :name
17
+ # t.string :email
18
+ # end
19
+ #
20
+ # # good
21
+ # create_table :users do |t|
22
+ # t.string :name
23
+ # t.string :email
24
+ #
25
+ # t.timestamps
26
+ # end
27
+ #
28
+ # # good
29
+ # create_table :users do |t|
30
+ # t.string :name
31
+ # t.string :email
32
+ #
33
+ # t.datetime :created_at, default: -> { 'CURRENT_TIMESTAMP' }
34
+ # end
35
+ #
36
+ # # good
37
+ # create_table :users do |t|
38
+ # t.string :name
39
+ # t.string :email
40
+ #
41
+ # t.datetime :updated_at, default: -> { 'CURRENT_TIMESTAMP' }
42
+ # end
43
+ class CreateTableWithTimestamps < Cop
44
+ MSG = 'Add timestamps when creating a new table.'
45
+
46
+ def_node_matcher :create_table_with_block?, <<-PATTERN
47
+ (block
48
+ (send nil? :create_table ...)
49
+ (args (arg _var))
50
+ _)
51
+ PATTERN
52
+
53
+ def_node_matcher :create_table_with_timestamps_proc?, <<-PATTERN
54
+ (send nil? :create_table (sym _) ... (block-pass (sym :timestamps)))
55
+ PATTERN
56
+
57
+ def_node_search :timestamps_included?, <<-PATTERN
58
+ (send _var :timestamps ...)
59
+ PATTERN
60
+
61
+ def_node_search :created_at_or_updated_at_included?, <<-PATTERN
62
+ (send _var :datetime
63
+ {(sym {:created_at :updated_at})(str {"created_at" "updated_at"})}
64
+ ...)
65
+ PATTERN
66
+
67
+ def on_send(node)
68
+ return unless node.command?(:create_table)
69
+
70
+ parent = node.parent
71
+
72
+ if create_table_with_block?(parent)
73
+ if parent.body.nil? || !time_columns_included?(parent.body)
74
+ add_offense(parent)
75
+ end
76
+ elsif create_table_with_timestamps_proc?(node)
77
+ # nothing to do
78
+ else
79
+ add_offense(node)
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def time_columns_included?(node)
86
+ timestamps_included?(node) || created_at_or_updated_at_included?(node)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks for the correct use of Date methods,
7
+ # such as Date.today, Date.current etc.
8
+ #
9
+ # Using `Date.today` is dangerous, because it doesn't know anything about
10
+ # Rails time zone. You must use `Time.zone.today` instead.
11
+ #
12
+ # The cop also reports warnings when you are using `to_time` method,
13
+ # because it doesn't know about Rails time zone either.
14
+ #
15
+ # Two styles are supported for this cop. When EnforcedStyle is 'strict'
16
+ # then the Date methods `today`, `current`, `yesterday`, and `tomorrow`
17
+ # are prohibited and the usage of both `to_time`
18
+ # and 'to_time_in_current_zone' are reported as warning.
19
+ #
20
+ # When EnforcedStyle is 'flexible' then only `Date.today` is prohibited
21
+ # and only `to_time` is reported as warning.
22
+ #
23
+ # @example EnforcedStyle: strict
24
+ # # bad
25
+ # Date.current
26
+ # Date.yesterday
27
+ # Date.today
28
+ # date.to_time
29
+ #
30
+ # # good
31
+ # Time.zone.today
32
+ # Time.zone.today - 1.day
33
+ #
34
+ # @example EnforcedStyle: flexible (default)
35
+ # # bad
36
+ # Date.today
37
+ # date.to_time
38
+ #
39
+ # # good
40
+ # Time.zone.today
41
+ # Time.zone.today - 1.day
42
+ # Date.current
43
+ # Date.yesterday
44
+ # date.in_time_zone
45
+ #
46
+ class Date < Cop
47
+ include ConfigurableEnforcedStyle
48
+
49
+ MSG = 'Do not use `Date.%<method_called>s` without zone. Use ' \
50
+ '`Time.zone.%<day>s` instead.'
51
+
52
+ MSG_SEND = 'Do not use `%<method>s` on Date objects, because they ' \
53
+ 'know nothing about the time zone in use.'
54
+
55
+ BAD_DAYS = %i[today current yesterday tomorrow].freeze
56
+
57
+ DEPRECATED_METHODS = [
58
+ { deprecated: 'to_time_in_current_zone', relevant: 'in_time_zone' }
59
+ ].freeze
60
+
61
+ DEPRECATED_MSG = '`%<deprecated>s` is deprecated. ' \
62
+ 'Use `%<relevant>s` instead.'
63
+
64
+ def on_const(node)
65
+ mod, klass = *node.children
66
+ # we should only check core Date class (`Date` or `::Date`)
67
+ return unless (mod.nil? || mod.cbase_type?) && method_send?(node)
68
+
69
+ check_date_node(node.parent) if klass == :Date
70
+ end
71
+
72
+ def on_send(node)
73
+ return unless node.receiver && bad_methods.include?(node.method_name)
74
+
75
+ return if safe_chain?(node) || safe_to_time?(node)
76
+
77
+ check_deprecated_methods(node)
78
+
79
+ add_offense(node, location: :selector,
80
+ message: format(MSG_SEND, method: node.method_name))
81
+ end
82
+ alias on_csend on_send
83
+
84
+ private
85
+
86
+ def check_deprecated_methods(node)
87
+ DEPRECATED_METHODS.each do |relevant:, deprecated:|
88
+ next unless node.method_name == deprecated.to_sym
89
+
90
+ add_offense(node, location: :selector,
91
+ message: format(DEPRECATED_MSG,
92
+ deprecated: deprecated,
93
+ relevant: relevant))
94
+ end
95
+ end
96
+
97
+ def check_date_node(node)
98
+ chain = extract_method_chain(node)
99
+
100
+ return if (chain & bad_days).empty?
101
+
102
+ method_name = (chain & bad_days).join('.')
103
+
104
+ day = method_name
105
+ day = 'today' if method_name == 'current'
106
+
107
+ add_offense(node, location: :selector,
108
+ message: format(MSG,
109
+ method_called: method_name,
110
+ day: day))
111
+ end
112
+
113
+ def extract_method_chain(node)
114
+ [node, *node.each_ancestor(:send)].map(&:method_name)
115
+ end
116
+
117
+ # checks that parent node of send_type
118
+ # and receiver is the given node
119
+ def method_send?(node)
120
+ return false unless node.parent&.send_type?
121
+
122
+ node.parent.receiver == node
123
+ end
124
+
125
+ def safe_chain?(node)
126
+ chain = extract_method_chain(node)
127
+
128
+ (chain & bad_methods).empty? || !(chain & good_methods).empty?
129
+ end
130
+
131
+ def safe_to_time?(node)
132
+ return unless node.method?(:to_time)
133
+
134
+ if node.receiver.str_type?
135
+ zone_regexp = /([+-][\d:]+|\dZ)\z/
136
+
137
+ node.receiver.str_content.match(zone_regexp)
138
+ else
139
+ node.arguments.one?
140
+ end
141
+ end
142
+
143
+ def good_days
144
+ style == :strict ? [] : %i[current yesterday tomorrow]
145
+ end
146
+
147
+ def bad_days
148
+ BAD_DAYS - good_days
149
+ end
150
+
151
+ def bad_methods
152
+ %i[to_time to_time_in_current_zone]
153
+ end
154
+
155
+ def good_methods
156
+ style == :strict ? [] : TimeZone::ACCEPTED_METHODS
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop looks for delegations that could have been created
7
+ # automatically with the `delegate` method.
8
+ #
9
+ # Safe navigation `&.` is ignored because Rails' `allow_nil`
10
+ # option checks not just for nil but also delegates if nil
11
+ # responds to the delegated method.
12
+ #
13
+ # The `EnforceForPrefixed` option (defaulted to `true`) means that
14
+ # using the target object as a prefix of the method name
15
+ # without using the `delegate` method will be a violation.
16
+ # When set to `false`, this case is legal.
17
+ #
18
+ # @example
19
+ # # bad
20
+ # def bar
21
+ # foo.bar
22
+ # end
23
+ #
24
+ # # good
25
+ # delegate :bar, to: :foo
26
+ #
27
+ # # good
28
+ # def bar
29
+ # foo&.bar
30
+ # end
31
+ #
32
+ # # good
33
+ # private
34
+ # def bar
35
+ # foo.bar
36
+ # end
37
+ #
38
+ # @example EnforceForPrefixed: true (default)
39
+ # # bad
40
+ # def foo_bar
41
+ # foo.bar
42
+ # end
43
+ #
44
+ # # good
45
+ # delegate :bar, to: :foo, prefix: true
46
+ #
47
+ # @example EnforceForPrefixed: false
48
+ # # good
49
+ # def foo_bar
50
+ # foo.bar
51
+ # end
52
+ #
53
+ # # good
54
+ # delegate :bar, to: :foo, prefix: true
55
+ class Delegate < Cop
56
+ MSG = 'Use `delegate` to define delegations.'
57
+
58
+ def_node_matcher :delegate?, <<-PATTERN
59
+ (def _method_name _args
60
+ (send (send nil? _) _ ...))
61
+ PATTERN
62
+
63
+ def on_def(node)
64
+ return unless trivial_delegate?(node)
65
+ return if private_or_protected_delegation(node)
66
+
67
+ add_offense(node, location: :keyword)
68
+ end
69
+
70
+ def autocorrect(node)
71
+ delegation = ["delegate :#{node.body.method_name}",
72
+ "to: :#{node.body.receiver.method_name}"]
73
+
74
+ if node.method_name == prefixed_method_name(node.body)
75
+ delegation << ['prefix: true']
76
+ end
77
+
78
+ lambda do |corrector|
79
+ corrector.replace(node.source_range, delegation.join(', '))
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def trivial_delegate?(def_node)
86
+ delegate?(def_node) &&
87
+ method_name_matches?(def_node.method_name, def_node.body) &&
88
+ arguments_match?(def_node.arguments, def_node.body)
89
+ end
90
+
91
+ def arguments_match?(arg_array, body)
92
+ argument_array = body.arguments
93
+
94
+ return false if arg_array.size != argument_array.size
95
+
96
+ arg_array.zip(argument_array).all? do |arg, argument|
97
+ arg.arg_type? &&
98
+ argument.lvar_type? &&
99
+ arg.children == argument.children
100
+ end
101
+ end
102
+
103
+ def method_name_matches?(method_name, body)
104
+ method_name == body.method_name ||
105
+ include_prefix_case? && method_name == prefixed_method_name(body)
106
+ end
107
+
108
+ def include_prefix_case?
109
+ cop_config['EnforceForPrefixed']
110
+ end
111
+
112
+ def prefixed_method_name(body)
113
+ [body.receiver.method_name, body.method_name].join('_').to_sym
114
+ end
115
+
116
+ def private_or_protected_delegation(node)
117
+ line = node.first_line
118
+ private_or_protected_before(line) ||
119
+ private_or_protected_inline(line)
120
+ end
121
+
122
+ def private_or_protected_before(line)
123
+ (processed_source[0..line].map(&:strip) & %w[private protected]).any?
124
+ end
125
+
126
+ def private_or_protected_inline(line)
127
+ processed_source[line - 1].strip =~ /\A(private )|(protected )/
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop looks for delegations that pass :allow_blank as an option
7
+ # instead of :allow_nil. :allow_blank is not a valid option to pass
8
+ # to ActiveSupport#delegate.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # delegate :foo, to: :bar, allow_blank: true
13
+ #
14
+ # # good
15
+ # delegate :foo, to: :bar, allow_nil: true
16
+ class DelegateAllowBlank < Cop
17
+ MSG = '`allow_blank` is not a valid option, use `allow_nil`.'
18
+
19
+ def_node_matcher :allow_blank_option, <<-PATTERN
20
+ (send nil? :delegate _ (hash <$(pair (sym :allow_blank) true) ...>))
21
+ PATTERN
22
+
23
+ def on_send(node)
24
+ allow_blank_option(node) do |offending_node|
25
+ add_offense(offending_node)
26
+ end
27
+ end
28
+
29
+ def autocorrect(pair_node)
30
+ lambda do |corrector|
31
+ corrector.replace(pair_node.key.source_range, 'allow_nil')
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks dynamic `find_by_*` methods.
7
+ # Use `find_by` instead of dynamic method.
8
+ # See. https://github.com/rubocop-hq/rails-style-guide#find_by
9
+ #
10
+ # @example
11
+ # # bad
12
+ # User.find_by_name(name)
13
+ #
14
+ # # bad
15
+ # User.find_by_name_and_email(name)
16
+ #
17
+ # # bad
18
+ # User.find_by_email!(name)
19
+ #
20
+ # # good
21
+ # User.find_by(name: name)
22
+ #
23
+ # # good
24
+ # User.find_by(name: name, email: email)
25
+ #
26
+ # # good
27
+ # User.find_by!(email: email)
28
+ class DynamicFindBy < Cop
29
+ MSG = 'Use `%<static_name>s` instead of dynamic `%<method>s`.'
30
+ METHOD_PATTERN = /^find_by_(.+?)(!)?$/.freeze
31
+
32
+ def on_send(node)
33
+ method_name = node.method_name.to_s
34
+
35
+ return if whitelist.include?(method_name)
36
+
37
+ static_name = static_method_name(method_name)
38
+
39
+ return unless static_name
40
+
41
+ add_offense(node,
42
+ message: format(MSG, static_name: static_name,
43
+ method: node.method_name))
44
+ end
45
+ alias on_csend on_send
46
+
47
+ def autocorrect(node)
48
+ keywords = column_keywords(node.method_name)
49
+
50
+ return if keywords.size != node.arguments.size
51
+
52
+ lambda do |corrector|
53
+ autocorrect_method_name(corrector, node)
54
+ autocorrect_argument_keywords(corrector, node, keywords)
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def autocorrect_method_name(corrector, node)
61
+ corrector.replace(node.loc.selector,
62
+ static_method_name(node.method_name.to_s))
63
+ end
64
+
65
+ def autocorrect_argument_keywords(corrector, node, keywords)
66
+ keywords.each.with_index do |keyword, idx|
67
+ corrector.insert_before(node.arguments[idx].loc.expression, keyword)
68
+ end
69
+ end
70
+
71
+ def whitelist
72
+ cop_config['Whitelist']
73
+ end
74
+
75
+ def column_keywords(method)
76
+ keyword_string = method.to_s[METHOD_PATTERN, 1]
77
+ keyword_string.split('_and_').map { |keyword| "#{keyword}: " }
78
+ end
79
+
80
+ # Returns static method name.
81
+ # If code isn't wrong, returns nil
82
+ def static_method_name(method_name)
83
+ match = METHOD_PATTERN.match(method_name)
84
+ return nil unless match
85
+
86
+ match[2] ? 'find_by!' : 'find_by'
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end