<html> <head> <title>How to script with Ruby and Zeitgeist</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <link rel="stylesheet" href="highlight.css" type="text/css"> </head> <body bgcolor="#FFFFFF" text="#000000"> <p><font size="6"><i><b>How to script with Ruby and Zeitgeist</b></i></font></p> <p align="left"><font size="4">A lot of the flexibility of the software system provided in this diploma thesis comes from the ability to perform a lot of operations through scripts. The system uses the scripting language <a href="http://www.ruby-lang.org/en/">Ruby</a>. In this HowTo we will learn how to execute scripts and we will meet a few built-in scripting commands for interaction with the object hierarchy.</font></p> <p align="left"><font size="4"><b><font size="5">Executing Scripts</font></b></font></p> <p align="left"><font size="4">The task of executing scripts is rather easy. It is done using a built-in class of Zeitgeist ... the ScriptServer. The ScriptServer is located in the object hierarchy at the position '/sys/server/script'. Using a CoreContext, we can access an object in the hierarchy. We retrieve the instance of the ScriptServer and then use it to execute a Ruby script:</font></p> <pre><span class="dir">#include <zeitgeist/zeitgeist.h> </span> <span class="key">using namespace </span>boost; <span class="key">using namespace </span>zeitgeist; <span class="typ">int </span>main() { Zeitgeist zeitgeist; shared_ptr<CoreContext> context = zeitgeist.CreateContext(); shared_ptr<ScriptServer> scriptServer = shared_static_cast<ScriptServer>(context->Get(<span class="str">"/sys/server/script"</span>)); scriptServer->Run(<span class="str">"scripttest.rb"</span>); <span class="key">return </span><span class="num">0</span>; } </pre> <p align="left"><font size="4">As the above program shows, the entire system relies heavily on the <a href="http://www.boost.org">Boost library</a> and its smart pointer facilities. This makes every object reference counted and ensures safe object deletion. Due to the template nature it does make a few code lines longer than usually necessary, but the added safety is worth it. The example above should be self-explanatory. We create the Zeitgeist object. This creates the object hierarchy and the built-in services (ScriptServer, LogServer and FileServer). Then, we create a context. This is necessary, because everytime we specify a path, we can specify a relative path. Thus, we need a position in the object hierarchy we need to be relative to ... this is provided by the CoreContext. Using it, we retrieve the ScriptServer instance and execute a script through it.</font></p> <p align="left"><font size="4"><b><font size="5">Builtin Script Commands</font></b></font></p> <p align="left"><font size="4">The ScriptServer has its own CoreContext associated with it. So, every script command can be considered to be executed at a specific location in a virtual file system. Basically, the scripts are run inside a 'shell'. Now, let's take a look at the builtin commands:</font></p> <p align="left"><font size="4"><b>importBundle (string path)</b></font></p> <p align="left"><font size="4">This command is analogous to calling ImportBundle() on the Core object. It takes a string as a parameter, which should be the location of a DLL containing a valid entry point function.</font></p> <p align="left"><font size="4"><b>selectObject (string path) / cd (string path)</b></font></p> <p align="left"><font size="4"> The actual name of the command is selectObject, but an alias to cd is provided. This command makes it possible to navigate the object hierarchy. It works similar to its command shell counterpart, except that the path always has to be given in the form of a string. This function accepts absolute and relative paths. It changes what I call the 'current working object' or the 'current selected object'. </font></p> <p align="left"><font size="4"><b>ls</b></font></p> <p align="left"><font size="4">This command lists the contents of the current working object.</font></p> <p align="left"><font size="4"><b>pushd</b></font></p> <p align="left"><font size="4">This command pushes the current working object on a stack for later use. This is extremely helpful when building larger scene graph hierarchies.</font></p> <p align="left"><font size="4"><b>popd</b></font></p> <p align="left"><font size="4">Pops a current working object off the stack.</font></p> <p align="left"><font size="4"><b>dirs</b></font></p> <p align="left"><font size="4">Lists the stack of current working objects which have been pushed via pushd.</font></p> <p align="left"><font size="4"><b>new (string className, string path)</b></font></p> <p align="left"><font size="4">This command creates instances of classes, names them and places them at a certain location in the object hierarchy. The first parameter is the name of the class to instantiate. This name is relative to the '/classes/' object. The second parameter is the name and path of the object to create. Some examples:</font></p> <p align="left"><font size="4">new ('Simple', 'test');</font></p> <p align="left"><font size="4">This creates an instance of the Simple class (/classes/Simple) and gives it the name 'test'. It is created at the current location in the object hierarchy. </font></p> <p align="left"><font size="4">new ('Simple', 'blub/test');</font></p> <p align="left"><font size="4">This creates an instance of the Simple class (/classes/Simple) and gives it the name 'test'. It is created under a subdirectory of the current location called 'blub' ... So if one would be located at the root directory (/). The new instance of Simple would be located at '/blub/test'.</font></p> <p align="left"><font size="4">It is also possible to specify namespace for class names. For example, you create the FontServer of Kerosin like this:</font></p> <p align="left"><font size="4">new ('kerosin/FontServer', '/sys/server/font');</font></p> <p align="left"><font size="4">The new function returns a Ruby proxy object to the created instance. The created instance is also made the current working object!! How the proxies can be used to invoke C++ side functions is discussed in the <a href="howto_function.html">next HowTo</a>.</font></p> <p align="left"><font size="4"><b>get (string path)</b></font></p> <p align="left"><font size="4">This command returns a Ruby proxy object of the instance specified by the parameter 'path'. How the proxies can be used to invoke C++ side functions is discussed in the <a href="howto_function.html">next HowTo</a>.</font></p> <p align="left"><font size="4"><b>delete (string path)</b></font></p> <p align="left"><font size="4">This command deletes an object (and its subobjects) from the object hierarchy. It basically unlinks the object from the hierarchy. This removes a reference. Only if no other references remain will the object be deleted!!!!</font></p> <p align="left"><font size="4">This already concludes our rundown of the builtin scripting functions of Zeitgeist. We can now create and manipulate object hierarchies with simple Ruby scripts. Some of the functions return a Ruby-side proxy object to an instance in the object hierarchy. How the proxies can be used to invoke C++ side functions and how to expose C++ side functions to the proxies is discussed in the <a href="howto_function.html">next HowTo</a>.</font></p> </body> </html>