module ConfigObject:This is a framework for the object-oriented interface to RASCL configurations. See the tutorial below for usage instructions.sig..end
class virtual config_base :Rascl.Dict.t -> Rascl.Dict.t -> string list option ->object..end
class virtual root_config :string list -> string -> Rascl.Dict.t -> Rascl.Dict.t ->object..end
class virtual sub_config :Rascl.Dict.t -> Rascl.Dict.t -> string list ->object..end
In order to use the OO interface, you need to create an object
specific to your application. This can be done by using pa_rascl.cmo,
a CamlP4-based code generator. pa_rascl.cmo parses a configuration
template that you create and outputs a module containing a single
function which creates a configuration object.
The configuration template syntax is as follows:
template : config-name space* '=' space* dictionary
config-name : ocaml-lowercase-ident
dictionary : '{' (property? | property (separator property)* ) '}'
property : name space* ':' space* type-and-default
| name space* '=' space* dictionary
name : ocaml-method-name
type-and-default : 'string' (space* '=' space* string-expr)?
| 'int' (space* '=' space* int-expr)?
| 'float' (space* '=' space* float-expr)?
| 'bool' (space* '=' space* bool-expr)?
| 'string list' (space* '=' space* string-list-expr)?
| 'int list' (space* '=' space* int-list-expr)?
| 'float list' (space* '=' space* float-list-expr)?
| 'bool list' (space* '=' space* bool-list-expr)?
The prefix ocaml- indicates that the item conforms to OCaml
syntax, and is fully documented in the OCaml manual (with the same
name, minus 'ocaml-'.
Items with the suffix -expr are documented in the RASCL language
specification. In general, the syntax for these types follows that
of OCaml, with the following exceptions:
Property names must be unique within any given dictionary, but may be repeated in sub-dictionaries.
For a sample template, see food.ml. You may also find it useful to
compare this file with the supplied config.example, which is the
RASCL configuration generated from it.
pa_rascl.cmo is a CamlP4 preprocessor. It depends on pa_extend.cmo
and q_MLast.cmo. It can be invoked as follows:
camlp4o -I <rascl_path> pa_extend.cmo q_MLast.cmo pa_rascl.cmo
So, the command to byte-compile a template called 'prefs.ml', using
ocamlfind, might look like this:
ocamlfind ocamlc -package rascl -pp \
'camlp4o -I `ocamlfind query rascl` pa_extend.cmo q_MLast.cmo \
pa_rascl.cmo' -c prefs.ml
The above command will produce two output files: the configuration
module, in this case called 'prefs.cmo', and a sample RASCL file
called 'config.example', which may be used as a default runtime
configuration file.
The module generated by pa_rascl.cmo contains a single public
function that generates a configuration object. Its signature is
as follows:
<function_name> : string list -> <object>
The function name is taken from the config-name specified in
the template. I.e., if the template contains:
prefs = {
....
}
then the function name will be prefs.
The argument to this function is a list of RASCL configuration files. These files will be loaded in the order that they appear in the list, and the values they contain will populate the configuration object. Later values replace earlier ones. Thus, typically the file list will contain a system-wide configuration file, then a user-specific file. If defaults have been defined for all properties, then the file list may be empty.
RASCL does not handle any errors encountered in trying to read configuration files, so your application code must ensure that all files exist and are readable.
The function returns an immediate object (i.e. it is not an instance
of any class) which inherits from ConfigObject.root_config. Thus it
it provides #load and #dump as described in the root_config
documentation. Its other public methods correspond to the property
names in your configuration template.
For every property defined in your configuration template, the
configuration object will have a 0-argument 'getter' method named
<prop_name>, and a 1-argument 'setter' named set_<prop_name>.
The type returned by the 'getter', and of the argument to the
'setter', will be whatever you specified in the template. Thus,
if your template consists of:
prefs = {
font_size : int = 12
color : string = "black"
}
then you can access the configuration object in the following manner:
prefs_obj#font_size ==> 12
prefs_obj#color ==> "black"
prefs_obj#set_font_size 10 ==> ()
prefs_obj#set_color "red" ==> ()
Nested dictionaries in the template (and in configuration files) correspond to nested sub-objects in the configuration object. Thus, given the following template:
prefs = {
font = {
family : string = "bitstream vera sans"
size : int = 10
weight : string = "bold"
slant : string = "roman"
}
}
the object will have the following interface:
<
load : unit -> unit
dump : unit
font : <
dump : unit
family : string
set_family : string -> unit
size : int
set_size : int -> unit
weight : string
set_weight : string -> unit
slant : string
set_slant : string -> unit
>
>
Note that there is no #set_font method. Nested objects cannot
be added or removed.