IMPORTANT NOTICE THIS IS STILL A WORK IN PROGRESS. IT IS NOT COMPLETE AND DOES NOT NECESSARILY MATCH WITH THE CURRENT STATE OF DEVELOPMENT!!!

Table of Contents

  1. Introduction
    1. What is Roll(s)?
    2. How to Install
    3. Development Status
  2. The Roll Command
    1. Roll Command Help
    2. Roll In and Out
    3. Roll Installs
  3. Using Rolled Libraries
    1. Versioned Layout
    2. Metadata File
    3. Load Scope
    4. Live Development
    5. The Library Class
    6. Related Locations
    7. Library Metadata
  4. It's How We Roll
    1. Benefits to Developers
    2. Are You Ready to Roll?

Introduction

What is Roll(s)?

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.

How to Install Rolls

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.

Development Status

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.

The Roll Command

The Roll Command Help

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.

Roll In and Out

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.

Roll Installs

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 Rolled Libraries

Require and Load

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.

The Library Class

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'

Related Locations

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'

Library Metadata

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.

Versioned Layout

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.

Load Path

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.

It's How We Roll

Benefits to Developers

Because Rolls in package agnostic, it largely removes the burden of distribution.

Are You Ready to Roll?

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.

    


Copyright (c) 2005,2008 TigerOps & Thomas Sawyer, all rights reserved.