rubocop-rails 2.6.0 → 2.7.0
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.
- checksums.yaml +4 -4
- data/config/default.yml +112 -3
- data/lib/rubocop/cop/mixin/active_record_helper.rb +7 -0
- data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +145 -0
- data/lib/rubocop/cop/rails/content_tag.rb +8 -21
- data/lib/rubocop/cop/rails/default_scope.rb +54 -0
- data/lib/rubocop/cop/rails/delegate.rb +1 -1
- data/lib/rubocop/cop/rails/file_path.rb +1 -1
- data/lib/rubocop/cop/rails/find_by_id.rb +103 -0
- data/lib/rubocop/cop/rails/inquiry.rb +34 -0
- data/lib/rubocop/cop/rails/link_to_blank.rb +2 -0
- data/lib/rubocop/cop/rails/mailer_name.rb +80 -0
- data/lib/rubocop/cop/rails/match_route.rb +117 -0
- data/lib/rubocop/cop/rails/negate_include.rb +39 -0
- data/lib/rubocop/cop/rails/pick.rb +7 -3
- data/lib/rubocop/cop/rails/pluck.rb +59 -0
- data/lib/rubocop/cop/rails/pluck_id.rb +58 -0
- data/lib/rubocop/cop/rails/pluck_in_where.rb +36 -0
- data/lib/rubocop/cop/rails/render_inline.rb +48 -0
- data/lib/rubocop/cop/rails/render_plain_text.rb +76 -0
- data/lib/rubocop/cop/rails/safe_navigation.rb +1 -1
- data/lib/rubocop/cop/rails/short_i18n.rb +76 -0
- data/lib/rubocop/cop/rails/skips_model_validations.rb +42 -7
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +4 -4
- data/lib/rubocop/cop/rails/where_exists.rb +68 -0
- data/lib/rubocop/cop/rails_cops.rb +14 -0
- data/lib/rubocop/rails/version.rb +1 -1
- metadata +19 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a83fc9ded75b4055903b32234c6c9b878e76b22ee64b7e0d88cf2f54ac690d5
|
4
|
+
data.tar.gz: cb9cbde85b8117645e16c66d1670cffc61893eba8b5d5e3f530e7e42d81b147f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9df513d510b7a71d81bcf655088528ca642599702eca3f20d83eac18adcc15a3d2ab277760b78b9f86fc90df474409eaee454c6857623a2f459304fb74fdde00
|
7
|
+
data.tar.gz: 252a9b09c51b33b4ff53454fcc6ccaec41e719c283939475e643074305221a33652a9ab5649b9b2c51f36784bb67eeef5dbceee9b3852de936fed78073241dc0
|
data/config/default.yml
CHANGED
@@ -37,6 +37,14 @@ Rails/ActiveRecordAliases:
|
|
37
37
|
VersionAdded: '0.53'
|
38
38
|
SafeAutoCorrect: false
|
39
39
|
|
40
|
+
Rails/ActiveRecordCallbacksOrder:
|
41
|
+
Description: 'Order callback declarations in the order in which they will be executed.'
|
42
|
+
StyleGuide: 'https://rails.rubystyle.guide/#callbacks-order'
|
43
|
+
Enabled: 'pending'
|
44
|
+
VersionAdded: '2.7'
|
45
|
+
Include:
|
46
|
+
- app/models/**/*.rb
|
47
|
+
|
40
48
|
Rails/ActiveRecordOverride:
|
41
49
|
Description: >-
|
42
50
|
Check for overriding Active Record methods instead of using
|
@@ -153,6 +161,12 @@ Rails/Date:
|
|
153
161
|
- strict
|
154
162
|
- flexible
|
155
163
|
|
164
|
+
Rails/DefaultScope:
|
165
|
+
Description: 'Avoid use of `default_scope`.'
|
166
|
+
StyleGuide: 'https://rails.rubystyle.guide#avoid-default-scope'
|
167
|
+
Enabled: false
|
168
|
+
VersionAdded: '2.7'
|
169
|
+
|
156
170
|
Rails/Delegate:
|
157
171
|
Description: 'Prefer delegate method for delegations.'
|
158
172
|
Enabled: true
|
@@ -234,6 +248,14 @@ Rails/FindBy:
|
|
234
248
|
Include:
|
235
249
|
- app/models/**/*.rb
|
236
250
|
|
251
|
+
Rails/FindById:
|
252
|
+
Description: >-
|
253
|
+
Favor the use of `find` over `where.take!`, `find_by!`, and `find_by_id!` when you
|
254
|
+
need to retrieve a single record by primary key when you expect it to be found.
|
255
|
+
StyleGuide: 'https://rails.rubystyle.guide/#find'
|
256
|
+
Enabled: 'pending'
|
257
|
+
VersionAdded: '2.7'
|
258
|
+
|
237
259
|
Rails/FindEach:
|
238
260
|
Description: 'Prefer all.find_each over all.find.'
|
239
261
|
StyleGuide: 'https://rails.rubystyle.guide#find-each'
|
@@ -300,6 +322,12 @@ Rails/IndexWith:
|
|
300
322
|
Enabled: true
|
301
323
|
VersionAdded: '2.5'
|
302
324
|
|
325
|
+
Rails/Inquiry:
|
326
|
+
Description: "Prefer Ruby's comparison operators over Active Support's `Array#inquiry` and `String#inquiry`."
|
327
|
+
StyleGuide: 'https://rails.rubystyle.guide/#inquiry'
|
328
|
+
Enabled: 'pending'
|
329
|
+
VersionAdded: '2.7'
|
330
|
+
|
303
331
|
Rails/InverseOf:
|
304
332
|
Description: 'Checks for associations where the inverse cannot be determined automatically.'
|
305
333
|
Enabled: true
|
@@ -325,6 +353,31 @@ Rails/LinkToBlank:
|
|
325
353
|
Enabled: true
|
326
354
|
VersionAdded: '0.62'
|
327
355
|
|
356
|
+
Rails/MailerName:
|
357
|
+
Description: 'Mailer should end with `Mailer` suffix.'
|
358
|
+
StyleGuide: 'https://rails.rubystyle.guide/#mailer-name'
|
359
|
+
Enabled: 'pending'
|
360
|
+
VersionAdded: '2.7'
|
361
|
+
Include:
|
362
|
+
- app/mailers/**/*.rb
|
363
|
+
|
364
|
+
Rails/MatchRoute:
|
365
|
+
Description: >-
|
366
|
+
Don't use `match` to define any routes unless there is a need to map multiple request types
|
367
|
+
among [:get, :post, :patch, :put, :delete] to a single action using the `:via` option.
|
368
|
+
StyleGuide: 'https://rails.rubystyle.guide/#no-match-routes'
|
369
|
+
Enabled: 'pending'
|
370
|
+
VersionAdded: '2.7'
|
371
|
+
Include:
|
372
|
+
- config/routes.rb
|
373
|
+
- config/routes/**/*.rb
|
374
|
+
|
375
|
+
Rails/NegateInclude:
|
376
|
+
Description: 'Prefer `collection.exclude?(obj)` over `!collection.include?(obj)`.'
|
377
|
+
StyleGuide: 'https://rails.rubystyle.guide#exclude'
|
378
|
+
Enabled: 'pending'
|
379
|
+
VersionAdded: '2.7'
|
380
|
+
|
328
381
|
Rails/NotNullColumn:
|
329
382
|
Description: 'Do not add a NOT NULL column without a default value.'
|
330
383
|
Enabled: true
|
@@ -350,10 +403,30 @@ Rails/OutputSafety:
|
|
350
403
|
|
351
404
|
Rails/Pick:
|
352
405
|
Description: 'Prefer `pick` over `pluck(...).first`.'
|
406
|
+
StyleGuide: 'https://rails.rubystyle.guide#pick'
|
353
407
|
Enabled: true
|
354
408
|
Safe: false
|
355
409
|
VersionAdded: '2.6'
|
356
410
|
|
411
|
+
Rails/Pluck:
|
412
|
+
Description: 'Prefer `pluck` over `map { ... }`.'
|
413
|
+
StyleGuide: 'https://rails.rubystyle.guide#pluck'
|
414
|
+
Enabled: 'pending'
|
415
|
+
VersionAdded: '2.7'
|
416
|
+
|
417
|
+
Rails/PluckId:
|
418
|
+
Description: 'Use `ids` instead of `pluck(:id)` or `pluck(primary_key)`.'
|
419
|
+
StyleGuide: 'https://rails.rubystyle.guide/#ids'
|
420
|
+
Enabled: 'pending'
|
421
|
+
Safe: false
|
422
|
+
VersionAdded: '2.7'
|
423
|
+
|
424
|
+
Rails/PluckInWhere:
|
425
|
+
Description: 'Use `select` instead of `pluck` in `where` query methods.'
|
426
|
+
Enabled: 'pending'
|
427
|
+
Safe: false
|
428
|
+
VersionAdded: '2.7'
|
429
|
+
|
357
430
|
Rails/PluralizationGrammar:
|
358
431
|
Description: 'Checks for incorrect grammar when using methods like `3.day.ago`.'
|
359
432
|
Enabled: true
|
@@ -441,6 +514,20 @@ Rails/RelativeDateConstant:
|
|
441
514
|
VersionChanged: '0.59'
|
442
515
|
AutoCorrect: false
|
443
516
|
|
517
|
+
Rails/RenderInline:
|
518
|
+
Description: 'Prefer using a template over inline rendering.'
|
519
|
+
StyleGuide: 'https://rails.rubystyle.guide/#inline-rendering'
|
520
|
+
Enabled: 'pending'
|
521
|
+
VersionAdded: '2.7'
|
522
|
+
|
523
|
+
Rails/RenderPlainText:
|
524
|
+
Description: 'Prefer `render plain:` over `render text:`.'
|
525
|
+
StyleGuide: 'https://rails.rubystyle.guide/#plain-text-rendering'
|
526
|
+
Enabled: 'pending'
|
527
|
+
VersionAdded: '2.7'
|
528
|
+
# Convert only when `content_type` is explicitly set to `text/plain`.
|
529
|
+
ContentTypeCompatibility: true
|
530
|
+
|
444
531
|
Rails/RequestReferer:
|
445
532
|
Description: 'Use consistent syntax for request.referer.'
|
446
533
|
Enabled: true
|
@@ -498,6 +585,16 @@ Rails/ScopeArgs:
|
|
498
585
|
Include:
|
499
586
|
- app/models/**/*.rb
|
500
587
|
|
588
|
+
Rails/ShortI18n:
|
589
|
+
Description: 'Use the short form of the I18n methods: `t` instead of `translate` and `l` instead of `localize`.'
|
590
|
+
StyleGuide: 'https://rails.rubystyle.guide/#short-i18n'
|
591
|
+
Enabled: 'pending'
|
592
|
+
VersionAdded: '2.7'
|
593
|
+
EnforcedStyle: conservative
|
594
|
+
SupportedStyles:
|
595
|
+
- conservative
|
596
|
+
- aggressive
|
597
|
+
|
501
598
|
Rails/SkipsModelValidations:
|
502
599
|
Description: >-
|
503
600
|
Use methods that skips model validations with caution.
|
@@ -505,20 +602,27 @@ Rails/SkipsModelValidations:
|
|
505
602
|
Reference: 'https://guides.rubyonrails.org/active_record_validations.html#skipping-validations'
|
506
603
|
Enabled: true
|
507
604
|
VersionAdded: '0.47'
|
508
|
-
VersionChanged: '
|
509
|
-
|
605
|
+
VersionChanged: '2.7'
|
606
|
+
ForbiddenMethods:
|
510
607
|
- decrement!
|
511
608
|
- decrement_counter
|
512
609
|
- increment!
|
513
610
|
- increment_counter
|
611
|
+
- insert
|
612
|
+
- insert!
|
613
|
+
- insert_all
|
614
|
+
- insert_all!
|
514
615
|
- toggle!
|
515
616
|
- touch
|
617
|
+
- touch_all
|
516
618
|
- update_all
|
517
619
|
- update_attribute
|
518
620
|
- update_column
|
519
621
|
- update_columns
|
520
622
|
- update_counters
|
521
|
-
|
623
|
+
- upsert
|
624
|
+
- upsert_all
|
625
|
+
AllowedMethods: []
|
522
626
|
|
523
627
|
Rails/TimeZone:
|
524
628
|
Description: 'Checks the correct usage of time zone aware methods.'
|
@@ -570,6 +674,11 @@ Rails/Validation:
|
|
570
674
|
Include:
|
571
675
|
- app/models/**/*.rb
|
572
676
|
|
677
|
+
Rails/WhereExists:
|
678
|
+
Description: 'Prefer `exists?(...)` over `where(...).exists?`.'
|
679
|
+
Enabled: 'pending'
|
680
|
+
VersionAdded: '2.7'
|
681
|
+
|
573
682
|
# Accept `redirect_to(...) and return` and similar cases.
|
574
683
|
Style/AndOr:
|
575
684
|
EnforcedStyle: conditionals
|
@@ -6,6 +6,8 @@ module RuboCop
|
|
6
6
|
module ActiveRecordHelper
|
7
7
|
extend NodePattern::Macros
|
8
8
|
|
9
|
+
WHERE_METHODS = %i[where rewhere].freeze
|
10
|
+
|
9
11
|
def_node_search :find_set_table_name, <<~PATTERN
|
10
12
|
(send self :table_name= {str sym})
|
11
13
|
PATTERN
|
@@ -72,6 +74,11 @@ module RuboCop
|
|
72
74
|
break pair.value.value.to_s
|
73
75
|
end
|
74
76
|
end
|
77
|
+
|
78
|
+
def in_where?(node)
|
79
|
+
send_node = node.each_ancestor(:send).first
|
80
|
+
send_node && WHERE_METHODS.include?(send_node.method_name)
|
81
|
+
end
|
75
82
|
end
|
76
83
|
end
|
77
84
|
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks that Active Record callbacks are declared
|
7
|
+
# in the order in which they will be executed.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# class Person < ApplicationRecord
|
12
|
+
# after_commit :after_commit_callback
|
13
|
+
# before_validation :before_validation_callback
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# class Person < ApplicationRecord
|
18
|
+
# before_validation :before_validation_callback
|
19
|
+
# after_commit :after_commit_callback
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
class ActiveRecordCallbacksOrder < Cop
|
23
|
+
MSG = '`%<current>s` is supposed to appear before `%<previous>s`.'
|
24
|
+
|
25
|
+
CALLBACKS_IN_ORDER = %i[
|
26
|
+
after_initialize
|
27
|
+
before_validation
|
28
|
+
after_validation
|
29
|
+
before_save
|
30
|
+
around_save
|
31
|
+
before_create
|
32
|
+
around_create
|
33
|
+
after_create
|
34
|
+
before_update
|
35
|
+
around_update
|
36
|
+
after_update
|
37
|
+
before_destroy
|
38
|
+
around_destroy
|
39
|
+
after_destroy
|
40
|
+
after_save
|
41
|
+
after_commit
|
42
|
+
after_rollback
|
43
|
+
after_find
|
44
|
+
after_touch
|
45
|
+
].freeze
|
46
|
+
|
47
|
+
CALLBACKS_ORDER_MAP = Hash[
|
48
|
+
CALLBACKS_IN_ORDER.map.with_index { |name, index| [name, index] }
|
49
|
+
].freeze
|
50
|
+
|
51
|
+
def on_class(class_node)
|
52
|
+
previous_index = -1
|
53
|
+
previous_callback = nil
|
54
|
+
|
55
|
+
defined_callbacks(class_node).each do |node|
|
56
|
+
callback = node.method_name
|
57
|
+
index = CALLBACKS_ORDER_MAP[callback]
|
58
|
+
|
59
|
+
if index < previous_index
|
60
|
+
message = format(MSG, current: callback,
|
61
|
+
previous: previous_callback)
|
62
|
+
add_offense(node, message: message)
|
63
|
+
end
|
64
|
+
previous_index = index
|
65
|
+
previous_callback = callback
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Autocorrect by swapping between two nodes autocorrecting them
|
70
|
+
def autocorrect(node)
|
71
|
+
previous = left_siblings_of(node).find do |sibling|
|
72
|
+
callback?(sibling)
|
73
|
+
end
|
74
|
+
|
75
|
+
current_range = source_range_with_comment(node)
|
76
|
+
previous_range = source_range_with_comment(previous)
|
77
|
+
|
78
|
+
lambda do |corrector|
|
79
|
+
corrector.insert_before(previous_range, current_range.source)
|
80
|
+
corrector.remove(current_range)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def defined_callbacks(class_node)
|
87
|
+
class_def = class_node.body
|
88
|
+
|
89
|
+
if class_def
|
90
|
+
class_def.each_child_node.select { |c| callback?(c) }
|
91
|
+
else
|
92
|
+
[]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def callback?(node)
|
97
|
+
node.send_type? && CALLBACKS_ORDER_MAP.key?(node.method_name)
|
98
|
+
end
|
99
|
+
|
100
|
+
def left_siblings_of(node)
|
101
|
+
siblings_of(node)[0, node.sibling_index]
|
102
|
+
end
|
103
|
+
|
104
|
+
def siblings_of(node)
|
105
|
+
node.parent.children
|
106
|
+
end
|
107
|
+
|
108
|
+
def source_range_with_comment(node)
|
109
|
+
begin_pos = begin_pos_with_comment(node)
|
110
|
+
end_pos = end_position_for(node)
|
111
|
+
|
112
|
+
Parser::Source::Range.new(buffer, begin_pos, end_pos)
|
113
|
+
end
|
114
|
+
|
115
|
+
def end_position_for(node)
|
116
|
+
end_line = buffer.line_for_position(node.loc.expression.end_pos)
|
117
|
+
buffer.line_range(end_line).end_pos
|
118
|
+
end
|
119
|
+
|
120
|
+
def begin_pos_with_comment(node)
|
121
|
+
annotation_line = node.first_line - 1
|
122
|
+
first_comment = nil
|
123
|
+
|
124
|
+
processed_source.comments_before_line(annotation_line)
|
125
|
+
.reverse_each do |comment|
|
126
|
+
if comment.location.line == annotation_line
|
127
|
+
first_comment = comment
|
128
|
+
annotation_line -= 1
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
start_line_position(first_comment || node)
|
133
|
+
end
|
134
|
+
|
135
|
+
def start_line_position(node)
|
136
|
+
buffer.line_range(node.loc.line).begin_pos - 1
|
137
|
+
end
|
138
|
+
|
139
|
+
def buffer
|
140
|
+
processed_source.buffer
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -40,8 +40,12 @@ module RuboCop
|
|
40
40
|
def autocorrect(node)
|
41
41
|
lambda do |corrector|
|
42
42
|
if method_name?(node.first_argument)
|
43
|
-
|
44
|
-
|
43
|
+
range = correction_range(node)
|
44
|
+
|
45
|
+
rest_args = node.arguments.drop(1)
|
46
|
+
replacement = "tag.#{node.first_argument.value}(#{rest_args.map(&:source).join(', ')})"
|
47
|
+
|
48
|
+
corrector.replace(range, replacement)
|
45
49
|
else
|
46
50
|
corrector.replace(node.loc.selector, 'tag')
|
47
51
|
end
|
@@ -56,25 +60,8 @@ module RuboCop
|
|
56
60
|
/^[a-zA-Z_][a-zA-Z_0-9]*$/.match?(node.value)
|
57
61
|
end
|
58
62
|
|
59
|
-
def
|
60
|
-
|
61
|
-
node.loc.selector,
|
62
|
-
"tag.#{node.first_argument.value}"
|
63
|
-
)
|
64
|
-
end
|
65
|
-
|
66
|
-
def remove_first_argument(corrector, node)
|
67
|
-
if node.arguments.length > 1
|
68
|
-
corrector.remove(
|
69
|
-
range_between(child_node_beg(node, 0), child_node_beg(node, 1))
|
70
|
-
)
|
71
|
-
elsif node.arguments.length == 1
|
72
|
-
corrector.remove(node.arguments[0].loc.expression)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def child_node_beg(node, index)
|
77
|
-
node.arguments[index].loc.expression.begin_pos
|
63
|
+
def correction_range(node)
|
64
|
+
range_between(node.loc.selector.begin_pos, node.loc.expression.end_pos)
|
78
65
|
end
|
79
66
|
end
|
80
67
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop looks for uses of `default_scope`.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# default_scope -> { where(hidden: false) }
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# scope :published, -> { where(hidden: false) }
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# def self.default_scope
|
17
|
+
# where(hidden: false)
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# def self.published
|
22
|
+
# where(hidden: false)
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
class DefaultScope < Cop
|
26
|
+
MSG = 'Avoid use of `default_scope`. It is better to use explicitly named scopes.'
|
27
|
+
|
28
|
+
def_node_matcher :method_call?, <<~PATTERN
|
29
|
+
(send nil? :default_scope ...)
|
30
|
+
PATTERN
|
31
|
+
|
32
|
+
def_node_matcher :class_method_definition?, <<~PATTERN
|
33
|
+
(defs _ :default_scope args ...)
|
34
|
+
PATTERN
|
35
|
+
|
36
|
+
def_node_matcher :eigenclass_method_definition?, <<~PATTERN
|
37
|
+
(sclass (self) $(def :default_scope args ...))
|
38
|
+
PATTERN
|
39
|
+
|
40
|
+
def on_send(node)
|
41
|
+
add_offense(node, location: :selector) if method_call?(node)
|
42
|
+
end
|
43
|
+
|
44
|
+
def on_defs(node)
|
45
|
+
add_offense(node, location: :name) if class_method_definition?(node)
|
46
|
+
end
|
47
|
+
|
48
|
+
def on_sclass(node)
|
49
|
+
eigenclass_method_definition?(node) { |default_scope| add_offense(default_scope, location: :name) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|