rubocop-rails 2.4.1

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