rubocop-rails 2.0.0

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