Skip to content

Commit

Permalink
Merge pull request #3 from driv3r/refactor-pipeline
Browse files Browse the repository at this point in the history
Refactor pipeline
  • Loading branch information
Leszek Zalewski authored Dec 30, 2020
2 parents e00f303 + 58a947a commit 560626a
Show file tree
Hide file tree
Showing 18 changed files with 627 additions and 153 deletions.
43 changes: 31 additions & 12 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,24 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Set up Rust
id: toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
target: ${{ matrix.target }}
components: rustfmt, clippy

- uses: actions/cache@v2
if: matrix.os != 'macos-latest'
with:
path: |
~/.cargo/registry
~/.cargo/git
$CARGO_HOME/bin/
$CARGO_HOME/registry/index/
$CARGO_HOME/registry/cache/
$CARGO_HOME/git/db/
target
key: ${{ runner.os }}-cargo-v1-${{ hashFiles('**/Cargo.lock') }}
key: ${{ runner.os }}-cargo-${{ steps.toolchain.outputs.rustc_hash}}-${{ hashFiles('**/Cargo.lock') }}

# Target older versions of OSX (as old as we reasonably can) to ensure we
# can run across a wide variety of systems.
Expand All @@ -48,15 +54,27 @@ jobs:
run: |
echo MACOSX_DEPLOYMENT_TARGET=10.7 >> $GITHUB_ENV
- name: Test
run: |
cargo check --all --all-targets --frozen
cargo build
cargo test
- uses: actions-rs/cargo@v1
with:
command: build

- name: Compile
run: |
cargo build --release --target ${{ matrix.target }}
- uses: actions-rs/cargo@v1
with:
command: test

- uses: actions-rs/cargo@v1
with:
command: clippy

- uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check

- uses: actions-rs/cargo@v1
with:
command: build
args: --release --target ${{ matrix.target }}

- uses: actions/upload-artifact@v2
if: ${{ matrix.os != 'windows-latest' }}
Expand All @@ -80,7 +98,7 @@ jobs:
- uses: actions/cache@v2
with:
path: vendor/bundle
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock')}}-ruby-2.5.0
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock')}}-${{ hashFiles('../.tool-versions') }}

- name: Set up Ruby
uses: ruby/setup-ruby@v1
Expand All @@ -97,6 +115,7 @@ jobs:
- name: Prep gems
run: |
sudo apt install liburcu-dev
gem install bundler -v 2.2.3
bundle install --path vendor/bundle
Expand Down
6 changes: 6 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ AllCops:
TargetRubyVersion: 2.5
NewCops: enable
SuggestExtensions: false
Exclude:
- 'node_modules/**/*'
- 'tmp/**/*'
- 'vendor/**/*'
- '.git/**/*'
- 'spec/benchmark.rb'

Style/StringLiterals:
Enabled: true
Expand Down
104 changes: 104 additions & 0 deletions BENCHMARK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Benchmarks

Compare various gems doing json schema validation.

At the moment we are only looking at gems implementing Draft 7 of json schema.

Update by running:

```bash
bundle exec ruby spec/benchmark.rb
```

> Run against:
>
> - #37~20.04.2-Ubuntu @ kernel: 5.8.0-34-generic
> - AMD Ryzen 9 3900X 12-Core
> - 32GB RAM
## Gem#valid?

Simple boolean result.

> Results in instructions/validations per second.
<table>
<thead>
<tr>
<th rowspan=2 >Gem</th>
<th colspan=2 >Tiny Schema</th>
<th colspan=2 >Schema</th>
<th >Big Schema</th>
</tr>
<tr>
<th>Valid</th>
<th>Invalid</th>
<th>Valid</th>
<th>Invalid</th>
<th>Valid</th>
</tr>
</thead>

<tbody>
<tr>
<th>RustyJSONSchema</th>
<td> 362966.1</td>
<td> 423275.2</td>
<td> 285533.3</td>
<td> 303590.7</td>
<td> 12.8</td>
</tr>
<tr>
<th>JSONSchemer</th>
<td> 98019.9 - 3.70x </td>
<td> 258478.2 - 1.64x </td>
<td> 14239.3 - 20.05x </td>
<td> 166074.7 - 1.83x </td>
<td> 0.5 - 25.42x </td>
</tr>
</tbody>
</table>

## Gem#validate

Returns a list of validation errors if any

> Results in instructions/validations per second.
<table>
<thead>
<tr>
<th rowspan=2 >Gem</th>
<th colspan=2 >Tiny Schema</th>
<th colspan=2 >Schema</th>
<th >Big Schema</th>
</tr>
<tr>
<th>Valid</th>
<th>Invalid</th>
<th>Valid</th>
<th>Invalid</th>
<th>Valid</th>
</tr>
</thead>

<tbody>
<tr>
<th>RustyJSONSchema</th>
<td> 180723.6</td>
<td> 148233.0</td>
<td> 151118.6</td>
<td> 121512.6</td>
<td> 13.0</td>
</tr>
<tr>
<th>JSONSchemer</th>
<td> 98568.1 - 1.83x </td>
<td> 99045.2 - 1.50x </td>
<td> 14385.3 - 10.51x </td>
<td> 12263.2 - 9.91x </td>
<td> 0.5 - 26.02x </td>
</tr>
</tbody>
</table>

5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.2.0]
### Added
- `validate` function returning array of error messages instead of simple boolean
- `validate` function returning array of error messages.
- tooling for testing memory usage locally

## [0.1.0]
### Added
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
rusty_json_schema (0.1.0)
rusty_json_schema (0.2.0)
ffi (~> 1.14)
json (>= 1.0)

Expand Down
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# RustyJSONSchema

FFI wrapper around [`jsonschema`](https://github.com/Stranger6667/jsonschema-rs) rust library. Props go to original project.
[![Gem Version](https://badge.fury.io/rb/rusty_json_schema.svg)](https://badge.fury.io/rb/rusty_json_schema)

FFI wrapper around [`jsonschema`](https://github.com/Stranger6667/jsonschema-rs) Rust library. Props go to original project.

Currently during heavy development.

Expand All @@ -14,11 +16,15 @@ gem "rusty_json_schema"

And then execute:

$ bundle install
```
$ bundle install
```

Or install it yourself as:

$ gem install rusty_json_schema
```
$ gem install rusty_json_schema
```

## Usage

Expand All @@ -32,13 +38,28 @@ Validate events like

```ruby
validator.valid?(event_json)
# => true/false
```

To get validation errors

```ruby
validator.validate(event_json)
# => ["invalid...", ...]
```

## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` 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).
To install this gem onto your local machine, run `bundle exec rake install`.

To release a new version:

- update version number in `version.rb` & `CHANGELOG.md`
- create GitHub release with tag being new version prefixed with `v`, i.e. for `VERSION="0.1.0"` it would be `v0.1.0`
- pull `*.gem` artifact from release build
- `gem push *.gem` in order to publish it in [rubygems.org](https://rubygems.org).

## Contributing

Expand Down
86 changes: 16 additions & 70 deletions lib/rusty_json_schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,27 @@
require "ffi"
require "json"

require_relative "rusty_json_schema/version"
require "rusty_json_schema/version"
require "rusty_json_schema/nodes_array"
require "rusty_json_schema/validator"
require "rusty_json_schema/binding"

# JSON Schema validation
#
# ## Example
#
# validator = RustyJSONSchema.build(schema) # where schema is a json string of the schema
# validator.valid?(event) # where event is a json string of schema
# validator = RustyJSONSchema.build(schema)
#
# validator.valid?(event)
# # => true/false
#
# validator.validate(event)
# # => [] / ["...error messages", ...]
#
module RustyJSONSchema

class Error < StandardError; end

class << self

attr_writer :processor
Expand All @@ -30,74 +39,11 @@ def dump(data)
end
end

def build(schema)
RustyJSONSchema::Validator::Binding.new(dump(schema))
end

end

class Error < StandardError; end

# Handles release of the pointer automatically
# with Ruby GC. This way we can intialize validator
# in Rust, and hold a reference in Ruby.
#
class Validator < FFI::AutoPointer

# Custom GC flow for our validator, freeing
# the object within Rust
#
def self.release(pointer)
Binding.free(pointer)
end

# Simple validation without actual error messages
#
def valid?(event)
Binding.is_valid(self, RustyJSONSchema.dump(event))
end

def validate(event)
Binding.validate(self, RustyJSONSchema.dump(event)).to_a
end

# FFI container for our library.
# Helper method that returns new instance of pointer
# to Validator struct.
#
module Binding

extend FFI::Library

lib_name =
case ::FFI::Platform::LIBSUFFIX
when "so", "dylib" then "libjson_schema"
when "dll" then "json_schema"
end

ffi_lib File.expand_path("ext/#{lib_name}.#{::FFI::Platform::LIBSUFFIX}", __dir__)

# nodoc
class NodesArray < FFI::ManagedStruct

layout :data, :pointer,
:len, :uint,
:cap, :uint

def to_a
self[:data].get_array_of_string(0, self[:len]).compact
end

def self.release(ptr)
Binding.free_array(ptr)
end

end

attach_function :new, :validator_new, [:string], Validator
attach_function :free, :validator_free, [Validator], :void
attach_function :free_array, :array_free, [NodesArray], :void
attach_function :is_valid, :validator_is_valid, [Validator, :string], :bool
attach_function :validate, :validator_validate, [Validator, :string], NodesArray.by_ref

def build(schema)
RustyJSONSchema::Binding.new(dump(schema))
end

end
Expand Down
Loading

0 comments on commit 560626a

Please sign in to comment.