/
associations.clj
80 lines (72 loc) · 3.15 KB
/
associations.clj
1
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
(ns datumbazo.associations
(:require [clojure.string :as str]
[datumbazo.util :as util]
[inflections.core :as infl]
[sqlingvo.core :as sql]))
(defprotocol IAssociation
(fetch [association record]
"Fetch all records in `association` of `record`."))
(defrecord BelongsTo [class-name foreign-key name primary-key table])
(defrecord HasMany [batch-size class-name dependent
foreign-key name primary-key table])
(defn- class-name
"Return the class name for `association`."
[association]
(->> [(infl/plural (name association))
(util/class-symbol {:name association})]
(concat (butlast (str/split (str *ns*) #"\.")))
(str/join ".")))
(defn belongs-to
"Add a belongs to association to a table."
[association & {:keys [foreign-key primary-key schema]}]
(fn [table]
(let [node (map->BelongsTo
{:class-name (class-name association)
:foreign-key (or foreign-key (infl/foreign-key association "-"))
:name association
:primary-key (or primary-key :id)
:schema schema
:table (keyword (infl/plural association))})]
[node (assoc-in table [:associations association] node)])))
(defn has-many
"Add a has many association to a table."
[association & {:keys [batch-size dependent foreign-key primary-key schema]}]
(fn [table]
(let [node (map->HasMany
{:batch-size (or batch-size 100)
:class-name (class-name association)
:dependent dependent
:foreign-key (or foreign-key (infl/foreign-key (:name table) "-"))
:name association
:primary-key (or primary-key :id)
:schema schema
:table (keyword association)})]
[node (assoc-in table [:associations association] node)])))
(extend-type BelongsTo
IAssociation
(fetch [association record]
(let [class (util/resolve-class (:class-name association))
columns (util/columns-by-class class)]
(->> @(sql/select (.db record) (map #(util/column-keyword % true) columns)
(sql/from
(util/table-keyword
{:schema (:schema association)
:name (:table association)}))
(sql/where `(= ~(:primary-key association)
~(get record (:foreign-key association)))))
(util/make-instances (.db record) class)
(first)))))
(extend-type HasMany
IAssociation
(fetch [association record]
(let [class (util/resolve-class (:class-name association))
table (util/table-by-class class)
columns (util/columns-by-class class)]
(->> (util/fetch-batch
(sql/select (.db record) (map #(util/column-keyword % true) columns)
(sql/from (util/table-keyword table))
(sql/where `(= ~(keyword (str (name (:name association)) "."
(name (:foreign-key association))))
~(:id record))))
{:size (:batch-size association)})
(util/make-instances (.db record) class)))))