Skip to content

Commit

Permalink
Ruby bindings for existing FFI methods, plus eval_rule() (#188)
Browse files Browse the repository at this point in the history
* Ruby binding

* use relative regorus crate in ruby instead of a published version, also cargo fmt

* remove unnecessary Cargo.toml, include the top level Cargo.lock in ruby gem

* ruby bindings continued- add eval_rule, fix _json methods, update README.md

also added rubocop-minitest and rubocop-rake, and added more test coverage

* update README.md to include Ruby bindings

Closes #191
  • Loading branch information
thedavemarshall authored Apr 2, 2024
1 parent 3d98c3b commit e86801b
Show file tree
Hide file tree
Showing 22 changed files with 757 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"bindings/python",
"bindings/wasm",
"bindings/java",
"bindings/ruby/ext/regorusrb",
]

[package]
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Regorus is also
- *extensible* - Extend the Rego language by implementing custom stateful builtins in Rust.
See [add_extension](https://github.com/microsoft/regorus/blob/fc68bf9c8bea36427dae9401a7d1f6ada771f7ab/src/engine.rs#L352).
Support for extensibility using other languages coming soon.
- *polyglot* - In addition to Rust, Regorus can be used from *C*, *C++*, *C#*, *Golang*, *Java*, *Javascript* and *Python*.
- *polyglot* - In addition to Rust, Regorus can be used from *C*, *C++*, *C#*, *Golang*, *Java*, *Javascript*, *Python*, and *Ruby*.
This is made possible by the excellent FFI tools available in the Rust ecosystem. See [bindings](#bindings) for information on how to use Regorus from different languages.

To try out a *Javascript(WASM)* compiled version of Regorus from your browser, visit [Regorus Playground](https://anakrish.github.io/regorus-playground/).
Expand Down Expand Up @@ -90,6 +90,8 @@ Regorus can be used from a variety of languages:
- *Javascript*: Regorus is compiled to WASM using [wasmpack](https://github.com/rustwasm/wasm-pack).
See [bindings/wasm](https://github.com/microsoft/regorus/tree/main/bindings/wasm) for an example of using Regorus from nodejs.
To try out a *Javascript(WASM)* compiled version of Regorus from your browser, visit [Regorus Playground](https://anakrish.github.io/regorus-playground/).
- *Ruby*: Ruby bindings are developed using [magnus](https://github.com/matsadler/magnus).
See [bindings/ruby](https://github.com/microsoft/regorus/tree/main/bindings/ruby).

To avoid operational overhead, we currently don't publish these bindings to various repositories.
It is straight-forward to build these bindings yourself.
Expand Down
14 changes: 14 additions & 0 deletions bindings/ruby/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/.bundle/
/.yardoc
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/
*.bundle
*.so
*.o
*.a
mkmf.log
target/
29 changes: 29 additions & 0 deletions bindings/ruby/.rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require:
- rubocop-minitest
- rubocop-rake

AllCops:
TargetRubyVersion: 3.0
NewCops: enable

Layout/LineLength:
Max: 180

Lint/EmptyClass:
Enabled: false

Metrics/ClassLength:
Exclude:
- 'test/**/*.rb'

Metrics/MethodLength:
Enabled: false

Style/StringLiterals:
EnforcedStyle: double_quotes

Style/StringLiteralsInInterpolation:
EnforcedStyle: double_quotes

Style/WordArray:
Enabled: false
1 change: 1 addition & 0 deletions bindings/ruby/.tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ruby 3.3.0
5 changes: 5 additions & 0 deletions bindings/ruby/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## [Unreleased]

## [0.1.0] - 2024-03-29

- Initial release
16 changes: 16 additions & 0 deletions bindings/ruby/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

source "https://rubygems.org"

# Specify your gem's dependencies in regorusrb.gemspec
gemspec

# These gems are required for local development and testing,
# but won't be included in the published gem
gem "minitest", "~> 5.16"
gem "rake", "~> 13.0"
gem "rake-compiler"
gem "rb_sys", "~> 0.9.63"
gem "rubocop", "~> 1.62", require: false
gem "rubocop-minitest", require: false
gem "rubocop-rake", require: false
61 changes: 61 additions & 0 deletions bindings/ruby/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
PATH
remote: .
specs:
regorusrb (0.1.0)

GEM
remote: https://rubygems.org/
specs:
ast (2.4.2)
json (2.7.1)
language_server-protocol (3.17.0.3)
minitest (5.22.3)
parallel (1.24.0)
parser (3.3.0.5)
ast (~> 2.4.1)
racc
racc (1.7.3)
rainbow (3.1.1)
rake (13.1.0)
rake-compiler (1.2.7)
rake
rb_sys (0.9.90)
regexp_parser (2.9.0)
rexml (3.2.6)
rubocop (1.62.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.31.2)
parser (>= 3.3.0.4)
rubocop-minitest (0.35.0)
rubocop (>= 1.61, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rake (0.6.0)
rubocop (~> 1.0)
ruby-progressbar (1.13.0)
unicode-display_width (2.5.0)

PLATFORMS
ruby
x86_64-linux

DEPENDENCIES
minitest (~> 5.16)
rake (~> 13.0)
rake-compiler
rb_sys (~> 0.9.63)
regorusrb!
rubocop (~> 1.62)
rubocop-minitest
rubocop-rake

BUNDLED WITH
2.5.7
1 change: 1 addition & 0 deletions bindings/ruby/LICENSE.txt
95 changes: 95 additions & 0 deletions bindings/ruby/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Regorusrb

**Regorus** is

- *Rego*-*Rus(t)* - A fast, light-weight [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/)
interpreter written in Rust.
- *Rigorous* - A rigorous enforcer of well-defined Rego semantics.

## Installation

Regorus can be used in Ruby by configuring bundler to build from the remote git source.

Use the bundler CLI to add the gem from remote git source:
`
bundle add regorus --git 'https://github.com/microsoft/regorus/tree/main/bindings/ruby'
`

or manually edit your gemfile to include the following
`
gem "regorus", git: "https://github.com/microsoft/regorus/tree/main/bindings/ruby"
`

It is not yet available in rubygems.

See [Repository](https://github.com/microsoft/regorus).

To build this gem locally without bundler,

`rake build`

then to install the gem and build the native extensions

`gem install --local ./pkg/regorusrb-0.1.0.gem`

## Usage

```ruby
require "regorus"

engine = Regorus::Engine.new

engine.add_policy_from_file('../../tests/aci/framework.rego')
engine.add_policy_from_file('../../tests/aci/api.rego')
engine.add_policy_from_file('../../tests/aci/policy.rego')


# can be strings or symbols
data = {
metadata: {
devices: {
"/run/layers/p0-layer0": "1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766",
"/run/layers/p0-layer1": "e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c",
"/run/layers/p0-layer2": "eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79",
"/run/layers/p0-layer3": "41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156",
"/run/layers/p0-layer4": "4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c",
"/run/layers/p0-layer5": "fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a"
}
}
}

engine.add_data(data)
input = {
"containerID": "container0",
"layerPaths": [
"/run/layers/p0-layer0",
"/run/layers/p0-layer1",
"/run/layers/p0-layer2",
"/run/layers/p0-layer3",
"/run/layers/p0-layer4",
"/run/layers/p0-layer5"
],
"target": "/run/gcs/c/container0/rootfs"
}

engine.set_input(input)

# Evaluate a specife rule
rule_results = engine.eval_rule('data.framework.mount_overlay')
puts rule_results # { "allowed" => true, "metadata" => [...]}

# Or evalute a full policy document
query_results = engine.eval_query('data.framework')
puts query_results[:result][0]

# Query results can can also be returned as JSON strings instead of Ruby Hash structure
results_json = engine.eval_query_as_json('data.framework.mount_overlay=x')
puts results_json
```

## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).

23 changes: 23 additions & 0 deletions bindings/ruby/Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

require "bundler/gem_tasks"
require "minitest/test_task"

Minitest::TestTask.create

require "rubocop/rake_task"

RuboCop::RakeTask.new

require "rb_sys/extensiontask"

desc "build the .gem file, including native extensions, according to the .gemspec"
task build: :compile

GEMSPEC = Gem::Specification.load("regorusrb.gemspec")

RbSys::ExtensionTask.new("regorusrb", GEMSPEC) do |ext|
ext.lib_dir = "lib/regorusrb"
end

task default: %i[compile test rubocop]
11 changes: 11 additions & 0 deletions bindings/ruby/bin/console
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require "bundler/setup"
require "regorus"

# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.

require "irb"
IRB.start(__FILE__)
8 changes: 8 additions & 0 deletions bindings/ruby/bin/setup
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
set -vx

bundle install

# Do any other automated setup that you need to do here
16 changes: 16 additions & 0 deletions bindings/ruby/ext/regorusrb/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "regorusrb"
version = "0.1.0"
edition = "2021"
description = "Ruby bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
publish = false

[lib]
crate-type = ["cdylib"]
path = "src/lib.rs"

[dependencies]
magnus = { version = "0.6.2" }
regorus = { path = "../../../.." }
serde_json = "1.0.115"
serde_magnus = "0.8.1"
6 changes: 6 additions & 0 deletions bindings/ruby/ext/regorusrb/extconf.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

require "mkmf"
require "rb_sys/mkmf"

create_rust_makefile("regorusrb/regorusrb")
Loading

0 comments on commit e86801b

Please sign in to comment.