<?xml version="1.0" encoding="UTF-8" ?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc=" http://purl.org/dc/elements/1.1/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>briancarper.net (λ) (Tag: Blog)</title><link>http://briancarper.net/tag/226/blog</link><description>Some guy's blog about programming and Linux and cows.</description><item><title>Who needs a DB?</title><link>http://briancarper.net/blog/who-needs-a-db</link><guid>http://briancarper.net/blog/who-needs-a-db</guid><pubDate>Wed, 20 May 2009 22:53:03 -0700</pubDate><description>&lt;p&gt;My blog is still working, in spite of my best efforts to crash it.  So that's good.  But lately I've been thinking that an SQL database is a lot of overkill just to run a little blog like this.&lt;/p&gt;

&lt;p&gt;My blog only has around 450 posts total (over the course of many years), and about an equal number of user comments (thanks to all commenters!).  Why do I need a full-blown database for that?  All of my posts plus comments plus all meta-data is only 2 MB as a flat text file, 700k gzipped.&lt;/p&gt;

&lt;p&gt;By far the most complicated part of my blog engine is the part that stuffs data into the database and gets it back out again in a sane manner (translating Clojure data to SQL values, and back again; splitting up my Clojure data structures into rows for different tables, and then re-combining values joined from multiple tables into one data structure).  Eliminating that mess would be nice.  &lt;/p&gt;

&lt;p&gt;Inevitably I ended up with some logic in the database too: enforcing uniqueness of primary keys, marking some fields as NOT NULL, giving default values and so on.  But a lot of other logic was in my Clojure code, e.g. higher-level semantic checking, and some things I wanted to set as default values were impossible to implement in SQL.  &lt;/p&gt;

&lt;p&gt;Wouldn't it be nice for all the logic to be in Clojure?  And the data store on disk to be a simple dump of a Clojure data structure?  I can (and did) write a few macros to give me SQL-like field declaration and data validation, for uniqueness of IDs and data types etc.  For my limited needs it works OK.  &lt;/p&gt;

&lt;p&gt;The next question is what format to use for dumping to disk.  Happily Clojure is Lisp, so dumping it as a huge s-exp via &lt;code&gt;pr-str&lt;/code&gt; works fine, and reading it back in later via &lt;code&gt;read-string&lt;/code&gt; is trivial.&lt;/p&gt;

&lt;p&gt;Some Java data types can't be printed readably by default, for example &lt;code&gt;java.util.Date&lt;/code&gt;s, which print like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#&amp;lt;Date Wed May 20 22:39:00 PDT 2009&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;#&amp;lt;&amp;gt;&lt;/code&gt; reader macro deliberately throws an error if you try to read that back in, because the reader isn't smart enough to craft Date objects from strings by default.  But Clojure is extensible; you can specify a readable-print method for any data type like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defmethod clojure.core/print-method java.util.Date [o w]
  (.write w (str &quot;#=&quot; `(java.util.Date. ~(.getTime o)))))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now dates print as &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#=(java.util.Date. 1242884415044)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and if you try to read that via &lt;code&gt;read-string&lt;/code&gt;, it'll create a Date object like you'd expect.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;user&amp;gt; (def x (read-string &quot;#=(java.util.Date. 1242884415044)&quot;))
#'user/x
user&amp;gt; (class x)
java.util.Date
user&amp;gt; (str x)
&quot;Wed May 20 22:40:15 PDT 2009&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Storing data in a plain file has another benefit of letting me grep my data from a command line, or even edit the data in a text editor and re-load it into the blog (God help me if that's ever necessary).&lt;/p&gt;

&lt;p&gt;Having multiple threads banging on a single file on disk is a horrible idea, but Clojure refs and agents and transactions handle that easily.  But I do have to work out how not to lose all my data in case the server crashes in the middle of a file update.  (I've lost data (in a recoverable way) due to a server crash in the middle of a MySQL update too, so this is a problem for everyone.)  Perhaps I'll keep a running history of my data, each update being a new timestamped file, so old files can't possibly be corrupted.  Or use the old write-to-tmp-file-and-rename-to-real-file routine.  Or heck, I could keep my data in Git and use Git commands from Clojure.  It'd be nice to have a history of edits.&lt;/p&gt;

&lt;p&gt;If this idea works out I'll upload code for everything to github, as usual.&lt;/p&gt;</description></item><item><title>Blog and CRUD</title><link>http://briancarper.net/blog/blog-and-crud</link><guid>http://briancarper.net/blog/blog-and-crud</guid><pubDate>Sun, 12 Apr 2009 20:01:35 -0700</pubDate><description>&lt;p&gt;I updated my &lt;a href=&quot;http://github.com/briancarper/cow-blog/tree/master&quot;&gt;blog source code on github&lt;/a&gt;.  I also split my CRUD library out into its own &lt;a href=&quot;http://github.com/briancarper/clj-crud/tree/master&quot;&gt;clj-crud&lt;/a&gt; repo.  It is cruddy, so the name is apt.&lt;/p&gt;

&lt;p&gt;This code still isn't polished enough for someone to drop it on a server and fire it up, but maybe it'll give someone some ideas.  I think the new code is cleaner and it'll be easier for me to add features now.&lt;/p&gt;

&lt;p&gt;Beware bugs, I'm positive I introduced some.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDIT&lt;/strong&gt;: A word about the CRUD library... persisting data to disk is hard when the data may be mutated by many threads at once and the destination for your data is an SQL database that may or may not even be running.  I have more respect for people who've written libraries that actually do this kind of thing and work right.  Granted I only spent 3 days on mine but still, it's tricky.&lt;/p&gt;

&lt;p&gt;I gave up for a while and tried &lt;a href=&quot;http://github.com/duelinmarkers/clj-record/tree/master&quot;&gt;clj-record&lt;/a&gt;, but it was prohibitively slow.  It has the old N+1 queries problem when trying to select an object which has N sub-objects.  In real life you'd write SQL joins to avoid such things.  Ruby on Rails on the other hand gets around this via some nasty &lt;code&gt;find&lt;/code&gt; syntax.  &lt;/p&gt;

&lt;p&gt;I get around it by having all my data in a Clojure ref in RAM already so it doesn't matter.  And by using hooks so each object keeps a list of its sub-objects and the list is always up-to-date (updates of sub-objects propagate to their parents).  But the crap I have to do to get this to just barely work is pretty painful.&lt;/p&gt;</description></item><item><title>Blog source code released</title><link>http://briancarper.net/blog/blog-source-code-released</link><guid>http://briancarper.net/blog/blog-source-code-released</guid><pubDate>Fri, 27 Mar 2009 02:14:53 -0700</pubDate><description>&lt;p&gt;By popular demand, I've released the source code for my blog.  Hope someone finds it useful.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/briancarper/cow-blog/tree/master&quot;&gt;http://github.com/briancarper/cow-blog/tree/master&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feedback and bug reports welcome, email me or post them somewhere on my blog and I'll find them.&lt;/p&gt;</description></item><item><title>Anti-spam field still holding</title><link>http://briancarper.net/blog/anti-spam-field-still-holding</link><guid>http://briancarper.net/blog/anti-spam-field-still-holding</guid><pubDate>Mon, 23 Mar 2009 19:38:27 -0700</pubDate><description>&lt;p&gt;So far my silly anti-spam measures are working.  Since last week I've had 1861 spam comment attempts, of which 0 were successful.  1857 of them didn't even alter the text my the captcha text field at all.  Four of them inexplicably HTML-escaped the &lt;code&gt;&amp;lt;&lt;/code&gt; into a &lt;code&gt;&amp;amp;lt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One feature I didn't implement from Wordpress is subscribing to comments via email.  Sending an email from Java is possible but a little bit painful to implement.  The Javamail API is a monster.&lt;/p&gt;

&lt;p&gt;I do think it's useful to be able to know when someone responds to comment you left, but is spamming your inbox really the best way?  I have to think there's a better way.&lt;/p&gt;

&lt;p&gt;I did implement an RSS feed for each individual post's comments.  And separate RSS feeds for all the tags on my blog, and all the categories.  When RSS feeds are generated dynamically, why not?  This is all of the code for the tag feeds:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defn tag-rss [tagname]
  (if-let [tag (get-tag tagname)]
    (rss
        (str &quot;briancarper.net Tag: &quot; (:name tag))
        (str &quot;http://briancarper.net/&quot; (:url tag))
        &quot;briancarper.net&quot;
        (map rss-item (take 25 (all-posts-with-tag tag))))
    (error-404 )))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Plus the routing code:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(GET &quot;/feed/tag/:name&quot; (tag-rss (route :name)))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But I haven't uploaded the comment-feed feature because I don't know if it's overkill.  Personally I am liberal with my RSS feeds, I just pop them into my Akregator and off I go.  But I don't know if other people take their feeds more seriously, or what.  RSS feeds can be a bit heavyweight.  Maybe I should make a feed for all of my comments across all posts.&lt;/p&gt;</description></item><item><title>Blog is still going strong</title><link>http://briancarper.net/blog/blog-is-still-going-strong</link><guid>http://briancarper.net/blog/blog-is-still-going-strong</guid><pubDate>Wed, 18 Mar 2009 22:01:11 -0700</pubDate><description>&lt;p&gt;After I implemented that silly CAPTCHA yesterday, the spam was stopped.  There's also a honeypot form field (it's hidden via CSS so humans don't know it's there, and if any bot POSTs text for that field, the data is rejected automatically).  It's silly and easily defeated, yet it stopped all 262 spam attempts since yesterday.  It looks like all the spam is for one site, but it's coming from a huge range of IPs.  So it's probably a botnet.  Thanks, MS Windows!&lt;/p&gt;

&lt;p&gt;I rewrote my whole CRUD layer so that I could use it for more than one database at once, and then rewrote my gallery code to take advantage, and now two hours later I have my &lt;a href=&quot;http://origamigallery.net&quot;&gt;origami gallery&lt;/a&gt; back up and running.  Both sites are running from the same JVM.  I wonder how many sites I can have going at once before the server melts into a puddle of Java-inflicted goo.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  PID PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
11338 16   0  512m 128m  12m S    0  0.3   0:28.33 java
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Good thing I have plenty of RAM on the server.  From looking at before and after shots of the memory usage, 66 MB is the JVM itself, and 40MB more is Jetty and Compojure and my code and all the dependencies.  Then the last ~20 MB or so is my database slurped into RAM.  So I can probably fit another few tens of thousands of posts and comments in here before I have to worry much.  The real test will be letting this thing run for a couple weeks and see how hard it leaks.&lt;/p&gt;</description></item><item><title>Fun with HTTP headers</title><link>http://briancarper.net/blog/fun-with-http-headers</link><guid>http://briancarper.net/blog/fun-with-http-headers</guid><pubDate>Tue, 17 Mar 2009 22:14:10 -0700</pubDate><description>&lt;p&gt;One fun thing about playing with Compojure is that it doesn't do much with HTTP headers for you, which is a good learning opportunity.  &lt;a href=&quot;http://tools.ietf.org/html/rfc2616&quot;&gt;RFC 2616&lt;/a&gt; is rather helpful here.&lt;/p&gt;

&lt;p&gt;For example I learned that if you don't set a &lt;code&gt;Cache-Control&lt;/code&gt; or &lt;code&gt;Expires&lt;/code&gt; header, your browser will happily re-fetch files over and over, which is a bit of performance hit.  Static files that don't change often like images etc. can be set with a higher &lt;code&gt;Expires&lt;/code&gt; value so they're cached.&lt;/p&gt;

&lt;p&gt;Another thing to keep in mind (note to self) is that using &lt;a href=&quot;http://httpd.apache.org/docs/2.0/mod/mod_proxy.html&quot;&gt;mod_proxy&lt;/a&gt; to forward traffic to a local Jetty server means that the &quot;remote IP&quot; you get from &lt;code&gt;(.getRemoteAddr request)&lt;/code&gt; will always be &lt;code&gt;127.0.0.1&lt;/code&gt;.  If you want the user's real remote IP, you have to look in the &lt;code&gt;X-Forwarded-For&lt;/code&gt; header (easily accessed as &lt;code&gt;(:x-forwarded-for headers)&lt;/code&gt; in Compojure.  Given that &lt;a href=&quot;http://en.wikipedia.org/wiki/Identicon&quot;&gt;Identicons&lt;/a&gt; are generated from a hash of an IP address, this has resulted in some screwed up (wrongly identical) avatars for a bunch of people in posts for the past couple days.  Oops.  Not much I can do to fix that now.&lt;/p&gt;

&lt;p&gt;In other non-news, I just the spam logging for the blog so I can see the kinds of things bots are doing to get around my feeble anti-spam measures.  Sadly the spam seems to have stopped entirely, right after I set this up.  How annoying.&lt;/p&gt;</description></item><item><title>Darn you, spammers.</title><link>http://briancarper.net/blog/darn-you-spammers</link><guid>http://briancarper.net/blog/darn-you-spammers</guid><pubDate>Tue, 17 Mar 2009 18:35:18 -0700</pubDate><description>&lt;p&gt;I was in a rush to get this darn blog finally done, so I threw some stupid anti-spam measures on here.  Namely, the comment form included 20 textareas, 19 of which were &lt;code&gt;display: hidden&lt;/code&gt; and one of which was randomly the right one, and any text in the hidden ones would cause the comment posting to fail.&lt;/p&gt;

&lt;p&gt;It only took a spam bot 48 hours to figure this out, I guess, because the last hour I've been hammered.  So I implemented a CAPTCHA as another short-term holdover until I can code up something good.  At least it immediately stopped this spam bot whose crap I've been deleting for the past hour.  &lt;/p&gt;

&lt;p&gt;Hopefully this isn't too intrusive.  I think it fits the site fairly well, as you will probably agree once you see it.&lt;/p&gt;</description></item><item><title>Clojure 1, PHP 0</title><link>http://briancarper.net/blog/clojure-1-php-0</link><guid>http://briancarper.net/blog/clojure-1-php-0</guid><pubDate>Mon, 16 Mar 2009 18:50:00 -0700</pubDate><description>&lt;h1&gt;Goodbye Wordpress&lt;/h1&gt;

&lt;p&gt;As I mentioned many times, I've been working on &lt;a href=&quot;/blog/migrating-away-from-wordpress-permalinks&quot;&gt;replacing Wordpress&lt;/a&gt; for my blogging needs.  Wordpress has been pretty good for the past three years, but it's time to move on, for a bunch of reasons.&lt;/p&gt;

&lt;p&gt;Primarily, the way Wordpress automatically mangles my text is annoying.  For example, it turns newlines into paragraphs inconsistently (especially when it comes to &lt;code&gt;pre&lt;/code&gt;/&lt;code&gt;code&lt;/code&gt; blocks).  This blog is mostly about programming, which means being able to post code without having my quotes turned into &quot;smart&quot; quotes and my &lt;code&gt;--flags&lt;/code&gt; turned into long-dashes is kind of important.  HTML is sometimes automatically escaped, and sometimes not.  I can't count how many comments I've gotten where someone posted some code, then posted again to inform me that Wordpress ate the code for dinner.  There are plugins to fix some of this, which break every time Wordpress releases a new version, and have never really worked that well for me.&lt;/p&gt;

&lt;p&gt;Writing a theme for Wordpress means a mix of PHP and HTML and CSS, which is painful to read and even more painful to write.  Aside from the considerable ugliness of PHP itself, there's a lot of weird magic involved with themes, based on naming conventions for files, weird fall-through behavior when certain theme files aren't present and so on.  The Wordpress API is enormous and not fun to work with if you want to do something other than the standard Wordpressy kind of blog structure.  Static pages aren't too much fun to work with in Wordpress either.&lt;/p&gt;

&lt;p&gt;Lately I think I was getting hammered with spam partly because Wordpress is such an easy target.  Askimet is nice but it wasn't catching enough lately; maybe 10-15 spams per week were slipping through.  And there was always the chance that some widely-known exploit in Wordpress was going to leave my site susceptible to some roving bot.&lt;/p&gt;

&lt;p&gt;And so on.&lt;/p&gt;

&lt;h1&gt;Hello Clojure&lt;/h1&gt;

&lt;p&gt;Why &lt;a href=&quot;http://clojure.org/&quot;&gt;Clojure&lt;/a&gt;?  Because it's awesome and fun and powerful and I wanted to learn it better.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/weavejester/compojure/tree/master&quot;&gt;Compojure&lt;/a&gt; is a web framework for Clojure that made a lot of this very easy.  Coming here from a Ruby on Rails background, Compojure has a lot going for it in comparison.  Compojure is lightweight and more low-level than Rails.  For example Compojure doesn't enforce MVC on you, doesn't force a unit testing framework on you, and doesn't care how you access your data.  Compojure just lets you route HTTP requests to Clojure functions based on the URL and request method (RESTfully: POST/GET/DELETE/PUT), and it gives you easy access to the request information, session, GET/POST parameters and cookies.&lt;/p&gt;

&lt;p&gt;Under the hood it's all servlets and &lt;a href=&quot;http://www.mortbay.org/jetty/&quot;&gt;Jetty&lt;/a&gt;, both of which are solid, stable, well-tested, well-documented technologies.  However, thankfully, all of that Java stuff &lt;em&gt;is&lt;/em&gt; under the hood, and well under it.  I didn't have to write a single line of Java or interact with single servlet directly.  Everything (session, params, headers) is a Clojure hash-map from the perspective of my code.&lt;/p&gt;

&lt;p&gt;Compojure also comes with a domain-specific language for writing HTML, which is similar to &lt;a href=&quot;http://www.weitz.de/cl-who/&quot;&gt;CL-WHO&lt;/a&gt; and myriad other Common Lisp HTML DSL's.  All of which are awesome.  I can't say enough how much nicer it is to write (or generate) structured s-exps than to write HTML by hand.  More on that below.&lt;/p&gt;

&lt;p&gt;Compojure doesn't come with any way to interact with a database, so I had to write one.  &lt;a href=&quot;http://code.google.com/p/clojure-contrib/&quot;&gt;clojure.contrib&lt;/a&gt; has an SQL lib which easily lets you interact with a MySQL database.  (Clojure can talk to MySQL via MySQL's JDBC connector, of course.)  I used &lt;code&gt;clojure.contrib.sql&lt;/code&gt; to write a small (192 lines) library which slurps up a bunch of database tables into Clojure refs, and provides a few functions for basic CRUD operations so that any updates to the ref data is also transparently reflected in the database.  The database is essentially only for keeping an on-disk cache of the data in case I need to restart the server.  The average number of DB queries per page is zero; everything except posting/editing/deleting data just reads out of a Clojure ref.&lt;/p&gt;

&lt;p&gt;With possibly multiple users posting data at once, it's nice to have Clojure's built-in concurrency support.  Updating the data refs with new data is always safe from multiple threads simply by throwing a &lt;code&gt;(dosync)&lt;/code&gt; around all of the write accesses.  This was completely painless to write.&lt;/p&gt;

&lt;p&gt;I decided I wanted to use &lt;a href=&quot;http://daringfireball.net/projects/markdown/&quot;&gt;Markdown&lt;/a&gt; for posting comments and authoring new pages.  This was also very simple to do; I outlined how to get Markdown working in Java and Clojure, in a &lt;a href=&quot;/blog/clojure-and-markdown-and-javascript-and-java-and&quot;&gt;previous post&lt;/a&gt;.  The real-time previews for comments are largely inspired by / ripped-off from &lt;a href=&quot;http://stackoverflow.com&quot;&gt;Stack Overflow&lt;/a&gt;, implemented mostly using open-source Javascript libraries like &lt;a href=&quot;http://attacklab.net/showdown/&quot;&gt;Showdown&lt;/a&gt;, &lt;a href=&quot;http://jquery.com/&quot;&gt;JQuery&lt;/a&gt;, &lt;a href=&quot;http://www.dennydotnet.com/post/2007/08/17/TypeWatch-jQuery-Plugin.aspx&quot;&gt;TypeWatch&lt;/a&gt; and &lt;a href=&quot;http://plugins.jquery.com/project/TextAreaResizer&quot;&gt;TextAreaResizer&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;A Brief Comparison: Clojure vs. Wordpress&lt;/h1&gt;

&lt;p&gt;All of my code including the CRUD library, all of the HTML for the templates and layout, admin controls, and all the glue to put it together is &lt;strong&gt;1,253&lt;/strong&gt; lines of code.  Wordpress is somewhere over &lt;strong&gt;78,000&lt;/strong&gt; lines of PHP depending what you count (doesn't include any themes or layout, but does include Wordpress features I didn't need and didn't implement).  It's still a pretty nice reduction in code overall, any way you look at it.&lt;/p&gt;

&lt;p&gt;As an example, in my old Wordpress site I had a plugin &lt;a href=&quot;http://zak.greant.com/catcloud&quot;&gt;catcloud&lt;/a&gt; to generate a &quot;tag cloud&quot;.  This plugin itself is 226 lines of PHP, not bad.  However, here's the Clojure code to generate a similar tag cloud (which you can see &lt;a href=&quot;/archives&quot;&gt;here&lt;/a&gt; currently):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defn tag-cloud []
  (let [tags (sort-by #(.toLowerCase (:name (first %))) (all-tags-with-counts))
        counts (map second tags)
        max-count (apply max counts)
        min-count (apply min counts)
        min-size 90.0
        max-size 200.0
        color-fn (fn [val]
                   (let [b (min (- 255 (Math/round (* val 255))) 200)]
                     (str &quot;rgb(&quot; b &quot;,&quot; b &quot;,&quot; b &quot;)&quot;)))
        tag-fn (fn [[tag c]]
                 (let [weight (/ (- (Math/log c) (Math/log min-count))
                                 (- (Math/log max-count) (Math/log min-count)))
                       size (+ min-size (Math/round (* weight
                                                       (- max-size min-size))))
                       color (color-fn (* weight 1.0))]
                   [:a {:href (:url tag)
                        :style (str &quot;font-size: &quot; size &quot;%;&quot; &quot;color:&quot; color)}
                    (:name tag)]))]
    (block nil
           [:h2 &quot;Tags&quot;]
           [:div.tag-cloud
            (apply html (interleave (map tag-fn tags)
                                    (repeat &quot; &quot;)))])))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is 10 times less code, which is a good reduction in my opinion.  Most of the code is the math to generate a weight logarithmically for each tag so they scale nicely.  &lt;code&gt;(all-tags-with-counts)&lt;/code&gt; fetches a seq of two-item pairs: the tags themselves (which are hash-maps) and a count of posts for each tag.  There are two locally-defined functions in the &lt;code&gt;let&lt;/code&gt; which generate the text color and the font size and HTML for each tag.&lt;/p&gt;

&lt;p&gt;The vectors that look like &lt;code&gt;[:h2 &quot;Tags&quot;]&lt;/code&gt; are input for Compojure's HTML-generating DSL; this would be transformed for example into &lt;code&gt;&amp;lt;h2&amp;gt;Tags&amp;lt;/h2&amp;gt;&lt;/code&gt;.  &lt;code&gt;(block ...)&lt;/code&gt; is a macro which wraps its content in HTML for the rounded borders of my layout.  &lt;code&gt;(Math/log ...)&lt;/code&gt; and friends are calls to standard Java math functions.&lt;/p&gt;

&lt;p&gt;This &lt;em&gt;whole function&lt;/em&gt; is less code than just the horrible boilerplate array declarations at the top of the Wordpress plugin:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$catcloud_field_data = array(
  array('name' =&amp;gt; 'Minimum Font Size', 'option' =&amp;gt; 'catcloud_min_font_size', 'size' =&amp;gt; '4', 'maxlength' =&amp;gt; '3',
       'default' =&amp;gt; '9', 'note' =&amp;gt; 'Used for the least frequent categories', 'validation' =&amp;gt; '/^\d{1,3}(\.\d{1,3})?$/'),
  array('name' =&amp;gt; 'Maximum Font Size', 'option' =&amp;gt; 'catcloud_max_font_size', 'size' =&amp;gt; '4', 'maxlength' =&amp;gt; '3',
       'default' =&amp;gt; '18', 'note' =&amp;gt; 'Used for the most frequent categories', 'validation' =&amp;gt; '/^\d{1,3}(\.\d{1,3})?$/'),
  array('name' =&amp;gt; 'Font Face', 'option' =&amp;gt; 'catcloud_font_face', 'size' =&amp;gt; '15', 'maxlength' =&amp;gt; '254',
       'default' =&amp;gt; '', 'note' =&amp;gt; 'Set an optional list of font faces', 'validation' =&amp;gt; '/.*/'),
  array('name' =&amp;gt; 'Font Units', 'option' =&amp;gt; 'catcloud_font_units', 'size' =&amp;gt; '3', 'maxlength' =&amp;gt; '2',
       'default' =&amp;gt; 'pt', 'note' =&amp;gt; 'Choose one of em, pt, px or %', 'validation' =&amp;gt; '/^(%|em|pt|px)$/'),
  array('name' =&amp;gt; 'Color Start', 'option' =&amp;gt; 'catcloud_color_start', 'size' =&amp;gt; '7', 'maxlength' =&amp;gt; '6',
       'default' =&amp;gt; '0066CC', 'note' =&amp;gt; 'For the least frequent categories. Use a hexadecimal RGB triplet. ie. 0066CC',
       'validation' =&amp;gt; '/^[\dA-F]{6}$/i'),
  array('name' =&amp;gt; 'Color End', 'option' =&amp;gt; 'catcloud_color_end', 'size' =&amp;gt; '7', 'maxlength' =&amp;gt; '6',
       'default' =&amp;gt; 'CC6600', 'note' =&amp;gt; 'For the most frequent categories. Use a hexadecimal RGB triplet. ie. CC6600',
       'validation' =&amp;gt; '/^[\dA-F]{6}$/i'),
  array('name' =&amp;gt; 'Before Category', 'option' =&amp;gt; 'catcloud_before', 'size' =&amp;gt; '3', 'maxlength' =&amp;gt; '20',
       'default' =&amp;gt; '[', 'note' =&amp;gt; 'Set the character(s) to display before category names', 'validation' =&amp;gt; '/.*/'),
  array('name' =&amp;gt; 'After Category', 'option' =&amp;gt; 'catcloud_after', 'size' =&amp;gt; '3', 'maxlength' =&amp;gt; '20',
       'default' =&amp;gt; ']', 'note' =&amp;gt; 'Set the character(s) to display after category names', 'validation' =&amp;gt; '/.*/'),
  array('name' =&amp;gt; 'Show Top N Categories', 'option' =&amp;gt; 'catcloud_top_n_cats', 'size' =&amp;gt; '5', 'maxlength' =&amp;gt; '3',
       'default' =&amp;gt; '', 'note' =&amp;gt; 'Show only the top N categories (where N is a number like 10 or 25 or whatever. Set to 0 or empty for no limit.',
       'validation' =&amp;gt; '/^\d*$/'),
  array('name' =&amp;gt; 'Excluded Categories', 'option' =&amp;gt; 'catcloud_excluded_cats', 'size' =&amp;gt; '15', 'maxlength' =&amp;gt; '254',
       'default' =&amp;gt; '', 'note' =&amp;gt; 'A comma-separated list of category ids.',
       'validation' =&amp;gt; '/^[\d, ]*$/'),
)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ugh.  As another example, here's the code that handles a POST request to add a new blog page:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defn do-new-post []
  (check-login
   (let [post (add-post *params*)]
     (sync-tags post (:all-tags *params*))
     (redirect-to &quot;/&quot;))))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It does exactly what it says: Check to make sure the user is logged in, add the post based on the POST params, sync up the tags for that post and redirect to the front page.  Lisp lets you say what you want very concisely, with a bare minimum of boilerplate.&lt;/p&gt;

&lt;p&gt;How about speed?  My Clojure code is actually generating HTML in the most brute-force and wasteful way possible.  The HTML for each page is regenerated from scratch, via a cascade of a couple dozen function and macro calls, every time you load a page.  But it's still pretty fast, a couple hundred milliseconds for most page requests.  This is slightly faster than the Wordpress version of my site.  If I ever have performance issues I can switch to another Clojure HTML library, like &lt;a href=&quot;http://github.com/mmcgrana/clj-html/tree/master&quot;&gt;clj-html&lt;/a&gt; which uses the same vector-style syntax but pre-compiles the HTML.&lt;/p&gt;

&lt;p&gt;How hard was it to set up on the server?  Wordpress is pretty famous for being dirt-easy to deploy anywhere.  My Clojure app by comparison was slightly more difficult, as you might expect, but it wasn't brain surgery.  My server runs Debian.  First I installed the JVM via &lt;code&gt;apt&lt;/code&gt;, then I &lt;code&gt;rsync&lt;/code&gt;ed a bunch of jar's and clj files to the server, then I installed &lt;code&gt;emacs&lt;/code&gt; and &lt;code&gt;screen&lt;/code&gt; also via &lt;code&gt;apt&lt;/code&gt;.  Then I put two lines into an Apache config file to proxy-forward traffic to a local port where &lt;code&gt;jetty&lt;/code&gt; would be listening.  I started Emacs, did &lt;code&gt;(require 'bcc.blog.server)&lt;/code&gt;, did &lt;code&gt;(bcc.blog.server/go)&lt;/code&gt; to start everything, and that's about it.  Took about 15 minutes to set up from scratch.  When I find a bug, I SSH in, re-attach to &lt;code&gt;screen&lt;/code&gt;, fix it in Emacs, hit &lt;code&gt;C-c C-c&lt;/code&gt; to recompile just the functions I need to update, and then detach from &lt;code&gt;screen&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;I'm pretty pleased with this so far.    It was fun to write and has all the features I used from Wordpress, plus more, and the building blocks are there to extend things if I imagine up a new feature I like.  &lt;/p&gt;

&lt;p&gt;Looks like my blog is still running today &lt;a href=&quot;/blog/new-blog-i-think&quot;&gt;in spite of my predictions&lt;/a&gt;.  Still waiting for the JVM to crash though, I know it's coming.  I plan to post the source code for some of this once I'm sure it works.&lt;/p&gt;</description></item><item><title>New Blog... I think...</title><link>http://briancarper.net/blog/new-blog-i-think</link><guid>http://briancarper.net/blog/new-blog-i-think</guid><pubDate>Mon, 16 Mar 2009 00:35:04 -0700</pubDate><description>&lt;p&gt;OK, here's the new blog.  Apologies to anyone who may be following my RSS feed, because the whole feed is probably going to be reset by switching blog engines.&lt;/p&gt;

&lt;p&gt;If you can call this an &quot;engine&quot;.  This is my Clojure rewrite.  I'll have much more to write about this tomorrow when I'm awake.  In the meantime, bug reports are welcome.&lt;/p&gt;

&lt;p&gt;Here are my estimates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;52% chance the blog is crashed and down by the time I wake up tomorrow.&lt;/li&gt;
&lt;li&gt;27% chance my feeble anti-spam measures are easily defeated, and hundreds of spam comments are waiting for me in the morning.&lt;/li&gt;
&lt;li&gt;14% chance the JVM brings down the whole server.&lt;/li&gt;
&lt;li&gt;7% chance everything works swimmingly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I had to take down my origami gallery site just to get this to run.  Fun times ahead.&lt;/p&gt;

&lt;p&gt;When I came up with this blog layout I thought it was great, but three weeks of looking at it and now I'm starting to hate it.  I can work on making it all pretty later though.&lt;/p&gt;

&lt;p&gt;Ah well, more tomorrow.  Keeping my fingers crossed.&lt;/p&gt;</description></item><item><title>Blog replacement fun</title><link>http://briancarper.net/blog/blog-replacement-fun</link><guid>http://briancarper.net/blog/blog-replacement-fun</guid><pubDate>Thu, 15 Jan 2009 21:55:11 -0800</pubDate><description>&lt;p&gt;So I'm still thinking how I want to replace this blog.  I still plan to write something from scratch, for fun's sake.&lt;/p&gt;

&lt;p&gt;One thing I'm sure of is that I don't want to write HTML by hand, at all, under any circumstances.  HTML and XML are not human-writable or human-readable languages.  They rely too much on things human beings suck at, namely consistency and repetition.  Forget a closing tag?  Typo a tag name?  Now your document is malformed.  Undefined behavior, at best.  It's too verbose, it has too much needless punctuation.&lt;/p&gt;

&lt;p&gt;It's also too hard to manipulate it or do anything with it after you write it.  There's XPath, which is itself a mess to work with, manipulating huge strings of crap via slightly smaller strings with its own funky syntax quirks.  I've never found an XML-parsing library with an interface that I liked, and I've had to use them extensively in Perl, Ruby and Python.&lt;/p&gt;

&lt;p&gt;So first thing, I'm going to convert all posts and comments into &lt;a href=&quot;http://daringfireball.net/projects/markdown/&quot;&gt;Markdown&lt;/a&gt; and use that for future posting and commenting.  I like Markdown.  It's hard to get wrong typing it by hand and it doesn't get in your way.   It also doesn't tie you to one implementation; you can turn Markdown into HTML client-side via Javascript or easily parse it server-side.  Or you can display it as-is and it's still readable.  It's a very nice idea.&lt;/p&gt;

&lt;p&gt;Second thing, I plan to use my programming language to write the HTML for the skeleton of my site for me.  Opening, closing, and properly nesting tags is something a machine should do for me.  Making sure my tags belong to a well-defined list of allowed valid HTML tags is something a machine should check for me.&lt;/p&gt;

&lt;p&gt;More than likely I'm going to write this in Clojure, because s-expressions (and better yet, a combination of Clojure literal lists, maps and arrays) makes writing HTML very easy and foolproof.  I've also written an HTML-producing DSL in Ruby in the past though; it's not hard to do in any language.&lt;/p&gt;

&lt;p&gt;Another thing I'm sure of is that I need a good anti-spam system but that I have no idea what that system should be.  Askimet in Wordpress has caught 50,000(!) spam comments since I started my blog.  Some spam still sneaks through on me now and then.  I've never used a CAPTCHA and don't plan to; they just don't work.  I'm probably going to come up with some funky custom anti-spam measures (which are invisible to users) and rely on the fact that no one is going to take the time to break it.  My site isn't a huge or popular target, so here's hoping.&lt;/p&gt;

&lt;p&gt;A third complication I'm dreading is how to do this without breaking every link anyone ever made to my site.  Wordpress's permalink system is OK, but I'd like to change it.  Problem is I can't change it; every link to my site from another site is a dependency.  So I might have to mod_rewrite redirect the old URLs to new ones, or use two permalink schemes simultaneously.  I don't know.  &lt;/p&gt;

&lt;p&gt;Fun times ahead.  How to design a blog is a problem lots of people have solved but no one has really solved perfectly, or else there wouldn't be so many frameworks and packages to do it.  The good thing about writing your own from scratch is that it'll work exactly how you want.  Wordpress is close but not close enough.&lt;/p&gt;</description></item><item><title>Heh</title><link>http://briancarper.net/blog/heh</link><guid>http://briancarper.net/blog/heh</guid><pubDate>Fri, 04 Apr 2008 16:07:14 -0700</pubDate><description>&lt;p&gt;I always know when something from my blog has been &lt;a href=&quot;http://reddit.com/info/6ef4g/comments/&quot;&gt;posted to Reddit&lt;/a&gt;, because I wake up and I have dozens more comments than I'd ever get otherwise, 90% of which are calling me an idiot.  &lt;/p&gt;

&lt;p&gt;I get a kick out of it though.  People so take everything so seriously, and can't recognize hyperbole when they see it.  It's amazing how quickly a straw man can be constructed and demolished in the course of a reader's short comment.  In all seriousness, people would do well to remember that you generally infer can't infer much of anything about a person's character or intelligence or beliefs by reading one blog post they made on their cow-website.  &lt;/p&gt;

&lt;p&gt;Easiest way to increase traffic to your blog: Write up a post insulting something, e.g. a programming language or text editor.  Use deliberately weak arguments, but not TOO weak.  Float enough truth in it to make it plausible.  The internet justice avengers will descend upon your blog like rabid wolves, because no one can resist pointing out when someone else is &lt;a href=&quot;http://xkcd.com/386/&quot;&gt;wrong on the internet&lt;/a&gt;.  I don't write posts like that (not on purpose anyways) but the temptation is always there. &lt;/p&gt;</description></item><item><title>Comment subscription</title><link>http://briancarper.net/blog/comment-subscription</link><guid>http://briancarper.net/blog/comment-subscription</guid><pubDate>Wed, 12 Sep 2007 13:43:35 -0700</pubDate><description>&lt;p&gt;I added the option to subscribe to the comments for any post in my blog (to receive an email when new comments are posted).  You don't have to post a comment yourself to subscribe.  This was accomplished via &lt;a href=&quot;http://txfx.net/code/wordpress/subscribe-to-comments/&quot;&gt;this Wordpress plugin&lt;/a&gt;.&lt;/p&gt;</description></item></channel></rss>

