プロジェクト

全般

プロフィール

SourceFifth » 履歴 » バージョン 3

Yuumi Yoshida, 2015-08-03 22:24

1 1 Yuumi Yoshida
*リスト1:web-server.scm
2 3 Yuumi Yoshida
3
~~~
4 1 Yuumi Yoshida
(use gauche.net) 
5
(use rfc.uri) 
6
(use www.cgi) 
7
(use gauche.reload) 
8
(use gauche.collection)
9
10
(add-load-path "./")
11
(use application) 
12
13
(define error-page
14
  "<html><body><h2>Error</h2></body></html>")
15
16
(define (web-server)
17
  (let1 server-socket (make-server-socket 'inet 3030 :reuse-addr? #t)
18
    (guard (e (else (socket-close server-socket) (raise e)))  ; TODO
19
     (while #t
20
       (let1 client-socket (socket-accept server-socket)
21
         (receive (path params)  (get-request (socket-input-port client-socket))
22
           (reload-modified-modules)
23
           (put-response 
24
            (guard (e (else (report-error e) error-page))
25
                   (render path params))
26
            (socket-output-port client-socket)))
27
         (socket-close client-socket))))))
28
      
29
(define (get-request in-port)
30
  (cond ((rxmatch #/^GET\s+(\S+)\s/ (read-line in-port)) 
31
         => (lambda (m) 
32
              (parse-uri (rxmatch-substring m 1))))
33
        (else (values "" ()))))
34
35
(define (parse-uri uri)
36
  (receive (host path query fragment) (uri-decompose-hierarchical uri) 
37
    (values path (cgi-parse-parameters :query-string (or query "")))))
38
39
(define (put-response content out-port)
40
  (display "HTTP/1.1 200 OK\r\n" out-port) 
41
  (display "Content-Type: text/html; charset=utf-8\r\n" out-port) 
42 2 Yuumi Yoshida
  (display #@"Content-Length: ,(string-size content)\r\n" out-port) 
43 1 Yuumi Yoshida
  (display "\r\n" out-port) 
44
  (display content out-port))
45
46
(web-server)
47 3 Yuumi Yoshida
~~~
48 1 Yuumi Yoshida
49
*リスト2:simple-template.scm
50 3 Yuumi Yoshida
51
~~~
52 1 Yuumi Yoshida
(define-module simple-template
53
  (use file.util)
54
  (export template templ-render))
55
(select-module simple-template)
56
57
(define-macro (template tmpl-file . args)
58 2 Yuumi Yoshida
  @(templ-render ,tmpl-file (quote ,args) (list ,@args)))
59 1 Yuumi Yoshida
60
(define (templ-render templ-file vars args)
61
  (apply (eval (templ-func templ-file vars) (interaction-environment))
62
         args))
63
64
(define (templ-func templ-file vars)
65
  (read-from-string 
66 2 Yuumi Yoshida
   #@"(lambda ,vars
67 1 Yuumi Yoshida
        (let1 port (open-output-string)
68
              ,(expand-templ (file->string templ-file))
69
              (get-output-string port)))"))
70
71
(define (expand-templ templ)
72 3 Yuumi Yoshida
  (cond ((#/(.*?)<%=(.*?)%>(.*)/ templ) 
73 1 Yuumi Yoshida
         => (lambda(m)
74
              #@",(expand-templ (m 1)) (display ,(m 2) port) ,(expand-templ (m 3))"))
75 3 Yuumi Yoshida
        ((#/(.*?)<%(.*?)%>(.*)/ templ) 
76 2 Yuumi Yoshida
         => (lambda(m)
77 1 Yuumi Yoshida
              #@",(expand-templ (m 1)) ,(m 2) ,(expand-templ (m 3))"))
78
        (else (format "(display ~s port)" templ))))
79
80
(provide "simple-template")
81 3 Yuumi Yoshida
~~~
82 2 Yuumi Yoshida
83 1 Yuumi Yoshida
*リスト3:application.scm
84 3 Yuumi Yoshida
85
~~~
86 1 Yuumi Yoshida
(define-module application
87
  (use srfi-13)
88
  (use www.cgi) 
89
  (use dbi)
90
  (use gauche.collection)
91
  (use simple-template)
92
  (export render))
93
(select-module application)
94
95
(define *db-name* "dbi:sqlite3:alih.db")
96
97
(define-macro (db-let* db-conn vars . body)
98 2 Yuumi Yoshida
  @(let1 ,db-conn (dbi-connect *db-name*)
99 1 Yuumi Yoshida
     (guard 
100
      (e (else (dbi-close ,db-conn) (raise e)))
101
      (let* (,@vars
102
             (ret-value (begin ,@body)))
103
        (dbi-close ,db-conn)
104
        ret-value))))
105
106
(define (render path params)
107
  (cond ((string= path "/edit") (edit params))
108
        ((string= path "/update") (update params))
109
        (else (index params))))
110
111
(define (index params)
112
  (db-let* conn
113
           ((res (dbi-do conn 
114
                         "SELECT id, no, name, g FROM player WHERE pos='F' ORDER BY no"))
115
            (getter (relation-accessor res)))
116
           (template "list.tmpl" res getter)))
117
118
(define (edit params)
119
  (db-let* conn 
120
           ((id (cgi-get-parameter "id" params))
121
            (res (dbi-do conn
122
                         "SELECT id, no, name, g FROM player WHERE id=?"
123
                         '()
124
                         id))
125
            (getter (relation-accessor res)))
126
           (template "edit.tmpl" res getter)))
127
128
(define (update params)
129
  (db-let* conn ()
130
           (dbi-do conn
131
                   "UPDATE player SET no=?, name=?, g=? WHERE id=?"
132
                   '() 
133
                   (cgi-get-parameter "no" params)
134
                   (cgi-get-parameter "name" params)
135
                   (cgi-get-parameter "g" params)
136
                   (cgi-get-parameter "id" params))
137
           (template "update.tmpl")))
138
139
(provide "application")
140 3 Yuumi Yoshida
~~~
141 1 Yuumi Yoshida
142
*リスト4:list.tmpl
143 3 Yuumi Yoshida
144
~~~
145 1 Yuumi Yoshida
<html>
146
  <body>
147
    <h2>Asia League Ice Hockey</h2>
148
    <table border="1">
149
    <form action="edit">
150 3 Yuumi Yoshida
    <% (for-each (lambda (row) %>
151 1 Yuumi Yoshida
      <tr>
152 3 Yuumi Yoshida
        <td><input type="radio" name="id" value="<%= (getter row "id")%>"></td>
153
        <td><%= (getter row "no") %></td>
154
        <td><%= (getter row "name") %></td>
155
        <td><%= (getter row "g") %></td>
156 1 Yuumi Yoshida
      </tr>
157 3 Yuumi Yoshida
     <% ) res) %>
158 1 Yuumi Yoshida
    </table>
159 2 Yuumi Yoshida
    <input type="submit" value="Edit">
160 1 Yuumi Yoshida
    </form> 
161
  </body>
162
</html>
163 3 Yuumi Yoshida
~~~
164 2 Yuumi Yoshida
165 1 Yuumi Yoshida
*リスト5:edit.tmpl
166 3 Yuumi Yoshida
167
~~~
168 1 Yuumi Yoshida
<html>
169
  <body>
170
    <h2>Edit</h2>
171
    <form action="update">
172 3 Yuumi Yoshida
    <% (let1 row (find (lambda (x) #t) res)  %>
173
    <input type="hidden" name="id" value="<%= (getter row "id")%>">
174 1 Yuumi Yoshida
    <table border="0">
175 3 Yuumi Yoshida
      <tr><td>No: </td><td><input type="text" name="no" value="<%= (getter row "no")%>"></td></tr>
176
      <tr><td>Name: </td><td><input type="text" name="name" value="<%= (getter row "name")%>"></td></tr>
177
      <tr><td>Goal: </td><td><input type="text" name="g" value="<%= (getter row "g")%>"></td></tr>
178
     <% ) %>
179 1 Yuumi Yoshida
    </table>
180
<input type="submit" value="Update">
181
    </form> 
182
  </body>
183 2 Yuumi Yoshida
</html>
184 3 Yuumi Yoshida
~~~
185 1 Yuumi Yoshida
186 2 Yuumi Yoshida
*リスト6:update.tmpl
187 3 Yuumi Yoshida
188
~~~
189 1 Yuumi Yoshida
<html>
190
  <head>
191
    <meta http-equiv="refresh" content="0;URL=/"> 
192
  </head>
193 2 Yuumi Yoshida
  <body></body>
194 1 Yuumi Yoshida
</html>
195 3 Yuumi Yoshida
~~~
196 2 Yuumi Yoshida
197 1 Yuumi Yoshida
*リスト7:application.scm
198 3 Yuumi Yoshida
199
~~~
200 1 Yuumi Yoshida
(define-module application
201
  (use srfi-13)
202
  (use srfi-27)
203
  (use www.cgi) 
204
  (use dbi)
205
  (use gauche.collection)
206
  (use simple-template)
207
  (export render))
208
(select-module application)
209
210
(define *db-name* "dbi:sqlite3:alih.db")
211
(define *conts* (make-hash-table 'eqv?))
212
(define *max-cid* (expt 2 64))
213
214
215
(define (get-cont params)
216
  (hash-table-get *conts* 
217
                  (cgi-get-parameter "@cont@" params :convert string->number)
218
                  #f))
219
220
(define (push-cont! cont) 
221
  (let1 cid (random-integer *max-cid*)
222
    (cond ((hash-table-get *conts* cid #f) (push-cont! cont))
223 2 Yuumi Yoshida
          (else (hash-table-put! *conts* cid cont) cid))))
224 1 Yuumi Yoshida
225
(define-macro (db-let* db-conn vars . body)
226
  @(let1 ,db-conn (dbi-connect *db-name*)
227
     (guard 
228
      (e (else (dbi-close ,db-conn) (raise e)))
229
      (let* (,@vars
230
             (ret-value (begin ,@body)))
231
        (dbi-close ,db-conn)
232 2 Yuumi Yoshida
        ret-value))))
233 1 Yuumi Yoshida
234
(define-macro (define-continuation args . body)
235
  @(push-cont! (lambda ,args ,@body)))
236
237
238
(define (render path params)
239
  (cond ((get-cont params) => (lambda(c) (c params)))
240
        ((string= path "/edit") (edit params))
241
        ((string= path "/update") (update params))
242
        (else (index params))))
243
244
(define (index params)
245
  (db-let* conn
246
           ((res (dbi-do conn 
247
                         "SELECT id, no, name, g FROM player WHERE pos='F' ORDER BY no"))
248
            (getter (relation-accessor res)))
249
           (template "list.tmpl" res getter)))
250
251
252
(define (edit params)
253
  (db-let* conn 
254
           ((id (cgi-get-parameter "id" params))
255
            (res (dbi-do conn
256
                         "SELECT id, no, name, g FROM player WHERE id=?"
257
                         '()
258
                         id))
259
            (getter (relation-accessor res))
260
            (cid (define-continuation (params)
261
                   (db-let* conn ()
262
                            (dbi-do conn
263
                                    "UPDATE player SET no=?, name=?, g=? WHERE id=?"
264
                                    '() 
265
                                    (cgi-get-parameter "no" params)
266
                                    (cgi-get-parameter "name" params)
267
                                    (cgi-get-parameter "g" params)
268
                                    id)
269 2 Yuumi Yoshida
                            (template "update.tmpl")))))
270 1 Yuumi Yoshida
   (template "edit.tmpl" res getter cid)))
271
272 2 Yuumi Yoshida
(provide "application")
273 3 Yuumi Yoshida
~~~
274 1 Yuumi Yoshida
275
*リスト8:edit.tmpl
276 3 Yuumi Yoshida
277
~~~
278 1 Yuumi Yoshida
<html>
279
  <body>
280
    <h2>Edit</h2>
281
    <form action="update">
282 3 Yuumi Yoshida
    <% (let1 row (find (lambda (x) #t) res)  %>
283
    <input type="hidden" name="@cont@" value="<%= cid %>">
284 1 Yuumi Yoshida
    <table border="0">
285 3 Yuumi Yoshida
      <tr><td>No: </td><td><input type="text" name="no" value="<%= (getter row "no")%>"></td></tr>
286
      <tr><td>Name: </td><td><input type="text" name="name" value="<%= (getter row "name")%>"></td></tr>
287
      <tr><td>Goal: </td><td><input type="text" name="g" value="<%= (getter row "g")%>"></td></tr>
288
     <% ) %>
289 2 Yuumi Yoshida
    </table>
290 1 Yuumi Yoshida
    <input type="submit" value="Update">
291
    </form> 
292
  </body>
293
</html>
294 3 Yuumi Yoshida
~~~