cel 0.2.0 → 0.2.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/CHANGELOG.md +42 -0
- data/README.md +20 -5
- data/lib/cel/ast/elements/protobuf.rb +9 -3
- data/lib/cel/ast/elements.rb +26 -13
- data/lib/cel/ast/types.rb +44 -1
- data/lib/cel/checker.rb +4 -4
- data/lib/cel/macro.rb +6 -3
- data/lib/cel/program.rb +13 -9
- data/lib/cel/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ed190ec0ddbda3e8c5229160dff580efa6cf1175f8b5ed5245e8de40f1feb800
|
|
4
|
+
data.tar.gz: 5f5d64c6a0199a0d47c7c29a3ef0c022f666e752147cadefbe6fc7090685e819
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c1c1aec4b0d8e54cf52a18057933c1ed214cddd79671ba6280f4e46f852c595f62259b4a10e2d8d0187d0b296f726b6dcae0b07aaccded157769b55f44d88900
|
|
7
|
+
data.tar.gz: 06c5d2653edca32b4cfb411a788734a0de1c9dcd6368f95b4ad4dcaeffe0b7fb82504c8e861d515acd19dbadb514804c5c6c108aa09e1600da0b9c1a60aec519
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.2] - 2023-08-17
|
|
4
|
+
|
|
5
|
+
* Reimplements `Cel::Literal#==` in a simpler way, to avoid several comparison issues arising from the previous implementation.
|
|
6
|
+
* fix initialization of `Cel::Duration` and `Cel::Timestamp` from string notation.
|
|
7
|
+
* fix protobuf-to-cel parsing of negative literals.
|
|
8
|
+
* more consistent usage of Cel types internally.
|
|
9
|
+
* fix condition clause evaluation.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## [0.2.1] - 2023-07-26
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Improvements
|
|
16
|
+
|
|
17
|
+
Collection type declarations are now better supported, i.e. declaring "an array of strings" is now possible:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
Cel::Types[:list, :string] # array of strings
|
|
21
|
+
Cel::Environment.new(
|
|
22
|
+
names: Cel::Types[:list, :string],
|
|
23
|
+
owned_by: Cel::Types[:map, :string] # hash map of string keys
|
|
24
|
+
)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Bugfixes
|
|
28
|
+
|
|
29
|
+
Collections passed as variables of an expression are now correctly cast to Cel types:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
env.evaluate("a", { a: [1, 2, 3] }) #=> now returns a Cel::List
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Conversely, custom functions now expose ruby types in the blocks, instead of its Cel counterparts:
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
func = Cel::Function(:list, :list, return_type: :list) do |a, b|
|
|
39
|
+
# a is now a ruby array, b as well
|
|
40
|
+
a & b
|
|
41
|
+
# function returns a ruby array, will be converted to a Cel::List for use in the expression
|
|
42
|
+
end
|
|
43
|
+
```
|
|
44
|
+
|
|
3
45
|
## [0.2.0] - 2023-01-17
|
|
4
46
|
|
|
5
47
|
### Features
|
data/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Cel::Ruby
|
|
2
2
|
|
|
3
3
|
[](http://rubygems.org/gems/cel)
|
|
4
|
-
[](https://gitlab.com/os85/cel-ruby/pipelines?page=1&scope=all&ref=master)
|
|
5
|
+
[](https://os85.gitlab.io/cel-ruby/coverage/#_AllFiles)
|
|
6
6
|
|
|
7
7
|
Pure Ruby implementation of Google Common Expression Language, https://opensource.google/projects/cel.
|
|
8
8
|
|
|
@@ -45,19 +45,20 @@ end
|
|
|
45
45
|
prg = env.program(ast)
|
|
46
46
|
# 1.3 evaluate
|
|
47
47
|
return_value = prg.evaluate(name: Cel::String.new("/groups/acme.co/documents/secret-stuff"),
|
|
48
|
-
group: Cel::String.new("acme.co"))
|
|
48
|
+
group: Cel::String.new("acme.co"))
|
|
49
49
|
|
|
50
50
|
# 2.1 parse and check
|
|
51
51
|
prg = env.program('name.startsWith("/groups/" + group)')
|
|
52
52
|
# 2.2 then evaluate
|
|
53
53
|
return_value = prg.evaluate(name: Cel::String.new("/groups/acme.co/documents/secret-stuff"),
|
|
54
|
-
group: Cel::String.new("acme.co"))
|
|
54
|
+
group: Cel::String.new("acme.co"))
|
|
55
55
|
|
|
56
56
|
# 3. or parse, check and evaluate
|
|
57
57
|
begin
|
|
58
58
|
return_value = env.evaluate(ast,
|
|
59
59
|
name: Cel::String.new("/groups/acme.co/documents/secret-stuff"),
|
|
60
|
-
group: Cel::String.new("acme.co")
|
|
60
|
+
group: Cel::String.new("acme.co")
|
|
61
|
+
)
|
|
61
62
|
rescue Cel::Error => e
|
|
62
63
|
STDERR.puts("evaluation error: #{e.message}")
|
|
63
64
|
raise e
|
|
@@ -66,6 +67,20 @@ end
|
|
|
66
67
|
puts return_value #=> true
|
|
67
68
|
```
|
|
68
69
|
|
|
70
|
+
### types
|
|
71
|
+
|
|
72
|
+
`cel-ruby` supports declaring the types of variables in the environment, which allows for expression checking:
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
env = Cel::Environment.new(
|
|
76
|
+
first_name: :string, # shortcut for Cel::Types[:string]
|
|
77
|
+
middle_names: Cel::Types[:list, :string], # list of strings
|
|
78
|
+
last_name: :string
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# you can use Cel::Types to access any type of primitive type, i.e. Cel::Types[:bytes]
|
|
82
|
+
```
|
|
83
|
+
|
|
69
84
|
### protobuf
|
|
70
85
|
|
|
71
86
|
If `google/protobuf` is available in the environment, `cel-ruby` will also be able to integrate with protobuf declarations in CEL expressions.
|
|
@@ -60,9 +60,15 @@ module Cel
|
|
|
60
60
|
|
|
61
61
|
value = value.fetch(key, value)
|
|
62
62
|
|
|
63
|
-
value =
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
value = case key
|
|
64
|
+
when "null_value"
|
|
65
|
+
Null.new if value.zero?
|
|
66
|
+
when "number_value"
|
|
67
|
+
value = -value.operands.first if value.is_a?(Operation) && value.op == "-" && value.unary?
|
|
68
|
+
Number.new(:double, value)
|
|
69
|
+
else
|
|
70
|
+
value
|
|
71
|
+
end
|
|
66
72
|
when "BoolValue", "google.protobuf.BoolValue"
|
|
67
73
|
value = value.nil? ? Bool.new(false) : value[Identifier.new("value")]
|
|
68
74
|
when "BytesValue", "google.protobuf.BytesValue"
|
data/lib/cel/ast/elements.rb
CHANGED
|
@@ -143,6 +143,7 @@ module Cel
|
|
|
143
143
|
end
|
|
144
144
|
|
|
145
145
|
def ==(other)
|
|
146
|
+
other = other.value if other.is_a?(Literal)
|
|
146
147
|
@value == other || super
|
|
147
148
|
end
|
|
148
149
|
|
|
@@ -176,6 +177,10 @@ module Cel
|
|
|
176
177
|
end
|
|
177
178
|
end
|
|
178
179
|
|
|
180
|
+
def to_ruby_type
|
|
181
|
+
@value
|
|
182
|
+
end
|
|
183
|
+
|
|
179
184
|
private
|
|
180
185
|
|
|
181
186
|
def check; end
|
|
@@ -190,7 +195,7 @@ module Cel
|
|
|
190
195
|
OUT
|
|
191
196
|
end
|
|
192
197
|
|
|
193
|
-
LOGICAL_OPERATORS.each do |op|
|
|
198
|
+
(LOGICAL_OPERATORS - %w[==]).each do |op|
|
|
194
199
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
195
200
|
def #{op}(other)
|
|
196
201
|
Bool.new(super)
|
|
@@ -204,13 +209,17 @@ module Cel
|
|
|
204
209
|
super(:bool, value)
|
|
205
210
|
end
|
|
206
211
|
|
|
207
|
-
LOGICAL_OPERATORS.each do |op|
|
|
212
|
+
(LOGICAL_OPERATORS - %w[==]).each do |op|
|
|
208
213
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
209
214
|
def #{op}(other)
|
|
210
215
|
Bool.new(super)
|
|
211
216
|
end
|
|
212
217
|
OUT
|
|
213
218
|
end
|
|
219
|
+
|
|
220
|
+
def !
|
|
221
|
+
Bool.new(super)
|
|
222
|
+
end
|
|
214
223
|
end
|
|
215
224
|
|
|
216
225
|
class Null < Literal
|
|
@@ -242,14 +251,6 @@ module Cel
|
|
|
242
251
|
Macro.matches(self, pattern)
|
|
243
252
|
end
|
|
244
253
|
|
|
245
|
-
LOGICAL_OPERATORS.each do |op|
|
|
246
|
-
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
247
|
-
def #{op}(other)
|
|
248
|
-
other.is_a?(Cel::Literal) ? Bool.new(super) : super
|
|
249
|
-
end
|
|
250
|
-
OUT
|
|
251
|
-
end
|
|
252
|
-
|
|
253
254
|
%i[+ -].each do |op|
|
|
254
255
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
255
256
|
def #{op}(other)
|
|
@@ -268,7 +269,7 @@ module Cel
|
|
|
268
269
|
[self]
|
|
269
270
|
end
|
|
270
271
|
|
|
271
|
-
LOGICAL_OPERATORS.each do |op|
|
|
272
|
+
(LOGICAL_OPERATORS - %w[==]).each do |op|
|
|
272
273
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
273
274
|
def #{op}(other)
|
|
274
275
|
Bool.new(super)
|
|
@@ -296,6 +297,10 @@ module Cel
|
|
|
296
297
|
def to_ary
|
|
297
298
|
[self]
|
|
298
299
|
end
|
|
300
|
+
|
|
301
|
+
def to_ruby_type
|
|
302
|
+
value.map(&:to_ruby_type)
|
|
303
|
+
end
|
|
299
304
|
end
|
|
300
305
|
|
|
301
306
|
class Map < Literal
|
|
@@ -318,6 +323,10 @@ module Cel
|
|
|
318
323
|
[self]
|
|
319
324
|
end
|
|
320
325
|
|
|
326
|
+
def to_ruby_type
|
|
327
|
+
value.to_h { |*args| args.map(&:to_ruby_type) }
|
|
328
|
+
end
|
|
329
|
+
|
|
321
330
|
def respond_to_missing?(meth, *args)
|
|
322
331
|
super || (@value && @value.keys.any? { |k| k.to_s == meth.to_s })
|
|
323
332
|
end
|
|
@@ -346,7 +355,7 @@ module Cel
|
|
|
346
355
|
class Timestamp < Literal
|
|
347
356
|
def initialize(value)
|
|
348
357
|
value = case value
|
|
349
|
-
when String then Time.parse(value)
|
|
358
|
+
when ::String then Time.parse(value)
|
|
350
359
|
when Numeric then Time.at(value)
|
|
351
360
|
else value
|
|
352
361
|
end
|
|
@@ -431,7 +440,7 @@ module Cel
|
|
|
431
440
|
class Duration < Literal
|
|
432
441
|
def initialize(value)
|
|
433
442
|
value = case value
|
|
434
|
-
when String
|
|
443
|
+
when ::String
|
|
435
444
|
init_from_string(value)
|
|
436
445
|
when Hash
|
|
437
446
|
seconds, nanos = value.values_at(:seconds, :nanos)
|
|
@@ -541,6 +550,10 @@ module Cel
|
|
|
541
550
|
end
|
|
542
551
|
end
|
|
543
552
|
|
|
553
|
+
def unary?
|
|
554
|
+
@operands.size == 1
|
|
555
|
+
end
|
|
556
|
+
|
|
544
557
|
def to_s
|
|
545
558
|
return "#{@op}#{@operands.first}" if @operands.size == 1
|
|
546
559
|
|
data/lib/cel/ast/types.rb
CHANGED
|
@@ -21,6 +21,8 @@ module Cel
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def cast(value)
|
|
24
|
+
value = value.value if value.is_a?(Literal)
|
|
25
|
+
|
|
24
26
|
case @type
|
|
25
27
|
when :int
|
|
26
28
|
Number.new(:int, Integer(value))
|
|
@@ -58,6 +60,14 @@ module Cel
|
|
|
58
60
|
def get(idx)
|
|
59
61
|
@type_list[idx]
|
|
60
62
|
end
|
|
63
|
+
|
|
64
|
+
def ==(other)
|
|
65
|
+
other == :list || super
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def cast(value)
|
|
69
|
+
List.new(value)
|
|
70
|
+
end
|
|
61
71
|
end
|
|
62
72
|
|
|
63
73
|
class MapType < Type
|
|
@@ -73,12 +83,45 @@ module Cel
|
|
|
73
83
|
_, value = @type_map.find { |k, _| k == attrib.to_s }
|
|
74
84
|
value
|
|
75
85
|
end
|
|
86
|
+
|
|
87
|
+
def ==(other)
|
|
88
|
+
other == :map || super
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def cast(value)
|
|
92
|
+
Map.new(value)
|
|
93
|
+
end
|
|
76
94
|
end
|
|
77
95
|
|
|
78
96
|
# Primitive Cel Types
|
|
79
97
|
|
|
80
98
|
PRIMITIVE_TYPES = %i[int uint double bool string bytes list map timestamp duration null_type type].freeze
|
|
81
|
-
|
|
99
|
+
COLTYPES = %i[list map].freeze
|
|
100
|
+
TYPES = (PRIMITIVE_TYPES - COLTYPES).to_h { |typ| [typ, Type.new(typ)] }
|
|
82
101
|
TYPES[:type] = Type.new(:type)
|
|
83
102
|
TYPES[:any] = Type.new(:any)
|
|
103
|
+
|
|
104
|
+
module CollectionTypeFetch
|
|
105
|
+
def [](*args)
|
|
106
|
+
col_type, elem_type = args
|
|
107
|
+
|
|
108
|
+
return super unless COLTYPES.include?(col_type)
|
|
109
|
+
|
|
110
|
+
return super if args.size > 2
|
|
111
|
+
|
|
112
|
+
elem_type ||= :any
|
|
113
|
+
|
|
114
|
+
type = case col_type
|
|
115
|
+
when :list
|
|
116
|
+
ListType.new([])
|
|
117
|
+
when :map
|
|
118
|
+
MapType.new({})
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
type.element_type = super(*elem_type)
|
|
122
|
+
type
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
TYPES.singleton_class.prepend(CollectionTypeFetch)
|
|
84
127
|
end
|
data/lib/cel/checker.rb
CHANGED
|
@@ -148,7 +148,7 @@ module Cel
|
|
|
148
148
|
case func
|
|
149
149
|
when :[]
|
|
150
150
|
attribute = var_type.get(args)
|
|
151
|
-
|
|
151
|
+
return TYPES[:any] unless attribute
|
|
152
152
|
when :all, :exists, :exists_one
|
|
153
153
|
check_arity(funcall, args, 2)
|
|
154
154
|
identifier, predicate = args
|
|
@@ -162,7 +162,7 @@ module Cel
|
|
|
162
162
|
return TYPES[:bool]
|
|
163
163
|
else
|
|
164
164
|
attribute = var_type.get(func)
|
|
165
|
-
|
|
165
|
+
return TYPES[:any] unless attribute
|
|
166
166
|
end
|
|
167
167
|
|
|
168
168
|
call(attribute)
|
|
@@ -357,7 +357,7 @@ module Cel
|
|
|
357
357
|
|
|
358
358
|
return unless typ
|
|
359
359
|
|
|
360
|
-
|
|
360
|
+
convert(typ) if id_call_chain.empty?
|
|
361
361
|
end
|
|
362
362
|
|
|
363
363
|
def convert(typ)
|
|
@@ -381,7 +381,7 @@ module Cel
|
|
|
381
381
|
end
|
|
382
382
|
end
|
|
383
383
|
|
|
384
|
-
TYPES[type]
|
|
384
|
+
type && types.is_a?(Type) ? types : TYPES[type]
|
|
385
385
|
end
|
|
386
386
|
|
|
387
387
|
def check_arity(func, args, arity)
|
data/lib/cel/macro.rb
CHANGED
|
@@ -43,21 +43,24 @@ module Cel
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def all(collection, identifier, predicate, context:)
|
|
46
|
-
collection.all? do |element, *|
|
|
46
|
+
return_value = collection.all? do |element, *|
|
|
47
47
|
Program.new(context.merge(identifier.to_sym => element)).evaluate(predicate).value
|
|
48
48
|
end
|
|
49
|
+
Bool.new(return_value)
|
|
49
50
|
end
|
|
50
51
|
|
|
51
52
|
def exists(collection, identifier, predicate, context:)
|
|
52
|
-
collection.any? do |element, *|
|
|
53
|
+
return_value = collection.any? do |element, *|
|
|
53
54
|
Program.new(context.merge(identifier.to_sym => element)).evaluate(predicate).value
|
|
54
55
|
end
|
|
56
|
+
Bool.new(return_value)
|
|
55
57
|
end
|
|
56
58
|
|
|
57
59
|
def exists_one(collection, identifier, predicate, context:)
|
|
58
|
-
collection.one? do |element, *|
|
|
60
|
+
return_value = collection.one? do |element, *|
|
|
59
61
|
Program.new(context.merge(identifier.to_sym => element)).evaluate(predicate).value
|
|
60
62
|
end
|
|
63
|
+
Bool.new(return_value)
|
|
61
64
|
end
|
|
62
65
|
|
|
63
66
|
def filter(collection, identifier, predicate, context:)
|
data/lib/cel/program.rb
CHANGED
|
@@ -57,20 +57,22 @@ module Cel
|
|
|
57
57
|
ev_operand
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
if
|
|
60
|
+
if operation.unary? &&
|
|
61
61
|
op != "!" # https://bugs.ruby-lang.org/issues/18246
|
|
62
62
|
# unary operations
|
|
63
|
-
values.first.__send__(:"#{op}@")
|
|
63
|
+
Literal.to_cel_type(values.first.__send__(:"#{op}@"))
|
|
64
64
|
elsif op == "&&"
|
|
65
|
-
Bool.new(values.all? { |x| true == x }) # rubocop:disable Style/YodaCondition
|
|
65
|
+
Bool.new(values.all? { |x| true == x.value }) # rubocop:disable Style/YodaCondition
|
|
66
66
|
elsif op == "||"
|
|
67
|
-
Bool.new(values.any? { |x| true == x }) # rubocop:disable Style/YodaCondition
|
|
67
|
+
Bool.new(values.any? { |x| true == x.value }) # rubocop:disable Style/YodaCondition
|
|
68
68
|
elsif op == "in"
|
|
69
69
|
element, collection = values
|
|
70
70
|
Bool.new(collection.include?(element))
|
|
71
71
|
else
|
|
72
72
|
op_value, *values = values
|
|
73
|
-
op_value.public_send(op, *values)
|
|
73
|
+
val = op_value.public_send(op, *values)
|
|
74
|
+
|
|
75
|
+
Literal.to_cel_type(val)
|
|
74
76
|
end
|
|
75
77
|
end
|
|
76
78
|
|
|
@@ -93,7 +95,7 @@ module Cel
|
|
|
93
95
|
when String
|
|
94
96
|
raise EvaluateError, "#{invoke} is not supported" unless String.method_defined?(func, false)
|
|
95
97
|
|
|
96
|
-
var.public_send(func, *args)
|
|
98
|
+
var.public_send(func, *args.map(&method(:call)))
|
|
97
99
|
when Message
|
|
98
100
|
# If e evaluates to a message and f is not declared in this message, the
|
|
99
101
|
# runtime error no_such_field is raised.
|
|
@@ -120,7 +122,7 @@ module Cel
|
|
|
120
122
|
end
|
|
121
123
|
|
|
122
124
|
def evaluate_condition(condition)
|
|
123
|
-
call(condition.if) ? call(condition.then) : call(condition.else)
|
|
125
|
+
call(condition.if).value ? call(condition.then) : call(condition.else)
|
|
124
126
|
end
|
|
125
127
|
|
|
126
128
|
def evaluate_standard_func(funcall)
|
|
@@ -134,8 +136,10 @@ module Cel
|
|
|
134
136
|
|
|
135
137
|
val.class
|
|
136
138
|
# MACROS
|
|
137
|
-
when :has
|
|
139
|
+
when :has
|
|
138
140
|
Macro.__send__(func, *args)
|
|
141
|
+
when :size
|
|
142
|
+
Cel::Number.new(:int, Macro.__send__(func, *args))
|
|
139
143
|
when :matches
|
|
140
144
|
Macro.__send__(func, *args.map(&method(:call)))
|
|
141
145
|
when :int, :uint, :string, :double, :bytes, :duration, :timestamp
|
|
@@ -153,7 +157,7 @@ module Cel
|
|
|
153
157
|
def evaluate_custom_func(func, funcall)
|
|
154
158
|
args = funcall.args
|
|
155
159
|
|
|
156
|
-
func.call(*args.map(&method(:call)))
|
|
160
|
+
func.call(*args.map(&method(:call)).map(&:to_ruby_type))
|
|
157
161
|
end
|
|
158
162
|
end
|
|
159
163
|
end
|
data/lib/cel/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cel
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tiago Cardoso
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-
|
|
11
|
+
date: 2023-08-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: minitest
|
|
@@ -88,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
89
|
version: '0'
|
|
90
90
|
requirements: []
|
|
91
|
-
rubygems_version: 3.
|
|
91
|
+
rubygems_version: 3.4.10
|
|
92
92
|
signing_key:
|
|
93
93
|
specification_version: 4
|
|
94
94
|
summary: Pure Ruby implementation of Google Common Expression Language, https://opensource.google/projects/cel.
|