rubocop 1.56.2 → 1.56.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/config/default.yml +1 -6
- data/lib/rubocop/cop/autocorrect_logic.rb +3 -1
- data/lib/rubocop/cop/bundler/duplicated_group.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/example_description.rb +42 -22
- data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +11 -2
- data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +2 -0
- data/lib/rubocop/cop/layout/dot_position.rb +1 -5
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +41 -8
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +3 -0
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
- data/lib/rubocop/cop/layout/space_after_not.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +11 -4
- data/lib/rubocop/cop/lint/useless_assignment.rb +37 -10
- data/lib/rubocop/cop/metrics/method_length.rb +1 -1
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -2
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +5 -7
- data/lib/rubocop/cop/style/arguments_forwarding.rb +8 -6
- data/lib/rubocop/cop/style/array_intersect.rb +13 -5
- data/lib/rubocop/cop/style/collection_methods.rb +2 -0
- data/lib/rubocop/cop/style/combinable_loops.rb +3 -2
- data/lib/rubocop/cop/style/empty_case_condition.rb +6 -1
- data/lib/rubocop/cop/style/for.rb +1 -1
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -1
- data/lib/rubocop/cop/style/operator_method_call.rb +6 -0
- data/lib/rubocop/cop/style/redundant_conditional.rb +1 -9
- data/lib/rubocop/cop/style/return_nil.rb +6 -2
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
- data/lib/rubocop/cop/style/symbol_array.rb +10 -11
- data/lib/rubocop/cop/style/yoda_expression.rb +8 -7
- data/lib/rubocop/file_finder.rb +4 -7
- data/lib/rubocop/magic_comment.rb +12 -10
- data/lib/rubocop/rspec/shared_contexts.rb +2 -3
- data/lib/rubocop/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3088047e83f70bb0665d28875e2498dce9f6c9066be1524691b3205b155953e
|
4
|
+
data.tar.gz: 37aa8682af6da472cb469924c7a5407b0b947f15a519356dc035f45e4e82982d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d075e0720342cb33925fbbb41d13ddbfe6d060dd92df26890b839e7fb3a65ea6b78e0f0cc42ed31fb038e363e2ec57c5e068e8372ed2e39f48cba2c922bc8e9a
|
7
|
+
data.tar.gz: 7807c06a028201f01c168298f3587160d7d84d7eb8d968b01ecf33772180b8bdeba5dd1fd6a38b60ed54bb259fc306eb46d174baee5f66222986d467b38e2a85
|
data/config/default.yml
CHANGED
@@ -3409,6 +3409,7 @@ Style/CollectionMethods:
|
|
3409
3409
|
PreferredMethods:
|
3410
3410
|
collect: 'map'
|
3411
3411
|
collect!: 'map!'
|
3412
|
+
collect_concat: 'flat_map'
|
3412
3413
|
inject: 'reduce'
|
3413
3414
|
detect: 'find'
|
3414
3415
|
find_all: 'select'
|
@@ -4130,12 +4131,6 @@ Style/InvertibleUnlessCondition:
|
|
4130
4131
|
:none?: :any?
|
4131
4132
|
:even?: :odd?
|
4132
4133
|
:odd?: :even?
|
4133
|
-
# `ActiveSupport` defines some common inverse methods. They are listed below,
|
4134
|
-
# and not enabled by default.
|
4135
|
-
# :present?: :blank?
|
4136
|
-
# :blank?: :present?
|
4137
|
-
# :include?: :exclude?
|
4138
|
-
# :exclude?: :include?
|
4139
4134
|
|
4140
4135
|
Style/IpAddresses:
|
4141
4136
|
Description: "Don't include literal IP addresses in code."
|
@@ -82,7 +82,9 @@ module RuboCop
|
|
82
82
|
node.array_type? && node.percent_literal?
|
83
83
|
end
|
84
84
|
|
85
|
-
percent_array.map(&:source_range).find
|
85
|
+
percent_array.map(&:source_range).find do |range|
|
86
|
+
offense_range.begin_pos > range.begin_pos && range.overlaps?(offense_range)
|
87
|
+
end
|
86
88
|
end
|
87
89
|
|
88
90
|
def range_of_first_line(range)
|
@@ -26,9 +26,7 @@ module RuboCop
|
|
26
26
|
# expect_no_offenses('...')
|
27
27
|
# end
|
28
28
|
class ExampleDescription < Base
|
29
|
-
|
30
|
-
attr_accessor :descriptions
|
31
|
-
end
|
29
|
+
extend AutoCorrector
|
32
30
|
|
33
31
|
MSG = 'Description does not match use of `%<method_name>s`.'
|
34
32
|
|
@@ -39,22 +37,31 @@ module RuboCop
|
|
39
37
|
expect_no_corrections
|
40
38
|
].to_set.freeze
|
41
39
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
40
|
+
EXPECT_NO_OFFENSES_DESCRIPTION_MAPPING = {
|
41
|
+
/\A(adds|registers|reports|finds) (an? )?offense/ => 'does not register an offense',
|
42
|
+
/\A(flags|handles|works)\b/ => 'does not register'
|
43
|
+
}.freeze
|
44
|
+
|
45
|
+
EXPECT_OFFENSE_DESCRIPTION_MAPPING = {
|
46
|
+
/\A(does not|doesn't) (register|find|flag|report)/ => 'registers',
|
47
|
+
/\A(does not|doesn't) add (a|an|any )?offense/ => 'registers an offense',
|
48
|
+
/\Aaccepts\b/ => 'registers'
|
49
|
+
}.freeze
|
46
50
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
/^accepts\b/
|
51
|
-
].freeze
|
51
|
+
EXPECT_NO_CORRECTIONS_DESCRIPTION_MAPPING = {
|
52
|
+
/\A(auto[- ]?)?correct/ => 'does not correct'
|
53
|
+
}.freeze
|
52
54
|
|
53
|
-
|
55
|
+
EXPECT_CORRECTION_DESCRIPTION_MAPPING = {
|
56
|
+
/\b(does not|doesn't) (auto[- ]?)?correct/ => 'autocorrects'
|
57
|
+
}.freeze
|
54
58
|
|
55
|
-
|
56
|
-
|
57
|
-
|
59
|
+
EXAMPLE_DESCRIPTION_MAPPING = {
|
60
|
+
expect_no_offenses: EXPECT_NO_OFFENSES_DESCRIPTION_MAPPING,
|
61
|
+
expect_offense: EXPECT_OFFENSE_DESCRIPTION_MAPPING,
|
62
|
+
expect_no_corrections: EXPECT_NO_CORRECTIONS_DESCRIPTION_MAPPING,
|
63
|
+
expect_correction: EXPECT_CORRECTION_DESCRIPTION_MAPPING
|
64
|
+
}.freeze
|
58
65
|
|
59
66
|
# @!method offense_example?(node)
|
60
67
|
def_node_matcher :offense_example?, <<~PATTERN
|
@@ -67,21 +74,34 @@ module RuboCop
|
|
67
74
|
|
68
75
|
def on_send(node)
|
69
76
|
parent = node.each_ancestor(:block).first
|
70
|
-
return unless parent && (
|
77
|
+
return unless parent && (current_description = offense_example?(parent))
|
71
78
|
|
72
79
|
method_name = node.method_name
|
73
80
|
message = format(MSG, method_name: method_name)
|
74
81
|
|
75
|
-
|
76
|
-
check_description(
|
82
|
+
description_map = EXAMPLE_DESCRIPTION_MAPPING[method_name]
|
83
|
+
check_description(current_description, description_map, message)
|
77
84
|
end
|
78
85
|
|
79
86
|
private
|
80
87
|
|
81
|
-
def check_description(
|
82
|
-
|
88
|
+
def check_description(current_description, description_map, message)
|
89
|
+
description_text = string_contents(current_description)
|
90
|
+
return unless (new_description = correct_description(description_text, description_map))
|
91
|
+
|
92
|
+
add_offense(current_description, message: message) do |corrector|
|
93
|
+
corrector.replace(current_description, "'#{new_description}'")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def correct_description(current_description, description_map)
|
98
|
+
description_map.each do |incorrect_description_pattern, preferred_description|
|
99
|
+
if incorrect_description_pattern.match?(current_description)
|
100
|
+
return current_description.gsub(incorrect_description_pattern, preferred_description)
|
101
|
+
end
|
102
|
+
end
|
83
103
|
|
84
|
-
|
104
|
+
nil
|
85
105
|
end
|
86
106
|
|
87
107
|
def string_contents(node)
|
@@ -14,6 +14,12 @@ module RuboCop
|
|
14
14
|
# node.method_name
|
15
15
|
#
|
16
16
|
# # bad
|
17
|
+
# node.send_node.method?(:method_name)
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# node.method?(:method_name)
|
21
|
+
#
|
22
|
+
# # bad
|
17
23
|
# node.send_node.receiver
|
18
24
|
#
|
19
25
|
# # good
|
@@ -24,11 +30,14 @@ module RuboCop
|
|
24
30
|
extend AutoCorrector
|
25
31
|
|
26
32
|
MSG = 'Remove the redundant `send_node`.'
|
27
|
-
RESTRICT_ON_SEND = %i[method_name receiver].freeze
|
33
|
+
RESTRICT_ON_SEND = %i[method_name method? receiver].freeze
|
28
34
|
|
29
35
|
# @!method dispatch_method(node)
|
30
36
|
def_node_matcher :dispatch_method, <<~PATTERN
|
31
|
-
|
37
|
+
{
|
38
|
+
(send $(send _ :send_node) {:method_name :receiver})
|
39
|
+
(send $(send _ :send_node) :method? _)
|
40
|
+
}
|
32
41
|
PATTERN
|
33
42
|
|
34
43
|
def on_send(node)
|
@@ -32,7 +32,7 @@ module RuboCop
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def on_send(node)
|
35
|
-
return unless node.dot? ||
|
35
|
+
return unless node.dot? || node.safe_navigation?
|
36
36
|
|
37
37
|
return correct_style_detected if proper_dot_position?(node)
|
38
38
|
|
@@ -133,10 +133,6 @@ module RuboCop
|
|
133
133
|
# l.(1) has no selector, so we use the opening parenthesis instead
|
134
134
|
node.loc.selector || node.loc.begin
|
135
135
|
end
|
136
|
-
|
137
|
-
def ampersand_dot?(node)
|
138
|
-
node.loc.respond_to?(:dot) && node.loc.dot && node.loc.dot.is?('&.')
|
139
|
-
end
|
140
136
|
end
|
141
137
|
end
|
142
138
|
end
|
@@ -3,7 +3,23 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Layout
|
6
|
-
# Enforces empty line after guard clause
|
6
|
+
# Enforces empty line after guard clause.
|
7
|
+
#
|
8
|
+
# This cop allows `# :nocov:` directive after guard clause because
|
9
|
+
# SimpleCov excludes code from the coverage report by wrapping it in `# :nocov:`:
|
10
|
+
#
|
11
|
+
# [source,ruby]
|
12
|
+
# ----
|
13
|
+
# def foo
|
14
|
+
# # :nocov:
|
15
|
+
# return if condition
|
16
|
+
# # :nocov:
|
17
|
+
# bar
|
18
|
+
# end
|
19
|
+
# ----
|
20
|
+
#
|
21
|
+
# Refer to SimpleCov's documentation for more details:
|
22
|
+
# https://github.com/simplecov-ruby/simplecov#ignoringskipping-code
|
7
23
|
#
|
8
24
|
# @example
|
9
25
|
#
|
@@ -42,19 +58,22 @@ module RuboCop
|
|
42
58
|
|
43
59
|
MSG = 'Add empty line after guard clause.'
|
44
60
|
END_OF_HEREDOC_LINE = 1
|
61
|
+
SIMPLE_DIRECTIVE_COMMENT_PATTERN = /\A# *:nocov:\z/.freeze
|
45
62
|
|
46
63
|
def on_if(node)
|
47
64
|
return if correct_style?(node)
|
48
65
|
return if multiple_statements_on_line?(node)
|
49
66
|
|
50
67
|
if node.modifier_form? && (heredoc_node = last_heredoc_argument(node))
|
51
|
-
|
68
|
+
if next_line_empty_or_allowed_directive_comment?(heredoc_line(node, heredoc_node))
|
69
|
+
return
|
70
|
+
end
|
52
71
|
|
53
72
|
add_offense(heredoc_node.loc.heredoc_end) do |corrector|
|
54
73
|
autocorrect(corrector, heredoc_node)
|
55
74
|
end
|
56
75
|
else
|
57
|
-
return if
|
76
|
+
return if next_line_empty_or_allowed_directive_comment?(node.last_line)
|
58
77
|
|
59
78
|
add_offense(offense_location(node)) { |corrector| autocorrect(corrector, node) }
|
60
79
|
end
|
@@ -70,7 +89,7 @@ module RuboCop
|
|
70
89
|
end
|
71
90
|
|
72
91
|
next_line = node_range.last_line + 1
|
73
|
-
if
|
92
|
+
if next_line_allowed_directive_comment?(next_line)
|
74
93
|
node_range = processed_source.comment_at_line(next_line)
|
75
94
|
end
|
76
95
|
|
@@ -88,21 +107,21 @@ module RuboCop
|
|
88
107
|
node.if_branch&.guard_clause?
|
89
108
|
end
|
90
109
|
|
91
|
-
def
|
110
|
+
def next_line_empty_or_allowed_directive_comment?(line)
|
92
111
|
return true if next_line_empty?(line)
|
93
112
|
|
94
113
|
next_line = line + 1
|
95
|
-
|
114
|
+
next_line_allowed_directive_comment?(next_line) && next_line_empty?(next_line)
|
96
115
|
end
|
97
116
|
|
98
117
|
def next_line_empty?(line)
|
99
118
|
processed_source[line].blank?
|
100
119
|
end
|
101
120
|
|
102
|
-
def
|
121
|
+
def next_line_allowed_directive_comment?(line)
|
103
122
|
return false unless (comment = processed_source.comment_at_line(line))
|
104
123
|
|
105
|
-
DirectiveComment.new(comment).enabled?
|
124
|
+
DirectiveComment.new(comment).enabled? || simplecov_directive_comment?(comment)
|
106
125
|
end
|
107
126
|
|
108
127
|
def next_line_rescue_or_ensure?(node)
|
@@ -145,6 +164,8 @@ module RuboCop
|
|
145
164
|
|
146
165
|
if node.if_branch.and_type?
|
147
166
|
node.if_branch.children.first
|
167
|
+
elsif use_heredoc_in_condition?(node.condition)
|
168
|
+
node.condition
|
148
169
|
else
|
149
170
|
node.if_branch.children.last
|
150
171
|
end
|
@@ -161,6 +182,12 @@ module RuboCop
|
|
161
182
|
node.respond_to?(:heredoc?) && node.heredoc?
|
162
183
|
end
|
163
184
|
|
185
|
+
def use_heredoc_in_condition?(condition)
|
186
|
+
condition.descendants.any? do |descendant|
|
187
|
+
descendant.respond_to?(:heredoc?) && descendant.heredoc?
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
164
191
|
def offense_location(node)
|
165
192
|
if node.loc.respond_to?(:end) && node.loc.end
|
166
193
|
node.loc.end
|
@@ -175,6 +202,12 @@ module RuboCop
|
|
175
202
|
|
176
203
|
parent.begin_type? && parent.single_line?
|
177
204
|
end
|
205
|
+
|
206
|
+
# SimpleCov excludes code from the coverage report by wrapping it in `# :nocov:`:
|
207
|
+
# https://github.com/simplecov-ruby/simplecov#ignoringskipping-code
|
208
|
+
def simplecov_directive_comment?(comment)
|
209
|
+
SIMPLE_DIRECTIVE_COMMENT_PATTERN.match?(comment.text)
|
210
|
+
end
|
178
211
|
end
|
179
212
|
end
|
180
213
|
end
|
@@ -25,6 +25,9 @@ module RuboCop
|
|
25
25
|
include Alignment
|
26
26
|
include Heredoc
|
27
27
|
extend AutoCorrector
|
28
|
+
extend TargetRubyVersion
|
29
|
+
|
30
|
+
minimum_target_ruby_version 2.3
|
28
31
|
|
29
32
|
TYPE_MSG = 'Use %<indentation_width>d spaces for indentation in a ' \
|
30
33
|
'heredoc by using `<<~` instead of `%<current_indent_type>s`.'
|
@@ -75,7 +75,7 @@ module RuboCop
|
|
75
75
|
def right_hand_side(send_node)
|
76
76
|
dot = send_node.loc.dot
|
77
77
|
selector = send_node.loc.selector
|
78
|
-
if send_node.dot? && selector && same_line?(dot, selector)
|
78
|
+
if (send_node.dot? || send_node.safe_navigation?) && selector && same_line?(dot, selector)
|
79
79
|
dot.join(selector)
|
80
80
|
elsif selector
|
81
81
|
selector
|
@@ -179,7 +179,7 @@ module RuboCop
|
|
179
179
|
# a.b
|
180
180
|
# .c
|
181
181
|
def semantic_alignment_base(node, rhs)
|
182
|
-
return unless rhs.source.start_with?('.')
|
182
|
+
return unless rhs.source.start_with?('.', '&.')
|
183
183
|
|
184
184
|
node = semantic_alignment_node(node)
|
185
185
|
return unless node&.loc&.selector
|
@@ -108,7 +108,7 @@ module RuboCop
|
|
108
108
|
!comment_within?(node) &&
|
109
109
|
node.each_descendant(:if, :case, :kwbegin, :def, :defs).none? &&
|
110
110
|
node.each_descendant(:dstr, :str).none? { |n| n.heredoc? || n.value.include?("\n") } &&
|
111
|
-
node.each_descendant(:begin).none? { |b| !b.single_line? }
|
111
|
+
node.each_descendant(:begin, :sym).none? { |b| !b.single_line? }
|
112
112
|
end
|
113
113
|
|
114
114
|
def convertible_block?(node)
|
@@ -82,16 +82,23 @@ module RuboCop
|
|
82
82
|
def autocorrect(corrector, offense_range:, send_node:)
|
83
83
|
corrector.replace(
|
84
84
|
offense_range,
|
85
|
-
add_safe_navigation_operator(
|
86
|
-
offense_range: offense_range,
|
87
|
-
send_node: send_node
|
88
|
-
)
|
85
|
+
add_safe_navigation_operator(offense_range: offense_range, send_node: send_node)
|
89
86
|
)
|
87
|
+
|
88
|
+
corrector.wrap(send_node, '(', ')') if require_parentheses?(send_node)
|
90
89
|
end
|
91
90
|
|
92
91
|
def brackets?(send_node)
|
93
92
|
send_node.method?(:[]) || send_node.method?(:[]=)
|
94
93
|
end
|
94
|
+
|
95
|
+
def require_parentheses?(send_node)
|
96
|
+
return false unless send_node.comparison_method?
|
97
|
+
return false unless (node = send_node.parent)
|
98
|
+
|
99
|
+
(node.respond_to?(:logical_operator?) && node.logical_operator?) ||
|
100
|
+
(node.respond_to?(:comparison_method?) && node.comparison_method?)
|
101
|
+
end
|
95
102
|
end
|
96
103
|
end
|
97
104
|
end
|
@@ -7,12 +7,18 @@ module RuboCop
|
|
7
7
|
# scope.
|
8
8
|
# The basic idea for this cop was from the warning of `ruby -cw`:
|
9
9
|
#
|
10
|
-
#
|
10
|
+
# [source,console]
|
11
|
+
# ----
|
12
|
+
# assigned but unused variable - foo
|
13
|
+
# ----
|
11
14
|
#
|
12
15
|
# Currently this cop has advanced logic that detects unreferenced
|
13
16
|
# reassignments and properly handles varied cases such as branch, loop,
|
14
17
|
# rescue, ensure, etc.
|
15
18
|
#
|
19
|
+
# NOTE: Given the assignment `foo = 1, bar = 2`, removing unused variables
|
20
|
+
# can lead to a syntax error, so this case is not autocorrected.
|
21
|
+
#
|
16
22
|
# @safety
|
17
23
|
# This cop's autocorrection is unsafe because removing assignment from
|
18
24
|
# operator assignment can cause NameError if this assignment has been used to declare
|
@@ -51,25 +57,24 @@ module RuboCop
|
|
51
57
|
scope.variables.each_value { |variable| check_for_unused_assignments(variable) }
|
52
58
|
end
|
53
59
|
|
60
|
+
# rubocop:disable Metrics/AbcSize
|
54
61
|
def check_for_unused_assignments(variable)
|
55
62
|
return if variable.should_be_unused?
|
56
63
|
|
57
64
|
variable.assignments.each do |assignment|
|
58
|
-
next if assignment.used?
|
65
|
+
next if assignment.used? || part_of_ignored_node?(assignment.node)
|
59
66
|
|
60
67
|
message = message_for_useless_assignment(assignment)
|
68
|
+
range = offense_range(assignment)
|
61
69
|
|
62
|
-
|
63
|
-
|
64
|
-
else
|
65
|
-
assignment.node.loc.name
|
66
|
-
end
|
67
|
-
|
68
|
-
add_offense(location, message: message) do |corrector|
|
69
|
-
autocorrect(corrector, assignment)
|
70
|
+
add_offense(range, message: message) do |corrector|
|
71
|
+
autocorrect(corrector, assignment) unless sequential_assignment?(assignment.node)
|
70
72
|
end
|
73
|
+
|
74
|
+
ignore_node(assignment.node) if chained_assignment?(assignment.node)
|
71
75
|
end
|
72
76
|
end
|
77
|
+
# rubocop:enable Metrics/AbcSize
|
73
78
|
|
74
79
|
def message_for_useless_assignment(assignment)
|
75
80
|
variable = assignment.variable
|
@@ -77,6 +82,28 @@ module RuboCop
|
|
77
82
|
format(MSG, variable: variable.name) + message_specification(assignment, variable).to_s
|
78
83
|
end
|
79
84
|
|
85
|
+
def offense_range(assignment)
|
86
|
+
if assignment.regexp_named_capture?
|
87
|
+
assignment.node.children.first.source_range
|
88
|
+
else
|
89
|
+
assignment.node.loc.name
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def sequential_assignment?(node)
|
94
|
+
if node.lvasgn_type? && node.expression&.array_type? &&
|
95
|
+
node.each_descendant.any?(&:assignment?)
|
96
|
+
return true
|
97
|
+
end
|
98
|
+
return false unless node.parent
|
99
|
+
|
100
|
+
sequential_assignment?(node.parent)
|
101
|
+
end
|
102
|
+
|
103
|
+
def chained_assignment?(node)
|
104
|
+
node.respond_to?(:expression) && node.expression&.lvasgn_type?
|
105
|
+
end
|
106
|
+
|
80
107
|
def message_specification(assignment, variable)
|
81
108
|
if assignment.multiple_assignment?
|
82
109
|
multiple_assignment_message(variable.name)
|
@@ -20,16 +20,17 @@ module RuboCop
|
|
20
20
|
range = offending_range(node, lhs, rhs, style)
|
21
21
|
check(range, node, lhs, rhs)
|
22
22
|
end
|
23
|
+
alias on_csend on_send
|
23
24
|
|
24
25
|
private
|
25
26
|
|
26
|
-
# In a chain of method calls, we regard the top
|
27
|
+
# In a chain of method calls, we regard the top call node as the base
|
27
28
|
# for indentation of all lines following the first. For example:
|
28
29
|
# a.
|
29
30
|
# b c { block }. <-- b is indented relative to a
|
30
31
|
# d <-- d is indented relative to a
|
31
32
|
def left_hand_side(lhs)
|
32
|
-
while lhs.parent&.
|
33
|
+
while lhs.parent&.call_type? && lhs.parent.loc.dot && !lhs.parent.assignment_method?
|
33
34
|
lhs = lhs.parent
|
34
35
|
end
|
35
36
|
lhs
|
@@ -75,7 +75,7 @@ module RuboCop
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def aligned_token?(range, line)
|
78
|
-
aligned_words?(range, line) ||
|
78
|
+
aligned_words?(range, line) || aligned_assignment?(range, line)
|
79
79
|
end
|
80
80
|
|
81
81
|
def aligned_operator?(range, line)
|
@@ -83,13 +83,11 @@ module RuboCop
|
|
83
83
|
end
|
84
84
|
|
85
85
|
def aligned_words?(range, line)
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
def aligned_dot?(range, line)
|
90
|
-
char = line[range.column]
|
86
|
+
left_edge = range.column
|
87
|
+
return true if /\s\S/.match?(line[left_edge - 1, 2])
|
91
88
|
|
92
|
-
|
89
|
+
token = range.source
|
90
|
+
token == line[left_edge, token.length]
|
93
91
|
end
|
94
92
|
|
95
93
|
def aligned_assignment?(range, line)
|
@@ -116,11 +116,11 @@ module RuboCop
|
|
116
116
|
end
|
117
117
|
|
118
118
|
def only_forwards_all?(send_classifications)
|
119
|
-
send_classifications.
|
119
|
+
send_classifications.all? { |_, c, _, _| c == :all }
|
120
120
|
end
|
121
121
|
|
122
122
|
def add_forward_all_offenses(node, send_classifications, forwardable_args)
|
123
|
-
send_classifications.each do |send_node,
|
123
|
+
send_classifications.each do |send_node, _c, forward_rest, _forward_kwrest|
|
124
124
|
register_forward_all_offense(send_node, send_node, forward_rest)
|
125
125
|
end
|
126
126
|
|
@@ -133,7 +133,7 @@ module RuboCop
|
|
133
133
|
|
134
134
|
rest_arg, kwrest_arg, _block_arg = *forwardable_args
|
135
135
|
|
136
|
-
send_classifications.each do |send_node,
|
136
|
+
send_classifications.each do |send_node, _c, forward_rest, forward_kwrest|
|
137
137
|
if forward_rest
|
138
138
|
register_forward_args_offense(def_node.arguments, rest_arg)
|
139
139
|
register_forward_args_offense(send_node, forward_rest)
|
@@ -157,7 +157,7 @@ module RuboCop
|
|
157
157
|
end
|
158
158
|
|
159
159
|
def classify_send_nodes(def_node, send_nodes, referenced_lvars, forwardable_args)
|
160
|
-
send_nodes.
|
160
|
+
send_nodes.filter_map do |send_node|
|
161
161
|
classification_and_forwards = classification_and_forwards(
|
162
162
|
def_node,
|
163
163
|
send_node,
|
@@ -165,8 +165,10 @@ module RuboCop
|
|
165
165
|
forwardable_args
|
166
166
|
)
|
167
167
|
|
168
|
-
|
169
|
-
|
168
|
+
next unless classification_and_forwards
|
169
|
+
|
170
|
+
[send_node, *classification_and_forwards]
|
171
|
+
end
|
170
172
|
end
|
171
173
|
|
172
174
|
def classification_and_forwards(def_node, send_node, referenced_lvars, forwardable_args)
|
@@ -11,6 +11,15 @@ module RuboCop
|
|
11
11
|
# The `array1.intersect?(array2)` method is faster than
|
12
12
|
# `(array1 & array2).any?` and is more readable.
|
13
13
|
#
|
14
|
+
# In cases like the following, compatibility is not ensured,
|
15
|
+
# so it will not be detected when using block argument.
|
16
|
+
#
|
17
|
+
# [source,ruby]
|
18
|
+
# ----
|
19
|
+
# ([1] & [1,2]).any? { |x| false } # => false
|
20
|
+
# [1].intersect?([1,2]) { |x| false } # => true
|
21
|
+
# ----
|
22
|
+
#
|
14
23
|
# @safety
|
15
24
|
# This cop cannot guarantee that `array1` and `array2` are
|
16
25
|
# actually arrays while method `intersect?` is for arrays only.
|
@@ -68,16 +77,15 @@ module RuboCop
|
|
68
77
|
RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
|
69
78
|
|
70
79
|
def on_send(node)
|
80
|
+
return if (parent = node.parent) && (parent.block_type? || parent.numblock_type?)
|
71
81
|
return unless (receiver, argument, method_name = bad_intersection_check?(node))
|
72
82
|
|
73
83
|
message = message(receiver.source, argument.source, method_name)
|
74
84
|
|
75
85
|
add_offense(node, message: message) do |corrector|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
corrector.replace(node, "!#{receiver.source}.intersect?(#{argument.source})")
|
80
|
-
end
|
86
|
+
bang = straight?(method_name) ? '' : '!'
|
87
|
+
|
88
|
+
corrector.replace(node, "#{bang}#{receiver.source}.intersect?(#{argument.source})")
|
81
89
|
end
|
82
90
|
end
|
83
91
|
|
@@ -25,6 +25,7 @@ module RuboCop
|
|
25
25
|
# # bad
|
26
26
|
# items.collect
|
27
27
|
# items.collect!
|
28
|
+
# items.collect_concat
|
28
29
|
# items.inject
|
29
30
|
# items.detect
|
30
31
|
# items.find_all
|
@@ -33,6 +34,7 @@ module RuboCop
|
|
33
34
|
# # good
|
34
35
|
# items.map
|
35
36
|
# items.map!
|
37
|
+
# items.flat_map
|
36
38
|
# items.reduce
|
37
39
|
# items.find
|
38
40
|
# items.select
|
@@ -93,8 +93,9 @@ module RuboCop
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def same_collection_looping_block?(node, sibling)
|
96
|
-
(sibling
|
97
|
-
|
96
|
+
return false if sibling.nil? || (!sibling.block_type? && !sibling.numblock_type?)
|
97
|
+
|
98
|
+
sibling.method?(node.method_name) &&
|
98
99
|
sibling.receiver == node.receiver &&
|
99
100
|
sibling.send_node.arguments == node.send_node.arguments
|
100
101
|
end
|
@@ -40,9 +40,13 @@ module RuboCop
|
|
40
40
|
extend AutoCorrector
|
41
41
|
|
42
42
|
MSG = 'Do not use empty `case` condition, instead use an `if` expression.'
|
43
|
+
NOT_SUPPORTED_PARENT_TYPES = %i[return break next send csend].freeze
|
43
44
|
|
45
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
44
46
|
def on_case(case_node)
|
45
|
-
|
47
|
+
if case_node.condition || NOT_SUPPORTED_PARENT_TYPES.include?(case_node.parent&.type)
|
48
|
+
return
|
49
|
+
end
|
46
50
|
|
47
51
|
branch_bodies = [*case_node.when_branches.map(&:body), case_node.else_branch].compact
|
48
52
|
|
@@ -52,6 +56,7 @@ module RuboCop
|
|
52
56
|
|
53
57
|
add_offense(case_node.loc.keyword) { |corrector| autocorrect(corrector, case_node) }
|
54
58
|
end
|
59
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
55
60
|
|
56
61
|
private
|
57
62
|
|
@@ -39,7 +39,7 @@ module RuboCop
|
|
39
39
|
|
40
40
|
MSG_IF = 'Avoid multi-line ternary operators, use `if` or `unless` instead.'
|
41
41
|
MSG_SINGLE_LINE = 'Avoid multi-line ternary operators, use single-line instead.'
|
42
|
-
SINGLE_LINE_TYPES = %i[return break next send].freeze
|
42
|
+
SINGLE_LINE_TYPES = %i[return break next send csend].freeze
|
43
43
|
|
44
44
|
def on_if(node)
|
45
45
|
return unless offense?(node)
|
@@ -23,6 +23,7 @@ module RuboCop
|
|
23
23
|
MSG = 'Redundant dot detected.'
|
24
24
|
RESTRICT_ON_SEND = %i[| ^ & <=> == === =~ > >= < <= << >> + - * / % ** ~ ! != !~].freeze
|
25
25
|
|
26
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
26
27
|
def on_send(node)
|
27
28
|
return unless (dot = node.loc.dot)
|
28
29
|
return if node.receiver.const_type? || !node.arguments.one?
|
@@ -33,8 +34,12 @@ module RuboCop
|
|
33
34
|
add_offense(dot) do |corrector|
|
34
35
|
wrap_in_parentheses_if_chained(corrector, node)
|
35
36
|
corrector.replace(dot, ' ')
|
37
|
+
|
38
|
+
selector = node.loc.selector
|
39
|
+
corrector.insert_after(selector, ' ') if selector.end_pos == rhs.source_range.begin_pos
|
36
40
|
end
|
37
41
|
end
|
42
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
38
43
|
|
39
44
|
private
|
40
45
|
|
@@ -54,6 +59,7 @@ module RuboCop
|
|
54
59
|
|
55
60
|
def wrap_in_parentheses_if_chained(corrector, node)
|
56
61
|
return unless node.parent&.call_type?
|
62
|
+
return if node.parent.first_argument == node
|
57
63
|
|
58
64
|
operator = node.loc.selector
|
59
65
|
|
@@ -70,19 +70,11 @@ module RuboCop
|
|
70
70
|
|
71
71
|
def replacement_condition(node)
|
72
72
|
condition = node.condition.source
|
73
|
-
expression =
|
73
|
+
expression = redundant_condition_inverted?(node) ? "!(#{condition})" : condition
|
74
74
|
|
75
75
|
node.elsif? ? indented_else_node(expression, node) : expression
|
76
76
|
end
|
77
77
|
|
78
|
-
def invert_expression?(node)
|
79
|
-
(
|
80
|
-
(node.if? || node.elsif? || node.ternary?) && redundant_condition_inverted?(node)
|
81
|
-
) || (
|
82
|
-
node.unless? && redundant_condition?(node)
|
83
|
-
)
|
84
|
-
end
|
85
|
-
|
86
78
|
def indented_else_node(expression, node)
|
87
79
|
"else\n#{indentation(node)}#{expression}"
|
88
80
|
end
|
@@ -3,9 +3,13 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
|
-
# Enforces consistency between
|
6
|
+
# Enforces consistency between `return nil` and `return`.
|
7
7
|
#
|
8
|
-
#
|
8
|
+
# This cop is disabled by default. Because there seems to be a perceived semantic difference
|
9
|
+
# between `return` and `return nil`. The former can be seen as just halting evaluation,
|
10
|
+
# while the latter might be used when the return value is of specific concern.
|
11
|
+
#
|
12
|
+
# Supported styles are `return` and `return_nil`.
|
9
13
|
#
|
10
14
|
# @example EnforcedStyle: return (default)
|
11
15
|
# # bad
|
@@ -173,7 +173,9 @@ module RuboCop
|
|
173
173
|
end
|
174
174
|
|
175
175
|
def correct_for_comment(corrector, node, if_branch)
|
176
|
-
comments = processed_source.ast_with_comments[if_branch]
|
176
|
+
comments = processed_source.ast_with_comments[if_branch].select do |comment|
|
177
|
+
comment.loc.line < if_branch.condition.first_line
|
178
|
+
end
|
177
179
|
comment_text = comments.map(&:text).join("\n") << "\n"
|
178
180
|
|
179
181
|
corrector.insert_before(node.loc.keyword, comment_text) unless comments.empty?
|
@@ -50,6 +50,14 @@ module RuboCop
|
|
50
50
|
PERCENT_MSG = 'Use `%i` or `%I` for an array of symbols.'
|
51
51
|
ARRAY_MSG = 'Use %<prefer>s for an array of symbols.'
|
52
52
|
DELIMITERS = ['[', ']', '(', ')'].freeze
|
53
|
+
SPECIAL_GVARS = %w[
|
54
|
+
$! $" $$ $& $' $* $+ $, $/ $; $: $. $< $= $> $? $@ $\\ $_ $` $~ $0
|
55
|
+
$-0 $-F $-I $-K $-W $-a $-d $-i $-l $-p $-v $-w
|
56
|
+
].freeze
|
57
|
+
REDEFINABLE_OPERATORS = %w(
|
58
|
+
| ^ & <=> == === =~ > >= < <= << >>
|
59
|
+
+ - * / % ** ~ +@ -@ [] []= ` ! != !~
|
60
|
+
).freeze
|
53
61
|
|
54
62
|
class << self
|
55
63
|
attr_accessor :largest_brackets
|
@@ -109,15 +117,6 @@ module RuboCop
|
|
109
117
|
end
|
110
118
|
|
111
119
|
def symbol_without_quote?(string)
|
112
|
-
special_gvars = %w[
|
113
|
-
$! $" $$ $& $' $* $+ $, $/ $; $: $. $< $= $> $? $@ $\\ $_ $` $~ $0
|
114
|
-
$-0 $-F $-I $-K $-W $-a $-d $-i $-l $-p $-v $-w
|
115
|
-
]
|
116
|
-
redefinable_operators = %w(
|
117
|
-
| ^ & <=> == === =~ > >= < <= << >>
|
118
|
-
+ - * / % ** ~ +@ -@ [] []= ` ! != !~
|
119
|
-
)
|
120
|
-
|
121
120
|
# method name
|
122
121
|
/\A[a-zA-Z_]\w*[!?]?\z/.match?(string) ||
|
123
122
|
# instance / class variable
|
@@ -125,8 +124,8 @@ module RuboCop
|
|
125
124
|
# global variable
|
126
125
|
/\A\$[1-9]\d*\z/.match?(string) ||
|
127
126
|
/\A\$[a-zA-Z_]\w*\z/.match?(string) ||
|
128
|
-
|
129
|
-
|
127
|
+
SPECIAL_GVARS.include?(string) ||
|
128
|
+
REDEFINABLE_OPERATORS.include?(string)
|
130
129
|
end
|
131
130
|
end
|
132
131
|
end
|
@@ -19,22 +19,23 @@ module RuboCop
|
|
19
19
|
# differently on different classes, and are not guaranteed to
|
20
20
|
# have the same result if reversed.
|
21
21
|
#
|
22
|
-
# @example SupportedOperators: ['*', '+', '&'']
|
22
|
+
# @example SupportedOperators: ['*', '+', '&', '|', '^'] (default)
|
23
23
|
# # bad
|
24
|
-
# 1 + x
|
25
24
|
# 10 * y
|
25
|
+
# 1 + x
|
26
26
|
# 1 & z
|
27
|
+
# 1 | x
|
28
|
+
# 1 ^ x
|
27
29
|
# 1 + CONST
|
28
30
|
#
|
29
31
|
# # good
|
30
|
-
# 60 * 24
|
31
|
-
# x + 1
|
32
32
|
# y * 10
|
33
|
+
# x + 1
|
33
34
|
# z & 1
|
35
|
+
# x | 1
|
36
|
+
# x ^ 1
|
34
37
|
# CONST + 1
|
35
|
-
#
|
36
|
-
# # good
|
37
|
-
# 1 | x
|
38
|
+
# 60 * 24
|
38
39
|
#
|
39
40
|
class YodaExpression < Base
|
40
41
|
extend AutoCorrector
|
data/lib/rubocop/file_finder.rb
CHANGED
@@ -6,12 +6,8 @@ module RuboCop
|
|
6
6
|
# Common methods for finding files.
|
7
7
|
# @api private
|
8
8
|
module FileFinder
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.root_level?(path, stop_dir)
|
14
|
-
(@root_level || stop_dir) == path.to_s
|
9
|
+
class << self
|
10
|
+
attr_accessor :root_level
|
15
11
|
end
|
16
12
|
|
17
13
|
def find_file_upwards(filename, start_dir, stop_dir = nil)
|
@@ -34,7 +30,8 @@ module RuboCop
|
|
34
30
|
file = dir + filename
|
35
31
|
yield(file.to_s) if file.exist?
|
36
32
|
|
37
|
-
|
33
|
+
dir = dir.to_s
|
34
|
+
break if dir == stop_dir || dir == FileFinder.root_level
|
38
35
|
end
|
39
36
|
end
|
40
37
|
end
|
@@ -7,7 +7,7 @@ module RuboCop
|
|
7
7
|
class MagicComment
|
8
8
|
# IRB's pattern for matching magic comment tokens.
|
9
9
|
# @see https://github.com/ruby/ruby/blob/b4a55c1/lib/irb/magic-file.rb#L5
|
10
|
-
TOKEN =
|
10
|
+
TOKEN = '(?<token>[[:alnum:]\-_]+)'
|
11
11
|
KEYWORDS = {
|
12
12
|
encoding: '(?:en)?coding',
|
13
13
|
frozen_string_literal: 'frozen[_-]string[_-]literal',
|
@@ -129,7 +129,7 @@ module RuboCop
|
|
129
129
|
# @return [String] if pattern matched
|
130
130
|
# @return [nil] otherwise
|
131
131
|
def extract(pattern)
|
132
|
-
@comment[pattern,
|
132
|
+
@comment[pattern, :token]
|
133
133
|
end
|
134
134
|
|
135
135
|
# Parent to Vim and Emacs magic comment handling.
|
@@ -157,10 +157,10 @@ module RuboCop
|
|
157
157
|
# @return [String] extracted value if it is found
|
158
158
|
# @return [nil] otherwise
|
159
159
|
def match(keyword)
|
160
|
-
pattern = /\A#{keyword}\s*#{self.class::OPERATOR}\s
|
160
|
+
pattern = /\A#{keyword}\s*#{self.class::OPERATOR}\s*#{TOKEN}\z/
|
161
161
|
|
162
162
|
tokens.each do |token|
|
163
|
-
next unless (value = token[pattern,
|
163
|
+
next unless (value = token[pattern, :token])
|
164
164
|
|
165
165
|
return value.downcase
|
166
166
|
end
|
@@ -188,7 +188,7 @@ module RuboCop
|
|
188
188
|
# @see https://www.gnu.org/software/emacs/manual/html_node/emacs/Specify-Coding.html
|
189
189
|
# @see https://github.com/ruby/ruby/blob/3f306dc/parse.y#L6873-L6892 Emacs handling in parse.y
|
190
190
|
class EmacsComment < EditorComment
|
191
|
-
REGEXP = /-\*-(
|
191
|
+
REGEXP = /-\*-(?<token>.+)-\*-/.freeze
|
192
192
|
FORMAT = '# -*- %s -*-'
|
193
193
|
SEPARATOR = ';'
|
194
194
|
OPERATOR = ':'
|
@@ -216,7 +216,7 @@ module RuboCop
|
|
216
216
|
#
|
217
217
|
# comment.encoding # => 'ascii-8bit'
|
218
218
|
class VimComment < EditorComment
|
219
|
-
REGEXP = /#\s*vim:\s*(
|
219
|
+
REGEXP = /#\s*vim:\s*(?<token>.+)/.freeze
|
220
220
|
FORMAT = '# vim: %s'
|
221
221
|
SEPARATOR = ', '
|
222
222
|
OPERATOR = '='
|
@@ -259,9 +259,11 @@ module RuboCop
|
|
259
259
|
# comment2.frozen_string_literal # => nil
|
260
260
|
# comment2.encoding # => 'utf-8'
|
261
261
|
class SimpleComment < MagicComment
|
262
|
+
FSTRING_LITERAL_COMMENT = 'frozen_string_literal:\s*(true|false)'
|
263
|
+
|
262
264
|
# Match `encoding` or `coding`
|
263
265
|
def encoding
|
264
|
-
extract(/\A\s
|
266
|
+
extract(/\A\s*\#\s*(#{FSTRING_LITERAL_COMMENT})?\s*#{KEYWORDS[:encoding]}: (#{TOKEN})/io)
|
265
267
|
end
|
266
268
|
|
267
269
|
# Rewrite the comment without a given token type
|
@@ -283,15 +285,15 @@ module RuboCop
|
|
283
285
|
# Case-insensitive and dashes/underscores are acceptable.
|
284
286
|
# @see https://github.com/ruby/ruby/blob/78b95b4/parse.y#L7134-L7138
|
285
287
|
def extract_frozen_string_literal
|
286
|
-
extract(/\A\s*#\s*#{KEYWORDS[:frozen_string_literal]}:\s
|
288
|
+
extract(/\A\s*#\s*#{KEYWORDS[:frozen_string_literal]}:\s*#{TOKEN}\s*\z/io)
|
287
289
|
end
|
288
290
|
|
289
291
|
def extract_shareable_constant_value
|
290
|
-
extract(/\A\s*#\s*#{KEYWORDS[:shareable_constant_value]}:\s
|
292
|
+
extract(/\A\s*#\s*#{KEYWORDS[:shareable_constant_value]}:\s*#{TOKEN}\s*\z/io)
|
291
293
|
end
|
292
294
|
|
293
295
|
def extract_typed
|
294
|
-
extract(/\A\s*#\s*#{KEYWORDS[:typed]}:\s
|
296
|
+
extract(/\A\s*#\s*#{KEYWORDS[:typed]}:\s*#{TOKEN}\s*\z/io)
|
295
297
|
end
|
296
298
|
end
|
297
299
|
end
|
@@ -11,6 +11,8 @@ RSpec.shared_context 'isolated environment' do # rubocop:disable Metrics/BlockLe
|
|
11
11
|
# Make sure to expand all symlinks in the path first. Otherwise we may
|
12
12
|
# get mismatched pathnames when loading config files later on.
|
13
13
|
tmpdir = File.realpath(tmpdir)
|
14
|
+
# Make upwards search for .rubocop.yml files stop at this directory.
|
15
|
+
RuboCop::FileFinder.root_level = tmpdir
|
14
16
|
|
15
17
|
virtual_home = File.expand_path(File.join(tmpdir, 'home'))
|
16
18
|
Dir.mkdir(virtual_home)
|
@@ -21,9 +23,6 @@ RSpec.shared_context 'isolated environment' do # rubocop:disable Metrics/BlockLe
|
|
21
23
|
root = example.metadata[:root]
|
22
24
|
working_dir = root ? File.join(base_dir, 'work', root) : File.join(base_dir, 'work')
|
23
25
|
|
24
|
-
# Make upwards search for .rubocop.yml files stop at this directory.
|
25
|
-
RuboCop::FileFinder.root_level = working_dir
|
26
|
-
|
27
26
|
begin
|
28
27
|
FileUtils.mkdir_p(working_dir)
|
29
28
|
|
data/lib/rubocop/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.56.
|
4
|
+
version: 1.56.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bozhidar Batsov
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date: 2023-
|
13
|
+
date: 2023-09-28 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: base64
|