XQuery/Dynamic Module Loading

Motivation

edit

You want to conditionally import a module. For example the module might provide a list of all the functions to style a web page such as header/footer and breadcrumbs.

Method

edit

Module import

edit

We will use the XQuery function util:import-module(). This function has three arguments:

  • $namespace: The full URI of the module that you are loading such as http://example.com/my-module
  • $prefix: the prefix you want to use to reference each function in the module such as style
  • $location: the database path that you will be loading the module from such as an absolute path/db/modules/my-module.xqm or a relative path my-module.xqm

For example the following will import a module called my-module from the /db/modules collection.:

  util:import-module(xs:anyURI('http://example.com/my-module'), 'style', xs:anyURI('/db/modules/my-module.xqm'))

The function xs:anyURI is used to cast each string into the URL type.

Function invocation

edit

Because the namespace is declare dynamically, the imported functions have to be invoked using util:eval. The input to this function is a string containing an XQuery expression. e.g.

  util:eval('style:header()')

Example

edit

The following will randomly load one of two style modules.

xquery version "1.0";
declare option exist:serialize "method=xhtml media-type=text/html omit-xml-declaration=yes indent=yes";
 
let $module := if (math:random() < 0.5)
    then
       util:import-module(
          xs:anyURI('http://example.com/style-a'),
          'style',
          xs:anyURI('style-a.xqm')
          )
    else
       util:import-module(
          xs:anyURI('http://example.com/style-b'),
          'style',
          xs:anyURI('style-b.xqm')
          )
return
<html>
   <head>
      <title>Test of Dynamic Module Import</title>
      {util:eval('style:import-css()')}
   </head>
   <body>
      {util:eval('style:header()')}
      {util:eval('style:breadcrumb()')}
      <h1>Test of Dynamic Module Import</h1>
      {util:eval('style:footer()')}
   </body>
</html>

Run

Style A Module

edit

Here is an example of a style module. It has four functions. One to import the CSS files, one for the header, one for the navigation breadcrumb and one for the footer.

xquery version "1.0";
module namespace style='http://example.com/style-a';

declare function style:import-css() {
   <link type="text/css" rel="stylesheet" href="style-a.css"/>
};

declare function style:header() {
<div class="header">
   <h1>Header for Style A</h1>
</div>
};

declare function style:breadcrumb() {
<div class="breadcrumb">
   <h1>Breadcrumb for Style A</h1>
</div>
};

declare function style:footer() {
<div class="footer">
   <h1>Footer for Style A</h1>
</div>
};

Style A CSS

edit
body {
    color: blue;
}

Style B Module

edit
xquery version "1.0";
module namespace style='http://example.com/style-b';

declare function style:import-css() {
   <link type="text/css" rel="stylesheet" href="style-b.css"/>
};

declare function style:header() {
<div class="header">
   <h1>Header for Style B</h1>
</div>
};

declare function style:breadcrumb() {
<div class="breadcrumb">
   <h1>Breadcrumb for Style B</h1>
</div>
};

declare function style:footer() {
<div class="footer">
   <h1>Footer for Style B</h1>
</div>
};

Style B CSS

edit
body {
   color: red;
}