Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I minify a Ruby source file?

Tags:

ruby

minify

I have a situation where I'd like to be able to minify (not compile) a Ruby script. The goals are to:

  1. Reduce the overall character count of the script;
  2. Perform a degree of obfuscation to make it difficult for others to modify the code.

We can assume that:

  1. Yes, I know what I'm doing, and I really do want to minify and obfuscate the code.
  2. The source Ruby code has a simple syntax and doesn't use any advanced metaprogramming techniques or the like.

Is there any existing library or tool for this? If not, what would be the best way to get started to develop a simple minifier (ideally, also written in Ruby)?

like image 541
Gavin Ballard Avatar asked Aug 26 '26 06:08

Gavin Ballard


1 Answers

I created a simple script that reads a Ruby file, generates a minified and obfuscated version, and then interprets the output to regenerate it again. I created a Ruby file and used command expansion to run a Shell script that may be executed with ruby main.rb:

main.rb

class MinifyAndObfuscateRuby
    def initialize(shell_script="./main.sh")
    @shell_script = shell_script
    run_shell_script
  end

  private

  def run_shell_script
    %x[sh #{@shell_script}]
  end
end

I wrote the Shell script, which takes an input Ruby source file and generates an output file based on the input. Optionally you can just run this directly with with sh main.sh (instead of using the main.rb wrapper that I added for testing with RSpec). Note that most of the main.sh code shared in the repository is shown below, but for brevity I have omitted details of the recover_source function, which tries to re-generate the original Ruby source file in a second output file.

main.sh

#!/bin/bash

# Purpose: Simple script that reads a Ruby file, generates a minified and 
# obfuscated version, and then interprets the output to regenerate it again. 
# Execute either by running `main.rb` Ruby file (uses command expansion to run this Shell script)
# with `ruby main.rb` or with directly with `sh main.sh`. Outputs are automatically
# generated in an ./outputs subdirectory.

# declare and instantiate variables
MINLEN=0 # optionally exclude iteration of blank lines when MINLENGTH is 1
input_file="./source.rb"
reverse=""
output_file="./outputs/output_minified_and_obfuscated.min.rb"
output_file_recovered="./outputs/output_unminified_and_unobfuscated.rb"

# obfuscate: by reversing each line
function obfuscate {
    for (( i=$len-1; i>=0; i-- )); do
        reverse="$reverse${line:$i:1}"
    done
    reverse+="\n"
}

# minify: find instances of the tuple keys in the variable containing the
# reversed input file string and replace with respective tuple values
function minify {
    find_data='eriuqer;*r* fed;*d* dne;*e* edulcni;*i* ssalc;*c* redaer_rtta;*ar*'; 
    for tuple in $find_data; do
        key=$(echo $tuple | cut -d ';' -f 1); 
        value=$(echo $tuple | cut -d ';' -f 2); 
        reverse=${reverse/$key/$value} 
    done
}

function process_source {
    # read lines from input file
    while IFS= read -r line || [ -n "$line" ]; do
        len=${#line}
        if [ "$len" -ge "$MINLEN" ]; then
            obfuscate
            minify
        fi
    done < "$input_file"

  echo "$output_file not found. Creating $output_file and adding minified and obfuscated contents"
  ! [[ -d "outputs" ]] && mkdir outputs
  touch $output_file
  echo $reverse >> $output_file
}

# check if output Ruby file already exists and if so regenerate source, otherwise create it
if [ -f "$output_file" ] && ! [ -f "$output_file_recovered" ]; then
  echo "$output_file already exists."
  recover_source # see source code for details of this method
  exit 0
elif [ -f "$input_file" ] && ! [ -f "$output_file_recovered" ]; then
    process_source
    exit 0
else
    echo "$output_file and $output_file_recovered have both already been generated."
    echo "Deleted temporary files and restarting process..."
    [ -f "$output_file" ] && rm -f "$output_file"
    [ -f "$output_file_recovered" ] && rm -f "$output_file_recovered"
    [ -d "outputs" ] && rmdir outputs
    exit 0
fi

The example source code file I used is shown below:

source.rb

require 'bigdecimal'

class Obfiscate
    include Comparable

  attr_reader :name

  def initialize(name)
    @name = name
  end
end

It applies a degree of obfuscation by reversing each line in the source file and uses regular expressions replace Ruby syntax with my own custom abbreviations (i.e. replace require with *r*, class with *c*, attr_accessor with *ar*, def with *d*, and end with *e*) to reduce the overall character count and optionally remove blank lines as shown in sample output below:

./outputs/output_minified_and_obfuscated.min.rb

'lamicedgib' *r*
etacsifbO *c*
elbarapmoC 
eman: *ar*
)eman(ezilaitini *d* 
eman = eman@ 
*e* 
*e*
like image 197
Luke Schoen Avatar answered Aug 29 '26 03:08

Luke Schoen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!