TOP | UP: Data Manipulation | NEXT: Non-Scalars |
An object is a database entry or variable - a storage location with a name and a value. This chapter discusses UserTalk verbs that manipulate objects as objects, creating them, deleting them, copying them, and so forth.
The reader should be thoroughly familiar with ways of referring to objects, and the circumstances under which an object reference will suffice to permit creation of an object. See Chapter 6, Referring to Database Entries, Chapter 8, Addresses, Chapter 9, Special Evaluation, Chapter 10, Datatypes, and Chapter 11, Arrays.
Verbs that operate on objects often take an address parameter that specifies the object. We have already pointed out (Chapter 8) the dangers of accidentally handing to a verb that expects an address parameter something that is not an address. The verbs studied in this chapter can destroy or alter the value of database entries, so those dangers are particularly acute.
WARNING Some of these verbs can be dangerous! Verbs such as new(), table.assign(), and delete() wipe out existing values and cannot be undone. Be careful what parameter you hand such verbs.
In a table edit window, table entry names are limited to 31 characters in length. A UserTalk command can give to a table entry a much longer name, up to 255 characters (and can then operate upon that entry); this is usually unwise, though, since it can cause problems when one encounters the entry in a table edit window.
To alter the value of an object, possibly creating the object if necessary:
=
table.assign (addrObject, value)
For details on the assignment operator (=), see Chapter 44, Operators. The difference between these two methods of assignment is that the assignment operator will balk at setting an existing non-scalar object to a scalar value, whereas table.assign() will replace anything with anything.
To create an "empty" object of specified type:
new (datatype, addrObject)
When using new() on a local variable, it is common practice to declare the local variable first, just to make quite certain that new() interprets the address correctly. So, for example:
local (myOutline)
new (outlineType, @myOutline)
To create all database tables needed to create a database entry:
table.surePath (string)
table.surePath() is a clever utility script, well worth studying, which solves the problem that it is impossible to refer to or take the address of a database entry whose parent table doesn't exist. (For this reason, string must be a string, not an address, and it must be a full path, not a partial reference.) table.surePath() ignores the last element of the path if the path doesn't end with a period, so you can use the same value both as the parameter to table.surePath() and to create the object. For example:
whatToCreate = "workspace.table1.table2.someString"
table.surePath (whatToCreate) « now table1 and table2 certainly exist
new (stringType, whatToCreate)
« or: whatToCreate^ = "hello there", or whatever
To obtain a name guaranteed unique in its table:
table.uniqueName (startOfName, addrTable)
toys.uniqueTableName (startOfName, addrTable, numberOfDigits)
table.uniqueName() can be used to guarantee that creating a new table entry won't overwrite an existing table entry; it does this by attaching a serial number to the end of the name. toys.uniqueTableName() is just the same, except that it pads the serial number with zeros so that the resulting names sort correctly. What these verbs return is actually an address, which is suitable for creating the entry:
local (whatName = table.uniqueName("testing", @workspace))
new (stringType, whatName)
« or: whatName^ = "hello there", or whatever
delete (addrObject)
Typically, the object is a database entry; there is usually no need to delete a variable, since it will eventually go out of scope anyway. See Chapter 7, The Scope of Variables and Handlers. See also Chapter 11 for other uses of delete().
table.emptyTable (addrTable)
To learn whether an object exists:
defined (objectReference)
To obtain the address of an object's containing table:
parentOf (objectReference)
To obtain a table entry's name:
nameOf (objectReference)
defined(), parentOf() and nameOf() are special forms; they take object references, not addresses, and they will never raise an error, even if objectReference doesn't exist or is somehow illegal. See Chapter 9.
defined() and parentOf() tell what will happen if you use a name. If defined( objectReference) is true, then it is legal to get the value of objectReference. If boolean(parentOf( objectReference)) is true, then it is legal to set the value of objectReference.
Also, defined() and parentOf() together tell you whether you're dealing with a database entry or a local variable. If defined( objectReference) and boolean(parentOf( objectReference)) are both false, then setting the value of objectReference will result in the creation of an implicitly declared local variable. This suggests the following utility script.
on willBeImplicitLocal(addr) |
if not defined(addr^) |
if not parentOf(addr^) |
return true |
return false |
To learn whether a defined object is a database entry, the simplest way is to find out, recursively, whether its ultimate parent is "root".
on isDatabaseEntry(addr) |
if defined(addr^) |
if (addr == "root") or (isDatabaseEntry(parentOf(addr^))) |
return true |
return false |
nameOf() is useful for obtaining just the last element of an address; for example, if addr is "suites.html.buildObject", we can use nameOf(addr^) to get just "buildObject". In this respect, parentOf() may be viewed as the complement of nameOf(), since we can use string(parentOf(addr^)) to get just "suites.html".
For the use of nameOf() with regard to array indexing, see Chapter 11.
table.rename (addrObject, nameString)
To obtain the address of the non-scalar currently selected in the frontmost window:
toys.getCursorAddress ()
To obtain the datatype of a value:
typeOf (value)
These verbs are all shortcuts, accomplishing nothing that could not be accomplished by direct assignment. For example, saying:
table.copy (@scratchpad.myString, @workspace)
has exactly the same effect as saying:
table.assign (@workspace.myString, scratchpad.myString)
As with table.assign(), there is no restriction on overwriting existing values. It might be wise to reread the warning near the start of this chapter. It is not an error to overwrite a value with itself.
To move or copy an object into a table:
table.copy (addrObject, addrTable)
table.move (addrObject, addrTable)
To move or copy all of a table's entries into a table:
table.copyContents (addrSourceTable, addrDestTable)
table.moveContents (addrSourceTable, addrDestTable)
To alter a table entry's address:
table.moveAndRename (addrSourceObject, addrDestObject)
There is no verb table.copyAndRename(); this is what table.assign() already does.
An object's age is the date when it was last modified. An object is dirty if it was modified more recently than the database was last saved.
Unfortunately, Frontier can report age only for a non-scalar; there is no way to find out when a scalar's value was last modified. The situation is somewhat alleviated by the fact that a table is considered modified if the value of one of its scalar entries is changed. However, changing the value of a non-scalar does not affect the age of its containing table; and many other things can cause a table to be considered modified, including simply scrolling its edit window.
Any time a non-scalar is modified, it and the database as a whole become dirty. That, at least, is how things should work. But, once again, scalars within tables can be troublesome; creating or setting a scalar in the database can dirty its containing table without dirtying the database. Also, Frontier might not share your idea of what modifies a non-scalar; for instance, changing the expansion state of subheads in an outline does not necessarily dirty the outline.
The situation is rather unsatisfactory, and somewhat diminishes the value of the following verbs.
To learn whether a given non-scalar is dirty:
window.isModified (addrObject)
To specify whether a given non-scalar should be considered dirty:
window.setModified (addrObject, dirty?)
Declaring the database as a whole to be non-dirty permits the database to be closed without saving and without the "Save?" dialog appearing. Otherwise, I find that window.setModified() with a dirty? value of true often doesn't work, and that the best way to ensure the dirtiness of an object is to alter its text.
To learn when a non-scalar was created or last modified:
timeCreated (addrObject)
timeModified (addrObject)
You can learn the timeCreated() and timeModified() of an object manually by selecting it within its containing table and choosing Get Info from the Table menu.
TOP | UP: Data Manipulation | NEXT: Non-Scalars |