├── contacts ├── resources │ ├── data │ │ ├── initial.edn │ │ └── schema.edn │ └── public │ │ ├── html │ │ └── index.html │ │ └── css │ │ └── main.css ├── .gitignore ├── doc │ └── intro.md ├── test │ └── contacts │ │ └── core_test.clj ├── src │ ├── clj │ │ └── contacts │ │ │ ├── util.clj │ │ │ ├── system.clj │ │ │ ├── core.clj │ │ │ ├── datomic.clj │ │ │ └── server.clj │ └── cljs │ │ └── contacts │ │ ├── util.cljs │ │ ├── components.cljs │ │ └── core.cljs ├── project.clj ├── contacts.iml └── LICENSE ├── .gitignore └── README.md /contacts/resources/data/initial.edn: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | contacts/.idea 2 | contacts/resources/public/js 3 | contacts/target* 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Example code for the Kitchen Table Coders Immutable Stack training. 2 | -------------------------------------------------------------------------------- /contacts/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | -------------------------------------------------------------------------------- /contacts/doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to contacts 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/great-documentation/what-to-write/) 4 | -------------------------------------------------------------------------------- /contacts/test/contacts/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns contacts.core-test 2 | (:require [clojure.test :refer :all] 3 | [contacts.core :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | -------------------------------------------------------------------------------- /contacts/src/clj/contacts/util.clj: -------------------------------------------------------------------------------- 1 | (ns contacts.util 2 | (:require [datomic.api :as d] 3 | [clojure.java.io :as io] 4 | [clojure.edn :as edn] 5 | 6 | ) 7 | (:import datomic.Util)) 8 | 9 | 10 | ;(def conn (get-conn)) 11 | 12 | -------------------------------------------------------------------------------- /contacts/resources/public/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /contacts/src/clj/contacts/system.clj: -------------------------------------------------------------------------------- 1 | (ns contacts.system 2 | (:require [com.stuartsierra.component :as component] 3 | contacts.server 4 | contacts.datomic)) 5 | 6 | (defn dev-system [config-options] 7 | (let [{:keys [db-uri web-port]} config-options] 8 | (component/system-map 9 | :db (contacts.datomic/new-database db-uri) 10 | :webserver 11 | (component/using 12 | (contacts.server/dev-server web-port) 13 | {:datomic-connection :db})))) 14 | 15 | (defn prod-system [config-options] 16 | (let [{:keys [db-uri]} config-options] 17 | (component/system-map 18 | :db (contacts.datomic/new-database db-uri) 19 | :webserver 20 | (component/using 21 | (contacts.server/prod-server) 22 | {:datomic-connection :db})))) 23 | 24 | (comment 25 | (def s (dev-system {:db-uri "datomic:mem://localhost:4334/contacts" 26 | :web-port 8081})) 27 | (def s1 (component/start s)) 28 | ) 29 | -------------------------------------------------------------------------------- /contacts/src/clj/contacts/core.clj: -------------------------------------------------------------------------------- 1 | (ns contacts.core 2 | (:require [com.stuartsierra.component :as component] 3 | [contacts.system :as system])) 4 | 5 | (def servlet-system (atom nil)) 6 | 7 | ;; ============================================================================= 8 | ;; Development 9 | 10 | (defn dev-start [] 11 | (let [sys (system/dev-system 12 | {:db-uri "datomic:mem://localhost:4334/contacts" 13 | :web-port 8080}) 14 | sys' (component/start sys)] 15 | (reset! servlet-system sys') 16 | sys')) 17 | 18 | ;; ============================================================================= 19 | ;; Production 20 | 21 | (defn service [req] 22 | ((:handler (:webserver @servlet-system)) req)) 23 | 24 | (defn start [] 25 | (let [s (system/prod-system 26 | {:db-uri "datomic:mem://localhost:4334/contacts"})] 27 | (let [started-system (component/start s)] 28 | (reset! servlet-system started-system)))) 29 | 30 | (defn stop []) 31 | -------------------------------------------------------------------------------- /contacts/src/cljs/contacts/util.cljs: -------------------------------------------------------------------------------- 1 | (ns contacts.util 2 | (:require [goog.events :as events] 3 | [cljs.reader :as reader] 4 | [cljs.core.async :refer [chan put! close!]]) 5 | (:import goog.net.EventType 6 | [goog.events EventType] 7 | [goog.net XhrIo])) 8 | 9 | (def ^:private meths 10 | {:get "GET" 11 | :put "PUT" 12 | :post "POST" 13 | :delete "DELETE"}) 14 | 15 | (defn edn-xhr [{:keys [method url data on-complete]}] 16 | (let [xhr (XhrIo.)] 17 | (events/listen xhr goog.net.EventType.COMPLETE 18 | (fn [e] 19 | (on-complete (reader/read-string (.getResponseText xhr))))) 20 | (. xhr 21 | (send url (meths method) (when data (pr-str data)) 22 | #js {"Content-Type" "application/edn"})))) 23 | 24 | (defn edn-chan [opts] 25 | (let [opts (if-not (contains? opts :method) 26 | (assoc opts :method :get) 27 | opts) 28 | c (chan)] 29 | (edn-xhr 30 | (assoc opts 31 | :on-complete (fn [edn] (put! c edn) (close! c)))) 32 | c)) 33 | -------------------------------------------------------------------------------- /contacts/resources/public/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0px; 3 | padding: 0px; 4 | width: 100%; 5 | } 6 | 7 | ul { 8 | margin: 0; 9 | padding: 0 0 0 10px; 10 | } 11 | 12 | li { 13 | list-style: none; 14 | margin: 0; 15 | padding: 10px; 16 | } 17 | 18 | label { 19 | font-size: 11px; 20 | font-weight: bold; 21 | text-transform: uppercase; 22 | } 23 | 24 | .button { 25 | font-family: sans-serif; 26 | padding: 5px 10px; 27 | border: 1px solid #666; 28 | border-radius: 4px; 29 | background-color: white; 30 | text-transform: uppercase; 31 | } 32 | 33 | #contacts { 34 | font-family: sans-serif; 35 | position: absolute; 36 | width: 400px; 37 | left: 50%; 38 | margin-left: -200px; 39 | margin-top: 10px; 40 | } 41 | 42 | #contacts-list, #contact-info { 43 | border: 1px solid #ccc; 44 | min-height: 50px; 45 | padding: 10px; 46 | margin-top: 10px; 47 | } 48 | 49 | #contacts-list li { 50 | cursor: pointer; 51 | } 52 | 53 | .section { 54 | margin-top: 20px; 55 | } 56 | 57 | .list { 58 | font-size: 13px; 59 | } 60 | 61 | .editable button { 62 | margin-left: 10px; 63 | } 64 | -------------------------------------------------------------------------------- /contacts/project.clj: -------------------------------------------------------------------------------- 1 | (defproject contacts "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | 7 | :jvm-opts ^:replace ["-Xms512m" "-Xmx512m" "-server"] 8 | 9 | :dependencies [[org.clojure/clojure "1.5.1"] 10 | [org.clojure/clojurescript "0.0-2173"] 11 | [com.datomic/datomic-free "0.9.4699"] 12 | [bidi "1.10.2"] 13 | [om "0.5.3"] 14 | [secretary "1.1.0"] 15 | [ring/ring "1.2.2"] 16 | [fogus/ring-edn "0.2.0"] 17 | [com.stuartsierra/component "0.2.1"] 18 | [org.clojure/core.async "0.1.278.0-76b25b-alpha"]] 19 | 20 | :source-paths ["src/clj"] 21 | 22 | :plugins [[lein-cljsbuild "1.0.2"] 23 | [lein-ring "0.8.10"] 24 | [lein-beanstalk "0.2.7"]] 25 | 26 | :ring {:handler contacts.core/service 27 | :init contacts.core/start 28 | :destroy contacts.core/stop} 29 | 30 | :cljsbuild { 31 | :builds [ 32 | {:id "dev" 33 | :source-paths ["src/cljs"] 34 | :compiler { 35 | :output-to "resources/public/js/main.js" 36 | :output-dir "resources/public/js/out" 37 | :optimizations :none 38 | :source-map true 39 | }} 40 | {:id "release" 41 | :source-paths ["src/cljs"] 42 | :compiler { 43 | :output-to "resources/public/js/main.js" 44 | :optimizations :advanced 45 | :output-wrapper true 46 | :pretty-print false 47 | :preamble ["react/react.min.js"] 48 | :externs ["react/externs/react.js"] 49 | }} 50 | ] 51 | }) 52 | -------------------------------------------------------------------------------- /contacts/src/cljs/contacts/components.cljs: -------------------------------------------------------------------------------- 1 | (ns contacts.components 2 | (:require [om.core :as om :include-macros true] 3 | [om.dom :as dom :include-macros true])) 4 | 5 | 6 | (defn display [show] 7 | (if show 8 | #js {} 9 | #js {:display "none"})) 10 | 11 | 12 | (defn handle-change [e owner] 13 | (om/set-state! owner :edit-text (.. e -target -value))) 14 | 15 | 16 | (defn end-edit [data edit-key owner cb] 17 | (om/set-state! owner :editing false) 18 | (om/update! data edit-key (om/get-state owner :edit-text)) 19 | (cb {:value @data :edit-key edit-key})) 20 | 21 | 22 | (defn editable [data owner {:keys [edit-key on-edit] :as opts}] 23 | (reify 24 | om/IInitState 25 | (init-state [_] 26 | {:editing false 27 | :edit-text ""}) 28 | om/IRenderState 29 | (render-state [_ {:keys [edit-text editing]}] 30 | (let [text (get data edit-key)] 31 | (dom/li nil 32 | (dom/span #js {:style (display (not editing))} text) 33 | (dom/input 34 | #js {:style (display editing) 35 | :value edit-text 36 | :onChange #(handle-change % owner) 37 | :onKeyPress #(when (and (om/get-state owner :editing) 38 | (== (.-keyCode %) 13)) 39 | (end-edit data edit-key owner on-edit)) 40 | :onBlur (fn [e] 41 | (when (om/get-state owner :editing) 42 | (end-edit data edit-key owner on-edit)))}) 43 | (dom/button 44 | #js {:style (display (not editing)) 45 | :onClick (fn [e] 46 | (om/set-state! owner :edit-text text) 47 | (om/set-state! owner :editing true))} 48 | "Edit")))))) 49 | -------------------------------------------------------------------------------- /contacts/resources/data/schema.edn: -------------------------------------------------------------------------------- 1 | [ 2 | ;; People 3 | 4 | {:db/id #db/id[:db.part/db] 5 | :db/ident :person/first-name 6 | :db/valueType :db.type/string 7 | :db/cardinality :db.cardinality/one 8 | :db/doc "A person's first name" 9 | :db.install/_attribute :db.part/db} 10 | 11 | {:db/id #db/id[:db.part/db] 12 | :db/ident :person/last-name 13 | :db/valueType :db.type/string 14 | :db/cardinality :db.cardinality/one 15 | :db/doc "A person's last name" 16 | :db.install/_attribute :db.part/db} 17 | 18 | ;; Email address 19 | 20 | {:db/id #db/id[:db.part/db] 21 | :db/ident :person/email 22 | :db/valueType :db.type/ref 23 | :db/cardinality :db.cardinality/many 24 | :db/isComponent true 25 | :db/doc "A person's email addresses" 26 | :db.install/_attribute :db.part/db} 27 | 28 | {:db/id #db/id[:db.part/db] 29 | :db/ident :email/address 30 | :db/valueType :db.type/string 31 | :db/cardinality :db.cardinality/one 32 | :db/doc "An email addres" 33 | :db.install/_attribute :db.part/db} 34 | 35 | ;; Telephone numbers 36 | 37 | {:db/id #db/id[:db.part/db] 38 | :db/ident :person/telephone 39 | :db/valueType :db.type/ref 40 | :db/cardinality :db.cardinality/many 41 | :db/isComponent true 42 | :db/doc "A person's telephone contact information" 43 | :db.install/_attribute :db.part/db} 44 | 45 | {:db/id #db/id[:db.part/db] 46 | :db/ident :telephone/number 47 | :db/valueType :db.type/string 48 | :db/cardinality :db.cardinality/one 49 | :db/doc "A telephone number" 50 | :db.install/_attribute :db.part/db} 51 | 52 | ;; Addresses 53 | 54 | {:db/id #db/id[:db.part/db] 55 | :db/ident :person/address 56 | :db/valueType :db.type/ref 57 | :db/cardinality :db.cardinality/many 58 | :db/isComponent true 59 | :db/doc "A person's physical addresses" 60 | :db.install/_attribute :db.part/db} 61 | 62 | {:db/id #db/id[:db.part/db] 63 | :db/ident :address/street 64 | :db/valueType :db.type/string 65 | :db/cardinality :db.cardinality/one 66 | :db/doc "A street" 67 | :db.install/_attribute :db.part/db} 68 | 69 | {:db/id #db/id[:db.part/db] 70 | :db/ident :address/city 71 | :db/valueType :db.type/string 72 | :db/cardinality :db.cardinality/one 73 | :db/doc "A city" 74 | :db.install/_attribute :db.part/db} 75 | 76 | {:db/id #db/id[:db.part/db] 77 | :db/ident :address/state 78 | :db/valueType :db.type/string 79 | :db/cardinality :db.cardinality/one 80 | :db/doc "A state" 81 | :db.install/_attribute :db.part/db} 82 | 83 | {:db/id #db/id[:db.part/db] 84 | :db/ident :address/zipcode 85 | :db/valueType :db.type/string 86 | :db/cardinality :db.cardinality/one 87 | :db/doc "A zipcode" 88 | :db.install/_attribute :db.part/db}] 89 | -------------------------------------------------------------------------------- /contacts/src/clj/contacts/datomic.clj: -------------------------------------------------------------------------------- 1 | (ns contacts.datomic 2 | (:require [datomic.api :as d] 3 | [com.stuartsierra.component :as component] 4 | [clojure.java.io :as io] 5 | [clojure.edn :as edn]) 6 | (:import datomic.Util)) 7 | 8 | 9 | ;; ============================================================================= 10 | ;; Helpers 11 | 12 | (defn convert-db-id [x] 13 | (cond 14 | (instance? datomic.query.EntityMap x) 15 | (assoc (into {} (map convert-db-id x)) 16 | :db/id (str (:db/id x))) 17 | 18 | (instance? clojure.lang.MapEntry x) 19 | [(first x) (convert-db-id (second x))] 20 | 21 | (coll? x) 22 | (into (empty x) (map convert-db-id x)) 23 | 24 | :else x)) 25 | 26 | 27 | ;; ============================================================================= 28 | ;; Queries 29 | 30 | (defn list-contacts [db] 31 | (map 32 | ;; won't roundtrip to conn bc segment already probably cached 33 | #(d/entity db (first %)) 34 | (d/q '[:find ?eid 35 | :where 36 | ;; talk about how we can make it do first OR last name 37 | [?eid :person/first-name]] 38 | db))) 39 | 40 | (defn display-contacts [db] 41 | (let [contacts (list-contacts db)] 42 | (map 43 | #(select-keys % [:db/id :person/last-name :person/first-name]) 44 | (sort-by :person/last-name (map convert-db-id contacts))))) 45 | 46 | 47 | (defn get-contact [db id-string] 48 | (convert-db-id (d/touch (d/entity db (edn/read-string id-string))))) 49 | 50 | 51 | (defn create-contact [conn data] 52 | (let [tempid (d/tempid :db.part/user) 53 | r @(d/transact conn [(assoc data :db/id tempid)])] 54 | (assoc data :db/id (str (d/resolve-tempid (:db-after r) (:tempids r) tempid))))) 55 | 56 | 57 | (defn update-contact [conn data] 58 | @(d/transact conn [(assoc data :db/id (edn/read-string (:db/id data)))]) 59 | true) 60 | 61 | 62 | (defn delete-contact [conn id] 63 | @(d/transact conn [[:db.fn/retractEntity (edn/read-string id)]]) 64 | true) 65 | 66 | 67 | ;; PHONE 68 | (defn create-phone [conn data] 69 | (let [tempid (d/tempid :db.part/user) 70 | r @(d/transact conn 71 | [(assoc 72 | data 73 | :db/id 74 | tempid 75 | :person/_telephone 76 | (edn/read-string (:person/_telephone data)))])] 77 | (assoc data :db/id (str (d/resolve-tempid (:db-after r) (:tempids r) tempid))))) 78 | 79 | 80 | (defn update-phone [conn data] 81 | @(d/transact conn [(assoc data :db/id (edn/read-string (:db/id data)))]) 82 | true) 83 | 84 | 85 | (defn delete-phone [conn id] 86 | @(d/transact conn [[:db.fn/retractEntity (edn/read-string id)]]) 87 | true) 88 | 89 | (defn create-phone [conn data]) 90 | 91 | (defn update-phone [conn data]) 92 | 93 | (defn delete-phone [conn data]) 94 | 95 | 96 | ;; return datoms to add 97 | 98 | (def initial-data 99 | (let [person-id (d/tempid :db.part/user) 100 | address-id (d/tempid :db.part/user) 101 | phone-id (d/tempid :db.part/user) 102 | email-id (d/tempid :db.part/user)] 103 | [{:db/id person-id 104 | :person/first-name "Bob" 105 | :person/last-name "Smith" 106 | :person/email email-id 107 | :person/telephone phone-id 108 | :person/address address-id} 109 | {:db/id email-id 110 | :email/address "bob.smith@gmail.com"} 111 | {:db/id phone-id 112 | :telephone/number "123-456-7890"} 113 | {:db/id address-id 114 | :address/street "111 Main St" 115 | :address/city "Brooklyn" 116 | :address/state "NY" 117 | :address/zipcode "11234"}])) 118 | 119 | 120 | (defrecord DatomicDatabase [uri schema initial-data connection] 121 | component/Lifecycle 122 | (start [component] 123 | (d/create-database uri) 124 | (let [c (d/connect uri)] 125 | @(d/transact c schema) 126 | @(d/transact c initial-data) 127 | (assoc component :connection c))) 128 | (stop [component])) 129 | 130 | (defn new-database [db-uri] 131 | (DatomicDatabase. db-uri 132 | (first (Util/readAll (io/reader (io/resource "data/schema.edn")))) 133 | initial-data 134 | nil)) 135 | 136 | ;; ============================================================================= 137 | ;; Query testing 138 | 139 | (comment 140 | (create-contact (:connection (:db @contacts.core/servlet-system)) 141 | {:person/first-name "person" :person/last-name "withphone"}) 142 | 143 | (delete-contact (:connection (:db @contacts.core/servlet-system)) 144 | "17592186045423") 145 | 146 | (update-contact (:connection (:db @contacts.core/servlet-system)) 147 | {:db/id "17592186045429" :person/first-name "Foooo"}) 148 | 149 | 150 | (create-phone (:connection (:db @contacts.core/servlet-system)) 151 | {:person/_telephone "17592186045438" 152 | :telephone/number "123-456-7890"}) 153 | 154 | (d/touch (d/entity (d/db (:connection (:db @contacts.core/servlet-system))) 155 | 17592186045438)) 156 | 157 | (update-phone (:connection (:db @contacts.core/servlet-system)) 158 | {:db/id "17592186045440" 159 | :telephone/number "000-456-7890"}) 160 | 161 | (delete-phone (:connection (:db @contacts.core/servlet-system)) 162 | "17592186045440") 163 | 164 | ) 165 | 166 | 167 | -------------------------------------------------------------------------------- /contacts/src/clj/contacts/server.clj: -------------------------------------------------------------------------------- 1 | (ns contacts.server 2 | (:require [contacts.util :as util] 3 | [ring.util.response :refer [file-response resource-response]] 4 | [ring.adapter.jetty :refer [run-jetty]] 5 | [ring.middleware.edn :refer [wrap-edn-params]] 6 | [ring.middleware.resource :refer [wrap-resource]] 7 | [bidi.bidi :refer [make-handler] :as bidi] 8 | [com.stuartsierra.component :as component] 9 | [datomic.api :as d] 10 | [contacts.datomic] 11 | )) 12 | 13 | ;; ============================================================================= 14 | ;; Routing 15 | 16 | (def routes 17 | ["" {"/" :index 18 | "/index.html" :index 19 | "/contacts" 20 | {:get 21 | {[""] :contacts 22 | ["/" :id] :contact-get} 23 | :post {[""] :contact-create} 24 | :put {["/" :id] :contact-update} 25 | :delete {["/" :id] :contact-delete}} 26 | "/phone" 27 | {:post {[""] :phone-create} 28 | :put {["/" :id] :phone-update} 29 | :delete {["/" :id] :phone-delete}}}]) 30 | 31 | ;; ============================================================================= 32 | ;; Handlers 33 | 34 | (defn index [req] 35 | (assoc (resource-response "html/index.html" {:root "public"}) 36 | :headers {"Content-Type" "text/html"})) 37 | 38 | 39 | (defn generate-response [data & [status]] 40 | {:status (or status 200) 41 | :headers {"Content-Type" "application/edn"} 42 | :body (pr-str data)}) 43 | 44 | 45 | ;; CONTACT HANDLERS 46 | 47 | (defn contacts [req] 48 | (generate-response 49 | (vec 50 | (contacts.datomic/display-contacts 51 | (d/db (:datomic-connection req)))))) 52 | 53 | 54 | (defn contact-get [req id] 55 | (generate-response 56 | (contacts.datomic/get-contact 57 | (d/db (:datomic-connection req)) id))) 58 | 59 | 60 | (defn contact-create [req] 61 | (generate-response 62 | (contacts.datomic/create-contact 63 | (:datomic-connection req) 64 | ;; must have form {:person/first-name "x" :person/last-name "y} 65 | (:edn-params req)))) 66 | 67 | 68 | (defn contact-update [req id] 69 | (generate-response 70 | (contacts.datomic/update-contact 71 | (:datomic-connection req) 72 | (assoc (:edn-params req) :db/id id)))) 73 | 74 | 75 | (defn contact-delete [req id] 76 | (generate-response 77 | (contacts.datomic/delete-contact 78 | (:datomic-connection req) 79 | id))) 80 | 81 | 82 | ;;;; PHONE HANLDERS 83 | 84 | (defn phone-create [req] 85 | (generate-response 86 | (contacts.datomic/create-phone 87 | (:datomic-connection req) 88 | ;; must have form {:person/_telephone person-id} 89 | (:edn-params req)))) 90 | 91 | (defn phone-update [req id] 92 | (generate-response 93 | (contacts.datomic/update-phone 94 | (:datomic-connection req) 95 | (assoc (:edn-params req) :db/id id)))) 96 | 97 | (defn phone-delete [req id] 98 | (generate-response 99 | (contacts.datomic/delete-phone 100 | (:datomic-connection req) 101 | id))) 102 | 103 | ;;;; PRIMARY HANDLER 104 | 105 | (defn handler [req] 106 | (let [match (bidi/match-route 107 | routes 108 | (:uri req) 109 | :request-method (:request-method req))] 110 | ;(println match) 111 | (case (:handler match) 112 | :index (index req) 113 | :contacts (contacts req) 114 | :contact-get (contact-get req (:id (:params match))) 115 | :contact-create (contact-create req) 116 | :contact-update (contact-update req (:id (:params match))) 117 | :contact-delete (contact-delete req (:id (:params match))) 118 | 119 | :phone-create (phone-create req) 120 | :phone-update (phone-update req (:id (:params match))) 121 | :phone-delete (phone-delete req (:id (:params match))) 122 | 123 | req))) 124 | 125 | 126 | (defn wrap-connection [handler conn] 127 | (fn [req] (handler (assoc req :datomic-connection conn)))) 128 | 129 | 130 | (defn contacts-handler [conn] 131 | (wrap-resource 132 | (wrap-edn-params (wrap-connection handler conn)) 133 | "public")) 134 | 135 | 136 | (defn contacts-handler-dev [conn] 137 | (fn [req] 138 | ((contacts-handler conn) req))) 139 | 140 | 141 | (defrecord WebServer [port handler container datomic-connection] 142 | component/Lifecycle 143 | (start [component] 144 | ;; NOTE: fix datomic-connection 145 | (if container 146 | (let [req-handler (handler (:connection datomic-connection)) 147 | container (run-jetty req-handler {:port port :join? false})] 148 | (assoc component :container container)) 149 | ;; if no container 150 | (assoc component :handler (handler (:connection datomic-connection))))) 151 | (stop [component] 152 | (.stop container))) 153 | 154 | 155 | (defn dev-server [web-port] (WebServer. web-port contacts-handler-dev true nil)) 156 | (defn prod-server [] (WebServer. nil contacts-handler false nil)) 157 | 158 | ;; ============================================================================= 159 | ;; Route Testing 160 | 161 | (comment 162 | 163 | ;; get contact 164 | (handler {:uri "/contacts/17592186045438" 165 | :request-method :get 166 | :datomic-connection (:connection (:db @contacts.core/servlet-system))}) 167 | 168 | ;; create contact 169 | (handler {:uri "/contacts" 170 | :request-method :post 171 | :edn-params {:person/first-name "Bib" :person/last-name "Bibooo"} 172 | :datomic-connection (:connection (:db @contacts.core/servlet-system))}) 173 | 174 | ;; update contact 175 | (handler {:uri "/contacts/17592186045434" 176 | :request-method :put 177 | :edn-params {:person/first-name "k" :person/last-name "b"} 178 | :datomic-connection (:connection (:db @contacts.core/servlet-system))}) 179 | 180 | ;; delete contact 181 | (handler {:uri "/contacts/17592186045434" 182 | :request-method :delete 183 | :datomic-connection (:connection (:db @contacts.core/servlet-system))}) 184 | 185 | ;; create phone 186 | (handler {:uri "/phone" 187 | :request-method :post 188 | :edn-params {:telephone/number "000-111-2222" 189 | :person/_telephone "17592186045438"} 190 | :datomic-connection (:connection (:db @contacts.core/servlet-system))}) 191 | 192 | ;; update phone 193 | (handler {:uri "/phone/17592186045444" 194 | :request-method :put 195 | :edn-params {:telephone/number "999-888-7777"} 196 | :datomic-connection (:connection (:db @contacts.core/servlet-system))}) 197 | 198 | 199 | ;; delete phone 200 | (handler {:uri "/phone/17592186045444" 201 | :request-method :delete 202 | :datomic-connection (:connection (:db @contacts.core/servlet-system))}) 203 | 204 | 205 | ) 206 | 207 | -------------------------------------------------------------------------------- /contacts/src/cljs/contacts/core.cljs: -------------------------------------------------------------------------------- 1 | (ns contacts.core 2 | (:require-macros [cljs.core.async.macros :refer [go]]) 3 | (:require [goog.dom :as gdom] 4 | [goog.events :as events] 5 | [secretary.core :as secretary :include-macros true :refer [defroute]] 6 | [om.core :as om :include-macros true] 7 | [om.dom :as dom :include-macros true] 8 | [cljs.core.async :refer [chan put! >! 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /contacts/LICENSE: -------------------------------------------------------------------------------- 1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and 10 | documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are 19 | distributed by that particular Contributor. A Contribution 'originates' from 20 | a Contributor if it was added to the Program by such Contributor itself or 21 | anyone acting on such Contributor's behalf. Contributions do not include 22 | additions to the Program which: (i) are separate modules of software 23 | distributed in conjunction with the Program under their own license 24 | agreement, and (ii) are not derivative works of the Program. 25 | 26 | "Contributor" means any person or entity that distributes the Program. 27 | 28 | "Licensed Patents" mean patent claims licensable by a Contributor which are 29 | necessarily infringed by the use or sale of its Contribution alone or when 30 | combined with the Program. 31 | 32 | "Program" means the Contributions distributed in accordance with this 33 | Agreement. 34 | 35 | "Recipient" means anyone who receives the Program under this Agreement, 36 | including all Contributors. 37 | 38 | 2. GRANT OF RIGHTS 39 | 40 | a) Subject to the terms of this Agreement, each Contributor hereby grants 41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 42 | reproduce, prepare derivative works of, publicly display, publicly perform, 43 | distribute and sublicense the Contribution of such Contributor, if any, and 44 | such derivative works, in source code and object code form. 45 | 46 | b) Subject to the terms of this Agreement, each Contributor hereby grants 47 | Recipient a non-exclusive, worldwide, royalty-free patent license under 48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 49 | transfer the Contribution of such Contributor, if any, in source code and 50 | object code form. This patent license shall apply to the combination of the 51 | Contribution and the Program if, at the time the Contribution is added by the 52 | Contributor, such addition of the Contribution causes such combination to be 53 | covered by the Licensed Patents. The patent license shall not apply to any 54 | other combinations which include the Contribution. No hardware per se is 55 | licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses 58 | to its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other 60 | intellectual property rights of any other entity. Each Contributor disclaims 61 | any liability to Recipient for claims brought by any other entity based on 62 | infringement of intellectual property rights or otherwise. As a condition to 63 | exercising the rights and licenses granted hereunder, each Recipient hereby 64 | assumes sole responsibility to secure any other intellectual property rights 65 | needed, if any. For example, if a third party patent license is required to 66 | allow Recipient to distribute the Program, it is Recipient's responsibility 67 | to acquire that license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient 70 | copyright rights in its Contribution, if any, to grant the copyright license 71 | set forth in this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under 76 | its own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title 84 | and non-infringement, and implied warranties or conditions of merchantability 85 | and fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered 92 | by that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such 95 | Contributor, and informs licensees how to obtain it in a reasonable manner on 96 | or through a medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within 105 | the Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, 108 | if any, in a manner that reasonably allows subsequent Recipients to identify 109 | the originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a 117 | manner which does not create potential liability for other Contributors. 118 | Therefore, if a Contributor includes the Program in a commercial product 119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend 120 | and indemnify every other Contributor ("Indemnified Contributor") against any 121 | losses, damages and costs (collectively "Losses") arising from claims, 122 | lawsuits and other legal actions brought by a third party against the 123 | Indemnified Contributor to the extent caused by the acts or omissions of such 124 | Commercial Contributor in connection with its distribution of the Program in 125 | a commercial product offering. The obligations in this section do not apply 126 | to any claims or Losses relating to any actual or alleged intellectual 127 | property infringement. In order to qualify, an Indemnified Contributor must: 128 | a) promptly notify the Commercial Contributor in writing of such claim, and 129 | b) allow the Commercial Contributor tocontrol, and cooperate with the 130 | Commercial Contributor in, the defense and any related settlement 131 | negotiations. The Indemnified Contributor may participate in any such claim 132 | at its own expense. 133 | 134 | For example, a Contributor might include the Program in a commercial product 135 | offering, Product X. That Contributor is then a Commercial Contributor. If 136 | that Commercial Contributor then makes performance claims, or offers 137 | warranties related to Product X, those performance claims and warranties are 138 | such Commercial Contributor's responsibility alone. Under this section, the 139 | Commercial Contributor would have to defend claims against the other 140 | Contributors related to those performance claims and warranties, and if a 141 | court requires any other Contributor to pay any damages as a result, the 142 | Commercial Contributor must pay those damages. 143 | 144 | 5. NO WARRANTY 145 | 146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON 147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR 149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A 150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the 151 | appropriateness of using and distributing the Program and assumes all risks 152 | associated with its exercise of rights under this Agreement , including but 153 | not limited to the risks and costs of program errors, compliance with 154 | applicable laws, damage to or loss of data, programs or equipment, and 155 | unavailability or interruption of operations. 156 | 157 | 6. DISCLAIMER OF LIABILITY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 166 | OF SUCH DAMAGES. 167 | 168 | 7. GENERAL 169 | 170 | If any provision of this Agreement is invalid or unenforceable under 171 | applicable law, it shall not affect the validity or enforceability of the 172 | remainder of the terms of this Agreement, and without further action by the 173 | parties hereto, such provision shall be reformed to the minimum extent 174 | necessary to make such provision valid and enforceable. 175 | 176 | If Recipient institutes patent litigation against any entity (including a 177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 178 | (excluding combinations of the Program with other software or hardware) 179 | infringes such Recipient's patent(s), then such Recipient's rights granted 180 | under Section 2(b) shall terminate as of the date such litigation is filed. 181 | 182 | All Recipient's rights under this Agreement shall terminate if it fails to 183 | comply with any of the material terms or conditions of this Agreement and 184 | does not cure such failure in a reasonable period of time after becoming 185 | aware of such noncompliance. If all Recipient's rights under this Agreement 186 | terminate, Recipient agrees to cease use and distribution of the Program as 187 | soon as reasonably practicable. However, Recipient's obligations under this 188 | Agreement and any licenses granted by Recipient relating to the Program shall 189 | continue and survive. 190 | 191 | Everyone is permitted to copy and distribute copies of this Agreement, but in 192 | order to avoid inconsistency the Agreement is copyrighted and may only be 193 | modified in the following manner. The Agreement Steward reserves the right to 194 | publish new versions (including revisions) of this Agreement from time to 195 | time. No one other than the Agreement Steward has the right to modify this 196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 197 | Eclipse Foundation may assign the responsibility to serve as the Agreement 198 | Steward to a suitable separate entity. Each new version of the Agreement will 199 | be given a distinguishing version number. The Program (including 200 | Contributions) may always be distributed subject to the version of the 201 | Agreement under which it was received. In addition, after a new version of 202 | the Agreement is published, Contributor may elect to distribute the Program 203 | (including its Contributions) under the new version. Except as expressly 204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 205 | licenses to the intellectual property of any Contributor under this 206 | Agreement, whether expressly, by implication, estoppel or otherwise. All 207 | rights in the Program not expressly granted under this Agreement are 208 | reserved. 209 | 210 | This Agreement is governed by the laws of the State of Washington and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. 215 | --------------------------------------------------------------------------------