Browse Source

Nawak: better, cleaner db abstraction

Erwan Ameil 11 years ago
parent
commit
9d7d34a585
5 changed files with 118 additions and 119 deletions
  1. 68 0
      nawak/app.nim
  2. 0 12
      nawak/model.nim
  3. 43 0
      nawak/model_postgre.nim
  4. 0 102
      nawak/nawak_app.nim
  5. 7 5
      nawak/setup.py

+ 68 - 0
nawak/app.nim

@@ -0,0 +1,68 @@
+import strtabs, strutils, math, algorithm
+import nawak_mongrel, jdump
+import fortunes_tmpl
+when defined(postgre_model):
+    import model_postgre
+
+get "/json":
+    var j: THello
+    j.message = "Hello, World!"
+    # jdump serialize the tuples of the model as json
+    return response(jdump(j), "application/json")
+
+get "/plaintext":
+    return response("Hello, World!", "text/plain")
+
+get "/db":
+    let w = getWorld(random(10_000)+1)
+    return response(jdump(w), "application/json")
+
+get "/fortunes":
+    var fortunes = getAllFortunes()
+    let new_fortune: TFortune =
+        (id: 0,
+         message: "Additional fortune added at request time.")
+    fortunes.add new_fortune
+    sort(fortunes, proc(x, y: TFortune): int =
+        return cmp(x.message, y.message))
+
+    return response(fortunes_tmpl(fortunes), "text/html; charset=utf-8")
+
+
+proc limit_queries(query_params: PStringTable): int =
+    result = 1
+    if query_params.hasKey("queries"):
+        try:
+            result = parseInt(query_params["queries"])
+        except EInvalidValue: discard
+        # clamp the number of queries
+        if result < 1: result = 1
+        elif result > 500: result = 500
+
+get "/queries":
+    let queries = limit_queries(request.query)
+
+    var world: seq[TWorld]
+    world.newSeq(queries)
+    for i in 0.. <queries:
+        world[i] = getWorld random(10_000) + 1
+    return response(jdump(world), "application/json")
+
+get "/updates":
+    let queries = limit_queries(request.query)
+
+    var world: seq[TWorld]
+    world.newSeq(queries)
+    for i in 0.. <queries:
+        world[i] = getWorld(random(10_000) + 1)
+        world[i].randomNumber = random(10_000) + 1
+        updateWorld world[i]
+
+    return response(jdump(world), "application/json")
+
+custom_page 404:
+    # customize the content of the 404 page
+    return response(404, """Nah, I've got nothing.<br>
+                            Here's a <b>404 Page Not Found</b> error for you.""")
+
+run(init=init_db, nb_threads=256)

+ 0 - 12
nawak/model.nim

@@ -1,15 +1,3 @@
-
 type THello* = tuple[message: string]
 type TWorld* = tuple[id: int, randomNumber: int]
 type TFortune* = tuple[id: int, message: string]
-
-
-import strutils
-import lib/db_postgres_redone
-
-proc unrowTWorld*(x: TRow): TWorld =
-    result.id = parseInt(x[0])
-    result.randomNumber = parseInt(x[1])
-
-proc unrowTFortune*(x: TRow): TFortune =
-    return (x[0].parseInt, x[1])

+ 43 - 0
nawak/model_postgre.nim

@@ -0,0 +1,43 @@
+import strutils
+# The following import belongs to the stdlib, but has been updated to support
+# queries with parameters (that are safer to counter SQL injections) and
+# prepared queries.
+# It will be merged eventually. For now, I included it in the repository.
+import lib/db_postgres_redone
+
+import model
+export model
+
+const qworld = "SELECT id, randomNumber FROM World WHERE id = $1"
+const qfortunes = "SELECT id, message FROM Fortune"
+const qupdates = "UPDATE World SET randomNumber = $1 WHERE id = $2"
+
+var db {.threadvar.}: TDbConn
+var qworld_prepared {.threadvar.}: TPreparedId
+var qfortunes_prepared {.threadvar.}: TPreparedId
+var qupdates_prepared {.threadvar.}: TPreparedId
+
+proc init_db*() {.procvar.} =
+    db = open("", "benchmarkdbuser", "benchmarkdbpass",
+              "host=localhost port=5432 dbname=hello_world")
+    # prepare queries
+    qworld_prepared = db.prepare("world", qworld, 1)
+    qfortunes_prepared = db.prepare("fortunes", qfortunes, 0)
+    qupdates_prepared = db.prepare("updates", qupdates, 2)
+
+
+proc getWorld*(n: int): TWorld =
+    #let row = db.getRow(qworld, n)
+    ## Yes, prepared queries are faster than unprepared ones
+    let row = db.getPRow(qworld_prepared, n)
+    result.id = parseInt(row[0])
+    result.randomNumber = parseInt(row[1])
+
+proc updateWorld*(w: TWorld) =
+    db.Exec(qupdates_prepared, $w.randomNumber, $w.id)
+
+proc getAllFortunes*(): seq[TFortune] =
+    let rows = db.getAllPRows(qfortunes_prepared)
+    result.newSeq(rows.len)
+    for j, row in rows.pairs:
+        result[j] = (row[0].parseInt, row[1])

+ 0 - 102
nawak/nawak_app.nim

@@ -1,102 +0,0 @@
-import strtabs, strutils, math, algorithm
-import nawak_mongrel, jdump
-import model, fortunes_tmpl
-
-# The following import belongs to the stdlib, but has been updated to support
-# queries with parameters (that are safer to counter SQL injections) and
-# prepared queries.
-# It will be merged eventually. For now, I included it in the repository.
-import lib/db_postgres_redone
-
-
-var db {.threadvar.}: TDbConn
-var qworld_prepared {.threadvar.}: TPreparedId
-var qfortunes_prepared {.threadvar.}: TPreparedId
-var qupdates_prepared {.threadvar.}: TPreparedId
-
-const qworld = "SELECT id, randomNumber FROM World WHERE id = $1"
-const qfortunes = "SELECT id, message FROM Fortune"
-const qupdates = "UPDATE World SET randomNumber = $1 WHERE id = $2"
-
-proc init() =
-    db = open("", "benchmarkdbuser", "benchmarkdbpass", "host=localhost port=5432 dbname=hello_world")
-    # prepare queries
-    qworld_prepared = db.prepare("world", qworld, 1)
-    qfortunes_prepared = db.prepare("fortunes", qfortunes, 0)
-    qupdates_prepared = db.prepare("updates", qupdates, 2)
-
-
-get "/json":
-    var j: THello
-    j.message = "Hello, World!"
-    # jdump serialize the tuples of the model as json
-    return response(jdump(j), "application/json")
-
-get "/plaintext":
-    return response("Hello, World!", "text/plain")
-
-
-get "/db":
-    let row = db.getPRow(qworld_prepared, random(10_000)+1)
-    var r = unrowTWorld(row)
-    return response(jdump(r), "application/json")
-
-#get "/db_unprepared":
-#    ## Yes, prepared queries are faster than unprepared ones
-#    var r = unrowTWorld( db.getRow(qworld, random(10_000) + 1) )
-#    return response(jdump(r), "application/json")
-
-get "/queries":
-    var queries = 1
-    if request.query.hasKey("queries"):
-        try:
-            queries = parseInt(request.query["queries"])
-        except EInvalidValue: discard
-        if queries < 1: queries = 1
-        elif queries > 500: queries = 500
-
-    var world: seq[TWorld]
-    world.newSeq(queries)
-    for i in 0.. <queries:
-        let row = db.getPRow(qworld_prepared, random(10_000) + 1)
-        world[i] = unrowTWorld(row)
-    return response(jdump(world), "application/json")
-
-get "/fortunes":
-    let rows = db.getAllPRows(qfortunes_prepared)
-    var fortunes: seq[TFortune]
-    fortunes.newSeq(rows.len)
-    for j, row in rows.pairs:
-        fortunes[j] = unrowTFortune(row)
-    let new_fortune: TFortune = (id: 0,
-                                 message: "Additional fortune added at request time.")
-    fortunes.add new_fortune
-    sort(fortunes, proc(x, y: TFortune): int =
-        return cmp(x.message, y.message))
-
-    return response(fortunes_tmpl(fortunes), "text/html; charset=utf-8")
-
-get "/updates":
-    var queries = 1
-    if request.query.hasKey("queries"):
-        try:
-            queries = parseInt(request.query["queries"])
-        except EInvalidValue: discard
-        if queries < 1: queries = 1
-        elif queries > 500: queries = 500
-
-    var world: seq[TWorld]
-    world.newSeq(queries)
-    for i in 0.. <queries:
-        world[i] = unrowTWorld(db.getPRow(qworld_prepared, random(10_000) + 1))
-        world[i].randomNumber = random(10_000) + 1
-        db.Exec(qupdates_prepared, $world[i].randomNumber, $world[i].id)
-
-    return response(jdump(world), "application/json")
-
-custom_page 404:
-    # customize the content of the 404 page
-    return response(404, """Nah, I've got nothing.<br>
-                            Here's a <b>404 Page Not Found</b> error for you.""")
-
-run(init=init, nb_threads=256)

+ 7 - 5
nawak/setup.py

@@ -7,17 +7,19 @@ from os.path import expanduser
 home = expanduser("~")
 
 def start(args, logfile, errfile):
+  setup_util.replace_text("nawak/model_postgre.nim", "host=.* port=5432",
+                          "host=" + args.database_host + " port=5432")
   # compile the app
-  setup_util.replace_text("nawak/nawak_app.nim", "host=.* port=5432", "host=" + args.database_host + " port=5432")
-  subprocess.check_call("nimrod c --threads:on -d:release --path:../installs/nawak/nawak nawak_app.nim",
-                        shell=True, cwd="nawak", stderr=errfile, stdout=logfile)
+  subprocess.check_call(
+      "nimrod c --threads:on -d:release -d:postgre_model --path:../installs/nawak/nawak -o:nawak_postgre app.nim",
+      shell=True, cwd="nawak", stderr=errfile, stdout=logfile)
   # launch mongrel2
   subprocess.check_call("mkdir -p run logs tmp", shell=True, cwd="nawak/conf", stderr=errfile, stdout=logfile)
   subprocess.check_call("sudo m2sh load -config mongrel2.conf", shell=True, cwd="nawak/conf", stderr=errfile, stdout=logfile)
   subprocess.check_call("sudo m2sh start -name test", shell=True, cwd="nawak/conf", stderr=errfile, stdout=logfile)
   
   # launch workers
-  subprocess.Popen("./nawak_app", shell=True, cwd="nawak", stderr=errfile, stdout=logfile)
+  subprocess.Popen("./nawak_postgre", shell=True, cwd="nawak", stderr=errfile, stdout=logfile)
   return 0
 
 def stop(logfile, errfile):
@@ -31,7 +33,7 @@ def stop(logfile, errfile):
   p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
   out, err = p.communicate()
   for line in out.splitlines():
-    if 'nawak_app' in line:
+    if 'nawak_postgre' in line:
       try:
         pid = int(line.split(None, 2)[1])
         os.kill(pid, 15)