Tips_todo   >   Misc   >   Multiple Libraries
I recommend that you build your application using multiple libraries. It makes your code a lot easier to 'view' because the code is broken into logical groups (libraries). Instead of looking at a list of ALL the schemas, or ALL the file classes, or ALL the window classes, or ALL the report classes, in your entire application, using multiple libraries you just view the classes in, for example, the Accounts Payable library.
There are just a few things to get used to/do differently with multiple libraries, but once you get those things set up, it's very easy to work with.
You need a 'main' library which the users double-click on to open your application. In the $construct of the Startup_Task of the main library, you call a method ie. OpenSublibraries, which the automatically opens your 'sub' libraries.
See 'Directory Structure' for a recommended way of storing your sub libaries.
In my app, the main library only contains 'generic' code. Superclass tables, superclass windows, superclass objects are all stored in the main library. That way if there is a problem with a superclass, I go straight to the main library and can always find it there. Commonly used menus, toolbars, and objects are also stored in the main library.
Multiple libraries do not require multiple data files. You can still store all your data in a single data file. The Current Session or Current Data file is where all your libraries get/insert/update the data. Unless you change the current session or current data file, your libraries will all point to the same data file.
There's a couple things you have to watch with multiple libraries.
1. Set the Default name of the library in the Property Manager. If you press F2, you will see a library called 'TIPS' in your library browser. Even through the file name is StudioTIPSvXXX.LBS, in the Library Browser it's called TIPS. If you select TIPS and press F6, then click the Prefs tab, the top property is $defaultname. Give every library a default name. I like to use short upper case names. It makes the libary name easy to see in your code, saves typing, and keeps your code strings shorter. (AP, AR, GL, PO, INV, PAY, EMP, ...)
2. Prefix references to classes with the library name, even when referring to classes inside the same library. That is especially true for the "main" library. That way the code will still work, even if you drag it to another library. When you refer to something in another library, Omnis automatically prefixes the library name.
3. Set the $designtaskname property in all your windows, reports, objects to the main library startup task. In my application, the main library default name is 'CSP', so all my Window, Report, Table, Schema, object classes have the $designtaskname property set to "CSP.Startup_Task".
4. If you need to access a Window, Report, object class from a different library, you must set the $external property of the class to kTrue. Otherwise it won't be visible in design mode. To keep the number of "objects" that are visible to a minimum, only set $external to "kTrue" for the classes that you need to access from other libraries, otherwise leave it at kFalse. (Thanks to Greg Olson-Hyde for this tip.)
5. Be aware of the current task instance and unless you need multiple tasks, just run your application under the main library's task instance... the one that automatically starts when the $construct of your main library's Startup_Task kicks in, as it is opened. When you open sublibraries from your main library check the "Do not close others" and "Do not open startup task", options. This will prevent the sub libraries from having their own tasks. Craig Lewis recommends that you delete the Startup_Task from each sublibrary.
Think of the task instance as a "container of instances". When you open your main library it automatically sets up a task instance. When you install a menu from the task instance, the menu is contained in the task instance. When you use that menu to open a window (in the same library, or another library), that window instance is now also contained in the task instance. Being part of the task instance gives anything within the task access to the task variables. They are said to be in "scope" of the task.
If you are working in development mode on a window in a sub library and you "Ctrl/Cmnd-T" to open the window instance, you will find if you step through code that is run by the window, the task variables in the main library's startup task instance are "out of scope", NULL values. You can't access them. That is because the window instance was not opened by something which was contained in to the main library startup task instance. When you did a "Ctrl/Cmnd-T" to open the window instance the window instance was created in its own task. Run time users don't get the chance to do this, it's more when you are developing that you run into this.
Multilibraries, I strongly recommend them!! There's lots of advantages, very few disadvantages. Easy to do. See the sample code in this section for "OpenSublibraries" and "OpenUtilitiesLibraries"Tired of manually uploading new versions of your software to the server, telling users to download
the updates, and then having users mess up on the relatively simply job of dragging the updated
version from the server to their computer?
Here's a strategy that I use with my multi-library application, that has relieved me of all of the above. (Credit for this idea goes to Herb Payerl who inspired me with somthing similar which he does at First Avenue Information Systems.)
Rather than having the users directly open the main library of your application have them double-click on an "AutoUpdater" library. Hide all your libraries in a subfolder. See "Directory Structure" for a suggested folder structure.
On the server, create a directory called "auto_updates". In the "auto_updates" folder on the server, make an exact copy of the folder structure and files which the users should have on their computers.
When the user opens the "Auto Updater" library on their local computer, it checks the server for any new versions, downloads them using FileOps (or FTP with the internet?), and then proceeds with opening the main library. The main library's Startup_Task then automatically opens all sublibraries on the user's local computer.
I developed a utility called "AutoUpdater" which does all of the above plus the following extra features:
1. Each time "AutoUpdater" is opened it compares the subfolder structure and files in the AutoUpdater folder to its own subfolder structure and files and automatically creates any new subfolders and downloads any new files. Initially all you need on the user's computer is the "auto_updater.lbs" in an empty folder. The first time "AutoUpdater" is opened it will automatically download all the subfolders and files from AutoUpdater folder on the server.
2. Allows the developer to select from a list of the open libraries which to upload to the server. AutoUpdater then closes the selected libraries and uploads them to the server along with a new version number. AutoUpdater then reopens the libraries so the developer can continue working.
3. Uses an auto_archives folder. Each time the developer uploads new libraries to the server, "AuotUpdater" first creates a subfolder in the auto_archives folder and copies the old libraries into the subfolder before uploading the new libraries. The auto_archives subfolders are named using the current date and time including seconds.
e.g. auto_archive_20001224_160455
4. Adds an "Updates" menu to the run time user's menus with a "Download Updates" menu item. If the user selects this option, "AutoUpdater" checks the server for any new updates and then closes those library(s), downloads them using FileOps, and then reopens the library(s) so the user can continue working. "AutoUpdater" does NOT allow development versions of Studio to use the "Download Updates" option. "AutoUpdater" does NOT even show the "Upload New Versions" menu option to runtime versions of Studio.
"AutoUpdater" has been tested in a Wintel/Mac environment. I developed "AutoUpdater" as an Open Source library to run faceless with anyone's application. "Faceless" meaning runtime users can't tell that someone else wrote the code. You can rename the "auto_updater.lbs" to whatever name you like. e.g. MyGreatApplication.LBS. "AutoUpdater" will be made available to StudioTips subscribers. (If you wish to beta test "AutoUpdater", send me an email.)
I'm hoping other developers will adopt "AutoUpdater", enhance it, and then send me their enhancements. (After testing them for a few weeks!) That way we'll all benefit from each other's efforts.There are many ways to set up your directory structure for multiple libraries. At the time of writing this, I have 15 libraries and am accessing the Omnis data file using OmnisSQL. The data file is on a Mac G4 server running Appleshare IP.
My application is called CSP (An acronym for Contacts SuperPro)
Here's what I have currently settled on for a file structure:
(Not all libraries are listed, just enough to give you a flavour for the structure.)
(">" character is used for indenting)
csp (folder)
> csp_startup.lbs (This is my "AutoUpdater" library - See Auto Updater in this section)
> > programs (folder) On the user's computers this folder and its subfolders are normally closed.
> > > modules (folder)
> > > > csp.lbs (The "Main" library which opens all the others and has superclass)
> > > > csp_ap.lbs (Accounts Payable)
> > > > csp_ar.lbs (Accounts Receiveable)
> > > > csp_gl.lbs (General Ledger)
> > > > csp_inv.lbs (Inventory)
> > > > csp_pay.lbs (Payroll)
> > > > csp_po.lbs (Purchasing)
> > > Open Source (folder)
> > > > print_labels.lbs
> > > > prompts.lbs
> > > > report_writer.lbs
> > > preferences (folder)
> > > > csp_localprefs.lbs (Stores the path(s) to the data file(s), and the local printer page setup)
> > > resources (folder)
> > > > sounds (folder)
> > > > > click.wav
> > > > > start.wav
> > > > images (folder)
> > > > > logo.gif
By default the folders are closed, so all the user sees is:
csp (open folder)
> csp_startup.LBS
> > programs (closed folder)
The user double-clicks on csp_startup.lbs, it checks the server for newer version libraries (see Auto Updater) and downloads them. Once the downloads are complete, csp_startup.lbs opens csp.lbs, the main library. csp.lbs handles things from there. It knows the subfolder names, and using its own $pathname can figure out the paths to the other libraries.
csp.lbs first opens csp_localprefs.lbs to get the default path to the data file. It then opens the data file and prompts the user to "Sign In". The user must successfully enter their ID and password (which is stored in the data file) before they can go any further in the application. Once they sign in, the main menu window opens listing all the modules and windows which they have access to.
Each user's password, access privileges, window sizes, digital signature, etc. are all stored in the data file under the user's profile.
When the csp.lbs (main library) is closed, its $destruct is responsible for closing all the libraries which csp.lbs opened.
Sample code for the $OpenAllLibraries is included in this section.
In the studiotips_stuff\demos folder there is a multi_library folder which gives you an idea of how you can set it up. Double-clicking on auto_updater.lbs opens Programs\main_library.lbs which then opens the other libraries. Main library uses its own "oConstants" object class for the paths. When you close main.lbs, it closes the other libraries. Feel free to experiment with the MultiLibrary demo. To open it up from StudioTips, click the "Run Demo" button.This method builds a list of all the files (libraries) in the 'utilities' folder and then opens the libraries. Open source libraries run in their own task instance, so the "Do not open startup task" check box is left unchecked. The path to the Open Source folder is obtained from the "oConstant" object class. (See the "Constants" section)
This method builds a list of all the files (libraries) in the 'Open Source' folder and then open the libraries. Open Source libraries run in their own task instance, so the "Do not open startup task" check box is left unchecked. The path to the Open Source folder is obtained from the "oConstant" object class. (See the "Constants" section)
"Open Source" libraries are libraries which Omnis developers share with eachother. Open Source libraries have very specific purposes and run "standalone". I've been getting pretty excited about the "Open Source" concept. At the time of writing this, I've got a couple "Open Source" libraries built and being tested live.
eg. "print_labels.lbs" - send it any list of records, it prompts the user for label size, then prints the labels.
eg. "prompts.lbs" - replaces the built in 'OK,Yes/No,No/Yes,Prompt for Input' with a much more flexible set of prompts.
By working together on "Open Source" libraries we can develop better and better utilities and all benefit.