power_assert 3.0.0 → 3.1.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/.github/workflows/ci.yml +1 -1
- data/lib/power_assert/configuration.rb +14 -2
- data/lib/power_assert/context.rb +3 -2
- data/lib/power_assert/parser.rb +186 -122
- data/lib/power_assert/version.rb +1 -1
- data/lib/power_assert.rb +3 -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: 85c370c0b3194fff746c4ee4af9a2f2e6ec67c4cc676bfb97a8a138df4a4a273
|
|
4
|
+
data.tar.gz: 21f6b36d50e8e0580889ff1ccacf88920075c90543f604b47a18e29360782d6b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 35c8a4cb2fd2539ff16faa1c03bffa965786f52dbaad41c86a410d89bfd22bfccb6c32f833030d94904454e03251ace97de0dbf4a41f5977cf8f9e749fff7875
|
|
7
|
+
data.tar.gz: f5a06634da7fc0bd3399a8ab9f1db0277421b65d66d460eaf7dd00f5281f5254d69fab8c0106c3bf4e614eb6f8b9ab8d9c6e70d613b08e271222a3c2db0d0c0f
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module PowerAssert
|
|
2
2
|
class << self
|
|
3
3
|
def configuration
|
|
4
|
-
@configuration ||= Configuration[false, true, false, :p]
|
|
4
|
+
@configuration ||= Configuration[false, true, false, :p, :ripper]
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
def configure
|
|
@@ -9,7 +9,7 @@ module PowerAssert
|
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
class Configuration < Struct.new(:lazy_inspection, :_redefinition, :colorize_message, :inspector)
|
|
12
|
+
class Configuration < Struct.new(:lazy_inspection, :_redefinition, :colorize_message, :inspector, :parser)
|
|
13
13
|
def colorize_message=(bool)
|
|
14
14
|
if bool
|
|
15
15
|
require 'irb/color'
|
|
@@ -41,6 +41,18 @@ module PowerAssert
|
|
|
41
41
|
end
|
|
42
42
|
super
|
|
43
43
|
end
|
|
44
|
+
|
|
45
|
+
def parser=(parser)
|
|
46
|
+
case parser
|
|
47
|
+
when :prism
|
|
48
|
+
require 'prism'
|
|
49
|
+
when :ripper
|
|
50
|
+
require 'ripper'
|
|
51
|
+
else
|
|
52
|
+
raise ArgumentError, "unknown parser: #{parser}"
|
|
53
|
+
end
|
|
54
|
+
super
|
|
55
|
+
end
|
|
44
56
|
end
|
|
45
57
|
private_constant :Configuration
|
|
46
58
|
end
|
data/lib/power_assert/context.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
require 'power_assert/parser'
|
|
1
2
|
require 'power_assert/configuration'
|
|
2
3
|
require 'power_assert/enable_tracepoint_events'
|
|
3
4
|
require 'power_assert/inspector'
|
|
4
|
-
require 'power_assert/parser'
|
|
5
5
|
|
|
6
6
|
module PowerAssert
|
|
7
7
|
class Context
|
|
@@ -30,7 +30,8 @@ module PowerAssert
|
|
|
30
30
|
line ||= File.open(path) {|fp| fp.each_line.drop(lineno - 1).first }
|
|
31
31
|
end
|
|
32
32
|
if line
|
|
33
|
-
|
|
33
|
+
parser_class = PowerAssert.configuration.parser == :prism ? Parser::PrismParser : Parser::RipperParser
|
|
34
|
+
@parser = parser_class.new(line, path, lineno, @assertion_proc.binding, assertion_method.to_s, @assertion_proc)
|
|
34
35
|
end
|
|
35
36
|
end
|
|
36
37
|
end
|
data/lib/power_assert/parser.rb
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
require 'ripper'
|
|
2
|
-
|
|
3
1
|
module PowerAssert
|
|
4
2
|
class Parser
|
|
5
3
|
Ident = Struct.new(:type, :name, :column)
|
|
6
4
|
|
|
5
|
+
class Branch < Array
|
|
6
|
+
end
|
|
7
|
+
|
|
7
8
|
attr_reader :line, :path, :lineno, :binding
|
|
8
9
|
|
|
9
10
|
def initialize(line, path, lineno, binding, assertion_method_name = nil, assertion_proc = nil)
|
|
@@ -18,7 +19,7 @@ module PowerAssert
|
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def idents
|
|
21
|
-
@idents ||=
|
|
22
|
+
@idents ||= []
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
def call_paths
|
|
@@ -52,134 +53,197 @@ module PowerAssert
|
|
|
52
53
|
str
|
|
53
54
|
end
|
|
54
55
|
|
|
55
|
-
class
|
|
56
|
-
|
|
56
|
+
class RipperParser < Parser
|
|
57
|
+
def idents
|
|
58
|
+
@idents ||=
|
|
59
|
+
begin
|
|
60
|
+
require 'ripper'
|
|
61
|
+
extract_idents(Ripper.sexp(@line_for_parsing))
|
|
62
|
+
end
|
|
63
|
+
end
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
AND_OR_OPS = %i(and or && ||)
|
|
68
|
+
|
|
69
|
+
#
|
|
70
|
+
# Returns idents as graph structure.
|
|
71
|
+
#
|
|
72
|
+
# +--c--b--+
|
|
73
|
+
# extract_idents(Ripper.sexp('a&.b(c).d')) #=> a--+ +--d
|
|
74
|
+
# +--------+
|
|
75
|
+
#
|
|
76
|
+
def extract_idents(sexp)
|
|
77
|
+
case sexp
|
|
78
|
+
in [:arg_paren | :assoc_splat | :fcall | :hash | :method_add_block | :string_literal | :return, s, *]
|
|
79
|
+
extract_idents(s)
|
|
80
|
+
in [:assign | :massign, _, s]
|
|
81
|
+
extract_idents(s)
|
|
82
|
+
in [:opassign, _, [_, op_name, [_, op_column]], s]
|
|
83
|
+
extract_idents(s) + [Ident[:method, op_name.sub(/=\z/, ''), op_column]]
|
|
84
|
+
in [:dyna_symbol, [Symbol, *] => s]
|
|
85
|
+
# s can be [:string_content, [..]] while parsing an expression like { "a": 1 }
|
|
86
|
+
extract_idents(s)
|
|
87
|
+
in [:dyna_symbol, ss]
|
|
88
|
+
ss.flat_map {|s| extract_idents(s) }
|
|
89
|
+
in [:assoclist_from_args | :bare_assoc_hash | :paren | :string_embexpr | :regexp_literal | :xstring_literal, ss, *]
|
|
90
|
+
ss.flat_map {|s| extract_idents(s) }
|
|
91
|
+
in [:command, s0, s1]
|
|
92
|
+
[s1, s0].flat_map {|s| extract_idents(s) }
|
|
93
|
+
in [:assoc_new | :dot2 | :dot3 | :string_content, *ss]
|
|
94
|
+
ss.flat_map {|s| extract_idents(s) }
|
|
95
|
+
in [:unary, mid, s]
|
|
96
|
+
handle_columnless_ident([], mid, extract_idents(s))
|
|
97
|
+
in [:binary, s0, op, s1] if AND_OR_OPS.include?(op)
|
|
98
|
+
extract_idents(s0) + [Branch[extract_idents(s1), []]]
|
|
99
|
+
in [:binary, s0, op, s1]
|
|
100
|
+
handle_columnless_ident(extract_idents(s0), op, extract_idents(s1))
|
|
101
|
+
in [:call, recv, [op_sym, op_name, _], method]
|
|
102
|
+
with_safe_op = ((op_sym == :@op and op_name == '&.') or op_sym == :"&.")
|
|
103
|
+
if method == :call
|
|
104
|
+
handle_columnless_ident(extract_idents(recv), :call, [], with_safe_op)
|
|
105
|
+
else
|
|
106
|
+
extract_idents(recv) + (with_safe_op ? [Branch[extract_idents(method), []]] : extract_idents(method))
|
|
107
|
+
end
|
|
108
|
+
in [:array, ss]
|
|
109
|
+
ss ? ss.flat_map {|s| extract_idents(s) } : []
|
|
110
|
+
in [:command_call, s0, _, s1, s2]
|
|
111
|
+
[s0, s2, s1].flat_map {|s| extract_idents(s) }
|
|
112
|
+
in [:aref, s0, s1]
|
|
113
|
+
handle_columnless_ident(extract_idents(s0), :[], extract_idents(s1))
|
|
114
|
+
in [:method_add_arg, s0, s1]
|
|
115
|
+
case extract_idents(s0)
|
|
116
|
+
in []
|
|
117
|
+
# idents(s0) may be empty(e.g. ->{}.())
|
|
118
|
+
extract_idents(s1)
|
|
119
|
+
in [*is0, Branch[is1, []]]
|
|
120
|
+
# Safe navigation operator is used. See :call clause also.
|
|
121
|
+
is0 + [Branch[extract_idents(s1) + is1, []]]
|
|
122
|
+
in [*is, i]
|
|
123
|
+
is + extract_idents(s1) + [i]
|
|
124
|
+
end
|
|
125
|
+
in [:args_add_block, [:args_add_star, ss0, *ss1], _]
|
|
126
|
+
(ss0 + ss1).flat_map {|s| extract_idents(s) }
|
|
127
|
+
in [:args_add_block, ss, _]
|
|
128
|
+
ss.flat_map {|s| extract_idents(s) }
|
|
129
|
+
in [:vcall, [:@ident, name, [_, column]]]
|
|
130
|
+
[Ident[@proc_local_variables.include?(name) ? :ref : :method, name, column]]
|
|
131
|
+
in [:vcall, _]
|
|
132
|
+
[]
|
|
133
|
+
in [:program, [[:method_add_block, [:method_add_arg, [:fcall, [:@ident | :@const, ^@assertion_method_name, _]], _], [:brace_block | :do_block, _, ss]]]]
|
|
134
|
+
ss.flat_map {|s| extract_idents(s) }
|
|
135
|
+
in [:program, [s, *]]
|
|
136
|
+
extract_idents(s)
|
|
137
|
+
in [:ifop, s0, s1, s2]
|
|
138
|
+
[*extract_idents(s0), Branch[extract_idents(s1), extract_idents(s2)]]
|
|
139
|
+
in [:if | :unless, s0, ss0, [_, ss1]]
|
|
140
|
+
[*extract_idents(s0), Branch[ss0.flat_map {|s| extract_idents(s) }, ss1.flat_map {|s| extract_idents(s) }]]
|
|
141
|
+
in [:if | :unless, s0, ss0, _]
|
|
142
|
+
[*extract_idents(s0), Branch[ss0.flat_map {|s| extract_idents(s) }, []]]
|
|
143
|
+
in [:if_mod | :unless_mod, s0, s1]
|
|
144
|
+
[*extract_idents(s0), Branch[extract_idents(s1), []]]
|
|
145
|
+
in [:var_ref | :var_field, [:@kw, 'self', [_, column]]]
|
|
146
|
+
[Ident[:ref, 'self', column]]
|
|
147
|
+
in [:var_ref | :var_field, [:@ident | :@const | :@cvar | :@ivar | :@gvar, ref_name, [_, column]]]
|
|
148
|
+
[Ident[:ref, ref_name, column]]
|
|
149
|
+
in [:var_ref | :var_field, _]
|
|
150
|
+
[]
|
|
151
|
+
in [:@ident | :@const | :@op, method_name, [_, column]]
|
|
152
|
+
[Ident[:method, method_name, column]]
|
|
96
153
|
else
|
|
97
|
-
|
|
154
|
+
[]
|
|
98
155
|
end
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
in []
|
|
108
|
-
# idents(s0) may be empty(e.g. ->{}.())
|
|
109
|
-
extract_idents(s1)
|
|
110
|
-
in [*is0, Branch[is1, []]]
|
|
111
|
-
# Safe navigation operator is used. See :call clause also.
|
|
112
|
-
is0 + [Branch[extract_idents(s1) + is1, []]]
|
|
113
|
-
in [*is, i]
|
|
114
|
-
is + extract_idents(s1) + [i]
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def str_indices(str, re, offset, limit)
|
|
159
|
+
idx = str.index(re, offset)
|
|
160
|
+
if idx and idx <= limit
|
|
161
|
+
[idx, *str_indices(str, re, idx + 1, limit)]
|
|
162
|
+
else
|
|
163
|
+
[]
|
|
115
164
|
end
|
|
116
|
-
in [:args_add_block, [:args_add_star, ss0, *ss1], _]
|
|
117
|
-
(ss0 + ss1).flat_map {|s| extract_idents(s) }
|
|
118
|
-
in [:args_add_block, ss, _]
|
|
119
|
-
ss.flat_map {|s| extract_idents(s) }
|
|
120
|
-
in [:vcall, [:@ident, name, [_, column]]]
|
|
121
|
-
[Ident[@proc_local_variables.include?(name) ? :ref : :method, name, column]]
|
|
122
|
-
in [:vcall, _]
|
|
123
|
-
[]
|
|
124
|
-
in [:program, [[:method_add_block, [:method_add_arg, [:fcall, [:@ident | :@const, ^@assertion_method_name, _]], _], [:brace_block | :do_block, _, ss]]]]
|
|
125
|
-
ss.flat_map {|s| extract_idents(s) }
|
|
126
|
-
in [:program, [s, *]]
|
|
127
|
-
extract_idents(s)
|
|
128
|
-
in [:ifop, s0, s1, s2]
|
|
129
|
-
[*extract_idents(s0), Branch[extract_idents(s1), extract_idents(s2)]]
|
|
130
|
-
in [:if | :unless, s0, ss0, [_, ss1]]
|
|
131
|
-
[*extract_idents(s0), Branch[ss0.flat_map {|s| extract_idents(s) }, ss1.flat_map {|s| extract_idents(s) }]]
|
|
132
|
-
in [:if | :unless, s0, ss0, _]
|
|
133
|
-
[*extract_idents(s0), Branch[ss0.flat_map {|s| extract_idents(s) }, []]]
|
|
134
|
-
in [:if_mod | :unless_mod, s0, s1]
|
|
135
|
-
[*extract_idents(s0), Branch[extract_idents(s1), []]]
|
|
136
|
-
in [:var_ref | :var_field, [:@kw, 'self', [_, column]]]
|
|
137
|
-
[Ident[:ref, 'self', column]]
|
|
138
|
-
in [:var_ref | :var_field, [:@ident | :@const | :@cvar | :@ivar | :@gvar, ref_name, [_, column]]]
|
|
139
|
-
[Ident[:ref, ref_name, column]]
|
|
140
|
-
in [:var_ref | :var_field, _]
|
|
141
|
-
[]
|
|
142
|
-
in [:@ident | :@const | :@op, method_name, [_, column]]
|
|
143
|
-
[Ident[:method, method_name, column]]
|
|
144
|
-
else
|
|
145
|
-
[]
|
|
146
165
|
end
|
|
147
|
-
end
|
|
148
166
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
167
|
+
MID2SRCTXT = {
|
|
168
|
+
:[] => '[',
|
|
169
|
+
:+@ => '+',
|
|
170
|
+
:-@ => '-',
|
|
171
|
+
:call => '('
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
def handle_columnless_ident(left_idents, mid, right_idents, with_safe_op = false)
|
|
175
|
+
left_max = left_idents.flatten.max_by(&:column)
|
|
176
|
+
right_min = right_idents.flatten.min_by(&:column)
|
|
177
|
+
bg = left_max ? left_max.column + left_max.name.length : 0
|
|
178
|
+
ed = right_min ? right_min.column - 1 : @line_for_parsing.length - 1
|
|
179
|
+
mname = mid.to_s
|
|
180
|
+
srctxt = MID2SRCTXT[mid] || mname
|
|
181
|
+
re = /
|
|
182
|
+
#{'\b' if /\A\w/ =~ srctxt}
|
|
183
|
+
#{Regexp.escape(srctxt)}
|
|
184
|
+
#{'\b' if /\w\z/ =~ srctxt}
|
|
185
|
+
/x
|
|
186
|
+
indices = str_indices(@line_for_parsing, re, bg, ed)
|
|
187
|
+
if indices.length == 1 or !(right_idents.empty? and left_idents.empty?)
|
|
188
|
+
ident = Ident[:method, mname, right_idents.empty? ? indices.first : indices.last]
|
|
189
|
+
left_idents + right_idents + (with_safe_op ? [Branch[[ident], []]] : [ident])
|
|
190
|
+
else
|
|
191
|
+
left_idents + right_idents
|
|
192
|
+
end
|
|
155
193
|
end
|
|
156
194
|
end
|
|
157
195
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
196
|
+
class PrismParser < Parser
|
|
197
|
+
def idents
|
|
198
|
+
@idents ||=
|
|
199
|
+
begin
|
|
200
|
+
require 'prism'
|
|
201
|
+
result = Prism.parse(@line_for_parsing, scopes: [@proc_local_variables.map(&:to_sym)])
|
|
202
|
+
result.success? ? extract_idents(result.value) : []
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
private
|
|
207
|
+
|
|
208
|
+
def extract_idents(node)
|
|
209
|
+
case node&.type
|
|
210
|
+
when nil
|
|
211
|
+
[]
|
|
212
|
+
when :program_node
|
|
213
|
+
if (statement = node.statements.body.first).is_a?(Prism::CallNode) && (statement.name == @assertion_method_name&.to_sym) && (block = statement.block).is_a?(Prism::BlockNode) && (body = block.body).is_a?(Prism::StatementsNode)
|
|
214
|
+
extract_idents(body)
|
|
215
|
+
else
|
|
216
|
+
extract_idents(statement)
|
|
217
|
+
end
|
|
218
|
+
when :call_node
|
|
219
|
+
message_ident =
|
|
220
|
+
if (node.name == :[] && !node.call_operator_loc)
|
|
221
|
+
Ident[:method, "[]", node.opening_loc.start_column]
|
|
222
|
+
elsif (message_loc = node.message_loc)
|
|
223
|
+
Ident[:method, node.name.name, message_loc.start_column]
|
|
224
|
+
else
|
|
225
|
+
Ident[:method, "call", node.opening_loc.start_column]
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
if node.safe_navigation?
|
|
229
|
+
extract_idents(node.receiver) + [Branch[extract_idents(node.arguments) + [message_ident], []]]
|
|
230
|
+
else
|
|
231
|
+
extract_idents(node.receiver) + extract_idents(node.arguments) + [message_ident]
|
|
232
|
+
end
|
|
233
|
+
when :call_operator_write_node, :class_variable_operator_write_node, :constant_operator_write_node, :constant_path_operator_write_node, :global_variable_operator_write_node, :index_operator_write_node, :instance_variable_operator_write_node, :local_variable_operator_write_node
|
|
234
|
+
binary_operator_loc = node.binary_operator_loc.chop
|
|
235
|
+
extract_idents(node.value) + [Ident[:method, binary_operator_loc.slice, binary_operator_loc.start_column]]
|
|
236
|
+
when :if_node
|
|
237
|
+
extract_idents(node.predicate) + [Branch[extract_idents(node.statements), extract_idents(node.subsequent)]]
|
|
238
|
+
when :unless_node
|
|
239
|
+
extract_idents(node.predicate) + [Branch[extract_idents(node.statements), extract_idents(node.else_clause)]]
|
|
240
|
+
when :and_node, :or_node
|
|
241
|
+
extract_idents(node.left) + [Branch[extract_idents(node.right), []]]
|
|
242
|
+
when :class_variable_read_node, :constant_read_node, :global_variable_read_node, :instance_variable_read_node, :local_variable_read_node, :self_node
|
|
243
|
+
[Ident[:ref, node.slice, node.start_column]]
|
|
244
|
+
else
|
|
245
|
+
node.compact_child_nodes.flat_map { |child| extract_idents(child) }
|
|
246
|
+
end
|
|
183
247
|
end
|
|
184
248
|
end
|
|
185
249
|
|
data/lib/power_assert/version.rb
CHANGED
data/lib/power_assert.rb
CHANGED
|
@@ -37,13 +37,15 @@ module PowerAssert
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def app_context?
|
|
40
|
-
top_frame = caller_locations.drop_while {|i| i.path
|
|
40
|
+
top_frame = caller_locations.drop_while {|i| i.path&.start_with?(POWER_ASSERT_LIB_DIR) }.first
|
|
41
41
|
top_frame and ! internal_file?(top_frame.path)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
private
|
|
45
45
|
|
|
46
46
|
def internal_file?(file)
|
|
47
|
+
return false unless file
|
|
48
|
+
|
|
47
49
|
INTERNAL_LIB_DIRS.find do |_, dir|
|
|
48
50
|
file.start_with?(dir)
|
|
49
51
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: power_assert
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kazuki Tsujimoto
|
|
@@ -58,7 +58,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
58
58
|
- !ruby/object:Gem::Version
|
|
59
59
|
version: '0'
|
|
60
60
|
requirements: []
|
|
61
|
-
rubygems_version:
|
|
61
|
+
rubygems_version: 4.0.16
|
|
62
62
|
specification_version: 4
|
|
63
63
|
summary: Power Assert for Ruby
|
|
64
64
|
test_files: []
|