Lisp bundle for Textmate April 9th, 2008
There's a nice Lisp bundle for Textmate available, it ads syntax highlighting and snippets for defun, defmacro, setf etc. Just check it out with subversion:
>$ cd Library/Application\ Support/Textmate/Bundles/ svn co http://macromates.com/svn/Bundles/trunk/Bundles/Lisp.tmbundle/
Common Lisp First Contact: an easygoing introduction to Lisp April 8th, 2008
Sven Van Caekenberghe, author of the KPAX web-application framework has published a nice and easy-to-read introduction to Common Lisp a called Common Lisp: First Contact. If you're not familiar with the Common Lisp programming languange and its syntax and have 10 minutes to spare it makes one hell of an introduction and the PDF is beautifully tuned for reading it on your widescreen monitor.
Make mistakes fast, fix them even faster November 13th, 2007
A great blog-post about Lisp and some of principles of Agile Development. Having a very short edit-compile-run cycle (put on your "I HEART REPL" badge) spoils you to no end. Also see this InfoQ article about the same topic.
I consider it one of the biggest advantages of a Lisp development environment, and am glad the same warm and fuzzy feeling is around when coding Ruby. Smalltalk programmers would agree also.
using Lisp to store your data on Amazon S3 May 29th, 2007
What you need
CL-S3 has the following dependencies, most of which are other packages from Sven, and most are installable using ASDF-INSTALL
- s-http-client, to speak the HTTP lingo
- s-xml, no webservices without XML right?
- s-utils, some utility code that CL-S3 uses
- s-base64, al your base are belong to us!
KPAX, Sven's web framework (for URI-encoding functions)
- ironclad, Nathan Froyds nice cryptography package, providing MD5 functionality
Download CL-S3 from Amazon S3 itself and symlink its cl-s3.asd in your ASDF systems directory.
So fire up your favourite REPL (I'm using Lispworks Personal Edition for this example) and load it al up using ASDF:
CL-USER 3 > (setf (logical-pathname-translations "home") `(("**;*.*.*" ,(concatenate 'string (namestring (user-homedir-pathname)) "**/*.*")))) (("**;*.*.*" "/Users/tarkin/**/*.*")) CL-USER 4 > (unless (member :asdf *features*) (load #p"home:Lisp;asdf;init-asdf")) ; Loading text file /Users/tarkin/lisp/asdf/init-asdf.lisp ; Loading text file /Users/tarkin/lisp/asdf/asdf.lisp ;Pushed #P"/Users/tarkin/lisp/asdf/systems/" onto ASDF central registry #P"/Users/tarkin/lisp/asdf/init-asdf.lisp" CL-USER 5 > (asdf:operate 'asdf:load-op :cl-s3) ; loading system definition from /Users/tarkin/lisp/asdf/systems/cl-s3.asd into ... ; Loading fasl file /Users/tarkin/Lisp/cl-s3/cl-s3-package.nfasl ; Loading fasl file /Users/tarkin/Lisp/cl-s3/cl-s3.nfasl NIL
Giving CL-S3 the lowdown on your account
Set up your Amazon webservices access identifiers (access key id and secret access key id)
CL-S3 7 > (setf *access-key-id* "AAAAYKR2D401BBB09CCC") "AAAAYKR2D401BBB09CCC" CL-S3 4 > (setf *secret-access-key* "ooh_my_secret_is_very_secret") "ooh_my_secret_is_very_secret"
Doing the deed
So now we should be able to query the service using CL-S3.
CL-S3 11 > (get-service) ;; CL-S3 GET http://s3.amazonaws.com/ ((|ListAllMyBucketsResult| :|xmlns| "http://s3.amazonaws.com/doc/2006-03-01/") (|Owner| (ID "beeec79bbda42e62b7ac74273effdabef1ddadf1b22b4c683cc91bf64d62f033") (|DisplayName| "nickypeeters")) (|Buckets| (|Bucket| (|Name| "lisp") (|CreationDate| "2007-05-29T13:46:12.000Z")))) 200 ((:X-AMZ-ID-2 . "wb2EU2Etuv8Mw6+Y9jonKPm6lf0uGMxfcspgwnKK0YRCYorteJ08FH1cmD5ZhtpU") (:X-AMZ-REQUEST-ID . "07C15D88FD3EB8CD") (:DATE . "Tue, 29 May 2007 17:51:23 GMT") (:CONTENT-TYPE . "application/xml") (:TRANSFER-ENCODING . "chunked") (:SERVER . "AmazonS3")) #<URI http://s3.amazonaws.com:80> :KEEP-ALIVE
Let's make a bucket to hold our objects in using put-bucket
CL-S3 15 > (put-bucket "zoetrope") ;; CL-S3 PUT http://s3.amazonaws.com/zoetrope "" 200 ((:X-AMZ-ID-2 . "Dxl2qxeYXo6VvuOBscjnWEQaPZjaQlk97E+XLsL+DVRVmQMafEaRrpEnFrWyf5k9") (:X-AMZ-REQUEST-ID . "BE93305AD58B1777") (:DATE . "Tue, 29 May 2007 18:01:36 GMT") (:LOCATION . "/zoetrope") (:CONTENT-LENGTH . "0") (:SERVER . "AmazonS3")) #<URI http://s3.amazonaws.com:80/zoetrope> :NEW
Put a little text under the new bucket using put-object. You need to provide a key (e.g. filename) and the content-type of the object (e.g. text/plain). S3 returns also returns the uri of the new object.
CL-S3 16 > (put-object "zoetrope" "practical-common-lisp" "Practical Common Lisp is a nice book on Common Lisp" "text/plain") ;; CL-S3 PUT http://s3.amazonaws.com/zoetrope/practical-common-lisp "" 200 ... #<URI http://s3.amazonaws.com:80/zoetrope/practical-common-lisp> :NEW
So lets see if Amazon S3 really has our little text at http://s3.amazonaws.com:80/zoetrope/practical-common-lisp using an s-http-client request
CL-S3 17 > (s-http-client:do-http-request "http://s3.amazonaws.com:80/zoetrope/practical-common-lisp") "<?xml version=\"1.0\" encoding=\"UTF-8\"?> <Error> <Code>AccessDenied</Code> <Message>Access Denied</Message><RequestId>4BA0925CDE61CC24</RequestId> <HostId>T2NKSHtx1nOaLcbcllgy62wPlDP/GPp//MecPlcORke/gFSE7M2zpVEevGtlmIGq</HostId> </Error>" 403 ...
Oops ! S3 returns an AccessDenied error in XML. By default any object you put is for-your-eyes-only. So let's make an authenticated request using get-object
CL-S3 20 > (get-object "zoetrope" "practical-common-lisp") ;; CL-S3 GET http://s3.amazonaws.com/zoetrope/practical-common-lisp "Practical Common Lisp is a nice book on Common Lisp" 200 ...
Uploading a file and making it public
CL-S3 doesn't have a file-upload utility method, so let's use a quick slurp-file function. We set the object to public and read-only by using the canned access-policy with the x-amz-acl header of public-read
CL-S3 21 > (defun contents-of-file (pathname) (with-output-to-string (contents) (with-open-file (in pathname :direction :input) (s-utils:copy-stream in contents)))) CL-S3 22 > (put-object "zoetrope" "lispification.pdf" (contents-of-file "/Users/tarkin/lispification.pdf") "application/pdf" :amz-headers '(("x-amz-acl" . "public-read"))) ;; CL-S3 PUT http://s3.amazonaws.com/zoetrope/lispification.pdf "" 200 ... #<URI http://s3.amazonaws.com:80/zoetrope/lispification.pdf> :KEEP-ALIVE
CL-S3 has 2 utility functions for downloading and uploading files
CL-S3 39 > (download-file "lisp" "lispification.pdf") ;; CL-S3 GET http://s3.amazonaws.com/lisp/lispification.pdf #<STREAM::LATIN-1-FILE-STREAM /Users/tarkin/lispification.pdf> 200 ... #<URI http://s3.amazonaws.com:80/lisp/lispification.pdf> :KEEP-ALIVE
You can see the function returns the file-stream where the file was written. You can optionally give a directory to save the file in. Uploading a file works in the same way.
CL-S3 40 > (upload-file "/Users/tarkin/Lisp/init_lispworks.lisp" "lisp" :mime-type "text/plain" :acl "public-read") ;; CL-S3 PUT http://s3.amazonaws.com/lisp/init_lispworks.lisp "" 200 ... #<URI http://s3.amazonaws.com:80/lisp/init_lispworks.lisp> :KEEP-ALIVE
Check out the CL-S3 API docs for more info.
Flex your XML muscles into a '( ) May 15th, 2007
For some reason it seems today is all about XML. A nice post over on defmacro.org introduces non-Lispers to the infamous parenthesis by ways of comparing XML to Lisp code. So if you already expose yourself to the wonders of XML as a developer, you should be able to gradually come to love the parenthesis.
And then I come across a screencast about Adobe Flex development which strikes me as a another example for the article above. Flex is a combination of lots of XML, intermingled with ActionScript. It even has special binding expressions that refer to data/code. A real pity that halfway through the screencast they have to resort to a CDATA statement to mix-in some actual code (an event handler in the example). No wonder we don't find XML magic [anymore] after learning Lisp.
Flex looks great though. I can see it getting some traction soon, especially if you take technologies like Apollo in account.
But wouldn't it be bliss if we could Flex our muscles in Lisp instead ?