(defpackage :fwoar.web-test (:import-from :fwoar.myway-acceptor :define-route-group) (:local-nicknames (:db :fwoar.web-test.db)) (:use :cl ) (:export )) (in-package :fwoar.web-test) (defclass my-server (fwoar.websocket-acceptor:websocket-acceptor fwoar.myway-acceptor:myway-acceptor) ((%models :initarg :models :reader models))) (defgeneric register-model (server model) (:method (server model) (error "~s is not a class or a class name" model)) (:method (server (model standard-class)) (error "can only register server-aware classes, got ~s!" model)) (:method ((server my-server) (model fwoar.server-aware-class:server-aware-class)) (setf (fwoar.server-aware-class:server model) server)) (:method ((server my-server) (model symbol)) (register-model server (find-class model)))) (defclass console-publisher () ()) (defmethod register-model ((publisher console-publisher) (model fwoar.server-aware-class:server-aware-class)) (setf (fwoar.server-aware-class:server model) publisher)) (defmethod update-instance-for-redefined-class ((instance my-server) added-slots discarded-slots property-list &key models) (when (member '%models added-slots) (setf (slot-value instance '%models) models))) (defclass user (hunchensocket:websocket-client) ((name :initarg :user-agent :reader name :initform (error "Name this user!")))) (defclass chat-room (hunchensocket:websocket-resource) ((name :initarg :name :initform (error "Name this room!") :reader name)) (:default-initargs :client-class 'user)) (defvar *chat-rooms* (list (make-instance 'chat-room :name "/bongo") (make-instance 'chat-room :name "/fury"))) (defmethod fwoar.websocket-acceptor:find-websocket-resource ((acceptor my-server) request) (find (hunchentoot:script-name request) *chat-rooms* :test #'string= :key #'name)) (defun broadcast (room message &rest args) (loop for peer in (hunchensocket:clients room) do (hunchensocket:send-text-message peer (apply #'format nil message args)))) (defmethod hunchensocket:client-connected ((room chat-room) user) ) (defmethod hunchensocket:client-disconnected ((room chat-room) user) ) (defmethod hunchensocket:text-message-received ((room chat-room) user message) ) (defun render-post (post) (spinneret:with-html (:section (:header (:h* (db:title post)) (:div.author (db:author post))) (:section (db:content post))))) (defun summarize (post) (spinneret:with-html (:li (:a :href (format nil "/post/~a" (db:id post)) (db:title post))))) (defmethod fwoar.server-aware-class:publish-value ((server my-server) (object db:blog-post) (slot (eql 'posts)) (old-value fset:map) (new-value fset:map)) (unless (fset:equal? old-value new-value) (broadcast (car *chat-rooms*) (yason:with-output-to-string* (:indent t) (yason:with-object () (yason:encode-object-elements "slot" (string-downcase slot) "type" (string-downcase (class-name (class-of object)))) (yason:with-object-element ("value") (yason:with-array () (mapcar (lambda (it) (let ((val (fset:lookup new-value it))) (yason:with-object () (yason:encode-object-elements "id" (id val) "title" (title val))))) (fset:convert 'list (fset:domain (fset:map-difference-2 new-value old-value))))))))))) (defmethod fwoar.server-aware-class:publish-value ((server console-publisher) (object db:blog-post) slot (old-value string) (new-value string)) (unless (equal old-value new-value) (format t "~&SLOT: ~s~%FROM: ~s~&TO: ~s~&" slot old-value new-value))) (defgeneric m-index (thing) ) (defclass blog () ((%montezuma-index :initarg :montezuma :reader m-index))) (defun posts (blog) (declare (ignorable blog)) (list (db:make-blog-post "First Post" "Ed L" "This is the first post") (db:make-blog-post "Second Post" "Ed L" "This is the second post") (db:make-blog-post "Third Post" "Ed L" "This is the third post") (db:make-blog-post "Fourth Post" "Ed L" "This is the fourth post"))) (defun connect-script () (ps:ps (defun summarize (post) (spinneret:with-html (:li (:a :href (ps:chain "/post/" (concat (ps:@ post id))) (ps:@ post title))))) (setf (ps:@ (ps:new (-web-socket "ws://localhost:12345/bongo")) onmessage) (lambda (e) (let ((data (ps:chain -j-s-o-n (parse (ps:@ e data))))) (ps:chain data value (map (lambda (x) (ps:chain document (query-selector "#posts") (append-child (summarize x))))))))))) (defun page () (spinneret:with-html-string (:doctype) (:html (:body (:main (:section (:h* "My Blog") (:ul#posts (loop for post in (fset:convert 'list (fset:range (posts blog))) do (summarize post))) (:script (connect-script)))))))) (define-route-group blog-methods (server blog) (:route "/" ((server my-server) (blog blog)) (lambda (params) (declare (ignore params)) )) (:route "/post/:id" ((server my-server) (blog blog)) (lambda (params) (spinneret:with-html-string (:doctype) (:html (:body (:main (:section (:h* "My Blog") (render-post (fset:lookup (posts blog) (getf params :id)))))))))))