1Nest Overview

In this section, we'll introduce the basic components that makes up a Nest application, as well as the concepts that must be known by an application designer to write a Nest application.

Although not strictly necessary, it's advisable to read this chapter prior moving to the quick start, so that the code written there can be understood in a broader context.

Overall architecture

Nest is mainly organized into four kind of elements:

The library is a set of common functions and objects that are exposed to both Nest components (Services and Data Managers) and to third party users, as the final application.

Services are the functional heart of the Nest system. As the name suggests, they provide "services" to the final application. They are able to configure themselves through various input sources, including input coming from web forms or query URIs, and there are various means through which they can be controlled by the host application.

Data managers provide a mapping between entities stored in external databases and objects seen in the Nest application. It is not mandatory to use data managers to handle persistent data, but they often provide a more abstract, simpler way to access databases.

Hooks are callback points invoked by the other elements that can be overridden by the host application, so that it can provide new functionalities either dynamically or statically - but on a per-site basis.

Nest application anatomy

An application is composed of pages, each one presenting exactly one view to the remote guest. An entry point (generally index.fal) loads the Nest system and perform what is called routing. This consists in determining which one page should be delivered to the user depending on the input variables.

The routed page is then loaded as a Falcon plugin (via the reflexive compiler interface) and executed in the current virtual machine context. This makes all the Nest system available to the loaded script. The routed script can then behave as a static page, or invoke Nest modules, namey services and/or data managers.

Nest modules are themselves plugins which the routed page can load through the faciliteis provided by Nest. Services work as abstract processors that can process input from several soruces and then can be rendered to the final page through a skin. Data managers bind database entities to objects, and they are generally used by services or as a mean to make database-bound data to travel across abstract services and the pages that are hosting them.

Nest site structure

Before descending in the details of the Nest routing process, it is useful to describe how Nest site and modules are structured and layed down.

Nest system has its own installation location and structure, which is not necessarily related to the way sites using Nest should be organized; at site developer level, the Nest module and utilities are opaque, and all that's required is that Nest and its components are correctly installed as a Falcon module somewhere in the FALCON_LOAD_PATH, or even directly in the default Falcon module location (e.g. /usr/lib/falcon).

Also, here we'll describe the default site structure; the vast majority of this settings can be changed, even dynamically, through configuration options that are explained later.

Consider the entry point of an application a script in a given location; usually, but not necessarily, the script is also the default script for the given directory (e.g. index.fal).

Then, the site will be organized as follows:


   /homedir
      index.fal
      /nest
         /pages
         /srv
         /dm
         /hooks

The nest directory is called root in the nest tree or referred as nest tree.

Note: It is advisable to place the nest tree outside the scope of the directories served by the web server, if possible, to minimize security risks. The default is to search it right under the entry point script just because some low-cost web hostings don't allow to write data outside the scope of the directories served by the provided web server.

Page routing

Typical applications will want to load more pages from a site, depending on the connection status and input parameters. This process of selection of the page that is served by Nest to the remote user is called routing and can be widely configured. However, the default mechanism is usually enough to serve the needs of even complex web applications; it consists of serving the page named after an input variable called pid. For example, an url like


   http://www.mysite.com/index.fal?pid=user_settings

would search for a falcon module named user_settings.fal or user_setting.ftd (or their equivalent fam module), in the nest/pages directory, and run it as a nest page.

If the "pid" variable is not given, the default routing scheme will fallback to the page ID home. This is configurable as well.

Framing

Sites, and especially web applications, are usually providing a common code that is consistent througout all the areas, which sourrounds the web site.

This is called framing. Nest doesn't force the users to use a particular framing scheme. For example, they may use the standard falcon include() function to include dynamically header and footer elements, or they may consistently load a set of site-specific function which include framing facilities. We'll illustrate some of this mechanisms later on in this document,

However, Nest provides a ready-to-use framing mechanism that fits most of the common needs of web applicaitons, and this is covered by this quickstart.

When the Nest.frame property is set to a function, then Nest will load the page that the routing system required and render it separately. The output of the rendered page can be retreived through the Nest.content method.

A frame function can also be used to load services that are common through all the pages sharing that frame.

For example, the following code can be placed on the entry point script to provide the same headers and footers for all the pages of a site:


// this is index.fal
load nest

Nest.frame = function()
            > '<html><head><title>My site</title></head><body>'
            > '<p>My site starts here...</p><hr/>'
            > Nest.content()
            > '<hr/><p>And this was My Site! ... </p></body></html>'
         end

Nest.route()

The function set in Nest.frame may be also a direct include that loads the frame page. The location of those pages is always relative to the entry point.

For example, to externalize the frame in another module, it is possible to write:


// this is index.fal
load nest

Nest.frame = .[include "frame.ftd"]
Nest.route()

Then, frame.ftd would beside index.fal, and would look like the following:


<!-- This is frame.ftd -->
<html>
   <head><title>My site</title></head>
   <body>
   <p>My site starts here...</p><hr/>
   <? Nest.content() ?>
   <hr/>
   <p>And this was My Site! ... </p>
   </body>
</html>

Framing can be disabled on specific pages adding the Falcon attibute nest_frame as false; for example:


<?
nest_frame: false
?>
<html>
<body>
<h1>Unframed page</h1>
<p>This page won't be framed, even if nest has a site-wide frame</p>
</body>
</html>

Note: Unframing .fal and .ftd files is useful to build simple AJAX based sites.

ov_services Services

Nest modular web application composition is based on the concept of services, that are modules preforming specific tasks and then, optionally, being rendered on the final page.

For the details, please refer to the "service" chapter. In this quickstart guide, we see how to setup a minimal service in a page, and their basic working principles.

Services are invoked through the Nest.service method, which returns a Service class instance. The instance can be configured on the fly during the Nest.service call, or it may be configured separately, after the instance is obtained.

Services are executed in different phases; part of them is run as they are first invoked, other parts of them are run after all the page has been composed. They can also register themselves to listen for other services activity, so they can effectively be invoked several time per page.

If a service is meant to output some data on the final page, the last action it performs is the render step. The default is that of passing a set of data to a simple script that must organize this data as an HTML output; this specialized script is called skin.

Services communicate with the rest of the Nest world through special entities called service variables.

Those special variables are declared by the service at startup, and Nest can treat them in very special ways. For example, service variables can be automatically filled with input from web forms or queries, or from session data, or they can be shared throughout the sytem. Mainly, they are turned into a dictionary and automatically fed into the skin, if the service doesn't provide a personalized rendering strategy.

Hooks

Hooks are callback functions that can be overridden by the final application. Instead of placing those callbacks directly on a callback point, Nests allows for a name-based invocation mechanism that allows for multiple services or multiple pages to invoke the same function by name.

Hooks can be declared directly through the Nest.addHook method, or they can be loaded from specially named plug-ins in the nest/hooks directory of the nest tree. Also, they can be provided with a config.fal facility file. In short, they can look like Nest modules, but their sole role is that of providing an abstract callback point; the fact that they can be loaded from a plug-in file and that they can be provided with a possibly complex configuration file is an extra facility offered by Nest that helps to configure and finetune web-oriented applications.

Declaring a hook is a matter of deriving from the Hook class, or creating a simple hook entity via this facility:


h = Nest.Hook( "my_hook",
         function( params )
            // do things...
            return "some_value"
         end
      )
// ....
Nest.addHook( h )

Hooks can be also referenced by name; in that case, a module named after the hook name is searched in the nest/hooks directory. If found, it must expose a class with the same name of the module and derived from Hook.

In both cases, Hooks are configured through an optional config.fal to be placed in their own nest/hooks/ directory, and/or through a dictionary of key/value pairs passed as an extra parameter in their constructor.

Data Managers

Data managers are simple database abstractions that make room for seeing an application database as a persistent storage of program entities.

The Data Manager controls the database connection and is responsible for loading the entities. Entities must be derived from the DBEntity class (providing an instance of the class is not enough).

Access to the data is regulated through views (instances of DBView), while data is usually represented by a dictionary of "record name" => value pairs; when the data is searched through the data manager, it is usually returned wrapped in a DBRecord, which carries also meta-informations and utility methods that operate on the returned data.

Data managers are configured through a configuration file named after their own name under nest/dm/.fal; all the entities they know are stored under their directory, along the scheme nest/dm//.fal, or they may be created autonomously by modules in need of injecting their own data model in the current manager. For example, the Wiki service can be configured to injects a default entity representing a wiki page in a given data manager at its startup.

As views are tighly bound with the user of an entity (it's a data entity user that knows how the data should be represented), DBView must be created and specifically assigned to their users, possibly as a part of their configuration.

Finally records are extracted directly from an entity, or pre-processed through a view, and must be uniquely identified by a code called eid. The eid is a string representing the value of all the key fields of an entity, transformed so that it can be easily serialized and passed as a parameter.

Made with http://www.falconpl.org