-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathohm-object.lisp
133 lines (121 loc) · 5.28 KB
/
ohm-object.lisp
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
;;; ohm-object.lisp
(in-package #:cl-ohm)
(defclass ohm-object ()
((id :reader ohm-id
:initarg :id)))
(defmethod print-object ((object ohm-object) stream)
(print-unreadable-object (object stream :type t :identity t)
(loop for (slot . rest) on (closer-mop:class-slots (class-of object))
for slot-name = (closer-mop:slot-definition-name slot)
do
(when (slot-boundp object slot-name)
(format stream "~S=~S"
slot-name
(slot-value object slot-name))
(when rest
(format stream " "))))))
(defmacro define-ohm-model (name superclasses &key attributes counters lists sets)
`(defclass ,name ,(if superclasses superclasses '(ohm-object))
(
,@(mapcar (lambda (attribute)
(let ((attr attribute))
;; make attribute a list
(unless (listp attr)
(setf attr (list attr)))
;; add accessor if missing
(unless (or (getf (cdr attr) :reader)
(getf (cdr attr) :writer)
(getf (cdr attr) :accessor))
(setf attr
(append attr
(list :accessor (car attr)))))
;; add initarg if missing
(unless (getf (cdr attr) :initarg)
(setf attr
(append attr
(list :initarg (intern (string (car attr)) :keyword)))))
attr))
attributes)
,@(mapcar (lambda (counter)
`(,counter
:accessor ,counter
:counterp t))
counters)
,@(mapcar (lambda (list)
(append list
(list :list-attr-p t
:accessor (car list))))
lists)
,@(mapcar (lambda (set)
(append set
(list :set-attr-p t
:accessor (car set))))
sets))
(:metaclass ohm-class)))
(defmethod initialize-instance :after ((instance ohm-object) &key)
(dolist (slot (closer-mop:class-slots (class-of instance)))
(let ((slot-name (closer-mop:slot-definition-name slot)))
;; FIXME: this is quite ugly and needs refactoring. Keeping it for now.
(cond
((counterp slot)
(setf (slot-value instance slot-name)
(make-instance 'ohm-counter
:key (object-key instance 'counters)
:name slot-name)))
((list-attr-p slot)
(let ((element-type (element-type slot)))
(assert (subtypep element-type 'ohm-object)
(element-type)
"Element type must be a persistable type. ~A is not persistable."
element-type)
(setf (slot-value instance slot-name)
(make-instance 'ohm-list
:key (object-key instance slot-name)
:element-type element-type))))
((set-attr-p slot)
(let ((element-type (element-type slot)))
(assert (subtypep element-type 'ohm-object)
(element-type)
"Element type must be a persistable type. ~A is not persistable."
element-type)
(setf (slot-value instance slot-name)
(make-instance 'ohm-set
:key (object-key instance slot-name)
:element-type element-type))))))))
(defun object->plist (object)
"Creates a plist of OBJECT's attributes."
(let ((attributes (remove-if (lambda (slot)
(or (counterp slot)
(list-attr-p slot)
(set-attr-p slot)
(eql 'indices (closer-mop:slot-definition-name
slot))))
(closer-mop:class-slots (class-of object)))))
(loop for attribute in attributes
nconc
(let ((attribute-name (closer-mop:slot-definition-name attribute)))
(when (slot-boundp object attribute-name)
(list (make-keyword attribute-name)
(slot-value object attribute-name)))))))
(defun normalize-plist (plist)
"Creates a proper plist of the given TUPLE."
(loop for (key value) on plist by #'cddr
append (list (make-keyword key) value)))
(defun plist->object (class-name plist)
"Creates an instance of CLASS-NAME with initargs found in plist."
(let ((norm-plist (normalize-plist plist)))
(apply #'make-instance class-name norm-plist)))
(defun fetch (namespace ids)
"Loads objects from the data store."
(with-connection ()
(with-pipelining
(loop for key in (keys namespace ids)
do (red:hgetall key)))))
(defun fetch-one (namespace id)
"Load one object from the data store."
(cl:first (fetch namespace (list id))))
(defun keys (namespace ids)
"Makes a list of keys for the given NAMESPACE and IDS."
(mapcar (lambda (id)
(make-key namespace id))
ids))