© Mansour Ayouni 2020
M. AyouniBeginning Ring Programminghttps://doi.org/10.1007/978-1-4842-5833-0_4

4. Outputs of Ring

Mansour Ayouni1 
(1)
Kalidia Consulting, Sousse, Tunisia
 

When you speak, you are emitting a voice in the air that is understandable by the audience. When you move, you are performing an animation of your body in the space that is observable by the attendees. When you code, you are exposing the data resulting from your computer thinking through a medium that is perceivable by the user of your program.

In this chapter, I will show you how Ring can display data on a screen and save it to a file, in a database, on a web page, and even in a printable document.

To do so, we will build on the NorthAfricaApp project we created in Chapter 3 and augment it with new capabilities other than simply “rendering” data in a string variable.

Nine Things You Will Learn

In this chapter, you’ll do the following:
  • Perform data outputs in Ring in many formats

  • Analyze an existing codebase

  • Design a clean layered software architecture in Ring

  • Change the core keywords and operators of the language

  • Use BASIC syntax in Ring programs

  • Write HTML pages in Ring

  • Use a Qt widget to render PDF files in Ring

  • Write text files and copy binary files

  • Handle errors and exceptions

Before We Start

To start with a clean base, download the zipped file of the NorthAfricaApp project available online1 or on the book’s page on the Apress web site, extract the folder under c: ingbookchap4, rename the folder to northAfricaAppV2, and then open the Project Files explorer in Ring Notepad using the menu View ➤ Project Files (Ctrl+J) if it is not open yet. Browse the tree view until you reach the folder you just downloaded, as shown in Figure 4-1. Then select the northafrica.ring file to open it in the Code Editor.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig1_HTML.jpg
Figure 4-1

Project Files explorer in Ring Notepad

Every time you have a programming project (an application with many files), create a folder for it (usually with the name of the app itself), and then use the Project Files explorer to get the following advantages:
  • All your files are visible in one place while you are programming.

  • You can go back and forth between your files instantaneously.

  • When you want to add a new file to your project, the current folder is selected by default so you just need to enter the name and click the Save button.

Let’s create a tempo.ring file and add it to the project. To do so, be sure that one of the files in the northAfricaV2 folder (or the folder itself) is selected in the Project Files explorer. Then select File ➤ New (Ctrl+N) in the main menu of Ring Notepad. When the window shown in Figure 4-2 is displayed, type tempo (without .ring because it will be added automatically) and click Save.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig2_HTML.jpg
Figure 4-2

Adding a new file to the northAfricaAppV2 project

The file tempo.ring is then added to the folder in the Project Files tree view and opened in the Code Editor.

Refactoring the Right Side

Sam and Dan are two programmers on the NorthAfricaApp team. Sam was the original designer of the app, but he is going on vacation.2 Dan is selected to implement a future enhancement. He will power-charge the app so it can expose the generated graph on several platforms: the screen, a text file, a database, a web page, and a printable file.

As a reminder from what we’ve done in Chapter 3, Figure 4-3 shows the graph as displayed in the Output window of Ring Notepad.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig3_HTML.jpg
Figure 4-3

The chart displayed in the Output window in Ring Notepad

To get this chart, you need to open the northafrica.ring file and execute it, that’s it.

Analyzing the Existing Architecture

Before starting on anything, Dan asks to see the existing architecture of the app. Sam answers him by showing him the diagram in Figure 4-4.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig4_HTML.jpg
Figure 4-4

Original architecture of the NorthAfricaApp

This is the conversation Sam and Dan have:
  • Sam: As you see, everything starts with the NorthAfrica.ring file where the call flow is defined. Then, the three layers of the app architecture are executed chronologically.

    In the first layer (1), a connection with the data source, being a text file or a database, is created.

    Then, the returned data is transformed in layer (2) to a data structure (a Ring list called aDatagraph) that describes the graph content and simplifies its future rendering in layer (3).

    Finally, layer (3) receives the datagraph, parses it, and renders it in a Ring string called cGraph. The string is used to show the graph on the screen (in the Output window in Ring Notepad).

    Is the app architecture understood? And your mission clear enough?

  • Dan: Yeah. I’ve been called on to take the same agility you made on the left side regarding multiple data input (in the previous chapter) and implement it on the right side by allowing multiple data output.

  • Sam: Exactly! I'm thrilled to know how you are going to approach that architecturally.

  • Dan: Thank you, Sam, that seems great. But, before that, I need to look closely to how your architecture design (shown in Figure 4-4) has been implemented in terms of Ring files and load statements. This is fundamental to assessing whether the diagram accurately reflects the reality of what you've done in the code.

  • Sam: OK. Let’s browse the codebase together. See Figure 4-5.
    ../images/485799_1_En_4_Chapter/485799_1_En_4_Fig5_HTML.jpg
    Figure 4-5

    Implementation in Ring of the program architecture shown in Figure 4-4

  • Sam: As you can see, the program execution starts in the file northafrica.ring by loading the connector, which is the txtconnector.ring file (1). But you can call the dbconnector.ring file instead, if you want. Whatever data source you specify, the connection returns a data structure hosted in an a[] list.

    Then the chartRenderer.ring file is loaded (2). At the beginning of this file, transformer.ring is loaded (3). The transformer works on the a[] dataset and transforms it to a better format, aData[], ready to be transformed to a datagraph.

    Then the execution flow comes back to the chartRenderer.ring again, carrying the aData[] list in its hands. At this level, the datagraph.ring file is loaded (4), and a Datagraph[] list is returned.3

    Finally, rendering of the graph inside a Ring string called cGraph is performed in file chartRenderer.ring (5), more specifically, using the showgraph() function.

    Is this clear enough, Dan?

  • Dan: Absolutely, you’ve done a great job.

    Still, the call flow you implemented using load command s breaks the logical order defined in the architecture diagram. This can be confusing! Rather, I’ll reorganize the sequence of calls to explicitly reflect the design of the architecture.

For that, Dan opens the northafrica.ring file and modifies it to look like Listing 4-1.4
// Layer 1
load "txtconnector.ring"
// Layer 2
load "transformer.ring"
load "datagraph.ring"
// Layer 3
load "chartrenderer.ring"
showgraph()
Listing 4-1

northafrica.ring

Of course, in chartRenderer.ring, the first two lines are no longer need. Dan deletes them.
load "transformer.ring"
load "datagraph.ring"

Then, he saves the file.

Next, he goes back to the main file northafrica.ring and executes it. In the Notepad Output window, the chart is displayed. Good move!

Designing a Target Architecture

Let’s take a look at the target architecture as drawn by Dan on the board of the room (Figure 4-6) and then compare it with the existing one in Figure 4-5.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig6_HTML.jpg
Figure 4-6

Target architecture to be implemented by Dan

Figure 4-6 demonstrates the wise decision taken by Dan of leaving the (1) and (2) layers untouched (same files), while moving the fabrication of the cGraph string from layer (3) back to layer (2). This means that cGraph is no longer considered as a rendering thing by Dan, but as an intermediary format provided by layer (2) and used by layer (3) to render the graph in many different formats.
  • On the screen, using a file called ou2screen.ring: cGraph is simply printed using the ? keyword.

  • In a text file, using a file called out2file.ring: cGraph is printed to the file using the write() standard function.

  • In an HTML web page, using a file called out2web.ring: He’s not sure how to do it, but, for sure, Dan will be looking toward the Ring WebLib library.

  • Finally, in a PDF document, using a file called out2pdf.ring: cGraph will be printed using a specific widget from the RingQt library that automates the use of PDF files.

Hence, by reviewing the code in the chartRenderer.ring file, we find that the showchart() function is doing two operations.
  • Fabricating the cGraph string from the datagraph matrix

  • Showing the graph by printing it on the screen using ? cGraph

A separation of concern (SoC) is required here. So, follow these steps:
  • Let’s save the current file (chartRenderer.ring) as cgraph.ring.

  • In cgraph.ring, delete the first line, func showgraph().

  • Also, inside the same cgraph.ring file, delete the two lines responsible for displaying the cGraph on the screen.

// Showing the graph on the screen
? cGraph
  • Save the file (cgraph.ring).

  • In the main northafrica.ring file, delete the last line, showgraph().

In the new design, chartRenderer.ring doesn’t exist. As shown in Figure 4-6, it will be replaced with a file for every target platform. Therefore, follow these steps:
  • In the main file, northafrica.ring, delete the line Load "chartRenderer.ring" (note that layer 3 became empty).

  • Open the current northAfricaAppV2 folder (in your operating system’s File Explorer).

  • Delete the chartRenderer.ring file.

Let’s get back to the main file called northafrica.ring . Listing 4-2 shows its updated content (Dan just added some useful comments).
// Layer 1
load "txtconnector.ring"          # can be also "dbconnector.ring"
                                  # text data returned in an a[] list
// Layer 2
load "transformer.ring"           # a[] transformed to aData[] list
load "datagraph.ring"             # aData[] mapped in an aDatagraph[]
load "cgraph.ring"                # cGraph made from aDatagraph[]
// Layer 3
                                  # cGraph will be used here
Listing 4-2

northafrica.ring

As you can understand from the flow of ideas conveyed by the code itself and by the comments added by Dan just now, you can test the program by simply adding ? cGraph at the end of the file. Do this and execute the file. The graph is shown in the Output window of Ring Notepad as expected.

However, this is a dirty maneuver to just be sure we didn’t corrupt the system with the changes we made. In respect to the new architecture design (see Figure 4-6), Dan will start implementing the rendering layer of the program (layer 3) by providing a file for the first target platform (the screen). To follow along, do these steps:
  • Create a new file in the current directory and call it out2screen.ring.

  • Inside the file, just write ? cGraph and save it.

  • Go back to the main northafrica.ring file.

  • Add Load "out2screen.ring" at the end of the file and save it.

The complete listing of the main northafrica.ring file looks like Listing 4-3.
// Layer 1
load "txtconnector.ring"           # can be also "dbconnector.ring"
                                   # text data returned in an a[] list
// Layer 2
load "transformer.ring"            # a[] transformed to aData[] list
load "datagraph.ring"              # aData[] mapped in an aDatagraph[]
load "cgraph.ring"                 # cGraph made from aDatagraph[]
// Layer 3
load "out2screen.ring"             # renders the graph on the screen
Listing 4-3

northafrica.ring

Execute it. Everything should be OK: the final result but also the underlining clean (or at least, willing to be clean) architecture!

Before he goes forward in implementing the other output formats, Dan will share with us some useful tips from the jar of his Ring expertise.

Using See Instead of ?

As you can see, “outputting” data to the screen is made possible with the use of the ? keyword . Ring brings another command you can use: the See command.5

Open the tempo.ring file in the current northafricaAppV2 folder. Then try Listing 4-4 (Ctrl+F5).
? "Apress"
SEE "Editions"
Listing 4-4

tempo.ring

They give the following clean output:
Apress
Editions

But what if you inverse the order of the two lines by saying SEE before ?? You get EditionsApress. Why?

This is because while the two keywords are equivalent in showing a string in the screen, one leaves a new line at the end (?) while the doesn’t (SEE).

This seems to be a minor difference, but in practice it isn’t. In fact, one can argue that ? can be replaced by SEE + NL, which is true, but adding a new line every time with code can be a waste of time. Anyway, Ring is made for freedom, and you are free to use one or another of the two keywords depending on your taste.

Inviting BASIC into Ring

What if you prefer the classic BASIC print command instead of SEE? It’s simple to do.

In fact, Ring offers the ability to change its keywords to new ones defined by the programmer, using the ChangeRingKeyword command .

At the beginning of your current tempo.ring file, add the following:
ChangeRingKeyword see print     # print is substituted to see
And then change ? and see to print in the remaining code. The tempo.ring file looks like Listing 4-5.
ChangeRingKeyword see print
print "Apress" + NL
print "Editions"
Listing 4-5

tempo.ring

Changing Ring keywords and even Ring operators (like +, -, <, and >) is a critical feature with a lot of power, since you are able to adjust the internal syntax of Ring dynamically without limit. This empowers you to start up your own programming language in minutes, not months. But this is also a dangerous feature that a programmer should use with a sense of responsibility.

In our case, the change we made will impact every future line of code. So, it is advisable to reset everything to its initial state at the end of the program, as shown in Listing 4-6.
// Changing a keyword
ChangeRingKeyword see print
print "Apress" + NL
print "Editions" + NL
// Back to the initial syntax
ChangeRingkeyword print see
SEE "Beginning Ring Programming"
Listing 4-6

tempo.ring

This syntax flexibility can be implemented on a wider scale by creating a syntax file to every programming language flavor you, or your team, prefer to use while writing Ring programs. Let’s experiment with it to use Ring like it was plain old BASIC.

To do so, create a new file called BasicOn.ring and put the code shown in Listing 4-7 inside it.
ChangeRingKeyword  see   print
ChangeRingKeyword  ok    endif
ChangeRingKeyword  next  endfor
ChangeRingKeyword  end   endwhile
Listing 4-7

basicon.ring

Save it in the current folder.

You may think now that we are going to use the Load command to import this file, but we don’t. In fact, Load is actionable by the compiler in the parsing phase, but ChangeKeyword is actionable before that, in the scanning phase.6 That’s why Ring provides a specific keyword for importing the syntax files: LoadSyntax.

Now, in tempo.ring, write the code shown in Listing 4-8, reread it, and then run it.
// Let's talk in basic
LoadSyntax "basicon.ring"
x = 10
while x > 0
            print "x = " + x + nl
            for t = 1 to 10
                        if t = 3
                                    print "number three" + nl
                        endif
            endfor
            x--
endwhile
# Back to Ring dialect
ChangeRingKeyword print    see
ChangeRingKeyword endif    ok
ChangeRingKeyword endfor   next
ChangeRingKeyword endwhile end
see "done" + nl
Listing 4-8

tempo.ring

Isn’t that wonderful? Every time you need it, you say it in BASIC while you are Ringling. The same can be made with any syntax of any programming language.

Do you know any champion other than Ring with such a flexibility?

Using the Standard print( ) Function

The Ring language comes with a standard print() function that you can use by loading the StdLib library.

In tempo.ring, erase everything and start typing load.

You will notice that an IntelliSense helper is displayed so you can select an option in a menu instead of typing it by hand. In our case, we select load "stdlib.ring" by clicking it using the mouse or directly by using the arrows and Enter keys on your keyboard, as shown in Figure 4-7.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig7_HTML.jpg
Figure 4-7

IntelliSense in Ring’s Code Editor

Simply write the code in Listing 4-9 to print the Hello! text in the Output window.
load "stdlib.ring"
print("Hello!")
Listing 4-9

tempo.ring

Then, you can use or , for example, in the text to generate respectively a new line and a tabulation, as shown in Listing 4-10.
load "stdlib.ring"
print("Hello! Dear friend")
Listing 4-10

tempo.ring

This will print the following:
Hello!
Dear friend
Also, you can insert the value of a variable in the text by using #{variable_name}, as shown in Listing 4-11.
load "stdlib.ring"
cName = "Arem"
print("Long live #{cName}!")
Listing 4-11

tempo.ring

You get the following:
Long live Arem!7

Output to a File

Let’s get back to the battle.

Dan created a new file called out2file.ring where he ordered Ring to render the cGraph string inside a text file called graph.txt. After creating this empty text file in the current folder (using the operating system’s File Explorer), he wrote the code shown in Listing 4-12 in the out2file.ring.
write("graph.txt",cGraph) # saving the cGraph in the file
cFile = read("graph.txt") # Reading the file content
? cFile  # Showing the file on screen
Listing 4-12

out2file.ring

Then, he went back to the main northafrica.ring file and said load "out2file.ring" instead of load "out2screen.ring". See Listing 4-13.
// Layer 1
load "txtconnector.ring"           # text data returned in an a[] list
// Layer 2
load "transformer.ring"            # a[] transformed to aData[] list
load "datagraph.ring"              # aData[] mapped in an aDatagraph[]
load "cgraph.ring"                 # cGraph made from aDatagraph[]
// Layer 3
load "out2file.ring"               # can also be "out2screen.ring"
Listing 4-13

northafrica.ring

Do the same as Dan and execute the northafrica.ring file. In the Notepad Output window, you get the chart, but most importantly, you’ll get the physical text file, in the application folder, filled with the same chart as it is shown in my Windows File Explorer, as shown in Figure 4-8.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig8_HTML.jpg
Figure 4-8

The chart rendered in a text file (graph.txt) using the write() function

write() is a powerful function not limited to writing to text files but also to binary files such as EXEs. To make a copy from the ring.exe executable, for example, it is enough to write the lines in Listing 4-14, in an empty tempo.ring file.
try
            cFile = read("c: ingin ing.exe")
            write("ring2.exe",cFile)
            SEE "Copying the binary file done successfully."
catch
            SEE "An error happened: " + NL
            SEE cCatchError
done
Listing 4-14

tempo.ring

Test this. Assuming that your Ring installation was made in the c: ing folder, then ring.exe is copied, and a new EXE file with the same content but with a different name (ring2.exe) is created in the current northafricaApp.ring folder.

Note that we used try/catch statements to capture any error that could happen (in the first block under try) and manage it by ourselves (in the second block under catch) without breaking the program execution. Usually, you would need this when the operation is not managed by Ring but by the system (connecting to a database, reading and writing files to disk, talking to the network, etc.).8

Output to a Database

In the previous chapter, we used SQL to design a TABLE inside a SQLite database, using the CREATE command , and saved some data in it using the INSERT command .

Dan appreciated our work and will do the same. His objective is to store the cGraph string inside a dedicated table in the same northafrica.db database. Then he created a file called out2db.ring with the code shown in Listing 4-15.
// Opening a connection with the db
load "sqlitelib.ring"
oSQLite = sqlite_init()
sqlite_open( oSQLite, "northafrica.db" )
sql = "SELECT * FROM COUNTRY"
aResult = sqlite_execute( oSQLite, sql )
? aResult
Listing 4-15

out2db.ring

In this code, Dan was just testing whether the database exists and contains data. Run the code and observe the result.

The database exists, and the data is stored there. Now, delete these lines because they have widely played their role:
sql = "SELECT * FROM COUNTRY"
aResult = sqlite_execute( oSQLite, sql )
? aResult
To store the cGraph inside the northafrica.db database, the steps are as follows:
  1. 1.

    Design a new TABLE and name it GRAPH.

     
  2. 2.

    Inside the table, create a field called content. This is where the cGraph is hosted.

     
  3. 3.

    If you want, you can add some additional information about each graph by adding other fields: a source field to specify the data source, and a comment field to say something about the graph.

     
The complete content of the out2db.ring file becomes Listing 4-16.
// Opening a connection with the db
load "sqlitelib.ring"
oSQLite = sqlite_init()
sqlite_open( oSQLite, "northafrica.db" )
// Creating the GRAPH table
sql = "
CREATE TABLE GRAPH (
            CONTENT TEXT NOT NULL,
            DATASOURCE TEXT NOT NULL,
            COMMENT TEXT NOT NULL ) ;
);
"
sqlite_execute( oSQLite, sql )
// Storing the cGraph in the table
sql = "
            INSERT INTO GRAPH
            ( CONTENT,
              DATASOURCE,
              COMMENT)
            VALUES
            ( '" + cGraph + "',
            'Wolfram|Alpha',
            'Your comment...');
"
sqlite_execute( oSQLite, sql )
// Reading the graph from the db
sql = "SELECT * FROM GRAPH"
aResult = sqlite_execute( oSQLite, sql )
? aResult[] // Try aResult[1][1][2] to show just the Graph
Listing 4-16

out2db.ring

To test it, you need to activate the main northafrica.ring file and replace load "out2file.ring" with load "out2db.ring" , as shown in Listing 4-17.
// Layer 1
load "txtconnector.ring"           # text data returned in an a[] list
// Layer 2
load "transformer.ring"            # a[] transformed to aData[] list
load "datagraph.ring"              # aData[] mapped in an aDatagraph[]
load "cgraph.ring"                 # cGraph made from aDatagraph[]
// Layer 3
load "out2db.ring"                 # or "out2screen" or "out2file"
Listing 4-17

northafrica.ring

Run the northafrica.ring file. Again, you should be able to see the graph in the Output window of Ring Notepad. In this case, we were successful to help Dan achieve the database option of his design.9

To summarize, three options for rendering the chart are now implemented: screen, text file, and database. Dan’s clean design enables us to navigate from one platform to another simply by specifying the corresponding file in the load command in the main northafrica.ring file: out2screen.ring to show the chart on the screen, out2file.ring to save it in a text file, and out2db.ring to store it in a database.

So nice! Let’s continue.

Output to a Web Page

If you remember, we dealt with printing in a web page in Chapter 1. Remember that when we write the code in Listing 4-18 in an empty tempo.ring file, "Hello, World!" must be replaced by cGrapgh.
load "weblib.ring"
import System.Web
new page { text("Hello, World!") }
Listing 4-18

tempo.ring

If you were Dan, then you would create a new file called out2web.ring and paste the code in Listing 4-19 inside it.
load "weblib.ring"
import System.Web
new Page { text(cGraph) }
Listing 4-19

out2web.ring

This shows the generated HTML code of the page in the Output window. But what we need is a concrete HTML file that hosts the graph, say graph.html. To do so, we need to avoid new Page and replace it with new htmlPage, as shown in Listing 4-20.
load "weblib.ring"
import System.Web
p = new htmlpage { text(cGraph) }
write( "graph.html",p.output() )
Listing 4-20

out2web.ring

Then in the main northafrica.ring file, you will change load "out2db.ring" with load "out2web.ring". Run it. Browse the current NorthAfricaApp folder and find graph.html. Double-click it and see the result in your browser. It works, but it is visually unsatisfying, as shown in Figure 4-9.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig9_HTML.jpg
Figure 4-9

The chart rendered in an HTML page, unorganized!

Maybe we are in trouble!

Don’t panic, this will always happen in the real world every time you use a technology that is new or you try to apply a general solution to a new terrain. In such cases, a kind of technical feasibility study needs to be performed.10

Studying the Feasibility of Printing cGraph in a Web Page

The goal of this study is to identify the source of the problem encountered in the rendering of the cGraph string inside a web page, as observed in Figure 4-9. After understanding what happened, it is required that we assess its impact on the solution we designed.

In fact, in our case, the problem comes from the ignorance by HTML of the new lines (NL) contained in the cGraph string. If we try an example, you can better understand it: in the out2web.ring file (Listing 4-20), replace text(cGraph) with text(c). Before this line, insert c = "Line1". What you get then is Listing 4-21.
load "weblib.ring"
import System.Web
c = "Line1"
p = new htmlpage { text(c) }
write( "graph.html",p.output() )
Listing 4-21

out2web.ring

Run it and refresh (F5) the same web page currently in your browser. Right now it shows “Line1” at the top of the page. What if we change c string to the following:
c = "
Line1
Line2
"
Refresh the page. You see? The new line is completely ignored, as shown in Figure 4-10.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig10_HTML.jpg
Figure 4-10

The new line ignored by the HTML page

Let’s try another option and replace the value of c in out2web.ring with Listing 4-22.
c = "Line1" + NL + "Line2"
Listing 4-22

Part of out2web.ring

It’s the same result.

A possible solution for that is to tell Ring to embrace the HTML standard and use the <div> tag, which helps structure the page in zones or divisions. The default behavior of <div> on all browsers is that it leaves an automatic new line at the end of its content.11 In our case, the tags will just contain one line of text each, as shown in Listing 4-23.
load "weblib.ring"
import System.Web
p = new htmlpage {
            div { text("Line1") }
            div { text("Line2") }
}
write("graph.html",p.output())
Listing 4-23

out2web.ring

Execute it and refresh the browser. Finally, it’s resolved, as shown in Figure 4-11.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig11_HTML.jpg
Figure 4-11

A new line generated in a web page using HTML divs

If you look to the source code of the graph.html file (just click it in the Project Files tree view in Ring Notepad), you will find that the generated HTML code by Ring looks like Listing 4-24.
<!DOCTYPE html>
<html lang="en">
<head>
   <title>Test</title>
   <meta charset='UTF-8'>
   <script
   src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js">
   </script>
</head>
<body>
            <div style="">
                        Line1
            </div>
            <div style="">
                        Line2
            </div>
</body>
</html>
Listing 4-24

graph.html

The limitation of interpreting a new line (NL) inside a string for one of our target platforms (namely, the web target) means that we can no longer rely on cGraph as a reliable intermediate format. A substantial change in the architecture of the app is then necessary!

Yet Another Architecture Refactoring

How are you going to make it, Dan?

Well, like Figure 4-12.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig12_HTML.jpg
Figure 4-12

cGraph is substituted by aDatagraph as an intermediary format

While looking at this new update of the architecture, it’s always good to take a look at its last version; see Figure 4-6. This is useful to capture the rationale of Dan’s thinking. Instead of building, in layer (2), on cGraph string as an intermediate format, we use the aDatagraph list instead so we are free to parse it line by line, in layer (3), and fabricate whatever output format we want: screen, text, database, web, or PDF.

Remember, in the chartRenderer.ring file, that aDatagraph list contains the code in Listing 4-25.
# Reminder: our aDatagraph contains:
            # [ 0, 0, 0, 0, 0, 1 ],
            # [ 0, 0, 0, 0, 0, 1 ],
            # [ 0, 0, 0, 0, 0, 1 ],
            # [ 0, 0, 0, 0, 0, 1 ],
            # [ 0, 0, 0, 0, 0, 1 ],
            # [ 0, 0, 1, 0, 0, 1 ],
            # [ 0, 1, 1, 0, 0, 1 ],
            # [ 0, 1, 1, 0, 0, 1 ],
            # [ 0, 1, 1, 1, 0, 1 ],
            # [ 1, 1, 1, 1, 1, 1 ]
Listing 4-25

Part of chartRenderer.ring

Keeping this matrix in mind, you can easily imagine how every module of the rendering layer (3) can take its own way by parsing the aDatagraph and formatting the output.

Now, create a third copy of the app folder and name it northafricaAppV3. Close all the opened files in Ring Notepad to start with a new and clean base.12

The first thing we should do is purely organizational and will be applied to the main northafrica.ring file . Look at its current structure in Listing 4-26 and tell me what load line should be moved from one layer to another.
// Layer 1
load "dbconnector.ring"
// Layer 2
load "transformer.ring"
load "datagraph.ring"
load "cgraph.ring"   # Do you suspect this line?
// Layer 3
load "out2db.ring"   # or "out2screen.ring" or "out2file.ring"
Listing 4-26

northafrica.ring

Bravo! It’s the line load "cgraph.ring" that should be cut from the // Layer 2 part and pasted at the beginning of the // Layer 3 part. Never underestimate the insistence of Dan to implement these details; trust me, these details are a result of the reflexes of the great software architects. Now, our main file is totally conformant to the logical software architecture designed in Figure 4-12; see Listing 4-27.
// Layer 1
load "dbconnector.ring"
// Layer 2
load "transformer.ring"
load "datagraph.ring"
// Layer 3
load "cgraph.ring"      # the file in the good logical place
load "out2screen.ring"  # or "out2file.ring" or "out2db.ring"
Listing 4-27

northafrica.ring

Once the form is OK, let’s tackle the substance of the problem by implementing the web target correctly.

Correctly Implementing the Web Target

After a hot coffee, on a rainy morning, Dan tells us the following.

All the rendering modules in layer (3) are based on the aDatagraph produced by the transformer in layer (2), right? At the same time, all the rendering modules but out2web.ring13 use the cGraph string generated by the cstring.ring file. Visually speaking, if we put a spotlight on part of the app architecture (go back to Figure 4-12), Figure 4-13 shows what I mean.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig13_HTML.jpg
Figure 4-13

Three modules in layer (3) making use of cstring.ring

Concerning out2pdf.ring, we cannot decide on it now because we didn’t experience it yet. So, we will leave it for later consideration. Let’s concentrate on the web side of our multiplatform rendering service.

Create a new file in the current folder northaAfricaAppV4 and name it out2web.ring.

Logically, the code of out2web.ring is similar to cstring.ring. In fact, we are parsing the aDatagraph line by line and then printing it either in a string variable or in an HTML page. Let’s recall the portion of code of cstring.ring that is responsible for this parsing and printing operation; see Listing 4-28.
// Constituting the graph string cGraph
for y = 1 to len(aDatagraph) // =10
              cGraph += spc(1) + graphline(y) + NL
next y
cGraph += graphlabels()
Listing 4-28

Part of cstring.ring

Then comes the code of the functions used: graphline() , graphlabels() , and spc() .

From a web page perspective, the same logic is used, but the printing occurs inside div tags in the htmlPage. In out2web.ring, write the code in Listing 4-29.
load "weblib.ring"
import System.Web
p = new htmlpage {
            for y = 1 to len(aDatagraph) // = 10
                       div { text( spc(1) + graphline(y) + NL ) }
            next y
            div { text( graphlabels() ) }
}
write( "graph.html",p.output() )
Listing 4-29

Part of out2web.ring

Save the file.

Of course, we need to add the code of the graphline(), graphlabels(), and spc() functions from cstring.ring to the current out2web.ring file so it works. But Dan won’t accept having it duplicated in two files, and he is right: duplication is always a good indication that your system requires a refactoring.

Don’t ask him, I’m sure he will be thinking of it this way: those functions are logically related to the datagraph, and all future modules that call the datagraph.ring file should be able to parse it line by line (using the graphline() function) and read its labels (using the graphlabels() function). Therefore, these must be moved to the bottom of the datagraph.ring file!

What do you think? If you have another strategy, try it. For me, I’ve moved them the Dan way, saved all the files, and tried to switch to the web output by doing these steps:
  • Going to northafrica.ring file

  • Changing load "out2db.ring" (or whatever platform is currently active) to load "out2web.ring"

  • Saving and executing the file (Ctrl+F5)

Doing so, the graph.html file is physically updated in the application folder. There we reach it and open it in the web browser to get the screen shown in Figure 4-14.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig14_HTML.jpg
Figure 4-14

Our graph, timidly displayed in a web page

Intellectually, I may be satisfied, but artistically nope. Let’s change it by using HTML tables, one of the complete set of features supported in Ring by the WebLib library and that enable you to write Ring code as if you are writing a pure HTML file!

Writing HTML in Ring

As you may know, HTML is a declarative language where you compose things to show them in a browser. To make a table, you specify its start and end tags (<table> and </table>), as well as its header (<th> and </th>), rows (<tr> and </tr>), and cells (<td> and </td>).

Ring frees your mind from learning another language while providing you with the same declarative style14 to compose your HTML table. Type Listing 4-30 in an empty tempo.ring file.15
Load "weblib.ring"
Import System.Web
New Page
{
            divstart([ :style = styledivcenter("400px","500px") ] )
               style(styletable() + styletablerows("t01"))
               tablestart([ :id = :t01, :style= stylewidth("100%") ])
                          rowstart([])
                                      headerstart([]) text("Number") headerend()
                                      headerstart([]) text("square") headerend()
                          rowend()
                          for x = 1 to 10
                                      rowstart([])
                                                cellstart([]) text(x) cellend()
                                                cellstart([]) text(x*x) cellend()
                                      rowend()
                          next
               tableend()
            divend()
}
Listing 4-30

tempo.ring

Execute it and observe the HTML output generated by this Ring code in the Notepad Output window. Compare every Ring command to its equivalent HTML tag. Think of it as if Ring were an HTML expert writing a web page for you, while you are describing it in Ring.16

With a minimal effort, you can put the generated code in a text file named test.html and then show it in the browser. Figure 4-15 shows what you get.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig15_HTML.jpg
Figure 4-15

A sample HTML table

This being said, I will leave it to you to do as a personal exercise where the out2web.ring module generates a beautiful HTML table containing the graph that you can display in the browser, centered on the page, similar to Figure 4-16.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig16_HTML.jpg
Figure 4-16

Our graph, faithfully displayed in an HTML table

Waiting for Dan to help you? Don’t bother him please, he has been switched to another task: generating the graph in a printed file (next section).

Output to a Printed File

Today’s printings happen mainly in PDF files. In this section, Dan shows us how to do it in Ring using the RingQt library. Qt was introduced to you in the first and third chapters, so please go back to them if you don’t remember how it works with Ring.

As you see, Dan has created a new file and named it out2pdf.ring . He saved it in the current folder. Inside the file, he tried the code in Listing 4-31.
load "guilib.ring"
// Creating a Qt application to host the PDF preview window
            new qApp {
                        win1 = new qwidget() {
                                    setwindowtitle("Printer Preview Dialog")
                                    setgeometry(100,100,675,480)
                                    printer1 = new qPrinter(0)
                                    show()
                                    // instanciating a Printer (PDF) Preview
                                    oPreview = new qPrintPreviewDialog(printer1) {
                                                setParent(win1)
                                                move(10,10)
                                                setPaintRequestEdevent("printPreview()")
                                    exec()
                                    }
                        }
                        show()
}
// Printing the graph inside the PDF preview
func printPreview()
            printer1 {
                        painter = new qpainter() {
                                    begin(printer1)
                                    myfont = new qfont("Times",18,-1,0)
                                    setfont(myfont)
                                    yy=10
                                    for y = 1 to len(aDatagraph) // =10
                                                yy += 40
                                                drawtext(10,yy, spc(1) + graphline(y) )
                                    next y
                        drawtext(10,yy+40, graphlabels() )
                        printer1.newpage()
                        endpaint()
                        }
             }
Listing 4-31

out2pdf.ring

Executing the code, you will see the screen in Figure 4-17 where you can print the file right to your printer.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig17_HTML.jpg
Figure 4-17

Printing the chart in a PDF file

When RingQt adds support for QChart widgets in the future,17 then come back to this exercise and do a better job of visualizing the generated chart as a native widget. For now, this is an explanation (a short one) of the code in Listing 4-31.

First, a new qApp object is created. This object is made with a window called win1. A virtual printer called printer1 is also created (based on the qPrinter class). The window will contain a widget (qWidget) hosting a printer preview dialog (qPrintPreviewDialog). The preview is stored in an object called oPreview. Its job is to show the content of the printer as constructed in the printPreview() function. exe() and show() are responsible for constructing the window and showing it on the screen.

By the way, the code inside the printPreview() function shows that we have used aDatagraph directly to fabricate the chart in the PDF. That brings us back to Dan’s architecture design to update it, as shown in Figure 4-18.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig18_HTML.jpg
Figure 4-18

The PDF output relies also on the aDatagraph list

There is an additional enhancement you can do, which I’ll leave as an exercise for you.

Look back closely at the code that implements the storage of the cGraph chart inside the database (go back to the file out2db.ring in Listing 4-14), specifically to the lines in Listing 4-32.
// Storing the cGraph in the table
sql = "
            INSERT INTO GRAPH
            ( CONTENT,
              DATASOURCE,
              COMMENT)
            VALUES
            ( '" + cGraph + "',
            'Wolfram|Alpha',
            'Your comment...');
"
sqlite_execute( oSQLite, sql )
Listing 4-32

Part of out2db.ring

You could say the following to Dan:
  • I see that we are storing the cString as is in the database.

  • A database is tabular by design and was meant essentially to store data records and not portions of text.

  • Why don’t we do the same as for out2web.ring and out2pdf.ring and free ourselves from the blindness of a compact string and get the data directly from the official intermediary: aDatagraph?

While you talk, Dan seems convinced. This encourages you to provide your first contribution to the architecture design of the NorthAfricaApp in the form of this change in the application diagram, as shown in Figure 4-19.
../images/485799_1_En_4_Chapter/485799_1_En_4_Fig19_HTML.jpg
Figure 4-19

Your contribution to the enhancement of the app architecture design

What remains is implementing this in Ring by adjusting the necessary code (in the portion listed in Listing 4-32) and adopting the good Loading call flow.

What’s Next

Congratulations. The first two chapters in the “Practical Ring” part (Chapters 3 and 4) are now done.

You are now aware of the inputs and outputs of Ring; you’ve understood them and practiced them. You know how to capture data, store it in the program, and expose it in various forms and on several platforms. You know that architecture is a strategic asset when it comes to coding for flexibility. At the same time, a lot of refactoring has been done to continuously enhance your program. You worked with practical examples, built around a real-world data acquisition and transformation application, called NorthAfricaApp, to see these dynamics in action.

It’s time, then, to look inside the box and discover how Ring applies algorithmic thinking on input data to transform it into outputs. An exciting fifth chapter is waiting for you!

Oh, yes, Tunisia won, for the 10th time, the men’s African Nations title in volleyball for 2019 against Cameroon. You can congratulate me for that, and for another thing also: my cousin Abdessalem Ayouni18 won the gold medal in the 800m at the African champion of athletics held in Rabat, Morocco on August 28th, 2019.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.65.247