rubocop-rails 2.22.2 → 2.25.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/README.md +7 -9
- data/config/default.yml +13 -4
- data/lib/rubocop/cop/mixin/active_record_helper.rb +15 -3
- data/lib/rubocop/cop/mixin/database_type_resolvable.rb +1 -1
- data/lib/rubocop/cop/mixin/target_rails_version.rb +29 -2
- data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +2 -0
- data/lib/rubocop/cop/rails/active_support_aliases.rb +6 -5
- data/lib/rubocop/cop/rails/active_support_on_load.rb +21 -1
- data/lib/rubocop/cop/rails/bulk_change_table.rb +1 -1
- data/lib/rubocop/cop/rails/content_tag.rb +1 -1
- data/lib/rubocop/cop/rails/dangerous_column_names.rb +1 -2
- data/lib/rubocop/cop/rails/expanded_date_range.rb +1 -1
- data/lib/rubocop/cop/rails/find_by.rb +3 -3
- data/lib/rubocop/cop/rails/find_by_id.rb +9 -23
- data/lib/rubocop/cop/rails/http_status.rb +12 -2
- data/lib/rubocop/cop/rails/inquiry.rb +1 -0
- data/lib/rubocop/cop/rails/not_null_column.rb +91 -13
- data/lib/rubocop/cop/rails/pick.rb +10 -5
- data/lib/rubocop/cop/rails/pluck.rb +1 -1
- data/lib/rubocop/cop/rails/pluck_id.rb +2 -1
- data/lib/rubocop/cop/rails/pluck_in_where.rb +18 -5
- data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +1 -2
- data/lib/rubocop/cop/rails/response_parsed_body.rb +52 -10
- data/lib/rubocop/cop/rails/reversible_migration.rb +1 -1
- data/lib/rubocop/cop/rails/save_bang.rb +2 -0
- data/lib/rubocop/cop/rails/skips_model_validations.rb +1 -1
- data/lib/rubocop/cop/rails/time_zone.rb +2 -1
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +12 -4
- data/lib/rubocop/cop/rails/unknown_env.rb +1 -1
- data/lib/rubocop/cop/rails/unused_ignored_columns.rb +6 -0
- data/lib/rubocop/cop/rails/validation.rb +5 -3
- data/lib/rubocop/cop/rails/where_equals.rb +3 -2
- data/lib/rubocop/cop/rails/where_exists.rb +9 -8
- data/lib/rubocop/cop/rails/where_missing.rb +6 -2
- data/lib/rubocop/cop/rails/where_not.rb +8 -6
- data/lib/rubocop/cop/rails/where_range.rb +157 -0
- data/lib/rubocop/cop/rails_cops.rb +1 -0
- data/lib/rubocop/rails/schema_loader/schema.rb +1 -0
- data/lib/rubocop/rails/schema_loader.rb +5 -15
- data/lib/rubocop/rails/version.rb +1 -1
- metadata +7 -6
@@ -0,0 +1,157 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Identifies places where manually constructed SQL
|
7
|
+
# in `where` can be replaced with ranges.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# User.where('age >= ?', 18)
|
12
|
+
# User.where.not('age >= ?', 18)
|
13
|
+
# User.where('age < ?', 18)
|
14
|
+
# User.where('age >= ? AND age < ?', 18, 21)
|
15
|
+
# User.where('age >= :start', start: 18)
|
16
|
+
# User.where('users.age >= ?', 18)
|
17
|
+
#
|
18
|
+
# # good
|
19
|
+
# User.where(age: 18..)
|
20
|
+
# User.where.not(age: 18..)
|
21
|
+
# User.where(age: ...18)
|
22
|
+
# User.where(age: 18...21)
|
23
|
+
# User.where(users: { age: 18.. })
|
24
|
+
#
|
25
|
+
# # good
|
26
|
+
# # There are no beginless ranges in ruby.
|
27
|
+
# User.where('age > ?', 18)
|
28
|
+
#
|
29
|
+
class WhereRange < Base
|
30
|
+
include RangeHelp
|
31
|
+
extend AutoCorrector
|
32
|
+
extend TargetRubyVersion
|
33
|
+
extend TargetRailsVersion
|
34
|
+
|
35
|
+
MSG = 'Use `%<good_method>s` instead of manually constructing SQL.'
|
36
|
+
|
37
|
+
RESTRICT_ON_SEND = %i[where not].freeze
|
38
|
+
|
39
|
+
# column >= ?
|
40
|
+
GTEQ_ANONYMOUS_RE = /\A([\w.]+)\s+>=\s+\?\z/.freeze
|
41
|
+
# column <[=] ?
|
42
|
+
LTEQ_ANONYMOUS_RE = /\A([\w.]+)\s+(<=?)\s+\?\z/.freeze
|
43
|
+
# column >= ? AND column <[=] ?
|
44
|
+
RANGE_ANONYMOUS_RE = /\A([\w.]+)\s+>=\s+\?\s+AND\s+\1\s+(<=?)\s+\?\z/i.freeze
|
45
|
+
# column >= :value
|
46
|
+
GTEQ_NAMED_RE = /\A([\w.]+)\s+>=\s+:(\w+)\z/.freeze
|
47
|
+
# column <[=] :value
|
48
|
+
LTEQ_NAMED_RE = /\A([\w.]+)\s+(<=?)\s+:(\w+)\z/.freeze
|
49
|
+
# column >= :value1 AND column <[=] :value2
|
50
|
+
RANGE_NAMED_RE = /\A([\w.]+)\s+>=\s+:(\w+)\s+AND\s+\1\s+(<=?)\s+:(\w+)\z/i.freeze
|
51
|
+
|
52
|
+
minimum_target_ruby_version 2.6
|
53
|
+
minimum_target_rails_version 6.0
|
54
|
+
|
55
|
+
def_node_matcher :where_range_call?, <<~PATTERN
|
56
|
+
{
|
57
|
+
(call _ {:where :not} (array $str_type? $_ +))
|
58
|
+
(call _ {:where :not} $str_type? $_ +)
|
59
|
+
}
|
60
|
+
PATTERN
|
61
|
+
|
62
|
+
def on_send(node)
|
63
|
+
return if node.method?(:not) && !where_not?(node)
|
64
|
+
|
65
|
+
where_range_call?(node) do |template_node, values_node|
|
66
|
+
column, value = extract_column_and_value(template_node, values_node)
|
67
|
+
|
68
|
+
return unless column
|
69
|
+
|
70
|
+
range = offense_range(node)
|
71
|
+
good_method = build_good_method(node.method_name, column, value)
|
72
|
+
message = format(MSG, good_method: good_method)
|
73
|
+
|
74
|
+
add_offense(range, message: message) do |corrector|
|
75
|
+
corrector.replace(range, good_method)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def where_not?(node)
|
83
|
+
receiver = node.receiver
|
84
|
+
receiver&.send_type? && receiver&.method?(:where)
|
85
|
+
end
|
86
|
+
|
87
|
+
# rubocop:disable Metrics
|
88
|
+
def extract_column_and_value(template_node, values_node)
|
89
|
+
value =
|
90
|
+
case template_node.value
|
91
|
+
when GTEQ_ANONYMOUS_RE
|
92
|
+
"#{values_node[0].source}.."
|
93
|
+
when LTEQ_ANONYMOUS_RE
|
94
|
+
range_operator = range_operator(Regexp.last_match(2))
|
95
|
+
"#{range_operator}#{values_node[0].source}" if target_ruby_version >= 2.7
|
96
|
+
when RANGE_ANONYMOUS_RE
|
97
|
+
if values_node.size >= 2
|
98
|
+
range_operator = range_operator(Regexp.last_match(2))
|
99
|
+
"#{values_node[0].source}#{range_operator}#{values_node[1].source}"
|
100
|
+
end
|
101
|
+
when GTEQ_NAMED_RE
|
102
|
+
value_node = values_node[0]
|
103
|
+
|
104
|
+
if value_node.hash_type?
|
105
|
+
pair = find_pair(value_node, Regexp.last_match(2))
|
106
|
+
"#{pair.value.source}.." if pair
|
107
|
+
end
|
108
|
+
when LTEQ_NAMED_RE
|
109
|
+
value_node = values_node[0]
|
110
|
+
|
111
|
+
if value_node.hash_type?
|
112
|
+
pair = find_pair(value_node, Regexp.last_match(2))
|
113
|
+
if pair && target_ruby_version >= 2.7
|
114
|
+
range_operator = range_operator(Regexp.last_match(2))
|
115
|
+
"#{range_operator}#{pair.value.source}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
when RANGE_NAMED_RE
|
119
|
+
value_node = values_node[0]
|
120
|
+
|
121
|
+
if value_node.hash_type?
|
122
|
+
range_operator = range_operator(Regexp.last_match(3))
|
123
|
+
pair1 = find_pair(value_node, Regexp.last_match(2))
|
124
|
+
pair2 = find_pair(value_node, Regexp.last_match(4))
|
125
|
+
"#{pair1.value.source}#{range_operator}#{pair2.value.source}" if pair1 && pair2
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
[Regexp.last_match(1), value] if value
|
130
|
+
end
|
131
|
+
# rubocop:enable Metrics
|
132
|
+
|
133
|
+
def range_operator(comparison_operator)
|
134
|
+
comparison_operator == '<' ? '...' : '..'
|
135
|
+
end
|
136
|
+
|
137
|
+
def find_pair(hash_node, value)
|
138
|
+
hash_node.pairs.find { |pair| pair.key.value.to_sym == value.to_sym }
|
139
|
+
end
|
140
|
+
|
141
|
+
def offense_range(node)
|
142
|
+
range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
|
143
|
+
end
|
144
|
+
|
145
|
+
def build_good_method(method_name, column, value)
|
146
|
+
if column.include?('.')
|
147
|
+
table, column = column.split('.')
|
148
|
+
|
149
|
+
"#{method_name}(#{table}: { #{column}: #{value} })"
|
150
|
+
else
|
151
|
+
"#{method_name}(#{column}: #{value})"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -12,10 +12,10 @@ module RuboCop
|
|
12
12
|
# So a cop that uses the loader should handle `nil` properly.
|
13
13
|
#
|
14
14
|
# @return [Schema, nil]
|
15
|
-
def load(target_ruby_version)
|
15
|
+
def load(target_ruby_version, parser_engine)
|
16
16
|
return @load if defined?(@load)
|
17
17
|
|
18
|
-
@load = load!(target_ruby_version)
|
18
|
+
@load = load!(target_ruby_version, parser_engine)
|
19
19
|
end
|
20
20
|
|
21
21
|
def reset!
|
@@ -38,23 +38,13 @@ module RuboCop
|
|
38
38
|
|
39
39
|
private
|
40
40
|
|
41
|
-
def load!(target_ruby_version)
|
41
|
+
def load!(target_ruby_version, parser_engine)
|
42
42
|
path = db_schema_path
|
43
43
|
return unless path
|
44
44
|
|
45
|
-
ast =
|
46
|
-
Schema.new(ast) if ast
|
47
|
-
end
|
48
|
-
|
49
|
-
def parse(path, target_ruby_version)
|
50
|
-
klass_name = :"Ruby#{target_ruby_version.to_s.sub('.', '')}"
|
51
|
-
klass = ::Parser.const_get(klass_name)
|
52
|
-
parser = klass.new(RuboCop::AST::Builder.new)
|
45
|
+
ast = RuboCop::ProcessedSource.new(File.read(path), target_ruby_version, path, parser_engine: parser_engine).ast
|
53
46
|
|
54
|
-
|
55
|
-
buffer.source = path.read
|
56
|
-
|
57
|
-
parser.parse(buffer)
|
47
|
+
Schema.new(ast) if ast
|
58
48
|
end
|
59
49
|
end
|
60
50
|
end
|
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.
|
4
|
+
version: 2.25.0
|
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:
|
13
|
+
date: 2024-05-17 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -66,7 +66,7 @@ dependencies:
|
|
66
66
|
requirements:
|
67
67
|
- - ">="
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: 1.
|
69
|
+
version: 1.31.1
|
70
70
|
- - "<"
|
71
71
|
- !ruby/object:Gem::Version
|
72
72
|
version: '2.0'
|
@@ -76,7 +76,7 @@ dependencies:
|
|
76
76
|
requirements:
|
77
77
|
- - ">="
|
78
78
|
- !ruby/object:Gem::Version
|
79
|
-
version: 1.
|
79
|
+
version: 1.31.1
|
80
80
|
- - "<"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '2.0'
|
@@ -232,6 +232,7 @@ files:
|
|
232
232
|
- lib/rubocop/cop/rails/where_missing.rb
|
233
233
|
- lib/rubocop/cop/rails/where_not.rb
|
234
234
|
- lib/rubocop/cop/rails/where_not_with_multiple_conditions.rb
|
235
|
+
- lib/rubocop/cop/rails/where_range.rb
|
235
236
|
- lib/rubocop/cop/rails_cops.rb
|
236
237
|
- lib/rubocop/rails.rb
|
237
238
|
- lib/rubocop/rails/inject.rb
|
@@ -245,7 +246,7 @@ metadata:
|
|
245
246
|
homepage_uri: https://docs.rubocop.org/rubocop-rails/
|
246
247
|
changelog_uri: https://github.com/rubocop/rubocop-rails/blob/master/CHANGELOG.md
|
247
248
|
source_code_uri: https://github.com/rubocop/rubocop-rails/
|
248
|
-
documentation_uri: https://docs.rubocop.org/rubocop-rails/2.
|
249
|
+
documentation_uri: https://docs.rubocop.org/rubocop-rails/2.25/
|
249
250
|
bug_tracker_uri: https://github.com/rubocop/rubocop-rails/issues
|
250
251
|
rubygems_mfa_required: 'true'
|
251
252
|
post_install_message:
|
@@ -263,7 +264,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
263
264
|
- !ruby/object:Gem::Version
|
264
265
|
version: '0'
|
265
266
|
requirements: []
|
266
|
-
rubygems_version: 3.5.
|
267
|
+
rubygems_version: 3.5.3
|
267
268
|
signing_key:
|
268
269
|
specification_version: 4
|
269
270
|
summary: Automatic Rails code style checking tool.
|