From 6a167143cb81c29d2fdab969c2d8ade56b3db1f8 Mon Sep 17 00:00:00 2001 From: thedavemarshall Date: Tue, 9 Apr 2024 20:58:25 -0500 Subject: [PATCH] update ruby bindings (#200) --- bindings/ruby/Cargo.toml | 3 +++ bindings/ruby/Gemfile | 2 +- bindings/ruby/Gemfile.lock | 12 +++++++----- bindings/ruby/LICENSE.txt | 22 +++++++++++++++++++++- bindings/ruby/README.md | 12 ++++++++---- bindings/ruby/Rakefile | 16 +++++++++++----- bindings/ruby/ext/regorusrb/Cargo.toml | 4 ++-- bindings/ruby/ext/regorusrb/extconf.rb | 4 +++- bindings/ruby/ext/regorusrb/src/lib.rs | 23 ++++++++++++++++++++--- bindings/ruby/lib/regorus.rb | 2 +- bindings/ruby/regorusrb.gemspec | 16 +++------------- bindings/ruby/test/test_regorus.rb | 24 ++++++++++++++++++++++++ 12 files changed, 104 insertions(+), 36 deletions(-) create mode 100644 bindings/ruby/Cargo.toml mode change 120000 => 100644 bindings/ruby/LICENSE.txt diff --git a/bindings/ruby/Cargo.toml b/bindings/ruby/Cargo.toml new file mode 100644 index 0000000..a864e4d --- /dev/null +++ b/bindings/ruby/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["ext/regorusrb"] +resolver = "2" diff --git a/bindings/ruby/Gemfile b/bindings/ruby/Gemfile index 21c6136..80c35e6 100644 --- a/bindings/ruby/Gemfile +++ b/bindings/ruby/Gemfile @@ -10,7 +10,7 @@ gemspec gem "minitest", "~> 5.16" gem "rake", "~> 13.0" gem "rake-compiler" -gem "rb_sys", "~> 0.9.63" +gem "rake-compiler-dock" gem "rubocop", "~> 1.62", require: false gem "rubocop-minitest", require: false gem "rubocop-rake", require: false diff --git a/bindings/ruby/Gemfile.lock b/bindings/ruby/Gemfile.lock index cc83c86..7059106 100644 --- a/bindings/ruby/Gemfile.lock +++ b/bindings/ruby/Gemfile.lock @@ -2,12 +2,13 @@ PATH remote: . specs: regorusrb (0.1.0) + rb_sys (~> 0.9.91) GEM remote: https://rubygems.org/ specs: ast (2.4.2) - json (2.7.1) + json (2.7.2) language_server-protocol (3.17.0.3) minitest (5.22.3) parallel (1.24.0) @@ -16,13 +17,14 @@ GEM racc racc (1.7.3) rainbow (3.1.1) - rake (13.1.0) + rake (13.2.1) rake-compiler (1.2.7) rake - rb_sys (0.9.90) + rake-compiler-dock (1.4.0) + rb_sys (0.9.91) regexp_parser (2.9.0) rexml (3.2.6) - rubocop (1.62.1) + rubocop (1.63.0) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) @@ -51,7 +53,7 @@ DEPENDENCIES minitest (~> 5.16) rake (~> 13.0) rake-compiler - rb_sys (~> 0.9.63) + rake-compiler-dock regorusrb! rubocop (~> 1.62) rubocop-minitest diff --git a/bindings/ruby/LICENSE.txt b/bindings/ruby/LICENSE.txt deleted file mode 120000 index 30cff74..0000000 --- a/bindings/ruby/LICENSE.txt +++ /dev/null @@ -1 +0,0 @@ -../../LICENSE \ No newline at end of file diff --git a/bindings/ruby/LICENSE.txt b/bindings/ruby/LICENSE.txt new file mode 100644 index 0000000..9e841e7 --- /dev/null +++ b/bindings/ruby/LICENSE.txt @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/bindings/ruby/README.md b/bindings/ruby/README.md index 6d2dc25..0313827 100644 --- a/bindings/ruby/README.md +++ b/bindings/ruby/README.md @@ -10,14 +10,18 @@ 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: +If using [Bundler](https://bundler.io/) to manage gems (recommended), edit your gemfile to include the following ` -bundle add regorus --git 'https://github.com/microsoft/regorus/tree/main/bindings/ruby' +gem "regorusrb", git: "https://github.com/microsoft/regorus/", glob: "bindings/ruby/*.gemspec" ` -or manually edit your gemfile to include the following +or manually install checkout the source and build the gem + ` -gem "regorus", git: "https://github.com/microsoft/regorus/tree/main/bindings/ruby" +git clone https://github.com/microsoft/regorus/ +cd regorus/bindings/ruby +rake && rake build # should eventually output 'regorusrb 0.1.0 built to pkg/regorusrb-0.1.0.gem.' +gem install --local ./pkg/regorusrb-0.1.0.gem ` It is not yet available in rubygems. diff --git a/bindings/ruby/Rakefile b/bindings/ruby/Rakefile index 9b8e4f2..7bf7edd 100644 --- a/bindings/ruby/Rakefile +++ b/bindings/ruby/Rakefile @@ -2,22 +2,28 @@ require "bundler/gem_tasks" require "minitest/test_task" +require "rake/extensiontask" +require "rubocop/rake_task" +require "rb_sys/extensiontask" 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" + ext.lib_dir = "lib/regorus" + ext.cross_compile = true + ext.cross_platform = %w[x86-mingw32 x64-mingw-ucrt x64-mingw32 x86-linux x86_64-linux x86_64-darwin arm64-darwin] end task default: %i[compile test rubocop] + +desc "Build native extension for a given platform (i.e. rake 'native[x86_64-linux]')" +task :native, [:platform] do |_t, platform:| + sh "bundle", "exec", "rb-sys-dock", "--platform", platform, "--build" +end diff --git a/bindings/ruby/ext/regorusrb/Cargo.toml b/bindings/ruby/ext/regorusrb/Cargo.toml index 8f5be43..180b5c0 100644 --- a/bindings/ruby/ext/regorusrb/Cargo.toml +++ b/bindings/ruby/ext/regorusrb/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["cdylib"] path = "src/lib.rs" [dependencies] -magnus = { version = "0.6.2" } -regorus = { path = "../../../.." } +magnus = { version = "0.6.3" } +regorus = { git = "https://github.com/microsoft/regorus" } serde_json = "1.0.115" serde_magnus = "0.8.1" diff --git a/bindings/ruby/ext/regorusrb/extconf.rb b/bindings/ruby/ext/regorusrb/extconf.rb index 2e7d4a7..f2c54b4 100644 --- a/bindings/ruby/ext/regorusrb/extconf.rb +++ b/bindings/ruby/ext/regorusrb/extconf.rb @@ -3,4 +3,6 @@ require "mkmf" require "rb_sys/mkmf" -create_rust_makefile("regorusrb/regorusrb") +create_rust_makefile("regorus/regorusrb") do |r| + r.auto_install_rust_toolchain = true +end diff --git a/bindings/ruby/ext/regorusrb/src/lib.rs b/bindings/ruby/ext/regorusrb/src/lib.rs index ed8e419..d9e36fe 100644 --- a/bindings/ruby/ext/regorusrb/src/lib.rs +++ b/bindings/ruby/ext/regorusrb/src/lib.rs @@ -158,9 +158,9 @@ impl Engine { }) } - fn eval_rule(&self, path: String) -> Result, Error> { + fn eval_rule(&self, query: String) -> Result, Error> { let result = - self.engine.borrow_mut().eval_rule(path).map_err(|e| { + self.engine.borrow_mut().eval_rule(query).map_err(|e| { Error::new(runtime_error(), format!("Failed to evaluate rule: {}", e)) })?; @@ -176,6 +176,21 @@ impl Engine { }), } } + + fn eval_bool_query(&self, query: String) -> Result { + self.engine + .borrow_mut() + .eval_bool_query(query, false) + .map_err(|e| Error::new(runtime_error(), format!("Failed to evaluate query: {}", e))) + } + + fn eval_allow_query(&self, query: String) -> Result { + Ok(self.engine.borrow_mut().eval_allow_query(query, false)) + } + + fn eval_deny_query(&self, query: String) -> Result { + Ok(self.engine.borrow_mut().eval_deny_query(query, false)) + } } #[magnus::init] @@ -219,6 +234,8 @@ fn init(ruby: &Ruby) -> Result<(), Error> { engine_class.define_method("eval_query", method!(Engine::eval_query, 1))?; engine_class.define_method("eval_query_as_json", method!(Engine::eval_query_as_json, 1))?; engine_class.define_method("eval_rule", method!(Engine::eval_rule, 1))?; - + engine_class.define_method("eval_bool_query", method!(Engine::eval_bool_query, 1))?; + engine_class.define_method("eval_allow_query", method!(Engine::eval_allow_query, 1))?; + engine_class.define_method("eval_deny_query", method!(Engine::eval_deny_query, 1))?; Ok(()) } diff --git a/bindings/ruby/lib/regorus.rb b/bindings/ruby/lib/regorus.rb index f37a89c..237d0f0 100644 --- a/bindings/ruby/lib/regorus.rb +++ b/bindings/ruby/lib/regorus.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require_relative "regorus/version" -require_relative "regorusrb/regorusrb" +require_relative "regorus/regorusrb" module Regorus class Engine; end diff --git a/bindings/ruby/regorusrb.gemspec b/bindings/ruby/regorusrb.gemspec index 579e72b..f110166 100644 --- a/bindings/ruby/regorusrb.gemspec +++ b/bindings/ruby/regorusrb.gemspec @@ -20,21 +20,11 @@ Gem::Specification.new do |spec| spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/bindings/ruby/CHANGELOG.md" spec.metadata["rubygems_mfa_required"] = "true" - # Specify which files should be added to the gem when it is released. - # The `git ls-files -z` loads the files in the RubyGem that have been added into git. - gemspec = File.basename(__FILE__) - spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls| - ls.readlines("\x0", chomp: true).reject do |f| - (f == gemspec) || - f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile]) - end - end - - # Ensure Cargo.lock is included - spec.files << "../../Cargo.lock" if File.exist?("../../Cargo.lock") + spec.files = Dir["lib/*.rb", "lib/regorus/*.rb", "ext/**/*.{rs,rb,lock,toml}", "Cargo.{lock,toml}", "LICENSE.txt", "README.md"] spec.bindir = "exe" spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.extensions = ["ext/regorusrb/Cargo.toml"] + spec.extensions = ["ext/regorusrb/extconf.rb"] + spec.add_dependency "rb_sys", "~> 0.9.91" end diff --git a/bindings/ruby/test/test_regorus.rb b/bindings/ruby/test/test_regorus.rb index 3bdf6b2..02df75c 100644 --- a/bindings/ruby/test/test_regorus.rb +++ b/bindings/ruby/test/test_regorus.rb @@ -119,6 +119,30 @@ class TestRegorus < Minitest::Test refute @engine.eval_rule("data.regorus_test.is_manager_bool") end + def test_eval_bool_query + assert @engine.eval_bool_query("1 < 2") + refute @engine.eval_bool_query("1 > 2") + assert_raises(RuntimeError) { @engine.eval_bool_query("1 + 1") } + assert_raises(RuntimeError) { @engine.eval_bool_query("true; true") } + assert_raises(RuntimeError) { @engine.eval_bool_query("true; false; true") } + end + + def test_eval_allow_query + assert @engine.eval_allow_query("1 < 2") + refute @engine.eval_allow_query("1 > 2") + refute @engine.eval_allow_query("1 + 1") + refute @engine.eval_allow_query("true; true") + refute @engine.eval_allow_query("true; false; true") + end + + def test_eval_deny_query + assert @engine.eval_deny_query("1 < 2") + refute @engine.eval_deny_query("1 > 2") + assert @engine.eval_deny_query("1 + 1") + assert @engine.eval_deny_query("true; true") + assert @engine.eval_deny_query("true; false; true") + end + def test_missing_rules_handling @engine.set_input(input_for(ALICE)) assert_raises(RuntimeError) { @engine.eval_rule("data.regorus_test.not_a_rule") }