FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index mod_harbour Using source on other .prgs
Posts: 990
Joined: Thu Nov 17, 2005 05:49 PM
Using source on other .prgs
Posted: Fri Jul 16, 2021 01:01 PM
Hello Mod-harbourers;

I have started using ModHarbour but so far not so much to build html pages but to trap webhooks. A webhook is a POST request sent to a page from services to notify or transmit certain data.

There is something I still can't figure out about Modharbour. It is the use of other functions and classes on other source files from my Index.prg source.

For example, my code below receives POST data from a service call Twilio. Anytime someone texts my Twilio phone number, this code receives and processes POST data from Twilio that contain all the SMS text information.

Notice how on this code I'm using functions like Connect2DD() and LogData() and ExecuteSQL(). These are my own functions. In order to be able to use them I find I have to include all their source into Index.prg. I want to avoid that.

Is there a better way to link other code, classes, and functions and use them on my Index.prg? Can I use my own created classes like TAdsQuery() that contain many data and methods without having to add all this code into Index.prg?

Code (fw): Select all Collapse
#include "\harbour\include\fileio.ch"
#include "\harbour\include\ads.ch"


#define CRLF Chr(13)+Chr(10)
#define MAX_LOG_SIZE 65536
#define LOG_DEBUG .T.
#define DEBUGFILE "trace.log"

function Main()
   Local h := AP_PostPairs()
   Local i, cTel, cConfirmCode
   Local cSql, lConfirmed

   LogData( DEBUGFILE, { "Twilio Page hit with", AP_Method() }, MAX_LOG_SIZE )

   For i := 1 To  LEN( h )
      LogData( DEBUGFILE, HGetPairAt( h, i ), MAX_LOG_SIZE )
   Next

   if AP_Method() == "POST" .AND. ; 
        hHasKey( h, "MessageSid") .AND. ;
        hHasKey( h, "AccountSid") .AND. ; 
        hHasKey( h, "From") .AND. ; 
        hHasKey( h, "Body") .AND. ;
        ;//At( ALLTRIM ( h[ "Body "] ),  [1,2] ) > 0  .AND. ;
        ( AdsGetConnectionType() != AE_NO_CONNECTION .OR. Connect2DD() )


      cConfirmCode := AllTrim( hb_ValToStr(  Val(  Left( AllTrim( h[ "Body" ] ), 1 ) ) + 1 ) )
      cTel         := StrTran( AllTrim( h[ 'From'] ), "+1", "" ) //remove +1 from tel numb

      lConfirmed := isConfirmed( cTel )
      //we need to first check if appntment hasnt been confirmed already.
      //patients sometimes send a 1 and then follow with more texts.
      //if appntmnet is already confirmed, should we ignore further replies...?
      //+1 was added to cConfirmCode
      if cConfirmCode != '2' .AND. cConfirmCode != '3' .and. !lconfirmed 
            cSql := "INSERT INTO SMSMessages( celnumber, Text ) Values( '$1$', '$2$' ) \n"
            ExecuteSQL( cSql, ADS_ADT, { cTel, "Este sistema automatizado solo entiende 1 para confirmar o 2 para cancelar su cita. Para preguntas marque 787-751-1487" } , LOG_DEBUG )

      elseif lConfirmed //once confirmed stop texting me!  call the office insetad.
         cSql := "INSERT INTO SMSMessages( celnumber, Text ) Values( '$1$', '$2$' ) \n"
         ExecuteSQL( cSql, ADS_ADT, { cTel, "Este sistema automatizado ya tiene su cita confirmada o cancelada previamente. Para preguntas marque 787-751-1487" } , LOG_DEBUG )

      else        //it hasn't been confirmed and a 1 or a 2 was received as expected.

         cSql := ; 
               "DECLARE tbl CURSOR ;                  \n"+;
               "OPEN tbl AS SELECT Top 1 guid, service, mrec, StartTime \n"+;
               "              FROM $3$  \n"+;
               "             WHERE tel = '$2$'        \n"+;
               "               AND starttime > Now()  \n"+;
               "               AND ( confirmed is null or confirmed= 1 or confirmed > 3 )\n"+;
               "          ORDER BY StartTime ;        \n"+;
               "                                      \n"+;
               "IF FETCH tbl then                     \n"+;
               "                                      \n"+;
               "   UPDATE $3$              \n"+;
               "      SET confirmed = $1$                \n"+;
               "    WHERE tel = '$2$'                    \n"+;
               "      AND CONVERT( starttime, SQL_DATE ) = convert( tbl.starttime, SQL_DATE );\n"+;
               "                                      \n"+;
               "end;                                  \n"+;
               "CLOSE tbl ; \n" 
                    
         ExecuteSQL( cSql, ADS_ADT, { cConfirmCode, cTel,'sfi_msk_appntmnts' }, LOG_DEBUG  )
         ExecuteSQL( cSql, ADS_ADT, { cConfirmCode, cTel,'mo_appntmnts' }, LOG_DEBUG  )
         ExecuteSQL( cSql, ADS_ADT, { cConfirmCode, cTel,'appntmnts' }, LOG_DEBUG  )
         ExecuteSQL( cSql, ADS_ADT, { cConfirmCode, cTel,'degetau_appntmnts' }, LOG_DEBUG  )

         if cConfirmCode = '2'   //.and. !isConfirmed?
            cSql := "INSERT INTO SMSMessages( celnumber, Text ) Values( '$1$', '$2$' ) \n"
            ExecuteSQL( cSql, ADS_ADT, { cTel, "Es importante llegar 15 minutos antes de la hora de su cita." } , LOG_DEBUG )
         endif 

      endif 

      AdsDisconnect()

   endif

return nil
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Using source on other .prgs
Posted: Sat Jul 17, 2021 10:05 PM
Dear Reinaldo,

mod_harbour provides "meta commands" which are instructions that will be executed before running the requested PRG

In example:

Your code ...

at the bottom of it:
// {% LoadPrg( "extra1.prg" ) %}
// {% LoadPrg( "extra2.prg" ) %}

Such code will be identified by mod_harbour and macro expanded. function LoadPrg() is quite simple:

Code (fw): Select all Collapse
static aPRGs := {}

function LoadPrg( cPrgName )

   local cCode := MemoRead( hb_GetEnv( "PRGPATH" ) + cPrgName )
   local nLen  := Len( hb_ATokens( cCode, Chr( 10 ) ) )
   local nAt

   if Len( aPRGs ) == 0
      AAdd( aPRGs, { AP_FileName(), ProcLine( 2 ) } )
   endif   

   if AScan( aPRGs, { | aPRG | cPrgName == aPRG[ 1 ] } ) == 0
      AAdd( aPRGs, { cPrgName, ATail( aPRGs )[ 2 ] + nLen } )
   else
      cCode = ""   
   endif   

return cCode

function GetPrg( nLine )

   local aPrg := { AP_FileName(), ProcLine( 2 ) }

   if Len( aPrgs ) > 0
      nAt = AScan( aPRGs, { | aPrg | nLine <= aPrg[ 2 ] } ) 
      if nAt != 0
         aPrg = aPrgs[ nAt ]
      endif   
   endif     

   aPRGs = {}

return aPrg


The above code must be included into mod_harbour. If you don't want to rebuild mod_harbour then do it this way:

// {% hb_MemoRead( hb_GetEnv( "PRGPATH" ) + "extra1.prg" ) %}

The advantage of using LoadPrg() is that GetPrg( nLine ) will provide you the right PRG name when there is an error in your code
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 990
Joined: Thu Nov 17, 2005 05:49 PM
Re: Using source on other .prgs
Posted: Sun Jul 18, 2021 02:47 PM

Hello Antonio and all other Mod_harbour'ers;

If I am to rebuild mod_harbour; would it make sense to also include some of my most frequently used code as libs linked to libharbour.dll?

Should I be concerned about speed degradation if I do that?

Thank you,

Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Using source on other .prgs
Posted: Sun Jul 18, 2021 03:11 PM

Reinaldo,

> If I am to rebuild mod_harbour; would it make sense to also include some of my most frequently used code as libs linked to libharbour.dll?

Yes, you can include any library in modharbour.hbp just remember to add this code in apache.prg for each library that you want to include:

define __HBEXTERN__HBTIP__REQUEST // replace HBTIP with your lib name

include "../../harbour/contrib/hbtip/hbtip.hbx" // generated by hbmk2

hbx files are generated by hbmk2 when you build your lib using hbmk2:
-hbx=yourlib.hbx

This asures that all the symbols in each library will be linked and fully available from mod_harbour.
Please review any hbx file like "../../harbour/contrib/hbtip/hbtip.hbx" as you can also create them manually using your source code editor.

> Should I be concerned about speed degradation if I do that?

not at all. Speed will be practically the same.
Just a very large libharbour.dll could slow down just a little bit because it is copied on each request. But for a few libs it is not noticeable :-)

regards, saludos

Antonio Linares
www.fivetechsoft.com

Continue the discussion