rubocop-rails 2.24.1 → 2.26.2
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 +16 -2
- data/config/default.yml +29 -6
- data/lib/rubocop/cop/mixin/target_rails_version.rb +29 -2
- data/lib/rubocop/cop/rails/action_order.rb +1 -5
- data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +1 -5
- data/lib/rubocop/cop/rails/application_record.rb +4 -0
- data/lib/rubocop/cop/rails/bulk_change_table.rb +10 -4
- data/lib/rubocop/cop/rails/compact_blank.rb +29 -8
- data/lib/rubocop/cop/rails/date.rb +2 -2
- data/lib/rubocop/cop/rails/enum_hash.rb +31 -8
- data/lib/rubocop/cop/rails/enum_syntax.rb +128 -0
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +29 -7
- data/lib/rubocop/cop/rails/file_path.rb +1 -1
- data/lib/rubocop/cop/rails/http_status.rb +12 -2
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +1 -1
- data/lib/rubocop/cop/rails/link_to_blank.rb +2 -2
- data/lib/rubocop/cop/rails/not_null_column.rb +93 -13
- data/lib/rubocop/cop/rails/pick.rb +4 -0
- data/lib/rubocop/cop/rails/pluck_in_where.rb +17 -8
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +29 -15
- data/lib/rubocop/cop/rails/present.rb +0 -2
- data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +0 -29
- data/lib/rubocop/cop/rails/redundant_foreign_key.rb +1 -1
- data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +9 -0
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +1 -1
- data/lib/rubocop/cop/rails/reflection_class_name.rb +1 -1
- data/lib/rubocop/cop/rails/render_plain_text.rb +6 -3
- data/lib/rubocop/cop/rails/request_referer.rb +1 -1
- data/lib/rubocop/cop/rails/root_pathname_methods.rb +15 -11
- data/lib/rubocop/cop/rails/skips_model_validations.rb +8 -3
- 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 +8 -3
- data/lib/rubocop/cop/rails/where_equals.rb +28 -12
- data/lib/rubocop/cop/rails/where_not.rb +11 -6
- data/lib/rubocop/cop/rails/where_range.rb +203 -0
- data/lib/rubocop/cop/rails_cops.rb +2 -0
- data/lib/rubocop/rails/schema_loader/schema.rb +1 -1
- data/lib/rubocop/rails/version.rb +1 -1
- metadata +8 -6
@@ -4,7 +4,8 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
6
|
# Identifies places where manually constructed SQL
|
7
|
-
# in `where` can be replaced with
|
7
|
+
# in `where` and `where.not` can be replaced with
|
8
|
+
# `where(attribute: value)` and `where.not(attribute: value)`.
|
8
9
|
#
|
9
10
|
# @safety
|
10
11
|
# This cop's autocorrection is unsafe because is may change SQL.
|
@@ -13,6 +14,7 @@ module RuboCop
|
|
13
14
|
# @example
|
14
15
|
# # bad
|
15
16
|
# User.where('name = ?', 'Gabe')
|
17
|
+
# User.where.not('name = ?', 'Gabe')
|
16
18
|
# User.where('name = :name', name: 'Gabe')
|
17
19
|
# User.where('name IS NULL')
|
18
20
|
# User.where('name IN (?)', ['john', 'jane'])
|
@@ -21,6 +23,7 @@ module RuboCop
|
|
21
23
|
#
|
22
24
|
# # good
|
23
25
|
# User.where(name: 'Gabe')
|
26
|
+
# User.where.not(name: 'Gabe')
|
24
27
|
# User.where(name: nil)
|
25
28
|
# User.where(name: ['john', 'jane'])
|
26
29
|
# User.where(users: { name: 'Gabe' })
|
@@ -29,25 +32,27 @@ module RuboCop
|
|
29
32
|
extend AutoCorrector
|
30
33
|
|
31
34
|
MSG = 'Use `%<good_method>s` instead of manually constructing SQL.'
|
32
|
-
RESTRICT_ON_SEND = %i[where].freeze
|
35
|
+
RESTRICT_ON_SEND = %i[where not].freeze
|
33
36
|
|
34
37
|
def_node_matcher :where_method_call?, <<~PATTERN
|
35
38
|
{
|
36
|
-
(call _ :where (array $str_type? $_ ?))
|
37
|
-
(call _ :where $str_type? $_ ?)
|
39
|
+
(call _ {:where :not} (array $str_type? $_ ?))
|
40
|
+
(call _ {:where :not} $str_type? $_ ?)
|
38
41
|
}
|
39
42
|
PATTERN
|
40
43
|
|
41
44
|
def on_send(node)
|
45
|
+
return if node.method?(:not) && !where_not?(node)
|
46
|
+
|
42
47
|
where_method_call?(node) do |template_node, value_node|
|
43
48
|
value_node = value_node.first
|
44
49
|
|
45
50
|
range = offense_range(node)
|
46
51
|
|
47
|
-
|
48
|
-
return unless
|
52
|
+
column, value = extract_column_and_value(template_node, value_node)
|
53
|
+
return unless value
|
49
54
|
|
50
|
-
good_method = build_good_method(
|
55
|
+
good_method = build_good_method(node.method_name, column, value)
|
51
56
|
message = format(MSG, good_method: good_method)
|
52
57
|
|
53
58
|
add_offense(range, message: message) do |corrector|
|
@@ -69,11 +74,12 @@ module RuboCop
|
|
69
74
|
range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
|
70
75
|
end
|
71
76
|
|
77
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
72
78
|
def extract_column_and_value(template_node, value_node)
|
73
79
|
value =
|
74
80
|
case template_node.value
|
75
81
|
when EQ_ANONYMOUS_RE, IN_ANONYMOUS_RE
|
76
|
-
value_node
|
82
|
+
value_node&.source
|
77
83
|
when EQ_NAMED_RE, IN_NAMED_RE
|
78
84
|
return unless value_node&.hash_type?
|
79
85
|
|
@@ -85,18 +91,28 @@ module RuboCop
|
|
85
91
|
return
|
86
92
|
end
|
87
93
|
|
88
|
-
|
94
|
+
column_qualifier = Regexp.last_match(1)
|
95
|
+
return if column_qualifier.count('.') > 1
|
96
|
+
|
97
|
+
[column_qualifier, value]
|
89
98
|
end
|
99
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
90
100
|
|
91
|
-
def build_good_method(column, value)
|
101
|
+
def build_good_method(method_name, column, value)
|
92
102
|
if column.include?('.')
|
93
103
|
table, column = column.split('.')
|
94
104
|
|
95
|
-
"
|
105
|
+
"#{method_name}(#{table}: { #{column}: #{value} })"
|
96
106
|
else
|
97
|
-
"
|
107
|
+
"#{method_name}(#{column}: #{value})"
|
98
108
|
end
|
99
109
|
end
|
110
|
+
|
111
|
+
def where_not?(node)
|
112
|
+
return false unless (receiver = node.receiver)
|
113
|
+
|
114
|
+
receiver.send_type? && receiver.method?(:where)
|
115
|
+
end
|
100
116
|
end
|
101
117
|
end
|
102
118
|
end
|
@@ -43,10 +43,10 @@ module RuboCop
|
|
43
43
|
|
44
44
|
range = offense_range(node)
|
45
45
|
|
46
|
-
|
47
|
-
return unless
|
46
|
+
column, value = extract_column_and_value(template_node, value_node)
|
47
|
+
return unless value
|
48
48
|
|
49
|
-
good_method = build_good_method(node.loc.dot&.source,
|
49
|
+
good_method = build_good_method(node.loc.dot&.source, column, value)
|
50
50
|
message = format(MSG, good_method: good_method)
|
51
51
|
|
52
52
|
add_offense(range, message: message) do |corrector|
|
@@ -68,13 +68,14 @@ module RuboCop
|
|
68
68
|
range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
|
69
69
|
end
|
70
70
|
|
71
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
71
72
|
def extract_column_and_value(template_node, value_node)
|
72
73
|
value =
|
73
74
|
case template_node.value
|
74
75
|
when NOT_EQ_ANONYMOUS_RE, NOT_IN_ANONYMOUS_RE
|
75
|
-
value_node
|
76
|
+
value_node&.source
|
76
77
|
when NOT_EQ_NAMED_RE, NOT_IN_NAMED_RE
|
77
|
-
return unless value_node
|
78
|
+
return unless value_node&.hash_type?
|
78
79
|
|
79
80
|
pair = value_node.pairs.find { |p| p.key.value.to_sym == Regexp.last_match(2).to_sym }
|
80
81
|
pair.value.source
|
@@ -84,8 +85,12 @@ module RuboCop
|
|
84
85
|
return
|
85
86
|
end
|
86
87
|
|
87
|
-
|
88
|
+
column_qualifier = Regexp.last_match(1)
|
89
|
+
return if column_qualifier.count('.') > 1
|
90
|
+
|
91
|
+
[column_qualifier, value]
|
88
92
|
end
|
93
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
89
94
|
|
90
95
|
def build_good_method(dot, column, value)
|
91
96
|
dot ||= '.'
|
@@ -0,0 +1,203 @@
|
|
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
|
+
# @safety
|
10
|
+
# This cop's autocorrection is unsafe because it can change the query
|
11
|
+
# by explicitly attaching the column to the wrong table.
|
12
|
+
# For example, `Booking.joins(:events).where('end_at < ?', Time.current)` will correctly
|
13
|
+
# implicitly attach the `end_at` column to the `events` table. But when autocorrected to
|
14
|
+
# `Booking.joins(:events).where(end_at: ...Time.current)`, it will now be incorrectly
|
15
|
+
# explicitly attached to the `bookings` table.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# # bad
|
19
|
+
# User.where('age >= ?', 18)
|
20
|
+
# User.where.not('age >= ?', 18)
|
21
|
+
# User.where('age < ?', 18)
|
22
|
+
# User.where('age >= ? AND age < ?', 18, 21)
|
23
|
+
# User.where('age >= :start', start: 18)
|
24
|
+
# User.where('users.age >= ?', 18)
|
25
|
+
#
|
26
|
+
# # good
|
27
|
+
# User.where(age: 18..)
|
28
|
+
# User.where.not(age: 18..)
|
29
|
+
# User.where(age: ...18)
|
30
|
+
# User.where(age: 18...21)
|
31
|
+
# User.where(users: { age: 18.. })
|
32
|
+
#
|
33
|
+
# # good
|
34
|
+
# # There are no beginless ranges in ruby.
|
35
|
+
# User.where('age > ?', 18)
|
36
|
+
#
|
37
|
+
class WhereRange < Base
|
38
|
+
include RangeHelp
|
39
|
+
extend AutoCorrector
|
40
|
+
extend TargetRubyVersion
|
41
|
+
extend TargetRailsVersion
|
42
|
+
|
43
|
+
MSG = 'Use `%<good_method>s` instead of manually constructing SQL.'
|
44
|
+
|
45
|
+
RESTRICT_ON_SEND = %i[where not].freeze
|
46
|
+
|
47
|
+
# column >= ?
|
48
|
+
GTEQ_ANONYMOUS_RE = /\A\s*([\w.]+)\s+>=\s+\?\s*\z/.freeze
|
49
|
+
# column <[=] ?
|
50
|
+
LTEQ_ANONYMOUS_RE = /\A\s*([\w.]+)\s+(<=?)\s+\?\s*\z/.freeze
|
51
|
+
# column >= ? AND column <[=] ?
|
52
|
+
RANGE_ANONYMOUS_RE = /\A\s*([\w.]+)\s+>=\s+\?\s+AND\s+\1\s+(<=?)\s+\?\s*\z/i.freeze
|
53
|
+
# column >= :value
|
54
|
+
GTEQ_NAMED_RE = /\A\s*([\w.]+)\s+>=\s+:(\w+)\s*\z/.freeze
|
55
|
+
# column <[=] :value
|
56
|
+
LTEQ_NAMED_RE = /\A\s*([\w.]+)\s+(<=?)\s+:(\w+)\s*\z/.freeze
|
57
|
+
# column >= :value1 AND column <[=] :value2
|
58
|
+
RANGE_NAMED_RE = /\A\s*([\w.]+)\s+>=\s+:(\w+)\s+AND\s+\1\s+(<=?)\s+:(\w+)\s*\z/i.freeze
|
59
|
+
|
60
|
+
minimum_target_ruby_version 2.6
|
61
|
+
minimum_target_rails_version 6.0
|
62
|
+
|
63
|
+
def_node_matcher :where_range_call?, <<~PATTERN
|
64
|
+
{
|
65
|
+
(call _ {:where :not} (array $str_type? $_ +))
|
66
|
+
(call _ {:where :not} $str_type? $_ +)
|
67
|
+
}
|
68
|
+
PATTERN
|
69
|
+
|
70
|
+
def on_send(node)
|
71
|
+
return if node.method?(:not) && !where_not?(node)
|
72
|
+
|
73
|
+
where_range_call?(node) do |template_node, values_node|
|
74
|
+
column, value = extract_column_and_value(template_node, values_node)
|
75
|
+
|
76
|
+
return unless column
|
77
|
+
|
78
|
+
range = offense_range(node)
|
79
|
+
good_method = build_good_method(node.method_name, column, value)
|
80
|
+
message = format(MSG, good_method: good_method)
|
81
|
+
|
82
|
+
add_offense(range, message: message) do |corrector|
|
83
|
+
corrector.replace(range, good_method)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def where_not?(node)
|
91
|
+
receiver = node.receiver
|
92
|
+
receiver&.send_type? && receiver&.method?(:where)
|
93
|
+
end
|
94
|
+
|
95
|
+
# rubocop:disable Metrics
|
96
|
+
def extract_column_and_value(template_node, values_node)
|
97
|
+
case template_node.value
|
98
|
+
when GTEQ_ANONYMOUS_RE
|
99
|
+
lhs = values_node[0]
|
100
|
+
operator = '..'
|
101
|
+
when LTEQ_ANONYMOUS_RE
|
102
|
+
if target_ruby_version >= 2.7
|
103
|
+
operator = range_operator(Regexp.last_match(2))
|
104
|
+
rhs = values_node[0]
|
105
|
+
end
|
106
|
+
when RANGE_ANONYMOUS_RE
|
107
|
+
if values_node.size >= 2
|
108
|
+
lhs = values_node[0]
|
109
|
+
operator = range_operator(Regexp.last_match(2))
|
110
|
+
rhs = values_node[1]
|
111
|
+
end
|
112
|
+
when GTEQ_NAMED_RE
|
113
|
+
value_node = values_node[0]
|
114
|
+
|
115
|
+
if value_node.hash_type?
|
116
|
+
pair = find_pair(value_node, Regexp.last_match(2))
|
117
|
+
lhs = pair.value
|
118
|
+
operator = '..'
|
119
|
+
end
|
120
|
+
when LTEQ_NAMED_RE
|
121
|
+
value_node = values_node[0]
|
122
|
+
|
123
|
+
if value_node.hash_type?
|
124
|
+
pair = find_pair(value_node, Regexp.last_match(2))
|
125
|
+
if pair && target_ruby_version >= 2.7
|
126
|
+
operator = range_operator(Regexp.last_match(2))
|
127
|
+
rhs = pair.value
|
128
|
+
end
|
129
|
+
end
|
130
|
+
when RANGE_NAMED_RE
|
131
|
+
value_node = values_node[0]
|
132
|
+
|
133
|
+
if value_node.hash_type?
|
134
|
+
pair1 = find_pair(value_node, Regexp.last_match(2))
|
135
|
+
pair2 = find_pair(value_node, Regexp.last_match(4))
|
136
|
+
|
137
|
+
if pair1 && pair2
|
138
|
+
lhs = pair1.value
|
139
|
+
operator = range_operator(Regexp.last_match(3))
|
140
|
+
rhs = pair2.value
|
141
|
+
end
|
142
|
+
end
|
143
|
+
else
|
144
|
+
return
|
145
|
+
end
|
146
|
+
|
147
|
+
if lhs
|
148
|
+
lhs_source = parentheses_needed?(lhs) ? "(#{lhs.source})" : lhs.source
|
149
|
+
end
|
150
|
+
|
151
|
+
if rhs
|
152
|
+
rhs_source = parentheses_needed?(rhs) ? "(#{rhs.source})" : rhs.source
|
153
|
+
end
|
154
|
+
|
155
|
+
column_qualifier = Regexp.last_match(1)
|
156
|
+
return if column_qualifier.count('.') > 1
|
157
|
+
|
158
|
+
[column_qualifier, "#{lhs_source}#{operator}#{rhs_source}"] if operator
|
159
|
+
end
|
160
|
+
# rubocop:enable Metrics
|
161
|
+
|
162
|
+
def range_operator(comparison_operator)
|
163
|
+
comparison_operator == '<' ? '...' : '..'
|
164
|
+
end
|
165
|
+
|
166
|
+
def find_pair(hash_node, value)
|
167
|
+
hash_node.pairs.find { |pair| pair.key.value.to_sym == value.to_sym }
|
168
|
+
end
|
169
|
+
|
170
|
+
def offense_range(node)
|
171
|
+
range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
|
172
|
+
end
|
173
|
+
|
174
|
+
def build_good_method(method_name, column, value)
|
175
|
+
if column.include?('.')
|
176
|
+
table, column = column.split('.')
|
177
|
+
|
178
|
+
"#{method_name}(#{table}: { #{column}: #{value} })"
|
179
|
+
else
|
180
|
+
"#{method_name}(#{column}: #{value})"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def parentheses_needed?(node)
|
185
|
+
!parentheses_not_needed?(node)
|
186
|
+
end
|
187
|
+
|
188
|
+
def parentheses_not_needed?(node)
|
189
|
+
node.variable? ||
|
190
|
+
node.literal? ||
|
191
|
+
node.reference? ||
|
192
|
+
node.const_type? ||
|
193
|
+
node.begin_type? ||
|
194
|
+
parenthesized_call_node?(node)
|
195
|
+
end
|
196
|
+
|
197
|
+
def parenthesized_call_node?(node)
|
198
|
+
node.call_type? && (node.arguments.empty? || node.parenthesized_call?)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -46,6 +46,7 @@ require_relative 'rails/duration_arithmetic'
|
|
46
46
|
require_relative 'rails/dynamic_find_by'
|
47
47
|
require_relative 'rails/eager_evaluation_log_message'
|
48
48
|
require_relative 'rails/enum_hash'
|
49
|
+
require_relative 'rails/enum_syntax'
|
49
50
|
require_relative 'rails/enum_uniqueness'
|
50
51
|
require_relative 'rails/env_local'
|
51
52
|
require_relative 'rails/environment_comparison'
|
@@ -138,3 +139,4 @@ require_relative 'rails/where_exists'
|
|
138
139
|
require_relative 'rails/where_missing'
|
139
140
|
require_relative 'rails/where_not'
|
140
141
|
require_relative 'rails/where_not_with_multiple_conditions'
|
142
|
+
require_relative 'rails/where_range'
|
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.26.2
|
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: 2024-
|
13
|
+
date: 2024-09-21 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -46,7 +46,7 @@ dependencies:
|
|
46
46
|
requirements:
|
47
47
|
- - ">="
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version: 1.
|
49
|
+
version: 1.52.0
|
50
50
|
- - "<"
|
51
51
|
- !ruby/object:Gem::Version
|
52
52
|
version: '2.0'
|
@@ -56,7 +56,7 @@ dependencies:
|
|
56
56
|
requirements:
|
57
57
|
- - ">="
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version: 1.
|
59
|
+
version: 1.52.0
|
60
60
|
- - "<"
|
61
61
|
- !ruby/object:Gem::Version
|
62
62
|
version: '2.0'
|
@@ -140,6 +140,7 @@ files:
|
|
140
140
|
- lib/rubocop/cop/rails/dynamic_find_by.rb
|
141
141
|
- lib/rubocop/cop/rails/eager_evaluation_log_message.rb
|
142
142
|
- lib/rubocop/cop/rails/enum_hash.rb
|
143
|
+
- lib/rubocop/cop/rails/enum_syntax.rb
|
143
144
|
- lib/rubocop/cop/rails/enum_uniqueness.rb
|
144
145
|
- lib/rubocop/cop/rails/env_local.rb
|
145
146
|
- lib/rubocop/cop/rails/environment_comparison.rb
|
@@ -232,6 +233,7 @@ files:
|
|
232
233
|
- lib/rubocop/cop/rails/where_missing.rb
|
233
234
|
- lib/rubocop/cop/rails/where_not.rb
|
234
235
|
- lib/rubocop/cop/rails/where_not_with_multiple_conditions.rb
|
236
|
+
- lib/rubocop/cop/rails/where_range.rb
|
235
237
|
- lib/rubocop/cop/rails_cops.rb
|
236
238
|
- lib/rubocop/rails.rb
|
237
239
|
- lib/rubocop/rails/inject.rb
|
@@ -245,7 +247,7 @@ metadata:
|
|
245
247
|
homepage_uri: https://docs.rubocop.org/rubocop-rails/
|
246
248
|
changelog_uri: https://github.com/rubocop/rubocop-rails/blob/master/CHANGELOG.md
|
247
249
|
source_code_uri: https://github.com/rubocop/rubocop-rails/
|
248
|
-
documentation_uri: https://docs.rubocop.org/rubocop-rails/2.
|
250
|
+
documentation_uri: https://docs.rubocop.org/rubocop-rails/2.26/
|
249
251
|
bug_tracker_uri: https://github.com/rubocop/rubocop-rails/issues
|
250
252
|
rubygems_mfa_required: 'true'
|
251
253
|
post_install_message:
|
@@ -263,7 +265,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
263
265
|
- !ruby/object:Gem::Version
|
264
266
|
version: '0'
|
265
267
|
requirements: []
|
266
|
-
rubygems_version: 3.
|
268
|
+
rubygems_version: 3.5.16
|
267
269
|
signing_key:
|
268
270
|
specification_version: 4
|
269
271
|
summary: Automatic Rails code style checking tool.
|