diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml new file mode 100644 index 0000000..a444fee --- /dev/null +++ b/.github/workflows/ruby.yml @@ -0,0 +1,85 @@ +name: Build, package, and upload Ruby gem + +on: + create: + tags: + - 'v*' + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + include: + - os: ubuntu-latest + NAME: linux + FILE: 'libhyperbuild_ruby_lib.so' + - os: windows-latest + NAME: windows + FILE: 'hyperbuild_ruby_lib.dll' + - os: macos-latest + NAME: macos + FILE: 'libhyperbuild_ruby_lib.dylib' + steps: + - uses: actions/checkout@v1 + - name: Get version + id: version + shell: bash + run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v} + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + default: true + - name: Build Ruby native library + run: cargo build --release + working-directory: ./ruby + - name: Upload built library + uses: actions/upload-artifact@v1 + with: + name: ${{ matrix.NAME }} + path: ./ruby/target/release/${{ matrix.FILE }} + package: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v1 + - name: Get version + id: version + shell: bash + run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v} + - name: Get file name + id: file + shell: bash + run: echo ::set-output name=FILE::${{ steps.version.outputs.VERSION }}.jar + - name: Set up JDK + uses: actions/setup-ruby@v1 + with: + ruby-version: '2.5' + - name: Download Windows built library + uses: actions/download-artifact@v1 + with: + name: windows + path: ruby/lib + - name: Download Linux built library + uses: actions/download-artifact@v1 + with: + name: linux + path: ruby/lib + - name: Download macOS built library + uses: actions/download-artifact@v1 + with: + name: macos + path: ruby/lib + - name: Package gem + working-directory: ./ruby + run: gem build hyperbuild.gemspec + - uses: chrislennon/action-aws-cli@v1.1 + - name: Upload to S3 + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: us-west-2 + run: aws s3 cp ./ruby/hyperbuild-${{ steps.version.outputs.VERSION }}.gem s3://${{ secrets.AWS_S3_BUCKET }}/hyperbuild/bin/${{ steps.version.outputs.VERSION }}.gem diff --git a/Cargo.toml b/Cargo.toml index 717f93e..d71f2a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hyperbuild" -description = "One-pass in-place HTML minifier written in Rust with context-aware whitespace handling" +description = "Fast one-pass in-place HTML minifier written in Rust with context-aware whitespace handling" license = "MIT" homepage = "https://github.com/wilsonzlin/hyperbuild" readme = "README.md" diff --git a/README.md b/README.md index d635ad4..00e6852 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A fast one-pass in-place HTML minifier written in Rust with context-aware whites Available as: - CLI for Windows, macOS, and Linux. - Rust library. -- Native library for Node.js, Python, and Java. +- Native library for Node.js, Python, Java, and Ruby. ## Features @@ -140,6 +140,25 @@ minified = hyperbuild.minify("

Hello, world!

") +
+Ruby + +hyperbuild is available as a [native module](https://github.com/danielpclark/rutie), and supports Ruby versions 2.5 and higher. + +##### Get + +Download and install the [gem](https://wilsonl.in/hyperbuild/bin/0.0.20.gem). + +##### Use + +```ruby +require 'hyperbuild' + +print hyperbuild.minify "

Hello, world!

" +``` + +
+ ## Minification ### Whitespace diff --git a/java/pom.xml b/java/pom.xml index c35eaef..58d524d 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -9,7 +9,7 @@ 0.0.20 hyperbuild - One-pass in-place HTML minifier written in Rust with context-aware whitespace handling + Fast one-pass in-place HTML minifier written in Rust with context-aware whitespace handling Wilson Lin diff --git a/nodejs/package.json b/nodejs/package.json index f52e7ed..d98a57f 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -1,7 +1,7 @@ { "name": "hyperbuild", "version": "0.0.20", - "description": "One-pass in-place HTML minifier written in Rust with context-aware whitespace handling", + "description": "Fast one-pass in-place HTML minifier written in Rust with context-aware whitespace handling", "main": "lib/index.js", "repository": { "type": "git", diff --git a/ruby/.gitignore b/ruby/.gitignore new file mode 100644 index 0000000..8b459a8 --- /dev/null +++ b/ruby/.gitignore @@ -0,0 +1,3 @@ +/Cargo.lock +/Gemfile.lock +/target diff --git a/ruby/Cargo.toml b/ruby/Cargo.toml new file mode 100644 index 0000000..754b2e5 --- /dev/null +++ b/ruby/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "hyperbuild-ruby" +publish = false +version = "0.0.20" +authors = ["Wilson Lin "] +edition = "2018" + +[lib] +name = "hyperbuild_ruby_lib" +crate-type = ["cdylib"] + +[dependencies] +hyperbuild = "0.0.20" +rutie = "0.7.0" diff --git a/ruby/Gemfile b/ruby/Gemfile new file mode 100644 index 0000000..b4e2a20 --- /dev/null +++ b/ruby/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gemspec diff --git a/ruby/hyperbuild.gemspec b/ruby/hyperbuild.gemspec new file mode 100644 index 0000000..6a88f79 --- /dev/null +++ b/ruby/hyperbuild.gemspec @@ -0,0 +1,16 @@ +require 'rake' + +Gem::Specification.new do |spec| + spec.name = "hyperbuild" + spec.version = "0.0.20" + spec.authors = ["Wilson Lin"] + spec.email = ["code@wilsonl.in"] + spec.license = "MIT" + spec.files = FileList["lib/*"].to_a + spec.summary = "Fast one-pass in-place HTML minifier written in Rust with context-aware whitespace handling" + spec.homepage = "https://github.com/wilsonzlin/hyperbuild" + + spec.require_paths = ["lib"] + + spec.add_dependency "fiddle", "~> 1.0" +end diff --git a/ruby/lib/hyperbuild.rb b/ruby/lib/hyperbuild.rb new file mode 100644 index 0000000..c843872 --- /dev/null +++ b/ruby/lib/hyperbuild.rb @@ -0,0 +1,18 @@ +require 'fiddle' + +class HyperbuildLoader + def self.operating_system + case RbConfig::CONFIG['host_os'].downcase + when /linux|bsd|solaris/ then 'linux' + when /darwin/ then 'macos' + when /mingw|mswin/ then 'windows' + else 'unknown' + end + end + + def self.lib_path + File.join(__dir__, operating_system) + end +end + +Fiddle::Function.new(Fiddle.dlopen(HyperbuildLoader.lib_path)['Init_hyperbuild'], [], Fiddle::TYPE_VOIDP).call diff --git a/ruby/src/lib.rs b/ruby/src/lib.rs new file mode 100644 index 0000000..df5f5c3 --- /dev/null +++ b/ruby/src/lib.rs @@ -0,0 +1,31 @@ +use hyperbuild::hyperbuild as hyperbuild_native; +use rutie::{Class, class, methods, Object, RString, VM}; +use std::str::from_utf8_unchecked; + +class!(Hyperbuild); + +methods! { + Hyperbuild, + _itself, + + fn minify(source: RString) -> RString { + let mut code = source + .map_err(|e| VM::raise_ex(e) ) + .unwrap() + .to_string() + .into_bytes(); + + hyperbuild_native(&mut code) + .map_err(|(err, pos)| VM::raise(Class::from_existing("SyntaxError"), format!("{} [Character {}]", err.message(), pos).as_str())) + .map(|out_len| RString::new_utf8(unsafe { from_utf8_unchecked(&code[0..out_len]) })) + .unwrap() + } +} + +#[allow(non_snake_case)] +#[no_mangle] +pub extern "C" fn Init_hyperbuild() { + Class::new("Hyperbuild", None).define(|itself| { + itself.def_self("minify", minify); + }); +} diff --git a/version b/version index a7ec279..19d18dc 100755 --- a/version +++ b/version @@ -48,11 +48,11 @@ if (cmd('git', 'status', '--porcelain', {throwOnStderr: true, captureStdio: true throw new Error(`Working directory not clean`); } -for (const f of ["Cargo.toml", "nodejs/native/Cargo.toml", "java/Cargo.toml", "python/Cargo.toml"]) { +for (const f of ["Cargo.toml", "nodejs/native/Cargo.toml", "java/Cargo.toml", "python/Cargo.toml", "ruby/Cargo.toml"]) { replaceInFile(f, /^version = "\d+\.\d+\.\d+"\s*$/m, `version = "${NEW_VERSION}"`); } -for (const f of ["README.md", "nodejs/native/Cargo.toml", "java/Cargo.toml", "python/Cargo.toml"]) { +for (const f of ["README.md", "nodejs/native/Cargo.toml", "java/Cargo.toml", "python/Cargo.toml", "ruby/Cargo.toml"]) { replaceInFile(f, /^hyperbuild = "\d+\.\d+\.\d+"\s*$/m, `hyperbuild = "${NEW_VERSION}"`); } @@ -60,6 +60,10 @@ for (const f of ["nodejs/package.json"]) { replaceInFile(f, /^(\s*)"version": "\d+\.\d+\.\d+",\s*$/m, `$1"version": "${NEW_VERSION}",`); } +for (const f of ["ruby/hyperbuild.gemspec"]) { + replaceInFile(f, /^(\s*spec\.version\s*=\s*)"\d+\.\d+\.\d+"\s*$/m, `$1"${NEW_VERSION}",`); +} + for (const f of ["java/pom.xml"]) { replaceInFile(f, /(hyperbuild<\/artifactId>\s*)\d+\.\d+\.\d+(<\/version>)/, `$1${NEW_VERSION}$2`); }