Greg Slepak avatar Greg Slepak committed 6f69f4e

on the way to 0.7

Comments (0)

Files changed (17)

example-site/dragonfly-api/SMTP/index.html

 <blockquote>
 <center><h1>Index</h1></center>
 <a href="smtp.lsp.html"><br/><h2>Module:&nbsp;smtp.lsp </h2></a>
-<p>Send mail using SMTP protocol</p>
+<p>Send mail using SMTP protocol. As of newLISP 10.2 this module comes with newLISP's built-in modules as smtpx.lsp.</p>
 <a href="smtp.lsp.html#SMTP_send-mail">send-mail</a>&nbsp; &nbsp; <a href="smtp.lsp.html#SMTP_get-error-text">get-error-text</a>&nbsp; &nbsp; <a href="smtp.lsp.html#SMTP_clear-attachments">clear-attachments</a>&nbsp; &nbsp; <a href="smtp.lsp.html#SMTP_attach-document">attach-document</a>&nbsp; &nbsp; <a href="smtp.lsp.html#SMTP_encode64-widthsafe">encode64-widthsafe</a>&nbsp; &nbsp; <a href="smtp.lsp.html#SMTP_encode64-line">encode64-line</a>&nbsp; &nbsp; 
 <br/><br/><center>- &part; -</center><br/>
 <center><font face='Arial' size='-2' color='#444444'>

example-site/dragonfly-api/SMTP/smtp.lsp.html

 			link="#376590" vlink="#551A8B" alink="#ffAA28">
 <blockquote>
 <center><h1>smtp.lsp</h1></center>
-<p><a href="index.html">Module index</a></p><br/><h2>Module:&nbsp;smtp.lsp </h2><p>Send mail using SMTP protocol</p>
+<p><a href="index.html">Module index</a></p><br/><h2>Module:&nbsp;smtp.lsp </h2><p>Send mail using SMTP protocol. As of newLISP 10.2 this module comes with newLISP's built-in modules as smtpx.lsp.</p>
 <b>Version: </b>3.0 - Partial rewrite for Dragonfly. Addition attachments, custom port and proper utf8 encoding for subject/message/attachments<br/>
 <b>Version: </b>2.3 - fix in mail-send-body, thanks to Alessandro<br/>
 <b>Version: </b>2.2 - doc changes<br/>

example-site/dragonfly-api/db/database.lsp.html

  <h3>DF.BLOB</h3>
  <tt>DF.BLOB</tt> is used to insert and retrieve (possibly large) binary data into databases.
  It is needed for two reasons:
- <em>ol</em>
+ <ol>
  <li>newLISP uses strings to buffer and store binary data, and that's already used to store text.</li>
  <li>BLOBs can be very large, so by storing them in a context we avoid excessive copying.</li>
- <em>/ol</em>
+ </ol>
  <p>Unlike the other two classes, <tt>DF.BLOB</tt> provides a basic working implementation for <tt>DF.SQL</tt> subclasses
  to use. You may of course subclass it if your database requires additional functionality. It requires
  special usage considerations, see its documentation below.</p>

example-site/dragonfly-api/db/database_orm.lsp.html

 <p><a href="index.html">Module index</a></p><br/><h2>Module:&nbsp;database_orm</h2><p>DB.OBJ - Simple ORM class for DF.DB</p>
 <b>Version: </b>1.0<br/>
 <b>Author: </b>Greg Slepak<br/>
- <p></p>
- <p>To accomplish this, the interface introduces three Objective newLISP classes:
- <tt>DF.DB</tt>, <tt>DF.SQL</tt>, and <tt>DF.BLOB</tt>.</p>
- <h3>DF.SQL</h3>
- A <tt>DF.SQL</tt> object is a wrapper around an SQL statement, is retrieved through one of
- two functions: <tt>DF.DB:execute-query</tt> and the lower-level <tt>DF.DB:preprare-sql</tt>.
- <p>It is used to retrieve rows from the result set of a query one-by-one.</p>
- <h3>Example</h3>
- <pre>
- (push-autorelease-pool) ; we<tt>re going to be using DF.BLOB</tt>s.
- (setf db (instantiate Sqlite3 ":memory:"))
- (if-not db (throw-error "couldn't open db"))
- (db:execute-update "CREATE TABLE fish (id INTEGER PRIMARY KEY, name TEXT, weight REAL, blah BLOB)")
- (db:execute-update "INSERT INTO fish (name,weight) VALUES (?,?)" '("flipper" 234.123))
- (db:execute-update "INSERT INTO fish (name,weight) VALUES (?1,?2)" '(("?1" "catfish") ("?2" 100.3)))
- (db:execute-update "INSERT INTO fish (blah) VALUES (?)" (list (DF.BLOB (dup "\000" 10))))
- (db:execute-update "INSERT INTO fish (blah) VALUES (:cat)" (list (list ":cat" (DF.BLOB (dup "\000" 10)))))
- (setf sql (db:execute-query "SELECT * FROM fish"))
- (do-while (list? row)
-     (push-autorelease-pool) ; "in case" we end up fetching a lot of large blobs
-     (setf row (sql:next-row))
-     (println "row: " row)
-     (pop-autorelease-pool)
- )
- (deallocate sql)
- (deallocate db)
- (pop-autorelease-pool) ; deallocate the blobs we created</pre>
+ <p><tt>DB.OBJ</tt> provides very basic Object-relational mapping (ORM) for <tt>DF.DB</tt>. Specifically,
+ each <tt>DB.OBJ</tt> object has accessors to manipulate or retrieve the values for some or all of the
+ columns of a specific row.</p>
+ <p>All of the functions documented here are declared global!</p>
+ <h3>Obtaining a <tt>DF.OBJ</tt> object</h3>
+ There are currently three functions for obtaining an object: <tt>create-dbobj</tt>, <tt>find-dbobj</tt> and <tt>find-or-create-dbobj</tt>.
+ <p>To obtain an object, one of the arguments that must be specified is called the <em>finder</em>.</p>
+ <p>The <em>finder</em> can either be multiple things:</p>
+ <ol><li>An <b>integer</b> - in which case it is assumed to refer to the exact ROWID of the row you want.</li>
+ <li>A <b>string</b> - in which case it is greated as the argument to the WHERE clause of an SQL statement. Be careful of SQL injection attacks when choosing this method, it may be preferable to instead use...</li>
+ <li>An <b>association list</b> - For example, to ask for the row(s) where the <tt>name</tt> is "Greg" and <tt>age</tt> is 12, we&apos;d pass: <tt>&apos;(("name" "Greg") ("age" 12))</tt></li>
+ </ol>
+ <p>In order to specify what attributes the object will have, a list is passed in containing (as strings) the desired columns.
+ Because this list simply contains portions of the SQL, <tt>&apos;("*")</tt> may be specified to indicate all columns.</p>
+ <h3>Manipulating values</h3>
+ <p>Once you've obtained an object you fetch its values by simply calling the appropriate method for that value.
+ The name of this method will be based upon the column it corresponds to (it can be altered using SQL aliases). The columns are alternatively called the object&apos;s "attributes" or "keys."</p>
+ <pre> (println "Person who is named: " (*obj*:name))
+ ;=> "John Doe"</pre>
+ <p>To modify a value simply pass in a value as the second argument:</p>
+ <pre> (*obj*:name "Greg Slepak")
+ (println "Person is now named: " (*obj*:name))
+ ;=> "Greg Slepak"</pre>
+ <p>It&apos;s important to note though that the values <b>aren&apos;t saved to the database until you call <tt>dbobj-save</tt> on the object!</b></p>
+ <pre> (dbobj-save *obj*)</pre>
+ <p>Querying a value from an object returns the latest value, even if it's not saved. To ask for the value prior to modification
+ pass <tt>true</tt> in as the third argument:</p>
+ <pre> (*obj*:name "John Doe") ; change it back
+ (println "Original value: " (*obj*:name nil true))
+ ;=> "Greg Slepak"</pre>
+ <h3>Objective newLISP</h3>
+ <p>The objects returned by these functions are ObjNL objects and thus must be properly memory managed using the conventions
+ in ObjNL to avoid memory leaks. See the <tt>release</tt>, <tt>retain</tt> and <tt>autorelease</tt> functions in the ObjNL documentation for more detail.</p>
  <h3>Version history</h3>
  <b>1.0</b> &bull; initial release
 
 
 
 
+<br/><br/><center>- &sect; -</center><br/>
+<a name="_create-dbobj"></a><h3><font color=#CC0000>create-dbobj</font></h3>
+<b>syntax: (<font color=#CC0000>create-dbobj</font> <em>ctx-db</em> <em>str-table</em> <em>list-data</em>)</b><br/>
+<b>parameter: </b><em>ctx-db</em> - A <tt>DF.DB</tt> instance<br/>
+<b>parameter: </b><em>str-table</em> - The table in which this "object" will be created in<br/>
+<b>parameter: </b><em>list-data</em> - Either an association list of column/values or a list of values for all the columns<br/>
+ <p>IMPORTANT: The returned object is NOT autoreleased! YOU are responsible for releasing it when you're done with it!</p>
+ <p><b>example:</b></p>
+ <pre> (setf db (instantiate Sqlite3 ":memory:"))
+ (db:execute-update "CREATE TABLE people (name TEXT, age INTEGER)")
+ (setf *obj* (create-dbobj db "people" '("Sue" 57)))
+ (println "Create a person named: " (*obj*:name))
+ &nbsp;
+ ; now we release it and create another object, this time using the alternate form, without an age:
+ (release *obj*)
+ (setf *obj* (create-dbobj db "people" '(("name" "Billy Jones"))))</pre>
 
+<br/><br/><center>- &sect; -</center><br/>
+<a name="_find-dbobj"></a><h3><font color=#CC0000>find-dbobj</font></h3>
+<b>syntax: (<font color=#CC0000>find-dbobj</font> <em>ctx-db</em> <em>str-table</em> <em>list-cols</em> <em>finder</em> [<em>limit</em>])</b><br/>
+<b>parameter: </b><em>ctx-db</em> - A <tt>DF.DB</tt> instance<br/>
+<b>parameter: </b><em>str-table</em> - The table in which this "object" will be created in<br/>
+<b>parameter: </b><em>list-cols</em> - A list of column names. You can use SQL aliases (e.g. "col AS alias") and special identifiers (like "*")<br/>
+<b>parameter: </b><em>finder</em> - As described at the start of this document. See example below too.<br/>
+<b>parameter: </b><em>int-limit</em> - Default is 1. If greater than 1 then a list of objects is returned.<br/>
+ <p><b>example:</b></p>
+ <pre> (push-autorelease-pool) ; even examples should show proper memory management
+ (setf db (instantiate Sqlite3 "path/to/people.db"))
+ ; get the object at row 10
+ (setf *obj* (autorelease (find-dbobj db "people" '("*") 10)))
+ ; get up to 50 teenagers
+ (setf teens (find-dbobj db "people" '("*") "age > 12 AND age < 20" 50))
+ (map autorelease teens)
+ ; find a person of a random age between 1 and 20
+ (setf X (int (random 1 20)))
+ (setf *obj* (autorelease (find-dbobj db "people" '("*") (list (list "age" X)))))
+ (pop-autorelease-pool)</pre>
 
+<br/><br/><center>- &sect; -</center><br/>
+<a name="_find-or-create-dbobj"></a><h3><font color=#CC0000>find-or-create-dbobj</font></h3>
+<b>syntax: (<font color=#CC0000>find-or-create-dbobj</font> <em>ctx-db</em> <em>str-table</em> <em>list-data</em> <em>finder</em>)</b><br/>
+ <p>This function simply calls <tt>create-dbobj</tt> if <tt>find-dbobj</tt> is unable to locate the object(s).
+ The values in <em>list-data</em> are ignored if an object is found, and the found object's values are used instead.</p>
+ <p>Note: unlike the more flexible <tt>create-dbobj</tt>, the <em>list-data</em> param must be an association list of columns/values.</p>
 
 
+<br/><br/><center>- &sect; -</center><br/>
+<a name="_dbobj-keys"></a><h3><font color=#CC0000>dbobj-keys</font></h3>
+<b>syntax: (<font color=#CC0000>dbobj-keys</font> <em>obj</em>)</b><br/>
+ <p>Returns the keys as strings in a list. Different words, same thing.</p>
 
+<br/><br/><center>- &sect; -</center><br/>
+<a name="_dbobj-values"></a><h3><font color=#CC0000>dbobj-values</font></h3>
+<b>syntax: (<font color=#CC0000>dbobj-values</font> <em>obj</em> [<em>bool-from-revert-set</em>])</b><br/>
+<b>parameter: </b><em>bool-from-revert-set</em> - If true, returns the uncommitted value.<br/>
+ <p>Returns a list of the values for all the keys.</p>
 
+<br/><br/><center>- &sect; -</center><br/>
+<a name="_dbobj-set-finder"></a><h3><font color=#CC0000>dbobj-set-finder</font></h3>
+<b>syntax: (<font color=#CC0000>dbobj-set-finder</font> <em>obj</em> <em>finder</em>)</b><br/>
+ <p>Updates how this object finds itself in the table (for updates).</p>
 
+<br/><br/><center>- &sect; -</center><br/>
+<a name="_dbobj-refetch"></a><h3><font color=#CC0000>dbobj-refetch</font></h3>
+<b>syntax: (<font color=#CC0000>dbobj-refetch</font> <em>obj</em>)</b><br/>
+ <p>Refetches the object's values from the table. Discards any changes. Returns an association list of the fetched keys/values.</p>
 
+<br/><br/><center>- &sect; -</center><br/>
+<a name="_dbobj-save"></a><h3><font color=#CC0000>dbobj-save</font></h3>
+<b>syntax: (<font color=#CC0000>dbobj-save</font> <em>obj</em>)</b><br/>
+ <p>Saves any changes to the database.</p>
+<p><b>return: </b>a list of saved differences on successful update, 0 if no update was needed, or nil if update failed</p>
 
-
+<br/><br/><center>- &sect; -</center><br/>
+<a name="_dbobj-delete"></a><h3><font color=#CC0000>dbobj-delete</font></h3>
+<b>syntax: (<font color=#CC0000>dbobj-delete</font> <em>obj</em>)</b><br/>
+ <p>Removes the row representing this <em>obj</em> from its table. Returns <tt>true</tt> on success.</p>
 
 
 

example-site/dragonfly-api/db/database_utils.lsp.html

 			link="#376590" vlink="#551A8B" alink="#ffAA28">
 <blockquote>
 <center><h1>database_utils.lsp</h1></center>
-<p><a href="index.html">Module index</a></p><br/><h2>Module:&nbsp;database_utils</h2><p>Utilities for using DF.DB</p>
+<p><a href="index.html">Module index</a></p><br/><h2>Module:&nbsp;database_utils</h2><p>Utilities for grabbing data out of a DF.DB</p>
 <b>Version: </b>1.0<br/>
 <b>Author: </b>Greg Slepak<br/>
+ <p>This file provides a convenience layer above the <tt>DF.DB</tt> and <tt>DF.SQL</tt> basic spec for fetching data.</p>
+ <p>All of the functions in this file are declared as global functions!</p>
 
 
 <br/><br/><center>- &sect; -</center><br/>
 <a name="_cast-if"></a><h3><font color=#CC0000>cast-if</font></h3>
 <b>syntax: (<font color=#CC0000>cast-if</font> <em>fn-test</em> <em>to</em> <em>from</em>)</b><br/>
- Equivalent to: <tt>(if (fn-test from) to from)</tt>
+ <p>Equivalent to: <tt>(if (fn-test from) to from)</tt></p>
+ <p>This is a useful function when you want to ensure that certain "empty" values get
+ mapped to something else, for example if you want to make sure that you adds NULLs instead
+ of empty strings to a column you'd use it like this:</p>
+ <pre> (cast-if null? nil value)</pre>
 
+<br/><br/><center>- &sect; -</center><br/>
+<a name="_for-query-with-db"></a><h3><font color=#CC0000>for-query-with-db</font></h3>
+<b>syntax: (<font color=#CC0000>for-query-with-db</font> <em>ctx-db</em> <em>str-query</em> [<em>body...</em>])</b><br/>
+ <p>This macro is useful for writing templates, say for example to display a table of data, or simply
+ to iterate over a set of results. It takes an SQL query string and then a body.
+ All of the column names from the SQL result are available for use in the
+ body as uppercase labels, which are then substituted with their values.</p>
+ <p><b>example:</b></p>
+ <pre> &lt;table&gt;
+ 	&lt;tr class="header"&gt;&lt;td&gt;ID&lt;/td&gt;&lt;td&gt;Name&lt;/td&gt;&lt;td&gt;Age&lt;/td&gt;&lt;/tr&gt;
+ 	&lt;% (for-query-with-db db "SELECT rowid,name,age FROM people" %&gt;
+ 		&lt;tr&gt;
+ 			&lt;td&gt;&lt;%=ROWID%&gt;&lt;/td&gt;
+ 			&lt;td&gt;&lt;%=NAME%&gt;&lt;/td&gt;
+ 			&lt;td&gt;&lt;%=AGE%&gt;&lt;/td&gt;
+ 		&lt;/tr&gt;
+ 	&lt;% ) %&gt;
+ &lt;/table&gt;</pre>
+ <p>This function requires <b>newLISP version >= 10.1.11</b>.</p>
 
+<br/><br/><center>- &sect; -</center><br/>
+<a name="_fn-query-with-db"></a><h3><font color=#CC0000>fn-query-with-db</font></h3>
+<b>syntax: (<font color=#CC0000>fn-query-with-db</font> <em>ctx-db</em> <em>str-query</em> <em>func</em> [<em>list-params</em>])</b><br/>
+ <p>This function is similar to <tt>for-query-with-db</tt> except that instead of taking
+ a body directly, you pass in a function <tt>func</tt> that takes a single argument&mdash;the
+ results as an association list&mdash;which contains the body that will be executed
+ for each of the rows. Additionally, this function allows you to use queries safely
+ with a <tt>WHERE</tt> clause by supplying parameters through <tt>list-params</tt>.</p>
+ <p><b>example:</b></p>
+ <pre> &lt;table&gt;
+ 	&lt;tr class="header"&gt;&lt;td&gt;ID&lt;/td&gt;&lt;td&gt;Name&lt;/td&gt;&lt;td&gt;Age&lt;/td&gt;&lt;/tr&gt;
+ 	&lt;% (fn-query-with-db db "SELECT rowid,name,age FROM people WHERE age < ?" (fn (row) %&gt;
+ 		&lt;tr&gt;
+ 			&lt;td&gt;&lt;%=(&lt;- "rowid" row)%&gt;&lt;/td&gt;
+ 			&lt;td&gt;&lt;%=(&lt;- "name" row)%&gt;&lt;/td&gt;
+ 			&lt;td&gt;&lt;%=(&lt;- "age" row)%&gt;&lt;/td&gt;
+ 		&lt;/tr&gt;
+ 	&lt;% ) '(25)) %&gt;
+ &lt;/table&gt;</pre>
 
+<br/><br/><center>- &sect; -</center><br/>
+<a name="_assoc-row-with-db"></a><h3><font color=#CC0000>assoc-row-with-db</font></h3>
+<b>syntax: (<font color=#CC0000>assoc-row-with-db</font> <em>ctx-db</em> <em>str-query</em> [<em>list-params</em>])</b><br/>
+<p><b>return: </b>An association list representing a single row where the keys are the column names and the values are the values for that column</p>
+<b>example:</b><blockquote><pre> (assoc-row-with-db db "SELECT name,age FROM people WHERE age &lt; ?" '(25))
+ ;=&gt; (("name" "Sally") ("age" 12))</pre></blockquote>
+<br/><br/><center>- &sect; -</center><br/>
+<a name="_assoc-rows-with-db"></a><h3><font color=#CC0000>assoc-rows-with-db</font></h3>
+<b>syntax: (<font color=#CC0000>assoc-rows-with-db</font> <em>ctx-db</em> <em>str-query</em> [<em>list-params</em>])</b><br/>
+ <p>Like <tt>assoc-row-with-db</tt> except returns multiple association lists for all the returned rows.</p>
 
-
+<br/><br/><center>- &sect; -</center><br/>
+<a name="_query-cell-with-db"></a><h3><font color=#CC0000>query-cell-with-db</font></h3>
+<b>syntax: (<font color=#CC0000>query-cell-with-db</font> <em>ctx-db</em> <em>str-query</em> [<em>list-params</em>])</b><br/>
+<p><b>return: </b>The exact value at a specific row/column or <tt>nil</tt> if not found.</p>
 
 <br/><br/><center>- &part; -</center><br/>
 <center><font face='Arial' size='-2' color='#444444'>

example-site/dragonfly-api/db/index.html

 <p>Generic database access interface for <a href="http://www.rundragonfly.com">Dragonfly</a> using Objective newLISP</p>
 <a href="database.lsp.html#_DF.DB">DF.DB</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.DB_open">open</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.DB_close">close</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.DB_prepare-sql">prepare-sql</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.DB_execute-update">execute-update</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.DB_execute-query">execute-query</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.DB_rows-for-query">rows-for-query</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.DB_rowid">rowid</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.DB_changes">changes</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.DB_version">version</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.DB_table-exists?">table-exists?</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.DB_connected?">connected?</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.DB_last-error">last-error</a>&nbsp; &nbsp; <a href="database.lsp.html#_DF.SQL">DF.SQL</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.SQL_bind-params">bind-params</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.SQL_next-row">next-row</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.SQL_reset">reset</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.SQL_close">close</a>&nbsp; &nbsp; <a href="database.lsp.html#DF.BLOB_DF.BLOB">DF.BLOB</a>&nbsp; &nbsp; <a href="database_orm.lsp.html"><br/><h2>Module:&nbsp;database_orm</h2></a>
 <p>DB.OBJ - Simple ORM class for DF.DB</p>
-<a href="database_sqlite3.lsp.html"><br/><h2>Module:&nbsp;database_sqlite3</h2></a>
+<a href="database_orm.lsp.html#_create-dbobj">create-dbobj</a>&nbsp; &nbsp; <a href="database_orm.lsp.html#_find-dbobj">find-dbobj</a>&nbsp; &nbsp; <a href="database_orm.lsp.html#_find-or-create-dbobj">find-or-create-dbobj</a>&nbsp; &nbsp; <a href="database_orm.lsp.html#_dbobj-keys">dbobj-keys</a>&nbsp; &nbsp; <a href="database_orm.lsp.html#_dbobj-values">dbobj-values</a>&nbsp; &nbsp; <a href="database_orm.lsp.html#_dbobj-set-finder">dbobj-set-finder</a>&nbsp; &nbsp; <a href="database_orm.lsp.html#_dbobj-refetch">dbobj-refetch</a>&nbsp; &nbsp; <a href="database_orm.lsp.html#_dbobj-save">dbobj-save</a>&nbsp; &nbsp; <a href="database_orm.lsp.html#_dbobj-delete">dbobj-delete</a>&nbsp; &nbsp; <a href="database_sqlite3.lsp.html"><br/><h2>Module:&nbsp;database_sqlite3</h2></a>
 <p>SQLite3 subclass of DF.DB. Only lists Sqlite3 specific functions.</p>
 <a href="database_sqlite3.lsp.html#_Sqlite3">Sqlite3</a>&nbsp; &nbsp; <a href="database_sqlite3.lsp.html#Sqlite3_open">open</a>&nbsp; &nbsp; <a href="database_sqlite3.lsp.html#Sqlite3_set-timeout">set-timeout</a>&nbsp; &nbsp; <a href="database_utils.lsp.html"><br/><h2>Module:&nbsp;database_utils</h2></a>
-<p>Utilities for using DF.DB</p>
-<a href="database_utils.lsp.html#_cast-if">cast-if</a>&nbsp; &nbsp; 
+<p>Utilities for grabbing data out of a DF.DB</p>
+<a href="database_utils.lsp.html#_cast-if">cast-if</a>&nbsp; &nbsp; <a href="database_utils.lsp.html#_for-query-with-db">for-query-with-db</a>&nbsp; &nbsp; <a href="database_utils.lsp.html#_fn-query-with-db">fn-query-with-db</a>&nbsp; &nbsp; <a href="database_utils.lsp.html#_assoc-row-with-db">assoc-row-with-db</a>&nbsp; &nbsp; <a href="database_utils.lsp.html#_assoc-rows-with-db">assoc-rows-with-db</a>&nbsp; &nbsp; <a href="database_utils.lsp.html#_query-cell-with-db">query-cell-with-db</a>&nbsp; &nbsp; 
 <br/><br/><center>- &part; -</center><br/>
 <center><font face='Arial' size='-2' color='#444444'>
 generated with <a href="http://newlisp.org">newLISP</a>&nbsp;

example-site/dragonfly-api/dragonfly.lsp.html

  If <tt>resource</tt> implements <tt>action</tt>, then that function is called.
  Like <tt>resource</tt>, <tt>action</tt> may only contain letters, numbers, and the underscore.
  If no <tt>action</tt> is specified, then the resource's default function is called instead.
+ If it is specified but no method of that name is found, <tt>catch-all</tt> will be called, which
+ by default calls <tt>Dragonfly:die</tt>. You can override <tt>catch-all</tt> and do dispatch within
+ it, as it takes the action name (as a string) as the first argument, followed by the
+ <tt>id</tt> and the <tt>response_format</tt>.
  <p>The optional paramters <tt>id</tt> and <tt>response_format</tt> are passed in to the function
- as parameters (in that order).</p>
+ as parameters (in that order, unless it is the <tt>catch-all</tt> function, in which case
+ 3 parameters are passed in as described above).</p>
  <p><tt>id</tt> may only contain numbers, and <tt>response_format</tt> may only contain letters.</p>
  <h3>Plugins</h3>
  There are two types of plugins, those in the <tt>plugins-active</tt> folder, and those

example-site/dragonfly-api/smtp/index.html

 <blockquote>
 <center><h1>Index</h1></center>
 <a href="smtp.lsp.html"><br/><h2>Module:&nbsp;smtp.lsp </h2></a>
-<p>Send mail using SMTP protocol</p>
+<p>Send mail using SMTP protocol. As of newLISP 10.2 this module comes with newLISP's built-in modules as smtpx.lsp.</p>
 <a href="smtp.lsp.html#SMTP_send-mail">send-mail</a>&nbsp; &nbsp; <a href="smtp.lsp.html#SMTP_get-error-text">get-error-text</a>&nbsp; &nbsp; <a href="smtp.lsp.html#SMTP_clear-attachments">clear-attachments</a>&nbsp; &nbsp; <a href="smtp.lsp.html#SMTP_attach-document">attach-document</a>&nbsp; &nbsp; <a href="smtp.lsp.html#SMTP_encode64-widthsafe">encode64-widthsafe</a>&nbsp; &nbsp; <a href="smtp.lsp.html#SMTP_encode64-line">encode64-line</a>&nbsp; &nbsp; 
 <br/><br/><center>- &part; -</center><br/>
 <center><font face='Arial' size='-2' color='#444444'>

example-site/dragonfly-api/smtp/smtp.lsp.html

 			link="#376590" vlink="#551A8B" alink="#ffAA28">
 <blockquote>
 <center><h1>smtp.lsp</h1></center>
-<p><a href="index.html">Module index</a></p><br/><h2>Module:&nbsp;smtp.lsp </h2><p>Send mail using SMTP protocol</p>
+<p><a href="index.html">Module index</a></p><br/><h2>Module:&nbsp;smtp.lsp </h2><p>Send mail using SMTP protocol. As of newLISP 10.2 this module comes with newLISP's built-in modules as smtpx.lsp.</p>
 <b>Version: </b>3.0 - Partial rewrite for Dragonfly. Addition attachments, custom port and proper utf8 encoding for subject/message/attachments<br/>
 <b>Version: </b>2.3 - fix in mail-send-body, thanks to Alessandro<br/>
 <b>Version: </b>2.2 - doc changes<br/>

example-site/dragonfly-framework/dragonfly.lsp

 ;; If 'resource' implements 'action', then that function is called.
 ;; Like 'resource', 'action' may only contain letters, numbers, and the underscore.
 ;; If no 'action' is specified, then the resource's default function is called instead.
+;; If it is specified but no method of that name is found, 'catch-all' will be called, which
+;; by default calls 'Dragonfly:die'. You can override 'catch-all' and do dispatch within
+;; it, as it takes the action name (as a string) as the first argument, followed by the
+;; 'id' and the 'response_format'.
 ;; <p>The optional paramters 'id' and 'response_format' are passed in to the function
-;; as parameters (in that order).</p>
+;; as parameters (in that order, unless it is the 'catch-all' function, in which case
+;; 3 parameters are passed in as described above).</p>
 ;; <p>'id' may only contain numbers, and 'response_format' may only contain letters.</p>
 ;; <h3>Plugins</h3>
 ;; There are two types of plugins, those in the 'plugins-active' folder, and those

example-site/dragonfly-framework/plugins-inactive/db/database_orm.lsp

 ;; @description DB.OBJ - Simple ORM class for DF.DB
 ;; @version 1.0
 ;; @author Greg Slepak
-;; <p></p>
-;; <p>To accomplish this, the interface introduces three Objective newLISP classes:
-;; 'DF.DB', 'DF.SQL', and 'DF.BLOB'.</p>
-;; <h3>DF.SQL</h3>
-;; A 'DF.SQL' object is a wrapper around an SQL statement, is retrieved through one of
-;; two functions: 'DF.DB:execute-query' and the lower-level 'DF.DB:preprare-sql'.
-;; <p>It is used to retrieve rows from the result set of a query one-by-one.</p>
-;; <h3>Example</h3>
-;; <pre>
-;; (push-autorelease-pool) ; we're going to be using DF.BLOB's.
-;; (setf db (instantiate Sqlite3 ":memory:"))
-;; (if-not db (throw-error "couldn't open db"))
-;; (db:execute-update "CREATE TABLE fish (id INTEGER PRIMARY KEY, name TEXT, weight REAL, blah BLOB)")
-;; (db:execute-update "INSERT INTO fish (name,weight) VALUES (?,?)" '("flipper" 234.123))
-;; (db:execute-update "INSERT INTO fish (name,weight) VALUES (?1,?2)" '(("?1" "catfish") ("?2" 100.3)))
-;; (db:execute-update "INSERT INTO fish (blah) VALUES (?)" (list (DF.BLOB (dup "\000" 10))))
-;; (db:execute-update "INSERT INTO fish (blah) VALUES (:cat)" (list (list ":cat" (DF.BLOB (dup "\000" 10)))))
-;; (setf sql (db:execute-query "SELECT * FROM fish"))
-;; (do-while (list? row)
-;;     (push-autorelease-pool) ; "in case" we end up fetching a lot of large blobs
-;;     (setf row (sql:next-row))
-;;     (println "row: " row)
-;;     (pop-autorelease-pool)
-;; )
-;; (deallocate sql)
-;; (deallocate db)
-;; (pop-autorelease-pool) ; deallocate the blobs we created</pre>
+;; <p>'DB.OBJ' provides very basic Object-relational mapping (ORM) for 'DF.DB'. Specifically,
+;; each 'DB.OBJ' object has accessors to manipulate or retrieve the values for some or all of the
+;; columns of a specific row.</p>
+;; <p>All of the functions documented here are declared global!</p>
+;; <h3>Obtaining a 'DF.OBJ' object</h3>
+;; There are currently three functions for obtaining an object: 'create-dbobj', 'find-dbobj' and 'find-or-create-dbobj'.
+;; <p>To obtain an object, one of the arguments that must be specified is called the <finder>.</p>
+;; <p>The <finder> can either be multiple things:</p>
+;; <ol><li>An <b>integer</b> - in which case it is assumed to refer to the exact ROWID of the row you want.</li>
+;; <li>A <b>string</b> - in which case it is greated as the argument to the WHERE clause of an SQL statement. Be careful of SQL injection attacks when choosing this method, it may be preferable to instead use...</li>
+;; <li>An <b>association list</b> - For example, to ask for the row(s) where the 'name' is "Greg" and 'age' is 12, we&apos;d pass: '&apos;(("name" "Greg") ("age" 12))'</li>
+;; </ol>
+;; <p>In order to specify what attributes the object will have, a list is passed in containing (as strings) the desired columns.
+;; Because this list simply contains portions of the SQL, '&apos;("*")' may be specified to indicate all columns.</p>
+;; <h3>Manipulating values</h3>
+;; <p>Once you've obtained an object you fetch its values by simply calling the appropriate method for that value.
+;; The name of this method will be based upon the column it corresponds to (it can be altered using SQL aliases). The columns are alternatively called the object&apos;s "attributes" or "keys."</p>
+;; <pre> (println "Person who is named: " (*obj*:name))
+;; ;=> "John Doe"</pre>
+;; <p>To modify a value simply pass in a value as the second argument:</p>
+;; <pre> (*obj*:name "Greg Slepak")
+;; (println "Person is now named: " (*obj*:name))
+;; ;=> "Greg Slepak"</pre>
+;; <p>It&apos;s important to note though that the values <b>aren&apos;t saved to the database until you call 'dbobj-save' on the object!</b></p>
+;; <pre> (dbobj-save *obj*)</pre>
+;; <p>Querying a value from an object returns the latest value, even if it's not saved. To ask for the value prior to modification
+;; pass 'true' in as the third argument:</p>
+;; <pre> (*obj*:name "John Doe") ; change it back
+;; (println "Original value: " (*obj*:name nil true))
+;; ;=> "Greg Slepak"</pre>
+;; <h3>Objective newLISP</h3>
+;; <p>The objects returned by these functions are ObjNL objects and thus must be properly memory managed using the conventions
+;; in ObjNL to avoid memory leaks. See the 'release', 'retain' and 'autorelease' functions in the ObjNL documentation for more detail.</p>
 ;; <h3>Version history</h3>
 ;; <b>1.0</b> &bull; initial release
 
 
 (new-class 'DB.OBJ)
 
+; TODO: transform all of these into an object-based system of transformations (i.e. Sqlite3Adapter, etc.)
 (set (global 'DBOBJ_SELECT_SQL)   (or DBOBJ_SELECT_SQL   "SELECT %s FROM %s WHERE %s LIMIT %d")
      (global 'DBOBJ_SELECT_SQL2)  (or DBOBJ_SELECT_SQL2  "SELECT * FROM %s") ; b/c INSERT doesn't tell us. we don't actually retrieve the rows.
      (global 'DBOBJ_UPDATE_SQL)   (or DBOBJ_UPDATE_SQL   "UPDATE %s SET %s=? WHERE %s")
 ; !Getting DF.OBJs
 ;---------------------------------------------------------------
 
-; The returned object is NOT autoreleased! YOU are responsible for releasing it when you're done with it!
+;; @syntax (create-dbobj <ctx-db> <str-table> <list-data>)
+;; @param <ctx-db> A 'DF.DB' instance
+;; @param <str-table> The table in which this "object" will be created in
+;; @param <list-data> Either an association list of column/values or a list of values for all the columns
+;; <p>IMPORTANT: The returned object is NOT autoreleased! YOU are responsible for releasing it when you're done with it!</p>
+;; <p><b>example:</b></p>
+;; <pre> (setf db (instantiate Sqlite3 ":memory:"))
+;; (db:execute-update "CREATE TABLE people (name TEXT, age INTEGER)")
+;; (setf *obj* (create-dbobj db "people" '("Sue" 57)))
+;; (println "Create a person named: " (*obj*:name))
+;; &nbsp;
+;; ; now we release it and create another object, this time using the alternate form, without an age:
+;; (release *obj*)
+;; (setf *obj* (create-dbobj db "people" '(("name" "Billy Jones"))))</pre>
 (define (create-dbobj db table data , qs sql cols result rowid idx)
 	(setf qs (join (dup "?" (length data) true) ","))
 	(if (list? (first data))
 	)
 )
 
-; The returned object is NOT autoreleased! YOU are responsible for releasing it when you're done with it!
+;; @syntax (find-dbobj <ctx-db> <str-table> <list-cols> <finder> [<limit>])
+;; @param <ctx-db> A 'DF.DB' instance
+;; @param <str-table> The table in which this "object" will be created in
+;; @param <list-cols> A list of column names. You can use SQL aliases (e.g. "col AS alias") and special identifiers (like "*")
+;; @param <finder> As described at the start of this document. See example below too.
+;; @param <int-limit> Default is 1. If greater than 1 then a list of objects is returned.
+; <p>IMPORTANT: The returned object is NOT autoreleased! YOU are responsible for releasing it when you're done with it!</p>
+;; <p><b>example:</b></p>
+;; <pre> (push-autorelease-pool) ; even examples should show proper memory management
+;; (setf db (instantiate Sqlite3 "path/to/people.db"))
+;; ; get the object at row 10
+;; (setf *obj* (autorelease (find-dbobj db "people" '("*") 10)))
+;; ; get up to 50 teenagers
+;; (setf teens (find-dbobj db "people" '("*") "age > 12 AND age < 20" 50))
+;; (map autorelease teens)
+;; ; find a person of a random age between 1 and 20
+;; (setf X (int (random 1 20)))
+;; (setf *obj* (autorelease (find-dbobj db "people" '("*") (list (list "age" X)))))
+;; (pop-autorelease-pool)</pre>
 (define (find-dbobj db table cols finder (limit 1) , data)
 	(when (integer? finder) (setf finder (string DBOBJ_ROWID_FINDER finder)))
 	(push DBOBJ_ROWID_COL cols)
 	)
 )
 
-
+;; @syntax (find-or-create-dbobj <ctx-db> <str-table> <list-data> <finder>)
+;; <p>This function simply calls 'create-dbobj' if 'find-dbobj' is unable to locate the object(s).
+;; The values in <list-data> are ignored if an object is found, and the found object's values are used instead.</p>
+;; <p>Note: unlike the more flexible 'create-dbobj', the <list-data> param must be an association list of columns/values.</p>
 (define (find-or-create-dbobj db table data finder)
 	(unless (find-dbobj db table (map first data) finder)
 		(create-dbobj db table data)
 ; !Manipulating DF.OBJs
 ;---------------------------------------------------------------
 
+;; @syntax (dbobj-keys <obj>)
+;; <p>Returns the keys as strings in a list. Different words, same thing.</p>
 (define (dbobj-keys obj)
 	(map first obj:change-set)
 )
 
+;; @syntax (dbobj-values <obj> [<bool-from-revert-set>])
+;; @param <bool-from-revert-set> If true, returns the uncommitted value.
+;; <p>Returns a list of the values for all the keys.</p>
 (define (dbobj-values obj from-revert-set)
 	(if from-revert-set
 		(map last obj:revert-set)
 	)
 )
 
+;; @syntax (dbobj-set-finder <obj> <finder>)
+;; <p>Updates how this object finds itself in the table (for updates).</p>
 (define (dbobj-set-finder obj finder)
 	(if (integer? finder)
 		(setf obj:finder (string DBOBJ_ROWID_FINDER finder))
 	)
 )
 
+;; @syntax (dbobj-refetch <obj>)
+;; <p>Refetches the object's values from the table. Discards any changes. Returns an association list of the fetched keys/values.</p>
 (define (dbobj-refetch obj)
 	(set 'obj:dirty      nil
 	     'obj:revert-set (first (dbobj-assoc-rows obj:db obj:table (map first obj:revert-set) obj:finder 1))
 	)
 )
 
-; returns list of saved differences on successful update, 0 if no update was needed, or nil if update failed
+;; @syntax (dbobj-save <obj>)
+;; <p>Saves any changes to the database.</p>
+;; @return a list of saved differences on successful update, 0 if no update was needed, or nil if update failed
 (define (dbobj-save obj , diff)
 	(if (null? (setf diff (difference obj:change-set obj:revert-set)))
 		0
 	)
 )
 
+;; @syntax (dbobj-delete <obj>)
+;; <p>Removes the row representing this <obj> from its table. Returns 'true' on success.</p>
 (define (dbobj-delete obj)
 	(when (dbobj-do-delete obj:db obj:table obj:finder)
 		(set 'obj:revert-set '() 'obj:change-set '())
 	(when (setf change-set (setf revert-set data))
 		(dolist (col (map first revert-set))
 			(letex (attr-sym (sym col) attr-str col)
-				(define (attr-sym value from-revert-set)
-					(if value
-						(begin
-							(setf (<- attr-str change-set) value)
-							(setf dirty true)
-							(when from-revert-set (setf (<- attr-str revert-set) value))) ; only do this if you're *SURE*
-						(if from-revert-set
-							(<- attr-str revert-set)
-							(<- attr-str change-set))))))))
+				(define (attr-sym)
+					(case (length $args)
+						(0	(<- attr-str change-set))
+						(1	(setf dirty true)
+							(setf (<- attr-str change-set) (first $args)))
+						(2	(if (args 1)
+								(if (first $args)
+									(setf (<- attr-str revert-set) (first $args)) ; only do this if you're *SURE*
+									(<- attr-str revert-set))
+								(attr-sym (first $args)))))))))) ; this is an odd scenario, but I suppose possible
 
 (context MAIN)

example-site/dragonfly-framework/plugins-inactive/db/database_utils.lsp

 ;; @module database_utils
-;; @description Utilities for using DF.DB
+;; @description Utilities for grabbing data out of a DF.DB
 ;; @version 1.0
 ;; @author Greg Slepak
+;; <p>This file provides a convenience layer above the 'DF.DB' and 'DF.SQL' basic spec for fetching data.</p>
+;; <p>All of the functions in this file are declared as global functions!</p>
 
 (context MAIN)
 
 ;; @syntax (cast-if <fn-test> <to> <from>)
-;; Equivalent to: '(if (fn-test from) to from)'
+;; <p>Equivalent to: '(if (fn-test from) to from)'</p>
+;; <p>This is a useful function when you want to ensure that certain "empty" values get
+;; mapped to something else, for example if you want to make sure that you adds NULLs instead
+;; of empty strings to a column you'd use it like this:</p>
+;; <pre> (cast-if null? nil value)</pre>
 (define (cast-if test to from)
 	(if (test from) to from)
 )
 (global 'cast-if)
 
+;; @syntax (for-query-with-db <ctx-db> <str-query> [<body...>])
+;; <p>This macro is useful for writing templates, say for example to display a table of data, or simply
+;; to iterate over a set of results. It takes an SQL query string and then a body.
+;; All of the column names from the SQL result are available for use in the
+;; body as uppercase labels, which are then substituted with their values.</p>
+;; <p><b>example:</b></p>
+;; <pre> &lt;table&gt;
+;; 	&lt;tr class="header"&gt;&lt;td&gt;ID&lt;/td&gt;&lt;td&gt;Name&lt;/td&gt;&lt;td&gt;Age&lt;/td&gt;&lt;/tr&gt;
+;; 	&lt;% (for-query-with-db db "SELECT rowid,name,age FROM people" %&gt;
+;; 		&lt;tr&gt;
+;; 			&lt;td&gt;&lt;%=ROWID%&gt;&lt;/td&gt;
+;; 			&lt;td&gt;&lt;%=NAME%&gt;&lt;/td&gt;
+;; 			&lt;td&gt;&lt;%=AGE%&gt;&lt;/td&gt;
+;; 		&lt;/tr&gt;
+;; 	&lt;% ) %&gt;
+;; &lt;/table&gt;</pre>
+;; <p>This function requires <b>newLISP version >= 10.1.11</b>.</p>
 ; define-smacro defined in utils.lsp (part of Dragonfly's core functions)
 (define-smacro (for-query-with-db db query)
 	(letn (ctx (prefix db) db (eval db) sql (db:prepare-sql (eval query)) keys '() values)
 	)
 )
 
+;; @syntax (fn-query-with-db <ctx-db> <str-query> <func> [<list-params>])
+;; <p>This function is similar to 'for-query-with-db' except that instead of taking
+;; a body directly, you pass in a function 'func' that takes a single argument&mdash;the
+;; results as an association list&mdash;which contains the body that will be executed
+;; for each of the rows. Additionally, this function allows you to use queries safely
+;; with a 'WHERE' clause by supplying parameters through 'list-params'.</p>
+;; <p><b>example:</b></p>
+;; <pre> &lt;table&gt;
+;; 	&lt;tr class="header"&gt;&lt;td&gt;ID&lt;/td&gt;&lt;td&gt;Name&lt;/td&gt;&lt;td&gt;Age&lt;/td&gt;&lt;/tr&gt;
+;; 	&lt;% (fn-query-with-db db "SELECT rowid,name,age FROM people WHERE age < ?" (fn (row) %&gt;
+;; 		&lt;tr&gt;
+;; 			&lt;td&gt;&lt;%=(&lt;- "rowid" row)%&gt;&lt;/td&gt;
+;; 			&lt;td&gt;&lt;%=(&lt;- "name" row)%&gt;&lt;/td&gt;
+;; 			&lt;td&gt;&lt;%=(&lt;- "age" row)%&gt;&lt;/td&gt;
+;; 		&lt;/tr&gt;
+;; 	&lt;% ) '(25)) %&gt;
+;; &lt;/table&gt;</pre>
 (define (fn-query-with-db db query func params , sql keys values)
 	(when (setf sql (db:prepare-sql query))
 		(when (or (not params) (sql:bind-params params))
 )
 (global 'fn-query-with-db)
 
+;; @syntax (assoc-row-with-db <ctx-db> <str-query> [<list-params>])
+;; @return An association list representing a single row where the keys are the column names and the values are the values for that column
+;; @example
+;; (assoc-row-with-db db "SELECT name,age FROM people WHERE age < ?" '(25))
+;; ;=> (("name" "Sally") ("age" 12))
 (define (assoc-row-with-db db query params , sql keys values result)
 	(when (setf sql (db:prepare-sql query))
 		(when (or (not params) (sql:bind-params params))
 )
 (global 'assoc-row-with-db)
 
+;; @syntax (assoc-rows-with-db <ctx-db> <str-query> [<list-params>])
+;; <p>Like 'assoc-row-with-db' except returns multiple association lists for all the returned rows.</p>
 (define (assoc-rows-with-db db query params , sql keys values rows)
 	(when (setf sql (db:prepare-sql query))
 		(when (and (or (not params) (sql:bind-params params))
 )
 (global 'assoc-rows-with-db)
 
+;; @syntax (query-cell-with-db <ctx-db> <str-query> [<list-params>])
+;; @return The exact value at a specific row/column or 'nil' if not found.
 (define (query-cell-with-db db query params , sql result)
 	(when (setf sql (db:prepare-sql query))
 		(when (or (not params) (sql:bind-params params))

example-site/dragonfly-framework/plugins-inactive/smtp.lsp

 ;; @module smtp.lsp 
-;; @description Send mail using SMTP protocol
+;; @description Send mail using SMTP protocol. As of newLISP 10.2 this module comes with newLISP's built-in modules as smtpx.lsp.
 ;; @version 3.0 - Partial rewrite for Dragonfly. Addition attachments, custom port and proper utf8 encoding for subject/message/attachments
 ;; @version 2.3 - fix in mail-send-body, thanks to Alessandro
 ;; @version 2.2 - doc changes

example-site/views/dragonfly_api.html

 	<div id="content">
 				
 		<div class="title nomargin">
-			<p><% (title "| Dragonfly web framework") %> | <%=Resource.Api:api-path%><p>
+			<p><% (title "| Dragonfly web framework") %><p>
 		</div>
 		
-		<!-- This inserts a div with id #api-browser -->
 		<%
+		; This inserts a div with id #api-browser
 		(if Resource.Api:api-path
 			(api-browser Resource.Api:api-path)
 			(api-browser "dragonfly-api")

example-site/views/dragonfly_builtin_plugins.html

 			newLISP's various database modules as well.
 		</p>
 		<p>
+			DF.DB also comes with a very basic ORM layer called DB.OBJ.
+		</p>
+		<p>
 			You can find it in the <span class="code">plugins-inactive/db</span> folder. The API is
-			<%(link_to "available here" "dragonfly_db_api")%>.
+			<%(link_to "available here" "api/db")%>.
 		</p>
 		<h2>Dragonfly SMTP</h2>
 		<p>

example-site/views/dragonfly_routes.html

 			<span class="code"> my_resource => load file: RESOURCES_PATH/my_resource.lsp</span>
 		</p>
 		<p>
-			<span class="code"><b>action</b></span> (optional) - specifies the function to be called on the resource. Like resource, <span class="code">action</span> may only contain letters, numbers, and the underscore. If no action is specified, then the resource's <a href="http://www.newlisp.org/downloads/newlisp_manual.html#default_function" target="_blank">default function</a> is called instead. Whichever function gets called, the <span class="code">id</span> and <span class="code">format</span> are passed in as parameters (in that order).
+			<span class="code"><b>action</b></span> (optional) - specifies the function to be called on the resource. Like resource, <span class="code">action</span> may only contain letters, numbers, and the underscore. If no action is specified, then the resource's <a href="http://www.newlisp.org/downloads/newlisp_manual.html#default_function" target="_blank">default function</a> is called instead.
+		</p>
+		<p>If an action is specified but it's not defined in the resource, then the resource's <span class="code">catch-all</span> function
+			is called, which by default displays an error 500 page.
+			<br/>
+			Whichever function gets called, the <span class="code">id</span> and <span class="code">format</span> are passed in as parameters, in that order,
+			except in the case of the <span class="code">catch-all</span> function, in which case the first parameter is the name of the requested
+			action, then followed by the <span class="code">id</span> and <span class="code">format</span>.
 		</p>
 		<p>
 			<span class="code"><b>id</b></span> (optional) - may only contain numbers and can be used to specify a specific object out of a collection.

example-site/views/dragonfly_sqlite3.html

 				They are now <%=(*obj*:age)%> years old.
 			</p>
 			<p>
+				<%
+					(*obj*:age 15)
+					(print "they're: " (*obj*:age nil true) " or " (*obj*:age))
+				%>
+			</p>
+			<p>
 				Further, I can create a new person. See:
 				<% (release *obj*) (setf *obj* (create-dbobj db "people" '("Sue" 57))) %>
 			</p>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.