ruby-rego 0.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 +7 -0
- data/.reek.yml +80 -0
- data/.vscode/extensions.json +19 -0
- data/.vscode/launch.json +35 -0
- data/.vscode/settings.json +25 -0
- data/.vscode/tasks.json +117 -0
- data/.yardopts +12 -0
- data/ARCHITECTURE.md +39 -0
- data/CHANGELOG.md +25 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +183 -0
- data/RELEASING.md +37 -0
- data/Rakefile +38 -0
- data/SECURITY.md +26 -0
- data/Steepfile +10 -0
- data/TODO.md +35 -0
- data/benchmark/builtin_calls.rb +29 -0
- data/benchmark/complex_policy.rb +19 -0
- data/benchmark/comprehensions.rb +19 -0
- data/benchmark/simple_rules.rb +20 -0
- data/examples/README.md +27 -0
- data/examples/sample_config.yaml +2 -0
- data/examples/simple_policy.rego +7 -0
- data/examples/validation_policy.rego +11 -0
- data/exe/rego-validate +6 -0
- data/lib/ruby/rego/ast/base.rb +95 -0
- data/lib/ruby/rego/ast/binary_op.rb +64 -0
- data/lib/ruby/rego/ast/call.rb +27 -0
- data/lib/ruby/rego/ast/composite.rb +48 -0
- data/lib/ruby/rego/ast/comprehension.rb +63 -0
- data/lib/ruby/rego/ast/every.rb +37 -0
- data/lib/ruby/rego/ast/import.rb +32 -0
- data/lib/ruby/rego/ast/literal.rb +70 -0
- data/lib/ruby/rego/ast/module.rb +32 -0
- data/lib/ruby/rego/ast/package.rb +22 -0
- data/lib/ruby/rego/ast/query.rb +63 -0
- data/lib/ruby/rego/ast/reference.rb +58 -0
- data/lib/ruby/rego/ast/rule.rb +114 -0
- data/lib/ruby/rego/ast/unary_op.rb +42 -0
- data/lib/ruby/rego/ast/variable.rb +22 -0
- data/lib/ruby/rego/ast.rb +17 -0
- data/lib/ruby/rego/builtins/aggregates.rb +124 -0
- data/lib/ruby/rego/builtins/base.rb +95 -0
- data/lib/ruby/rego/builtins/collections/array_ops.rb +103 -0
- data/lib/ruby/rego/builtins/collections/object_ops.rb +120 -0
- data/lib/ruby/rego/builtins/collections/set_ops.rb +51 -0
- data/lib/ruby/rego/builtins/collections.rb +137 -0
- data/lib/ruby/rego/builtins/comparisons/casts.rb +139 -0
- data/lib/ruby/rego/builtins/comparisons.rb +84 -0
- data/lib/ruby/rego/builtins/numeric_helpers.rb +56 -0
- data/lib/ruby/rego/builtins/registry.rb +199 -0
- data/lib/ruby/rego/builtins/registry_helpers.rb +27 -0
- data/lib/ruby/rego/builtins/strings/case_ops.rb +22 -0
- data/lib/ruby/rego/builtins/strings/concat.rb +19 -0
- data/lib/ruby/rego/builtins/strings/formatting.rb +35 -0
- data/lib/ruby/rego/builtins/strings/helpers.rb +62 -0
- data/lib/ruby/rego/builtins/strings/number_helpers.rb +48 -0
- data/lib/ruby/rego/builtins/strings/search.rb +63 -0
- data/lib/ruby/rego/builtins/strings/split.rb +19 -0
- data/lib/ruby/rego/builtins/strings/substring.rb +22 -0
- data/lib/ruby/rego/builtins/strings/trim.rb +42 -0
- data/lib/ruby/rego/builtins/strings/trim_helpers.rb +62 -0
- data/lib/ruby/rego/builtins/strings.rb +58 -0
- data/lib/ruby/rego/builtins/types.rb +89 -0
- data/lib/ruby/rego/call_name.rb +55 -0
- data/lib/ruby/rego/cli.rb +1122 -0
- data/lib/ruby/rego/compiled_module.rb +114 -0
- data/lib/ruby/rego/compiler.rb +1097 -0
- data/lib/ruby/rego/environment/overrides.rb +33 -0
- data/lib/ruby/rego/environment/reference_resolution.rb +86 -0
- data/lib/ruby/rego/environment.rb +230 -0
- data/lib/ruby/rego/environment_pool.rb +71 -0
- data/lib/ruby/rego/error_handling.rb +58 -0
- data/lib/ruby/rego/error_payload.rb +34 -0
- data/lib/ruby/rego/errors.rb +196 -0
- data/lib/ruby/rego/evaluator/assignment_support.rb +126 -0
- data/lib/ruby/rego/evaluator/binding_helpers.rb +60 -0
- data/lib/ruby/rego/evaluator/comprehension_evaluator.rb +182 -0
- data/lib/ruby/rego/evaluator/expression_dispatch.rb +45 -0
- data/lib/ruby/rego/evaluator/expression_evaluator.rb +492 -0
- data/lib/ruby/rego/evaluator/object_literal_evaluator.rb +52 -0
- data/lib/ruby/rego/evaluator/operator_evaluator.rb +163 -0
- data/lib/ruby/rego/evaluator/query_node_builder.rb +38 -0
- data/lib/ruby/rego/evaluator/reference_key_resolver.rb +50 -0
- data/lib/ruby/rego/evaluator/reference_resolver.rb +352 -0
- data/lib/ruby/rego/evaluator/rule_evaluator/bindings.rb +70 -0
- data/lib/ruby/rego/evaluator/rule_evaluator.rb +550 -0
- data/lib/ruby/rego/evaluator/rule_value_provider.rb +56 -0
- data/lib/ruby/rego/evaluator/variable_collector.rb +221 -0
- data/lib/ruby/rego/evaluator.rb +174 -0
- data/lib/ruby/rego/lexer/number_reader.rb +68 -0
- data/lib/ruby/rego/lexer/stream.rb +137 -0
- data/lib/ruby/rego/lexer/string_reader.rb +90 -0
- data/lib/ruby/rego/lexer/template_string_reader.rb +62 -0
- data/lib/ruby/rego/lexer.rb +206 -0
- data/lib/ruby/rego/location.rb +73 -0
- data/lib/ruby/rego/memoization.rb +67 -0
- data/lib/ruby/rego/parser/collections.rb +173 -0
- data/lib/ruby/rego/parser/expressions.rb +216 -0
- data/lib/ruby/rego/parser/precedence.rb +42 -0
- data/lib/ruby/rego/parser/query.rb +139 -0
- data/lib/ruby/rego/parser/references.rb +115 -0
- data/lib/ruby/rego/parser/rules.rb +310 -0
- data/lib/ruby/rego/parser.rb +210 -0
- data/lib/ruby/rego/policy.rb +50 -0
- data/lib/ruby/rego/result.rb +91 -0
- data/lib/ruby/rego/token.rb +206 -0
- data/lib/ruby/rego/unifier.rb +451 -0
- data/lib/ruby/rego/value.rb +379 -0
- data/lib/ruby/rego/version.rb +7 -0
- data/lib/ruby/rego/with_modifiers/with_modifier.rb +37 -0
- data/lib/ruby/rego/with_modifiers/with_modifier_applier.rb +48 -0
- data/lib/ruby/rego/with_modifiers/with_modifier_builtin_override.rb +128 -0
- data/lib/ruby/rego/with_modifiers/with_modifier_context.rb +120 -0
- data/lib/ruby/rego/with_modifiers/with_modifier_path_key_resolver.rb +42 -0
- data/lib/ruby/rego/with_modifiers/with_modifier_path_override.rb +99 -0
- data/lib/ruby/rego/with_modifiers/with_modifier_root_scope.rb +58 -0
- data/lib/ruby/rego.rb +72 -0
- data/sig/objspace.rbs +4 -0
- data/sig/psych.rbs +7 -0
- data/sig/rego_validate.rbs +382 -0
- data/sig/ruby/rego.rbs +2150 -0
- metadata +172 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: b0d83031bd5f63d45c1b362c9c43554d9d64a01f5bd880ab0886ed4ef33ec343
|
|
4
|
+
data.tar.gz: df9479201e044759b097f18bad760c3677811eba312b3ad1288406828dc7a4f9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 75ad38b961532569e213ec86d48636a1a22626b5f0de69107216278d276aff1644ae0c975b6e36a5758bea86521681f6d9864e29c7a95fd1fc327e106cb3dc8a
|
|
7
|
+
data.tar.gz: 252603fe30c8c3b8c4e1bf80dea95ab215980d67c47c856df5c7d66fe8bb9ceb636985952bd5557346e17690fbd0f76bf019ea14dc037f58860bdc23065005fc
|
data/.reek.yml
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Reek configuration
|
|
2
|
+
# The lexer is inherently complex and table-driven; exclude it from high-churn detectors.
|
|
3
|
+
# Core parser/evaluator/unifier paths are algorithmic and currently exceed Reek thresholds;
|
|
4
|
+
# suppress targeted detectors to keep the signal actionable until a deeper refactor.
|
|
5
|
+
# TODO(2026-02-09): Narrow these exclusions as evaluator/parser refactors progress.
|
|
6
|
+
|
|
7
|
+
detectors:
|
|
8
|
+
TooManyMethods:
|
|
9
|
+
exclude:
|
|
10
|
+
- Ruby::Rego::Lexer
|
|
11
|
+
- Ruby::Rego::Parser
|
|
12
|
+
- Ruby::Rego::Compiler
|
|
13
|
+
- Ruby::Rego::Evaluator::ReferenceResolver
|
|
14
|
+
- Ruby::Rego::Evaluator::RuleEvaluator
|
|
15
|
+
- Ruby::Rego::Unifier
|
|
16
|
+
TooManyConstants:
|
|
17
|
+
exclude:
|
|
18
|
+
- Ruby::Rego::Lexer
|
|
19
|
+
- Ruby::Rego::Evaluator::OperatorEvaluator
|
|
20
|
+
TooManyInstanceVariables:
|
|
21
|
+
exclude:
|
|
22
|
+
- Ruby::Rego::Lexer
|
|
23
|
+
- Ruby::Rego::Evaluator::ReferenceResolver
|
|
24
|
+
TooManyStatements:
|
|
25
|
+
exclude:
|
|
26
|
+
- Ruby::Rego::Lexer
|
|
27
|
+
- Ruby::Rego::Parser
|
|
28
|
+
- Ruby::Rego::Evaluator::ReferenceResolver
|
|
29
|
+
- Ruby::Rego::Evaluator::RuleEvaluator
|
|
30
|
+
- Ruby::Rego::Evaluator::OperatorEvaluator
|
|
31
|
+
- Ruby::Rego::Unifier
|
|
32
|
+
- Ruby::Rego::WithModifiers::WithModifierBuiltinOverride
|
|
33
|
+
- Ruby::Rego::ParserError#initialize
|
|
34
|
+
- Ruby::Rego::TypeError#initialize
|
|
35
|
+
- Ruby::Rego::UnificationError#initialize
|
|
36
|
+
DuplicateMethodCall:
|
|
37
|
+
exclude:
|
|
38
|
+
- Ruby::Rego::Lexer
|
|
39
|
+
- Ruby::Rego::Evaluator::ReferenceResolver
|
|
40
|
+
- Ruby::Rego::Evaluator::RuleEvaluator
|
|
41
|
+
- Ruby::Rego::Unifier
|
|
42
|
+
- Ruby::Rego::WithModifiers::WithModifierBuiltinOverride
|
|
43
|
+
UtilityFunction:
|
|
44
|
+
exclude:
|
|
45
|
+
- Ruby::Rego::Lexer
|
|
46
|
+
- Ruby::Rego::Evaluator::ReferenceResolver
|
|
47
|
+
- Ruby::Rego::Unifier
|
|
48
|
+
FeatureEnvy:
|
|
49
|
+
exclude:
|
|
50
|
+
- Ruby::Rego::Lexer
|
|
51
|
+
- Ruby::Rego::Evaluator::RuleEvaluator
|
|
52
|
+
- Ruby::Rego::Evaluator::ReferenceKeyResolver
|
|
53
|
+
- Ruby::Rego::Evaluator::BoundVariableCollector
|
|
54
|
+
- Ruby::Rego::Unifier
|
|
55
|
+
ControlParameter:
|
|
56
|
+
exclude:
|
|
57
|
+
- Ruby::Rego::Lexer
|
|
58
|
+
- Ruby::Rego::Evaluator::RuleEvaluator
|
|
59
|
+
- Ruby::Rego::Unifier
|
|
60
|
+
- Ruby::Rego::WithModifiers::WithModifierBuiltinOverride
|
|
61
|
+
NilCheck:
|
|
62
|
+
exclude:
|
|
63
|
+
- Ruby::Rego::Lexer
|
|
64
|
+
- Ruby::Rego::Evaluator::ReferenceResolver
|
|
65
|
+
- Ruby::Rego::Unifier
|
|
66
|
+
RepeatedConditional:
|
|
67
|
+
exclude:
|
|
68
|
+
- Ruby::Rego::Lexer
|
|
69
|
+
- Ruby::Rego::Evaluator::ReferenceResolver
|
|
70
|
+
- Ruby::Rego::Evaluator::RuleEvaluator
|
|
71
|
+
NestedIterators:
|
|
72
|
+
exclude:
|
|
73
|
+
- Ruby::Rego::Evaluator::RuleEvaluator
|
|
74
|
+
UncommunicativeVariableName:
|
|
75
|
+
accept:
|
|
76
|
+
- e
|
|
77
|
+
- _
|
|
78
|
+
exclude:
|
|
79
|
+
- Ruby::Rego::Builtins::Collections::ObjectOps#self.object_remove
|
|
80
|
+
- Ruby::Rego::Evaluator::ExpressionDispatch#handler_for
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
|
3
|
+
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
|
4
|
+
|
|
5
|
+
// List of extensions which should be recommended for users of this workspace.
|
|
6
|
+
"recommendations": [
|
|
7
|
+
"Shopify.ruby-lsp",
|
|
8
|
+
"soutaro.steep-vscode",
|
|
9
|
+
"soutaro.rbs-syntax",
|
|
10
|
+
"rubocop.vscode-rubocop",
|
|
11
|
+
"davidanson.vscode-markdownlint",
|
|
12
|
+
"markis.code-coverage",
|
|
13
|
+
"GitHub.copilot-chat",
|
|
14
|
+
"GitHub.vscode-pull-request-github",
|
|
15
|
+
"eamodio.gitlens"
|
|
16
|
+
],
|
|
17
|
+
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
|
18
|
+
"unwantedRecommendations": []
|
|
19
|
+
}
|
data/.vscode/launch.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.2.0",
|
|
3
|
+
"configurations": [
|
|
4
|
+
{
|
|
5
|
+
"type": "rdbg",
|
|
6
|
+
"name": "Debug RSpec file",
|
|
7
|
+
"request": "launch",
|
|
8
|
+
"command": "bundle",
|
|
9
|
+
"script": "exec",
|
|
10
|
+
"args": ["rspec", "${file}"],
|
|
11
|
+
"cwd": "${workspaceFolder}",
|
|
12
|
+
"useTerminal": true
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"type": "rdbg",
|
|
16
|
+
"name": "Debug RSpec example",
|
|
17
|
+
"request": "launch",
|
|
18
|
+
"command": "bundle",
|
|
19
|
+
"script": "exec",
|
|
20
|
+
"args": ["rspec", "${file}:${lineNumber}"],
|
|
21
|
+
"cwd": "${workspaceFolder}",
|
|
22
|
+
"useTerminal": true
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"type": "rdbg",
|
|
26
|
+
"name": "Debug RSpec suite",
|
|
27
|
+
"request": "launch",
|
|
28
|
+
"command": "bundle",
|
|
29
|
+
"script": "exec",
|
|
30
|
+
"args": ["rspec"],
|
|
31
|
+
"cwd": "${workspaceFolder}",
|
|
32
|
+
"useTerminal": true
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"[ruby]": {
|
|
3
|
+
"editor.defaultFormatter": "Shopify.ruby-lsp",
|
|
4
|
+
"editor.formatOnSave": true
|
|
5
|
+
},
|
|
6
|
+
"rubyLsp.formatter": "rubocop_internal",
|
|
7
|
+
"rubyLsp.linters": ["rubocop_internal"],
|
|
8
|
+
"editor.insertSpaces": true,
|
|
9
|
+
"editor.tabSize": 2,
|
|
10
|
+
"files.trimTrailingWhitespace": true,
|
|
11
|
+
"files.insertFinalNewline": true,
|
|
12
|
+
"search.exclude": {
|
|
13
|
+
"**/coverage/**": true,
|
|
14
|
+
"**/tmp/**": true
|
|
15
|
+
},
|
|
16
|
+
"steep.enabled": true,
|
|
17
|
+
"steep.command": "bundle exec steep",
|
|
18
|
+
"steep.steepfile": "Steepfile",
|
|
19
|
+
"steep.gemfile": "Gemfile",
|
|
20
|
+
"steep.loglevel": "info",
|
|
21
|
+
"steep.hideDiagnostics": "Information",
|
|
22
|
+
"chat.tools.terminal.autoApprove": {
|
|
23
|
+
"bundle": true
|
|
24
|
+
}
|
|
25
|
+
}
|
data/.vscode/tasks.json
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "2.0.0",
|
|
3
|
+
"tasks": [
|
|
4
|
+
{
|
|
5
|
+
"label": "RSpec: current file",
|
|
6
|
+
"type": "shell",
|
|
7
|
+
"command": "bundle",
|
|
8
|
+
"args": ["exec", "rspec", "${file}"],
|
|
9
|
+
"options": {
|
|
10
|
+
"shell": {
|
|
11
|
+
"executable": "/bin/zsh",
|
|
12
|
+
"args": ["-lc"]
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"group": "test",
|
|
16
|
+
"problemMatcher": []
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"label": "RSpec: suite",
|
|
20
|
+
"type": "shell",
|
|
21
|
+
"command": "bundle",
|
|
22
|
+
"args": ["exec", "rspec"],
|
|
23
|
+
"options": {
|
|
24
|
+
"shell": {
|
|
25
|
+
"executable": "/bin/zsh",
|
|
26
|
+
"args": ["-lc"]
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"group": "test",
|
|
30
|
+
"problemMatcher": []
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"label": "Reek",
|
|
34
|
+
"type": "shell",
|
|
35
|
+
"command": "bundle",
|
|
36
|
+
"args": ["exec", "reek", "${workspaceFolder}/lib"],
|
|
37
|
+
"options": {
|
|
38
|
+
"shell": {
|
|
39
|
+
"executable": "/bin/zsh",
|
|
40
|
+
"args": ["-lc"]
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"group": "none",
|
|
44
|
+
"problemMatcher": []
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"label": "Steep",
|
|
48
|
+
"type": "shell",
|
|
49
|
+
"command": "bundle",
|
|
50
|
+
"args": ["exec", "steep", "check"],
|
|
51
|
+
"options": {
|
|
52
|
+
"shell": {
|
|
53
|
+
"executable": "/bin/zsh",
|
|
54
|
+
"args": ["-lc"]
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"group": "none",
|
|
58
|
+
"problemMatcher": []
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"label": "TypeProf",
|
|
62
|
+
"type": "shell",
|
|
63
|
+
"command": "bundle",
|
|
64
|
+
"args": ["exec", "typeprof", "lib/**/*.rb"],
|
|
65
|
+
"options": {
|
|
66
|
+
"shell": {
|
|
67
|
+
"executable": "/bin/zsh",
|
|
68
|
+
"args": ["-lc"]
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"group": "none",
|
|
72
|
+
"problemMatcher": []
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"label": "Bundler Audit",
|
|
76
|
+
"type": "shell",
|
|
77
|
+
"command": "bundle",
|
|
78
|
+
"args": ["exec", "bundler-audit", "check", "--update"],
|
|
79
|
+
"options": {
|
|
80
|
+
"shell": {
|
|
81
|
+
"executable": "/bin/zsh",
|
|
82
|
+
"args": ["-lc"]
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"group": "none",
|
|
86
|
+
"problemMatcher": []
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"label": "RubyCritic",
|
|
90
|
+
"type": "shell",
|
|
91
|
+
"command": "bundle",
|
|
92
|
+
"args": ["exec", "rubycritic", "lib"],
|
|
93
|
+
"options": {
|
|
94
|
+
"shell": {
|
|
95
|
+
"executable": "/bin/zsh",
|
|
96
|
+
"args": ["-lc"]
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"group": "none",
|
|
100
|
+
"problemMatcher": []
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"label": "Rubocop",
|
|
104
|
+
"type": "shell",
|
|
105
|
+
"command": "bundle",
|
|
106
|
+
"args": ["exec", "rubocop", "--format", "simple", "--no-color"],
|
|
107
|
+
"options": {
|
|
108
|
+
"shell": {
|
|
109
|
+
"executable": "/bin/zsh",
|
|
110
|
+
"args": ["-lc"]
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"group": "none",
|
|
114
|
+
"problemMatcher": []
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}
|
data/.yardopts
ADDED
data/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Ruby::Rego is a pure Ruby implementation of the Rego policy language. It follows a compiler-style pipeline: parse source into an AST, compile the AST into indexed structures, and evaluate those structures against input and data.
|
|
6
|
+
|
|
7
|
+
## Component responsibilities
|
|
8
|
+
|
|
9
|
+
- Lexer: converts source code into tokens.
|
|
10
|
+
- Parser: builds an AST from the token stream.
|
|
11
|
+
- AST nodes: model Rego constructs (rules, expressions, references).
|
|
12
|
+
- Compiler: validates and indexes rules, produces a CompiledModule.
|
|
13
|
+
- CompiledModule: immutable bundle of rules, package path, and dependency graph.
|
|
14
|
+
- Evaluator: executes queries or rule groups using the compiled module.
|
|
15
|
+
- Environment: stores input, data, local bindings, and builtins.
|
|
16
|
+
- Values: wrap Ruby primitives for Rego semantics and undefined handling.
|
|
17
|
+
- Builtins: registry of supported functions.
|
|
18
|
+
- Unifier: resolves pattern matching and binding logic.
|
|
19
|
+
- CLI: validates inputs against policies via the public API.
|
|
20
|
+
|
|
21
|
+
## Data flow
|
|
22
|
+
|
|
23
|
+
1. Source -> Lexer -> Tokens
|
|
24
|
+
2. Tokens -> Parser -> AST::Module
|
|
25
|
+
3. AST -> Compiler -> CompiledModule
|
|
26
|
+
4. CompiledModule + input/data -> Evaluator -> Result
|
|
27
|
+
|
|
28
|
+
The evaluator walks the AST through rule evaluators and expression evaluators, resolving references against the Environment and invoking builtins when needed. Errors bubble up with location metadata to provide actionable diagnostics.
|
|
29
|
+
|
|
30
|
+
## Extension points
|
|
31
|
+
|
|
32
|
+
- Builtins: add new functions by extending the builtin registry and adding tests.
|
|
33
|
+
- Parser: add new syntax by introducing AST nodes and parser rules.
|
|
34
|
+
- Evaluator: support new expressions or keywords by adding evaluators.
|
|
35
|
+
- CLI: add new flags or output modes by extending the CLI classes.
|
|
36
|
+
|
|
37
|
+
## Error handling
|
|
38
|
+
|
|
39
|
+
Public API calls wrap errors with location information to present a consistent error shape. Internal helpers raise specialized error subclasses to clarify failure sources (lexer, parser, compiler, evaluator, unifier).
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
- Documentation refresh, architecture notes, and runnable examples.
|
|
8
|
+
- YARD documentation for public APIs.
|
|
9
|
+
|
|
10
|
+
## 0.1.0
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
- Lexer, parser, and AST support for core Rego syntax.
|
|
15
|
+
- Compiler that validates, indexes, and freezes rules.
|
|
16
|
+
- Evaluator with rule execution, unification, and reference resolution.
|
|
17
|
+
- Core built-in functions (types, aggregates, strings, collections, comparisons).
|
|
18
|
+
- CLI for validation workflows.
|
|
19
|
+
|
|
20
|
+
### Planned additions
|
|
21
|
+
|
|
22
|
+
- Expanded built-in function coverage.
|
|
23
|
+
- Broader OPA compatibility for advanced keywords and patterns.
|
|
24
|
+
- Performance tuning and memoization work.
|
|
25
|
+
- Compliance and integration test suites.
|
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
"ruby-rego" follows [The Ruby Community Conduct Guideline](https://www.ruby-lang.org/en/conduct) in all "collaborative space", which is defined as community communications channels (such as mailing lists, submitted patches, commit comments, etc.):
|
|
4
|
+
|
|
5
|
+
* Participants will be tolerant of opposing views.
|
|
6
|
+
* Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
|
|
7
|
+
* When interpreting the words and actions of others, participants should always assume good intentions.
|
|
8
|
+
* Behaviour which can be reasonably considered harassment will not be tolerated.
|
|
9
|
+
|
|
10
|
+
If you have any concerns about behaviour within this project, please contact us at ["me@r6e.dev"](mailto:"me@r6e.dev").
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rob Trame
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Ruby::Rego
|
|
2
|
+
|
|
3
|
+
Ruby::Rego is a pure Ruby implementation of the Open Policy Agent (OPA) Rego policy language. The project targets a clean, Ruby-idiomatic API with strong test coverage and type signatures while working toward broader Rego compatibility.
|
|
4
|
+
|
|
5
|
+
## Project goals
|
|
6
|
+
|
|
7
|
+
- Provide a stable Ruby API for parsing, compiling, and evaluating Rego policies.
|
|
8
|
+
- Offer a deterministic evaluator with clear error reporting.
|
|
9
|
+
- Keep compiled modules immutable and safe to reuse.
|
|
10
|
+
- Ship a CLI for common validation workflows.
|
|
11
|
+
|
|
12
|
+
## Status
|
|
13
|
+
|
|
14
|
+
The gem is under active development and does not yet cover the full OPA specification. Please review the supported feature list below before relying on it in production.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Install the gem and add it to your Gemfile:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
bundle add ruby-rego
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
If you are not using Bundler, install it directly:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
gem install ruby-rego
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
require "ruby/rego"
|
|
34
|
+
|
|
35
|
+
policy = <<~REGO
|
|
36
|
+
package example
|
|
37
|
+
default allow = false
|
|
38
|
+
allow { input.user == "admin" }
|
|
39
|
+
REGO
|
|
40
|
+
|
|
41
|
+
result = Ruby::Rego.evaluate(policy, input: {"user" => "admin"}, query: "data.example.allow")
|
|
42
|
+
puts result.value.to_ruby
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## API documentation
|
|
46
|
+
|
|
47
|
+
### Basic parsing
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
require "ruby/rego"
|
|
51
|
+
|
|
52
|
+
tokens = Ruby::Rego::Lexer.new(policy).tokenize
|
|
53
|
+
ast_module = Ruby::Rego::Parser.new(tokens).parse
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Policy evaluation
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
require "ruby/rego"
|
|
60
|
+
|
|
61
|
+
compiled = Ruby::Rego.compile(policy)
|
|
62
|
+
evaluator = Ruby::Rego::Evaluator.new(compiled, input: {"user" => "admin"})
|
|
63
|
+
result = evaluator.evaluate("data.example.allow")
|
|
64
|
+
puts result.to_h
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Validation use case
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
require "ruby/rego"
|
|
71
|
+
require "yaml"
|
|
72
|
+
|
|
73
|
+
policy_source = File.read("examples/validation_policy.rego")
|
|
74
|
+
config_hash = YAML.safe_load(File.read("examples/sample_config.yaml"))
|
|
75
|
+
|
|
76
|
+
policy = Ruby::Rego::Policy.new(policy_source)
|
|
77
|
+
result = policy.evaluate(input: config_hash, query: "data.validation.deny")
|
|
78
|
+
|
|
79
|
+
if result.undefined?
|
|
80
|
+
puts "No decision"
|
|
81
|
+
elsif result.success?
|
|
82
|
+
puts "OK"
|
|
83
|
+
else
|
|
84
|
+
puts "Errors: #{result.value.to_ruby.inspect}"
|
|
85
|
+
end
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### CLI usage
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
bundle exec exe/rego-validate --policy examples/validation_policy.rego --config examples/sample_config.yaml
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The CLI attempts to infer a validation query in this order: `deny`, `violations`, `violation`, `errors`, `error`, then falls back to `allow`. You can override this with `--query`.
|
|
95
|
+
|
|
96
|
+
## Supported Rego features
|
|
97
|
+
|
|
98
|
+
### Language support
|
|
99
|
+
|
|
100
|
+
- Packages and imports.
|
|
101
|
+
- Rule definitions (complete and partial rules, defaults, else).
|
|
102
|
+
- Literals and references, including input/data.
|
|
103
|
+
- Collections: arrays, objects, sets.
|
|
104
|
+
- Comprehensions (array, object, set).
|
|
105
|
+
- Operators: assignment, unification, comparisons, arithmetic, and boolean logic.
|
|
106
|
+
- Keywords: `some`, `not`, `every` (experimental), and `with` (limited).
|
|
107
|
+
|
|
108
|
+
### Built-in functions
|
|
109
|
+
|
|
110
|
+
Built-ins are currently limited to core categories: types, aggregates, strings, collections, and comparisons. See the builtins registry for the current list.
|
|
111
|
+
|
|
112
|
+
### Known limitations
|
|
113
|
+
|
|
114
|
+
- Not full OPA spec coverage yet.
|
|
115
|
+
- Advanced `with` semantics, partial evaluation, and additional built-ins are still in progress.
|
|
116
|
+
- Performance work is ongoing; expect lower throughput than OPA.
|
|
117
|
+
|
|
118
|
+
## Performance and benchmarks
|
|
119
|
+
|
|
120
|
+
Ruby::Rego focuses on correctness and clarity over raw throughput. Expect performance to scale with
|
|
121
|
+
policy complexity: deep references, large comprehensions, and heavy use of `with` modifiers cost more.
|
|
122
|
+
Memoization caches rule outputs and static references during a single evaluation to reduce repeated work.
|
|
123
|
+
|
|
124
|
+
### Comparison with OPA
|
|
125
|
+
|
|
126
|
+
OPA (Go) is highly optimized and typically faster, especially on large policies or high-throughput workloads.
|
|
127
|
+
Use Ruby::Rego when you need a pure Ruby runtime or tight integration with Ruby applications, and prefer OPA
|
|
128
|
+
for latency-critical or batch-heavy policy evaluation.
|
|
129
|
+
|
|
130
|
+
### Tips for performant policies
|
|
131
|
+
|
|
132
|
+
- Prefer indexed data structures and avoid deep, repeated reference chains.
|
|
133
|
+
- Keep comprehensions small; filter early and avoid nested comprehensions when possible.
|
|
134
|
+
- Use built-ins like `count`, `sum`, and `object.get` instead of manual loops.
|
|
135
|
+
- Avoid `with` modifiers on hot paths; they are intentionally isolated and reset memoization caches.
|
|
136
|
+
- Keep rule dependencies shallow to minimize repeated rule evaluation.
|
|
137
|
+
|
|
138
|
+
### Running benchmarks
|
|
139
|
+
|
|
140
|
+
Benchmarks use `benchmark-ips` and live in the `benchmark/` directory:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
bundle exec ruby benchmark/simple_rules.rb
|
|
144
|
+
bundle exec ruby benchmark/comprehensions.rb
|
|
145
|
+
bundle exec ruby benchmark/builtin_calls.rb
|
|
146
|
+
bundle exec ruby benchmark/complex_policy.rb
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### CLI profiling
|
|
150
|
+
|
|
151
|
+
Use `--profile` to capture timing and memory deltas for compilation and evaluation. Profiling output is
|
|
152
|
+
emitted to stderr so JSON output remains machine-readable.
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
bundle exec exe/rego-validate --policy examples/validation_policy.rego --config examples/sample_config.yaml --profile
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Contributing
|
|
159
|
+
|
|
160
|
+
Bug reports and pull requests are welcome on GitHub at [https://github.com/r6e/ruby-rego](https://github.com/r6e/ruby-rego).
|
|
161
|
+
|
|
162
|
+
1. Run `bin/setup` to install dependencies.
|
|
163
|
+
2. Run tests with `bundle exec rspec`.
|
|
164
|
+
3. Run quality checks:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
bundle exec rubocop
|
|
168
|
+
bundle exec reek lib/
|
|
169
|
+
bundle exec rubycritic lib/
|
|
170
|
+
bundle exec steep check
|
|
171
|
+
bundle exec typeprof lib/**/*.rb
|
|
172
|
+
bundle exec bundler-audit check --update
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Please include tests and documentation updates with your changes.
|
|
176
|
+
|
|
177
|
+
## License
|
|
178
|
+
|
|
179
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
180
|
+
|
|
181
|
+
## Code of Conduct
|
|
182
|
+
|
|
183
|
+
Everyone interacting in the Ruby::Rego project is expected to follow the code of conduct.
|
data/RELEASING.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Releasing Ruby::Rego
|
|
2
|
+
|
|
3
|
+
## 1) Prepare the release
|
|
4
|
+
|
|
5
|
+
- Update [lib/ruby/rego/version.rb](lib/ruby/rego/version.rb) with the new version.
|
|
6
|
+
- Update [CHANGELOG.md](CHANGELOG.md) with notable changes and release date.
|
|
7
|
+
- Run the full suite locally:
|
|
8
|
+
- `bundle exec rspec`
|
|
9
|
+
- `bundle exec rubocop --format simple --no-color`
|
|
10
|
+
- `bundle exec reek lib`
|
|
11
|
+
- `bundle exec steep check`
|
|
12
|
+
- `bundle exec typeprof lib/**/*.rb`
|
|
13
|
+
- Verify coverage is above 90% in `coverage/`.
|
|
14
|
+
|
|
15
|
+
## 2) Build and validate the gem
|
|
16
|
+
|
|
17
|
+
- Build the gem:
|
|
18
|
+
- `gem build ruby-rego.gemspec`
|
|
19
|
+
- Install the built gem locally:
|
|
20
|
+
- `gem install ./ruby-rego-<version>.gem`
|
|
21
|
+
- Smoke test the CLI:
|
|
22
|
+
- `rego-validate --policy examples/validation_policy.rego --config examples/sample_config.yaml`
|
|
23
|
+
|
|
24
|
+
## 3) Tag and publish
|
|
25
|
+
|
|
26
|
+
- Create a signed tag:
|
|
27
|
+
- `git tag -a v<version> -m "Release v<version>"`
|
|
28
|
+
- Push tags and main:
|
|
29
|
+
- `git push origin main --tags`
|
|
30
|
+
- Publish to RubyGems:
|
|
31
|
+
- `gem push ruby-rego-<version>.gem`
|
|
32
|
+
|
|
33
|
+
## 4) Post-release checks
|
|
34
|
+
|
|
35
|
+
- Confirm the release appears on RubyGems.
|
|
36
|
+
- Confirm the GitHub Pages docs deployed successfully.
|
|
37
|
+
- Create a GitHub release with release notes from the changelog.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "rspec/core/rake_task"
|
|
5
|
+
require "rake"
|
|
6
|
+
|
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
8
|
+
|
|
9
|
+
require "rubocop/rake_task"
|
|
10
|
+
|
|
11
|
+
RuboCop::RakeTask.new
|
|
12
|
+
|
|
13
|
+
desc "Run Reek"
|
|
14
|
+
task :reek do
|
|
15
|
+
sh "bundle exec reek lib"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
desc "Run RubyCritic"
|
|
19
|
+
task :rubycritic do
|
|
20
|
+
sh "bundle exec rubycritic lib"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
desc "Run Steep type checking"
|
|
24
|
+
task :steep do
|
|
25
|
+
sh "bundle exec steep check"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
desc "Run TypeProf for signatures"
|
|
29
|
+
task :typeprof do
|
|
30
|
+
sh "bundle exec typeprof lib/**/*.rb"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
desc "Run bundler-audit security checks"
|
|
34
|
+
task :bundler_audit do
|
|
35
|
+
sh "bundle exec bundler-audit check --update"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
task default: %i[spec rubocop reek rubycritic steep typeprof bundler_audit]
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
We support the latest release on the `main` branch. Security fixes are released as soon as possible. If you are using an older version, please upgrade to the latest release.
|
|
6
|
+
|
|
7
|
+
## Reporting a Vulnerability
|
|
8
|
+
|
|
9
|
+
Please report security issues responsibly by using GitHub Security Advisories:
|
|
10
|
+
|
|
11
|
+
1. Go to the repository’s “Security” tab.
|
|
12
|
+
2. Click “Report a vulnerability”.
|
|
13
|
+
3. Provide a detailed report with steps to reproduce, impact, and any suggested fixes.
|
|
14
|
+
|
|
15
|
+
You can also open a private advisory using:
|
|
16
|
+
[https://github.com/r6e/ruby-rego/security/advisories/new](https://github.com/r6e/ruby-rego/security/advisories/new)
|
|
17
|
+
|
|
18
|
+
We will acknowledge receipt as soon as possible, typically within 5 business days, and will work with you to understand and resolve the issue. We may ask for additional information during triage.
|
|
19
|
+
|
|
20
|
+
## Disclosure Policy
|
|
21
|
+
|
|
22
|
+
We follow coordinated disclosure. Please do not publicly disclose the issue until we have had a chance to investigate and provide a fix or mitigation.
|
|
23
|
+
|
|
24
|
+
## Supported Scope
|
|
25
|
+
|
|
26
|
+
This policy applies to the code and artifacts in this repository, including the published gem and documentation. Vulnerabilities in third‑party dependencies should be reported to their respective maintainers.
|