Lesson 5. User-defined classes in Parser

In all previous lessons we manipulated classes and objects predefined in Parser, such as class table. This class has its own methods, which we have widely used. The list of all its methods can be found in the reference. Still, if a language doesn't extend beyond basic classes, it may finally become a serious limitation. To satisfy all users' needs we allow them to create their own (user-defined) classes with methods and fields. In this lesson we will create a new class of objects.

Actually, anything may be an object: forum, guestbook, different sections or even entire site. Here we have approached the next stage of structuring-structuring at the level of objects, not methods. What did we do in previous lessons? We just divided separate code pieces into methods and called them when necessary. However, our script could be greatly improved if we included our own objects. For instance, we could create a class
forum and use its methods: "delete message," or "show all messages" and fields, such as "number of messages". By this we provide a modular approach, which is significantly better than just using multiple scattered and unrelated functions: all code and data (methods and fields) are assembled into one whole and used with one certain object, which is "forum". In terminology used by document-oriented programming such an approach is called 'encapsulation.' Moreover, having once created class forum for one project, we can use it for different projects without changing anything in it.

Before we start explaining user-defined classes by the example of guestbook, which we are going to create during this lesson, we would like to remind you of the logic of working with objects. First, we must create an object of a certain class with the help of constructor and then call methods of an object of the class or the fields of the object we have created. When working with user-defined classes, we do just the same, keeping to the same sequence.

Let's again start with determining what we're going to do, since, as we'd say, clearly indicated target is half the battle. Thus, before creating a class we must understand exactly what an object of the class will do (in other words, what methods it will have). Let's assume our methods will: a) display messages in guestbook; b) output a form, which a visitor will need to fill to add a new message; and c) process new message and add it to guestbook. We will store our messages in DB-the same way we did with our news.

While it seems quite clear with methods of a class is quite clear, the essence of constructor remains rather vague. As we know from our previous lessons, to start working with an object we must first create it. Let's use a constructor to create a table with messages which will be further used by the method responsible for showing them.

The task is now clear. Let's now implement it. The first thing we need to do is create table
gbook in DB p3test:

id
int not null auto_increment primary key
author
varchar(255)
email
varchar(255)
date
date
body
text


Now we should get the idea behind such things in Parser as class
MAIN and inheritance. As it has been already said, a class is a unity containing all objects, their methods and fields. Class MAIN combines methods and fields given in auto.p and the requested document (for example, index.html). Each level in directory tree inherits methods given in auto.p files located in parent directories. All these methods, including those given in requested HTML document become static functions of class MAIN while all variables in auto.p files and the requested HTML document become static fields of class MAIN.

   /
   |__ auto.p
   |__ news/
   |   |___auto.p
   |   |___index.html
   |   |___details/
   |       |_______ auto.p
   |       |_______index.html
   |__contacts/ |
            |_______auto.p
          |_______index.html

   

As a user loads
/news/details/index.html, class MAIN will be dynamically combined from of methods given in root directory's auto.p, as well as auto.p files located in /news/ and /news/details/. Methods given in /contacts/auto.p will not be accessible for pages in /news/ and its subdirectories.

It is now clear with
MAIN, but, prior to creating a user-defined class, we should first learn how we can call methods and refer to variables contained in class MAIN from within a user-defined class. Methods of class MAIN are called as static functions:

^MAIN:method[]

while variables, which are fields of class
MAIN, are referred to as static fields:

$MAIN:field

Let's get to practice now. We add to root directory's
auto.p another method which we can use to connect to DB and send an SQL query.

@dbconnect[code]
^
connect[$connect_string]{$code}
# connect_string is defined in method @auto[] and is
$connect_string[mysql://root@localhost/p3test]

We put this method to root auto.p so that the DB server could be easily accessible from any page-methods located in root auto.p will always be inherited. Note: we reserve place for an argument. In our case the argument is one-code, with which we will submit SQL-queries. We can declare more arguments for a method. In this case, we will separate them with semicolon.

Further, we create directory-for instance,
classes-in which we will store our user-defined classes. In this directory we create file gbook.p (we advise you to store user-defined classes in files with name extension .p) and put into it to it the following code:

@CLASS
gbook

@load[]
^MAIN:dbconnect{
   $messages[^
table::sql{select author, email, date, body from gbook}]
}

@show_messages[]
^if($messages){
   ^messages.
menu{
      <table width="100%">
         <tr>
         <td align="left"><b>$messages.
author
            ^if(def $messages.
email){
               $messages.email
            }{
               No e-mail address
            }
</b>
         
</td>
         <td align=
"right">$messages.
date</td>
         </tr>
      </table>
      <table width="100%">
      <tr>
         <td>
$messages.
body</td>
      </tr>
      </table>
   
}[
<table width="100%" border="0" bgcolor="000000" cellspacing="0"> 
        <tr><td>&nbsp^;</td></tr>
   </table>
]

}{
   Guestbook is empty.
}

@show_form[]
<hr />
<br />

$date[^
date::now[]]
<center>
<form method=
"POST"
>
<p>
Author<sup>*</sup><input name="author"><br
 />
E-mail&nbsp;&nbsp;<input name="email"><br
 />
Text<br />
<textarea cols="50" name="text" rows="5"></textarea>
</p>
<p>
<input type=
"submit" value="Send" name="post" />&nbsp;&nbsp;&nbsp;
<input type=
"reset" value="Cancel" />
</p>
</form>
</center>

@test_and_post_message[]
^if(
def $form:post){
   ^if(def $form:author){
      ^MAIN:dbconnect{
         ^
void:sql{insert into gbook
            (author, email, date, body)
         values (
            '$form:author',
            '$form:email',
            '${date.year}-${date.month}-${date.day}',
            '$form:text'
         
)}
      }
      $
response:location[$request:uri]
   }{
      <center>Field 'author' must be filled in</center>
   }
}

Look at the code. In first line we indicate that this is a file with user-defined class:

@CLASS

If you need to use some other user-defined class as a parent class
, you should connect to it and declare it as a base class. In this case, you will have such a construction:

@CLASS
name of the class


@USE
file of parent class


@BASE 
name of parent class
   

In the line following
@CLASS we write the name of our class-gbook. You should remember that Parser is case-sensitive, so gbook and Gbook are different names. The name of the class doesn't have to be the same as the name of the file it is stored in. Moreover, you can use any non-Latin characters for your names (for example, Cyrillic).

Further in the code, we define methods of the class. We do it the same way we defined usual methods in previous lessons.

The first method,
load, will be constructor of our class. We should remember that the purpose of a constructor is to create an object. Moreover, it can also declare variables and assign values to them. These variables are fields of an object of user-defined class. In our case, by using constructor sql of class table, we create a table. Note: in the methods of the new class we freely use methods of system classes and method dbconnect of class MAIN:

@load[]
^MAIN:dbconnect{
   $messages[^table::sql{select author, email, date, body from gbook}]
}

As it has already been mentioned, if we want to use methods of a class beyond it, we should specify what class we use:

^class_name:method[properties]
$class_name:variable

and if the class we use is yet another user-defined class, we should add the following construction to the beginning of the code:

@USE
file of parent class

Such a construction allows us to use module stored in another file. The description of how Parser works with paths can be found in Attachment 1.


So, our new constructor will create table with messages connecting to a specified DB. Now that it is clear with the constructor, we will need to define methods of the new class. Method
show_messages outputs messages contained in table gb created in method load. We go through the table, line by line, with the help of method menu of class table, which we have already used previously. There is nothing new in other methods, either:

show_form-outputs form to add a new message

test_and_post_message
-checks if button post was clicked, if field author was filled in and, if all conditions were met, adds a new entry to DB using method dbconnect defined in class MAIN.

By this we finish creating user-defined class
gbook. All we need to do now is tell Parser on what page we are going to use it. We do it by writing in the first line of /gbook/index.html:

@USE
/classes/gbook.p

Now we can create object of class
gbook and use its methods within this page. We will do it in the main information part:

@body_main[]
Parser3 Example: Guestbook<br />
<hr />

$gb[^gbook::load[]]
^gb.show_messages[]
^gb.show_form[]
^gb.test_and_post_message[]

# and, of course, we shouldn't forget about other parts
@greeting[]
Leave your mark on history…

@body_additional[]
Chronicles…

In this piece we use an object of newly created user class the same way we use any other object: we create it by using constructor of the class and then call methods defined in the new class. See how gracious the solution turned out to be: our code is clearly readable and, looking at this piece, we instantly understand what it does. Everything related to our guestbook is located in a separate file where we list all of its opportunities. If we need a new method to use with our guestbook, we will just need to add it to
/classes/guestbook.p. Everything can be easily enhanced and it doesn't take much to understand what to change and where, if we need to.

In conclusion, it should be noted that we would better place methods like
dbconnect somewhere beyond class MAIN (so that MAIN wouldn't be overloaded with methods). Such a solution would also make the whole project easier to read and understand. We can make methods of this class available by adding construction

@USE


wherever we'd need to use it.

Let's sum it up,


What have we done?
We have created a user-defined class and guestbook for our site based on the class we have made.

What have we learnt?
·Class MAIN;  
·how to create a user-defined class;  
·how to pass arguments to a method.  

What should we remember?
Classes are the "top level" of structuring. That is why we should always aim at dividing our code into classes. By this, you can make the logic of our projects' work most comprehensible and our further work-most comfortable.

What's next?
By this, we have finished our exemplary site. Of course, it is not perfect and shouldn't be used as it is now. Before we place it in the Internet, you still have a couple of things to do: enhance our calendar in news section, teach our guestbook to check whether messages posted by visitors are correct, etc., but we didn't target at making up a full-scale site. We just wanted to show that Parser is an easy tool to increase your productivity. Now, that you have acquired all basic skills required for full-range work, you just need to reinforce them. Now you have all necessary knowledge to do the whole rest of work by yourself. Remember, "practice makes perfect."

Good luck!



Copyright © 1997–2021 Art. Lebedev Studio | http://www.artlebedev.com Last updated: 21.09.2007