-
Notifications
You must be signed in to change notification settings - Fork 0
Application
Configuration | Table Of Contents
Using require and load via Realms isn't much different than using it without Realms. For instance the demo app included in the Realms distribution can be tested (after realming it in) from irb just by typing:
require 'fruitapp/tryme'The Realm system sees that 'fruitapp' is a Ruby library, selects the most recent version, then proceeds to require tryme.rb.
The difference between Realms and Ruby's normal behaviour is in how it treats the first part of the path. Realms always uses it as the name of the library, or gem.
Rather then have Realms automatically select the latest version 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 version parameter is optional --when no version constraint is given the most recent available version is selected. The library method also returns returns a Library instance.
As mentioned in the features, Realms provides internal library files access to the library files around them without specifying absolute path names (i.e. relative to the standard $LOAD_PATH). This is useful because it allows one the freedom to reversion directories or rename subdirectories without needing to update every require reference in one's scripts.
Albeit rare, you should keep in mind that a conflict can arise if your library contains a file within it's local scope that has the same name as an external library or a built-in Ruby standard library file. This will only effect a library trying to access both files. When this occurs the local library file will take precedence. There are two way to circumvent this. Firstly, you can specifically specify the library if it is another realmed library.
library('alib').require 'afile.rb'Or you can use the specialized 'ruby:' library to access standard ruby.
require 'ruby:ostruct.rb'Developers often need to isolate a project's dependencies, both to ensure best-fit version resolution and to isolate the project from any malformed libraries that might erroneously clobber lib paths of another project (yes, sadly this can happen). Library makes this easy to handle in either of two ways.
First you can set the run mode to "development".
export RUBY_LIBRARY_MODE="development"This mode will have Library look for a .index file in a Project's root directory
relative to the current working directory. If will use the requirements information
of this file to peardown the current library ledger to just the needed libraries
with the best-fit of versions for all dependencies involved. Note, this will
not automatically download missing dependencies or best-fit versions from
a remote source (e.g. rubygems.org). So you will still have to install potential
dependencies first.
The other option is to generate an isolation file. MORE TO COME...
Library, as the name make clear, objectifies the concept of a Ruby library,
which naturally means the underlying API has a Library class.
The basics of the Library API are fairly simple. Given a location on disc
that houses a Ruby library, e.g. projects/hello, a new Library instance
can be created like any other object, using new.
mylib = Library.new('projects/hello')With a library object in hand, we can require or load files from that library.
mylib.require 'world'Or look at information about the library.
mylib.name #=> 'hello'
mylib.version #=> '1.0.0'Crating a library object vianew gives us a one-off object. But to persist
the library and make it available by name in the global Ledger, we can use
instance constructor instead.
Library.instance('projects/hello')Or, delving down a bit deeper into the belly of system, one could simply feed the path to the master Ledger instance.
$LEDGER << 'projects/hello'Both have the same exact effect. Our library will then be available via
Library's various look-up methods. There are a few of these. One of these is
the Kernel method library.
library('hello')Another is Library.[] class method.
Library['hello']There are many other useful Library methods, see the API Documentation for additional details.
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'] #=> nilLibrary 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 is 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 obvious 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, Realms 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.
# library directory
library('fruitapp').libdir #=> [ "/usr/local/lib/site_ruby/1.8/fruitapp/1.0.0/" ]
# 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 Realms, Gems or some other system.
With Realms, 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 realm.rb."
library('fruitapp').author #=> "Trans"The field names are arbitrary and are dervied from a project's PROFILE file.