by Unlicensed user
As Clarion developers we (well, most of us) live and die by embed code. Embeds are, after all, at the heart of Clarion's effectiveness. They're the primary way we add custom code to applications that are otherwise template-generated.
...
But as useful as the Clarion architecture is, it has a problem. And that problem is going to grow in size as your applications grow in size and complexity.
The problem is embeds.
Not that there are embeds. We'd be lost without them. Embed points make it possible to plug our custom code into the almost any location within the standard Clarion architecture.
The problem is that embeds are almost always misused.!
Example #1: The Invoice app
...
Extracting business logic
My task , in this series of articles , is to explore the ways that business logic can be teased out of the embed point and put into some code that's easy to understand, easy to maintain and modify, and easy to test.
Of course , I'll still need those embeds so I can call that business logic, wherever it ends up. But the point of this exercise is to figure out how business logic can be coded for maximum benefit. Stuffing it all in embed points just has too many drawbacks.
...
There are just five embeds in the parser procedure. First, there's some global data:
Code Block |
---|
! Queue to hold TXA list
txaq QUEUE(File:queue),pre()
END
state LONG(0)
LineNo long |
Then there's a line of code in the Init method to load up the queue of TXAs to import:
Code Block |
---|
!Get all files and directories
DIRECTORY(txaq,'*.TXA',ff_:NORMAL) |
There's one small routine that gets called regularly by the main import code, and which is really just a bit of debugging code:
Code Block |
---|
CheckForMissedEmbed routine
if sub(txt:rec,1,8) = '[SOURCE]'
db.out('*** MISSED EMBED at line ' & lineno |
& ', state ' & state & ' ***')
end |
There's an embed for variables used in the parsing code:
Code Block |
---|
x long
vars group,pre()
procname string(200)
procFromABC string(60)
procCategory string(60)
embedname string(60)
embedPriority long
embedParam1 string(200)
embedParam2 string(200)
embedParam3 string(200)
whenLevel byte
end
LastProcName like(procname)
lastEmbedName string(500)
currembedname string(500) |
And finally there's one very large block of code to import the TXAs, which I won't reproduce in its entirety. It's basically a state engine that process the TXA in a loop and figures out where the embeds are and what they contain:
Code Block |
---|
access:TextFile.Open()
Access:TextFile.UseFile()
set(TextFile)
state = 0
lineNo = 0
clear(procname)
clear(lastprocname)
clear(lastembedname)
clear(currembedname)
LOOP
next(TextFile)
if errorcode() then break.
lineNo += 1
CASE state
OF 0 ! search for the start of a module or procedure, or an embed
if sub(txt:rec,1,11) = '[PROCEDURE]'
clear(vars)
state = 10
elsif sub(txt:rec,1,8) = '[MODULE]'
clear(vars)
procName = '[MODULE]'
elsif sub(txt:rec,1,7) = 'EMBED %'
embedName = sub(txt:rec,7,len(txt:rec))
state = 30
elsif sub(txt:rec,1,8) = '[SOURCE]'
state = 50
end
OF 10 ! get procedure name details
! OF cases for a bunch of other states
of 50 ! Add the procedure and embed records
if sub(txt:rec,1,8) = 'PRIORITY'
embedPriority = sub(txt:rec,10,len(txt:rec))
if lastprocname <> procname
clear(EMP:record)
EMP:Proc = procname
EMP:ProcFromABC = ProcFromABC
EMP:ProcCategory = ProcCategory
EMP:EmbedAppID = EMA:EmbedAppID
Access:EmbedProc.Insert()
lastprocname = procname
end
! Add the embed record
currEmbedName = clip(embedName) & clip(embedparam1) & |
clip(embedparam2) & clip(embedparam3) & embedpriority
if currEmbedName <> lastEmbedName
! Add the embed record yada yada
end
state = 51
end
! OF cases for a bunch of other states
END
end
?progressVar{prop:progress} = records(txaq)
setcursor()
Access:TextFile.Close() |
There's a ton more code, but you get the idea. This code is completely dependent on a specific database structure, and it references a control on the current window. It's sprinkled throughout a bunch of generated code. Those are all problems, to some degree. But mostly it's just hard to reuse this code somewhere else.
...
Which approach did I choose? Tune in next time to find outRead on....