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

Object Database

 [1/7] from: louisaturk::eudoramail::com at: 9-Apr-2001 6:35


I am trying to learn how to make an object database using REBOL. I need a simple, but complete, working example. Can anyone supply one? Also, what are probably dumb questions: 1. If an object database contains data, will that data be lost if another field is added? 2. Is there any way to change the name of an object in a database without losing the data in the object? 3. Would it be possible to use email addresses for the names of the objects? Thanks in advance for any help. Louis

 [2/7] from: carl:rebol at: 9-Apr-2001 8:28


What!? You just want us to just whip out an object database, eh? ;) Ok, why not.... Here is a small "contacts" database keyed by email. You can expand or reduce the record definition without corrupting or affecting the database. REBOL [Title: "Email Contact Database"] db-file: %data.r record: context [name: email: phone: web: none] database: [] load-data: has [data] [ data: load/all db-file clear database foreach item blk [ item: make record item repend database [item/email item] ] ] 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' ] ] ] You can expand/contract the record definition at any time. This is untested... but should be close to working, less a few minor typos. If you expect to grow this database to a large size, you will want to MAKE HASH! the database when you load it. -Carl PS: The remove/part on find really does work. Remove none is allowed in Core 2.5.

 [3/7] from: louisaturk:eudoramail at: 11-Apr-2001 1:47


Dear Carl, I am amazed that such a small amount of code can do so much, and also that the creator of this language himself would help me like this. Many thanks! I have some questions. I have been studying your code carefully trying to learn how it works. I've been on this list for quite a while, but have only recently taken time to buy Ralph's book and Elan and John's book to really get serious about learning the language. I'm a real beginner, so please excuse me if my questions betray that fact. So far I have figured out how to use your functions to enter data into a record, and then save it to a file. By entering "print read %data.r" at the command line, I am able to see that I have entered the data successfully. So I think I understand how to use insert-data and save-data. However, when I type load-data I get an error message ("blk has no value"); what am I doing wrong? Also, I assume that I must learn how to get load-data to work before find-data and remove-data will work; but to keep from having to ask you later, how are these two functions used? I want to use this database for an email list. Most of the data remains the same, but email addresses change often. Sometime I do not even have a name---only an email address that has to be removed or changed. How do I change an email address? You say:
> If you expect to grow this database to a large >size, you will want to MAKE HASH! the database when you load >it.
Will this modification do it?:
>load-data: has [data] [ > data: make hash! load/all db-file
<<quoted lines omitted: 4>>
> ] >]
Will your code work with Rebol/View also? Am I correct in assuming that View would be ideal for creating a user interface for the database? Many thanks for your help, and for creating a truly exciting language. Louis At 08:28 AM 4/9/2001 -0700, you wrote:
>What!? You just want us to just whip out an object database, eh? ;)
Well, I wasn't expect anyone to whip out one especially for me,

 [4/7] from: carl:rebol at: 11-Apr-2001 5:49


Wow... so many questions. It's a good think I can't sleep tonight... My replies are mixed in below:
> -----Original Message----- > From: [rebol-bounce--rebol--com] [mailto:[rebol-bounce--rebol--com]]On Behalf Of
<<quoted lines omitted: 7>>
> the creator of this language himself would help me like this. > Many thanks!
Whatever I can do to help!
> I have some questions. I have been studying your code carefully > trying to
<<quoted lines omitted: 8>>
> save-data. However, when I type load-data I get an error message ("blk > has no value"); what am I doing wrong? Also, I assume that I must learn
I'm sorry. Change blk to data. A typo. Did I mention that it still needed your help debugging it? ;)
> how to get load-data to work before find-data and remove-data will work; > but to keep from having to ask you later, how are these two > functions used?
The database works from memory. Load-data brings it into memory where the find, remove and other functions operate on it. So, yes, you have to load it first, or at least insert-data a few times to create some data records. 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 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.
> I want to use this database for an email list. Most of the data remains > the same, but email addresses change often. Sometime I do not > even have a > name---only an email address that has to be removed or changed. How do I > change an email address?
The data is organized (keyed) by email. To change an email address, but keep the rest of the record intact, you'll need a new function: 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. That's a feature, but it is not obvious, so make sure that you comment that line.
> You say: > > If you expect to grow this database to a large
<<quoted lines omitted: 9>>
> > ] > >]
You will want to 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. Get to know the code and debug it before hashing it.
> Will your code work with Rebol/View also? Am I correct in assuming that > View would be ideal for creating a user interface for the database?
Yes and yes. You will want to add two new functions: one to copy data from a record into a view form, and another to copy it back or create a new record in the database.
> Many thanks for your help, and for creating a truly exciting language.
When you are done, you will have written an actual database system from scratch in REBOL. Not bad for a couple pages of code, eh?

 [5/7] from: louisaturk:eudoramail at: 13-Apr-2001 14:18


Scott, Thanks much for the help. I would really appreciate it if you would send me a copy of the View interface when you finish it. Louis At 12:14 AM 4/13/2001 -0700, you wrote:

 [6/7] from: koolauscott::yahoo at: 13-Apr-2001 0:14


*****BEGINNER'S QUESTIONS***** Dr. Turk, I hope you don't mind my replying as I'm not much more than a beginner myself. I remember the difficulties I had when I was first starting out with Rebol. I still don't understand 90% of the stuff people talk about on this mailing list but I've learned enough Rebol to enjoy it. I think it is best to read through the core guide and then the Rebol for Dummies first before trying Rebol - The Official Guide. I started out with The Official Guide and knocked my head against it for 3 months, and while I'm sure it improved my character, I wouldn't recommend it for a beginner. There is a typo in the script you're using; try the one attached. It is an amazing script; I've been playing with it the past couple of days. I've just started to learn View and have been working on an interface for this script. The only databases I've done in Rebol have been small flat-files and they look really bloated compared to this. I've tried to answer the questions you raised as best I could, I hope it helps. To see the data you have entered use ==> probe database You can't print an object; use probe. The database block contains the database when it is loaded into Rebol. The %data file is just used to store the database to disk. All the database functions work with database block. load-data loads the disk copy of the database from disk and into the block called database. save-data writes the database block to the %data file, when you are done making modifications to the database. Thus find-data and remove-data can be called after you create a record with insert-data. Those functions only work with the database block and have nothing to do with the %data file. Hash I've added a line that turns the database block from a block! type to a hash! Change an Email Address First look-up the record to change with find-data and assign it to a variable ==> x: find-data [bill--example--com] then probe x to see the object ==> probe x To change bill's email address use ==> x/email: [bill--newemail--net] When you call save-data and then load-data you can use find-data with the new email address. ################################################# Email Contact Database REBOL [Title: "Email Contact Database"] db-file: %data record: context [name: email: phone: web: none] 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: to-hash database ] ] 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' ] ] ] ################################################## --- "Dr. Louis A. Turk" <[louisaturk--eudoramail--com]> wrote:

 [7/7] from: jregent:centrum:cz at: 16-Apr-2001 21:12


Hi Carl, Luis, Dr. Turk &comp I play with Carl's Object Database functions and View too. Carl's code is now modified in fly by actual field list of current file. Layout, context, data manipulation function etc. are generated before doing. This db-edit we will to use for administration of small files and learning. I offer cooperation with share, testing, debuging and finishing this code. Some coment's and idas are welcome. Field-list can be stored in db-file.. If field is named "web" as (example 5 ), data of this field are not saved. I thing name "web" is in conflict with internal word? If are used keys Up/Down many times Rebol crasch (GPF) Rebol is best :-) Many thanks for your work and inspiration. By Jan Regent ---------------------------------------------------------------------------- ------ rebol Title: "db-edit" author: "Jan Regent" need: "view 1.0" ] ;--------------------------------------------------------------------------- -- ; db-lib ;--------------------------------------------------------------------------- -- find-data: func [key] [select database key] remove-data: func [key] [ remove/part find database key 2] ;=========================================================================== ======= db actions save-data: has [data] [ data: copy [] foreach [key obj] database [ append/only data third obj ] save db-file data ] save-data-formated: has [data][ save-data file: read db-file formated-db-file: to-file join db-file "-temp.dat" if exists? formated-db-file [delete formated-db-file] ;--------------------------------------------------------------- record parsing file: replace file "[[" "[" ;! I don't undestand why save-data saved record with "[[" file: replace file "]]" "]" ;! debuging needed while [true] [ parse file [thru "[" copy qrecord to "]" ] file: remove/part file length? qrecord if not found? find qrecord field-list/1 delete db-file qnew-file: split-path db-file rename formated-db-file qnew-file/2 return ] write/append/lines formated-db-file "[" ;------------------------------------------------------- field parsing for i 1 ((length? field-list) - 1) 1 [ i1: i + 1 field-label: field-list/:i field-label2: field-list/:i1 parse qrecord [thru field-label copy qfield to field-label2 ] write/append/lines formated-db-file join " " [field-label " " qfield] ] qfield: remove/part qrecord ((index? find qrecord field-label2) + length? field-label2) write/append/lines formated-db-file join " " [ field-label2 " " qfield] write/append/lines formated-db-file "]" ] ] load-data-template: {has [data] [ data: load/all db-file clear database foreach item data [ item: make record item repend database [item/x-x-key-x-x item] ] ] } load-data-gen: has [][ qitem: copy field-list/1 qitem: replace qitem ":" "" load-data-template: replace load-data-template "x-x-key-x-x" qitem load-data: do load-data-template ] db-create: func [ fields [string!]] [ qfile: split-path db-file if not exists? qfile/1 [ make-dir/deep qfile/1] ddf-file: to-file join db-file ".ddf" write/lines ddf-file "<field-list>" write/lines/append ddf-file join " " fields write/lines/append ddf-file "</field-list>" cmd: join "field-list: [" [ fields "]" ] debug-log cmd do cmd db-context-create insert-data-gen insert-data-new save-data-formated rec-count: 1 rec-count-save ] rec-count-save: has [] [ rec-count-file: to-file join db-file ".reccount" write/lines rec-count-file join "<rec-count> " [rec-count " </rec-count>" ] ] rec-count-read: has [] [ rec-count-file: to-file join db-file ".reccount" parse read rec-count-file [thru "<rec-count>" copy qrec-count to </rec-count> ] qrec-count: trim qrec-count rec-count: to-integer qrec-count ] db-open: has [][ ddf-file: to-file join db-file ".ddf" parse read ddf-file [thru "<field-list>" copy fields to "</field-list>" ] cmd: join "field-list: [" [ fields "]" ] debug-log cmd do cmd db-context-create insert-data-gen rec-count-read ] db-context-create: has [] [ cmd: "context [" foreach item field-list [cmd: join cmd [item " "]] cmd: join cmd " none ]" debug-log cmd record: do cmd ] ;=========================================================================== ==== record actions save-record: has [][ foreach item field-list [ qitem: replace to-string item ":" "" cmd: join "rec/" [item " to-string " qitem "/text"] debug-log cmd do cmd ] record-no: (2 * this-record) - 1 do join "database/" [record-no ": rec"] ] find-record-no: has [][ record-no: (2 * this-record) - 1 rec: find-data database/:record-no if not none? rec [ unview focus name view layout current-layout ] ] insert-data-gen: has [] [ qfunc: {insert-data: func [} foreach item field-list [ qitem: copy item qitem: replace qitem ":" "'" qfunc: join qfunc [qitem " "] ] append qfunc "]" append qfunc newline ;append qfunc { "Insert record into db-file, generated function, key is x-x-x"} ;qfunc: replace qfunc "x-x-x" field-list/1 append qfunc newline qfunc: join qfunc [ {[ repend database [ } replace copy field-list/1 ":" "'" { make record [ } ] foreach item field-list [ qitem: copy item qitem: replace qitem ":" "'" qfunc: join qfunc [" " item " " qitem newline] ] qfunc: join qfunc { ] ] ] } debug qfunc do qfunc ] insert-data-new: has [] [ cmd: "insert-data " foreach item field-list [ cmd: join cmd [{ "" }]] debug-log cmd do cmd ] ;======================================================================== view layouts/ actions gen-form: has [] [ simple-layout: copy [] append simple-layout join {[ h3 } [main-title ] append simple-layout { across ;----------------------------------------------------------------------- fields section } foreach item field-list [ qline: join " label " [{"} item {" 100x24 right } item { }] append simple-layout qline qitem: join "rec/" item qitem: replace qitem ":" "" curr-value: to-string do qitem qline: join {field 362 } [ qitem " return" ] append simple-layout qline append simple-layout newline ] append simple-layout { ;----------------------------------------------------------------------- control section arrow left keycode [up ] [ show-page "prev-record" ] arrow right keycode [down ] [ show-page "next-record" ] label "find: " 42x24 right web: field 362 " " return guide 125x100 button 100 "New" #"^n" [ show-page "new-record" ] button "Del" #"^d" [ ] ;show-page "del-record" button "Close" #"^q" [ save-record save-data-formated unview ] return ] } if exists? %db-edit.frm [ delete %db-edit.frm] write %db-edit.frm simple-layout ] show-page: func [arrow-key][ save-record if "prev-record" = arrow-key [ if this-record > 1 [ this-record: this-record - 1 find-record-no ] ] if "next-record" = arrow-key [ if this-record < rec-count [ this-record: this-record + 1 find-record-no ] ] if "new-record" = arrow-key [ insert-data-new rec-count: rec-count + 1 rec-count-save this-record: rec-count find-record-no ] if "del-record" = arrow-key [ remove-data to-word replace field-list/1 ":" "" rec-count: rec-count - 1 if rec-count = 0 [ rec-count: 1 insert-data-new ] rec-count-save if this-record > rec-count [this-record: rec-count] find-record-no ] ;print [arrow-key this-record ] ] ;=============================================================================================== ; main ;======================================================================= ======================== db-edit: func [db-name [file!] fields [string!] ] [ db-file: copy db-name if not exists? db-file [db-create fields ] db-open load-data-gen load-data rec: find-data database/1 main-title: join {"Data: } [ db-name {"} ] gen-form current-layout: load %db-edit.frm ;debug-log current-layout view layout current-layout ] debug-log: func [text ][ if debug [ write/append/lines %debug.txt "" write/append/lines %debug.txt "" write/append/lines %debug.txt "" write/append/lines %debug.txt to-string text ] ] ;----------------------------------------------------------- <global words> debug: true if debug [if exists? %debug.txt [delete %debug.txt]] record: context [] field-list: [] db-file: %contact-db.dat main-title: join {"} [ db-file {"} ] database: [] this-record: 1 rec-count: 0 rec: "" ;---------------------------------------------------------- </global words> ; examples - only one line can be unremarked ;1 db-edit %db\aa.dat {"email:" "name:" "phone:" "web1:" "web3:" } ;2 ;db-edit %db\cars.dat {"number:" "color:" "type:" "technical-checking:" year: } ;3 ;db-edit %db\users.dat {"email:" "name:" "phone:" "id:" "mobile:" } ;4 ;db-edit %db\main.cfg {"work-dir:" "backup-dir:" "created:" "rights:" } ;5 ;db-edit %db\web.cfg {"name:" "web:" "page:" "rights:" } -- Attached file included as plaintext by Listar -- -- File: db-edit.r rebol [ Title: "db-edit" author: "Jan Regent" need: "view 1.0" ] ;----------------------------------------------------------------------------- ; db-lib ;----------------------------------------------------------------------------- find-data: func [key] [select database key] remove-data: func [key] [ remove/part find database key 2] ;================================================================================== db actions save-data: has [data] [ data: copy [] foreach [key obj] database [ append/only data third obj ] save db-file data ] save-data-formated: has [data][ save-data file: read db-file formated-db-file: to-file join db-file "-temp.dat" if exists? formated-db-file [delete formated-db-file] ;--------------------------------------------------------------- record parsing file: replace file "[[" "[" ;! I don't undestand why save-data saved record with "[[" file: replace file "]]" "]" ;! debuging needed while [true] [ parse file [thru "[" copy qrecord to "]" ] file: remove/part file length? qrecord if not found? find qrecord field-list/1 [ delete db-file qnew-file: split-path db-file rename formated-db-file qnew-file/2 return ] write/append/lines formated-db-file "[" ;------------------------------------------------------- field parsing for i 1 ((length? field-list) - 1) 1 [ i1: i + 1 field-label: field-list/:i field-label2: field-list/:i1 parse qrecord [thru field-label copy qfield to field-label2 ] write/append/lines formated-db-file join " " [field-label " " qfield] ] qfield: remove/part qrecord ((index? find qrecord field-label2) + length? field-label2) write/append/lines formated-db-file join " " [ field-label2 " " qfield] write/append/lines formated-db-file "]" ] ] load-data-template: {has [data] [ data: load/all db-file clear database foreach item data [ item: make record item repend database [item/x-x-key-x-x item] ] ] } load-data-gen: has [][ qitem: copy field-list/1 qitem: replace qitem ":" "" load-data-template: replace load-data-template "x-x-key-x-x" qitem load-data: do load-data-template ] db-create: func [ fields [string!]] [ qfile: split-path db-file if not exists? qfile/1 [ make-dir/deep qfile/1] ddf-file: to-file join db-file ".ddf" write/lines ddf-file "<field-list>" write/lines/append ddf-file join " " fields write/lines/append ddf-file "</field-list>" cmd: join "field-list: [" [ fields "]" ] debug-log cmd do cmd db-context-create insert-data-gen insert-data-new save-data-formated rec-count: 1 rec-count-save ] rec-count-save: has [] [ rec-count-file: to-file join db-file ".reccount" write/lines rec-count-file join "<rec-count> " [rec-count " </rec-count>" ] ] rec-count-read: has [] [ rec-count-file: to-file join db-file ".reccount" parse read rec-count-file [thru "<rec-count>" copy qrec-count to "</rec-count>" ] qrec-count: trim qrec-count rec-count: to-integer qrec-count ] db-open: has [][ ddf-file: to-file join db-file ".ddf" parse read ddf-file [thru "<field-list>" copy fields to "</field-list>" ] cmd: join "field-list: [" [ fields "]" ] debug-log cmd do cmd db-context-create insert-data-gen rec-count-read ] db-context-create: has [] [ cmd: "context [" foreach item field-list [cmd: join cmd [item " "]] cmd: join cmd " none ]" debug-log cmd record: do cmd ] ;=============================================================================== record actions save-record: has [][ foreach item field-list [ qitem: replace to-string item ":" "" cmd: join "rec/" [item " to-string " qitem "/text"] debug-log cmd do cmd ] record-no: (2 * this-record) - 1 do join "database/" [record-no ": rec"] ] find-record-no: has [][ record-no: (2 * this-record) - 1 rec: find-data database/:record-no if not none? rec [ unview focus name view layout current-layout ] ] insert-data-gen: has [] [ qfunc: {insert-data: func [} foreach item field-list [ qitem: copy item qitem: replace qitem ":" "'" qfunc: join qfunc [qitem " "] ] append qfunc "]" append qfunc newline ;append qfunc { "Insert record into db-file, generated function, key is x-x-x"} ;qfunc: replace qfunc "x-x-x" field-list/1 append qfunc newline qfunc: join qfunc [ {[ repend database [ } replace copy field-list/1 ":" "'" { make record [ } ] foreach item field-list [ qitem: copy item qitem: replace qitem ":" "'" qfunc: join qfunc [" " item " " qitem newline] ] qfunc: join qfunc { ] ] ] } debug qfunc do qfunc ] insert-data-new: has [] [ cmd: "insert-data " foreach item field-list [ cmd: join cmd [{ "" }]] debug-log cmd do cmd ] ;======================================================================== view layouts/ actions gen-form: has [] [ simple-layout: copy [] append simple-layout join {[ h3 } [main-title ] append simple-layout { across ;----------------------------------------------------------------------- fields section } foreach item field-list [ qline: join " label " [{"} item {" 100x24 right } item { }] append simple-layout qline qitem: join "rec/" item qitem: replace qitem ":" "" curr-value: to-string do qitem qline: join {field 362 } [ qitem " return" ] append simple-layout qline append simple-layout newline ] append simple-layout { ;----------------------------------------------------------------------- control section arrow left keycode [up ] [ show-page "prev-record" ] arrow right keycode [down ] [ show-page "next-record" ] label "find: " 42x24 right web: field 362 " " return guide 125x100 button 100 "New" #"^n" [ show-page "new-record" ] button "Del" #"^d" [ ] ;show-page "del-record" button "Close" #"^q" [ save-record save-data-formated unview ] return ] } if exists? %db-edit.frm [ delete %db-edit.frm] write %db-edit.frm simple-layout ] show-page: func [arrow-key][ save-record if "prev-record" = arrow-key [ if this-record > 1 [ this-record: this-record - 1 find-record-no ] ] if "next-record" = arrow-key [ if this-record < rec-count [ this-record: this-record + 1 find-record-no ] ] if "new-record" = arrow-key [ insert-data-new rec-count: rec-count + 1 rec-count-save this-record: rec-count find-record-no ] if "del-record" = arrow-key [ remove-data to-word replace field-list/1 ":" "" rec-count: rec-count - 1 if rec-count = 0 [ rec-count: 1 insert-data-new ] rec-count-save if this-record > rec-count [this-record: rec-count] find-record-no ] ;print [arrow-key this-record ] ] ;=============================================================================================== ; main ;===================================================

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