Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Demo fails: REBLOCKS/PAGE::*CURRENT-PAGE* is unbound #4

Open
hraban opened this issue Jul 5, 2024 · 4 comments
Open

Demo fails: REBLOCKS/PAGE::*CURRENT-PAGE* is unbound #4

hraban opened this issue Jul 5, 2024 · 4 comments

Comments

@hraban
Copy link

hraban commented Jul 5, 2024

I have create a small demo based off of the tutorial:

(defpackage test
  (:use #:cl
        #:arrow-macros
        #:reblocks-ui/form
        #:reblocks/html)
  (:import-from #:reblocks/widget
                #:render
                #:update
                #:defwidget)
  (:import-from #:reblocks/actions
                #:make-js-action)
  (:import-from #:reblocks/app-actions
                #:define-action)
  (:import-from #:reblocks/app
                #:defapp)
  (:import-from #:reblocks-websocket
                #:websocket-widget)
  (:export #:main))

(in-package #:test)

(defapp page :prefix "/")

(defwidget counter-box (reblocks-websocket:websocket-widget)
  ((counter :initform 0
            :accessor counter)))

(defmethod initialize-instance ((instance counter-box) &rest restargs)
  (declare (ignorable restargs))
  (call-next-method)

  (reblocks-websocket:in-thread ("Update counter")
    (sleep 3)
    ;; Updating counter
    (incf (counter instance))
    (update instance)))

(defmethod reblocks/page:init-page ((app page) (url-path string) expire-at)
  (declare (ignorable app url-path expire-at))
  (make-instance 'counter-box))

(defmethod render ((instance counter-box))
  (with-html
    (:h1 "Counter: " (counter instance))
    (with-html-form (:POST (lambda (&key action &allow-other-keys)
                             (cond
                               ((equal action "+")
                                (incf (counter instance)))
                               ((equal action "-")
                                (decf (counter instance))))
                             (update instance)))
      (:input :type :submit :name "action" :value "+")
      (:input :type :submit :name "action" :value "-"))))

(defun main ()
  (reblocks/server:start :port 4000))

But on first page load I get this error:

The variable REBLOCKS/PAGE::*CURRENT-PAGE* is unbound.
   [Condition of type UNBOUND-VARIABLE]

Restarts:
 0: [CONTINUE] Retry using REBLOCKS/PAGE::*CURRENT-PAGE*.
 1: [USE-VALUE] Use specified value.
 2: [STORE-VALUE] Set specified value and use it.
 3: [ABORT] Abort request processing and return 500.
 4: [RESET-SESSION] Reset current Weblocks session and return 500.
 5: [ABORT] abort thread (#<THREAD tid=13315 "hunchentoot-worker-127.0.0.1:61318" RUNNING {700F0146F3}>)

Backtrace:
  0: ((:METHOD INITIALIZE-INSTANCE (TEST::COUNTER-BOX)) #<TEST::COUNTER-BOX {700FF5DF13}>) [fast-method]
  1: (SB-PCL::FAST-MAKE-INSTANCE #<unavailable argument> #<unavailable &MORE argument>)
  2: ((:METHOD REBLOCKS/PAGE:INIT-PAGE :AROUND (T STRING T)) #<TEST::PAGE {700877F113}> "/websocket" NIL) [fast-method]
  3: (REBLOCKS/PAGE::CALL-WITH-PAGE-DEFAULTS #<FUNCTION (LAMBDA NIL :IN REBLOCKS/REQUEST-HANDLER::HANDLE-NORMAL-REQUEST) {700FF1B2EB}>)
  4: ((LAMBDA (#:G43 &REST #:G44) :IN REBLOCKS/REQUEST-HANDLER:HANDLE-REQUEST) NIL)
  5: ((:METHOD REBLOCKS/REQUEST-HANDLER:HANDLE-REQUEST (REBLOCKS/APP:APP)) #<TEST::PAGE {700877F113}>) [fast-method]
  6: ((FLET "doit-46" :IN REBLOCKS/REQUEST-HANDLER:HANDLE-REQUEST))
  7: ((:METHOD REBLOCKS/REQUEST-HANDLER:HANDLE-REQUEST :AROUND (T)) #<TEST::PAGE {700877F113}>) [fast-method]
  8: (LOG4CL-EXTRAS/ERROR::CALL-WITH-LOG-UNHANDLED #<FUNCTION (LAMBDA NIL :IN REBLOCKS/REQUEST-HANDLER:HANDLE-REQUEST) {700FA5E66B}> :DEPTH NIL :ERRORS-TO-IGNORE NIL)
  9: ((LAMBDA NIL :IN REBLOCKS/REQUEST-HANDLER:HANDLE-REQUEST))
 10: (REBLOCKS/ERROR-HANDLER::CALL-WITH-HANDLED-ERRORS #<FUNCTION (LAMBDA NIL :IN REBLOCKS/REQUEST-HANDLER:HANDLE-REQUEST) {700FA5E4DB}>)
 11: ((FLET REBLOCKS/RESPONSE::WITH-RESPONSE-THUNK :IN REBLOCKS/SERVER::HANDLE-HTTP-REQUEST))
 12: (REBLOCKS/RESPONSE::CALL-WITH-RESPONSE #<FUNCTION (FLET REBLOCKS/RESPONSE::WITH-RESPONSE-THUNK :IN REBLOCKS/SERVER::HANDLE-HTTP-REQUEST) {700FA5E2FB}>)
 13: ((LAMBDA NIL :IN REBLOCKS/SERVER::HANDLE-HTTP-REQUEST))
 14: (REBLOCKS/APP::CALL-IN-WEBAPP #<TEST::PAGE {700877F113}> #<FUNCTION (LAMBDA NIL :IN REBLOCKS/SERVER::HANDLE-HTTP-REQUEST) {700FA5D82B}>)
 15: ((FLET REBLOCKS/HOOKS:CALL-NEXT-HOOK :IN REBLOCKS/COMMANDS-HOOK::RESET-COMMANDS-LIST))
 16: ((FLET REBLOCKS/COMMANDS-HOOK::RESET-COMMANDS-LIST :IN "/nix/store/iaddlq8j9c8p9aqvz5i0rfyf4ij3mk4s-system-reblocks/src/commands-hook.lisp") (#<FUNCTION (LAMBDA (#:G23 &REST #:G24) :IN REBLOCKS/SERVER..
 17: ((FLET REBLOCKS/DEBUG::TRACK-LATEST-SESSION :IN REBLOCKS/DEBUG:ON) (#<FUNCTION (FLET REBLOCKS/COMMANDS-HOOK::RESET-COMMANDS-LIST :IN "/nix/store/iaddlq8j9c8p9aqvz5i0rfyf4ij3mk4s-system-reblocks/src/co..
 18: ((LAMBDA NIL :IN REBLOCKS/SERVER::HANDLE-HTTP-REQUEST))
 19: ((FLET "WITHOUT-INTERRUPTS-BODY-" :IN SB-THREAD::CALL-WITH-RECURSIVE-LOCK))
 --more--

I had a look in the guts of reblocks-websocket and it seems quite fundamental; you need the page to send updates, but this initializer is building that very same page. How do you break that cycle?¯

@svetlyak40wt
Copy link
Member

I've never tried to execute IN-THREAD during the page initialization. Previously I did this inside the RENDER method.

However, if you are calling update on counter-box widget, it will cause creation of a new thread each time. To prevent this, wrap your in-thread form with (unless reblocks-websocket:*background* (in-thread ...)).

@hraban
Copy link
Author

hraban commented Jul 5, 2024

Ah, I was going off of the tutorial in the documentation here https://40ants.com/reblocks-websocket/ which has:

(defmethod initialize-instance ((instance counter-box) &rest restargs)
  (declare (ignorable restargs))
  (call-next-method)

  (reblocks-websocket:in-thread ("Update counter")
    (sleep 3)
    ;; Updating counter
    (incf (counter instance))
    (reblocks:update instance)))

I'm still not quite clear on where else to put the code. Everywhere I put it leads to either errors, deadlocks, concurrent hash access violations, etc. Do you maybe have a live example somewhere?

@svetlyak40wt
Copy link
Member

Try to move it to the RENDER method.

@hraban
Copy link
Author

hraban commented Jul 5, 2024

Great thanks, this is definitely an improvement.

These also get triggered for async POSTs though--I guess that makes sense, but is there an easy way to only run code on the initial actual payload and not on async requests? found it.

Feel free to use this to update the docs for this package!

Current code:

(defpackage test
  (:use #:cl
        #:reblocks-ui/form
        #:reblocks/html)
  (:import-from #:reblocks-parenscript)
  (:import-from #:reblocks/widget
                #:render
                #:update
                #:defwidget)
  (:import-from #:reblocks/app
                #:defapp)
  (:import-from #:reblocks-websocket
                #:websocket-widget)
  (:export #:main))

(in-package #:test)

(defapp page :prefix "/")

(defwidget counter-box (reblocks-websocket:websocket-widget)
  ((counter :initform 0
            :accessor counter)))

(defmethod reblocks/page:init-page ((app page) (url-path string) expire-at)
  (declare (ignorable app url-path expire-at))
  (make-instance 'counter-box))

(defmethod render ((instance counter-box))
  (unless (or reblocks-websocket:*background* (reblocks/request:ajax-request-p))
    (reblocks-websocket:in-thread ("Update counter")
      (sleep 3)
      ;; Updating counter
      (incf (counter instance))
      (update instance)))
  (with-html
    (:h1 "Counter: " (counter instance))
    (with-html-form (:POST (lambda (&key btn &allow-other-keys)
                             (cond
                               ((equal btn "+")
                                (incf (counter instance)))
                               ((equal btn "-")
                                (decf (counter instance))))
                             (update instance)))
      (:input :type :submit :name "btn" :value "+")
      (:input :type :submit :name "btn" :value "-"))))

(defun main ()
  (reblocks/server:start :port 4000))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants