Skip to content

Commit

Permalink
added create-with macro, relate support a hash-map, test cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
wilkes committed Jul 21, 2010
1 parent 2d7d1bf commit c0df945
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 45 deletions.
26 changes: 18 additions & 8 deletions src/karras/entity.clj
Expand Up @@ -58,6 +58,9 @@ Example:
(keyword? x) (recur (cons y zs) (assoc results x {}))
:else (throw (IllegalArgumentException. (str x " is not a keyword or a map"))))))))

(defn- assoc-meta-cache [v]
(with-meta v (merge {:cache (atom nil)} (meta v))))

(declare make)
(defmulti convert
"Multimethod used to convert a entity.
Expand All @@ -72,13 +75,11 @@ Example:

(defmethod convert :reference
[field-spec val]
(with-meta (make (:of field-spec) val :no-defaults)
{:cache (atom nil)}))
(assoc-meta-cache (make (:of field-spec) val :no-defaults)))

(defmethod convert :references
[field-spec vals]
(with-meta (map #(make (:of field-spec) % :no-defaults) vals)
{:cache (atom nil)}))
(assoc-meta-cache (map #(make (:of field-spec) % :no-defaults) vals)))

(defmethod convert :default
[_ val]
Expand Down Expand Up @@ -312,14 +313,14 @@ Example:
[entity k & vs]
(if-not (remove nil? (map :_id vs))
(throw (IllegalArgumentException. "All references must have an :_id.")))
(assoc entity k (concat (or (k entity) []) (map make-reference vs))))
(assoc entity k (assoc-meta-cache (concat (or (k entity) []) (map make-reference vs)))))

(defn set-reference
"Set an :_id to the key of the given entity"
[entity k v]
(if-not (:_id v)
(throw (IllegalArgumentException. "Reference must have an :_id.")))
(assoc entity k (make-reference v)))
(assoc entity k (assoc-meta-cache (make-reference v))))

(defn get-reference
"Fetch the entity or entities referrenced by the given key."
Expand Down Expand Up @@ -363,14 +364,23 @@ Example:
{:_db \"db-name\" :_id ObjectId :_ref \"collection-name\")}"
[parent k & vs]
(let [field-spec (field-spec-of (class parent) k)
saved-entities (map #(if (:_id %) % (save %)) vs)]
related-type (:of field-spec)
saved-entities (map #(if (:_id %)
%
(create related-type %))
vs)]
(if (= :reference (:type field-spec))
(set-reference parent k (first saved-entities))
(if (= :references (:type field-spec))
(apply add-reference parent k saved-entities)
parent))))

(defn fetch-refer-to
(defmacro create-with [type hmap & relations]
`(-> (make ~type ~hmap)
~@relations
save))

(defn fetch-refers-to
"Given an entity, a type and a field, fetch all the entities of the given type
that refer to the given entity. Takes the same options as fetch. "
[entity referrer-type referrer-field & options]
Expand Down
72 changes: 35 additions & 37 deletions test/karras/test_entity.clj
Expand Up @@ -37,7 +37,7 @@
:middle-initial
:last-name
:birthday {:type ::my-date}
:blood-alcohol-level {:default 0.0}
:counter {:default 0}
:address {:type Address}
:phones {:type :list :of Phone}
:responsibity {:type :reference :of Resposibility}]
Expand Down Expand Up @@ -66,16 +66,15 @@

(use-fixtures :each (fn [t]
(mongo
(drop-collection (collection-for Person))
(drop-collection (collection-for Company))
(drop-collection (collection-for Simple))
(t))))
(doseq [t [Person Company Simple]]
(drop-collection (collection-for t)))
(t))))

(deftest test-parse-fields
(let [parsed? (fn [fields expected-parsed-fields]
(expect (parse-fields fields) => expected-parsed-fields))]
(testing "empty fields"
(parsed? nil {}))
(parsed? nil {}))
(testing "no type specified"
(parsed? [:no-type] {:no-type {}}))
(testing "type specified"
Expand All @@ -95,7 +94,7 @@

(deftest test-entity-spec
(doseq [e [Address Phone Person]]
(expect e => not-nil?)))
(expect (entity-spec e) => not-nil?)))

(deftest test-entity-spec-in
(expect (entity-spec-of Person :address) => (entity-spec Address))
Expand All @@ -111,7 +110,7 @@

(deftest test-make
(testing "flat"
(expect (class (make Phone {:number "555-555-1212"})) => Phone))
(expect (class (make Phone {})) => Phone))
(testing "nested"
(let [address (make Address {:city "Nashville"
:street {:number "123"
Expand All @@ -130,7 +129,7 @@
(expect (-> person :address class) => Address)
(expect (-> person :address :street class) => Street)
(expect (-> person :phones first class)=> Phone)
(expect (-> person :blood-alcohol-level) => 0.0)
(expect (-> person :counter) => 0.0)
(expect (-> person :phones first :country-code) => 1)
(expect (-> person save :_id) => not-nil?)))
(testing "preserves the metadata of original hash")
Expand All @@ -155,13 +154,12 @@
(expect (fetch-one Person (where (eq :_id (:_id person))))
=> person))
(testing "fetch-all"
(expect (first (fetch-all Person))
=> person))
(expect (fetch-all Person) => [person]))
(testing "fetch"
(expect (first (fetch Person (where (eq :last-name "Smith"))))
=> person)
(expect (first (fetch Person (where (eq :last-name "Nobody"))))
=> nil)
(expect (fetch Person (where (eq :last-name "Smith")))
=> [person])
(expect (fetch Person (where (eq :last-name "Nobody")))
=> [])
(expect (fetch-one Person (where (eq :last-name "Nobody")))
=> nil))
(testing "save"
Expand All @@ -175,7 +173,7 @@
(testing "deletion"
(dotimes [x 5]
(create Person {:first-name "John" :last-name (str "Smith" (inc x))}))
(expect (first (distinct-values Person :first-name)) => "John")
(expect (distinct-values Person :first-name) => #{"John"})
(expect (count-instances Person) => 6)
(testing "delete"
(delete person)
Expand All @@ -198,19 +196,18 @@
(deftest test-ensure-indexes
(expect (list-indexes Person) => empty?)
(ensure-indexes)
;; 2 + _id index
(expect (count (list-indexes Person)) => 3))
(expect (count (list-indexes Person)) => 3)) ;; 2 + _id index

(deftest test-references
(testing "saving"
(let [john (-> (make Person {:first-name "John" :last-name "Smith"})
(relate :responsibity (make Resposibility {:name "in charge"}))
save)
(let [john (create-with Person
{:first-name "John" :last-name "Smith"}
(relate :responsibity {:name "in charge"}))
jane (create Person {:first-name "Jane" :last-name "Doe"})
company (-> (make Company {:name "Acme"})
(relate :ceo john)
(relate :employees jane)
save)]
company (create-with Company
{:name "Acme"}
(relate :ceo john)
(relate :employees jane))]
(expect (-> company :ceo :_id) => (:_id john))
(expect (-> company :employees first :_id) => (:_id jane))))
(testing "reading"
Expand All @@ -229,26 +226,27 @@
(expect (grab-in company [:ceo :first-name]) => "John")
(expect (grab-in company [:ceo :responsibity :name]) => "in charge"))))
(testing "updating"
(let [bill (create Person {:first-name "Bill" :last-name "Jones"})
company (-> (fetch-one Company (where (eq :name "Acme")))
(add-reference :employees bill))
[jane bill] (get-reference company :employees)]
(let [company (-> (fetch-one Company (where (eq :name "Acme")))
(relate :employees {:first-name "Bill" :last-name "Jones"}))
[jane bill] (grab company :employees)]
(expect (:first-name jane) => "Jane")
(expect (:first-name bill) => "Bill")))
(testing "reverse look up company from person"
(let [company (fetch-one Company (where (eq :name "Acme")))
john (fetch-one Person (where (eq :first-name "John")))
jane (fetch-one Person (where (eq :first-name "Jane")))]
(expect (fetch-refer-to john Company :ceo) => [company])
(expect (fetch-refer-to jane Company :employees) => [company]))))
(expect (fetch-refers-to john Company :ceo) => [company])
(expect (fetch-refers-to jane Company :employees) => [company]))))

(deftest test-grab-caching
(let [john (create Person {:first-name "John" :last-name "Smith"})
jane (create Person {:first-name "Jane" :last-name "Doe"})
company (-> (create Company {:name "Acme"})
(relate :ceo john)
(relate :employees jane)
save)]
(let [company (create-with Company
{:name "Acme"}
(relate :ceo
{:first-name "John" :last-name "Smith"})
(relate :employees
{:first-name "Jane" :last-name "Doe"}))
john (fetch-one Person (where (eq :first-name "John")))
jane (fetch-one Person (where (eq :first-name "Jane")))]
(testing "single reference"
(expect (grab company :ceo) => :fake-result
(fake (get-reference company :ceo) => :fake-result))
Expand Down

0 comments on commit c0df945

Please sign in to comment.