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

REBOL Relational Object-oriented Database Ver. 0.4

 [1/3] from: louisaturk:eudoramail at: 26-Jun-2001 19:51


Hi everybody, Special thanks to Carl, Scott, Holger, Jeff, and everyone else that helped me with this project. Below is version 0.4 of the database. Some of my code may be rather crude, since I am still learning this language, but the code written by the other contributors looks great (to me at least). Unless there are some bugs I missed, the program works fine. It is not relational yet, but since it will be by version 1.0 (I hope) I'm leaving relational in the name to keep myself motivated to finish it. PLANNED IMPROVEMENTS (calling all experts for help!): 1. An edit-data function that will display the current data in the field so that the arrow keys can be used to position the cursor to change just a letter or so---so that the entire field does not have to be retyped to correct typos. I hate unnecessary typing! I am hoping that there are secrets hidden somewhere in REBOL that will make this function easy to write. 2. Relate two or more databases together on key fields so that reports can be written using data from all the databases. 3. Compress (and encrypt, if possible) the data file as it is being backed up. 4. Would there be any practical advantage to putting a View face on this program, other than looks? Would using a View interface slow down performance? If anyone finds any bugs or has suggestions, please let me know! Again, many thanks to all who have helped me with this! Sincerely, Louis REBOL [ Title: "REBOL Relational Object DBMS" Date: "26 June 2001" Version: 0.4 File: %db.r Author: "Carl Sassenrath, G. Scott Jones, and Louis A. Turk" Email: [louisaturk--eudoramail--com] Language: 'English Purpose: "A simple but complete relational object-oriented dbms." Comments: {Carl Sassenrath 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 History: { 26-Jun-2001 Louis A. Turk changed the follow: 1) Corrected a bug that allowed import of duplicate records. 2) Added a List-Dir choice to the main menu to make it easier to find import files. 3) Added backup and restore utilities. 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 History ] ;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." print " 8. List Current Directory." print " 9. Backup the Database to Floppy Disk A: " print " 0. Restore the Database from Floppy Disk A: " 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 database 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] ] if choice = #"

 [2/3] from: gjones05:mail:orion at: 27-Jun-2001 8:45


From: "Dr. Louis A. Turk" ...
> Below is version 0.4 of the database. Some of my code may be rather crude, > since I am still learning this language, but the code written by the other
<<quoted lines omitted: 11>>
> be written using data from all the databases. > 3. Compress (and encrypt, if possible) the data file as it is being backed
up.
> 4. Would there be any practical advantage to putting a View face on this > program, other than looks? Would using a View interface slow down
performance? ... Hi, Louis, Numbers 1 and 4 are probably best answered together. To my knowledge there is no pre-built way to edit text. Like the kbhit function, it should be "do-able", but is it worth the effort when this type of functionality comes with /View at no cost. I don't recall running across an example in the library, but asking in a separately titled thread (like you did with kbhit and compress) may elicit a better response. I suspect that /View eclipsed the development of more sophisticated text-based editing components. An additional advantage that a /View version would offer is more flexibility in laying out the relationship between the key field and data and access to any field for editing without having to drill down to each field each time. As far as performance, of course, there is a bit more overhead, but I have found /View to be sufficiently efficient to satisfactorily handle data display in fairly complex data structures. If you have not used the /View VID, I suspect that you could feel very comfortable using it after just a day or two of immersion. Your db has a very snappy feel as is, so I personally would not feel compelled to convert if it accomplishes what you wish for it to accomplish. Number 3 has been addressed in a separate thread. I remain a bit puzzled about number 2, and maybe it is only do to mis-communication. The beauty of the system that Carl S. prototyped for you is that the data object is nearly infinitely expandable to encompass any combination of data fields that you might conceive of. Only one data "table" is needed. The relationship is inherent in the object concept. While it is certainly "do-able" to create a separate table that shares the key with the initial table, I am unclear whether there would be any advantage to this approach, especially for the small scale database that you have envisioned. Can you give a hypothetical example of the kind of additional data that you might wish to store? Maybe with a concrete example, I could at least prototype a method to demonstrate the idea. Regards, --Scott Jones

 [3/3] from: louisaturk:eudoramail at: 7-Jul-2001 15:35


Hi Scott and everybody, Sorry to take to so long to respond. I had to take care of some other responsibilities, and it took a lot longer than I thought it would. I'm going to do as you suggested and split this up into separate posts with separate new subject lines. It is time to let this thread come to an end. Many thanks to all of you that have helped me with this database. Thank to you I have really learned a lot about REBOL. I realize however that I am still just a beginner. I can see that there is much power there which I do not yet know how to tap. Louis

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