1 ;;;; -*- lisp -*- common.lisp -- common functionality used by CL-Trane libraries and projects
3 ;;;; Copyright (c) 2008, Maciej Pasternacki <maciej@pasternacki.net>
4 ;;;; All rights reserved. This file is available on the terms
5 ;;;; detailed in COPYING file included with it.
7 ;; Package for internal use in libraries and projects
8 (defpackage #:trane-common
9 (:use
#:common-lisp
) ; referenced with package name: #:py-configparser
10 (:export
#:id
#:slug
#:slug-dao
#:slugify
11 #:dirtyness-mixin
#:dirty-p
#:mark-as-dirty
#:update-dao-if-dirty
12 #:*config
* #:*init-functions
* #:on-init
#:*setup-functions
* #:on-setup
14 #:make-keyword
#:named-lambda
#:random-string
))
16 ;; Package for use outside of project
17 (defpackage #:cl-trane
20 (in-package #:trane-common
)
22 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
23 ;;; Common access methods for database objects
25 (defgeneric id
(object)
26 (:documentation
"Numeric ID or :NULL for DAOs and, if it makes sense, other objects.
28 Usually it will be a reader method automatically defined for ID column of a DAO."))
30 ;;; To enable using integer as a DAO when only ID is needed
31 (defmethod id ((i integer
))
34 ;;; NULL database id for NIL object.
35 (defmethod id ((n null
))
38 (defmethod id ((n string
))
39 (parse-integer n
:junk-allowed t
))
41 (defgeneric slug
(object)
42 (:documentation
"Get short, unique, urlified name of OBJECT."))
44 (defun slug-dao (class slug
)
45 "Select DAO of CLASS having \"slug\" column set to SLUG."
46 (first (postmodern:select-dao class
(:= 'slug slug
))))
48 ;;; FIXME:flatten characters, unicode and so on
50 (iterate (for cs in-string str
)
51 (for c
= (char-downcase cs
))
52 (for safe-p
= (find c
"abcdefghijklmnopqrstuvwxyz"))
53 (for previous-safe-p previous safe-p initially t
)
55 (unless previous-safe-p
56 (collect #\- result-type string
))
57 (collect c result-type string
))))
59 (defclass dirtyness-mixin
()
60 ((dirty-p :initform nil
:accessor dirty-p
)))
62 (defun mark-as-dirty (dao)
63 (setf (dirty-p dao
) t
))
65 (defun update-dao-if-dirty (dao)
68 (setf (dirty-p dao
) nil
)))
70 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
71 ;;; Initialization and setup protocol
73 (defvar *config
* (py-configparser:make-config
)
74 "Parsed configuration file.")
76 (defvar *config-files
* (list "config.ini")
77 "List of default configuration files.")
79 (defun read-configuration (&rest files
)
80 "Read configuration from FILES.
82 If no files are given, read files listed in *CONFIG-FILES*."
83 (py-configparser:read-files
*config
* (or files
*config-files
*)))
85 (defvar *init-functions
* ()
86 "Functions (symbols) that are FUNCALLed on initialization.
88 No assumption about init function order should be made.")
91 "Initialize everything."
93 (mapcar #'funcall
*init-functions
*))
95 (defmacro on-init
(&body body
)
96 "BODY should be sequence of DEFUN forms containing functions
97 that are to be executed on initialization."
99 (loop for defun
in body
100 collect
`(push ,defun
*init-functions
*))))
102 (defvar *setup-functions
* ()
103 "Functions (symbols) that are FUNCALLed on setup.
105 No assumption about setup function order should be made.")
110 (mapcar #'funcall
*setup-functions
*))
112 (defmacro on-setup
(&body body
)
113 "BODY should be sequence of DEFUN forms containing functions
114 that are to be executed on setup."
116 (loop for defun
in body
117 collect
`(push ,defun
*setup-functions
*))))
119 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
123 "Database connection specification")
125 (defmethod hunchentoot:dispatch-request
:around
(dispatch-table)
126 "Take care of database connection during HTTP request."
127 (postmodern:with-connection
*db
*
131 (defun init-db-connection ()
132 "Initialize database connection from config.ini"
134 (py-configparser:get-option
*config
* "db" name
)))
135 (setf *db
* (list (opt "database")
141 (defmethod print-object :around
((timestamp simple-date
:timestamp
) stream
)
144 (multiple-value-bind (year month day hour minute second millisecond
)
145 (simple-date:decode-timestamp timestamp
)
146 (declare (ignore millisecond
))
147 (format stream
"~D-~2,'0D-~2,'0D ~2,'0D:~2,'0D:~2,'0D"
148 year month day hour minute second
))))
150 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
151 ;;; General utility functions
154 (defun make-keyword (str)
155 "Convert a string or symbol to a keyword."
156 (intern (string-upcase (string str
)) :keyword
))
158 (defmacro named-lambda
(name args
&body body
)
159 "Version of LAMBDA that returns anonymous function defined with
160 FLET and named NAME, which it's PRINTed with a name in most Lisp
162 `(flet ((,name
,args
,@body
))
165 (defun random-string (&key
166 (alphabet "123456790abcdefghijklmnopqrstuvwxyz")
169 "Simple random string for initial password to use in account activation process."
170 (coerce (loop for i from
0 to
(+ min-length
(random (- max-length min-length
)))
171 collect
(aref alphabet
(random (length alphabet
))))
174 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
175 ;;; Externally exported symbols
176 (import 'init
:cl-trane
)
177 (import 'setup
:cl-trane
)
178 (export 'init
:cl-trane
)
179 (export 'setup
:cl-trane
)