rubocop-rails 2.20.2 → 2.22.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -2
  3. data/config/default.yml +68 -8
  4. data/lib/rubocop/cop/mixin/database_type_resolvable.rb +66 -0
  5. data/lib/rubocop/cop/mixin/index_method.rb +2 -2
  6. data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +1 -1
  7. data/lib/rubocop/cop/rails/action_controller_test_case.rb +2 -2
  8. data/lib/rubocop/cop/rails/action_filter.rb +3 -0
  9. data/lib/rubocop/cop/rails/bulk_change_table.rb +5 -38
  10. data/lib/rubocop/cop/rails/dangerous_column_names.rb +447 -0
  11. data/lib/rubocop/cop/rails/date.rb +1 -1
  12. data/lib/rubocop/cop/rails/duplicate_association.rb +69 -12
  13. data/lib/rubocop/cop/rails/dynamic_find_by.rb +3 -3
  14. data/lib/rubocop/cop/rails/env_local.rb +46 -0
  15. data/lib/rubocop/cop/rails/file_path.rb +4 -1
  16. data/lib/rubocop/cop/rails/freeze_time.rb +1 -1
  17. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +1 -1
  18. data/lib/rubocop/cop/rails/helper_instance_variable.rb +1 -1
  19. data/lib/rubocop/cop/rails/http_status.rb +4 -3
  20. data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +63 -13
  21. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +7 -8
  22. data/lib/rubocop/cop/rails/not_null_column.rb +13 -3
  23. data/lib/rubocop/cop/rails/output.rb +3 -2
  24. data/lib/rubocop/cop/rails/rake_environment.rb +20 -4
  25. data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +207 -0
  26. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +7 -0
  27. data/lib/rubocop/cop/rails/reversible_migration.rb +1 -1
  28. data/lib/rubocop/cop/rails/root_pathname_methods.rb +38 -4
  29. data/lib/rubocop/cop/rails/save_bang.rb +9 -4
  30. data/lib/rubocop/cop/rails/schema_comment.rb +16 -10
  31. data/lib/rubocop/cop/rails/select_map.rb +78 -0
  32. data/lib/rubocop/cop/rails/time_zone.rb +12 -5
  33. data/lib/rubocop/cop/rails/transaction_exit_statement.rb +29 -10
  34. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +1 -1
  35. data/lib/rubocop/cop/rails/unknown_env.rb +5 -1
  36. data/lib/rubocop/cop/rails/unused_render_content.rb +67 -0
  37. data/lib/rubocop/cop/rails/where_exists.rb +0 -1
  38. data/lib/rubocop/cop/rails_cops.rb +6 -0
  39. data/lib/rubocop/rails/schema_loader.rb +1 -1
  40. data/lib/rubocop/rails/version.rb +1 -1
  41. data/lib/rubocop-rails.rb +8 -0
  42. metadata +9 -3
@@ -8,6 +8,7 @@ module RuboCop
8
8
  # @example EnforcedStyle: symbolic (default)
9
9
  # # bad
10
10
  # render :foo, status: 200
11
+ # render :foo, status: '200'
11
12
  # render json: { foo: 'bar' }, status: 200
12
13
  # render plain: 'foo/bar', status: 304
13
14
  # redirect_to root_url, status: 301
@@ -50,7 +51,7 @@ module RuboCop
50
51
  PATTERN
51
52
 
52
53
  def_node_matcher :status_code, <<~PATTERN
53
- (hash <(pair (sym :status) ${int sym}) ...>)
54
+ (hash <(pair (sym :status) ${int sym str}) ...>)
54
55
  PATTERN
55
56
 
56
57
  def on_send(node)
@@ -108,7 +109,7 @@ module RuboCop
108
109
  private
109
110
 
110
111
  def symbol
111
- ::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number)
112
+ ::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number.to_i)
112
113
  end
113
114
 
114
115
  def number
@@ -133,7 +134,7 @@ module RuboCop
133
134
  end
134
135
 
135
136
  def offensive?
136
- !node.int_type? && !permitted_symbol?
137
+ !node.int_type? && !permitted_symbol? && number
137
138
  end
138
139
 
139
140
  def message
@@ -5,7 +5,13 @@ module RuboCop
5
5
  module Rails
6
6
  # Checks for places where I18n "lazy" lookup can be used.
7
7
  #
8
- # @example
8
+ # This cop has two different enforcement modes. When the EnforcedStyle
9
+ # is `lazy` (the default), explicit lookups are added as offenses.
10
+ #
11
+ # When the EnforcedStyle is `explicit` then lazy lookups are added as
12
+ # offenses.
13
+ #
14
+ # @example EnforcedStyle: lazy (default)
9
15
  # # en.yml
10
16
  # # en:
11
17
  # # books:
@@ -28,11 +34,29 @@ module RuboCop
28
34
  # end
29
35
  # end
30
36
  #
37
+ # @example EnforcedStyle: explicit
38
+ # # bad
39
+ # class BooksController < ApplicationController
40
+ # def create
41
+ # # ...
42
+ # redirect_to books_url, notice: t('.success')
43
+ # end
44
+ # end
45
+ #
46
+ # # good
47
+ # class BooksController < ApplicationController
48
+ # def create
49
+ # # ...
50
+ # redirect_to books_url, notice: t('books.create.success')
51
+ # end
52
+ # end
53
+ #
31
54
  class I18nLazyLookup < Base
55
+ include ConfigurableEnforcedStyle
32
56
  include VisibilityHelp
33
57
  extend AutoCorrector
34
58
 
35
- MSG = 'Use "lazy" lookup for the text used in controllers.'
59
+ MSG = 'Use %<style>s lookup for the text used in controllers.'
36
60
 
37
61
  RESTRICT_ON_SEND = %i[translate t].freeze
38
62
 
@@ -42,23 +66,45 @@ module RuboCop
42
66
 
43
67
  def on_send(node)
44
68
  translate_call?(node) do |key_node|
45
- key = key_node.value
46
- return if key.to_s.start_with?('.')
69
+ case style
70
+ when :lazy
71
+ handle_lazy_style(node, key_node)
72
+ when :explicit
73
+ handle_explicit_style(node, key_node)
74
+ end
75
+ end
76
+ end
77
+
78
+ private
47
79
 
48
- controller, action = controller_and_action(node)
49
- return unless controller && action
80
+ def handle_lazy_style(node, key_node)
81
+ key = key_node.value
82
+ return if key.to_s.start_with?('.')
50
83
 
51
- scoped_key = get_scoped_key(key_node, controller, action)
52
- return unless key == scoped_key
84
+ controller, action = controller_and_action(node)
85
+ return unless controller && action
53
86
 
54
- add_offense(key_node) do |corrector|
55
- unscoped_key = key_node.value.to_s.split('.').last
56
- corrector.replace(key_node, "'.#{unscoped_key}'")
57
- end
87
+ scoped_key = get_scoped_key(key_node, controller, action)
88
+ return unless key == scoped_key
89
+
90
+ add_offense(key_node) do |corrector|
91
+ unscoped_key = key_node.value.to_s.split('.').last
92
+ corrector.replace(key_node, "'.#{unscoped_key}'")
58
93
  end
59
94
  end
60
95
 
61
- private
96
+ def handle_explicit_style(node, key_node)
97
+ key = key_node.value
98
+ return unless key.to_s.start_with?('.')
99
+
100
+ controller, action = controller_and_action(node)
101
+ return unless controller && action
102
+
103
+ scoped_key = get_scoped_key(key_node, controller, action)
104
+ add_offense(key_node) do |corrector|
105
+ corrector.replace(key_node, "'#{scoped_key}'")
106
+ end
107
+ end
62
108
 
63
109
  def controller_and_action(node)
64
110
  action_node = node.each_ancestor(:def).first
@@ -90,6 +136,10 @@ module RuboCop
90
136
 
91
137
  path.delete_suffix('Controller').underscore
92
138
  end
139
+
140
+ def message(_range)
141
+ format(MSG, style: style)
142
+ end
93
143
  end
94
144
  end
95
145
  end
@@ -122,24 +122,23 @@ module RuboCop
122
122
  parent = node.each_ancestor(:class, :module).first
123
123
  return unless parent
124
124
 
125
+ # NOTE: a `:begin` node may not exist if the class/module consists of a single statement
125
126
  block = parent.each_child_node(:begin).first
126
- return unless block
127
-
128
127
  defined_action_methods = defined_action_methods(block)
129
128
 
130
- methods = array_values(methods_node).reject do |method|
131
- defined_action_methods.include?(method)
132
- end
129
+ unmatched_methods = array_values(methods_node) - defined_action_methods
130
+ return if unmatched_methods.empty?
133
131
 
134
- message = message(methods, parent)
135
- add_offense(node, message: message) unless methods.empty?
132
+ message = message(unmatched_methods, parent)
133
+ add_offense(node, message: message)
136
134
  end
137
135
 
138
136
  private
139
137
 
140
138
  def defined_action_methods(block)
141
- defined_methods = block.each_child_node(:def).map(&:method_name)
139
+ return [] unless block
142
140
 
141
+ defined_methods = block.each_child_node(:def).map(&:method_name)
143
142
  defined_methods + aliased_action_methods(block, defined_methods)
144
143
  end
145
144
 
@@ -3,8 +3,13 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # Checks for add_column call with NOT NULL constraint
7
- # in migration file.
6
+ # Checks for add_column call with NOT NULL constraint in migration file.
7
+ #
8
+ # `TEXT` can have default values in PostgreSQL, but not in MySQL.
9
+ # It will automatically detect an adapter from `development` environment
10
+ # in `config/database.yml` or the environment variable `DATABASE_URL`
11
+ # when the `Database` option is not set. If the database is MySQL,
12
+ # this cop ignores offenses for the `TEXT`.
8
13
  #
9
14
  # @example
10
15
  # # bad
@@ -17,6 +22,8 @@ module RuboCop
17
22
  # add_reference :products, :category
18
23
  # add_reference :products, :category, null: false, default: 1
19
24
  class NotNullColumn < Base
25
+ include DatabaseTypeResolvable
26
+
20
27
  MSG = 'Do not add a NOT NULL column without a default value.'
21
28
  RESTRICT_ON_SEND = %i[add_column add_reference].freeze
22
29
 
@@ -45,7 +52,10 @@ module RuboCop
45
52
 
46
53
  def check_add_column(node)
47
54
  add_not_null_column?(node) do |type, pairs|
48
- return if type.respond_to?(:value) && (type.value == :virtual || type.value == 'virtual')
55
+ if type.respond_to?(:value)
56
+ return if type.value == :virtual || type.value == 'virtual'
57
+ return if (type.value == :text || type.value == 'text') && database == MYSQL
58
+ end
49
59
 
50
60
  check_pairs(pairs)
51
61
  end
@@ -23,6 +23,7 @@ module RuboCop
23
23
 
24
24
  MSG = "Do not write to stdout. Use Rails's logger if you want to log."
25
25
  RESTRICT_ON_SEND = %i[ap p pp pretty_print print puts binwrite syswrite write write_nonblock].freeze
26
+ ALLOWED_TYPES = %i[send csend block numblock].freeze
26
27
 
27
28
  def_node_matcher :output?, <<~PATTERN
28
29
  (send nil? {:ap :p :pp :pretty_print :print :puts} ...)
@@ -39,8 +40,8 @@ module RuboCop
39
40
  PATTERN
40
41
 
41
42
  def on_send(node)
42
- return if node.parent&.call_type?
43
- return unless output?(node) || io_output?(node)
43
+ return if ALLOWED_TYPES.include?(node.parent&.type)
44
+ return if !output?(node) && !io_output?(node)
44
45
 
45
46
  range = offense_range(node)
46
47
 
@@ -45,16 +45,24 @@ module RuboCop
45
45
  return if with_dependencies?(task_method)
46
46
 
47
47
  add_offense(task_method) do |corrector|
48
- task_name = task_method.arguments[0]
49
- task_dependency = correct_task_dependency(task_name)
50
-
51
- corrector.replace(task_name, task_dependency)
48
+ if with_arguments?(task_method)
49
+ new_task_dependency = correct_task_arguments_dependency(task_method)
50
+ corrector.replace(task_arguments(task_method), new_task_dependency)
51
+ else
52
+ task_name = task_method.first_argument
53
+ new_task_dependency = correct_task_dependency(task_name)
54
+ corrector.replace(task_name, new_task_dependency)
55
+ end
52
56
  end
53
57
  end
54
58
  end
55
59
 
56
60
  private
57
61
 
62
+ def correct_task_arguments_dependency(task_method)
63
+ "#{task_arguments(task_method).source} => :environment"
64
+ end
65
+
58
66
  def correct_task_dependency(task_name)
59
67
  if task_name.sym_type?
60
68
  "#{task_name.source.delete(':|\'|"')}: :environment"
@@ -80,6 +88,14 @@ module RuboCop
80
88
  end
81
89
  end
82
90
 
91
+ def task_arguments(node)
92
+ node.arguments[1]
93
+ end
94
+
95
+ def with_arguments?(node)
96
+ node.arguments.size > 1 && node.arguments[1].array_type?
97
+ end
98
+
83
99
  def with_dependencies?(node)
84
100
  first_arg = node.arguments[0]
85
101
  return false unless first_arg
@@ -0,0 +1,207 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Detect redundant `all` used as a receiver for Active Record query methods.
7
+ #
8
+ # @safety
9
+ # This cop is unsafe for autocorrection if the receiver for `all` is not an Active Record object.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # User.all.find(id)
14
+ # User.all.order(:created_at)
15
+ # users.all.where(id: ids)
16
+ # user.articles.all.order(:created_at)
17
+ #
18
+ # # good
19
+ # User.find(id)
20
+ # User.order(:created_at)
21
+ # users.where(id: ids)
22
+ # user.articles.order(:created_at)
23
+ #
24
+ # @example AllowedReceivers: ['ActionMailer::Preview', 'ActiveSupport::TimeZone'] (default)
25
+ # # good
26
+ # ActionMailer::Preview.all.first
27
+ # ActiveSupport::TimeZone.all.first
28
+ class RedundantActiveRecordAllMethod < Base
29
+ include ActiveRecordHelper
30
+ include AllowedReceivers
31
+ include RangeHelp
32
+ extend AutoCorrector
33
+
34
+ MSG = 'Redundant `all` detected.'
35
+
36
+ RESTRICT_ON_SEND = [:all].freeze
37
+
38
+ # Defined methods in `ActiveRecord::Querying::QUERYING_METHODS` on activerecord 7.1.0.
39
+ QUERYING_METHODS = %i[
40
+ and
41
+ annotate
42
+ any?
43
+ async_average
44
+ async_count
45
+ async_ids
46
+ async_maximum
47
+ async_minimum
48
+ async_pick
49
+ async_pluck
50
+ async_sum
51
+ average
52
+ calculate
53
+ count
54
+ create_or_find_by
55
+ create_or_find_by!
56
+ create_with
57
+ delete_all
58
+ delete_by
59
+ destroy_all
60
+ destroy_by
61
+ distinct
62
+ eager_load
63
+ except
64
+ excluding
65
+ exists?
66
+ extending
67
+ extract_associated
68
+ fifth
69
+ fifth!
70
+ find
71
+ find_by
72
+ find_by!
73
+ find_each
74
+ find_in_batches
75
+ find_or_create_by
76
+ find_or_create_by!
77
+ find_or_initialize_by
78
+ find_sole_by
79
+ first
80
+ first!
81
+ first_or_create
82
+ first_or_create!
83
+ first_or_initialize
84
+ forty_two
85
+ forty_two!
86
+ fourth
87
+ fourth!
88
+ from
89
+ group
90
+ having
91
+ ids
92
+ in_batches
93
+ in_order_of
94
+ includes
95
+ invert_where
96
+ joins
97
+ last
98
+ last!
99
+ left_joins
100
+ left_outer_joins
101
+ limit
102
+ lock
103
+ many?
104
+ maximum
105
+ merge
106
+ minimum
107
+ none
108
+ none?
109
+ offset
110
+ one?
111
+ only
112
+ optimizer_hints
113
+ or
114
+ order
115
+ pick
116
+ pluck
117
+ preload
118
+ readonly
119
+ references
120
+ regroup
121
+ reorder
122
+ reselect
123
+ rewhere
124
+ second
125
+ second!
126
+ second_to_last
127
+ second_to_last!
128
+ select
129
+ sole
130
+ strict_loading
131
+ sum
132
+ take
133
+ take!
134
+ third
135
+ third!
136
+ third_to_last
137
+ third_to_last!
138
+ touch_all
139
+ unscope
140
+ update_all
141
+ where
142
+ with
143
+ without
144
+ ].to_set.freeze
145
+
146
+ POSSIBLE_ENUMERABLE_BLOCK_METHODS = %i[any? count find none? one? select sum].freeze
147
+
148
+ def_node_matcher :followed_by_query_method?, <<~PATTERN
149
+ (send (send _ :all) QUERYING_METHODS ...)
150
+ PATTERN
151
+
152
+ def on_send(node)
153
+ return if !followed_by_query_method?(node.parent) || possible_enumerable_block_method?(node)
154
+ return if node.receiver ? allowed_receiver?(node.receiver) : !inherit_active_record_base?(node)
155
+
156
+ range_of_all_method = offense_range(node)
157
+ add_offense(range_of_all_method) do |collector|
158
+ collector.remove(range_of_all_method)
159
+ collector.remove(node.parent.loc.dot)
160
+ end
161
+ end
162
+
163
+ private
164
+
165
+ def possible_enumerable_block_method?(node)
166
+ parent = node.parent
167
+ return false unless POSSIBLE_ENUMERABLE_BLOCK_METHODS.include?(parent.method_name)
168
+
169
+ parent.parent&.block_type? || parent.parent&.numblock_type? || parent.first_argument&.block_pass_type?
170
+ end
171
+
172
+ def offense_range(node)
173
+ range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
174
+ end
175
+
176
+ # TODO: In the future, please support only RuboCop 1.52+ and use `RuboCop::Cop::AllowedReceivers`:
177
+ # https://github.com/rubocop/rubocop/blob/v1.52.0/lib/rubocop/cop/mixin/allowed_receivers.rb
178
+ # At that time, this duplicated module implementation can be removed.
179
+ module AllowedReceivers
180
+ def allowed_receiver?(receiver)
181
+ receiver_name = receiver_name(receiver)
182
+
183
+ allowed_receivers.include?(receiver_name)
184
+ end
185
+
186
+ def receiver_name(receiver)
187
+ return receiver_name(receiver.receiver) if receiver.receiver && !receiver.receiver.const_type?
188
+
189
+ if receiver.send_type?
190
+ if receiver.receiver
191
+ "#{receiver_name(receiver.receiver)}.#{receiver.method_name}"
192
+ else
193
+ receiver.method_name.to_s
194
+ end
195
+ else
196
+ receiver.source
197
+ end
198
+ end
199
+
200
+ def allowed_receivers
201
+ cop_config.fetch('AllowedReceivers', [])
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end
@@ -53,6 +53,12 @@ module RuboCop
53
53
  # @example source that matches - by a foreign key
54
54
  # validates :user_id, presence: true
55
55
  #
56
+ # @example source that DOES NOT match - if condition
57
+ # validates :user_id, presence: true, if: condition
58
+ #
59
+ # @example source that DOES NOT match - unless condition
60
+ # validates :user_id, presence: true, unless: condition
61
+ #
56
62
  # @example source that DOES NOT match - strict validation
57
63
  # validates :user_id, presence: true, strict: true
58
64
  #
@@ -65,6 +71,7 @@ module RuboCop
65
71
  $[
66
72
  (hash <$(pair (sym :presence) true) ...>) # presence: true
67
73
  !(hash <$(pair (sym :strict) {true const}) ...>) # strict: true
74
+ !(hash <$(pair (sym {:if :unless}) _) ...>) # if: some_condition or unless: some_condition
68
75
  ]
69
76
  )
70
77
  PATTERN
@@ -307,7 +307,7 @@ module RuboCop
307
307
 
308
308
  def within_reversible_or_up_only_block?(node)
309
309
  node.each_ancestor(:block).any? do |ancestor|
310
- (ancestor.block_type? && ancestor.send_node.method?(:reversible)) || ancestor.send_node.method?(:up_only)
310
+ (ancestor.block_type? && ancestor.method?(:reversible)) || ancestor.method?(:up_only)
311
311
  end
312
312
  end
313
313
 
@@ -12,7 +12,7 @@ module RuboCop
12
12
  # `Style/FileRead`, `Style/FileWrite` and `Rails/RootJoinChain`.
13
13
  #
14
14
  # @safety
15
- # This cop is unsafe for autocorrection because `Dir`'s `children`, `each_child`, `entries`, and `glob`
15
+ # This cop is unsafe for autocorrection because ``Dir``'s `children`, `each_child`, `entries`, and `glob`
16
16
  # methods return string element, but these methods of `Pathname` return `Pathname` element.
17
17
  #
18
18
  # @example
@@ -32,13 +32,28 @@ module RuboCop
32
32
  # Rails.root.join('db', 'schema.rb').write(content)
33
33
  # Rails.root.join('db', 'schema.rb').binwrite(content)
34
34
  #
35
- class RootPathnameMethods < Base
35
+ class RootPathnameMethods < Base # rubocop:disable Metrics/ClassLength
36
36
  extend AutoCorrector
37
37
  include RangeHelp
38
38
 
39
39
  MSG = '`%<rails_root>s` is a `Pathname` so you can just append `#%<method>s`.'
40
40
 
41
- DIR_METHODS = %i[children delete each_child empty? entries exist? glob mkdir open rmdir unlink].to_set.freeze
41
+ DIR_GLOB_METHODS = %i[glob].to_set.freeze
42
+
43
+ DIR_NON_GLOB_METHODS = %i[
44
+ children
45
+ delete
46
+ each_child
47
+ empty?
48
+ entries
49
+ exist?
50
+ mkdir
51
+ open
52
+ rmdir
53
+ unlink
54
+ ].to_set.freeze
55
+
56
+ DIR_METHODS = (DIR_GLOB_METHODS + DIR_NON_GLOB_METHODS).freeze
42
57
 
43
58
  FILE_METHODS = %i[
44
59
  atime
@@ -134,7 +149,8 @@ module RuboCop
134
149
 
135
150
  RESTRICT_ON_SEND = (DIR_METHODS + FILE_METHODS + FILE_TEST_METHODS + FILE_UTILS_METHODS).to_set.freeze
136
151
 
137
- def_node_matcher :pathname_method, <<~PATTERN
152
+ # @!method pathname_method_for_ruby_2_5_or_higher(node)
153
+ def_node_matcher :pathname_method_for_ruby_2_5_or_higher, <<~PATTERN
138
154
  {
139
155
  (send (const {nil? cbase} :Dir) $DIR_METHODS $_ $...)
140
156
  (send (const {nil? cbase} {:IO :File}) $FILE_METHODS $_ $...)
@@ -143,6 +159,16 @@ module RuboCop
143
159
  }
144
160
  PATTERN
145
161
 
162
+ # @!method pathname_method_for_ruby_2_4_or_lower(node)
163
+ def_node_matcher :pathname_method_for_ruby_2_4_or_lower, <<~PATTERN
164
+ {
165
+ (send (const {nil? cbase} :Dir) $DIR_NON_GLOB_METHODS $_ $...)
166
+ (send (const {nil? cbase} {:IO :File}) $FILE_METHODS $_ $...)
167
+ (send (const {nil? cbase} :FileTest) $FILE_TEST_METHODS $_ $...)
168
+ (send (const {nil? cbase} :FileUtils) $FILE_UTILS_METHODS $_ $...)
169
+ }
170
+ PATTERN
171
+
146
172
  def_node_matcher :dir_glob?, <<~PATTERN
147
173
  (send
148
174
  (const {cbase nil?} :Dir) :glob ...)
@@ -183,6 +209,14 @@ module RuboCop
183
209
  yield(method, path, args, rails_root)
184
210
  end
185
211
 
212
+ def pathname_method(node)
213
+ if target_ruby_version >= 2.5
214
+ pathname_method_for_ruby_2_5_or_higher(node)
215
+ else
216
+ pathname_method_for_ruby_2_4_or_lower(node)
217
+ end
218
+ end
219
+
186
220
  def build_path_glob_replacement(path, method)
187
221
  receiver = range_between(path.source_range.begin_pos, path.children.first.loc.selector.end_pos).source
188
222
 
@@ -188,7 +188,7 @@ module RuboCop
188
188
  end
189
189
 
190
190
  def persisted_referenced?(assignment)
191
- return unless assignment.referenced?
191
+ return false unless assignment.referenced?
192
192
 
193
193
  assignment.variable.references.any? do |reference|
194
194
  call_to_persisted?(reference.node.parent)
@@ -235,10 +235,10 @@ module RuboCop
235
235
 
236
236
  def in_condition_or_compound_boolean?(node)
237
237
  node = node.block_node || node
238
- parent = node.parent
238
+ parent = node.each_ancestor.find { |ancestor| !ancestor.begin_type? }
239
239
  return false unless parent
240
240
 
241
- operator_or_single_negative?(parent) || (conditional?(parent) && node == parent.condition)
241
+ operator_or_single_negative?(parent) || (conditional?(parent) && node == deparenthesize(parent.condition))
242
242
  end
243
243
 
244
244
  def operator_or_single_negative?(node)
@@ -249,6 +249,11 @@ module RuboCop
249
249
  parent.if_type? || parent.case_type?
250
250
  end
251
251
 
252
+ def deparenthesize(node)
253
+ node = node.children.last while node.begin_type?
254
+ node
255
+ end
256
+
252
257
  def checked_immediately?(node)
253
258
  node.parent && call_to_persisted?(node.parent)
254
259
  end
@@ -298,7 +303,7 @@ module RuboCop
298
303
 
299
304
  node = assignable_node(node)
300
305
  method, sibling_index = find_method_with_sibling_index(node.parent)
301
- return unless method && (method.def_type? || method.block_type?)
306
+ return false unless method && (method.def_type? || method.block_type?)
302
307
 
303
308
  method.children.size == node.sibling_index + sibling_index
304
309
  end
@@ -74,17 +74,25 @@ module RuboCop
74
74
  def on_send(node)
75
75
  if add_column_without_comment?(node)
76
76
  add_offense(node, message: COLUMN_MSG)
77
- elsif create_table?(node)
78
- if create_table_without_comment?(node)
79
- add_offense(node, message: TABLE_MSG)
80
- elsif create_table_column_call_without_comment?(node)
81
- add_offense(node.parent.body, message: COLUMN_MSG)
82
- end
77
+ elsif create_table_without_comment?(node)
78
+ add_offense(node, message: TABLE_MSG)
79
+ elsif create_table_with_block?(node.parent)
80
+ check_column_within_create_table_block(node.parent.body)
83
81
  end
84
82
  end
85
83
 
86
84
  private
87
85
 
86
+ def check_column_within_create_table_block(node)
87
+ if node.begin_type?
88
+ node.child_nodes.each do |child_node|
89
+ add_offense(child_node, message: COLUMN_MSG) if t_column_without_comment?(child_node)
90
+ end
91
+ elsif t_column_without_comment?(node)
92
+ add_offense(node, message: COLUMN_MSG)
93
+ end
94
+ end
95
+
88
96
  def add_column_without_comment?(node)
89
97
  add_column?(node) && !add_column_with_comment?(node)
90
98
  end
@@ -93,10 +101,8 @@ module RuboCop
93
101
  create_table?(node) && !create_table_with_comment?(node)
94
102
  end
95
103
 
96
- def create_table_column_call_without_comment?(node)
97
- create_table_with_block?(node.parent) &&
98
- t_column?(node.parent.body) &&
99
- !t_column_with_comment?(node.parent.body)
104
+ def t_column_without_comment?(node)
105
+ t_column?(node) && !t_column_with_comment?(node)
100
106
  end
101
107
  end
102
108
  end