diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..67f342d --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +pom.xml +*jar +lib +classes +build.xml +manifest.mf +nbproject diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ca3719c --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010 Tero Parviainen + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..fc1e64c --- /dev/null +++ b/README @@ -0,0 +1,15 @@ +# todos-server + +FIXME: write description + +## Usage + +FIXME: write + +## Installation + +FIXME: write + +## License + +FIXME: write diff --git a/project.clj b/project.clj new file mode 100644 index 0000000..b1d209c --- /dev/null +++ b/project.clj @@ -0,0 +1,8 @@ +(defproject todos-server "1.0.0-SNAPSHOT" + :description "Backend for the SproutCore tutorial app" + :dependencies [[org.clojure/clojure "1.1.0"] + [org.clojure/clojure-contrib "1.1.0"] + + [compojure "0.4.0-RC3"] + [congomongo "0.1.2-SNAPSHOT"]] + :dev-dependencies [[ring/ring-httpcore-adapter "0.2.0"]]) diff --git a/src/todos_server/api.clj b/src/todos_server/api.clj new file mode 100644 index 0000000..13ef2a3 --- /dev/null +++ b/src/todos_server/api.clj @@ -0,0 +1,43 @@ +(ns todos-server.api + (:use clojure.contrib.json.write + clojure.contrib.json.read + clojure.contrib.duck-streams + compojure.core + todos-server.db)) + +(defn- emit-json + "Turn the object to JSON, and emit it with the correct content type" + [x] + {:headers {"Content-Type" "application/json"} + :body (json-str {:content x})}) + +(defn- parse-json + "Parse the request body into a Clojure data structure" + [body] + (read-json (slurp* body))) + +(defn guid [task] + (str "/tasks/" (:id task))) + +(defn with-guid [task] + (assoc task :guid (guid task))) + +(defroutes main-routes + (GET "/tasks" [] + (emit-json + (map with-guid (find-all-tasks)))) + (GET "/tasks/:id" [id] + (emit-json + (with-guid (find-task id)))) + (POST "/tasks" {body :body} + (let [saved-task (add-task (parse-json body))] + {:status 201 + :headers {"Location" (guid saved-task)}})) + (PUT "/tasks/:id" {body :body {id "id"} :route-params} + (update-task id (parse-json body))) + (DELETE "/tasks/:id" [id] + (destroy-task id) + {:status 200})) + + + \ No newline at end of file diff --git a/src/todos_server/db.clj b/src/todos_server/db.clj new file mode 100644 index 0000000..f99e2fc --- /dev/null +++ b/src/todos_server/db.clj @@ -0,0 +1,38 @@ +(ns todos-server.db + (:use todos-server.util) + (:use somnium.congomongo)) + +(mongo! :db "todos") + +(defn- extern-id + "Returns a version of obj with a string :id based on its :_id" + [obj] + (-> obj + (assoc :id (str (:_id obj))) + (dissoc :_id))) + +(defn- intern-id + "Returns a version of obj with an ObjectId based on its :id" + [obj] + (-> obj + (assoc :_id (object-id (:id obj))) + (dissoc :id))) + +(defn find-all-tasks [] + (map extern-id (fetch :tasks))) + +(defn find-task [id] + (extern-id (fetch-by-id :tasks id))) + +(defn add-task [task] + (extern-id (insert! :tasks task))) + +(defn update-task [id task] + (let [task-in-db (fetch-by-id :tasks id)] + (update! :tasks + task-in-db + (merge-with-kw-keys task-in-db task)))) + +(defn destroy-task [id] + (destroy! :tasks + (fetch-by-id :tasks id))) diff --git a/src/todos_server/util.clj b/src/todos_server/util.clj new file mode 100644 index 0000000..d3ca5d8 --- /dev/null +++ b/src/todos_server/util.clj @@ -0,0 +1,18 @@ +(ns todos-server.util) + +(defn keywordify-keys + "Returns a map otherwise same as the argument but + with all keys turned to keywords" + [m] + (zipmap + (map keyword (keys m)) + (vals m))) + +(defn merge-with-kw-keys + "Merges maps converting all keys to keywords" + [& maps] + (reduce + merge + (map keywordify-keys maps))) + + diff --git a/test/todos_server/core_test.clj b/test/todos_server/core_test.clj new file mode 100644 index 0000000..63b229c --- /dev/null +++ b/test/todos_server/core_test.clj @@ -0,0 +1,6 @@ +(ns todos-server.core-test + (:use [todos-server.core] :reload-all) + (:use [clojure.test])) + +(deftest replace-me ;; FIXME: write + (is false))