プロジェクト

全般

プロフィール

操作

*リスト1:web-server.scm

(use gauche.net) 
(use rfc.uri) 
(use www.cgi) 
(use gauche.reload) 
(use gauche.collection)

(add-load-path "./")
(use application) 

(define error-page
  "<html><body><h2>Error</h2></body></html>")

(define (web-server)
  (let1 server-socket (make-server-socket 'inet 3030 :reuse-addr? #t)
    (guard (e (else (socket-close server-socket) (raise e)))  ; TODO
     (while #t
       (let1 client-socket (socket-accept server-socket)
         (receive (path params)  (get-request (socket-input-port client-socket))
           (reload-modified-modules)
           (put-response 
            (guard (e (else (report-error e) error-page))
                   (render path params))
            (socket-output-port client-socket)))
         (socket-close client-socket))))))

(define (get-request in-port)
  (cond ((rxmatch #/^GET\s+(\S+)\s/ (read-line in-port)) 
         => (lambda (m) 
              (parse-uri (rxmatch-substring m 1))))
        (else (values "" ()))))

(define (parse-uri uri)
  (receive (host path query fragment) (uri-decompose-hierarchical uri) 
    (values path (cgi-parse-parameters :query-string (or query "")))))

(define (put-response content out-port)
  (display "HTTP/1.1 200 OK\r\n" out-port) 
  (display "Content-Type: text/html; charset=utf-8\r\n" out-port) 
  (display #@"Content-Length: ,(string-size content)\r\n" out-port) 
  (display "\r\n" out-port) 
  (display content out-port))

(web-server)

*リスト2:simple-template.scm

(define-module simple-template
  (use file.util)
  (export template templ-render))
(select-module simple-template)

(define-macro (template tmpl-file . args)
  @(templ-render ,tmpl-file (quote ,args) (list ,@args)))

(define (templ-render templ-file vars args)
  (apply (eval (templ-func templ-file vars) (interaction-environment))
         args))

(define (templ-func templ-file vars)
  (read-from-string 
   #@"(lambda ,vars
        (let1 port (open-output-string)
              ,(expand-templ (file->string templ-file))
              (get-output-string port)))"))

(define (expand-templ templ)
  (cond ((#/(.*?)<%=(.*?)%>(.*)/ templ) 
         => (lambda(m)
              #@",(expand-templ (m 1)) (display ,(m 2) port) ,(expand-templ (m 3))"))
        ((#/(.*?)<%(.*?)%>(.*)/ templ) 
         => (lambda(m)
              #@",(expand-templ (m 1)) ,(m 2) ,(expand-templ (m 3))"))
        (else (format "(display ~s port)" templ))))

(provide "simple-template")

*リスト3:application.scm

(define-module application
  (use srfi-13)
  (use www.cgi) 
  (use dbi)
  (use gauche.collection)
  (use simple-template)
  (export render))
(select-module application)

(define *db-name* "dbi:sqlite3:alih.db")

(define-macro (db-let* db-conn vars . body)
  @(let1 ,db-conn (dbi-connect *db-name*)
     (guard 
      (e (else (dbi-close ,db-conn) (raise e)))
      (let* (,@vars
             (ret-value (begin ,@body)))
        (dbi-close ,db-conn)
        ret-value))))

(define (render path params)
  (cond ((string= path "/edit") (edit params))
        ((string= path "/update") (update params))
        (else (index params))))

(define (index params)
  (db-let* conn
           ((res (dbi-do conn 
                         "SELECT id, no, name, g FROM player WHERE pos='F' ORDER BY no"))
            (getter (relation-accessor res)))
           (template "list.tmpl" res getter)))

(define (edit params)
  (db-let* conn 
           ((id (cgi-get-parameter "id" params))
            (res (dbi-do conn
                         "SELECT id, no, name, g FROM player WHERE id=?"
                         '()
                         id))
            (getter (relation-accessor res)))
           (template "edit.tmpl" res getter)))

(define (update params)
  (db-let* conn ()
           (dbi-do conn
                   "UPDATE player SET no=?, name=?, g=? WHERE id=?"
                   '() 
                   (cgi-get-parameter "no" params)
                   (cgi-get-parameter "name" params)
                   (cgi-get-parameter "g" params)
                   (cgi-get-parameter "id" params))
           (template "update.tmpl")))

(provide "application")

*リスト4:list.tmpl

<html>
  <body>
    <h2>Asia League Ice Hockey</h2>
    <table border="1">
    <form action="edit">
    <% (for-each (lambda (row) %>
      <tr>
        <td><input type="radio" name="id" value="<%= (getter row "id")%>"></td>
        <td><%= (getter row "no") %></td>
        <td><%= (getter row "name") %></td>
        <td><%= (getter row "g") %></td>
      </tr>
     <% ) res) %>
    </table>
    <input type="submit" value="Edit">
    </form> 
  </body>
</html>

*リスト5:edit.tmpl

<html>
  <body>
    <h2>Edit</h2>
    <form action="update">
    <% (let1 row (find (lambda (x) #t) res)  %>
    <input type="hidden" name="id" value="<%= (getter row "id")%>">
    <table border="0">
      <tr><td>No: </td><td><input type="text" name="no" value="<%= (getter row "no")%>"></td></tr>
      <tr><td>Name: </td><td><input type="text" name="name" value="<%= (getter row "name")%>"></td></tr>
      <tr><td>Goal: </td><td><input type="text" name="g" value="<%= (getter row "g")%>"></td></tr>
     <% ) %>
    </table>
<input type="submit" value="Update">
    </form> 
  </body>
</html>

*リスト6:update.tmpl

<html>
  <head>
    <meta http-equiv="refresh" content="0;URL=/"> 
  </head>
  <body></body>
</html>

*リスト7:application.scm

(define-module application
  (use srfi-13)
  (use srfi-27)
  (use www.cgi) 
  (use dbi)
  (use gauche.collection)
  (use simple-template)
  (export render))
(select-module application)

(define *db-name* "dbi:sqlite3:alih.db")
(define *conts* (make-hash-table 'eqv?))
(define *max-cid* (expt 2 64))


(define (get-cont params)
  (hash-table-get *conts* 
                  (cgi-get-parameter "@cont@" params :convert string->number)
                  #f))

(define (push-cont! cont) 
  (let1 cid (random-integer *max-cid*)
    (cond ((hash-table-get *conts* cid #f) (push-cont! cont))
          (else (hash-table-put! *conts* cid cont) cid))))

(define-macro (db-let* db-conn vars . body)
  @(let1 ,db-conn (dbi-connect *db-name*)
     (guard 
      (e (else (dbi-close ,db-conn) (raise e)))
      (let* (,@vars
             (ret-value (begin ,@body)))
        (dbi-close ,db-conn)
        ret-value))))

(define-macro (define-continuation args . body)
  @(push-cont! (lambda ,args ,@body)))


(define (render path params)
  (cond ((get-cont params) => (lambda(c) (c params)))
        ((string= path "/edit") (edit params))
        ((string= path "/update") (update params))
        (else (index params))))

(define (index params)
  (db-let* conn
           ((res (dbi-do conn 
                         "SELECT id, no, name, g FROM player WHERE pos='F' ORDER BY no"))
            (getter (relation-accessor res)))
           (template "list.tmpl" res getter)))


(define (edit params)
  (db-let* conn 
           ((id (cgi-get-parameter "id" params))
            (res (dbi-do conn
                         "SELECT id, no, name, g FROM player WHERE id=?"
                         '()
                         id))
            (getter (relation-accessor res))
            (cid (define-continuation (params)
                   (db-let* conn ()
                            (dbi-do conn
                                    "UPDATE player SET no=?, name=?, g=? WHERE id=?"
                                    '() 
                                    (cgi-get-parameter "no" params)
                                    (cgi-get-parameter "name" params)
                                    (cgi-get-parameter "g" params)
                                    id)
                            (template "update.tmpl")))))
   (template "edit.tmpl" res getter cid)))

(provide "application")

*リスト8:edit.tmpl

<html>
  <body>
    <h2>Edit</h2>
    <form action="update">
    <% (let1 row (find (lambda (x) #t) res)  %>
    <input type="hidden" name="@cont@" value="<%= cid %>">
    <table border="0">
      <tr><td>No: </td><td><input type="text" name="no" value="<%= (getter row "no")%>"></td></tr>
      <tr><td>Name: </td><td><input type="text" name="name" value="<%= (getter row "name")%>"></td></tr>
      <tr><td>Goal: </td><td><input type="text" name="g" value="<%= (getter row "g")%>"></td></tr>
     <% ) %>
    </table>
    <input type="submit" value="Update">
    </form> 
  </body>
</html>

Yuumi Yoshida さんが9年以上前に更新 · 3件の履歴