rubocop-rails 2.17.0 → 2.17.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +4 -4
  3. data/lib/rubocop/cop/mixin/active_record_helper.rb +2 -2
  4. data/lib/rubocop/cop/mixin/index_method.rb +1 -1
  5. data/lib/rubocop/cop/mixin/migrations_helper.rb +1 -1
  6. data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +15 -10
  7. data/lib/rubocop/cop/rails/action_controller_test_case.rb +1 -1
  8. data/lib/rubocop/cop/rails/action_order.rb +39 -3
  9. data/lib/rubocop/cop/rails/application_controller.rb +1 -1
  10. data/lib/rubocop/cop/rails/application_job.rb +1 -1
  11. data/lib/rubocop/cop/rails/application_mailer.rb +1 -1
  12. data/lib/rubocop/cop/rails/application_record.rb +1 -1
  13. data/lib/rubocop/cop/rails/content_tag.rb +1 -1
  14. data/lib/rubocop/cop/rails/dot_separated_keys.rb +1 -1
  15. data/lib/rubocop/cop/rails/duration_arithmetic.rb +2 -2
  16. data/lib/rubocop/cop/rails/dynamic_find_by.rb +16 -6
  17. data/lib/rubocop/cop/rails/file_path.rb +3 -3
  18. data/lib/rubocop/cop/rails/freeze_time.rb +7 -5
  19. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +1 -1
  20. data/lib/rubocop/cop/rails/helper_instance_variable.rb +1 -1
  21. data/lib/rubocop/cop/rails/http_status.rb +1 -6
  22. data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +2 -0
  23. data/lib/rubocop/cop/rails/i18n_locale_texts.rb +2 -2
  24. data/lib/rubocop/cop/rails/index_by.rb +1 -1
  25. data/lib/rubocop/cop/rails/index_with.rb +1 -1
  26. data/lib/rubocop/cop/rails/mailer_name.rb +3 -3
  27. data/lib/rubocop/cop/rails/migration_class_name.rb +1 -1
  28. data/lib/rubocop/cop/rails/output.rb +1 -1
  29. data/lib/rubocop/cop/rails/pluck.rb +29 -10
  30. data/lib/rubocop/cop/rails/require_dependency.rb +1 -1
  31. data/lib/rubocop/cop/rails/root_pathname_methods.rb +38 -14
  32. data/lib/rubocop/cop/rails/short_i18n.rb +1 -1
  33. data/lib/rubocop/cop/rails/skips_model_validations.rb +1 -1
  34. data/lib/rubocop/cop/rails/time_zone.rb +15 -2
  35. data/lib/rubocop/cop/rails/time_zone_assignment.rb +1 -1
  36. data/lib/rubocop/cop/rails/to_s_with_argument.rb +38 -1
  37. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +10 -1
  38. data/lib/rubocop/cop/rails/where_missing.rb +9 -2
  39. data/lib/rubocop/cop/rails/where_not_with_multiple_conditions.rb +1 -1
  40. data/lib/rubocop/rails/version.rb +1 -1
  41. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2a7a516c82d0953f3f1995b190ddc427aa251583287a9232229e82a218c93ba6
4
- data.tar.gz: f224a35df6de69c04c7c6f6314f9cfa713cc5accfba1bbce9d5554c7fd50a121
3
+ metadata.gz: 8a9beeaefa16cbe4afb5d598878444a835536ccda5d9b46c3e44e111513f1d8e
4
+ data.tar.gz: 669a9681ce5ab1fca038229a831f7e5acae6bbdcc3a58ecfc88d65fd54ef7190
5
5
  SHA512:
6
- metadata.gz: 5b3277765f3819855d3e29d3226c81ebc6231b1b1aaf583e1ae86db9e18b7b450bbc2b3f9bccaaa85623ca57e0ac139abde604c35bccc81c3081a80c4392f35d
7
- data.tar.gz: a3d7b79f10c2090b04a05d1b30cc3c639645aa6b3c2ad33247fecfa7597907238d8f6e727a8e0dd088b92d69842c488489e9883f9acfd4551e08a9b8f3dd1f8c
6
+ metadata.gz: 72ca38ec9a9136bfa5bd635db9290314a4d1a4c8e3942f15aa62a823885ff1a1783f884598dce6f52765db75daccf8e4054d3faecdf973da6f2c53b10dc2c153
7
+ data.tar.gz: 536c7d8f5dfec22444d2d0fc4b297eac18f7b87cc6c871ce250bec58a2a6d380e814749534e5aedc47c0a66c7ae80ac8c61ffec330093c575cd1cc8dd76e4de9
data/config/default.yml CHANGED
@@ -73,7 +73,7 @@ Rails/ActionControllerFlashBeforeRender:
73
73
  StyleGuide: 'https://rails.rubystyle.guide/#flash-before-render'
74
74
  Reference: 'https://api.rubyonrails.org/classes/ActionController/FlashBeforeRender.html'
75
75
  Enabled: 'pending'
76
- SafeAutocorrect: false
76
+ SafeAutoCorrect: false
77
77
  VersionAdded: '2.16'
78
78
 
79
79
  Rails/ActionControllerTestCase:
@@ -81,7 +81,7 @@ Rails/ActionControllerTestCase:
81
81
  StyleGuide: 'https://rails.rubystyle.guide/#integration-testing'
82
82
  Reference: 'https://api.rubyonrails.org/classes/ActionController/TestCase.html'
83
83
  Enabled: 'pending'
84
- SafeAutocorrect: false
84
+ SafeAutoCorrect: false
85
85
  VersionAdded: '2.14'
86
86
  Include:
87
87
  - '**/test/**/*.rb'
@@ -540,7 +540,7 @@ Rails/I18nLazyLookup:
540
540
  Enabled: pending
541
541
  VersionAdded: '2.14'
542
542
  Include:
543
- - 'controllers/**/*'
543
+ - 'app/controllers/**/*.rb'
544
544
 
545
545
  Rails/I18nLocaleAssignment:
546
546
  Description: 'Prefer the usage of `I18n.with_locale` instead of manually updating `I18n.locale` value.'
@@ -873,7 +873,7 @@ Rails/RootJoinChain:
873
873
  Rails/RootPathnameMethods:
874
874
  Description: 'Use `Rails.root` IO methods instead of passing it to `File`.'
875
875
  Enabled: pending
876
- SafeAutocorrect: false
876
+ SafeAutoCorrect: false
877
877
  VersionAdded: '2.16'
878
878
 
879
879
  Rails/RootPublicPath:
@@ -10,8 +10,8 @@ module RuboCop
10
10
 
11
11
  def_node_matcher :active_record?, <<~PATTERN
12
12
  {
13
- (const nil? :ApplicationRecord)
14
- (const (const nil? :ActiveRecord) :Base)
13
+ (const {nil? cbase} :ApplicationRecord)
14
+ (const (const {nil? cbase} :ActiveRecord) :Base)
15
15
  }
16
16
  PATTERN
17
17
 
@@ -134,7 +134,7 @@ module RuboCop
134
134
  end
135
135
 
136
136
  def self.from_hash_brackets_map(node, match)
137
- new(match, node.children.last, 'Hash['.length, ']'.length)
137
+ new(match, node.children.last, "#{node.receiver.source}[".length, ']'.length)
138
138
  end
139
139
 
140
140
  def strip_prefix_and_suffix(node, corrector)
@@ -8,7 +8,7 @@ module RuboCop
8
8
 
9
9
  def_node_matcher :migration_class?, <<~PATTERN
10
10
  (class
11
- (const nil? _)
11
+ (const {nil? cbase} _)
12
12
  (send
13
13
  (const (const {nil? cbase} :ActiveRecord) :Migration)
14
14
  :[]
@@ -37,14 +37,14 @@ module RuboCop
37
37
  ^(send (send nil? :flash) :[]= ...)
38
38
  PATTERN
39
39
 
40
- def_node_search :redirect_to?, <<~PATTERN
41
- (send nil? :redirect_to ...)
40
+ def_node_search :render?, <<~PATTERN
41
+ (send nil? :render ...)
42
42
  PATTERN
43
43
 
44
44
  def_node_search :action_controller?, <<~PATTERN
45
45
  {
46
- (const nil? :ApplicationController)
47
- (const (const nil? :ActionController) :Base)
46
+ (const {nil? cbase} :ApplicationController)
47
+ (const (const {nil? cbase} :ActionController) :Base)
48
48
  }
49
49
  PATTERN
50
50
 
@@ -53,7 +53,7 @@ module RuboCop
53
53
  def on_send(flash_node)
54
54
  return unless flash_assignment?(flash_node)
55
55
 
56
- return if followed_by_redirect_to?(flash_node)
56
+ return unless followed_by_render?(flash_node)
57
57
 
58
58
  return unless instance_method_or_block?(flash_node)
59
59
 
@@ -66,13 +66,18 @@ module RuboCop
66
66
 
67
67
  private
68
68
 
69
- def followed_by_redirect_to?(flash_node)
69
+ def followed_by_render?(flash_node)
70
70
  flash_assigment_node = find_ancestor(flash_node, type: :send)
71
- context = flash_assigment_node.parent
71
+ context = flash_assigment_node
72
+ if (node = context.each_ancestor(:if, :rescue).first)
73
+ context = node
74
+ elsif context.right_siblings.empty?
75
+ return true
76
+ end
77
+ context = context.right_siblings
72
78
 
73
- flash_index = context.children.index(flash_assigment_node)
74
- context.each_child_node.with_index.any? do |node, index|
75
- index > flash_index && redirect_to?(node)
79
+ context.compact.any? do |render_candidate|
80
+ render?(render_candidate)
76
81
  end
77
82
  end
78
83
 
@@ -30,7 +30,7 @@ module RuboCop
30
30
 
31
31
  def_node_matcher :action_controller_test_case?, <<~PATTERN
32
32
  (class
33
- (const nil? _)
33
+ (const {nil? cbase} _)
34
34
  (const (const {nil? cbase} :ActionController) :TestCase) _)
35
35
  PATTERN
36
36
 
@@ -6,7 +6,8 @@ module RuboCop
6
6
  # Enforces consistent ordering of the standard Rails RESTful controller actions.
7
7
  #
8
8
  # The cop is configurable and can enforce any ordering of the standard actions.
9
- # All other methods are ignored.
9
+ # All other methods are ignored. So, the actions specified in `ExpectedOrder` should be
10
+ # defined before actions not specified.
10
11
  #
11
12
  # [source,yaml]
12
13
  # ----
@@ -35,6 +36,7 @@ module RuboCop
35
36
  extend AutoCorrector
36
37
  include VisibilityHelp
37
38
  include DefNode
39
+ include RangeHelp
38
40
 
39
41
  MSG = 'Action `%<current>s` should appear before `%<previous>s`.'
40
42
 
@@ -71,9 +73,43 @@ module RuboCop
71
73
  current: current.method_name
72
74
  )
73
75
  add_offense(current, message: message) do |corrector|
74
- corrector.replace(current, previous.source)
75
- corrector.replace(previous, current.source)
76
+ current = correction_target(current)
77
+ previous = correction_target(previous)
78
+
79
+ swap_range(corrector, current, previous)
80
+ end
81
+ end
82
+
83
+ def correction_target(def_node)
84
+ range_with_comments_and_lines(def_node.each_ancestor(:if).first || def_node)
85
+ end
86
+
87
+ def add_range(range1, range2)
88
+ range1.with(
89
+ begin_pos: [range1.begin_pos, range2.begin_pos].min,
90
+ end_pos: [range1.end_pos, range2.end_pos].max
91
+ )
92
+ end
93
+
94
+ def range_with_comments(node)
95
+ ranges = [
96
+ node,
97
+ *processed_source.ast_with_comments[node]
98
+ ].map do |element|
99
+ element.location.expression
76
100
  end
101
+ ranges.reduce do |result, range|
102
+ add_range(result, range)
103
+ end
104
+ end
105
+
106
+ def range_with_comments_and_lines(node)
107
+ range_by_whole_lines(range_with_comments(node), include_final_newline: true)
108
+ end
109
+
110
+ def swap_range(corrector, range1, range2)
111
+ corrector.insert_before(range2, range1.source)
112
+ corrector.remove(range1)
77
113
  end
78
114
  end
79
115
  end
@@ -25,7 +25,7 @@ module RuboCop
25
25
 
26
26
  MSG = 'Controllers should subclass `ApplicationController`.'
27
27
  SUPERCLASS = 'ApplicationController'
28
- BASE_PATTERN = '(const (const nil? :ActionController) :Base)'
28
+ BASE_PATTERN = '(const (const {nil? cbase} :ActionController) :Base)'
29
29
 
30
30
  # rubocop:disable Layout/ClassStructure
31
31
  include RuboCop::Cop::EnforceSuperclass
@@ -28,7 +28,7 @@ module RuboCop
28
28
 
29
29
  MSG = 'Jobs should subclass `ApplicationJob`.'
30
30
  SUPERCLASS = 'ApplicationJob'
31
- BASE_PATTERN = '(const (const nil? :ActiveJob) :Base)'
31
+ BASE_PATTERN = '(const (const {nil? cbase} :ActiveJob) :Base)'
32
32
 
33
33
  # rubocop:disable Layout/ClassStructure
34
34
  include RuboCop::Cop::EnforceSuperclass
@@ -28,7 +28,7 @@ module RuboCop
28
28
 
29
29
  MSG = 'Mailers should subclass `ApplicationMailer`.'
30
30
  SUPERCLASS = 'ApplicationMailer'
31
- BASE_PATTERN = '(const (const nil? :ActionMailer) :Base)'
31
+ BASE_PATTERN = '(const (const {nil? cbase} :ActionMailer) :Base)'
32
32
 
33
33
  # rubocop:disable Layout/ClassStructure
34
34
  include RuboCop::Cop::EnforceSuperclass
@@ -29,7 +29,7 @@ module RuboCop
29
29
 
30
30
  MSG = 'Models should subclass `ApplicationRecord`.'
31
31
  SUPERCLASS = 'ApplicationRecord'
32
- BASE_PATTERN = '(const (const nil? :ActiveRecord) :Base)'
32
+ BASE_PATTERN = '(const (const {nil? cbase} :ActiveRecord) :Base)'
33
33
 
34
34
  # rubocop:disable Layout/ClassStructure
35
35
  include RuboCop::Cop::EnforceSuperclass
@@ -81,7 +81,7 @@ module RuboCop
81
81
  def allowed_name?(argument)
82
82
  return false unless argument.str_type? || argument.sym_type?
83
83
 
84
- !/^[a-zA-Z\-][a-zA-Z\-0-9]*$/.match?(argument.value)
84
+ !/^[a-zA-Z-][a-zA-Z\-0-9]*$/.match?(argument.value)
85
85
  end
86
86
 
87
87
  def correction_range(node)
@@ -24,7 +24,7 @@ module RuboCop
24
24
  TRANSLATE_METHODS = %i[translate t].freeze
25
25
 
26
26
  def_node_matcher :translate_with_scope?, <<~PATTERN
27
- (send {nil? (const nil? :I18n)} {:translate :t} ${sym_type? str_type?}
27
+ (send {nil? (const {nil? cbase} :I18n)} {:translate :t} ${sym_type? str_type?}
28
28
  (hash <$(pair (sym :scope) ${array_type? sym_type?}) ...>)
29
29
  )
30
30
  PATTERN
@@ -70,8 +70,8 @@ module RuboCop
70
70
  # @return [Boolean] true if matches
71
71
  def_node_matcher :time_current?, <<~PATTERN
72
72
  {
73
- (send (const _ :Time) :current)
74
- (send (send (const _ :Time) :zone) :now)
73
+ (send (const {nil? cbase} :Time) :current)
74
+ (send (send (const {nil? cbase} :Time) :zone) :now)
75
75
  }
76
76
  PATTERN
77
77
 
@@ -53,7 +53,7 @@ module RuboCop
53
53
  method_name = node.method_name
54
54
  static_name = static_method_name(method_name)
55
55
  return unless static_name
56
- return if node.arguments.any? { |argument| IGNORED_ARGUMENT_TYPES.include?(argument.type) }
56
+ return unless dynamic_find_by_arguments?(node)
57
57
 
58
58
  message = format(MSG, static_name: static_name, method: method_name)
59
59
  add_offense(node, message: message) do |corrector|
@@ -65,12 +65,8 @@ module RuboCop
65
65
  private
66
66
 
67
67
  def autocorrect(corrector, node)
68
- keywords = column_keywords(node.method_name)
69
-
70
- return if keywords.size != node.arguments.size
71
-
72
68
  autocorrect_method_name(corrector, node)
73
- autocorrect_argument_keywords(corrector, node, keywords)
69
+ autocorrect_argument_keywords(corrector, node, column_keywords(node.method_name))
74
70
  end
75
71
 
76
72
  def allowed_invocation?(node)
@@ -120,6 +116,20 @@ module RuboCop
120
116
 
121
117
  match[2] ? 'find_by!' : 'find_by'
122
118
  end
119
+
120
+ def dynamic_find_by_arguments?(node)
121
+ dynamic_find_by_arguments_count?(node) && dynamic_find_by_arguments_type?(node)
122
+ end
123
+
124
+ def dynamic_find_by_arguments_count?(node)
125
+ column_keywords(node.method_name).size == node.arguments.size
126
+ end
127
+
128
+ def dynamic_find_by_arguments_type?(node)
129
+ node.arguments.none? do |argument|
130
+ IGNORED_ARGUMENT_TYPES.include?(argument.type)
131
+ end
132
+ end
123
133
  end
124
134
  end
125
135
  end
@@ -34,15 +34,15 @@ module RuboCop
34
34
  RESTRICT_ON_SEND = %i[join].freeze
35
35
 
36
36
  def_node_matcher :file_join_nodes?, <<~PATTERN
37
- (send (const nil? :File) :join ...)
37
+ (send (const {nil? cbase} :File) :join ...)
38
38
  PATTERN
39
39
 
40
40
  def_node_search :rails_root_nodes?, <<~PATTERN
41
- (send (const nil? :Rails) :root)
41
+ (send (const {nil? cbase} :Rails) :root)
42
42
  PATTERN
43
43
 
44
44
  def_node_matcher :rails_root_join_nodes?, <<~PATTERN
45
- (send (send (const nil? :Rails) :root) :join ...)
45
+ (send #rails_root_nodes? :join ...)
46
46
  PATTERN
47
47
 
48
48
  def on_dstr(node)
@@ -29,17 +29,17 @@ module RuboCop
29
29
 
30
30
  MSG = 'Use `freeze_time` instead of `travel_to`.'
31
31
  NOW_METHODS = %i[now new current].freeze
32
- CONV_METHODS = %i[to_time in_time_zone].freeze
32
+ CONVERT_METHODS = %i[to_time in_time_zone].freeze
33
33
  RESTRICT_ON_SEND = %i[travel_to].freeze
34
34
 
35
35
  # @!method time_now?(node)
36
36
  def_node_matcher :time_now?, <<~PATTERN
37
- (const nil? {:Time :DateTime})
37
+ (const {nil? cbase} {:Time :DateTime})
38
38
  PATTERN
39
39
 
40
40
  # @!method zoned_time_now?(node)
41
41
  def_node_matcher :zoned_time_now?, <<~PATTERN
42
- (send (const nil? :Time) :zone)
42
+ (send (const {nil? cbase} :Time) :zone)
43
43
  PATTERN
44
44
 
45
45
  def on_send(node)
@@ -63,9 +63,11 @@ module RuboCop
63
63
  end
64
64
 
65
65
  def current_time_with_convert?(node, method_name)
66
- return false unless CONV_METHODS.include?(method_name)
66
+ return false unless CONVERT_METHODS.include?(method_name)
67
+
68
+ child_node, child_method_name, time_argument = *node.children
69
+ return if time_argument
67
70
 
68
- child_node, child_method_name = *node.children
69
71
  current_time?(child_node, child_method_name)
70
72
  end
71
73
  end
@@ -37,7 +37,7 @@ module RuboCop
37
37
  RESTRICT_ON_SEND = %i[has_many has_one].freeze
38
38
 
39
39
  def_node_search :active_resource_class?, <<~PATTERN
40
- (const (const nil? :ActiveResource) :Base)
40
+ (const (const {nil? cbase} :ActiveResource) :Base)
41
41
  PATTERN
42
42
 
43
43
  def_node_matcher :association_without_options?, <<~PATTERN
@@ -37,7 +37,7 @@ module RuboCop
37
37
  def_node_matcher :form_builder_class?, <<~PATTERN
38
38
  (const
39
39
  (const
40
- (const nil? :ActionView) :Helpers) :FormBuilder)
40
+ (const {nil? cbase} :ActionView) :Helpers) :FormBuilder)
41
41
  PATTERN
42
42
 
43
43
  def on_ivar(node)
@@ -12,7 +12,6 @@ module RuboCop
12
12
  # render plain: 'foo/bar', status: 304
13
13
  # redirect_to root_url, status: 301
14
14
  # head 200
15
- # get '/foobar', to: redirect('/foobar/baz', status: 301)
16
15
  #
17
16
  # # good
18
17
  # render :foo, status: :ok
@@ -20,7 +19,6 @@ module RuboCop
20
19
  # render plain: 'foo/bar', status: :not_modified
21
20
  # redirect_to root_url, status: :moved_permanently
22
21
  # head :ok
23
- # get '/foobar', to: redirect('/foobar/baz', status: :moved_permanently)
24
22
  #
25
23
  # @example EnforcedStyle: numeric
26
24
  # # bad
@@ -29,7 +27,6 @@ module RuboCop
29
27
  # render plain: 'foo/bar', status: :not_modified
30
28
  # redirect_to root_url, status: :moved_permanently
31
29
  # head :ok
32
- # get '/foobar', to: redirect('/foobar/baz', status: :moved_permanently)
33
30
  #
34
31
  # # good
35
32
  # render :foo, status: 200
@@ -37,20 +34,18 @@ module RuboCop
37
34
  # render plain: 'foo/bar', status: 304
38
35
  # redirect_to root_url, status: 301
39
36
  # head 200
40
- # get '/foobar', to: redirect('/foobar/baz', status: 301)
41
37
  #
42
38
  class HttpStatus < Base
43
39
  include ConfigurableEnforcedStyle
44
40
  extend AutoCorrector
45
41
 
46
- RESTRICT_ON_SEND = %i[render redirect_to head redirect].freeze
42
+ RESTRICT_ON_SEND = %i[render redirect_to head].freeze
47
43
 
48
44
  def_node_matcher :http_status, <<~PATTERN
49
45
  {
50
46
  (send nil? {:render :redirect_to} _ $hash)
51
47
  (send nil? {:render :redirect_to} $hash)
52
48
  (send nil? :head ${int sym} ...)
53
- (send nil? :redirect _ $hash)
54
49
  }
55
50
  PATTERN
56
51
 
@@ -34,6 +34,8 @@ module RuboCop
34
34
 
35
35
  MSG = 'Use "lazy" lookup for the text used in controllers.'
36
36
 
37
+ RESTRICT_ON_SEND = %i[translate t].freeze
38
+
37
39
  def_node_matcher :translate_call?, <<~PATTERN
38
40
  (send nil? {:translate :t} ${sym_type? str_type?} ...)
39
41
  PATTERN
@@ -69,7 +69,7 @@ module RuboCop
69
69
  class I18nLocaleTexts < Base
70
70
  MSG = 'Move locale texts to the locale files in the `config/locales` directory.'
71
71
 
72
- RESTRICT_ON_SEND = %i[validates redirect_to []= mail].freeze
72
+ RESTRICT_ON_SEND = %i[validates redirect_to redirect_back []= mail].freeze
73
73
 
74
74
  def_node_search :validation_message, <<~PATTERN
75
75
  (pair (sym :message) $str)
@@ -94,7 +94,7 @@ module RuboCop
94
94
  add_offense(text_node)
95
95
  end
96
96
  return
97
- when :redirect_to
97
+ when :redirect_to, :redirect_back
98
98
  text_node = redirect_to_flash(node).to_a.last
99
99
  when :[]=
100
100
  text_node = flash_assignment?(node)
@@ -46,7 +46,7 @@ module RuboCop
46
46
 
47
47
  def_node_matcher :on_bad_hash_brackets_map, <<~PATTERN
48
48
  (send
49
- (const _ :Hash)
49
+ (const {nil? cbase} :Hash)
50
50
  :[]
51
51
  (block
52
52
  (call _ {:map :collect})
@@ -49,7 +49,7 @@ module RuboCop
49
49
 
50
50
  def_node_matcher :on_bad_hash_brackets_map, <<~PATTERN
51
51
  (send
52
- (const _ :Hash)
52
+ (const {nil? cbase} :Hash)
53
53
  :[]
54
54
  (block
55
55
  (call _ {:map :collect})
@@ -34,8 +34,8 @@ module RuboCop
34
34
 
35
35
  def_node_matcher :mailer_base_class?, <<~PATTERN
36
36
  {
37
- (const (const nil? :ActionMailer) :Base)
38
- (const nil? :ApplicationMailer)
37
+ (const (const {nil? cbase} :ActionMailer) :Base)
38
+ (const {nil? cbase} :ApplicationMailer)
39
39
  }
40
40
  PATTERN
41
41
 
@@ -44,7 +44,7 @@ module RuboCop
44
44
  PATTERN
45
45
 
46
46
  def_node_matcher :class_new_definition?, <<~PATTERN
47
- (send (const nil? :Class) :new #mailer_base_class?)
47
+ (send (const {nil? cbase} :Class) :new #mailer_base_class?)
48
48
  PATTERN
49
49
 
50
50
  def on_class(node)
@@ -29,7 +29,7 @@ module RuboCop
29
29
 
30
30
  basename = basename_without_timestamp_and_suffix(processed_source.file_path)
31
31
 
32
- class_identifier = node.identifier
32
+ class_identifier = node.identifier.location.name
33
33
  camelized_basename = camelize(basename)
34
34
  return if class_identifier.source.casecmp(camelized_basename).zero?
35
35
 
@@ -32,7 +32,7 @@ module RuboCop
32
32
  (send
33
33
  {
34
34
  (gvar #match_gvar?)
35
- {(const nil? :STDOUT) (const nil? :STDERR)}
35
+ (const {nil? cbase} {:STDOUT :STDERR})
36
36
  }
37
37
  {:binwrite :syswrite :write :write_nonblock}
38
38
  ...)
@@ -26,34 +26,53 @@ module RuboCop
26
26
  minimum_target_rails_version 5.0
27
27
 
28
28
  def_node_matcher :pluck_candidate?, <<~PATTERN
29
- ({block numblock} (send _ {:map :collect}) $_argument (send (lvar $_element) :[] $_key))
29
+ ({block numblock} (send _ {:map :collect}) $_argument (send lvar :[] $_key))
30
30
  PATTERN
31
31
 
32
32
  def on_block(node)
33
- pluck_candidate?(node) do |argument, element, key|
33
+ pluck_candidate?(node) do |argument, key|
34
+ next unless use_one_block_argument?(argument)
35
+
34
36
  match = if node.block_type?
35
- argument.children.first.source.to_sym == element
37
+ block_argument = argument.children.first.source
38
+ use_block_argument_in_key?(block_argument, key)
36
39
  else # numblock
37
- argument == 1 && element == :_1
40
+ argument == 1 && use_block_argument_in_key?('_1', key)
38
41
  end
39
42
  next unless match
40
43
 
41
- replacement = "pluck(#{key.source})"
42
- message = message(replacement, node)
43
-
44
- add_offense(offense_range(node), message: message) do |corrector|
45
- corrector.replace(offense_range(node), replacement)
46
- end
44
+ register_offense(node, key)
47
45
  end
48
46
  end
49
47
  alias on_numblock on_block
50
48
 
51
49
  private
52
50
 
51
+ def use_one_block_argument?(argument)
52
+ return true if argument == 1 # Checks for numbered argument `_1`.
53
+
54
+ argument.respond_to?(:one?) && argument.one?
55
+ end
56
+
57
+ def use_block_argument_in_key?(block_argument, key)
58
+ return false if block_argument == key.source
59
+
60
+ key.each_descendant(:lvar).none? { |lvar| block_argument == lvar.source }
61
+ end
62
+
53
63
  def offense_range(node)
54
64
  node.send_node.loc.selector.join(node.loc.end)
55
65
  end
56
66
 
67
+ def register_offense(node, key)
68
+ replacement = "pluck(#{key.source})"
69
+ message = message(replacement, node)
70
+
71
+ add_offense(offense_range(node), message: message) do |corrector|
72
+ corrector.replace(offense_range(node), replacement)
73
+ end
74
+ end
75
+
57
76
  def message(replacement, node)
58
77
  current = offense_range(node).source
59
78
 
@@ -26,7 +26,7 @@ module RuboCop
26
26
  RESTRICT_ON_SEND = %i[require_dependency].freeze
27
27
 
28
28
  def_node_matcher :require_dependency_call?, <<~PATTERN
29
- (send {nil? (const _ :Kernel)} :require_dependency _)
29
+ (send {nil? (const {nil? cbase} :Kernel)} :require_dependency _)
30
30
  PATTERN
31
31
 
32
32
  def on_send(node)
@@ -163,12 +163,11 @@ module RuboCop
163
163
  def on_send(node)
164
164
  evidence(node) do |method, path, args, rails_root|
165
165
  add_offense(node, message: format(MSG, method: method, rails_root: rails_root.source)) do |corrector|
166
- if dir_glob?(node)
167
- replacement = build_path_glob(path, method)
168
- else
169
- replacement = "#{path.source}.#{method}"
170
- replacement += "(#{args.map(&:source).join(', ')})" unless args.empty?
171
- end
166
+ replacement = if dir_glob?(node)
167
+ build_path_glob_replacement(path, method)
168
+ else
169
+ build_path_replacement(path, method, args)
170
+ end
172
171
 
173
172
  corrector.replace(node, replacement)
174
173
  end
@@ -184,18 +183,26 @@ module RuboCop
184
183
  yield(method, path, args, rails_root)
185
184
  end
186
185
 
187
- def build_path_glob(path, method)
186
+ def build_path_glob_replacement(path, method)
188
187
  receiver = range_between(path.loc.expression.begin_pos, path.children.first.loc.selector.end_pos).source
189
188
 
190
- argument = if path.arguments.one?
191
- path.first_argument.source
192
- else
193
- join_arguments(path.arguments)
194
- end
189
+ argument = path.arguments.one? ? path.first_argument.source : join_arguments(path.arguments)
195
190
 
196
191
  "#{receiver}.#{method}(#{argument})"
197
192
  end
198
193
 
194
+ def build_path_replacement(path, method, args)
195
+ path_replacement = path.source
196
+ if path.arguments? && !path.parenthesized_call?
197
+ path_replacement[' '] = '('
198
+ path_replacement << ')'
199
+ end
200
+
201
+ replacement = "#{path_replacement}.#{method}"
202
+ replacement += "(#{args.map(&:source).join(', ')})" unless args.empty?
203
+ replacement
204
+ end
205
+
199
206
  def include_interpolation?(arguments)
200
207
  arguments.any? do |argument|
201
208
  argument.children.any? { |child| child.respond_to?(:begin_type?) && child.begin_type? }
@@ -203,11 +210,28 @@ module RuboCop
203
210
  end
204
211
 
205
212
  def join_arguments(arguments)
206
- quote = include_interpolation?(arguments) ? '"' : "'"
207
- joined_arguments = arguments.map(&:value).join('/')
213
+ use_interpolation = false
214
+
215
+ joined_arguments = arguments.map do |arg|
216
+ if arg.respond_to?(:value)
217
+ arg.value
218
+ else
219
+ use_interpolation = true
220
+ "\#{#{arg.source}}"
221
+ end
222
+ end.join('/')
223
+ quote = enforce_double_quotes? || include_interpolation?(arguments) || use_interpolation ? '"' : "'"
208
224
 
209
225
  "#{quote}#{joined_arguments}#{quote}"
210
226
  end
227
+
228
+ def enforce_double_quotes?
229
+ string_literals_config['EnforcedStyle'] == 'double_quotes'
230
+ end
231
+
232
+ def string_literals_config
233
+ config.for_cop('Style/StringLiterals')
234
+ end
211
235
  end
212
236
  end
213
237
  end
@@ -49,7 +49,7 @@ module RuboCop
49
49
  RESTRICT_ON_SEND = PREFERRED_METHODS.keys.freeze
50
50
 
51
51
  def_node_matcher :long_i18n?, <<~PATTERN
52
- (send {nil? (const nil? :I18n)} ${:translate :localize} ...)
52
+ (send {nil? (const {nil? cbase} :I18n)} ${:translate :localize} ...)
53
53
  PATTERN
54
54
 
55
55
  def on_send(node)
@@ -57,7 +57,7 @@ module RuboCop
57
57
 
58
58
  def_node_matcher :good_touch?, <<~PATTERN
59
59
  {
60
- (send (const nil? :FileUtils) :touch ...)
60
+ (send (const {nil? cbase} :FileUtils) :touch ...)
61
61
  (send _ :touch {true false})
62
62
  }
63
63
  PATTERN
@@ -228,12 +228,25 @@ module RuboCop
228
228
  acceptable
229
229
  end
230
230
 
231
- # Time.new can be called with a time zone offset
231
+ # Time.new, Time.at, and Time.now can be called with a time zone offset
232
232
  # When it is, that should be considered safe
233
233
  # Example:
234
234
  # Time.new(1988, 3, 15, 3, 0, 0, "-05:00")
235
235
  def offset_provided?(node)
236
- node.arguments.size >= 7
236
+ case node.method_name
237
+ when :new
238
+ node.arguments.size == 7 || offset_option_provided?(node)
239
+ when :at, :now
240
+ offset_option_provided?(node)
241
+ end
242
+ end
243
+
244
+ def offset_option_provided?(node)
245
+ options = node.last_argument
246
+ options&.hash_type? &&
247
+ options.each_pair.any? do |pair|
248
+ pair.key.sym_type? && pair.key.value == :in && !pair.value.nil_type?
249
+ end
237
250
  end
238
251
  end
239
252
  end
@@ -23,7 +23,7 @@ module RuboCop
23
23
  RESTRICT_ON_SEND = %i[zone=].freeze
24
24
 
25
25
  def_node_matcher :time_zone_assignment?, <<~PATTERN
26
- (send (const nil? :Time) :zone= ...)
26
+ (send (const {nil? cbase} :Time) :zone= ...)
27
27
  PATTERN
28
28
 
29
29
  def on_send(node)
@@ -21,6 +21,37 @@ module RuboCop
21
21
  extend AutoCorrector
22
22
  extend TargetRailsVersion
23
23
 
24
+ # These types are defined by the following files in ActiveSupport:
25
+ # lib/active_support/core_ext/array/conversions.rb
26
+ # lib/active_support/core_ext/date/conversions.rb
27
+ # lib/active_support/core_ext/date_time/conversions.rb
28
+ # lib/active_support/core_ext/numeric/conversions.rb
29
+ # lib/active_support/core_ext/range/conversions.rb
30
+ # lib/active_support/core_ext/time/conversions.rb
31
+ # lib/active_support/time_with_zone.rb
32
+ EXTENDED_FORMAT_TYPES = Set.new(
33
+ %i[
34
+ currency
35
+ db
36
+ delimited
37
+ human
38
+ human_size
39
+ inspect
40
+ iso8601
41
+ long
42
+ long_ordinal
43
+ nsec
44
+ number
45
+ percentage
46
+ phone
47
+ rfc822
48
+ rounded
49
+ short
50
+ time
51
+ usec
52
+ ]
53
+ )
54
+
24
55
  MSG = 'Use `to_formatted_s` instead.'
25
56
 
26
57
  RESTRICT_ON_SEND = %i[to_s].freeze
@@ -28,13 +59,19 @@ module RuboCop
28
59
  minimum_target_rails_version 7.0
29
60
 
30
61
  def on_send(node)
31
- return if node.arguments.empty?
62
+ return unless rails_extended_to_s?(node)
32
63
 
33
64
  add_offense(node.loc.selector) do |corrector|
34
65
  corrector.replace(node.loc.selector, 'to_formatted_s')
35
66
  end
36
67
  end
37
68
  alias on_csend on_send
69
+
70
+ private
71
+
72
+ def rails_extended_to_s?(node)
73
+ node.first_argument&.sym_type? && EXTENDED_FORMAT_TYPES.include?(node.first_argument.value)
74
+ end
38
75
  end
39
76
  end
40
77
  end
@@ -139,11 +139,20 @@ module RuboCop
139
139
  pairs = node.arguments.last
140
140
  return unless pairs.hash_type?
141
141
 
142
+ return true if condition_hash_part?(pairs, keys: %i[if unless])
143
+
144
+ uniqueness_node = uniqueness_part(node)
145
+ return unless uniqueness_node&.hash_type?
146
+
147
+ condition_hash_part?(uniqueness_node, keys: %i[if unless conditions])
148
+ end
149
+
150
+ def condition_hash_part?(pairs, keys:)
142
151
  pairs.each_pair.any? do |pair|
143
152
  key = pair.key
144
153
  next unless key.sym_type?
145
154
 
146
- key.value == :if || key.value == :unless
155
+ keys.include?(key.value)
147
156
  end
148
157
  end
149
158
 
@@ -38,7 +38,9 @@ module RuboCop
38
38
  def on_send(node)
39
39
  return unless node.first_argument.sym_type?
40
40
 
41
- where_node_and_argument(root_receiver(node)) do |where_node, where_argument|
41
+ root_receiver = root_receiver(node)
42
+ where_node_and_argument(root_receiver) do |where_node, where_argument|
43
+ next unless root_receiver == root_receiver(where_node)
42
44
  next unless same_relationship?(where_argument, node.first_argument)
43
45
 
44
46
  range = range_between(node.loc.selector.begin_pos, node.loc.expression.end_pos)
@@ -50,7 +52,12 @@ module RuboCop
50
52
  private
51
53
 
52
54
  def root_receiver(node)
53
- node&.parent&.send_type? ? root_receiver(node.parent) : node
55
+ parent = node.parent
56
+ if !parent&.send_type? || parent.method?(:or) || parent.method?(:and)
57
+ node
58
+ else
59
+ root_receiver(parent)
60
+ end
54
61
  end
55
62
 
56
63
  def same_relationship?(where, left_joins)
@@ -45,7 +45,7 @@ module RuboCop
45
45
 
46
46
  def multiple_arguments_hash?(hash)
47
47
  return true if hash.pairs.size >= 2
48
- return false unless hash.values[0].hash_type?
48
+ return false unless hash.values[0]&.hash_type?
49
49
 
50
50
  multiple_arguments_hash?(hash.values[0])
51
51
  end
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Rails
5
5
  # This module holds the RuboCop Rails version information.
6
6
  module Version
7
- STRING = '2.17.0'
7
+ STRING = '2.17.4'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.17.0
4
+ version: 2.17.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2022-10-22 00:00:00.000000000 Z
13
+ date: 2022-12-25 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -235,7 +235,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
235
235
  - !ruby/object:Gem::Version
236
236
  version: '0'
237
237
  requirements: []
238
- rubygems_version: 3.2.22
238
+ rubygems_version: 3.3.26
239
239
  signing_key:
240
240
  specification_version: 4
241
241
  summary: Automatic Rails code style checking tool.