πŸ›  GGI.pm - A package to support perl-programs on Gemini servers

Similar as the *CGI.pm* module makes the life of the Perl developer easier when writing HTML CGI-programs, the *GGI.pm* module tries to do the same for the development of Gemini-programs. I named it *GGI* (Gemini Gateway Interface) because i wanted to have a short name, closely related to *CGI*.

Please find the short, generated technical documentation and the download-link to the module below. For a more detailed documentation,please continue to read this page.

πŸ“„ GGI technical documentation (using pod2gmi)

πŸ’Ύ Download the perl package (use "save as" in your browser…)

πŸ” High level overview

GGI…

πŸ“š Example programs explained in details

The example-programs (and more!), described below and more are available in the laboratory- or the toolbox-section of this site:

βš— The laboratory: Test-pages for CGI and UniCode

🧰 The ToolBox: A set of useful tools for web- and Gemini-developers

β… . Simple example, print the output of the ps command as gemin text

A list of all processes in a Linux-system can be displayed by running the command *ps -𝘧𝘈*. With a small perl-program as a wrapper, the output of the utility can be captured and reformatted to be displayed properly in a Gemini browser. The perl-program starts with the bash shebang line to start the perl-compreter, followed by the modules that are being used:

  #!/bin/perl
  use warnings;
  use strict;
  use utf8::all;
  use GGI;

In the next section, the command *ps -𝘧𝘈* is executed, using the back-tick mechanism and the output is captured into a perl-array. The system-specific new-line characters are removed from the output via *chomp*:

  my @output=`ps -fA`;
  chomp @output;

In the third and final section, a new GGI-object is created and the captured output is printed as a Gemini document:

  my $ggi = new GGI;
  $ggi->H1('Result of ps -fA');
  $ggi->Pre(@output);
  $ggi->H1(qq(🏑 Home-Link));
  $ggi->Link(qq(/ 🏑 Go back to the server's home.));

The constructor for the GGI-object will print a 𝘚𝘜𝘊𝘊𝘌𝘚𝘚 header by default, so the main program is relieved of that duty. The *top* example on the lab-page works in the very same way.

πŸ§ͺ Direct link to *ps* CGI-program

πŸ§ͺ Direct link to *top* CGI-program

β…‘. Reading a single value as input from the user

As demonstrated on my static Gemini test-page,

πŸ§ͺ Some tests with text formatting, tables and Unicode trickeries,

the Unicode-system offers some interesting ways to "format" text without actually formatting it. The little utility called *UnicodeText* that accepts a string, converts it into its "formatted" Unicode equivalents and prints the the result into a Gemini-document. The program starts with the bash shebang line to start the perl-compreter, followed by the modules that are being used:

  #!/bin/perl
  use warnings;
  use strict;
  use GGI;

In the next section the program will test if it was invoked with a parameter (the string you want to "format") or if an *INPUT* header must be returned to the client:

  my $ggi = GGI->new(1); # New GGI object without writing any header
  if (not defined $ggi->Param) { # No parameter present: Ask for the text to be converted
   $ggi->ResponseHeader('INPUT',qq(Please enter the text you want to convert?));
  } # END if

If there is a parameter-value present, it will be retrieved and printed into a Gemini-document in all avaliable "formats" by the next section of the program, followed by a link-section:

  my $Text = $ggi->Param(0);
  $ggi->H1(qq(βš™ Unicode Text Converter));
  $ggi->H2(qq(Bold Text));
  $ggi->print($ggi->UC_Bold($Text));
  $ggi->H2(qq(Italic Text));
  $ggi->print($ggi->UC_Italic($Text));
  $ggi->H2(qq(Bold & Italic Text));
  $ggi->print($ggi->UC_BoldItalic($Text));
  $ggi->H2(qq(Script Text));
  $ggi->print($ggi->UC_Script($Text));
  $ggi->H2(qq(Outline Text));
  $ggi->print($ggi->UC_Outline($Text));
  $ggi->H2(qq(Gothic Text));
  $ggi->print($ggi->UC_Gothic($Text));
  $ggi->H2(qq(Gothic & Bold Text));
  $ggi->print($ggi->UC_BoldGothic($Text));

  $ggi->H1(qq(πŸ”— Link-Section));
  $ggi->Link($ggi->ScriptName . ' βš™ Convert another text.');
  $ggi->Link('/DevToolBox.gmi 🧰 Back to the ToolBox.');
  $ggi->Link('/ 🏑 Go back to the server\'s home.');

Details of how parameters work in detail will be explained in the next paragraph of this document. *Caution*, this is tough technical stuff, get yourself and aspirine πŸ’Š or two to avoid a serious headache πŸ˜‰.

β…’. A more complex example, read parameter values and accumulate them in the *PATH_INFO* part of the URL

This little test program demonstrates how to use the Gemini INPUT status-code and how to accumulate multiple input values into the *PATH_INFO* part of the URL; It also shows how much work is done in the background by the GGI module. As usual, the perl-program starts with the bash shebang line to start the perl-compreter, followed by the modules that are being used:

  #!/bin/perl
  use warnings;
  use strict;
  use utf8::all;
  use GGI;

In the next section the program will create a new GGI object, this time without printing the default 𝘚𝘜𝘊𝘊𝘌𝘚𝘚 header, as we want to control the flow. If there are no parameters present in the URL, this means that the program was started for the first time, so we ask the user how many parameter-values should be queried

  my $ggi = GGI->new(1);         # New GGI object without writing any header
  if (not defined $ggi->Param) { # No parameter present: Get the number of parameters wanted
    $ggi->ResponseHeader('INPUT',qq(GGI-Test, ParamTest: How many parameters do you want to test?)); 
  } # END if

Once again, the GGI module helps the developer and exits the program with a return-code of zero [0], so that the server can send the *INPUT* header to the Gemini-client.

The Gemini-client will ask the user Β»GGI-Test, ParamTest: How many parameters do you want to test?<<, read the user's input, append the entered value to the end of the URL, separated by a question-mark and send that request back to the server.

When the program is invoked for the second time the URL contains a query part, which is captured by the GGI-module in its constructor and moved into the *PATH_INFO* part of the URL. A *REDIRECT* header to the new URL is then sent back to the client.

The next time the program is invoked and the flow of control is returned to the main program, there URL will contain a parameter but o query-part, so the third section of the program will be reached:

  my $Current = $ggi->Param+1;   # How many parameters are there
  my $Wanted  = $ggi->Param(0);  # How many parameters are wanted?
  if ($Current <= $Wanted) {     # Get the next parameter
    $ggi->ResponseHeader('INPUT',qq(GGI-Test, Paramtest: Please enter a value for Parameter $Current of $Wanted));
  } # END if

Using the *Param* method the program determines how many values should be queried and how many have already been entered. If not enough values have been enterd so far, another *INPUT* header is being sent to the Gemini client and another redirect round-trip is being performed.

Let's step through an example and look at the URL's that are being involved:

At this point, the number of requested values has been entered and the last section of the program is invoked, where the parameter-valuess are being retrieved from the URL and printed out. Notice again, that the GGI-module relieves you from the necessity of generating a 𝘚𝘜𝘊𝘊𝘌𝘚𝘚 header.

# Done with parameters! - Main program
$ggi->H1("Hello World!");

if (defined $ggi->Param) {
  $ggi->H2("The parameters from the URL");
  $ggi->List(@{$ggi->Param('*')});
} else {
  $ggi->H1("No Parameters.");
} # END if

$ggi->H1(qq(🏑 Home-Link));
$ggi->Link(qq(/ 🏑 Go back to the server's home.));

If your face looks now like this πŸ€ͺ or this 😡, don't worry i felt the same when i read about the way multiple parameters for GGI-programs must be handled within the Gemini protocol. I went through this excercise a couple of times and decided to mechanize it.

β…£. Something useful: Displaying the content of a Database-table as a table

[TODO: Will be added very soon and announced on gemini@lists.orbitalfox.eu]

πŸ“§ Contact information

Questions, comments or suggestions: Please send me an e-mail.

πŸ”— Link-Section

🏑 Back to this server's home-page