Introduction

Model-View-controller

The Model

insert-entry: title url score -> entry-id

increase-score : entry-id ->

decrease-score : entry-id ->

top : natural -> (list (list entry-id title url score))

page : natural -> (list (list entry-id title url score))

url-in-db? : url -> boolean

Implementation of the model

(require (planet "download-sqlite.scm"

("soegaard" "sqlite.plt" 1 0))





(require

(planet "sqlite.ss" ("jaymccarthy" "sqlite.plt"))

(planet "sqlite.ss" ("soegaard" "sqlite.plt" 1 0)))





(define db (open (string->path "c:/listit.sb")))



(define (create-table-entries)

(exec/ignore

db

#<<SQL

CREATE TABLE entries (

entry_id INTEGER PRIMARY KEY,

title TEXT,

url TEXT,

score INTEGER )

SQL

))



exec/ignore



(define (insert-entry title url score)

(insert db (sql (INSERT INTO entries (title url score)

VALUES (,title ,url ,score)))))



sql

insert

(insert-entry "Everything Scheme" "http://www.scheme.dk/blog/" 42)

"INSERT INTO entries (title, url, score) VALUES ('Everything Scheme', 'http://www.scheme.dk/blog/', '42')"

current-database

db

(current-database)

Testing

Welcome to DrScheme , version 369.3-svn10jan2007.

Language: ( module ...).

> ( insert-entry "Everything Scheme" "http://www.scheme.dk/blog/" 42 )

1

> ( insert-entry "Reddit" "http://www.reddit.com" 7 )

2

> ( insert-entry "PLT Scheme" "http://www.plt-scheme.org" 5 )

3

> ( top 2 )

( #4 ( "entry_id" "title" "url" "score" )

#4 ( "1" "Everything Scheme" "http://www.scheme.dk/blog/" "42" )

#4 ( "2" "Reddit" "http://www.reddit.com" "7" ))

> ( increase-score 3 )

> ( increase-score 3 )

> ( increase-score 3 )

> ( increase-score 3 )

> ( top 2 )

( #4 ( "entry_id" "title" "url" "score" )

#4 ( "1" "Everything Scheme" "http://www.scheme.dk/blog/" "42" )

#4 ( "3" "PLT Scheme" "http://www.plt-scheme.org" "9" ))

>

The Program



( module model mzscheme

( provide ( all-defined ))



( require

( planet "sqlite.ss" ( "jaymccarthy" "sqlite.plt" ))

( planet "sqlite.ss" ( "soegaard" "sqlite.plt" 1 0 )))





( define PAGE-LIMIT ( make-parameter 50 )) ( define DATABASE-PATH ( string->path "listit.db" ))

( define current-database ( make-parameter #f ))





( define-syntax db

( syntax-id-rules () [ db ( or ( current-database )

( let ([ d ( open DATABASE-PATH )])

( current-database d )

d ))]))





( define ( create-table-entries )

( exec/ignore

db

#<<SQL

CREATE TABLE entries (

entry_id INTEGER PRIMARY KEY ,

title TEXT ,

url TEXT ,

score INTEGER )

SQL

))



( define ( drop-table-entries )

( exec/ignore db "DROP TABLE entries" ))







( define ( insert-entry title url score )

( insert db ( sql ( INSERT INTO entries ( title url score )

VALUES ( , title , url , score )))))



( define ( increase-score entry-id )

( update db ( sql ( UPDATE entries

SET ( score = ( + score 1 ))

WHERE ( = entry_id , entry-id )))))



( define ( decrease-score entry-id )

( update db ( sql ( UPDATE entries

SET ( score = ( - score 1 ))

WHERE ( = entry_id , entry-id )))))







( define ( top n )

( select db ( sql ( SELECT ( entry_id title url score )

FROM entries

ORDER-BY ( score DESC )

LIMIT , n ))))



( define ( page n )

( select db ( sql ( SELECT ( entry_id title url score )

FROM entries

ORDER-BY ( score DESC )

LIMIT , ( PAGE-LIMIT ) OFFSET , ( * ( PAGE-LIMIT ) n )))))



( define ( entries-with-url url-str )

( select db ( sql ( SELECT ( entry_id title url score )

FROM entries

WHERE , ( format "url='~a'" url-str )))))



( define ( url-in-db? url-str )

( let ([ result ( entries-with-url db url-str )])

( if ( null? ( entries-with-url db url-str ))

#f

result )))







( unless ( and ( file-exists? DATABASE-PATH )

( table-exists? db "'entries'" ))

( create-table-entries )

( current-database ( make-parameter ( open DATABASE-PATH )))))



From time to time people ask how to develop for the web with PLT Scheme on the PLT mailing list . The quick answer is "Just as in any other language", but that's not how to get people hooked on Scheme. To write a decent web-application require knowledge of a range of subjects such as HTML, databases, servlets, and web-servers. For some reason there is a lack of tutorials on these subjects, so I have decided to make an attempt at writing, if not a complete tutorial, then an elaborate get-started example.The example application will be a mini version of Reddit called ListIt. The front page consists of a list of links to interesting articles, users can vote the articles up and down, and submit new articles. The hope is that the example is small enough to be easily understood, but on the other hand large enough to illustrate as many aspects as possible. Please leave comments on the blog: Did the example hit home? Is a paragraph in need of a rewrite? Did I skip something?First things first. How should the program be organized? There is no need to reinvent the wheel, so I have chosen to use the Model-View-Controller architecture, which works just as well for web applications as it does for graphical user interfaces.In a nutshell the Model-View-Controller architecture works like this: The model holds the data, the view displays data. User interactions goes through the controller in order to keep a separation between the model and the view. (See section 22.3 of HTDP or Wikipedia for more on MVC ).In our case we will represent the model, the view and the controllers as three separate Scheme modules. The model will use a database to hold the links, the view will consists of functions generating HTML and the controller will the web-servlet that reacts on the user actions.Today we will concentrate on the model. Each entry in our database consists of an entry-id , a title to display, an url to the article and a score representing the votes. Since we expect many entries in our database, we will think of them as divided into page. The number of entries in each page is given by the parameter PAGE-LIMIT.The interface to our model consists of the following functions:Insert a new entry into the database.Increase the score of an existing entryDecrease the score of an existing entryReturn the given number of entries with the highest scoresReturn the list of entries in the given page.Is the url already listed?These functions are the only ones to be exposed to the controller.To implement these functions we will use an SQLite database. It wouldn't be unreasonable to argue that it would be easier to use a hash-table, but I want to illustrate how to use SQLite.SQLite is small database engine, which comes in the form of a single self-contained, zero-configuration DLL-file on Windows or a a so-file on other platforms. We will use Jay McCarthy and Noel Welsh's PLT Scheme bindings sqlite.plt . On top of these binding we'll use a S-expression to SQL-string library written by me (it will appear on PLaneT soon - it has been submitted). On Windows you download SQLite by pasting the following into the DrScheme interaction window (the REPL):To use the two SQLite packages, we start our module withOpening the database is simple:At least it will be, after it is created. The following function creates an empty database with a single table "entries":The #<< starts a so called here-string . The functionexecutes an SQL-statement and ignores the result (there is no S-expression syntax for CREATE TABLE yet).Once we have created the our table, we can begin writing the functions in our interface.The first is:The macroconverts an S-expression representation of an SQL-statement into a string, which is then handed to SQLite by. The string produced by the sql macro frombecomesThe remaining functions from the interface are all simple SQL-statements, which can be studied in the full program below.A loose end: In the source below the parameteris used to hold the database. As a convenience I have with the help of syntax-id-rules defined the identifierto expand to. But in order make everything work also as when the database isn't created yet, the actual definition below is a little more involved.To test the model, open the "model.scm" in DrScheme. In the "Language" menu use "Choose Language" to choose the "Module" language. Click and "Run" and you are ready to test it:

Labels: model, mvc, PLT, Scheme, servlet, web-server