collect.lisp 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. ;;;; collect.lisp
  2. ;;;;
  3. ;;;; Copyright (c) 2015 Samuel W. Flint <swflint@flintfam.org>
  4. (in-package #:collect)
  5. ;;; "collect" goes here. Hacks and glory await!
  6. (defmacro define-application (name file &body body-forms)
  7. (declare (string name))
  8. `(progn
  9. (restas:define-module ,name
  10. (:use :cl
  11. :cl-who
  12. :parenscript
  13. :sqlite
  14. :restas
  15. :iter)
  16. (:import-from #:hunchentoot
  17. #:post-parameter)
  18. (:import-from #:lass
  19. #:compile-and-write))
  20. (in-package ,name)
  21. (defvar *tables*)
  22. (defvar *db*)
  23. (defun start (port)
  24. (setq *db* (connect ,file))
  25. (iter (for (name . query))
  26. (do (execute-non-query *db* query)))
  27. (restas:start ,name :port 8080))
  28. ,@body-forms))
  29. (defun to-sql-type (type)
  30. (cadr (assoc type '((:integer "INTEGER")
  31. (:text "TEXT")
  32. (:real "REAL")
  33. (:blob "BLOB")))))
  34. (defun relation (relationship)
  35. (if (not (null relationship))
  36. (destructuring-bind (table column) relationship
  37. (declare (string table column))
  38. (format nil " REFERENCES ~a (~a)"
  39. table
  40. column))
  41. ""))
  42. (defun col-to-clause (column)
  43. (destructuring-bind (name type &key (nullable t) (primary nil) relationship) column
  44. (declare (string name)
  45. (keyword type)
  46. (ignorable nullable relationship))
  47. (if (null primary)
  48. (if (null nullable)
  49. (format nil "~a ~a~a,"
  50. name
  51. (to-sql-type type)
  52. (relation relationship))
  53. (format nil "~a ~a~a NOT NULL,"
  54. name
  55. (to-sql-type type)
  56. (relation relationship)))
  57. (format nil "~a ~a PRIMARY KEY,"
  58. name
  59. (to-sql-type type)))))
  60. (defmacro define-table (name (&rest cols) &key &allow-other-keys)
  61. (let* ((clauses (map 'list #'col-to-clause cols))
  62. (sql-command (format nil "CREATE TABLE IF NOT EXISTS ~a~%(~%~{~a~%~});"
  63. name
  64. clauses)))
  65. `(push (cons ,name ,sql-command) *tables*)))
  66. (defun make-form-entry (entry)
  67. (destructuring-bind (label column &key (type :text) other-column options) entry
  68. (declare (string label column)
  69. (ignorable other-column options))
  70. (let ((label-name (concatenate 'string "fld_" column))
  71. (the-options (if (null options)
  72. (if (not (null other-column))
  73. `(,(car other-column) ,(cadr other-column))))))
  74. (case type
  75. (:text
  76. `(:tr
  77. (:td ,label)
  78. (:td (:input :type :text
  79. :name ,label-name))))
  80. (:drop-down
  81. `(:tr
  82. (:td ,label)
  83. (:td
  84. (:select :name ,label-name
  85. (loop for option in ,the-options
  86. collect (make-option-drop-down option))))))
  87. (:radio-buttons
  88. `(:tr
  89. (:td ,label)
  90. (:td (loop for option in ,the-options
  91. collect (make-option-radio option ,label-name)))))))))
  92. (defun make-option-drop-down (option)
  93. (let ((name (first option))
  94. (value (second option)))
  95. `(:option :value ,value ,name)))
  96. (defun make-option-radio (option label)
  97. (let ((name (first option))
  98. (value (second option)))
  99. `(:div (:input :type "radio" :name ,label :value ,value)
  100. ,name (:br))))
  101. (defmacro define-form (name table (&rest fields) &key &allow-other-keys)
  102. (let* ((columns (map 'list #'second fields))
  103. (field-names (map 'list #'(lambda (column)
  104. (concatenate 'string "fld_" column))
  105. columns))
  106. (form-entries (map 'list #'make-form-entry fields)))
  107. `(list (list ,@columns)
  108. (list ,@field-names))))