As I said in the subject, I'm working on a JS-only (no server-side code) HTML5 webapp in Parenscript with jQuery. I have extensive JS experience, but I'm really new to Common Lisp.
I wanted a setup where I could edit a Parenscript file, hit save, then hit refresh in my browser and immediately see the new code, just as if I were working in Javascript. I built a little server using Hunchentoot and cl-who that runs ps-compile-file on my parenscript source file as needed. This works fairly well, but maybe is not the best way to accomplish this.
However, there's a problem; I wanted to use parenscript functions without the namespace, so I defined a package in serve.lisp that uses parenscript, then put (in-package :innkeeper) at the top of my parenscript file. This doesn't work properly in the 2.2 release of Parenscript (which is what Quicklisp provides). It _does_ work in the git repo version (as of commit 0e01e555f404a7dcb8befb7587288c38abf526c2), but it's annoying to not be able to use Quicklisp for everything.
Once I have a release version I'll want to come up with some lisp program that compiles the parenscript to javascript, minifies it, and sticks it in an also-minified html file from cl-who, so the whole thing is compiled down to a single file. I'd appreciate any best-practice tips on that too.
Just in case my description was unclear, here's what I think are the relevant bits. If needed, I can provide the entire files off-list.
; relevant bits of serve.lisp (defpackage "INNKEEPER" (:use "COMMON-LISP" "HUNCHENTOOT" "CL-WHO" "PARENSCRIPT") (:export :start-innserver :stop-innserver))
(in-package :innkeeper)
(define-easy-handler (jsgen :uri "/innkeeper.js") () (setf (content-type*) "application/javascript") (ps-compile-file "/Users/jon/code/innkeeper/innkeeper.lisp"))
; beginning (and a few representative lines) of innkeeper.lisp
(in-package :innkeeper)
(defmacro immediate (&body body) `((lambda () ,@body)))
(var *inn* (create setup (lambda () ((@ ($ "#top-tabs") tabs)))))
(j-query #'(@ *inn* setup))
On Jan 8, 2011, at 1:44 PM, Jon Rosebaugh chairos@gmail.com wrote:
As I said in the subject, I'm working on a JS-only (no server-side code) HTML5 webapp in Parenscript with jQuery. I have extensive JS experience, but I'm really new to Common Lisp.
I wanted a setup where I could edit a Parenscript file, hit save, then hit refresh in my browser and immediately see the new code, just as if I were working in Javascript.
I have such a setup, except that it can handle several files, either js or parenscript sources, with dependencies between the files. It is an extension to ASDF. In addition, there is a rough slime plugin that will send changes you make in emacs across a websocket to your browser.
Embarrassingly not much is documented, but the relevant libraries are paren-files for compilation and slime-proxy. Paren-files is pretty well tested and mature, but needs docs. The other lib needs some love, especially with the elisp side of things.
I built a little server using Hunchentoot and cl-who that runs ps-compile-file on my parenscript source file as needed. This works fairly well, but maybe is not the best way to accomplish this.
However, there's a problem; I wanted to use parenscript functions without the namespace, so I defined a package in serve.lisp that uses parenscript, then put (in-package :innkeeper) at the top of my parenscript file. This doesn't work properly in the 2.2 release of Parenscript (which is what Quicklisp provides). It _does_ work in the git repo version (as of commit 0e01e555f404a7dcb8befb7587288c38abf526c2), but it's annoying to not be able to use Quicklisp for everything.
Yeah, Common Lisp will develop your character with charming exercises like that. In my experience it's usually worth it anyway.
Once I have a release version I'll want to come up with some lisp program that compiles the parenscript to javascript, minifies it, and sticks it in an also-minified html file from cl-who, so the whole thing is compiled down to a single file. I'd appreciate any best-practice tips on that too.
Just in case my description was unclear, here's what I think are the relevant bits. If needed, I can provide the entire files off-list.
; relevant bits of serve.lisp (defpackage "INNKEEPER" (:use "COMMON-LISP" "HUNCHENTOOT" "CL-WHO" "PARENSCRIPT") (:export :start-innserver :stop-innserver))
(in-package :innkeeper)
(define-easy-handler (jsgen :uri "/innkeeper.js") () (setf (content-type*) "application/javascript") (ps-compile-file "/Users/jon/code/innkeeper/innkeeper.lisp"))
; beginning (and a few representative lines) of innkeeper.lisp
(in-package :innkeeper)
(defmacro immediate (&body body) `((lambda () ,@body)))
(var *inn* (create setup (lambda () ((@ ($ "#top-tabs") tabs)))))
(j-query #'(@ *inn* setup))
parenscript-devel mailing list parenscript-devel@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-
Red
Jon,
Let me describe my setup to you,
I have written a product (currently not released and currently only on mac) that combines a web-browser with a webserver. The webserver accepts a call containing the javascript you want to evaluate and returns the evaluation of it.
This works very well with parenscript. I have written a macro that essentially 1) translates the Parenscript code to JS 2) sends this stuff to the web-browser and 3) returns the evaluated code
Because I am working with cappuccino, and that uses 'objective-j', my macro name is 'oj'
so if I evaluate from lisp
(oj (+ 1 2))
I get '3' back in my repl (no going to the browser, but it was actually computed in the browser)
likewise, when I have my js application loaded in the browser I can do this:
(oj (set-frame emailtable 20 20 330 240) (set-frame subject 20 20 350 32) (set-frame body 20 20 350 190)
(next-to emailtable subject) (below subject body) (below emailtable reset-email-button) (next-to reset-email-button save-email-button))
the macro runs a bunch of parenscript macros to generate the stuff that plays nice with the cappuccino framework, which gets evaluated in the browser.
The effect is immediate - Lets say I change the size of emailtable (in lisp), and re-evaluate it ( in emacs C-M-x), everything adjusts on the screen of the browser - no reload.
This way, I get the stuff that we all love about lisp when I am working with javascript.
Now there are times when I need to reload the whole enchelada (sp??) - for this I wrote a maco that steps through all of the 'oj' macros and sends it a peice at a time to the browser. (otherwise I'd send it 90k of stuff all in one request, which currently breaks somewhere)
Finally, I have one more macro that I can wrap all of the other 'oj' macros with that will write it to a file if I want to push it to an external website.
If you are using mac and want to try my little browser, please let me know. Even though I consider it 'pre-alpha' it is really quite useful.
Hope this helps, Kelly
On Sat, Jan 8, 2011 at 4:44 PM, Jon Rosebaugh chairos@gmail.com wrote:
As I said in the subject, I'm working on a JS-only (no server-side code) HTML5 webapp in Parenscript with jQuery. I have extensive JS experience, but I'm really new to Common Lisp.
I wanted a setup where I could edit a Parenscript file, hit save, then hit refresh in my browser and immediately see the new code, just as if I were working in Javascript. I built a little server using Hunchentoot and cl-who that runs ps-compile-file on my parenscript source file as needed. This works fairly well, but maybe is not the best way to accomplish this.
However, there's a problem; I wanted to use parenscript functions without the namespace, so I defined a package in serve.lisp that uses parenscript, then put (in-package :innkeeper) at the top of my parenscript file. This doesn't work properly in the 2.2 release of Parenscript (which is what Quicklisp provides). It _does_ work in the git repo version (as of commit 0e01e555f404a7dcb8befb7587288c38abf526c2), but it's annoying to not be able to use Quicklisp for everything.
Once I have a release version I'll want to come up with some lisp program that compiles the parenscript to javascript, minifies it, and sticks it in an also-minified html file from cl-who, so the whole thing is compiled down to a single file. I'd appreciate any best-practice tips on that too.
Just in case my description was unclear, here's what I think are the relevant bits. If needed, I can provide the entire files off-list.
; relevant bits of serve.lisp (defpackage "INNKEEPER" (:use "COMMON-LISP" "HUNCHENTOOT" "CL-WHO" "PARENSCRIPT") (:export :start-innserver :stop-innserver))
(in-package :innkeeper)
(define-easy-handler (jsgen :uri "/innkeeper.js") () (setf (content-type*) "application/javascript") (ps-compile-file "/Users/jon/code/innkeeper/innkeeper.lisp"))
; beginning (and a few representative lines) of innkeeper.lisp
(in-package :innkeeper)
(defmacro immediate (&body body) `((lambda () ,@body)))
(var *inn* (create setup (lambda () ((@ ($ "#top-tabs") tabs)))))
(j-query #'(@ *inn* setup))
parenscript-devel mailing list parenscript-devel@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
We have a PS program split across a dozen or so Lisp files and a "compiler" (really just a build script in the form of a CL function) that processes each file by binding *PARENSCRIPT-STREAM*, reading in PS forms from the top level of the file, and calling PS* on them to create a .js file. Then a second script assembles the component .js files into a big one, which is the entire client program. That thing is given a SHA1 hash as part of its name so it can be browser-cached forever.
This setup has proven pretty good. It's simple. It works somewhat like ASDF so it feels natural in a CL environment. It only recompiles individual js files when they change, so the round trip from editing PS code to refreshing the page in the browser is reasonably fast.
Another advantage is for debugging. When browsers, even bad ones, report an error, they provide the line number at which the error occurred. Because the big .js file is named with a SHA1 hash, it's very quick to go back to Emacs, open the precise version of the program that's producing the error (ido-mode makes this particularly quick as one only needs to type in 4 or 5 characters) and M-g M-g to the offending line number. This is so much easier than having to open the script in some horrible dinky client-side tool (even the not-so-bad ones like Firebug) and scroll around.
As I said, pretty simple. Before this we had a more complicated approach that didn't work nearly as well.
Daniel
On Sat, Jan 8, 2011 at 2:44 PM, Jon Rosebaugh chairos@gmail.com wrote:
As I said in the subject, I'm working on a JS-only (no server-side code) HTML5 webapp in Parenscript with jQuery. I have extensive JS experience, but I'm really new to Common Lisp.
I wanted a setup where I could edit a Parenscript file, hit save, then hit refresh in my browser and immediately see the new code, just as if I were working in Javascript. I built a little server using Hunchentoot and cl-who that runs ps-compile-file on my parenscript source file as needed. This works fairly well, but maybe is not the best way to accomplish this.
However, there's a problem; I wanted to use parenscript functions without the namespace, so I defined a package in serve.lisp that uses parenscript, then put (in-package :innkeeper) at the top of my parenscript file. This doesn't work properly in the 2.2 release of Parenscript (which is what Quicklisp provides). It _does_ work in the git repo version (as of commit 0e01e555f404a7dcb8befb7587288c38abf526c2), but it's annoying to not be able to use Quicklisp for everything.
Once I have a release version I'll want to come up with some lisp program that compiles the parenscript to javascript, minifies it, and sticks it in an also-minified html file from cl-who, so the whole thing is compiled down to a single file. I'd appreciate any best-practice tips on that too.
Just in case my description was unclear, here's what I think are the relevant bits. If needed, I can provide the entire files off-list.
; relevant bits of serve.lisp (defpackage "INNKEEPER" (:use "COMMON-LISP" "HUNCHENTOOT" "CL-WHO" "PARENSCRIPT") (:export :start-innserver :stop-innserver))
(in-package :innkeeper)
(define-easy-handler (jsgen :uri "/innkeeper.js") () (setf (content-type*) "application/javascript") (ps-compile-file "/Users/jon/code/innkeeper/innkeeper.lisp"))
; beginning (and a few representative lines) of innkeeper.lisp
(in-package :innkeeper)
(defmacro immediate (&body body) `((lambda () ,@body)))
(var *inn* (create setup (lambda () ((@ ($ "#top-tabs") tabs)))))
(j-query #'(@ *inn* setup))
parenscript-devel mailing list parenscript-devel@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
We have a PS program split across a dozen or so Lisp files and a "compiler" (really just a build script in the form of a CL function) that processes each file by binding *PARENSCRIPT-STREAM*, reading in PS forms from the top level of the file, and calling PS* on them to create a .js file.
Have you tried ps-compile-file to do this, and if so, how did it fall short of doing what you wanted done?
Vladimir
Sometimes we want to transform the PS expressions before compiling them, as when instrumenting them for profiling purposes. So it's easier (nearly trivial anyway) to always process the forms one-by-one.
On Mon, Jan 10, 2011 at 10:19 PM, Vladimir Sedach vsedach@gmail.com wrote:
We have a PS program split across a dozen or so Lisp files and a "compiler" (really just a build script in the form of a CL function) that processes each file by binding *PARENSCRIPT-STREAM*, reading in PS forms from the top level of the file, and calling PS* on them to create a .js file.
Have you tried ps-compile-file to do this, and if so, how did it fall short of doing what you wanted done?
Vladimir
parenscript-devel mailing list parenscript-devel@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
I wanted a setup where I could edit a Parenscript file, hit save, then hit refresh in my browser and immediately see the new code, just as if I were working in Javascript. I built a little server using Hunchentoot and cl-who that runs ps-compile-file on my parenscript source file as needed. This works fairly well, but maybe is not the best way to accomplish this.
I think the simplest way to accomplish this is using something like this script:
(defvar last-modified-time 0)
(loop (sleep 0.1) (with-open-file (in "/home/viper/file.paren" :direction :input) (when (< last-modified-time (file-write-date in)) (with-open-file (out "/home/viper/file.js" :direction :output :if-exists :supersede :if-does-not-exist :create) (princ (ps:ps-compile-stream in) out) (setf last-modified-time (file-write-date in))))))
It would be nice to polish up slime-proxy; there you won't even have to hit refresh in the browser for the changes to take effect (what Kelly McDonald is talking about). I think Daniel was the first to suggest a "SLIME for Parenscript" project to me a couple of years ago, but until slime-proxy came along no one had done much work on that. It would be really nice to have things like autocompletion and other SLIME features in an Emacs REPL that hooks up to the browser.
However, there's a problem; I wanted to use parenscript functions without the namespace, so I defined a package in serve.lisp that uses parenscript, then put (in-package :innkeeper) at the top of my parenscript file. This doesn't work properly in the 2.2 release of Parenscript (which is what Quicklisp provides). It _does_ work in the git repo version (as of commit 0e01e555f404a7dcb8befb7587288c38abf526c2), but it's annoying to not be able to use Quicklisp for everything.
I think I know where this got fixed, but I'm not sure. I'll double check that the unit test for that got added, and then make a new release and ask Zach to add it to Quicklisp.
Once I have a release version I'll want to come up with some lisp program that compiles the parenscript to javascript, minifies it, and sticks it in an also-minified html file from cl-who, so the whole thing is compiled down to a single file. I'd appreciate any best-practice tips on that too.
If you use ps:obfuscate-package and set *PS-PRINT-PRETTY* to nil, that does almost as good a job as most minifiers. I should mention that in the reference manual.
Hope that helps, Vladimir
parenscript-devel@common-lisp.net