4 ms·
I couldn't resist making a Common Lisp version. DB, REST and SECURITY are imaginary packages. CLSQL is an existing library for interfacing with databases. You
by junke 10y ago
I couldn't resist making a Common Lisp version.
DB, REST and SECURITY are imaginary packages. CLSQL is an existing library for interfacing with databases.
You define the meat of your code with a primary method:
(defmethod get-user-preferences (id)
(user-preferences
(db:get-user id)))
Then, you add security checks with an AROUND qualifier; CALL-NEXT-METHOD is a little like super() in Java, but with multiple-inheritance and method qualifiers (around, before, after, ...):
(defmethod get-user-preferences :around ((id integer))
(if (security:allowedp id)
(call-next-method)
rest:+FORBIDDEN+))
You can add logging, executed before the primary method, after security checks.
(defmethod get-user-preferences :before (id)
(log "Getting user preferences for id ~S." id))
Likewise, you could log the outcome of the security check;
(defmethod security:allowedp :around (id)
(let ((result (call-next-method)))
(log "Access ~:[denied~;granted~] for id ~S." result id)
result)))
Also, you can wrap the call to db:get-user with the appropriate dynamic context where a database connection is open (it could be from a pool, or something else) within a transaction.
(defmethod db:get-user :around ((id integer))
(clsql:with-database (db *database*)
(clsql:with-transaction (:database db)
(call-next-method))))
I did not find an equivalent for the read-only flag.
If more methods need to follow this pattern, it might be preferable to define them directly with a macro which always open the connection and the transaction. The WITH-X macro ensure that transactions and connections are properly handled when unwinding the stack.
- gerbilly 10y agoAOP huh? Nice. The scenario i described above was from a real project I was on a few years ago. The way I handled it (partially) was via the use of AOP. I also made use of dependency injection. These two approaches combined managed to reduce the boilerplate to near zero. Of course, in Java AOP means you have to take care to enable it in each part of the toolchain (build system, CDP, IDE ...) but it wasn't too bad. I'm not sure which gang of 4 patterns I may have been using, and I don't care, but I do know that leaving it simple would have led to chaos, because I'd seen it happen before.