Understanding Modules ===================== Alain Reguera Delgado v1.0, Oct 2013 Introduction ------------ From version 0.5 of The CentOS Artwork Repository on, the *centos-art.sh* script was redesigned to introduce the idea of modules to its base design. Modules are a collection of functions written in Bash that can call one another to create individual execution environments. They may be nested efficiently to achieve high levels of maintainability and extensibility. This make possible for modules writers to divide complicated tasks into smaller tasks that can be easier to debug, maintain and share with other modules efficiently (e.g., instead of loading modules all at once, they are only loaded at demand and unset once they conclude their execution). Inside the *centos-art.sh* script there are two kinds of functions, which are: "global functions" and "module-specific functions." The global functions are defined in the very beginning of *centos-art.sh* script and remain in memory for you to call whenever you need it using a regular function call syntax. The module-specific functions, on the other hand, are defined at demand for specific tasks and removed from memory once the task they were created for is over. The collection of all module-specific functions related to the same task is what we call a module environment. Module environments can be either top-module, sub-module or sib-module and all are executed through the *tcar_setModuleEnvironment* global function. This article describes the modular design of *centos-art.sh* script. It is a good place to start if you are planning to contribute new module environments to *centos-art.sh* script. The next section delves into what a module environment is, the tree module types you can find and the correct way of execute them. The Module Environment ---------------------- The module environment provides an array of strings (called the environment). You can use this environment to deploy solutions for specific problems. Inside the *centos-art.sh* script, module environments are made of small functions that create small environments to solve small problems. Then they are combined in a specific order to solve bigger problems. The process of initiating a module environment inside *centos-art.sh* script starts at the end of the *centos-art.sh* file itself, with the invocation of the *tcar_setModuleEnvironment* function. This function, at this point, executes the module environment for the module name you specified in the first argument of the command-line. The modules you specified in the command-line are also known as top-modules. Top-modules initiate the higher module environment inside *centos-art.sh* script. This means that variables and functions defined inside it will be available as long as its execution-time last. Top-modules are stored directly inside the +Modules/+ directory of *centos-art.sh* script, as described in <>. Top-modules use to be small and have well defined goals. Top-modules exist to define global variables for task-specific actions, interpret module-specific options passed through the command-line and execute the appropriate actions based on them. These actions can be involve calling other top-modules or sub-modules. Sub-modules are also module environments. They are executed by the *tcar_setModuleEnvironment* function when the *-t sub-module* option is passed to it. Sub-modules have the characteristic of being nested modules. They are generally executed one inside another to create a chain of module environments. A chain of module environments is very useful in situations where you want to divide one large task into smaller tasks and also control which of those smaller tasks are loaded based on specific conditions (e.g., you want to render both images and documentation). In a chain of modules, module environments at a lower level in the chain (those started last) have access to higher module environments (those started first), but not the opposite. When processing information this way, module environments aren't destroyed until the last module environment loaded is done. At that point, module environments are destroyed in the reverse order they were loaded (e.g., if the chain of module was executed as ``render->images->svg'', then, when ``svg'' module environment is done, the chain of modules will be destroy ``images'' and finally ``render''). Module environments can also share one common module environment on top of them to create sibling processing (sib-module). Sibling processing is very useful in situations where you start processing information in a way and then need to jump to another related but different kind of processing (e.g., when you are rendering documentation you first start rendering it as html and then jump into manpage). In this situation, you open a module environment that contains information useful for two or more different but related kind of processing and jump from one to another without destroying the common information. However, when one of the specific ways of processing information is done, it is destroyed. This, before opening a new way of processing (e.g., once html processing is done, the html module is destroyed and, then, the manpage module is loaded). So, only the required modules are active while processing information. When we say that a module environment is destroyed it means that all the related variables and functions which make that module environment useful are removed from *centos-art.sh* script execution environment, which make the module environment inoperable to all effects, indeed. However, it is worth to know that all global array variables the *tcar_setModuleEnvironment* function uses to store module-specific information remain active until the last module environment is destroyed (i.e., the top-module executed from the command-line). This make possible for *centos-art.sh* script to ``remember'' information about all different but related module environments loaded to perform specific tasks. Such remembering feature is what make possible to implement sib-modules as described above. [[module-implementation]] Module Implementation --------------------- The implementation of module environments take place in two locations. The first location is the module directory structure and the second location *tcar_setModuleEnvironment* function. The module directory structure contains a standard distribution of files and directories that the *tcar_setModuleEnvironment* function recognizes and uses for executing functions inside it. Each module environment you want to execute needs a module directory in the *centos-art.sh* script tree. In this directory you organize the functions that make your module environment useful. The directory structure of your module directory is important because *tcar_setModuleEnvironment* makes use of it to know module-specific information. The *tcar_setModuleEnvironment* function is stored in the +Scripts/+ directory. It uses global array variables to store information about the modules and (non-array) local variables to handle the information stored about modules in each call of the function. Each time you call the *tcar_setModuleEnvironment* function, it increments the array variables counter, stores module-specific information in the array variables, reset (non-array) local variables using the current module's information stored in the array variables and executes the module's initialization script from (non-array) local variables. When the module's environment finishes its execution, the array counter decrements and the module's environment is destroyed, to free the memory it was using. This process repeats continually, each time you call the *tcar_setModuleEnvironment* function. [TIP] ====================================================================== In case you want to see how modules are created and destroyed inside, pass the *--debug* option to *centos-art.sh* script. It will activate the script internal debugger for you to see the whole process. ====================================================================== To illustrate how modules need to be implemented inside *centos-art.sh* script in order for *tcar_setModuleEnvironment* to execute them correctly, we'll use a module named ``hello'' as example in the next sections to describe the steps you need to follow for creating new modules from scratch. The purpose of *hello* module is to print out greetings to standard output and exit successfully. See *hello(1)*, for more information about it. Planning New Module ------------------- Planning is probably the first thing you need to do in order to create a new module to *centos-art.sh* script. It would be very helpful if you read documentation and bugs related to modules and function in order to find out possible tasks you might take as motivation for your contributions. Such investigation always help. The motivation behind the *hello* module came with the need of having a simple module that could be used as reference to describe how to create and execute new modules inside *centos-art.sh* script. The top modules we have by now aren't suitable for a simple description about how to create new modules from scratch. Instead, I thought that creating a simple module for that purpose would be better. Suddenly, I recalled the GNU's hello package which describes their standards about well written GNU packages and found motivation enough to create a *hello* module under the same idea but this time focused on *centos-art.sh* script, instead. The motivation for planning your own modules might vary. No matter what it would be, use it with confidence, plan your module, set the final spot where you want to get to and then, read the next section. It describes the steps you need to follow in order to write a functional module inside *centos-art.sh* script. Creating New Module ------------------- The <>, shows a basic module structure. In this example, we see that module directories are written capitalized while module initialization file and related functions are all written in lower-case. Note also how the module directory and files inside it use the module's name in their file names to get identified. This is a convention that should be followed in order for *centos-art.sh* script to execute modules. Another convention you should follow, when creating related functions, is using underscore (``_'') to separate the module's name from the function's descriptive name. In these cases, the function's descriptive name is always written in camel-case followed by the +.sh+ extension. [[directory-structure]] .The directory structure ====================================================================== ---------------------------------------------------------------------- . |-- COPYING <-- Contains the GPL license. |-- Locales/ <-- localization of all sh files. |-- Manuals/ <-- manuals for main and global functions. |-- Modules/ <-- top-modules are stored here. | `-- Hello/ <-- top-module directory. | `-- hello.sh <-- top-module initialization file. |-- Scripts/ <-- global functions are stored here. |-- centos-art.conf.sh <-- main configuration file. `-- centos-art.sh <-- main initialization file. ---------------------------------------------------------------------- ====================================================================== [[directory-structure-extended]] .The directory structure with extended functionality ====================================================================== ---------------------------------------------------------------------- . |-- COPYING |-- Locales/ |-- Manuals/ |-- Modules/ | `-- Hello/ | |-- Modules/ <-- Hello sub-modules are stored here. | | |-- Actions/ <-- Sub-module of Hello but sib-module of Lowercase and Uppercase | | | `-- actions.sh <-- sub-module initialization file. | | |-- Lowercase/ <-- Sub-module of Hello but sib-module of Actions and Uppercase. | | | `-- lowercase.sh | | `-- Uppercase/ <-- Sub-module of Hello but sib-module of Actions and Lowercase. | | `-- uppercase.sh | `-- hello.sh |-- Scripts/ |-- centos-art.conf.sh `-- centos-art.sh ---------------------------------------------------------------------- ====================================================================== The module's initialization file contains the main function definition of your module. It is a good place to define variables that must be always available inside the module. There is also a top-comment that collects information about the function files you are writing (e.g., a small description, a written by section, the copyright note and the legal status of the file). Even using a top comment like this is not required for *centos-art.sh* script to execute modules properly, it is very useful as matter of consistency and style inside it (and the copyright and legal notice might be required for legal protection of your code as set by GPL). Finally, there is the function definition named +hello+ just as the directory that holds it but all in lowercase. Inside this function definition is where we write what we want the *hello* module does for us. This way, following with the *hello* example, we create an array variable inside it holding all the suggestions we would like to print, as described in <>. [[initialization-file]] .The initialization file of hello module ====================================================================== ---------------------------------------------------------------------- #!/bin/bash ###################################################################### # # hello.sh -- Print out greetings to standard output and exit # successfully. # # Written by: # * Alain Reguera Delgado , 2013 # # Copyright (C) 2009-2013 The CentOS Artwork SIG # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or (at # your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # ###################################################################### function hello { tcar_printMessage "Hello, World!" } ---------------------------------------------------------------------- ====================================================================== You can nest modules by creating directory structures like this, inside the Modules/ directory of the higher module you want to extend its functionality. Inside the repository, modules related to *centos-art.sh* script are stored in the directory +Automation/Modules/${MODULE_NAME}/+. *Modules/*:: This directory contains module's modules. *Manuals/*:: This directory contains module's documentation produced by *help* module. The structure of this directory looks as follow: + ---------------------------------------------------------------------- Manuals/ |-- ${LANG}/ | |-- man${SECTION_NUMBER} | `-- ${MODULE_NAME}.${SECTION_NUMBER} `-- man${SECTION_NUMBER} `-- ${MODULE_NAME}.${SECTION_NUMBER} ---------------------------------------------------------------------- *Locales/*:: This directory contains module's translations produced by *locale* module. The structure of this directory looks as follow: + ---------------------------------------------------------------------- Locales/ `-- ${LANG}/ |-- LC_MESSAGES | |-- ${MODULE_NAME}.sh.mo | `-- ${MODULE_NAME}.docbook.mo |-- ${MODULE_NAME}.sh.po |-- ${MODULE_NAME}.sh.pot |-- ${MODULE_NAME}.docbook.po `-- ${MODULE_NAME}.docbook.pot ---------------------------------------------------------------------- *Scripts/*:: This directory contains function scripts written by module's writers. Here is where all the tasks the module is useful for are written and stored in. As convention the following structure is used: + ---------------------------------------------------------------------- Scripts/ `-- ${MODULE_NAME}_${FUNCTION_NAME}.sh ---------------------------------------------------------------------- + {asccidoc-br} + Inside each function script, there is a top comment where you should put the name of the function script, a brief description about what it does, as well as author and copying information. After the top comment and separated by one white line, you should define the function sentence using the long format. + ---------------------------------------------------------------------- #!/bin/bash ###################################################################### # # ${MODULE_NAME}_${FUNCTION_NAME}.sh -- ${FUNCTION_DESCRIPTION} # # Written by: # * ${AUTHOR_NAME} <${AUTHOR_EMAIL}>, ${YEARS} # # Copyright (C) ${YEAR} The CentOS Artwork SIG # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # ###################################################################### function ${MODULE_NAME}_${FUNCTION_NAME} { ... } ---------------------------------------------------------------------- + [NOTE] If your are planning to contribute a new module to *centos-art.sh* script, please, consider using the layout described above for all your function scripts, consistently. *$\{MODULE_NAME}.asciidoc*:: This file contains the module's documentation source. From this file it is possible to produce the same documentation in other formats including manpage, html and pdf. Whenever you need to improve the module's documentation, edit this file. *$\{MODULE_NAME}.conf*:: This file contains the module's configuration variables. These variables are exported to the environment and remain there as long as the script execution environment is alive. Some variables are read-only others not. + The configuration file provides explanation about each environment variable it exports. If you want to know more about what these variables are, open this file and read the comments near each variable. *$\{MODULE_NAME}.sh*:: This is the module's initialization script. The first file executed when the module called from the command-line. This file provides access to argument parsing and controls how module-specific function scripts are called. This is the starting point for writing modules. You can write a complete module using this file only but, frequently, it is convenient as the module complexity grows to divide it in smaller pieces (function scripts) to improve maintainability and error findings. // vim: set syntax=asciidoc: