In this post we will be covering MOCA local syntax advance operators and get a better understanding of how context works. This will help you develop more robust mcmds and control the expected behavior of local syntax. This post doesn't cover what MOCA and local syntax is which can be found in:
<MOCA 101> for more information.
MOCA is RedPrairie proprietary server technology and uses a scripting language called
local syntax. The code pattern for this language is quite unique due to the nature of how code actions works. The paradigm of thinking how to develop local syntax components differs from normal languages like:
JAVA, C#, etc... The problem with local syntax is that people only learn the basics of the language which is to pipe ( | ) one action to another, and disregard the other constructs to build correct programs.
Simple example command:
list orders where ordnum = 'ORD123'
|
list order lines where ordnum = @ordnum
Not understanding how context works and what really is happening can be very dangerous. A badly coded component can either create performance issues or even worst it could execute over an unintended context.
Let's start with
what is "Context"? I define context as the current knowledge your program has and actions is going to execute over it. For every action there is a current set of variables and values, and that's your current context. As actions execute, the context changes. The looping nature of MOCA local syntax means that if you are piping a result set into a command, each iteration (or record piped in) will be it's own context.
But that's simple... and I think most developers understand it. The trick (or advance) comes into how to control context in scenarios where the normal pipe ( | ) controller is not enough. For this you need to understand the advance features of MOCA local syntax and how each construct affects context execution.
In this post we will cover the following control mechanisms:
- & - (ampersand): combines result sets.
- >> - (redirection): stores a current result set into a variable
- {} - (curly-brackets): creates new context.
- ; - (semi-colon): breaks current context.
- @variable - variable operators
& - (ampersand): combines result sets
Let's start easy... The & (ampersand) is just a control operator for combining result sets. This is very handy "union" operator for MOCA. What needs to be guaranteed is the results sets for A & B have the same structure. Meaning the fields and types are the equivalent plus they are in the same order.
list orders where ordnum = 'ORD123'
&
list orders where ordnum = 'ORD456'
| wh_id |
client_id |
ordnum |
ordtyp |
| WMD1 |
---- |
ORD123 |
CUSTOMER |
| WMD1 |
---- |
ORD456 |
CUSTOMER |
>> - (redirection): stores a current result set into a variable
The redirection of a result set into a variable is a handy operator. Some mcmds can process these type of complex variables and become useful for post-processing or combining results.
[select distinct ordnum
from ord_line
where prtnum = 'SOMEPART'
and prt_client_id = '----'
and wh_id = 'WMD1'
order by ordnum] >> res
|
convert column results to string
where res = @res
and colnam = 'ordnum'
and separator = ','
| result_string |
| ORD001,ORD002,ORD003 |
{} - (curly-brackets): creates new context
The way to understand context is to compare it against the concept of "scope" in normal programming languages. The current context is the set of variable and values your actions are executing upon. This is why is so important to understand how context behaves. Because is not just about the context you have for the current action, but also the context you publish out for the next actions.
The
#1 rule you need to know about context is that only your last result will get published out ALWAYS. All internal context will not survive and make it into an exterior context. Think of this as each mcmd you execute runs within their own context and nothing you do inside of it gets published out except the last result. Also keep in mind that every time you open a
new context {} (curly-brackets), all the current context will be piped into this new context.
The best way to show this is with examples...
First let's use a simple case where
@a gets piped into a new context and the inner context has visibility at the values from it's exterior context. Yet, the result of the inner context gets published out where
@a has a value of a=1.
publish data where a = 1
|
{
publish data where a=@a and b=2
}
Now let's look at a case where inner context doesn't makes it out. In this example you can see how
@a gets overwritten and the value is a=2, not a=1. Also did you see
@b? The value of
@b is not published out because is not the last result of the inner context.
publish data where a = 1
|
{
publish data where b = 2
|
publish data where a = 2
}
|
publish data where a = @a and b = @b
Changing the case a little, you can see that
@a is NOT overwritten and the value stays as a=1. Also see how
@b now gets publish out of the context as well.
publish data where a = 1
|
{
publish data where a = 2
|
publish data where b = 2
}
|
publish data where a = @a and b = @b
On an interesting note... when you do an
if (@clause) statement, the {} curly-brackets for the if statement causes a new context and only the last result gets published out.
publish data where a = 1
|
if (@a=1)
{
publish data where a = 2
|
publish data where b = 2
}
|
publish data where a = @a and b = @b
After you start using new context correctly in your code, you will realize the following advantages:
- Code behavior will be more predictable and you can control better the (IN/OUT) values.
- New context behaves as piping into a mcmd. Meaning that you can develop a complex command in one long mcmd script, and then safely turn those contexts into mcmds themselves for better maintenance.
- Actually you can control multiple actions over a common context. See more below.
; - (semi-colon): breaks current context
The
; (semi-colon) is the operator for "breaking" current local context. When context gets "broken", all the variables above gets "trashed" so you can start with a new context. You can imagine the following independent sequences: CMD1; CMD2; ETC; All these actions execute independent from each other.
But that's not really the power of this operator. Instead this operator becomes very powerful when you need multiple actions over a common context. Like always, examples are the best!
In the code below we get the orders to process from the the first command. The processing needs to execute different actions over the published context of "get rush orders...". As you see in the sub-sequence actions, we need to first notify the host and then print the label.
get rush orders for processing where wh_id = @wh_id
|
{
notify host where ordnum = @ordnum
;
print label where ordnum = @ordnum
}
|
publish data where ordnum = @ordnum
So why use ; (semi-colon) and not | (pipe)? Well, because the results published of "notify host" could potentially change the expected behavior of "print label". And really your code for "print label" needs to use the context of the "get rush orders".
Dissecting the code above, when you "break context"
it really only breaks the current local context. For that reason after "get rush orders..." we open a new context. This new context will operate over the results piped in. As execution flows in the inner context and you break it, the next action will start with the same context piped in. Which in this case is the row(s) from "get rush orders...".
Other details to notice of this example are the following. The last publish statement is there just to make obvious that breaking context only affect local context. So the @ordnum variable publish from "get rush orders..." will still be available. Another detail to be careful is the result set of "print label" does make it out of the local context since it is the last result. For such case you probably want to add "; noop" or redirect the result to ">> trash".
@variable - variable operators
In MOCA we have special variable operators which manage how values get substituted. I am just presenting them quick for completion of this post. (Thanks
Yang Ren @LinkedIn)
- @variable: normal variable
- @+variable: optional variable
- @@variable: environment variable
- @%variable: like operator variable
- @*: pull all variables from context
- @-variable: force substitution
- ^aliasname: variable value is "alias" as..
Below are examples where you can see how the operators work base on having the variable on context or not. It shouldn't be that hard to follow, but ask if you have questions.
LocalSyntax with variables in context
publish data where ordnum = 'JON%' and exactorder = 'JONORD005'
|
[select * from ord
where
(ordnum = @ordnum
or ordnum like @ordnum
or @%ordnum
or @+ordnum
or @+ord.ordnum^exactorder
or ordnum = @-ordnum
)
and wh_id = @@wh_id
and rownum < 10
and @*] catch(-1403)
LocalSyntax with variables in context - SUBSTITUTION
select * from ord
where
(ordnum = 'JON%'
or ordnum like 'JON%'
or ordnum LIKE 'JON%'
or ordnum = 'JON%'
or ord.ordnum = 'JONORD005'
or ordnum = 'JON%'
)
and wh_id = 'SBN01'
and rownum < 10
and 1=1
LocalSyntax NO context
[select * from ord
where
(ordnum = @ordnum
or ordnum like @ordnum
or @%ordnum
or @+ordnum
or @+ord.ordnum^exactorder
or ordnum = @-ordnum
)
and wh_id = @@wh_id
and rownum < 10
and @*] catch(-1403)
LocalSyntax NO context - SUBSTITUTION
select * from ord
where
(ordnum = NULL
or ordnum like NULL
or 1=1
or 1=1
or 1=1
or ordnum = NULL
)
and wh_id = 'SBN01'
and rownum < 10
and 1=1