Roll (or Rolls) is a library manger. This is similiar to a package manager, but with the key distinction that it circumvents any need for a package. In other words, programs can be installed directly from their scm repositories.
This is especailly useful to Ruby developers. Rolls makes life easier because it effectively nullifies any installation process. If you are working on a project, just 'roll in' the working directory, and any changes will be live. There is no need to go through a re-install process. This can also mitigate the need to handle dependent vendor repositories per project.
Under the hood, Rolls is an object-oriented library ledgering system. In other words, a library, which is essentially a location in a file system, can be instantiated as an object, then used to provide useful meta-information about library, in addition to providing the usual #require and #load to load the files belonging to the library.
A releated question is, what isn't Rolls? Rolls is not a package manager. Unlike RubyGems, Rolls simply manages libraries. It does not care how they got to the system. As long as Rolls knows were to look, and the package conforms to some simple conventions, then Rolls will serve the files up.
Roll is best installed directly to your systems site-ruby location.
To do this you will need Ruby Setup.
With Ruby Setup, installation is straight-forward. Download the package file,
decompress it, 'cd' into the uncompressed directory and run
sudo setup.rb
.
$ wget http://rubyforge.org/frs/download.php/11057/roll-x.y.z.tar.gz $ tar -xvzf roll-x.y.z $ cd roll-x.y.z $ sudo setup.rb
Once Rolls is intalled you can start using it by adding -roll
to your
RUBYOPT
environment variable.
$ export RUBYOPT="-roll"
You may want to add this to the master profile or to your own .bashrc file so it starts automatically in the future.
Lastly, to get binary executable support, you will need add a small script to .bashrc or the equivalent to your particular shell start-up code. Here is an example of a possible setup.
# At the end of ~/.bashrc if [ -f ~/.rubyrc ]; then . ~/.rubyrc fi
# cat ~/.rubyrc export RUBYFORGE_USERNAME="yourname" export RUBYOPT="-roll -rubygems" export PATH="$PATH:$(roll path)"
NOTE: Roll does not yet support executables on Windows. It's being worked on, but library support should work fine.
Rolls has actually gone through a number of re-writes to test various possible implementations and refine it's functionality. At this point it is fairly stable, though some minor parts of the API may yet change. As a developer's tool it is close to rock solid (I have been using for well over a year). The install command is much more recent, however, and still needs testing and refinement.
Rolls provides a command line tool called, obvious enough, roll
.
$ roll --help usage: roll[options] [arguments] commands: in/insert insert current project into ledger out/remove remove current project from ledger ledger list the ledger entries clean clean ledger of invalid entries path output ledger bin PATH install install a project uninstall uninstall a project update update a project versions list project versions help provide help information
You can use help to get more information on each of these commands.
As a developer the first handful of commands will be of most use. For instance, lets say you are working on a project that uses Ruby Facets and you want to have the project close at hand. So you have cloned the git repository.
$ git clone git://rubyforge.org/facets.git facets
All you need to do to bring it online is to cd into project, ensure it has a VERSION file and the roll the library in. Facets has a VERSION file so all you'd need to do is:
$ cd facets $ roll in
This will add the current project's location to your library ledger
(stored in ~/$XDG_CONFIG_HOME/roll/ledger.list
).
The only caveat to this is that you need to make sure the project has a VERSION file in the format of (using Facets as an example):
$ cat VERSION facets 2.5.1 stable (2008-11-30)
If it does not, simply create one. You don't need to check it into the repository.
As a general user, Rolls can be used to install software in such a way as to alleviate the project maintatiner from the chore of creating special install packages.
$ roll install foo
By default, projects are looked for at rubyforge.org. One can specify that they are looked for at github instead with the '-g' option.
If no version is given, the latest tagged version will be installed.
You can install a specific version using the --version
option.
Using a rolled Ruby library isn't much different than using a non-rolled one. For instance the demo app incuded in the Rolls distribution can be tested (after rolling it in) from irb just by typing:
require 'fruitapp:tryme'
The roll system will see that 'fruitapp' is a rolled library and automatically
instantiate it, selecting the most recent version, then proceed to require tryme.rb
.
For backward compatability, it is also possible to do:
require 'fruitapp/tryme'
In this case though, Rolls will first try to load the file using Ruby's normal require mechinsim, failing that
Rolls has to figure out that fruitapp
is a rolled library. While fairly marginal, this does increae
load times. Efficiency increases approx. 2-3 times by explicitly telling Rolls that fruitapp
is a rolled library using the ':'. However, the trade-off is that your library/applcation will require the
the use Rolls to function. Through-out this guide we will usually use the ':' to keep things clear. Just
remember that you can use '/' in it's place.
Rather then have Rolls automatically select the latest verison of a library you can manually activate
a specific version via a version constraint. This is most easily achieved with the provided
#library
Kernel method.
library 'fruitapp', '= 1.0' require 'fruitapp:tryme'
So in this example, specifically version 1.0 of fruitapp will be used.
The #library
method does more than simply select a version.
In fact, the verison parameter is optional --when no version constraint is given
the most recent available version is selected. The #library
method
also returns returns a Library object.
When you use the Kernel method, #library
, you're actually just using a shortcut for
instantiating a new Library object. You can do the same thing using Library.instance
or Library.open
methods.
Library.instance('fruitapp') #=> #<Library fruitapp/1.0.1> Library.open('fruitapp') #=> #<Library fruitapp/1.0.1>
The #instance
and #open
methods require the library name as the
first parameter .
The name is then used as the ledger key to track the library. The difference between
#instance
and #open
is that #open
will raise an error if the library is not found and can also take a block which yields on the
library. In addition, there is a shorter alias for #instance
provided as #[]
.
Like #instance
, it too will not raise an error if the the library is not found,
but will simply return nil
.
Library['fruitapp'] #=> #<Library fruitapp/1.0.1> Library['notthere'] #=> nil
Library is multiton, which means only one instance exists per name.
Calling #library
, Library.instance
, Library.open
or
Library.[]
repeatedly using the same name will return the very same
Library object.
When selecting a version, the constraint is a simple string starting with an
operator, like =
or >=
, followed by the string
representaion of a version number. For instance,
Library.instance('fruitapp', '~> 1.0') #=> #<Library fruitapp/0.9> Library.instance('fruitapp', '== 2.0') #=> #<Library fruitapp/2.0>
Once a version is selected the version can not be changed. A Library::VersionConflict will be raised if one attempts to do so.
NOTE: This restriction has put in place to prevent conflicts which can arise when libraries extend core functionality. Certainly it would be nice if multiple-versions could work harmoniously, but this not even remotely possible until such time as Ruby supports selector namespaces. In the future though we may be able to reduce the restrinction to just the use of #require and #load.
Now, with a library in hand, the most useful method provided is #require.
library('fruitapp').require 'tryme'
As you can see this is pure OOP. You could store the reference to the library for later access, even pass it around as an argument.
fruitlib = Library.open('fruitapp', '=1.0') fruitlib.require 'tryme'
NOTE: This section is not wholey correct.
To facilitate access to file locations pertaining to
a library, Rolls provides some convenient methods.
Normally this information is accessed by using rbconfig.rb and
building a path based on information in the Config::CONFIG hash.
For instance, Config:CONFIG['datadir']
on a Debian system points to /usr/share. With Roll you can look up
the data dir specific to the current library via the #datadir
method, and likewise for the other directories. Here's a example rundown
with resolutions for a Debian system.
# configuration dir library('fruitapp').confdir #=> "/etc/fruitapp/" # versioned data dir library('fruitapp').datadir #=> "/usr/share/fruitapp/1.0.0/" # ensure non-versioned data dir library('fruitapp').datadir(true) #=> "/usr/share/fruitapp/"
Another way to access these locations is via Ruby's own Config module. Eg. Config.datadir('fruitapp'). This provides a wholly general interface to this information, irregardless of the system providing it, whether it be Rolls, Gems or some other system.
With Rolls, a project's bin/
directory is not versioned, unlike
the lib/
directory. It doesn need to be because a simple
convention makes it possible to version executable files: In so far as an executable
is to be versioned (and it's generally a good idea to do so) one should wrap the logic
in a file under the versioned lib/
directry, then simply requiring or
load the lib file into the executable file. It's a simple enough practice and doing
it in this manner means that no specialized action is required of any packaging
or distribution system. Here is a good example of such a file.
#!/usr/bin/env ruby version = "> 0" if ARGV.size > 0 && ARGV[0][0]==95 && ARGV[0][-1]==95 if Library::Version.correct?(ARGV[0][1..-2]) version = ARGV[0][1..-2] ARGV.shift end end library 'camping', version load 'camping'
Additional meta-information may also be accessed via the
Library
interface. This is optional information
that may or may not be provided by the project developers.
To tell if any metadata has been provided, query the
library with the #metadata?
call. If true, then
other information will be availabe. For instance:
library('fruitapp').title #=> "Fruit Basket Application" library('fruitapp').summary #=> "Example application to demonstrate roll.rb." library('fruitapp').author #=> "Trans"
The field names are arbitrary and are dervied from individual files of
a project's meta/
directory, or from a META.yml
file.
Roll supports library versioning simply by working with the typical repository layout, whether using Subversion, Darcs, Git or some other versioning system, Rolls doesn't much care, as long as you follow some basic conventions and provide the necessary metadata. The metadata file means there is no need for special installation repositories, procedures or supporting programs.
Your directory layout should generally follow the conventions set
by Minero Aoki's setup.rb
. Roll simply adds a couple
additional details.
Here's an example of a typical Subversion project layout.
fruitapp/ tags/ 1.0.0/ VERSION bin/ ... lib/ fruitapp/ index.rb ... trunk/ VERSION bin/ ... lib/ fruitapp/ index.rb ...
Notice there is 1.0.0 tagged version and a new verison currently being worked on. The above layout is your typcial subversoin repository. Another viable layout is:
fruitapp/ 1.0.0/ VERSION bin/ ... lib/ fruitapp/ index.rb ... current/ VERSION bin/ ... lib/ fruitapp/ index.rb ...
This demonstrates the two varieties of Subversion layout that Rolls can comprehend. Other SCM's that use directories for tagging with have the same layout. Git, and any SCM that tracks tags internally, on the other hand, will of course not have these additonal layer of subdirectories.
At this point you may be wondering how renaming the lib directory with a changing version is even possible --how will the internal require statements find their files? It's actually quite simple. Rolls keeps a map of available libraries and their versions. When a library is first referenced it is instatiated with a particluar version. From then on requests against that libary are routed to that library object where the correct path is used.
It important to keep the VERSION file up to date. The version number in the
VERSION file must correspond to the tag name for
roll install
to
work correctly. This is becuase the install command uses the tag name to
locate versions, but Roll's require
method uses the VERSION file.
One of the most important settings in the .roll file is the the load path, By default the load scope of a library is it's lib/ directory. But you can vary the load path if need be.
Let us consider an example. Assume the following lib/
layout:
fruitapp/ fruit/ apple.rb basket/ wicker.rb
By providing the library with a scope detailing which sub-directories are to be accessible via the directory name, the internal directories are automatically exposed to one another.
loadpath: - lib - lib/fruit - lib/basket
So given the above, when requiring against the library, the system will search
all three internal paths instead of just the main lib/ path. The file
apple.rb
can contain require 'fruitapp/wicker.rb'
and it will find the file in the basket/
directory.
Because Rolls in package agnostic, it largely removes the burden of distribution.
When you are ready to roll, we've created this spiffy graphic you can use to let other know you offer a rolls compatible distribution.