Skip to content

Instantly share code, notes, and snippets.

@ashafa
Created April 4, 2009 00:03
Show Gist options
  • Save ashafa/90052 to your computer and use it in GitHub Desktop.
Save ashafa/90052 to your computer and use it in GitHub Desktop.
;; I want to be able to do something like this...
(defdbtest has-and-belongs-to-many-creates-find-functions
(let [humedai (manufacturer/create (valid-manufacturer-with {:name "Humedai Automotive"}))
s3000xi (product/create {:name "S-3000xi" :manufacturer_id (:id humedai)})
s3000xl (product/create {:name "S-3000xl" :manufacturer_id (:id humedai)})
chambars (dealer/create {:name "Chambers Motors"})
ira (dealer/create {:name "IRA Autos"})
tundre (dealer/create {:name "Tundre Automotives"})]
(dealer/insert-relationship chambars (:id s3000xi))
(dealer/insert-relationship chambars (:id s3000xl))
(dealer/insert-relationship ira (:id s3000xl))
(product/insert-relationship s3000xl (:id tundre))
(are (= _1 _2)
[s3000xi s3000xl] (dealer/find-products chambars)
[chambars] (product/find-dealers s3000xi)
[chambars ira tundre] (product/find-dealers s3000xl))))
;; So I did this...
(defn has-and-belongs-to-many
"Called indirectly via clj-record.core/init-model.
Experimental."
[model-name association-name table-name]
(let [associated-model-name (singularize (name association-name))
foreign-key-attribute-1 (keyword (str model-name "_id"))
foreign-key-attribute-2 (keyword (str associated-model-name "_id"))
join-table-name (if (keyword? table-name) (name table-name) table-name)
; Next line assumes the join table namespace is the same as the table name created in the database. Ideally,
; maybe create the table in the DB *only if it does not exist* and call the init-model on the join table model
; in this fn. This would get rid of the model clj file for the join table.
join-table-namespace (symbol (str (str-utils/str-join "."
(drop-last (str-utils/re-split #"\." (str *ns*))))
"." join-table-name))
find-fn-name (symbol (str "find-" association-name))
destroy-fn-name (symbol "destroy-relationship")
insert-fn-name (symbol "insert-relationship")]
`(do
(require '~join-table-namespace)
(defn ~find-fn-name [record#]
(let [associations# (clj-record.core/find-records ~join-table-name {~foreign-key-attribute-1 (record# :id)})]
(if (empty? associations#)
associations#
(clj-record.core/find-records ~associated-model-name {:id (apply query/in
(map ~foreign-key-attribute-2 associations#))}))))
(defn ~destroy-fn-name [record#]
(clj-record.core/destroy-records ~join-table-name {~foreign-key-attribute-2 (record# :id)}))
(defn ~insert-fn-name [record# id#]
(clj-record.core/insert ~join-table-name {~foreign-key-attribute-1 (record# :id) ~foreign-key-attribute-2 id#})))))
;; The model init would look like this for both of the affected models...
(clj-record.core/init-model
:table-name "productos"
(:associations
(belongs-to manufacturer)
(has-and-belongs-to-many dealers :dealers_products)))
(clj-record.core/init-model
:table-name "dealers"
(:associations
(has-and-belongs-to-many products :dealers_products)))
;; And this is the join table model init which I mentioned in the comment in
;; the has-and-belongs-to-many assocaition fn that I think I can get rid
;; of if it's eval'd in the association fn.
(clj-record.core/init-model :table-name "dealers_products") ;; :table-name included to prevent pluralization.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment