[Omnis-Newsletter] Omnis Technical Newsletter

omnis-news-admin@omnis.net omnis-news-admin@omnis.net
Wed, 3 Oct 2001 16:33:41 +0100


Omnis Technical Newsletter October 3rd, 2001

========================================

UNSUBSCRIBE OPTIONS: You have been sent this email because you have directly
signed up for, or expressed an interest in receiving a technical newsletter
when you downloaded an evaluation of Omnis Studio, or registered the Lite
version of Omnis Studio. If, however you feel you have received this email
in error you can unsubscribe as well as change your subscription options at
www.omnis.net/newsletter.

N.B.  If you subscribed by checking a box on one of our forms, you will not
have received a password. You will need to submit your email address at
www.omnis.net/newsletter and select the 'email me my password' option on the
next page in order to receive this.


========================================

WELCOME!

Welcome and thank you for subscribing to the Omnis Technical Newsletter.
Published fortnightly, it is intended for Omnis developers of all levels and
experience, for those people evaluating Omnis Studio, or for developers
moving from a similar tool. We think you'll find the content both
interesting and useful for your Omnis development needs and hopefully it
will help you become more productive in Omnis application design.

In this newsletter, David Swain shows how you can use Lookup Channels to
store and retrieve 'static' data in your application, which is enabled using
the native Omnis database engine, a standard 'built-in' part of the Omnis
Studio product. Regular subscribers to the Omnis Technical Newsletter will
notice the absence of Geir Fjaerli who is currently in the US working on an
Omnis development project but will return to these pages in our next issue.
You may find it easier to work through the exercises and examples in this
newsletter by printing it out before you begin.

CONTENTS:
-About the Author
-Lookup Channels, By David Swain
-Omnis at SYSTEMS 2001 in Munich
-Developer Resources on the Omnis web site
-Copyright and Unsubscribe details


========================================

About the Author

David Swain is the founder and president of Polymath Business Systems, for
many years a leading provider of Omnis training. His expertise in Omnis
programming and his ability to make complex concepts understandable are
recognized throughout the worldwide Omnis community.


========================================

Lookup Channels
By David Swain, Polymath Business Systems Inc
Email: dataguru@polymath-bus-sys.com
Web: www.polymath-bus-sys.com

----------------------------------------------------------------------

In the last issue I described one rather obscure but useful Omnis feature
with the name "lookup". There is another equally obscure and useful feature
with a similar name that we will examine here. Although this feature makes
use of the native Omnis data storage technology, it makes some techniques
possible that should be of interest to SQL and D3/mvDesigner programmers as
well as those who use the native Omnis datafile.

Background

There are a number of facts about Omnis that contribute to the use and the
power of Lookup Channels. Since not all Omnis Studio programmers know about
the native Omnis data storage technology these days, a little history and
background information are in order.

First, Omnis Studio has a powerful and robust database engine that is
especially good at performing "exact match finds" or "distinct selects"
based on a specified index value.

When lookup channels were first introduced with Omnis 7 in 1992, we could
only have one open datafile (and only one open library for that matter) at a
time. Lookup channels allowed us to have read-only access to additional
datafiles. Although there are no such limitations today, some other more
modern aspects of Omnis Studio continue to make lookup channels worth
understanding.

An Omnis Studio library file is simply a special case of an Omnis Studio
datafile. This means that besides the "data" that is contained in the
Classes of our libraries, we can also store "reference data" for our
applications in File Slots within a library. For this specific technique, we
are talking about static data that isn't likely to change over time - or at
least won't change faster than we can deploy updated libraries to our users.
For example, we could use this to store valid State, Province and/or Country
Codes for data verification rather than clutter our production database with
such trivia.

There are other techniques that can be used when our data repository is also
a native Omnis datafile, but let's focus on this more universal example for
right now. Let's first assume we already have such a Slot in our library and
describe how we get to it and how we might use it. If you are convinced this
might be worth doing, we'll explore how you actually get the data into the
library later in this article, in case you haven't used native Omnis data
storage techniques before.

Setting Up the Channel

To establish a lookup channel, we use the "Open lookup file" method command.
If you're poking around in the commands list of the Method Editor, it's in
the "Data files" command group. Although there is only one entry box in the
details area of the Method Editor for this command, there are four
parameters for the command in its most general form. Also, these parameters
must be delimited using forward slash ("/") characters, not commas.

The entry field for these parameters expects to see a literal value, so
quotes are not used unless one of the parameter values actually contains a
quote character (an unlikely situation). If we wish to derive a parameter
value from a variable or an expression, we can use square bracket notation
within the string. The expression enclosed in the square brackets is then a
"live" expression, so Omnis Studio will perform syntax checking on the
expression as we attempt to leave the parameter entry field.

The first parameter is the name we wish to give the channel. This parameter
is optional if we only intend to have one lookup channel open, but it is
good form to name the channel anyway. This name then serves as a reference
or an "alias" to the information contained in the other parameters. If our
library executes the "Open lookup file" command specifying a name that is
already in use, the lookup channel with that name is redefined using the new
set of parameters. This name is not case sensitive.

The second parameter is the path to the Omnis datafile containing the data
we wish to access using the lookup channel. If only a partial pathname is
given (such as only the name of the datafile), Omnis follows slightly
different rules for locating the file depending on the operating system
under which it is running. (These rules are given in the Reference Manual so
I won't repeat them here.) If a complete pathname is given, that name is
immediately used. In our example, the easiest thing to do is to use the
function sys(10) in square bracket notation. This is the complete pathname
of the current library in the proper syntax for the current operating
system. The pathname is also not case sensitive.

The third parameter is the name of the File Slot in the target datafile that
we wish to access using the lookup channel. This name IS case sensitive, so
care must be taken to spell and capitalize it correctly. It is important to
remember that a File Class with the same name as this File Slot is not
needed in the library executing this code. Only the library that is used for
managing the contents of this data requires the File Class. (This is further
explained below.) It is also important to note that we can establish more
than one lookup channel to the same File Slot in the same datafile if we
need to.

The final parameter is the NUMBER of the field or column within the
specified File Slot that we wish to use as our "lookup index". This
indicates an interesting thing about Omnis datafiles: Each column in a File
Slot, though created from a File Class, knows nothing of the name of its
associated data-bound variable from that File Class. The File Slot contains
only the data description (data type and subtype) of each column and the
column number. It also knows whether the column is indexed. For this command
to execute without error, only the number of an indexed column can be used
here. For this command to execute with precision, this column should be
uniquely indexed; otherwise only the first record with a specified value for
this index will be accessible.

The final parameter is optional. If it is not supplied, the first column in
the File Slot will be used as the index. If that column is not an indexed
column, a runtime error will be generated as mentioned below.

If we choose to omit an optional parameter, we should still include the
delimiting forward slash characters in the parameter string.

Suppose we have organized our File Class as follows:

Name: stateFile

1    seqno    Sequence
2    code     Character  2   Indexed
3    name     Character 25

A Sequence-type column is an internal record pointer and is intrinsically
indexed by default. Each record is numbered sequentially as it is stored.
This value exists in a record whether or not we provide a column for it, but
such a column gives us a convenient "handle" on a record that we can use to
advantage occasionally. I will use this column in a related technique later
in this article.

If we wish to set up a lookup channel named "states" to the "code" index of
this File Slot stored in our library, we would issue the following command:

Open lookup file {states/[sys(10)]/stateFile/2}

The channel is now open and ready for use. A lookup channel is global in
scope, so it can be used anywhere within an application once established. We
can emulate more restrictive scopes as follows:

The "Open lookup file" command is reversible. We can open a lookup channel
in such a way that it only exists locally for a method by executing the
command in a reversible block at the beginning of that method. Of course, it
is still accessible from any subroutine called by that method, so it is not
"local" in the strictest sense. But it is automatically closed when the
method completes execution, so it's existence parallels that of the method.

Similarly, we can open a lookup channel in such a way that it only exists
for the life of an instance by executing the "Open lookup file" command in a
reversible block in the $construct method of an instantiable class. But that
channel can still be used from anywhere within Omnis until the instance that
opened it closes.

If we need to close an established lookup channel under other circumstances,
we use the "Close lookup file" command and specify the name of the channel
to be closed. If no name was used to open the channel, no name is required
to close the channel.

The "Open lookup file" command also affects the Flag. If the pathname given
in the second parameter cannot be found, the Flag is cleared to kFalse. On
the other hand, if the File Slot name is misspelled or the index column
number is not valid, an Omnis error is generated as follows and execution is
halted:

E102890: Open lookup command without a valid index number

Of course, you will catch this problem during testing so your users will
never see this, right?

Retrieving a Value

Once a lookup channel is established, we access it using the lookup()
function. Unlike a "Single file find" or most select statements, this
function retrieves a single value rather than an entire record image. (Nor
does it affect the Current Record Buffer for all you Omnis native users.)
This description indicates that we must somehow specify which record we want
to access and which column's value we wish to retrieve.

The lookup() function takes three parameters in is most general form. Since
lookup() is a function, all its parameters are treated as expressions. These
parameters are delimited by the normal parameter delimiter for your system
(commas are used in my examples).

The first parameter is the name of the lookup channel we wish to use. Since
this is treated as an expression, it must be enclosed in quotes - unless the
name is stored in a variable, in which case the name of that variable would
be used without quotes. This parameter is optional, but we must explore the
other parameters before discussing this optionality.

The second parameter is the value we wish to use to locate a record along
the lookup index. On evaluation of the function, Omnis directly accesses the
lookup index using this value. Most often this parameter will be simply a
variable name or a discreet value, but a more complex expression can also be
used.

The third parameter is the number of the column whose value we wish to
retrieve. This number can be given explicitly or derived from a variable or
from a more complex expression. In any event, we must know how data is
stored in the File Slot in order to use this correctly.

To restate our example, we wish to use a lookup() function to test the
validity of a State or Province Code value entered by the user. In the
$event method of the field where the value is to be entered, we would place
the following code:

On evAfter
    If stateCode<>lookup('states',stateCode,2)
        OK message  {[stateCode] is not a valid State or Province
abbreviation.}
        Quit event handler (Discard event)
    End If

In this case, we are retrieving the value we are using for the retrieval!
Actually, we are testing to see whether this value exists in the File Slot.
If it does not, the value returned by the lookup() function will be an empty
string (which will differ from the value entered). Notice that this does not
affect the (CRB) value of our variable, so we can compare what is retrieved
with what was entered. If our data entry were done into a row variable, then
the "rowname.columnname" notation would be used instead of the instance
variable shown above. (This will not disallow leaving the field empty, but
we can do that with other code.)

Both the first and the third parameters are optional, but they cannot be
indiscriminately dropped. If only two parameters are included, it is assumed
that the third is missing. So if it is our intention to not use a first
parameter value but to still supply the third, we must include an empty
string (two matching quotes with nothing in between) as the first parameter
value. If "states" were our only lookup channel (or if we had not bothered
to name the channel), we could use the following in the code above:

lookup('',stateCode,2)

If we omit the third parameter, Omnis uses the second column by default as
the value to retrieve. Remember that it uses the first column in the File
Slot for the index if no column number is supplied in the "Open lookup file"
command. Left to its own devices, Omnis assumes our first column is the
index and the second column is the one we will want to retrieve. In our
example, we would have been safe to use:

lookup('states',stateCode)

When Omnis see the lookup() function with only two parameters given, it
assumes that the third has been omitted.

We could also have used the shortest form:

lookup(stateCode)

When Omnis see the lookup() function with only one parameter given, it
assumes that the first and third have been omitted. In this case, it uses
the first lookup channel established (whether or not it had originally been
named) and the second column in the associated File Slot as the retrieval
value. In any case, the index value must be supplied.

Related Techniques

We could also use this lookup channel to retrieve the full name of the State
or Province for display on a window or report. We just have to remember that
this value is in column 3 of the File Slot. We can use the lookup() function
anywhere an expression can be used, so we can calculate the value of a
variable with it, use it for the calculation of a display field, or any
number of other uses.

We could also build a list of State/Province codes and names by establishing
a second lookup channel on this File Slot. This time we will use the
Sequence column (column number 1) as the lookup index. Assuming that we have
a continuous record number stream in this File Slot, we can begin with the
first record and continue retrieving values until we encounter an empty
code. For each valid record, we add a line to our list. We then sort the
results once all records have been accessed.

The variables "stateList", "name" and "code" could be instance variables or
of any appropriate scope. The variable "record" is a local variable of "long
integer" type initialized to 1. Here is the code:

Begin reversible block
    Open lookup file {stateseq/[sys(10)]/stateFile/1}
    Set current list stateList
    Clear sort fields
    Set sort field name (Upper case)
End reversible block
Define list {name,code}
While len(lookup('stateseq',record,2))
    Add line to list
{(lookup('stateseq',record,3),lookup('stateseq',record,2))}
    Calculate record as record+1
End While
Sort list

More Interesting Techniques

We can also access multiple records from within the same expression.
Consider the case of a currency exchange function. We would have a File Slot
containing columns for Country Code and Exchange Rate (columns 1 and 2
respectively). The rates can be based on any currency as long as all are
based on the same currency. All we need to do is divide an amount by the
"from" currency rate and multiply that result by the "to" currency rate.
(Yes, I know this is an over-simplification.) Here is how we could do this
using lookup().

Our method will have three parameters:

amount     Number 2dp
fromCode   Character 2
toCode     Character 2

The method would then be:

Begin reversible block
    Open lookup file {exchange/[sys(10)]/xchRate/1}
End reversible block
Quit method amount/lookup('exchange',fromCode)*lookup('exchange',toCode)

All we need to do is make sure the country codes supplied to the method are
valid.

Advantages

Using the lookup technique for accessing static reference data is certainly
a choice and not a requirement, but it can be seen as advantageous even if
you normally don't use a native Omnis data repository. Here are a few of the
advantages it offers over other techniques.

1) It executes much faster than techniques where the reference data is
stored in comments in a method somewhere in the library. For that matter, it
is also faster than a "select distinct".

2) It requires much less RAM (and less setup execution time) than
maintaining a list containing the reference data.

3) It does not require a separate cursor or select statement, nor does it
clutter up the database with "non-data" tables.

4) Values from more than one record can be used in the same expression
without additional lines of code being executed and without the overhead of
a list to hold the contents of a select table.

Disadvantages

The main disadvantage of this technique is that we have to know information
about the structure of the File Slot that we can't look up in the Catalog
(unless we also have the related File Class in our library).

There may also be the religious disadvantage for some who simply refuse to
use native Omnis data storage techniques.

If the reference data must be updated frequently, there is less advantage to
storing this data in the library itself - but the data being "static" was a
stated criterion for this technique. If we desire to access more "dynamic"
reference data using this technique, we can store the data in a separate
datafile at a specific and definable location. (How do you suppose Omnis
Studio handles icons, for example?) We can either distribute this datafile
to each user as needed (and trust them to put it in the right place) or keep
it on a central server. The retrieval is still faster than with other
techniques with these scenarios, but some other advantages are diminished.

Setting Up a File Class

OK. You've decided this technique may be worth exploring, but you don't know
quite what to do with File Classes and datafiles. Here are some quick tips:

We can't just copy the data for our File Slot into the library or other
datafile. We must use some library (not necessarily the target library) to
define the data to be stored there and then to actually load in the data.

If you intend to store the data in the distribution library, it might be a
good idea to use a separate library to manage that data. You need to create
a File Class to define the data structure for your slot. If you have only
used Schema Classes, the techniques for File Classes are similar, but the
layout of the Class Editor is different. There is a list of "Fields"
(data-bound variables) at the top of the Editor window. Each line is
numbered. If you select a line in that list, you can manage the details of
that line in the lower part of the Editor window. Here you can set a name
and data type for each column as well as specifying whether the column is to
be indexed. For more details on this, see the Omnis Studio manuals. The
important thing is to remember which column number is associated with each
column you define.

The fastest way to make a viable data entry window is to use the Omnis Form
Wizard in the Window Class section of the Component Store. Select "One field
per file field" (the default) for the window type and follow the rest of the
prompts to completion.

Before opening your window, you should open the datafile (library) where you
wish to store the data. You normally do this with the Data File Browser, but
the "Open" command from this window will only display ".df1" files. Your
library is a ".lbs" file, so we have to approach this in another way. Go to
a Code Class (the OO guys are gritting their teeth once again) or some other
class with a Method Editor and create a new method. The one line in this
method will be an "Open data file" command. You next need to specify the
full pathname of your target file. Having done that, execute this line of
code using Command/Control-E. (This executes as a Code method no matter what
kind of class you are using, by the way, but that doesn't matter since this
command does not need to execute in Instance Space.)

If you now open the Data File Browser, you will see that your target file is
now open as a datafile and that it also appears in the "recent files" list
at the bottom of the "Datafile" menu on that window (so you can access it
more easily next time). You can now use your entry window to manage the
content of your File Slot.

There are certainly other ways to get data into a File Slot, but that will
have to wait for another article. I hope you have found this one to be
useful.


========================================

Omnis at SYSTEMS 2001 in Munich

Raining Data Germany will present Omnis Studio at Systems 2001 Trade Show in
Munich, 15 - 19 October (Hall A2, Booth No. 251), now in its twentieth year
and one of the largest gatherings of the IT industry in the world. You will
be able to see Omnis Studio 3 in action via demonstrations from the Omnis
consulting team, and talk to other Omnis developers who will present their
Omnis applications on our booth.

If you are especially interested in Linux, you can visit the Omnis
presentation area in the LinuxPark (Hall A4, Booth no 101-B10). More
information about Systems can be found at: http://messe.systems.de/
(Note: The Systems website is in German, so for pages in English please
click through to the translated pages.)

For more Omnis related news, please go to: www.omnis.net/news


========================================

Developer Resources on the Omnis web site

The Resources section of the Omnis web site provides a wealth of information
for the beginner and experienced Omnis developer. The information available
includes:
-Tech Notes: in-depth technical information about a range of Omnis features
-Information Notes: various notes about Y2K, Windows 2000 and Database
support
-White Papers: articles of general interest to prospective Omnis developers
-Conversion Tools: information about converting from Omnis 7 to Studio
-FAQs: all your product and company related questions answered!

Please go to the following page to explore the resources available:
www.omnis.net/develop/resources/index.html


========================================

I hope you've found this issue of the Omnis Tech Newsletter both interesting
and informative. Please send me your comments and feedback, and include
suggestions for future articles if you like. We would like to hear from
you...

Regards,
--Andrew Smith.
Omnis Technical Newsletter Editor
Email: editor@omnis.net

========================================

No part of this newsletter may be reproduced, transmitted, stored in a
retrieval system or translated into any language in any form by any means
without the written permission of Raining Data.
(c) Copyright Raining Data, Inc., and its licensors 2001. All rights
reserved.
Omnis(r) is a registered trademark and Omnis 7(tm), and Omnis Studio are
trademarks of Raining Data UK Ltd. Other products mentioned are trademarks
or registered trademarks of their corporations.

========================================

To unsubscribe from this newsletter or change your subscription options,
please go to:
www.omnis.net/newsletter