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,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks for the use of the has_and_belongs_to_many macro.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # # has_and_belongs_to_many :ingredients
11
+ #
12
+ # # good
13
+ # # has_many :ingredients, through: :recipe_ingredients
14
+ class HasAndBelongsToMany < Cop
15
+ MSG = 'Prefer `has_many :through` to `has_and_belongs_to_many`.'
16
+
17
+ def on_send(node)
18
+ return unless node.command?(:has_and_belongs_to_many)
19
+
20
+ add_offense(node, location: :selector)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop looks for `has_many` or `has_one` associations that don't
7
+ # specify a `:dependent` option.
8
+ # It doesn't register an offense if `:through` option was specified.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # class User < ActiveRecord::Base
13
+ # has_many :comments
14
+ # has_one :avatar
15
+ # end
16
+ #
17
+ # # good
18
+ # class User < ActiveRecord::Base
19
+ # has_many :comments, dependent: :restrict_with_exception
20
+ # has_one :avatar, dependent: :destroy
21
+ # has_many :patients, through: :appointments
22
+ # end
23
+ class HasManyOrHasOneDependent < Cop
24
+ MSG = 'Specify a `:dependent` option.'
25
+
26
+ def_node_search :active_resource_class?, <<~PATTERN
27
+ (const (const nil? :ActiveResource) :Base)
28
+ PATTERN
29
+
30
+ def_node_matcher :association_without_options?, <<~PATTERN
31
+ (send nil? {:has_many :has_one} _)
32
+ PATTERN
33
+
34
+ def_node_matcher :association_with_options?, <<~PATTERN
35
+ (send nil? {:has_many :has_one} _ (hash $...))
36
+ PATTERN
37
+
38
+ def_node_matcher :dependent_option?, <<~PATTERN
39
+ (pair (sym :dependent) !nil)
40
+ PATTERN
41
+
42
+ def_node_matcher :present_option?, <<~PATTERN
43
+ (pair (sym :through) !nil)
44
+ PATTERN
45
+
46
+ def_node_matcher :with_options_block, <<~PATTERN
47
+ (block
48
+ (send nil? :with_options
49
+ (hash $...))
50
+ (args) ...)
51
+ PATTERN
52
+
53
+ def on_send(node)
54
+ return if active_resource?(node.parent)
55
+
56
+ unless association_without_options?(node)
57
+ return if valid_options?(association_with_options?(node))
58
+ end
59
+
60
+ return if valid_options_in_with_options_block?(node)
61
+
62
+ add_offense(node, location: :selector)
63
+ end
64
+
65
+ private
66
+
67
+ def valid_options_in_with_options_block?(node)
68
+ return true unless node.parent
69
+
70
+ n = node.parent.begin_type? ? node.parent.parent : node.parent
71
+
72
+ contain_valid_options_in_with_options_block?(n)
73
+ end
74
+
75
+ def contain_valid_options_in_with_options_block?(node)
76
+ if with_options_block(node)
77
+ return true if valid_options?(with_options_block(node))
78
+
79
+ return false unless node.parent
80
+
81
+ return true if contain_valid_options_in_with_options_block?(
82
+ node.parent.parent
83
+ )
84
+ end
85
+
86
+ false
87
+ end
88
+
89
+ def valid_options?(options)
90
+ return true unless options
91
+ return true if options.any? do |o|
92
+ dependent_option?(o) || present_option?(o)
93
+ end
94
+
95
+ false
96
+ end
97
+
98
+ def active_resource?(node)
99
+ return false if node.nil?
100
+
101
+ active_resource_class?(node)
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks for use of the helper methods which reference
7
+ # instance variables.
8
+ #
9
+ # Relying on instance variables makes it difficult to re-use helper
10
+ # methods.
11
+ #
12
+ # If it seems awkward to explicitly pass in each dependent
13
+ # variable, consider moving the behaviour elsewhere, for
14
+ # example to a model, decorator or presenter.
15
+ #
16
+ # @example
17
+ # # bad
18
+ # def welcome_message
19
+ # "Hello #{@user.name}"
20
+ # end
21
+ #
22
+ # # good
23
+ # def welcome_message(user)
24
+ # "Hello #{user.name}"
25
+ # end
26
+ class HelperInstanceVariable < Cop
27
+ MSG = 'Do not use instance variables in helpers.'
28
+
29
+ def on_ivar(node)
30
+ add_offense(node)
31
+ end
32
+
33
+ def on_ivasgn(node)
34
+ add_offense(node, location: :name)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop is used to identify usages of http methods like `get`, `post`,
7
+ # `put`, `patch` without the usage of keyword arguments in your tests and
8
+ # change them to use keyword args. This cop only applies to Rails >= 5.
9
+ # If you are running Rails < 5 you should disable the
10
+ # Rails/HttpPositionalArguments cop or set your TargetRailsVersion in your
11
+ # .rubocop.yml file to 4.0, etc.
12
+ #
13
+ # @example
14
+ # # bad
15
+ # get :new, { user_id: 1}
16
+ #
17
+ # # good
18
+ # get :new, params: { user_id: 1 }
19
+ class HttpPositionalArguments < Cop
20
+ extend TargetRailsVersion
21
+
22
+ MSG = 'Use keyword arguments instead of ' \
23
+ 'positional arguments for http call: `%<verb>s`.'
24
+ KEYWORD_ARGS = %i[
25
+ method params session body flash xhr as headers env
26
+ ].freeze
27
+ HTTP_METHODS = %i[get post put patch delete head].freeze
28
+
29
+ minimum_target_rails_version 5.0
30
+
31
+ def_node_matcher :http_request?, <<~PATTERN
32
+ (send nil? {#{HTTP_METHODS.map(&:inspect).join(' ')}} !nil? $_ ...)
33
+ PATTERN
34
+
35
+ def on_send(node)
36
+ http_request?(node) do |data|
37
+ return unless needs_conversion?(data)
38
+
39
+ add_offense(node, location: :selector,
40
+ message: format(MSG, verb: node.method_name))
41
+ end
42
+ end
43
+
44
+ # given a pre Rails 5 method: get :new, {user_id: @user.id}, {}
45
+ #
46
+ # @return lambda of auto correct procedure
47
+ # the result should look like:
48
+ # get :new, params: { user_id: @user.id }, session: {}
49
+ # the http_method is the method used to call the controller
50
+ # the controller node can be a symbol, method, object or string
51
+ # that represents the path/action on the Rails controller
52
+ # the data is the http parameters and environment sent in
53
+ # the Rails 5 http call
54
+ def autocorrect(node)
55
+ lambda do |corrector|
56
+ corrector.replace(node.loc.expression, correction(node))
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def needs_conversion?(data)
63
+ return true unless data.hash_type?
64
+
65
+ data.each_pair.none? do |pair|
66
+ special_keyword_arg?(pair.key) ||
67
+ format_arg?(pair.key) && data.pairs.one?
68
+ end
69
+ end
70
+
71
+ def special_keyword_arg?(node)
72
+ node.sym_type? && KEYWORD_ARGS.include?(node.value)
73
+ end
74
+
75
+ def format_arg?(node)
76
+ node.sym_type? && node.value == :format
77
+ end
78
+
79
+ def convert_hash_data(data, type)
80
+ return '' if data.hash_type? && data.empty?
81
+
82
+ hash_data = if data.hash_type?
83
+ format('{ %<data>s }',
84
+ data: data.pairs.map(&:source).join(', '))
85
+ else
86
+ # user supplies an object,
87
+ # no need to surround with braces
88
+ data.source
89
+ end
90
+
91
+ format(', %<type>s: %<hash_data>s', type: type, hash_data: hash_data)
92
+ end
93
+
94
+ def correction(node)
95
+ http_path, *data = *node.arguments
96
+
97
+ controller_action = http_path.source
98
+ params = convert_hash_data(data.first, 'params')
99
+ session = convert_hash_data(data.last, 'session') if data.size > 1
100
+
101
+ format(correction_template(node), name: node.method_name,
102
+ action: controller_action,
103
+ params: params,
104
+ session: session)
105
+ end
106
+
107
+ def correction_template(node)
108
+ if parentheses?(node)
109
+ '%<name>s(%<action>s%<params>s%<session>s)'
110
+ else
111
+ '%<name>s %<action>s%<params>s%<session>s'
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Enforces use of symbolic or numeric value to define HTTP status.
7
+ #
8
+ # @example EnforcedStyle: symbolic (default)
9
+ # # bad
10
+ # render :foo, status: 200
11
+ # render json: { foo: 'bar' }, status: 200
12
+ # render plain: 'foo/bar', status: 304
13
+ # redirect_to root_url, status: 301
14
+ #
15
+ # # good
16
+ # render :foo, status: :ok
17
+ # render json: { foo: 'bar' }, status: :ok
18
+ # render plain: 'foo/bar', status: :not_modified
19
+ # redirect_to root_url, status: :moved_permanently
20
+ #
21
+ # @example EnforcedStyle: numeric
22
+ # # bad
23
+ # render :foo, status: :ok
24
+ # render json: { foo: 'bar' }, status: :not_found
25
+ # render plain: 'foo/bar', status: :not_modified
26
+ # redirect_to root_url, status: :moved_permanently
27
+ #
28
+ # # good
29
+ # render :foo, status: 200
30
+ # render json: { foo: 'bar' }, status: 404
31
+ # render plain: 'foo/bar', status: 304
32
+ # redirect_to root_url, status: 301
33
+ #
34
+ class HttpStatus < Cop
35
+ include ConfigurableEnforcedStyle
36
+
37
+ def_node_matcher :http_status, <<~PATTERN
38
+ {
39
+ (send nil? {:render :redirect_to} _ $hash)
40
+ (send nil? {:render :redirect_to} $hash)
41
+ }
42
+ PATTERN
43
+
44
+ def_node_matcher :status_code, <<~PATTERN
45
+ (hash <(pair (sym :status) ${int sym}) ...>)
46
+ PATTERN
47
+
48
+ def on_send(node)
49
+ http_status(node) do |hash_node|
50
+ status = status_code(hash_node)
51
+ return unless status
52
+
53
+ checker = checker_class.new(status)
54
+ return unless checker.offensive?
55
+
56
+ add_offense(checker.node, message: checker.message)
57
+ end
58
+ end
59
+
60
+ def autocorrect(node)
61
+ lambda do |corrector|
62
+ checker = checker_class.new(node)
63
+ corrector.replace(node.loc.expression, checker.preferred_style)
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def checker_class
70
+ case style
71
+ when :symbolic
72
+ SymbolicStyleChecker
73
+ when :numeric
74
+ NumericStyleChecker
75
+ end
76
+ end
77
+
78
+ # :nodoc:
79
+ class SymbolicStyleChecker
80
+ MSG = 'Prefer `%<prefer>s` over `%<current>s` ' \
81
+ 'to define HTTP status code.'
82
+ DEFAULT_MSG = 'Prefer `symbolic` over `numeric` ' \
83
+ 'to define HTTP status code.'
84
+
85
+ attr_reader :node
86
+ def initialize(node)
87
+ @node = node
88
+ end
89
+
90
+ def offensive?
91
+ !node.sym_type? && !custom_http_status_code?
92
+ end
93
+
94
+ def message
95
+ format(MSG, prefer: preferred_style, current: number.to_s)
96
+ end
97
+
98
+ def preferred_style
99
+ symbol.inspect
100
+ end
101
+
102
+ private
103
+
104
+ def symbol
105
+ ::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number)
106
+ end
107
+
108
+ def number
109
+ node.children.first
110
+ end
111
+
112
+ def custom_http_status_code?
113
+ node.int_type? &&
114
+ !::Rack::Utils::SYMBOL_TO_STATUS_CODE.value?(number)
115
+ end
116
+ end
117
+
118
+ # :nodoc:
119
+ class NumericStyleChecker
120
+ MSG = 'Prefer `%<prefer>s` over `%<current>s` ' \
121
+ 'to define HTTP status code.'
122
+ DEFAULT_MSG = 'Prefer `numeric` over `symbolic` ' \
123
+ 'to define HTTP status code.'
124
+ PERMITTED_STATUS = %i[error success missing redirect].freeze
125
+
126
+ attr_reader :node
127
+ def initialize(node)
128
+ @node = node
129
+ end
130
+
131
+ def offensive?
132
+ !node.int_type? && !permitted_symbol?
133
+ end
134
+
135
+ def message
136
+ format(MSG, prefer: preferred_style, current: symbol.inspect)
137
+ end
138
+
139
+ def preferred_style
140
+ number.to_s
141
+ end
142
+
143
+ private
144
+
145
+ def number
146
+ ::Rack::Utils::SYMBOL_TO_STATUS_CODE[symbol]
147
+ end
148
+
149
+ def symbol
150
+ node.value
151
+ end
152
+
153
+ def permitted_symbol?
154
+ node.sym_type? && PERMITTED_STATUS.include?(node.value)
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end