-
Notifications
You must be signed in to change notification settings - Fork 13
File Loading Facility
The file loading facility allows you to manage Q the installation and loading of versioned Q folders in a simple way. It also provides a basic capacity to prevent reloading the same source file.
There are five functions exposed for the purposes of loading file, one helper function to find an absolute path, and three variables set when a file is loading to allow user extensibility; one provides the name of the package being loaded and the other provides the path of the file being loaded.
The file loading facility has a concept of version strings which allow the user to flexibly specify which versions of a shared library should and should not be loaded. The version strings are based on simple comparison operators that are identical to those available in Q. A version string can have multiple constraints and a package must satisfy all of them before it will be loaded.
The operators supported are: =,<>,<=,>=,<,>
Example:
This version string requires a package with a version number greater than or equal to 1.1:
">=1.1"
This version string requires a package greater than 1.2.4 and less than 1.6:
">1.2.4,<1.6"
This version string requires a package between 1.2 and 1.3, but not between 1.2.2 and 1.2.5 (this example is highly unlikely):
">1.2,<1.3,<1.2.2,>1.2.5"
Both of these version strings require a package numbered exactly 2.4.5:
"2.4.5"
"=2.4.5"
Whitespace between the operator and the version number is not allowed, but all other whitespace is ignored.
By setting this to any symbol, library code will be assumed to be at packageName/newSymbol. For example if .utl.PKGSTRUCTURE is set to `foo, then using .utl.require "bar" will attempt to load code from bar/foo/init.q
The variable .utl.FILELOADING has the absolute path, as a symbol, of the file that is being loaded through the file loading facility. If a file or path is loaded through the standard \l directive, .utl.FILELOADING will not subsequently change. Only files loaded through the file loading facility will be placed into .utl.FILELOADING. This variable is intended to allow meta data to be embeded into elements of a large system where the file that a code object or data object has originated from is not immediately apparent. This variable is unset after the file has succesfully loaded.
The variable .utl.PKGLOADING contains the absolute path, as a symbol, of the package that is being loaded. In the parlance of the file loading facility, a package is any directory found in the .utl.QPATH variable (see the installation instructions to change the paths that end up in this variable) and may have a version number. As an example, the following are all package names: "foo", "foo-2", "foo-2.3.1", "bar-2.2". This variable is intended to allow users to load related files from a package without knowing the version number of the package being loaded or the path where the package has been placed and may be used along with the file loading functions to accomplish this easily.
The variable .utl.LOADED contains a list of all of the file paths that have been loaded.
.utl.require is the basic function that will be used most often. It takes one argument: Either a symbol file path (as hsym produces) to be loaded, a path string representing a package to be loaded, or a generic list containing two elements: a directory of an symbol file path and a path string. .utl.require does not accept a version string, see .utl.requireV if you would like to use a version string. If .utl.require is given a file to load that has already been loaded by one of the other file loading facility functions, it will not be loaded.
Given a symbol file path, .utl.require will simply attempt to load that path. In this way, it can be seen as a substitute for \l
e.g.:
q).utl.require `:/foo/bar/baz.q
q)
Given a path string, .utl.require will look within the list of directories contained in .utl.QPATH and attempt to find a matching package. The order of package preference is as follows: Any package that has the exact name provided to .utl.require will be loaded , if there is no such package with a precise match, the highest numbered package will be chosen. If no package is available that fits either criteria, an error is raised indicating that no suitable package could be found. The actual file loaded when a path is found is always init.q. This is intended to be a standard entry point for any packages and provided that multiple files are in the package, should contain only other file loading facility statements.
e.g.:
Provided the following packages are available: "foo", "foo-1.2", "foo-2" and that they are located within "/my/path"
q).utl.require "foo"
q) / The file "/my/path/foo/init.q" was loaded
q).utl.require "foo-1.2"
q) / The file "/my/path/foo.1.2/init.q" was loaded
q).utl.require "foo-2.2"
'A matching package was not found for 'foo-2.2' with version string ''. Paths searched:
:/my/path
Provided the following packages are available: "foo-1.2", "foo-2" and that they are located within "/my/path"
q).utl.require "foo"
q) / The file "/my/path/foo-2/init.q" was loaded
Given a list of a symbol file path and path string, .utl.require will find the exact path specified and then infer the remaining element of the path to be loaded from the string portion. This capacity is intended to be used in conjunction with .utl.PKGLOADING to allow for relative paths within a package to be loaded easily. It is not generally suggested that this method be used without referring to .utl.PKGLOADING.
e.g.:
Inside of a package inside the init.q file:
.utl.require .utl.PKGLOADING,"file.q"
.utl.require .utl.PKGLOADING,"other/file.q"
.utl.load operates in the same manner as .utl.require with the exception that a file that has already been loaded will be reloaded.
.utl.pkg operates in the same manner as .utl.load but understands the context of the currently loading package.
This allows you to shorten code that was written as the following:
.utl.load .utl.PKGLOADING,"/foo.q"
to
.utl.pkg "foo.q"
.utl.requireV operates in the same manner as .utl.require with the excpetion that it also accepts a version string argument. Also, given that a version string is provided, it will not choose an exact match first, but will prefer any version that is known to fall within the version string. Lacking a match in this manner, it will then fall back to the exact match under the assumption that the default version is intended to be a production ready version. For this reason, it is inadvisable to mix versioned and unversioned packages.
Provided the following packages are available: "foo", "foo-1.2", "foo-2" and that they are located within "/my/path"
q).utl.requireV["foo";"<2"]
q) / The file "/my/path/foo-1.2/init.q" was loaded
q).utl.requireV["foo";">=3"]
q) / The file "/my/path/foo/init.q" was loaded
.utl.load operates in the same manner as .utl.requireV with the exception that a file that has already been loaded will be reloaded.
.utl.baseLoadV is the function underlying .utl.require, .utl.load, .utl.requireV, and .utl.loadV. It is exposed so that users may use the loading facility programatically. The three arguments it takes are a symbol file path/path string/pair, a version string (which may simply be the empty string list "" to emulate .utl.require and .utl.load), and a boolean which indicates whether reloading is allowed or not.
.utl.realPath will take a symbol file path that may be relative or not and will return the absolute version of that path