Tar2RubyScript

A Tool for Distributing Ruby Applications

Fri May 25 15:06:29 UTC 2007
Erik Veenstra <tar2rubyscript@erikveen.dds.nl>


PDF version (A4)

1. Introduction

2. Internals
          2.1. Creating the RBA
                    a) General
                    b) tar2rubyscript.bat and tar2rubyscript.sh
          2.2. Executing the RBA

3. Usage
          3.1. Tar2RubyScript
                    a) General Invocation
                    b) The TAR Archive Variant
                    c) The Directory Variant
          3.2. init.rb
          3.3. RBA (An Application)
          3.4. RBA (A Library)

4. Examples
          4.1. Usage of Tar2RubyScript
                    a) The TAR Archive Variant
                    b) The Directory Variant
                    c) A Library (Without init.rb)
                    d) A Library (With init.rb)
          4.2. tar2rubyscript.sh and tar2rubyscript.bat
          4.3. oldlocation and newlocation
          4.4. Combination of Tar2RubyScript and RubyScript2Exe

5. License
          5.1. License of Tar2RubyScript
          5.2. License of your Application

6. Download

7. Known Issues

8. Credits


1. Introduction

Tar2RubyScript transforms a directory tree, containing your application, into one single Ruby script, along with some code to handle this archive. This script can be distributed to our friends. When they've installed Ruby, they just have to double click on it and your application is up and running!

So, it's a way of executing your application, not of installing it. You might think of it as the Ruby version of Java's JAR... Let's call it an RBA (Ruby Archive).

"It's Ruby's JAR..."

Like packing related application files into one RBA application, you could as well pack related library files into one RBA library. Now you don't need to install the compound library in the traditional way before using it. Just require the RBA.

Because the RBA is pure Ruby and no other programs or libraries are needed, it's easy to distribute it to friends. They don't have to install anything but Ruby itself.

Unlike the JAR-people, we don't need a new extension for RBA's. A JAR isn't a Java Class, it contains a Java class; an RBA both is and contains a Ruby script. It's also easier to change the format of an RBA in the future, because the algorithm to handle the RBA comes with it at a cost in bytes of less then 10K. Another difference between the two is the entry point: JAR does something with a manifest; RBA just loads init.rb . And, well, they compress, we don't.

If you like Tar2RubyScript, you might want to read Distributing Ruby Applications. It's about how I build, pack and distribute my Ruby applications. Theory and practice.

[images/tar2rubyscript3.gif]

The combination of Tar2RubyScript and RubyScript2Exe is of special interest: A complete Ruby application can be distributed as one executable:

This combination isn't necessary. Each application can be used without the other.


2. Internals

2.1. Creating the RBA

a) General

Tar2RubyScript generates a base64 encoded string from a TAR archive or directory, which contains a Ruby application, and attaches that string to a Ruby extraction script. The result is just one file, a pure Ruby script, containing multiple files, stored as comments.

Tar2RubyScript contains two scripts: init.rb, which is the script that is run when building the RBA, and tarrubyscript.rb, which is the script that is run when unpacking/running the RBA. In fact, tarrubyscript.rb is the RBA, without the archive. These scripts run on two distinct moments in time.

The external program tar is still needed for generating the TAR archive internally. (Once again: Not for extracting/using it.) I assume all Linux and Unix systems have one. I embedded tar.exe, because only a few Windows systems have it.

[images/tar2rubyscript2.dia.gif]

b) tar2rubyscript.bat and tar2rubyscript.sh

When creating the RBA, the archive/directory is extracted/copied to a temporary directory. If the archive/directory contains tar2rubyscript.bat (Windows) or tar2rubyscript.sh (Linux, Cygwin (Posix in general?)), that script will be executed in the temporary directory. Finally, the temporary directory is packed to a new TAR archive which becomes part of the RBA. Thus, the original TAR archive isn't necessarily the same as the one which is part of the RBA. This can be avoided with --tar2rubyscript-preserve. When using --tar2rubyscript-preserve in combination with a TAR archive, no external TAR commands are run. No unpacking, no packing. This might be necessary on platforms with deviant behavior (e.g. TAR on Solaris). Using --tar2rubyscript-preserve in combination with a directory is pointless.

2.2. Executing the RBA

When the RBA is executed, it splits up into two parts (the script and the string), decodes the base64 encoded string to a temporary TAR archive, unpacks the archive to a temporary directory, jumps to that directory, eventually jumps to an existing subdirectory, and finally loads init.rb. Et voila, your application is up and running. When the application has ended, the RBA deletes the temporary directory recursively.

When unpacking the archive, everything but paths, filenames and contents is disregarded. No permissions, no owners, no groups. However, hard links and soft links are supported (not on Windows). Note that both hard and soft links make your RBA platform dependent.

(Use these links with care. It's not thoroughly tested...)

Because of the cleaning of temporary files (done by the OS), all files in the temporary directory are touched every hour. This prevents the deletion of files of long running jobs. This is done by a background thread that keeps visiting all temporary files in the temporary directory and its subdirectories.

ENV["PATH"] is extended with both oldlocation and newlocation, just before loading init.rb. The OS might need this to find shared objects (e.g. DLL's).


3. Usage

[images/tar2rubyscript1.dia.gif]

3.1. Tar2RubyScript

a) General Invocation

If you use the original tar2rubyscript.rb:

c:\home\erik> ruby tar2rubyscript.rb ...

If you installed the gem, it's:

c:\home\erik> tar2rubyscript ...

b) The TAR Archive Variant

Create a TAR archive, with a root that contains at least the file init.rb or a root that contains just one directory which contains at least the file init.rb. If the root of the archive contains only one directory, a cd to that directory will be done when executing the RBA.

Create the RBA:

c:\home\erik> tar cf application.tar application[/]
c:\home\erik> ruby tar2rubyscript.rb application.tar \
                                     [application.rb [license.txt]]

If the TAR archive also contains tar2rubyscript.bat (Windows) or tar2rubyscript.sh (Linux, Cygwin (Posix in general?)), that script will be executed "in the archive". Changes in the archive aren't permanent.

If application.rb is not provided or if it equals to "-", it will be derived from application.tar .

If a license is provided, it will be put at the beginning of the RBA.

c) The Directory Variant

Create a directory, which contains at least the file init.rb.

Create the RBA:

c:\home\erik> ruby tar2rubyscript.rb application[/] \
                                     [application.rb [license.txt]]

If the TAR archive also contains tar2rubyscript.bat (Windows) or tar2rubyscript.sh (Linux, Cygwin (Posix in general?)), that script will be executed in a copy of the directory, just before the internal TAR archive is being created.

(Changes in the directory aren't permanent anymore!)

If application.rb is not provided or if it equals to "-", it will be derived from application/ .

If a license is provided, it will be put at the beginning of the RBA.

3.2. init.rb

Because init.rb executes in the temporary directory, all references to files relative to the original directory have to be escaped [1] by oldlocation. For example, instead of...

file = ARGV.shift

...you have to use:

file = oldlocation(ARGV.shift)

If you want to execute a block of code in the original directory, you can use oldlocation as well:

oldlocation do
  Dir.new('.').each do |f|
    # ...
  end
end

Put this at the beginning of the script to let it run in the original directory (see the examples):

Dir.chdir oldlocation

Now you have to escape the files in the temporary directory:

file = newlocation("...")
or
newlocation{...}

I created oldandnewlocation.rb that can be "require"d in init.rb, so the application can run without being wrapped by Tar2RubyScript. This script doesn't do anything but defining oldlocation and newlocation.

TAR2RUBYSCRIPT is set to true before loading init.rb, so it's possible to detect whether the application is run as an RBA or not:

puts "Running as an RBA." if defined?(TAR2RUBYSCRIPT)

3.3. RBA (An Application)

c:\home\erik> ruby application.rb [parameters]

ParameterDescription
--tar2rubyscript-listJust list the contents of the RBA.
--tar2rubyscript-justextractJust extract the RBA, but not execute it.
--tar2rubyscript-totarJust convert the RBA (back) into a TAR file.

If one of these parameters is used, Tar2RubyScript does just that. It doesn't execute init.rb.

If none of these parameters is used, Tar2RubyScript executes init.rb with the given parameters. To be forward compatible, all parameters starting with --tar2rubyscript- will be deleted before the execution of init.rb .

The exit code of the RBA is the same as the exit code of your application.

3.4. RBA (A Library)

Packing a library into an RBA is basically the same as packing an application. It's just that you don't need the init.rb.

Using it is different from an application. Suppose you packed a directory with the file lib_a.rb and lib_b.rb into the RBA testlib.rb. An application, which is going to use both libraries, needs to require "testlib", before require-ing the two libraries:

require "testlib"
require "lib_a"
require "lib_b"

I usually put the last two lines in the init.rb of the RBA. That will reduce the require-ing in the application to just one line:

require "testlib"

It's possible to use multiple libraries in one library RBA and multiple library RBA's in your application, which can be an RBA as well. newlocation escapes the files of the last RBA, either library or application. It's not possible (yet) to access the files in other RBA's.


4. Examples

4.1. Usage of Tar2RubyScript

a) The TAR Archive Variant

$ rm -rf demo demo.tar demo.rb
$
$ mkdir demo
$
$ echo 'puts File.new("demo1.txt").readlines' >  demo/init.rb
$ echo 'puts File.new("demo2.txt").readlines' >> demo/init.rb
$ echo 'Hello                               ' >  demo/demo1.txt
$ echo 'World!                              ' >  demo/demo2.txt
$
$ tar cf demo.tar demo/
$
$ ruby tar2rubyscript.rb demo.tar          # Results in demo.rb
$
$ ruby demo.rb
Hello
World!

b) The Directory Variant

$ rm -rf demo demo.tar demo.rb
$
$ mkdir demo
$
$ echo 'puts File.new("demo1.txt").readlines' >  demo/init.rb
$ echo 'puts File.new("demo2.txt").readlines' >> demo/init.rb
$ echo 'Hello                               ' >  demo/demo1.txt
$ echo 'World!                              ' >  demo/demo2.txt
$
$ ruby tar2rubyscript.rb demo/          # Results in demo.rb
$
$ ruby demo.rb
Hello
World!

c) A Library (Without init.rb)

$ rm -rf demo demo.tar demo.rb
$
$ mkdir demo
$
$ echo 'puts "Hello"                        ' >  demo/lib_a.rb
$ echo 'puts "World!"                       ' >  demo/lib_b.rb
$
$ ruby tar2rubyscript.rb demo/          # Results in demo.rb
$
$ ruby -e 'require "demo" ; require "lib_a" ; require "lib_b"'
Hello
World!

d) A Library (With init.rb)

$ rm -rf demo demo.tar demo.rb
$
$ mkdir demo
$
$ echo 'require "lib_a.rb"                  ' >  demo/init.rb
$ echo 'require "lib_b.rb"                  ' >> demo/init.rb
$ echo 'puts "Hello"                        ' >  demo/lib_a.rb
$ echo 'puts "World!"                       ' >  demo/lib_b.rb
$
$ ruby tar2rubyscript.rb demo/          # Results in demo.rb
$
$ ruby -e 'require "demo"'
Hello
World!

4.2. tar2rubyscript.sh and tar2rubyscript.bat

$ rm -rf demo demo.tar demo.rb
$
$ mkdir demo
$
$ echo 'puts File.new("demo1.txt").readlines' >  demo/init.rb
$ echo 'puts File.new("demo2.txt").readlines' >> demo/init.rb
$ echo 'Hello                               ' >  demo/demo1.txt
$ echo 'echo World! > demo2.txt             ' >  demo/tar2rubyscript.sh
$
$ ruby tar2rubyscript.rb demo/          # Results in demo.rb
". ./tar2rubyscript.sh"
$
$ ruby demo.rb
Hello
World!

4.3. oldlocation and newlocation

$ rm -rf demo demo.tar demo.rb
$
$ mkdir demo
$
$ echo 'p [1, oldlocation("file.ext")]      ' >  demo/init.rb
$ echo 'p [2, newlocation("file.ext")]      ' >> demo/init.rb
$ echo '                                    ' >> demo/init.rb
$ echo '      p [3, Dir.pwd]                ' >> demo/init.rb
$ echo 'oldlocation do                      ' >> demo/init.rb
$ echo '      p [4, Dir.pwd]                ' >> demo/init.rb
$ echo '  newlocation do                    ' >> demo/init.rb
$ echo '      p [5, Dir.pwd]                ' >> demo/init.rb
$ echo '  end                               ' >> demo/init.rb
$ echo '      p [6, Dir.pwd]                ' >> demo/init.rb
$ echo 'end                                 ' >> demo/init.rb
$ echo '      p [7, Dir.pwd]                ' >> demo/init.rb
$
$ ruby tar2rubyscript.rb demo/          # Results in demo.rb
$
$ ruby demo.rb
[1, "/home/erik/file.ext"]
[2, "/tmp/tar2rubyscript.d.597/demo/file.ext"]
[3, "/tmp/tar2rubyscript.d.597/demo"]
[4, "/home/erik"]
[5, "/tmp/tar2rubyscript.d.597/demo"]
[6, "/home/erik"]
[7, "/tmp/tar2rubyscript.d.597/demo"]

4.4. Combination of Tar2RubyScript and RubyScript2Exe

Create a directory application/ which contains at least init.rb .

$ ruby tar2rubyscript.rb application/
$ ruby rubyscript2exe.rb application.rb

5. License

5.1. License of Tar2RubyScript

Tar2RubyScript, Copyright (C) 2003 Erik Veenstra <tar2rubyscript@erikveen.dds.nl>

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License (GPL), version 2, as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. See the GNU General Public License (GPL) for more details.

You should have received a copy of the GNU General Public License (GPL) along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

The full text of the license can be found here.

5.2. License of your Application

Whatever...


6. Download

Current version is 0.4.8 (08.03.2006). It's a stable release.

Tested on:

You only need tar2rubyscript.rb . It's the current version, built by Tar2RubyScript itself. You can download tar2rubyscript.tar.gz if you want to play with the internals of Tar2RubyScript. Tar2RubyScript is available as tar2rubyscript.gem as well.

Send me reports of all bugs and glitches you find. Propositions for enhancements are welcome, too. This helps us to make our software better.

A change log and older versions can be found here. A generated log file can be found here.

Tar2RubyScript is available on SourceForge.net and on RubyForge .


7. Known Issues


8. Credits


Notes

[1]I once heard: "I don't want to change my scripts to be compatible with your tool!". And I agree. I've had some thoughts in that direction before I introduced oldlocation and newlocation. But that ideology is less important than a solid solution for deciding which files are to be loaded from the temporary directory (help files, images, licenses, default configurations, etc.) and which files are to be loaded from the user directory (databases, user configurations, etc.). This decision can't be made by Tar2RubyScript. Somehow, the programmer has to make this decision. And it's not only about files, but also about blocks of code. (e.g. Dir.new('.')) Sometimes, you want to open the user directory; sometimes, you want to open the temporary directory.) It's up to the programmer. That's where oldlocation and newlocation popped up.

 RSS 

SourceForge.net Logo

RubyForge

Nedstat Basic - Free web site statistics

aior all allinone allinoneruby applications archive bin browser code codesnippet codesnippets compile compiler computer computerlanguage dialog dialogs distribute distributing distributingrubyapplications distribution eee eee-file eeefile erik erikveen erikveenstra exe executable exerb file graphical graphicaluserinterface gui gz html http httpserver iloveruby interface jar jit just justintime lang language one pack package packaging packing packingrubyapplications programming programminglanguage rar rb rb2bin rb2exe rba rbarchive rbtobin rbtoexe rbw ruby ruby2bin ruby2exe rubyapplications rubyarchive rubycompiler rubyscript rubyscript2 rubyscript2exe rubyscripts rubyscripttoexe rubytobin rubytoexe rubyweb rubywebdialog rubywebdialogs script scripts server snippet snippets t2rb t2rs tar tar2rb tar2rbscript tar2rs tar2rscript time ui user userinterface veenstra web webbrowser webdialog webdialogs window windowinbrowser windows wrap wrapper wxruby zip