cel 0.2.0 → 0.2.1
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 +33 -0
- data/README.md +20 -5
- data/lib/cel/ast/elements.rb +12 -0
- data/lib/cel/ast/types.rb +42 -1
- data/lib/cel/checker.rb +3 -3
- data/lib/cel/program.rb +7 -5
- 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: 54879dbe064eebebe6597acd098c6b10b8376f7b2333d2f822ca02c24a0815bb
|
4
|
+
data.tar.gz: 95e2fed808711b64aedc8ed6aff2f8bc8eb9e5f9562907c0d794df186a35f035
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59349c9633015f0a63c297573e30e33d459de63f1af8ff032f808b045c069716c19142e2208f653864bd4a2363009dd7cb54c8feed3160e6c792c260853cc79f
|
7
|
+
data.tar.gz: 2070807c5f7a01217117d96373372e111697727069b915a665acfc6fd2a58c6ff4adfb4fd747522585d7b03733cfb77ee2b7b726b6c36edb3f6d828a93ffed10
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,38 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.2.1] - 2023-07-26
|
4
|
+
|
5
|
+
|
6
|
+
### Improvements
|
7
|
+
|
8
|
+
Collection type declarations are now better supported, i.e. declaring "an array of strings" is now possible:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
Cel::Types[:list, :string] # array of strings
|
12
|
+
Cel::Environment.new(
|
13
|
+
names: Cel::Types[:list, :string],
|
14
|
+
owned_by: Cel::Types[:map, :string] # hash map of string keys
|
15
|
+
)
|
16
|
+
```
|
17
|
+
|
18
|
+
### Bugfixes
|
19
|
+
|
20
|
+
Collections passed as variables of an expression are now correctly cast to Cel types:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
env.evaluate("a", { a: [1, 2, 3] }) #=> now returns a Cel::List
|
24
|
+
```
|
25
|
+
|
26
|
+
Conversely, custom functions now expose ruby types in the blocks, instead of its Cel counterparts:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
func = Cel::Function(:list, :list, return_type: :list) do |a, b|
|
30
|
+
# a is now a ruby array, b as well
|
31
|
+
a & b
|
32
|
+
# function returns a ruby array, will be converted to a Cel::List for use in the expression
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
3
36
|
## [0.2.0] - 2023-01-17
|
4
37
|
|
5
38
|
### 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.
|
data/lib/cel/ast/elements.rb
CHANGED
@@ -176,6 +176,10 @@ module Cel
|
|
176
176
|
end
|
177
177
|
end
|
178
178
|
|
179
|
+
def to_ruby_type
|
180
|
+
@value
|
181
|
+
end
|
182
|
+
|
179
183
|
private
|
180
184
|
|
181
185
|
def check; end
|
@@ -296,6 +300,10 @@ module Cel
|
|
296
300
|
def to_ary
|
297
301
|
[self]
|
298
302
|
end
|
303
|
+
|
304
|
+
def to_ruby_type
|
305
|
+
value.map(&:to_ruby_type)
|
306
|
+
end
|
299
307
|
end
|
300
308
|
|
301
309
|
class Map < Literal
|
@@ -318,6 +326,10 @@ module Cel
|
|
318
326
|
[self]
|
319
327
|
end
|
320
328
|
|
329
|
+
def to_ruby_type
|
330
|
+
value.to_h { |*args| args.map(&:to_ruby_type) }
|
331
|
+
end
|
332
|
+
|
321
333
|
def respond_to_missing?(meth, *args)
|
322
334
|
super || (@value && @value.keys.any? { |k| k.to_s == meth.to_s })
|
323
335
|
end
|
data/lib/cel/ast/types.rb
CHANGED
@@ -58,6 +58,14 @@ module Cel
|
|
58
58
|
def get(idx)
|
59
59
|
@type_list[idx]
|
60
60
|
end
|
61
|
+
|
62
|
+
def ==(other)
|
63
|
+
other == :list || super
|
64
|
+
end
|
65
|
+
|
66
|
+
def cast(value)
|
67
|
+
List.new(value)
|
68
|
+
end
|
61
69
|
end
|
62
70
|
|
63
71
|
class MapType < Type
|
@@ -73,12 +81,45 @@ module Cel
|
|
73
81
|
_, value = @type_map.find { |k, _| k == attrib.to_s }
|
74
82
|
value
|
75
83
|
end
|
84
|
+
|
85
|
+
def ==(other)
|
86
|
+
other == :map || super
|
87
|
+
end
|
88
|
+
|
89
|
+
def cast(value)
|
90
|
+
Map.new(value)
|
91
|
+
end
|
76
92
|
end
|
77
93
|
|
78
94
|
# Primitive Cel Types
|
79
95
|
|
80
96
|
PRIMITIVE_TYPES = %i[int uint double bool string bytes list map timestamp duration null_type type].freeze
|
81
|
-
|
97
|
+
COLTYPES = %i[list map].freeze
|
98
|
+
TYPES = (PRIMITIVE_TYPES - COLTYPES).to_h { |typ| [typ, Type.new(typ)] }
|
82
99
|
TYPES[:type] = Type.new(:type)
|
83
100
|
TYPES[:any] = Type.new(:any)
|
101
|
+
|
102
|
+
module CollectionTypeFetch
|
103
|
+
def [](*args)
|
104
|
+
col_type, elem_type = args
|
105
|
+
|
106
|
+
return super unless COLTYPES.include?(col_type)
|
107
|
+
|
108
|
+
return super if args.size > 2
|
109
|
+
|
110
|
+
elem_type ||= :any
|
111
|
+
|
112
|
+
type = case col_type
|
113
|
+
when :list
|
114
|
+
ListType.new([])
|
115
|
+
when :map
|
116
|
+
MapType.new({})
|
117
|
+
end
|
118
|
+
|
119
|
+
type.element_type = super(*elem_type)
|
120
|
+
type
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
TYPES.singleton_class.prepend(CollectionTypeFetch)
|
84
125
|
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)
|
@@ -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/program.rb
CHANGED
@@ -93,7 +93,7 @@ module Cel
|
|
93
93
|
when String
|
94
94
|
raise EvaluateError, "#{invoke} is not supported" unless String.method_defined?(func, false)
|
95
95
|
|
96
|
-
var.public_send(func, *args)
|
96
|
+
var.public_send(func, *args.map(&method(:call)))
|
97
97
|
when Message
|
98
98
|
# If e evaluates to a message and f is not declared in this message, the
|
99
99
|
# runtime error no_such_field is raised.
|
@@ -134,10 +134,12 @@ module Cel
|
|
134
134
|
|
135
135
|
val.class
|
136
136
|
# MACROS
|
137
|
-
when :has
|
138
|
-
Macro.__send__(func, *args)
|
137
|
+
when :has
|
138
|
+
Bool.new(Macro.__send__(func, *args))
|
139
|
+
when :size
|
140
|
+
Cel::Number.new(:int, Macro.__send__(func, *args))
|
139
141
|
when :matches
|
140
|
-
Macro.__send__(func, *args.map(&method(:call)))
|
142
|
+
Bool.new(Macro.__send__(func, *args.map(&method(:call))))
|
141
143
|
when :int, :uint, :string, :double, :bytes, :duration, :timestamp
|
142
144
|
type = TYPES[func]
|
143
145
|
type.cast(call(args.first))
|
@@ -153,7 +155,7 @@ module Cel
|
|
153
155
|
def evaluate_custom_func(func, funcall)
|
154
156
|
args = funcall.args
|
155
157
|
|
156
|
-
func.call(*args.map(&method(:call)))
|
158
|
+
func.call(*args.map(&method(:call)).map(&:to_ruby_type))
|
157
159
|
end
|
158
160
|
end
|
159
161
|
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.1
|
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-07-26 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.
|