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.
Files changed (124) hide show
  1. checksums.yaml +7 -0
  2. data/.reek.yml +80 -0
  3. data/.vscode/extensions.json +19 -0
  4. data/.vscode/launch.json +35 -0
  5. data/.vscode/settings.json +25 -0
  6. data/.vscode/tasks.json +117 -0
  7. data/.yardopts +12 -0
  8. data/ARCHITECTURE.md +39 -0
  9. data/CHANGELOG.md +25 -0
  10. data/CODE_OF_CONDUCT.md +10 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +183 -0
  13. data/RELEASING.md +37 -0
  14. data/Rakefile +38 -0
  15. data/SECURITY.md +26 -0
  16. data/Steepfile +10 -0
  17. data/TODO.md +35 -0
  18. data/benchmark/builtin_calls.rb +29 -0
  19. data/benchmark/complex_policy.rb +19 -0
  20. data/benchmark/comprehensions.rb +19 -0
  21. data/benchmark/simple_rules.rb +20 -0
  22. data/examples/README.md +27 -0
  23. data/examples/sample_config.yaml +2 -0
  24. data/examples/simple_policy.rego +7 -0
  25. data/examples/validation_policy.rego +11 -0
  26. data/exe/rego-validate +6 -0
  27. data/lib/ruby/rego/ast/base.rb +95 -0
  28. data/lib/ruby/rego/ast/binary_op.rb +64 -0
  29. data/lib/ruby/rego/ast/call.rb +27 -0
  30. data/lib/ruby/rego/ast/composite.rb +48 -0
  31. data/lib/ruby/rego/ast/comprehension.rb +63 -0
  32. data/lib/ruby/rego/ast/every.rb +37 -0
  33. data/lib/ruby/rego/ast/import.rb +32 -0
  34. data/lib/ruby/rego/ast/literal.rb +70 -0
  35. data/lib/ruby/rego/ast/module.rb +32 -0
  36. data/lib/ruby/rego/ast/package.rb +22 -0
  37. data/lib/ruby/rego/ast/query.rb +63 -0
  38. data/lib/ruby/rego/ast/reference.rb +58 -0
  39. data/lib/ruby/rego/ast/rule.rb +114 -0
  40. data/lib/ruby/rego/ast/unary_op.rb +42 -0
  41. data/lib/ruby/rego/ast/variable.rb +22 -0
  42. data/lib/ruby/rego/ast.rb +17 -0
  43. data/lib/ruby/rego/builtins/aggregates.rb +124 -0
  44. data/lib/ruby/rego/builtins/base.rb +95 -0
  45. data/lib/ruby/rego/builtins/collections/array_ops.rb +103 -0
  46. data/lib/ruby/rego/builtins/collections/object_ops.rb +120 -0
  47. data/lib/ruby/rego/builtins/collections/set_ops.rb +51 -0
  48. data/lib/ruby/rego/builtins/collections.rb +137 -0
  49. data/lib/ruby/rego/builtins/comparisons/casts.rb +139 -0
  50. data/lib/ruby/rego/builtins/comparisons.rb +84 -0
  51. data/lib/ruby/rego/builtins/numeric_helpers.rb +56 -0
  52. data/lib/ruby/rego/builtins/registry.rb +199 -0
  53. data/lib/ruby/rego/builtins/registry_helpers.rb +27 -0
  54. data/lib/ruby/rego/builtins/strings/case_ops.rb +22 -0
  55. data/lib/ruby/rego/builtins/strings/concat.rb +19 -0
  56. data/lib/ruby/rego/builtins/strings/formatting.rb +35 -0
  57. data/lib/ruby/rego/builtins/strings/helpers.rb +62 -0
  58. data/lib/ruby/rego/builtins/strings/number_helpers.rb +48 -0
  59. data/lib/ruby/rego/builtins/strings/search.rb +63 -0
  60. data/lib/ruby/rego/builtins/strings/split.rb +19 -0
  61. data/lib/ruby/rego/builtins/strings/substring.rb +22 -0
  62. data/lib/ruby/rego/builtins/strings/trim.rb +42 -0
  63. data/lib/ruby/rego/builtins/strings/trim_helpers.rb +62 -0
  64. data/lib/ruby/rego/builtins/strings.rb +58 -0
  65. data/lib/ruby/rego/builtins/types.rb +89 -0
  66. data/lib/ruby/rego/call_name.rb +55 -0
  67. data/lib/ruby/rego/cli.rb +1122 -0
  68. data/lib/ruby/rego/compiled_module.rb +114 -0
  69. data/lib/ruby/rego/compiler.rb +1097 -0
  70. data/lib/ruby/rego/environment/overrides.rb +33 -0
  71. data/lib/ruby/rego/environment/reference_resolution.rb +86 -0
  72. data/lib/ruby/rego/environment.rb +230 -0
  73. data/lib/ruby/rego/environment_pool.rb +71 -0
  74. data/lib/ruby/rego/error_handling.rb +58 -0
  75. data/lib/ruby/rego/error_payload.rb +34 -0
  76. data/lib/ruby/rego/errors.rb +196 -0
  77. data/lib/ruby/rego/evaluator/assignment_support.rb +126 -0
  78. data/lib/ruby/rego/evaluator/binding_helpers.rb +60 -0
  79. data/lib/ruby/rego/evaluator/comprehension_evaluator.rb +182 -0
  80. data/lib/ruby/rego/evaluator/expression_dispatch.rb +45 -0
  81. data/lib/ruby/rego/evaluator/expression_evaluator.rb +492 -0
  82. data/lib/ruby/rego/evaluator/object_literal_evaluator.rb +52 -0
  83. data/lib/ruby/rego/evaluator/operator_evaluator.rb +163 -0
  84. data/lib/ruby/rego/evaluator/query_node_builder.rb +38 -0
  85. data/lib/ruby/rego/evaluator/reference_key_resolver.rb +50 -0
  86. data/lib/ruby/rego/evaluator/reference_resolver.rb +352 -0
  87. data/lib/ruby/rego/evaluator/rule_evaluator/bindings.rb +70 -0
  88. data/lib/ruby/rego/evaluator/rule_evaluator.rb +550 -0
  89. data/lib/ruby/rego/evaluator/rule_value_provider.rb +56 -0
  90. data/lib/ruby/rego/evaluator/variable_collector.rb +221 -0
  91. data/lib/ruby/rego/evaluator.rb +174 -0
  92. data/lib/ruby/rego/lexer/number_reader.rb +68 -0
  93. data/lib/ruby/rego/lexer/stream.rb +137 -0
  94. data/lib/ruby/rego/lexer/string_reader.rb +90 -0
  95. data/lib/ruby/rego/lexer/template_string_reader.rb +62 -0
  96. data/lib/ruby/rego/lexer.rb +206 -0
  97. data/lib/ruby/rego/location.rb +73 -0
  98. data/lib/ruby/rego/memoization.rb +67 -0
  99. data/lib/ruby/rego/parser/collections.rb +173 -0
  100. data/lib/ruby/rego/parser/expressions.rb +216 -0
  101. data/lib/ruby/rego/parser/precedence.rb +42 -0
  102. data/lib/ruby/rego/parser/query.rb +139 -0
  103. data/lib/ruby/rego/parser/references.rb +115 -0
  104. data/lib/ruby/rego/parser/rules.rb +310 -0
  105. data/lib/ruby/rego/parser.rb +210 -0
  106. data/lib/ruby/rego/policy.rb +50 -0
  107. data/lib/ruby/rego/result.rb +91 -0
  108. data/lib/ruby/rego/token.rb +206 -0
  109. data/lib/ruby/rego/unifier.rb +451 -0
  110. data/lib/ruby/rego/value.rb +379 -0
  111. data/lib/ruby/rego/version.rb +7 -0
  112. data/lib/ruby/rego/with_modifiers/with_modifier.rb +37 -0
  113. data/lib/ruby/rego/with_modifiers/with_modifier_applier.rb +48 -0
  114. data/lib/ruby/rego/with_modifiers/with_modifier_builtin_override.rb +128 -0
  115. data/lib/ruby/rego/with_modifiers/with_modifier_context.rb +120 -0
  116. data/lib/ruby/rego/with_modifiers/with_modifier_path_key_resolver.rb +42 -0
  117. data/lib/ruby/rego/with_modifiers/with_modifier_path_override.rb +99 -0
  118. data/lib/ruby/rego/with_modifiers/with_modifier_root_scope.rb +58 -0
  119. data/lib/ruby/rego.rb +72 -0
  120. data/sig/objspace.rbs +4 -0
  121. data/sig/psych.rbs +7 -0
  122. data/sig/rego_validate.rbs +382 -0
  123. data/sig/ruby/rego.rbs +2150 -0
  124. 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
+ }
@@ -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
+ }
@@ -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
@@ -0,0 +1,12 @@
1
+ --markup markdown
2
+ --markup-provider kramdown
3
+ --no-private
4
+ --protected
5
+ --hide-tag private
6
+ --output-dir doc/yard
7
+ README.md
8
+ ARCHITECTURE.md
9
+ CHANGELOG.md
10
+ CODE_OF_CONDUCT.md
11
+ LICENSE.txt
12
+ lib/**/*.rb
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.
@@ -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.