mutant 0.10.31 → 0.10.35
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/lib/mutant/cli/command/environment/irb.rb +21 -0
- data/lib/mutant/cli/command/environment/run.rb +24 -1
- data/lib/mutant/cli/command/root.rb +2 -2
- data/lib/mutant/cli/command/util.rb +100 -0
- data/lib/mutant/cli/command.rb +4 -0
- data/lib/mutant/mutation.rb +9 -1
- data/lib/mutant/mutator/node/binary.rb +5 -6
- data/lib/mutant/mutator/node/numblock.rb +21 -0
- data/lib/mutant/parallel/worker.rb +0 -3
- data/lib/mutant/pipe.rb +2 -12
- data/lib/mutant/reporter/cli/printer/mutation.rb +58 -0
- data/lib/mutant/reporter/cli/printer/mutation_result.rb +3 -23
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant.rb +5 -0
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b90188339d2ae50f1bc48723f56dc2740b6932a99cd8aae263543734aa66c494
|
4
|
+
data.tar.gz: dcd1e40f9dc9b37467ec067142da4f2c1c6351191de235b2a88237c16b6b936c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 323cc89a39fb5163c7778d5a9ae7e989e207c974436207803d91118c502e17219b47514573d24d2e9003043bb170744110e68c9fa8dc2201236566d1a2f701c7
|
7
|
+
data.tar.gz: 4f27f289f727c157237f65a15241b8adce8f91027894690994908eb698895a4c81f57344138e7c1a010454aee06776a41460f1ed37a4f164871b58e71291c1ca
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
module CLI
|
5
|
+
class Command
|
6
|
+
class Environment
|
7
|
+
class IRB < self
|
8
|
+
NAME = 'irb'
|
9
|
+
SHORT_DESCRIPTION = 'Run irb with mutant environment loaded'
|
10
|
+
SUBCOMMANDS = EMPTY_ARRAY
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def action
|
15
|
+
bootstrap.fmap { TOPLEVEL_BINDING.irb }
|
16
|
+
end
|
17
|
+
end # IRB
|
18
|
+
end # Environment
|
19
|
+
end # Command
|
20
|
+
end # CLI
|
21
|
+
end # Mutant
|
@@ -7,7 +7,7 @@ module Mutant
|
|
7
7
|
class Run < self
|
8
8
|
NAME = 'run'
|
9
9
|
SHORT_DESCRIPTION = 'Run code analysis'
|
10
|
-
SLEEP =
|
10
|
+
SLEEP = 60
|
11
11
|
SUBCOMMANDS = EMPTY_ARRAY
|
12
12
|
|
13
13
|
UNLICENSED = <<~MESSAGE.lines.freeze
|
@@ -16,6 +16,20 @@ module Mutant
|
|
16
16
|
See https://github.com/mbj/mutant#licensing
|
17
17
|
MESSAGE
|
18
18
|
|
19
|
+
NO_TESTS_MESSAGE = <<~'MESSAGE'
|
20
|
+
===============
|
21
|
+
Mutant found no tests. Mutation testing cannot be started.
|
22
|
+
|
23
|
+
This can have various reasons:
|
24
|
+
|
25
|
+
* You did not setup an integration, see:
|
26
|
+
https://github.com/mbj/mutant/blob/main/docs/configuration.md#integration
|
27
|
+
* You set environment variables like RSPEC_OPTS that filter out all tests.
|
28
|
+
* You set configuration optiosn like `config.filter_run :focus` which do
|
29
|
+
make rspec to not report any test.
|
30
|
+
===============
|
31
|
+
MESSAGE
|
32
|
+
|
19
33
|
# Test if command needs to be executed in zombie environment
|
20
34
|
#
|
21
35
|
# @return [Bool]
|
@@ -28,10 +42,19 @@ module Mutant
|
|
28
42
|
def action
|
29
43
|
soft_fail(License.call(world))
|
30
44
|
.bind { bootstrap }
|
45
|
+
.bind(&method(:validate_tests))
|
31
46
|
.bind(&Runner.public_method(:call))
|
32
47
|
.bind(&method(:from_result))
|
33
48
|
end
|
34
49
|
|
50
|
+
def validate_tests(environment)
|
51
|
+
if environment.integration.all_tests.length.zero?
|
52
|
+
Either::Left.new(NO_TESTS_MESSAGE)
|
53
|
+
else
|
54
|
+
Either::Right.new(environment)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
35
58
|
def from_result(result)
|
36
59
|
if result.success?
|
37
60
|
Either::Right.new(nil)
|
@@ -4,13 +4,13 @@ module Mutant
|
|
4
4
|
module CLI
|
5
5
|
class Command
|
6
6
|
class Environment < self
|
7
|
-
SUBCOMMANDS = [Environment::Subject, Environment::Show, Environment::Test].freeze
|
7
|
+
SUBCOMMANDS = [Environment::Subject, Environment::Show, Environment::IRB, Environment::Test].freeze
|
8
8
|
end # Environment
|
9
9
|
|
10
10
|
class Root < self
|
11
11
|
NAME = 'mutant'
|
12
12
|
SHORT_DESCRIPTION = 'mutation testing engine main command'
|
13
|
-
SUBCOMMANDS = [Environment::Run, Environment, Subscription].freeze
|
13
|
+
SUBCOMMANDS = [Environment::Run, Environment, Subscription, Util].freeze
|
14
14
|
end # Root
|
15
15
|
end # Command
|
16
16
|
end # CLI
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
module CLI
|
5
|
+
class Command
|
6
|
+
class Util < self
|
7
|
+
NAME = 'util'
|
8
|
+
SHORT_DESCRIPTION = 'Utility subcommands'
|
9
|
+
|
10
|
+
class Mutation < self
|
11
|
+
NAME = 'mutation'
|
12
|
+
SHORT_DESCRIPTION = 'Print mutations of a code snippet'
|
13
|
+
SUBCOMMANDS = [].freeze
|
14
|
+
OPTIONS = %i[add_target_options].freeze
|
15
|
+
|
16
|
+
def action
|
17
|
+
@targets.each(&method(:print_mutations))
|
18
|
+
Either::Right.new(nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
class Target
|
24
|
+
include Adamantium
|
25
|
+
|
26
|
+
def node
|
27
|
+
Unparser.parse(source)
|
28
|
+
end
|
29
|
+
memoize :node
|
30
|
+
|
31
|
+
class File < self
|
32
|
+
include Concord.new(:pathname, :source)
|
33
|
+
|
34
|
+
public :source
|
35
|
+
|
36
|
+
def identification
|
37
|
+
"file:#{pathname}"
|
38
|
+
end
|
39
|
+
end # File
|
40
|
+
|
41
|
+
class Source < self
|
42
|
+
include Concord::Public.new(:source)
|
43
|
+
|
44
|
+
def identification
|
45
|
+
'<cli-source>'
|
46
|
+
end
|
47
|
+
end # source
|
48
|
+
end # Target
|
49
|
+
|
50
|
+
def initialize(_arguments)
|
51
|
+
super
|
52
|
+
|
53
|
+
@targets = []
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_target_options(parser)
|
57
|
+
parser.on('-e', '--evaluate SOURCE') do |source|
|
58
|
+
@targets << Target::Source.new(source)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def print_mutations(target)
|
63
|
+
world.stdout.puts(target.identification)
|
64
|
+
Mutator.mutate(target.node).each do |mutation|
|
65
|
+
Reporter::CLI::Printer::Mutation.call(
|
66
|
+
world.stdout,
|
67
|
+
Mutant::Mutation::Evil.new(target, mutation)
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_remaining_arguments(arguments)
|
73
|
+
@targets.concat(
|
74
|
+
arguments.map do |argument|
|
75
|
+
parse_pathname(argument)
|
76
|
+
.bind(&method(:read_file))
|
77
|
+
.from_right { |error| return Either::Left.new(error) }
|
78
|
+
end
|
79
|
+
)
|
80
|
+
|
81
|
+
Either::Right.new(self)
|
82
|
+
end
|
83
|
+
|
84
|
+
def read_file(pathname)
|
85
|
+
Either::Right.new(Target::File.new(pathname, pathname.read))
|
86
|
+
rescue StandardError => exception
|
87
|
+
Either::Left.new("Cannot read file: #{exception}")
|
88
|
+
end
|
89
|
+
|
90
|
+
def parse_pathname(input)
|
91
|
+
Either.wrap_error(ArgumentError) { Pathname.new(input) }
|
92
|
+
.lmap(&:message)
|
93
|
+
end
|
94
|
+
end # Mutation
|
95
|
+
|
96
|
+
SUBCOMMANDS = [Mutation].freeze
|
97
|
+
end # Util
|
98
|
+
end # Command
|
99
|
+
end # CLI
|
100
|
+
end # Mutant
|
data/lib/mutant/cli/command.rb
CHANGED
@@ -69,6 +69,8 @@ module Mutant
|
|
69
69
|
false
|
70
70
|
end
|
71
71
|
|
72
|
+
abstract_method :action
|
73
|
+
|
72
74
|
private
|
73
75
|
|
74
76
|
def subcommands
|
@@ -128,6 +130,8 @@ module Mutant
|
|
128
130
|
end
|
129
131
|
|
130
132
|
def add_global_options(parser)
|
133
|
+
parser.separator("mutant version: #{VERSION}")
|
134
|
+
parser.separator(nil)
|
131
135
|
parser.separator('Global Options:')
|
132
136
|
parser.separator(nil)
|
133
137
|
|
data/lib/mutant/mutation.rb
CHANGED
@@ -72,6 +72,15 @@ module Mutant
|
|
72
72
|
)
|
73
73
|
end
|
74
74
|
|
75
|
+
# Rendered mutation diff
|
76
|
+
#
|
77
|
+
# @return [String, nil]
|
78
|
+
# the diff, if present
|
79
|
+
def diff
|
80
|
+
Unparser::Diff.build(original_source, source)
|
81
|
+
end
|
82
|
+
memoize :diff
|
83
|
+
|
75
84
|
private
|
76
85
|
|
77
86
|
def sha1
|
@@ -80,7 +89,6 @@ module Mutant
|
|
80
89
|
|
81
90
|
# Evil mutation that should case mutations to fail tests
|
82
91
|
class Evil < self
|
83
|
-
|
84
92
|
SYMBOL = 'evil'
|
85
93
|
TEST_PASS_SUCCESS = false
|
86
94
|
|
@@ -21,8 +21,11 @@ module Mutant
|
|
21
21
|
emit_singletons
|
22
22
|
emit_promotions
|
23
23
|
emit_operator_mutations
|
24
|
-
|
25
|
-
emit_left_mutations
|
24
|
+
|
25
|
+
emit_left_mutations do |mutation|
|
26
|
+
!(n_irange?(mutation) || n_erange?(mutation)) || !mutation.children.fetch(1).nil?
|
27
|
+
end
|
28
|
+
|
26
29
|
emit_right_mutations
|
27
30
|
end
|
28
31
|
|
@@ -35,10 +38,6 @@ module Mutant
|
|
35
38
|
emit(right)
|
36
39
|
end
|
37
40
|
|
38
|
-
def emit_left_negation
|
39
|
-
emit(s(node.type, n_not(left), right))
|
40
|
-
end
|
41
|
-
|
42
41
|
end # Binary
|
43
42
|
end # Node
|
44
43
|
end # Mutator
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Mutator
|
5
|
+
class Node
|
6
|
+
class Numblock < self
|
7
|
+
|
8
|
+
handle(:numblock)
|
9
|
+
|
10
|
+
children :receiver, :count, :block
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def dispatch
|
15
|
+
emit_nil
|
16
|
+
emit_receiver_mutations(&method(:n_send?))
|
17
|
+
end
|
18
|
+
end # Numblock
|
19
|
+
end # Node
|
20
|
+
end # Mutator
|
21
|
+
end # Mutant
|
data/lib/mutant/pipe.rb
CHANGED
@@ -35,18 +35,6 @@ module Mutant
|
|
35
35
|
reader
|
36
36
|
end
|
37
37
|
|
38
|
-
# Set binmode (again)
|
39
|
-
#
|
40
|
-
# Ruby has a bug where the binmode setting may be lost duringa fork.
|
41
|
-
# This API allows to set the binmode again.
|
42
|
-
#
|
43
|
-
# @return [self]
|
44
|
-
def reset_binmode
|
45
|
-
reader.binmode
|
46
|
-
writer.binmode
|
47
|
-
self
|
48
|
-
end
|
49
|
-
|
50
38
|
class Connection
|
51
39
|
include Anima.new(:marshal, :reader, :writer)
|
52
40
|
|
@@ -69,6 +57,7 @@ module Mutant
|
|
69
57
|
|
70
58
|
fail Error, 'message to big' if bytesize > MAX_BYTES
|
71
59
|
|
60
|
+
io.binmode
|
72
61
|
io.write([bytesize].pack(HEADER_FORMAT))
|
73
62
|
io.write(body)
|
74
63
|
end
|
@@ -76,6 +65,7 @@ module Mutant
|
|
76
65
|
private
|
77
66
|
|
78
67
|
def read(bytes)
|
68
|
+
io.binmode
|
79
69
|
io.read(bytes) or fail Error, 'Unexpected EOF'
|
80
70
|
end
|
81
71
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Reporter
|
5
|
+
class CLI
|
6
|
+
class Printer
|
7
|
+
# Reporter for mutations
|
8
|
+
class Mutation < self
|
9
|
+
NO_DIFF_MESSAGE = <<~'MESSAGE'
|
10
|
+
--- Internal failure ---
|
11
|
+
BUG: A generated mutation did not result in exactly one diff hunk!
|
12
|
+
This is an invariant violation by the mutation generation engine.
|
13
|
+
Please report a reproduction to https://github.com/mbj/mutant
|
14
|
+
Original unparsed source:
|
15
|
+
%s
|
16
|
+
Original AST:
|
17
|
+
%s
|
18
|
+
Mutated unparsed source:
|
19
|
+
%s
|
20
|
+
Mutated AST:
|
21
|
+
%s
|
22
|
+
MESSAGE
|
23
|
+
|
24
|
+
SEPARATOR = '-----------------------'
|
25
|
+
|
26
|
+
# Run report printer
|
27
|
+
#
|
28
|
+
# @return [undefined]
|
29
|
+
def run
|
30
|
+
diff = object.diff
|
31
|
+
diff = color? ? diff.colorized_diff : diff.diff
|
32
|
+
|
33
|
+
if diff
|
34
|
+
output.write(diff)
|
35
|
+
else
|
36
|
+
print_no_diff_message
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def print_no_diff_message
|
41
|
+
info(
|
42
|
+
NO_DIFF_MESSAGE,
|
43
|
+
object.original_source,
|
44
|
+
original_node.inspect,
|
45
|
+
object.source,
|
46
|
+
object.node.inspect
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def original_node
|
51
|
+
object.subject.node
|
52
|
+
end
|
53
|
+
|
54
|
+
end # MutationResult
|
55
|
+
end # Printer
|
56
|
+
end # CLI
|
57
|
+
end # Reporter
|
58
|
+
end # Mutant
|
@@ -27,7 +27,7 @@ module Mutant
|
|
27
27
|
|
28
28
|
NO_DIFF_MESSAGE = <<~'MESSAGE'
|
29
29
|
--- Internal failure ---
|
30
|
-
BUG: A
|
30
|
+
BUG: A generated mutation did not result in exactly one diff hunk!
|
31
31
|
This is an invariant violation by the mutation generation engine.
|
32
32
|
Please report a reproduction to https://github.com/mbj/mutant
|
33
33
|
Original unparsed source:
|
@@ -66,23 +66,7 @@ module Mutant
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def evil_details
|
69
|
-
|
70
|
-
diff = color? ? diff.colorized_diff : diff.diff
|
71
|
-
if diff
|
72
|
-
output.write(diff)
|
73
|
-
else
|
74
|
-
print_no_diff_message
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def print_no_diff_message
|
79
|
-
info(
|
80
|
-
NO_DIFF_MESSAGE,
|
81
|
-
mutation.original_source,
|
82
|
-
original_node.inspect,
|
83
|
-
mutation.source,
|
84
|
-
mutation.node.inspect
|
85
|
-
)
|
69
|
+
visit(Mutation, mutation)
|
86
70
|
end
|
87
71
|
|
88
72
|
def noop_details
|
@@ -90,11 +74,7 @@ module Mutant
|
|
90
74
|
end
|
91
75
|
|
92
76
|
def neutral_details
|
93
|
-
info(NEUTRAL_MESSAGE,
|
94
|
-
end
|
95
|
-
|
96
|
-
def original_node
|
97
|
-
mutation.subject.node
|
77
|
+
info(NEUTRAL_MESSAGE, mutation.node.inspect, mutation.source)
|
98
78
|
end
|
99
79
|
|
100
80
|
end # MutationResult
|
data/lib/mutant/version.rb
CHANGED
data/lib/mutant.rb
CHANGED
@@ -4,6 +4,7 @@ require 'diff/lcs'
|
|
4
4
|
require 'diff/lcs/hunk'
|
5
5
|
require 'digest/sha1'
|
6
6
|
require 'etc'
|
7
|
+
require 'irb'
|
7
8
|
require 'json'
|
8
9
|
require 'open3'
|
9
10
|
require 'optparse'
|
@@ -152,6 +153,7 @@ require 'mutant/mutator/node/match_current_line'
|
|
152
153
|
require 'mutant/mutator/node/index'
|
153
154
|
require 'mutant/mutator/node/procarg_zero'
|
154
155
|
require 'mutant/mutator/node/kwargs'
|
156
|
+
require 'mutant/mutator/node/numblock'
|
155
157
|
require 'mutant/loader'
|
156
158
|
require 'mutant/context'
|
157
159
|
require 'mutant/scope'
|
@@ -193,10 +195,12 @@ require 'mutant/cli'
|
|
193
195
|
require 'mutant/cli/command'
|
194
196
|
require 'mutant/cli/command/subscription'
|
195
197
|
require 'mutant/cli/command/environment'
|
198
|
+
require 'mutant/cli/command/environment/irb'
|
196
199
|
require 'mutant/cli/command/environment/run'
|
197
200
|
require 'mutant/cli/command/environment/show'
|
198
201
|
require 'mutant/cli/command/environment/subject'
|
199
202
|
require 'mutant/cli/command/environment/test'
|
203
|
+
require 'mutant/cli/command/util'
|
200
204
|
require 'mutant/cli/command/root'
|
201
205
|
require 'mutant/runner'
|
202
206
|
require 'mutant/runner/sink'
|
@@ -212,6 +216,7 @@ require 'mutant/reporter/cli/printer/env'
|
|
212
216
|
require 'mutant/reporter/cli/printer/env_progress'
|
213
217
|
require 'mutant/reporter/cli/printer/env_result'
|
214
218
|
require 'mutant/reporter/cli/printer/isolation_result'
|
219
|
+
require 'mutant/reporter/cli/printer/mutation'
|
215
220
|
require 'mutant/reporter/cli/printer/mutation_result'
|
216
221
|
require 'mutant/reporter/cli/printer/status_progressive'
|
217
222
|
require 'mutant/reporter/cli/printer/subject_result'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mutant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.
|
4
|
+
version: 0.10.35
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Markus Schirp
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-10-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: diff-lcs
|
@@ -180,12 +180,14 @@ files:
|
|
180
180
|
- lib/mutant/cli.rb
|
181
181
|
- lib/mutant/cli/command.rb
|
182
182
|
- lib/mutant/cli/command/environment.rb
|
183
|
+
- lib/mutant/cli/command/environment/irb.rb
|
183
184
|
- lib/mutant/cli/command/environment/run.rb
|
184
185
|
- lib/mutant/cli/command/environment/show.rb
|
185
186
|
- lib/mutant/cli/command/environment/subject.rb
|
186
187
|
- lib/mutant/cli/command/environment/test.rb
|
187
188
|
- lib/mutant/cli/command/root.rb
|
188
189
|
- lib/mutant/cli/command/subscription.rb
|
190
|
+
- lib/mutant/cli/command/util.rb
|
189
191
|
- lib/mutant/config.rb
|
190
192
|
- lib/mutant/config/coverage_criteria.rb
|
191
193
|
- lib/mutant/context.rb
|
@@ -268,6 +270,7 @@ files:
|
|
268
270
|
- lib/mutant/mutator/node/next.rb
|
269
271
|
- lib/mutant/mutator/node/noop.rb
|
270
272
|
- lib/mutant/mutator/node/nthref.rb
|
273
|
+
- lib/mutant/mutator/node/numblock.rb
|
271
274
|
- lib/mutant/mutator/node/op_asgn.rb
|
272
275
|
- lib/mutant/mutator/node/or_asgn.rb
|
273
276
|
- lib/mutant/mutator/node/procarg_zero.rb
|
@@ -316,6 +319,7 @@ files:
|
|
316
319
|
- lib/mutant/reporter/cli/printer/env_progress.rb
|
317
320
|
- lib/mutant/reporter/cli/printer/env_result.rb
|
318
321
|
- lib/mutant/reporter/cli/printer/isolation_result.rb
|
322
|
+
- lib/mutant/reporter/cli/printer/mutation.rb
|
319
323
|
- lib/mutant/reporter/cli/printer/mutation_result.rb
|
320
324
|
- lib/mutant/reporter/cli/printer/status_progressive.rb
|
321
325
|
- lib/mutant/reporter/cli/printer/subject_result.rb
|
@@ -357,14 +361,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
357
361
|
requirements:
|
358
362
|
- - ">="
|
359
363
|
- !ruby/object:Gem::Version
|
360
|
-
version: '2.
|
364
|
+
version: '2.6'
|
361
365
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
362
366
|
requirements:
|
363
367
|
- - ">="
|
364
368
|
- !ruby/object:Gem::Version
|
365
369
|
version: '0'
|
366
370
|
requirements: []
|
367
|
-
rubygems_version: 3.
|
371
|
+
rubygems_version: 3.1.6
|
368
372
|
signing_key:
|
369
373
|
specification_version: 4
|
370
374
|
summary: ''
|