Ur/Web Tutorial

Ur/Web Tutorial

Step 2

Previous | Next

OK, so we succeeded in getting an application running, but it didn't really do much. In this step we are going to add a database and create a way of viewing this data.

Table Schema

We now consider exactly what it is we wish to store in our database. We're building a blog system, so my approximation of useful properties of interest is:

We return now to our urblog.ur source file.

urblog.ur

table entry : { Id : int, Title : string, Created : time, Author : string, Body : string }
  PRIMARY KEY Id

Tables exist as primitive entities in Ur/Web, which is one of the features that permits it to enforce such stringent checks on your programs. For anyone who has struggled through the kinds of web programming where SQL queries are represented as non-semantic strings and in which databases and programs need not agree on their structure, this might come as a refreshing surprise.

With this in mind, we have defined a table 'entry', with some fields. Note that the capitalisation of these field names is significant. Field names start with upper-case letters.

So, we have a table. Now we can construct a function that will query and list all of the entries in this table:

fun list () =
   rows <- queryX (SELECT * FROM entry)
      (fn row => 
          <xml>
            <div>
              <h1>{[row.Entry.Title]}</h1>
              <h2>By {[row.Entry.Author]} at {[row.Entry.Created]}</h2>
              <p>{[row.Entry.Body]}</p>
           </div>
         </xml>
      );
    return 
      <xml>
        <head>
         <title>All Entries</title>
        </head>
        <body>
          <h1>All Entries</h1>
          {rows}
        </body>
      </xml>

If you're reasonably comfortable with map, then our definition of list using queryX should make a reasonable amount of sense. It takes as its two arguments an SQL query and a function that knows how to turn result rows into XML fragments. We complicate things slightly because we bind the result of this to rows so that we can display it inside a custom XML fragment, which we do by applying sequencing (;) and returning rows inside our custom XML fragment.

Once again, if you're familiar with languages where SQL queries are just strings, you'll be pleasantly surprised to find that Ur/Web ensures that your SQL is well-formed and supplies a DBMS-agnostic syntax.

urblog.urs

We need to add our newly defined 'list' function to the signature for our Urblog application, giving it the type for top-level pages that take no parameters:

val list : unit -> transaction page
val main : unit -> transaction page

urblog.urp

We need to configure our database, and where the SQL file that Ur/Web will generate will go:

database dbname=urblog
sql urblog.sql

urblog

The newline before enumerating the filename prefixes is signficant.

Putting it all together

Once again, we need to compile our application. Because we're interacting with a database, you might need to specify which DBMS you would like Ur/Web to compile your application against (PostgreSQL is the default):

urweb -dbms mysql urblog

Depending on your database configuration, you might need to add some extra information via the -db command line option:

urweb -db "host=localhost user=username password=******* dbname=urblog" -dbms mysql urblog

This generates a binary and an SQL file suitable for loading into your database engine. The UrBlog application will expect this database structure to exist within a database called 'urblog'. You will need to create this database automatically and ensure you set appropriate permissions before you load the schema description that Ur/Web generated. For MySQL, the output SQL file might be loaded in this manner:

mysql -u username -p urblog < urblog.sql

Finally, we can access this application via the URL: http://localhost:8080/Urblog/list. There is no data in the database at present, so it's going to be particularly bare. If you're so inclined, you could manually insert some rows into the database table to verify that it works. We'll get back to adding data later.

Previous | Next