Mailing List Archive: 49091 messages
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

REBOL Relational Object-oriented Database

 [1/22] from: louisaturk::eudoramail::com at: 20-Jun-2001 4:09


Dear Carl and anyone else that might be able to help, I am hoping you can spare a few minutes to help me past a few problems. I am still working on a very simple character-based user interface for your database functions---see my unfinished work below. Once it is finished, I may try to put a View GUI on it, but for right now I want to keep it real simple so I can understand what is going on. I know I could use MySQL and avoid having to learn this, but I have reasons for wanting a full REBOL relational object-oriented dbms. I would really appreciate it if Carl Sassenrath would help with the core database functions as he is the most knowledgeable about the inner workings of REBOL, and it is he that provided the original functions that showed me the great potential of REBOL to do what I need done. I think that many others will also appreciate his concise, elegant contributions of basic building components to help us learn basic, practical skills. If Carl would please take out a few minutes to whip out the remaining needed function, I would be most thankful. Sorry to impose on him like this, but ask and ye shall receive. "Ye have not because ye ask not." You might want to do the program below, add a few records, and then step through the menu choices so that you can see what I am talking about in the following questions: 1. How can a field in a record be edited (not just replaced)? Carl, a function please. 2. What is the purpose of the 2 in the remove-data function? 3. Every time the insert-data function is called a box opens asking me for permission to write to disk. How do I prevent this in such a way as to not open my computer up for possible invasion? (Later: well, its not asking me now, but I don't know why.) 4. How do I print out a list of all or some of the fields in all of the records (say for a report)? I am using probe right now so I can at least see what is in the database, but I need formatted reports. 5. How can this database be related to a second database by say the phone number field? How can the data from both databases be accessed at once? 6. How can data be exported from a database as a comma separated file. 7. How can data be imported into a database from a comma separated file. 8. What is the maximum number of records practical to search with REBOL on a computer with 512 MB of RAM? With 20,000 records or so what would performance be like? 9. How can I get instant action upon pressing a key (say for a menu choice), without having to also hit the enter key? I hate to have to hit 2 keys when hitting one would work just as well. Thanks, Louis REBOL [Title: "REBOL Relational Object DBMS" Date: "19 June 2001" Version: 0.01 File: %db.r Author: "Carl Sessenrath and Louis A. Turk" Email: [louisaturk--eudoramail--com] Language: 'English Purpose: "A simple but complete relational object-oriented dbms." Comments: {Carl Sessenrath made these object oriented database management system functions as an example to teach me, Louis A. Turk, how to program an object database myself. Many thanks Carl! The comments about features below are mostly Carl's, edited by Louis for this script header. Features: 1. The db works from memory. Load-data brings it into memory where the find, remove and other functions operate on it. So, you have to load it first, or at least insert-data a few times to create some data records. 2. You can expand or reduce the record definition without corrupting or affecting the database. 3. If you expect to grow this database to a large size, you will want to MAKE HASH! the database when you load it. make the hash after the database is created (after the foreach loop). database: make hash! database But, don't worry about that until your database gets big. For a few hundred names, you don't need it. 4. The functions work like this: rec: find-data [bob--example--com] print rec/name print rec/phone etc. remove-data [bob--example--com] save-data ; write back to disk insert-data [kit--example--com] "Kitty Carson" none http:/www.a.com save-data ; write back to disk 5. You can use NONE for any missing value above. You don't need to do the save-data each time... but you must do it sooner or later. 6. The data is organized (keyed) by email. To change an email address, but keep the rest of the record intact, use the function change-data. Let's examine change-data: change-data: func [email-old email-new /local record] [ record: find database email-old if none? record [alert reform ["Missing record:" email-old] exit] insert clear first record email-new ; see note below ] Note: This is subtle, so should be noted... I'm clearing out the actual memory string for the old email, then inserting the new string into it. Also, this string is shared. It is used both in the database block (as the key) and in the record object itself. So changing it here will change it in both places. Please send any bug reports or features you add to: [louisaturk--eudoramail--com] } ; End of Comments ] ;End of Rebol Script Header ;#!/rebol/rebol -cs ;open/allow %data.r [read write] db-file: %data.r record: context [name: email: phone: web: none] ;Place the none value in all the varables. database: [] load-data: has [data] [ data: load/all db-file clear database foreach item data [ item: make record item repend database [item/email item] ] ] change-data: func [email-old email-new /local record] [ record: find database email-old if none? record [alert reform ["Missing record:" email-old] exit] insert clear first record email-new ; See note in feature #6 above. ] save-data: has [data] [ data: copy [] foreach [key obj] database [ append/only data third obj ] save db-file data ] find-data: func [email] [select database email] remove-data: func [email] [remove/part find database email 2] insert-data: func [email' name' phone' web'] [ repend database [ email' make record [ email: email' name: name' phone: phone' web: web' ] ] ] {Below is a simple character based user interface for REBOL/Core added by Louis A. Turk} load-data forever [ cls: "^(1B)[J" ; Clear the Screen. print cls print "^/ REBOL RELATIONAL OBJECT-ORIENTED DBMS" ; ^/ is a line feed. print " =====================================^/" print " 1. Add Record." print " 2. Edit a Field In a Record." print " 3. Delete Record." print " 4. Change Email Address Only." print " 5. Print Reports." print " 6. Exit.^/" choice: ask " Enter the number of your choice: " if choice = "1" [ forever [ print cls print " ADD A RECORD" print " ============^/" email: ask " Email Address : " if email = "" [break] rec: find database email if not rec = none [ print cls print " Record is already in database.^/" ask " Hit Any Key To Continue." break ] name: ask " Full Name : " phone: ask " Area Code & Phone# : " web: ask " Website URL : " insert-data email name phone web save-data ] ; End forever loop for choice 1 ] ; End if choice 1 if choice = "2" [ forever [ print cls print " EDIT A FIELD IN A RECORD" print " ========================^/" email-old: ask " Email Address: " record: find database email-old if record = none [ print cls print "^/ Missing record: " email-old ask "^/ Hit Any Key To Continue." break ] record: find-data email-old print ["^/ 1. Email : " record/email] print [" 2. Name : " record/name] print [" 3. Phone : " record/phone] print [" 4. URL : " record/web "^/"] choice: ask " Enter number of field to change: " ; I'm using this method of accessing fields since records in databases ; to be expanded from this one may have 10 to 30 fields with only ; one usually needing editing. if choice = "1" [ print cls print " EDIT EMAIL ADDRESS" print " ==================^/" ask "New function needed here to edit the email field." save-data choice: "0" ;print ["The email address has been changed to " email-new "."] ] if choice = "2" [ print cls print " CHANGE NAME" print " ===========^/" name-old: record/name ask "New function needed here to edit the name field." save-data choice: "0" ] if choice = "3" [ print cls print " CHANGE PHONE" print " ===========^/" ask "New function needed here to edit the phone number field." save-data choice: "0" ] if choice = "4" [ print cls print " CHANGE URL" print " ===========^/" ask "New function needed here to edit the url field." save-data choice: "0" ] ] ; End forever loop ] ; End if choice 2 if choice = "3" [ forever [ print cls print " DELETE A RECORD" print " ===============^/" email-old: ask " Email Address Of Record To Delete: " if email-old = "" [ break ] ; End if. record: find database email-old if none? record [ print cls print " Record not found.^/" ask " Hit Any Key To Continue." break ] remove-data email-old save-data ] ; End forever loop for choice 3. ] ; End if choice 3 if choice = "4" [ forever [ print cls print " CHANGE EMAIL ADDRESS ONLY" print " ==========================^/" email-old: ask " Email Address To Be Changed: " record: find database email-old if none? record [ print cls print "Missing record: " email-old ask "Hit Any Key To Continue." break ] email-new: ask " New Email Address: " change-data email-old email-new save-data ] ; End forever loop for choice 4 ] ; End if choice 4 if choice = "5" [ print cls print " PRINT FORMATTED REPORTS" print " =======================^/" print "I need to know how to print formatted reports, instead of just using probe as below.^/" probe database ask "Press Enter To Continue." ] if choice = "6" [ break ] ; End if choice 6 if choice > "6" [ print "Error: Choice must be number between 1 and 6.^/" ask "Hit Any Key To Continue." ] ; End if for choice 6. ] ; End forever loop for main program.

 [2/22] from: al:bri:xtra at: 21-Jun-2001 21:59


Louis wrote:
> Dear Carl and anyone else that might be able to help, > > I am hoping you can spare a few minutes to help me past a few problems.
I think you'll need to read "Rebol The Official Guide" -- it has a demonstration database program developed through the book. That should give you some ideas. Also, reading "Rebol for Dummies" will help if you get stuck. I hope that helps! Andrew Martin ICQ: 26227169 http://zen.scripterz.org

 [3/22] from: louisaturk:eudoramail at: 21-Jun-2001 17:03


Andrew, Thanks for replying! You can tell I still have a lot to learn about REBOL. :>) My study of REBOL keeps getting interrupted, as I have many responsibilities. I really feel out of place among all the pros on this list. But I see some important uses for REBOL so I keep trying. Yes, I have both books and have been reading them. I have read the For Dummies book several times. It has enabled me to write several very useful programs (thanks Ralph!). It does not, however, say much about relational object-oriented databases. The Official Guide would be a lot easier for me to understand if it contained a very simple but working bare bones Relational Object-oriented Database right at the beginning before going into so much detail. After running the program and seeing what it does, I could then more easily understand what the book is explaining. I have looked for such a program on the CD that came with the book, but so far haven't found one. The database functions Carl wrote for me were so concise I was hoping for one more like them to complete this program. I was hoping for him to write this function so that I could give him credit for all of the actual database functions in the program, but if he is too busy to help I can certainly understand that, and help from anyone will be appreciated. Most people on this list are probably using MySQL or some other such database. There doesn't seem to be much interest in a REBOL Relational Object-oriented Database. Am I being foolish is wanting to learn how to do this? Or is it just that I shouldn't bother the list when the information is already available in The Official Guide? Or am I just asking too many questions in one post? Louis At 09:59 PM 6/21/2001 +1200, you wrote:

 [4/22] from: gjones05:mail:orion at: 21-Jun-2001 17:22


Hi, Louis, All questions are welcomed, in my opinion, but your was asking for a lot in one chunk. ;-) Furthermore, it is possible that you might have offended some of us lowly list members by asking for responses only from the "The Creator" (of REBOL, that is ;-). The following may be of some help, but the answers are a bit terse. Feel free to ask for further clarification if it makes no sense. From: "Dr. Louis A. Turk"
> 1. How can a field in a record be edited (not just replaced)? Carl, a > function please.
***old version*** change-data: func [email-old email-new /local record] [ record: find database email-old if none? record [alert reform ["Missing record:" email-old] exit] insert clear first record email-new ; See note in feature #6 above. ] ***new version change-data: func [email-old email-new /local record] [ record: find database email-old if none? record [alert reform ["Missing record:" email-old] exit] record/1: email-new ; See note in feature #6 above. ]
> 2. What is the purpose of the 2 in the remove-data function?
The 'find returns an index to the record. The refinement /part on 'remove says to remove from the index position to the length of 2, which removes the email address and the record.
> 3. Every time the insert-data function is called a box opens asking me for > permission to write to disk. How do I prevent this in such a way as to not > open my computer up for possible invasion? (Later: well, its not asking me > now, but I don't know why.)
REBOL security defaults to allowing local file read operations, but it requests permission to write a file locally. When the program needs to write to a local file, it will request permission. If you click "yes", then only the single write operation will occur. If you click "Allow All" then it will no longer ask each time. If your program and datafile are in a *non-shared* directory on your computer, then there is no risk of someone tapping into your computer. What constitutes a non-shared directory depends on the platform. There are different ways to change the default levels, depending on how the script will be used.
> 4. How do I print out a list of all or some of the fields in all of the > records (say for a report)? I am using probe right now so I can at least > see what is in the database, but I need formatted reports.
forskip database 2 [ print ["Email: " database/2/email] print ["Name: " database/2/name] print ["Phone: " database/2/phone] print ["Web URL: " database/2/web] ]
> 5. How can this database be related to a second database by say the phone > number field? How can the data from both databases be accessed at once?
Say the second database has a structure like phone and type (meaning "Home", Work , etc), then assume the phone number is stored as a string (do to local differences). Complete phone numbers are unique by definition, so a second database could be structured as a simple block with alternating values of numbers and types, like: ["01-713-555-1212" "Work" "01-713-555-2121" "Home"] A simple 'select using the phone number field will return the phone location type on this table (just like you used email address to look up a full record on the first). Using related tables does sort of violate the idea behind an object oriented database, though. :-) REBOL is flexible enough to allow this type of information to be further embedded within a record, but might add a bit of complexity though, depending on how it is implemented.
> 6. How can data be exported from a database as a comma separated file.
write %/path/to/my-csv-file "" ;to get fresh file emit: func [blk] [repend blk] forskip database 2 [ csv-line: make string! 1000 emit [{"} database/2/email {","} database/2/name {","} database/2/phone {","} database/2/web {"^/}] write/append %/path/to/my-csv-file csv-line ]
> 7. How can data be imported into a database from a comma separated file.
foreach line read/lines %/path/to/my-csv-file [ foreach [email name phone web] parse line none [ email: to-email trim email trim name trim phone web: to-url trim web ;;;;;then insert fields into a database ] ]
> 8. What is the maximum number of records practical to search with REBOL on > a computer with 512 MB of RAM? With 20,000 records or so what would > performance be like?
Assuming a somewhat fast processor (500 MHZ) and the database block is made into a hash, performance should be quite tolerable for a single user up into this range of size.
> 9. How can I get instant action upon pressing a key (say for a menu > choice), without having to also hit the enter key? I hate to have to hit 2 > keys when hitting one would work just as well.
I don't know this one!! Hope this helps. --Scott Jones

 [5/22] from: gjones05:mail:orion at: 21-Jun-2001 18:19


From: "GS Jones"
> > 6. How can data be exported from a database as a comma separated file. > write %/path/to/my-csv-file "" ;to get fresh file
<<quoted lines omitted: 5>>
> write/append %/path/to/my-csv-file csv-line > ]
Whoops. Error... emit: func [blk] [repend blk] should be ... emit: func [blk] [repend csv-line blk] Sorry. --Scott Jones

 [6/22] from: gchiu:compkarori at: 22-Jun-2001 15:48


> Relational Object-oriented Database
Um, what exactly is a relational objected oriented database? -- Graham Chiu

 [7/22] from: louisaturk:eudoramail at: 22-Jun-2001 1:24


Graham, What I think one is (at least what I meant when I wrote that title): Relational = having two or more databases joined together by a key field (or fields) so that the proper data can be pulled from the two (or more) databases as though they were one. Object-oriented=the concept of bundling data and functions together in a container called an object so that the code can be easily reused (and extended) and so that two different objects may contain symbols that contain identical names with conflict. I am not a professional programmer. If I am wrong please let me know. Louis At 03:48 PM 6/22/2001 +1200, you wrote:

 [8/22] from: m:converso:bitinia at: 22-Jun-2001 8:57


Louis, 1.- I agree completely with you ! 2.- I'm a proud owner of the two books too 2.1.- but the two books lacks of certain very basic steps, if it is true that Rebol can be a language for (serious) beginners thanks a lot MAurizio Dr. Louis A. Turk wrote:

 [9/22] from: louisaturk:eudoramail at: 22-Jun-2001 4:01


Scott or whoever is online, Is there an argument missing in the emit function below? csv: ask "Name of CSV File to Write to: " csv-file: to-file csv write csv-file "" ;to get fresh file emit: func [blk] [repend blk] forskip database 2 [ csv-line: make string! 1000 emit [{"} database/2/email {","} database/2/name {","} database/2/phone {","} database/2/web {"^/}] write/append csv-file csv-line ] I get the following erro message: ** Script Error: repend is missing its value argument ** Where: emit ** Near: repend blk At 05:22 PM 6/21/2001 -0500, you wrote:

 [10/22] from: philb:upnaway at: 22-Jun-2001 16:21


Hi Loius, The term database is usually meant to be more than 1 table, which is what confused me about your question. For example I have used an oracle database that consists of hundreds of tables. Cant remember the exact definition of Relaional but I think we get the idea of what you were talking about. I agree with your definition of Object Oriented. Cheers Phil -- Original Message -- Graham, What I think one is (at least what I meant when I wrote that title): Relational = having two or more databases joined together by a key field (or fields) so that the proper data can be pulled from the two (or more) databases as though they were one. Object-oriented=the concept of bundling data and functions together in a container called an object so that the code can be easily reused (and extended) and so that two different objects may contain symbols that contain identical names with conflict. I am not a professional programmer. If I am wrong please let me know. Louis At 03:48 PM 6/22/2001 +1200, you wrote:

 [11/22] from: philb:upnaway at: 22-Jun-2001 16:27


Hi Louis, the help for repend is
>> help repend
USAGE: REPEND series value /only DESCRIPTION: Appends a reduced value to a series and returns the series head. REPEND is a function value. ARGUMENTS: series -- (Type: series port) value -- (Type: any) REFINEMENTS: /only -- Appends a block value as a block so the repend statment is missing its 2nd value. emit: func [blk] [repend blk] Dont know what the function is trying to do though ... so I cant be much help there :-( Cheers Phil -- Original Message -- Scott or whoever is online, Is there an argument missing in the emit function below? csv: ask "Name of CSV File to Write to: " csv-file: to-file csv write csv-file "" ;to get fresh file emit: func [blk] [repend blk] forskip database 2 [ csv-line: make string! 1000 emit [{"} database/2/email {","} database/2/name {","} database/2/phone {","} database/2/web {"^/}] write/append csv-file csv-line ] I get the following erro message: ** Script Error: repend is missing its value argument ** Where: emit ** Near: repend blk At 05:22 PM 6/21/2001 -0500, you wrote:

 [12/22] from: louisaturk:eudoramail at: 22-Jun-2001 4:44


Sorry, I forgot about Scott's second post that made this correction. Louis At 04:01 AM 6/22/2001 -0500, you wrote:

 [13/22] from: louisaturk:eudoramail at: 22-Jun-2001 4:58


Phil, Many thanks. Scott, corrected this for me; I just forgot. What are we doing up so early in the morning? Hey, I haven't gone to bed yet! If I hit the sack now I can sleep for two hours! Good night! Louis At 04:27 PM 6/22/2001 +0800, you wrote:

 [14/22] from: philb:upnaway at: 22-Jun-2001 17:20


Hi Louis, its still late afternoon/early evening here .... just goes to show you can get help off the list alomost any time of the day :-) (Except I wasnt much help :-/ ) Cheers Phil -- original message -- Phil, Many thanks. Scott, corrected this for me; I just forgot. What are we doing up so early in the morning? Hey, I haven't gone to bed yet! If I hit the sack now I can sleep for two hours! Good night! Louis At 04:27 PM 6/22/2001 +0800, you wrote:

 [15/22] from: gjones05:mail:orion at: 22-Jun-2001 13:57


From: "Dr. Louis A. Turk"
> Hi Scott, > > I can't get this one to work.
... Yes, I was partially asleep at the wheel. I had only briefed through some of the particulars of your implementation, and had forgotten that Carl S. had you inserting the objects instead of just structured data (like, duh, why where you calling object-oriented??? I was so busy yesterday evening that I should have put off answering until I had more time. Sorry!) His technique apparently is careful to pick the actual "location" that contains the email address, so that changing it for the "key" field also changes it for the data field (they are the same location, in essence; how clever of Carl!!). As far as changing the other fields, here is a sample of one of your selections: if choice = "2" [ print cls print " CHANGE NAME" print " ===========^/" name-old: record/name ask "New function needed here to edit the name field." save-data choice: "0" ] record/name currently points to actual value of the current record/name (Hey, Joel, like that tautology?). One option would be to directly assign a new name to that field. Like: record/name: copy ask "Change name to: " I have no direct way to check this method with your current program, but I believe that it would work correctly. This method would give the user very little option to cancel a change. I guess the next statement could check for an empty string, and, if present, reset the name back to the old name: name-old: record/name record/name: copy ask "Change name to: " if record/name = "" [record/name: name-old] ;no copy command should be needed here I suspect that what you may want is a way to make several changes to a record, and then decide whether to "commit" the changes. This method might prove to be more robust, and could also update the email info if needed. It would require a change in the flow of the program as it stands. If this is what you were hoping for, it may be helpful to see another copy of the program as it exists after recent updates.
> PS Your print report code and export data code works great. I haven't > had time to put your import data code into the program yet; I'll let you > know how it works. Thanks again.
I guess it is a miracle that it works given that I had not looked carefully enough at the details of the implementation. Good luck! --Scott Jones

 [16/22] from: gjones05:mail:orion at: 22-Jun-2001 9:42


From: "Dr. Louis A. Turk"
> Scott, > > I received your second message at the same time as the first one, but > forgot. I remembered just a moment or two after sending my argument > question. Sorry to waste your time that way.
You didn't waste my time. No problem.
> By the way, many thanks for your help. You have really saved me a lot of
time! I'm glad that it helped some. --Scott Jones

 [17/22] from: louisaturk:eudoramail at: 22-Jun-2001 12:04


Hi Scott, I can't get this one to work. At 05:22 PM 6/21/2001 -0500, you wrote:
>change-data: func [email-old email-new /local record] [ > record: find database email-old > if none? record [alert reform ["Missing record:" email-old] exit] > record/1: email-new >]
I think I may not have made clear what I need the function to do: after finding the record using Carl's find-data function, I need a function that will pull up whichever field I choose (with its contents), and allow me to edit (correct typos in) those contents. The program will then write the change to the file using Carl's save-data function. Perhaps the function should be named edit-data instead of change-data to make its use clearer. Louis PS Your print report code and export data code works great. I haven't had time to put your import data code into the program yet; I'll let you know how it works. Thanks again.

 [18/22] from: gjones05:mail:orion at: 22-Jun-2001 5:40


From: "Dr. Louis A. Turk"
> Is there an argument missing in the emit function below?
Hi, Louis, Yes. I caught the mistake an hour after I sent the message and sent a second message. I have reattached that message below. This emit technique is a slight variation on Carl's suggestion to me once, so it is almost as good as coming from "The Creator" (of REBOL, of course;-). I checked that this works, but I used a diferent dataset so obviously I did more cutting than pasting when I created the code for your structure. Sorry for the over site. From: "GS Jones"
> > 6. How can data be exported from a database as a comma separated file. > write %/path/to/my-csv-file "" ;to get fresh file
<<quoted lines omitted: 5>>
> write/append %/path/to/my-csv-file csv-line > ]
Whoops. Error... emit: func [blk] [repend blk] should be ... emit: func [blk] [repend csv-line blk] Sorry. --Scott Jones

 [19/22] from: louisaturk:eudoramail at: 22-Jun-2001 10:17


Scott, I received your second message at the same time as the first one, but forgot. I remembered just a moment or two after sending my argument question. Sorry to waste your time that way. By the way, many thanks for your help. You have really saved me a lot of time! Louis At 05:40 AM 6/22/2001 -0500, you wrote:

 [20/22] from: louisaturk:eudoramail at: 26-Jun-2001 4:42


Dear Carl, At 08:38 PM 6/25/2001 -0700, you wrote:
>Louis, > >Sorry about the delay in getting back to you. >Let me take a look... if I can find some time >in the next few days. Feel free to nag me. > >-Carl
Many, many thanks. I am stuck! (Holger, Jeff, Scott, and others have also given me a lot of help (some of which I obviously haven't completely understood or implemented properly. So help from them (or anyone else) would also be appreciated.) The latest version of my program is below. I am having problems in the following areas. 1. The edit-data function isn't working---possibly because I don't understand how to use it. 2. The kbhit function usually works, but sometimes gives the following error: Enter the # of Choice (or ESC to Exit): ** Script Error: choice has no v alue ** Where: forever ** Near: choice = kbhit if choice
>>
Usually I get that error when the program first starts. If I then type kbhit from the console, and hit a key, it will work properly, and I can restart the program and it will work. 3. Print Reports does print the data to the screen, but it also causes loss of all the data! For your own testing purposes, after typing in some data save it to a CSV file using choice 6 from the main menu. Then if you lose it using choice 5 you can then read it back in using choice 7. The rest of the program seems to be working ok, but testing and bug reporting from anyone would be greatly apprecitated. Louis REBOL [Title: "REBOL Relational Object DBMS" Date: "19 June 2001" Version: 0.2 File: %db.r Author: "Carl Sessenrath and Louis A. Turk" Email: [louisaturk--eudoramail--com] Language: 'English Purpose: "A simple but complete relational object-oriented dbms." Comments: {Carl Sessenrath made these object oriented database management system functions as an example to teach me, Louis A. Turk, how to program an object database myself. Many thanks Carl! The comments about features below are mostly Carl's, edited by Louis for this script header. Features: 1. The db works from memory. Load-data brings it into memory where the find, remove and other functions operate on it. So, you have to load it first, or at least insert-data a few times to create some data records. 2. You can expand or reduce the record definition without corrupting or affecting the database. 3. If you expect to grow this database to a large size, you will want to MAKE HASH! the database when you load it. make the hash after the database is created (after the foreach loop). database: make hash! database But, don't worry about that until your database gets big. For a few hundred names, you don't need it. 4. The functions work like this: rec: find-data [bob--example--com] print rec/name print rec/phone etc. remove-data [bob--example--com] save-data ; write back to disk insert-data [kit--example--com] "Kitty Carson" none http:/www.a.com save-data ; write back to disk 5. You can use NONE for any missing value above. You don't need to do the save-data each time... but you must do it sooner or later. 6. The data is organized (keyed) by email. To change an email address, but keep the rest of the record intact, use the function change-data. Let's examine change-data: change-data: func [email-old email-new /local record] [ record: find database email-old if none? record [alert reform ["Missing record:" email-old] exit] insert clear first record email-new ; see note below ] Note: This is subtle, so should be noted... I'm clearing out the actual memory string for the old email, then inserting the new string into it. Also, this string is shared. It is used both in the database block (as the key) and in the record object itself. So changing it here will change it in both places. Please send any bug reports or features you add to: [louisaturk--eudoramail--com] } ; End of Comments ] ;End of Rebol Script Header db-file: %data.r record: context [name: email: phone: web: none] ;Place the none value in all the varables. database: [] load-data: has [data] [ data: load/all db-file clear database foreach item data [ item: make record item repend database [item/email item] ] database: make hash! database ] change-data: func [email-old email-new /local record] [ record: find database email-old if none? record [alert reform ["Missing record:" email-old] exit] insert clear first record email-new ; See note in feature #6 above. ] save-data: has [data] [ data: copy [] foreach [key obj] database [ append/only data third obj ] save db-file data ] find-data: func [email] [select database email] remove-data: func [email] [remove/part find database email 2] insert-data: func [email' name' phone' web'] [ repend database [ email' make record [ email: email' name: name' phone: phone' web: web' ] ] ] edit-data: func [field index /local record] [ record: find database field if none? record [alert reform ["Missing record:" field] exit] record/index: field ] {Holger and Jeff helped me with this function. I changed it some trying to learn how to use it.} kbhit: does [ con: open/direct/binary/no-wait console:// until [wait con] choice: to-char pick con 1 close con choice ] ;kbhit: does [ ; con: open/direct/binary/no-wait console:// ; until [wait con] ; first reduce [to-string copy con close con] ;] {Below is a simple character based user interface for REBOL/Core added by Louis A. Turk} load-data forever [ cls: "^(1B)[J" ; Clear the Screen. print cls print "^/ REBOL RELATIONAL OBJECT-ORIENTED DBMS" ; ^/ is a line feed. print " =====================================^/" print " 1. Add Record." print " 2. Edit a Field In a Record." print " 3. Delete Record." print " 4. Change Email Address Only." print " 5. Print Reports." print " 6. Export Data To CSV File." print " 7. Import (APPEND) Data From CSV File." prin "^/ Enter the # of Choice (or ESC to Exit): " choice = kbhit if choice = #"1" [ forever [ print cls print " ADD A RECORD" print " ============^/" email: ask " Email Address : " if email = "" [break] rec: find database email either rec = none [ name: ask " Full Name : " phone: ask " Area Code & Phone# : " web: ask " Website URL : " insert-data email name phone web save-data ][ ask "^/ Record is already in database. Continue? " ] ] ; End forever loop for choice 1 ] ; End if choice 1 if choice = #"2" [ forever [ print cls print " EDIT A FIELD IN A RECORD" print " ========================^/" email-old: ask " Email Address: " record: find database email-old if record = none [ print cls print "^/ Missing record: " email-old ask "^/ Hit Any Key To Continue." break ] record: find-data email-old print ["^/ 1. Email : " record/email] print [" 2. Name : " record/name] print [" 3. Phone : " record/phone] print [" 4. URL : " record/web "^/"] prin [" Enter number of field to change: "] choice: kbhit ; I'm using this method of accessing fields since records in databases ; to be expanded from this one may have 10 to 30 fields with only ; one usually needing editing. if choice = #"1" [ print cls print " EDIT EMAIL ADDRESS" print " ==================^/" edit-data record/email 2 save-data choice: "0" ; Added this hack to solve a logic problem. ask "^/The email address has been changed to: " email-new . Continue? ? ] if choice = "2" [ print cls print " CHANGE NAME" print " ===========^/" name-old: record/name ask "New function needed here to edit the name field." save-data choice: "0" ] if choice = "3" [ print cls print " CHANGE PHONE" print " ===========^/" ask "New function needed here to edit the phone number field." save-data choice: "0" ] if choice = "4" [ print cls print " CHANGE URL" print " ===========^/" ask "New function needed here to edit the url field." save-data choice: "0" ] ] ; End forever loop ] ; End if choice 2 if choice = #"3" [ forever [ print cls print " DELETE A RECORD" print " ===============^/" email-old: ask " Email Address Of Record To Delete: " ;if email-old = "" [ ; break ;] ; End if. record: find database email-old if none? record [ print cls print " Record not found.^/" ask " Hit Any Key To Continue." break ] remove-data email-old save-data ] ; End forever loop for choice 3. ] ; End if choice 3 if choice = #"4" [ forever [ print cls print " CHANGE EMAIL ADDRESS ONLY" print " ==========================^/" email-old: ask " Email Address To Be Changed: " if email-old = "" [break] record: find database email-old if none? record [ print cls print "Missing record: " email-old ask "Hit Any Key To Continue." break ] email-new: ask " New Email Address: " change-data email-old email-new save-data ] ; End forever loop for choice 4 ] ; End if choice 4 {Scott Jones provided the core code for choices 4, 5, and 6 below.} if choice = #"5" [ print cls print " PRINT FORMATTED REPORTS" print " =======================^/" forskip database 2 [ print ["^/Email: " database/2/email] print ["Name: " database/2/name] print ["Phone: " database/2/phone] print ["Web URL: " database/2/web] ] prin "^/Continue? " kbhit ] if choice = #"6" [ print cls print " EXPORT DATA TO CSV FILE" print " =======================^/" csv: ask "Name of CSV File to Write to: " either not csv = "" [ csv-file: to-file csv write csv-file "" ;to get fresh file emit: func [blk] [repend csv-line blk] forskip database 2 [ csv-line: make string! 1000 emit [{"} database/2/email {","} database/2/name {","} database/2/phone {","} database/2/web {"^/}] write/append csv-file csv-line ] prin "^/The file has been written. Continue? " kbhit ][prin "^/You forget to enter a file name. Continue? " kbhit] ] if choice = #"7" [ print cls csv: ask "Name of CSV File to Read From: " either not csv = "" [ csv-file: to-file csv open/read csv-file foreach [email name phone web] parse line none [ line: to-string read/lines csv-file email: to-email trim email name: trim name phone: trim phone either web = "" [web: none][web: to-url trim web] if not find-data email [insert-data email name phone web] save-data ] prin "^/The Data Has Been Appended to the Database. Continue? " kbhit ][prin "^/You forget to enter a file name. Continue? " kbhit] ] ] ; End forever loop for main program.

 [21/22] from: gjones05:mail:orion at: 26-Jun-2001 8:08


Hi, Louis, It looks like your database is coming along famously. LT> The latest version of my program is below. I am having problems in the LT> following areas. LT> 1. The edit-data function isn't working---possibly because I don't LT> understand how to use it. Possibly because I didn't explain it very well. :-) LT> 2. The kbhit function usually works, but sometimes gives the following error: LT> Enter the # of Choice (or ESC to Exit): ** Script Error: choice has no value LT> ** Where: forever LT> ** Near: choice = kbhit LT> if choice LT> Usually I get that error when the program first starts. If I then type LT> kbhit from the console, and hit a key, it will work properly, and I can LT> restart the program and it will work. Adding a default choice of 0 seemed to fix the problem. LT> 3. Print Reports does print the data to the screen, but it also causes LT> loss of all the data! For your own testing purposes, after typing in some LT> data save it to a CSV file using choice 6 from the main menu. Then if you LT> lose it using choice 5 you can then read it back in using choice 7. Forskip has the "side effect" of causing the series pointer to point to the tail. One must explicitly reset the series pointer back to the head of the series. Here is what I've come up with this morning. I made a number of changes that are dcumented within the code below. I think that it is getting close. Good work, Louis, I think the kbhit makes it easier to use! --Scott Jones ;;;;;;;;;;;;;;;;;;;;;;;;; REBOL [ Title: "REBOL Relational Object DBMS" Date: "26 June 2001" Version: 0.3 File: %db.r Author: "Carl Sassenrath and Louis A. Turk" Email: [louisaturk--eudoramail--com] Language: 'English Purpose: "A simple but complete relational object-oriented dbms." Comments: {Carl Sessenrath made these object oriented database management system functions as an example to teach me, Louis A. Turk, how to program an object database myself. Many thanks Carl! The comments about features below are mostly Carl's, edited by Louis for this script header. Features: 1. The db works from memory. Load-data brings it into memory where the find, remove and other functions operate on it. So, you have to load it first, or at least insert-data a few times to create some data records. 2. You can expand or reduce the record definition without corrupting or affecting the database. 3. If you expect to grow this database to a large size, you will want to MAKE HASH! the database when you load it. make the hash after the database is created (after the foreach loop). database: make hash! database But, don't worry about that until your database gets big. For a few hundred names, you don't need it. 4. The functions work like this: rec: find-data [bob--example--com] print rec/name print rec/phone etc. remove-data [bob--example--com] save-data ; write back to disk insert-data [kit--example--com] "Kitty Carson" none http:/www.a.com save-data ; write back to disk 5. You can use NONE for any missing value above. You don't need to do the save-data each time... but you must do it sooner or later. 6. The data is organized (keyed) by email. To change an email address, but keep the rest of the record intact, use the function change-data. Let's examine change-data: change-data: func [email-old email-new /local record] [ record: find database email-old if none? record [alert reform ["Missing record:" email-old] exit] insert clear first record email-new ; see note below ] Note: This is subtle, so should be noted... I'm clearing out the actual memory string for the old email, then inserting the new string into it. Also, this string is shared. It is used both in the database block (as the key) and in the record object itself. So changing it here will change it in both places. Please send any bug reports or features you add to: [louisaturk--eudoramail--com] 26-Jun-2001 G. Scott Jones, M.D. changed the following 1) Added check for existence of data file and check for empty data file. 2) Commented out hash until fully debugged 3) Changed name of update function for key data from change-data to update-key-data 4) Added default choice of 0 to avoid initial error 5) Added additional forever loop to allow editing of multiple fields in edit record section 6) Changed from if construct to switch inside edit record ? whether need to set choice to 0 anymore 7) Changed method for updating no-key fields 8) Added ability to edit key-field email from within edit record. ? whether need separate email update function. 9) Added checks for empty data on edit/update 10) Reset database to head after forskip (NB: forskip leaves the series pointer pointing at the tail) in print section 11) Changed csv import to leave data elements in same format as "native" data file format (namely, string). 12) Made a few miscellaneous formatting changes, including recommended REBOL indent 13) Other miscellaneous changes mostly commented in code. } ; End of Comments ] ;End of Rebol Script Header db-file: %data.r record: context [name: email: phone: web: none] ;Place the none value in all the varables. database: copy [] load-data: has [data] [ if exists? db-file [ data: load/all db-file clear database if data [ foreach item data [ item: make record item repend database [item/email item] ] ] ;database: make hash! database ] ] update-key-data: func [email-old email-new /local record] [ record: find database email-old if none? record [alert reform ["Missing record:" email-old] exit] insert clear first record email-new ; See note in feature #6 above. ] ; ? whether needed edit-data: func [field index /local record] [ record: find database field if none? record [alert reform ["Missing record:" field] exit] record/index: field ] save-data: has [data] [ data: copy [] foreach [key obj] database [ append/only data third obj ] save db-file data ] find-data: func [email] [select database email] remove-data: func [email] [remove/part find database email 2] insert-data: func [email' name' phone' web'] [ repend database [ email' make record [ email: email' name: name' phone: phone' web: web' ] ] ] {Holger and Jeff helped me with this function. I changed it some trying to learn how to use it.} kbhit: does [ con: open/direct/binary/no-wait console:// until [wait con] choice: to-char pick con 1 close con choice ] ;kbhit: does [ ; con: open/direct/binary/no-wait console:// ; until [wait con] ; first reduce [to-string copy con close con] ;] {Below is a simple character based user interface for REBOL/Core added by Louis A. Turk} load-data choice: 0 ; to avoid initial error forever [ cls: "^(1B)[J" ; Clear the Screen. print cls print "^/ REBOL RELATIONAL OBJECT-ORIENTED DBMS" ; ^/ is a line feed. print " =====================================^/" print " 1. Add Record." print " 2. Edit a Field In a Record." print " 3. Delete Record." print " 4. Change Email Address Only." print " 5. Print Reports." print " 6. Export Data To CSV File." print " 7. Import (APPEND) Data From CSV File." prin "^/ Enter the # of Choice (or ESC to Exit): " choice = kbhit ;may wish to change this to a switch construct in future if choice = #"1" [ forever [ print cls print " ADD A RECORD" print " ============^/" email: ask " Email Address: " if email = "" [break] ;gsj added to avoid empty error rec: find database email either rec = none [ name: ask " Full Name: " phone: ask " Area Code & Phone#: " web: ask " Website URL: " insert-data email name phone web save-data ][ ask "^/ Record is already in database. Continue? " ] ] ; End forever loop for choice 1 ] ; End if choice 1 if choice = #"2" [ forever [ print cls print " EDIT A FIELD IN A RECORD" print " ========================^/" email-old: ask " Email Address: " if email-old = "" [break] forever [ record: find database email-old if record = none [ print cls print "^/ Missing record: " email-old ask "^/ Hit Any Key To Continue." break ] record: find-data email-old print ["^/ 1. Email : " record/email] print [" 2. Name : " record/name] print [" 3. Phone : " record/phone] print [" 4. URL : " record/web] print [" 5. Return to Edit Menu" "^/"] prin [" Enter number of field to change: "] choice: kbhit ; I'm using this method of accessing fields since records in databases ; to be expanded from this one may have 10 to 30 fields with only ; one usually needing editing. switch/default choice [ #"1" [ print cls print " EDIT EMAIL ADDRESS" print " ==================^/" email-new: ask rejoin [" New Email Address to replace " email-old ": "] if email-new <> "" [ update-key-data email-old email-new save-data email-old: copy email-new ] choice: "0" ; Added this hack to solve a logic problem. ] #"2" [ print cls print " CHANGE NAME" print " ===========^/" new-name: ask rejoin ["Enter name to replace " record/name ": "] if new-name <> "" [record/name: copy new-name save-data] choice: "0" ] #"3" [ print cls print " CHANGE PHONE" print " ===========^/" new-phone: ask rejoin ["Enter phone to replace " record/phone ": "] if new-phone <> "" [record/phone: copy new-phone save-data] choice: "0" ] #"4" [ print cls print " CHANGE URL" print " ===========^/" new-url: ask rejoin ["Enter url to replace " record/web ": "] if new-url <> "" [record/web: copy new-url save-data] choice: "0" ] ][break] ;end of switch and switch/default ] ; end of forever for editing a record ] ; End forever loop ] ; End if choice 2 if choice = #"3" [ forever [ print cls print " DELETE A RECORD" print " ===============^/" email-old: ask " Email Address Of Record To Delete: " ;if email-old = "" [ ; break ;] ; End if. record: find database email-old if none? record [ print cls print " Record not found.^/" ask " Hit Any Key To Continue." break ] remove-data email-old save-data ] ; End forever loop for choice 3. ] ; End if choice 3 if choice = #"4" [ forever [ print cls print " CHANGE EMAIL ADDRESS ONLY" print " ==========================^/" email-old: ask " Email Address To Be Changed: " if email-old = "" [break] record: find database email-old if none? record [ print cls print "Missing record: " email-old ask "Hit Any Key To Continue." break ] email-new: ask rejoin [" New Email Address to replace " email-old ": "] if email-new <> "" [ update-key-data email-old email-new save-data ] ] ; End forever loop for choice 4 ] ; End if choice 4 {Scott Jones provided the core code for choices 4, 5, and 6 below.} if choice = #"5" [ print cls print " PRINT FORMATTED REPORTS" print " =======================^/" forskip database 2 [ print ["^/ Email: " database/2/email] print [" Name: " database/2/name] print [" Phone: " database/2/phone] print ["Web URL: " database/2/web] ] prin "^/Press any key to continue..." database: head database ;reset following forskip kbhit ] if choice = #"6" [ print cls print " EXPORT DATA TO CSV FILE" print " =======================^/" csv: ask "Name of CSV File to Write to: " either not csv = "" [ csv-file: to-file csv write csv-file "" ;to get fresh file emit: func [blk] [repend csv-line blk] forskip database 2 [ csv-line: make string! 1000 emit [{"} database/2/email {","} database/2/name {","} database/2/phone {","} database/2/web {"^/}] write/append csv-file csv-line ] database: head database ;reset following forskip prin "^/The file has been written. Continue? " kbhit ][prin "^/You forget to enter a file name. Continue? " kbhit] ] if choice = #"7" [ print cls csv: ask "Name of CSV File to Read From: " either not csv = "" [ csv-file: to-file csv data: read/lines csv-file foreach line data [ foreach [email name phone web] parse line none [ email: trim email name: trim name phone: trim phone web: trim web if not find-data email [insert-data email name phone web] save-data ] ] prin "^/The Data Has Been Appended to the Database. Continue? " kbhit ][prin "^/You forget to enter a file name. Continue? " kbhit] ] ] ; End forever loop for main program.

 [22/22] from: louisaturk:eudoramail at: 26-Jun-2001 20:05


Hi, Scott, At 08:08 AM 6/26/2001 -0500, you wrote:
>Hi, Louis, > >It looks like your database is coming along famously.
Yes, thanks to you doing most of the hard work. I've listed you as the second author after Carl. You guys are great teachers---Holger and Jeff also. Thanks! I am just about ready now to add a few more fields, and put the DBMS to practical use. Louis

Notes
  • Quoted lines have been omitted from some messages.
    View the message alone to see the lines that have been omitted