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!
") +Hello, world!
" +``` + +"]
+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`);
}