<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-23529072678094516</id><updated>2024-09-22T20:38:51.747-04:00</updated><category term="projects"/><category term="ruby"/><category term="clojure"/><category term="style"/><category term="rails"/><category term="metaprogramming"/><category term="tdd"/><category term="agile"/><category term="appengine"/><category term="gotchas"/><category term="bdd"/><category term="cloud"/><category term="about"/><category term="actionscript"/><category term="dotnet"/><category term="git"/><category term="heroku"/><category term="iphone"/><category term="java"/><category term="textmate"/><category term="tools"/><title type='text'>El Humidor</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>50</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-5166745477224592046</id><published>2012-11-27T14:56:00.000-05:00</published><updated>2012-11-27T14:56:32.907-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="clojure"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><category scheme="http://www.blogger.com/atom/ns#" term="style"/><title type='text'>Why Not To Use My Library clj-record</title><content type='html'>&lt;p&gt;In 2008 I discovered &lt;a href=&quot;http://www.clojure.org/&quot;&gt;Clojure&lt;/a&gt;, and I was extremely excited to finally have an easy
entry-way to Lisp. Pretty early on I wanted to get my head around one of
Lisp&#39;s killer features: macros. A little playing around in the REPL was easy
but not satisfying: I wanted to build something powerful.&lt;/p&gt;

&lt;p&gt;Having spent a few years doing &lt;a href=&quot;http://www.rubyonrails.org/&quot;&gt;Ruby on Rails&lt;/a&gt;, I thought ActiveRecord&#39;s
macro-like capabilities (&lt;code&gt;has_many&lt;/code&gt; and whatnot) would be a good goal. So I
built &lt;a href=&quot;https://github.com/duelinmarkers/clj-record&quot;&gt;clj-record&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In retrospect, I went too far trying to build in Rails-like magic, so I want
to point out the design flaws that I recommend you avoid in your own work and
recommend that you choose something other than clj-record for your Clojure
projects that use an RDBMS.&lt;/p&gt;

&lt;p&gt;The API of clj-record is all in one macro: &lt;code&gt;clj-record.core/init-model&lt;/code&gt;.
It commits a few macro sins.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It looks at the namespace from which you call it to find the name of your model.&lt;/li&gt;
&lt;li&gt;It intentionally captures a &lt;code&gt;db&lt;/code&gt; var from your namespace.&lt;/li&gt;
&lt;li&gt;It defs a bunch of fns in your namespace (most of which are just partially
applied versions of &lt;code&gt;clj-record.core&lt;/code&gt; fns).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So you do this:&lt;/p&gt;

&lt;script src=&quot;//gist.github.com/4156536.js?file=init_model_usage.clj&quot;&gt;&lt;/script&gt;

&lt;noscript&gt;&lt;code&gt;&lt;pre&gt;
(ns my.model.widget
  (:require clj-record.boot
            [my.model.config :refer (db)]))

(clj-record.core/init-model
  (:associations
    (has-many sizes))
  (:validation
    (:name &quot;Name is required&quot; #(not (empty? %)))))
&lt;/pre&gt;&lt;/code&gt;&lt;/noscript&gt;

&lt;p&gt;and that turns into something like this (leaving out the &lt;code&gt;ns&lt;/code&gt;):&lt;/p&gt;

&lt;script src=&quot;//gist.github.com/4156536.js?file=init_model_expansion.clj&quot;&gt;&lt;/script&gt;

&lt;noscript&gt;&lt;code&gt;&lt;pre&gt;
; model configuration
(clj-record.meta/init-model-metadata &quot;user&quot;)
(clj-record.core/set-db-spec &quot;user&quot; db)
(clj-record.core/set-table-name &quot;user&quot; &quot;users&quot;)
; expose model metadata
(def model-name &quot;user&quot;)
(def table-name (clj-record.core/table-name &quot;user&quot;))
(defn model-metadata [&amp; args]
  (apply clj-record.meta/model-metadata-for &quot;user&quot; args))
(defn table-name [] (clj-record.core/table-name &quot;user&quot;))
; data manipulation
(defn record-count
  ([] (clj-record.core/record-count &quot;user&quot;))
  ([attributes] (clj-record.core/record-count &quot;user&quot; attributes)))
(defn get-record [id]
  (clj-record.core/get-record &quot;user&quot; id))
(defn all-records []
  (clj-record.core/all-records &quot;user&quot;))
(defn find-records [attributes]
  (clj-record.core/find-records &quot;user&quot; attributes))
; ... and so on.
&lt;/pre&gt;&lt;/code&gt;&lt;/noscript&gt;

&lt;p&gt;This provided really good practice (and an example I&#39;ve gone back to more than
once) for quoting, syntax quoting, unquoting, and unquote-splicing.&lt;/p&gt;

&lt;p&gt;But this sucks!&lt;/p&gt;

&lt;p&gt;What init-model should do is return a data-structure that
you then pass to clj-record&#39;s find, insert, and update fns.
In addition to being simpler to understand than the hidden registry of model
metadata that clj-record has to maintain, it would make it more natural to
write generic, composable code, since the model itself would be a value.&lt;/p&gt;

&lt;p&gt;If that were all init-model needed to do, the code (both inside clj-record and
user code) would probably be even simpler if there were no macro at all. A set
of functions could enhance the model data-structure as needed so that even
model setup code could be composed like any other series of fn-calls.&lt;/p&gt;

&lt;p&gt;Maybe it would be worthwhile having a macro that went something like this:&lt;/p&gt;

&lt;script src=&quot;//gist.github.com/4156536.js?file=defmodel_macro.clj&quot;&gt;&lt;/script&gt;

&lt;noscript&gt;&lt;code&gt;&lt;pre&gt;
(defmodel widget db
  (associations
    (:has-many :sizes))
  (validation
    (:name &quot;Name is required&quot; #(not (empty? %)))))
&lt;/pre&gt;&lt;/code&gt;&lt;/noscript&gt;

&lt;p&gt;This macro would do a &lt;code&gt;def&lt;/code&gt;, but it&#39;s completely intuitive what that def is.
The third &quot;sin&quot; on my list above was about defining a bunch of fns in your
namespace. I call this a sin because it&#39;s not clear from reading the code
that there are a bunch of vars defined, making both discovery and debugging
more difficult.
There may be cases where &quot;hidden defs&quot; are justified by performance gains or
code reduction, but the fns defined by init-model don&#39;t pass that test
(because &lt;code&gt;(clj-record.core/find-records widget attributes)&lt;/code&gt; is no worse and
more easily composed with other generic model-processing fns than
&lt;code&gt;(my.model.widget/find-records attributes)&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;If no one else were writing libraries, I&#39;d try to remedy this in the next
major revision of clj-record, but someone else has done this work (or
something very much like it) already.
&lt;a href=&quot;http://chris-granger.com/&quot;&gt;Chris Granger&lt;/a&gt; wrote &lt;a href=&quot;http://sqlkorma.com/&quot;&gt;korma&lt;/a&gt;, and it offers similar functionality in a saner way.
I&#39;m also not using Clojure with a SQL database, so I wouldn&#39;t be able to
&lt;a href=&quot;http://c2.com/cgi/wiki?DogFood&quot;&gt;eat my own dogfood&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you&#39;re modeling entities in a relational database, I&#39;d recommend
korma over clj-record. There may be even better options for you
(including just using &lt;a href=&quot;https://github.com/clojure/java.jdbc&quot;&gt;&lt;code&gt;clojure.java.jdbc&lt;/code&gt;&lt;/a&gt; directly), so look around.&lt;/p&gt;

&lt;p&gt;If you&#39;re writing macros, don&#39;t capture vars, don&#39;t make their functionality
depend on globals like &lt;code&gt;*ns*&lt;/code&gt;, and consider alternatives to creating vars
in the caller&#39;s namespace. While you&#39;re at it, consider whether the job would
be better done with plain old functions.&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/5166745477224592046/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/5166745477224592046' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/5166745477224592046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/5166745477224592046'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2012/11/why-not-to-use-my-library-clj-record.html' title='Why Not To Use My Library clj-record'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-730658286023948875</id><published>2012-11-14T18:33:00.001-05:00</published><updated>2012-11-15T09:48:41.046-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="clojure"/><category scheme="http://www.blogger.com/atom/ns#" term="style"/><title type='text'>Using Aleph&#39;s Asynchronous HTTP Client for OAuth</title><content type='html'>&lt;p&gt;
  If you&#39;re implementing authentication in your  web app
  on the server-side using some third party&#39;s OAuth,
  you ideally want to dedicate minimal system resources to waiting for HTTP responses.
  If you&#39;re working in &lt;a href=&quot;http://clojure.org/&quot;&gt;Clojure&lt;/a&gt;, you can do this using
  &lt;a href=&quot;https://github.com/ztellman/aleph&quot; title=&quot;Aleph on GitHub&quot;&gt;Aleph&lt;/a&gt;&#39;s asynchronous
  &lt;a href=&quot;http://ztellman.github.com/aleph/aleph.http-api.html#aleph.http/http-client&quot; title=&quot;API docs for aleph.http/htt-client&quot;&gt;http client&lt;/a&gt;.
  Here I&#39;ll walk through using it to do
  &lt;a href=&quot;http://developers.facebook.com/docs/howtos/login/server-side-login/&quot; title=&quot;Facebook Login for Server-side Apps&quot;
    &gt;server-side authentication with Facebook&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
  [There&#39;s nothing auth-specific about this. Any integration with HTTP end points might benefit from similar treatment.]
&lt;/p&gt;
&lt;p&gt;
  Here&#39;s what our application needs to do:
  &lt;ol&gt;
    &lt;li&gt;
      Send the user to Facebook&#39;s OAuth &quot;dialog,&quot; passing along a &lt;code&gt;redirect_uri&lt;/code&gt; parameter.
      On the dialog they&#39;ll see what permissions your app wants and click a button to say they want to log in.
      Facebook will redirect them back to your &lt;code&gt;redirect_uri&lt;/code&gt;
      with a &lt;code&gt;code&lt;/code&gt; parameter in the query string.
    &lt;/li&gt;
    &lt;li&gt;
      Make a server-side request for an access-token for the user,
      passing along the &lt;code&gt;code&lt;/code&gt; parameter and your own &lt;code&gt;redirect_uri&lt;/code&gt;
    &lt;/li&gt;
    &lt;li&gt;
      Make a second server-side request for whatever information the app needs (e.g., the user&#39;s name and email address),
      passing along the access-token.
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/p&gt;
&lt;p&gt;
  We can&#39;t do anything for the user between steps two and three, so we&#39;ll perform them one after another.
  It&#39;s easy to picture what that might look like if we aren&#39;t worried about tying up a thread waiting for responses.
&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4075541.js?file=synchronous_version.clj&quot;&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;
(defn fb-user-details-from-code [code redirect-uri]
  (get-fb-user-with-token
    (get-fb-access-token code redirect-uri)))
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;

&lt;p&gt;
  Ideally we&#39;d like an asynchronous implementation to read just as clearly.
&lt;/p&gt;
&lt;p&gt;
  We&#39;ll start with the easiest implementation to understand, which uses &lt;code&gt;on-realized&lt;/code&gt; to register
  success and error callbacks on each HTTP request. The fn now takes success and error callbacks.
  In an aleph web application, those would each enqueue a ring-style response map onto the response channel.
&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4075541.js?file=using_on_realized.clj&quot;&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;
(defn fb-user-details-from-code [code redirect-uri success-callback error-callback]
  (on-realized
    (http-request {:url (access-token-url code auth-redirect-uri)})
    #(on-realized
       (http-request {:url (fb-user-url (read-access-token-from-response %))})
       #(success-callback (read-user-from-response %))
       error-callback)
    error-callback))
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;

&lt;p&gt;
  Unfortunately this reads terribly due to the nested callbacks.
&lt;/p&gt;
&lt;p&gt;
  We can use lamina&#39;s &lt;code&gt;run-pipeline&lt;/code&gt; macro to create a cleaner version of the same thing.
&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4075541.js?file=one_big_pipeline.clj&quot;&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;
(defn fb-user-details-from-code [code redirect-uri success-callback error-callback]
  (run-pipeline (access-token-url code auth-redirect-uri)
    {:error-handler error-callback}
    #(http-request {:url %})
    read-access-token-from-response
    fb-user-url
    #(http-request {:url %})
    read-user-from-response
    success-callback))
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;

&lt;p&gt;
  At this point, however, we realize that we&#39;re missing something. In addition to the user data we get from the second
  HTTP request, we want the &quot;fb-user&quot; passed to the success callback to have the access-token and access-token-expiration
  we got in response to the first HTTP request.
&lt;/p&gt;
&lt;p&gt;
  We can achieve that by breaking the pipeline into two and holding onto the result of the first
  so that we can refer to it again later (in the call to &lt;code&gt;combine-results&lt;/code&gt;.
&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4075541.js?file=broken_into_two_pipelines.clj&quot;&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;
(defn fb-user-details-from-code [code redirect-uri success-callback error-callback]
  (let [access-token-result (run-pipeline (access-token-url code redirect-uri)
                              {:error-handler (fn [_])}
                              #(http-request {:url %})
                              read-access-token-from-response)]
    (run-pipeline access-token-result
      {:error-handler error-callback}
      fb-user-url
      #(http-request {:url %})
      read-user-from-response
      #(combine-results % @access-token-result)
      #(do
         (success-callback %)
         (lamina.connections/close-connection client)))))
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;

&lt;p&gt;
  Note the deref (&quot;&lt;code&gt;@&lt;/code&gt;&quot;) in that second reference to &lt;code&gt;access-token-result&lt;/code&gt;.
  It&#39;s needed because the pipeline returns a result-channel which may not yet be realized.
  We don&#39;t have to worry about the deref blocking, since the value is guaranteed to be realized
  before the second pipeline will proceed past the initial value, but it &lt;em&gt;looks&lt;/em&gt; like
  something that might block. The whole thing&#39;s also a bit sloppy.
  Zack Tellman pointed out that it&#39;s a bit cleaner to split them like so.
&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4075541.js?file=nesting_pipelines.clj&quot;&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;
(defn fb-user-details-from-code [code redirect-uri success-callback error-callback]
  (run-pipeline (access-token-url code redirect-uri)
    {:error-handler error-callback}
    #(http-request {:url %})
    read-access-token-from-response
    (fn [access-token]
      (run-pipeline access-token
        {:error-handler (fn [_])}
        fb-user-url
        #(http-request {:url %})
        read-user-from-response
        #(combine-results % access-token)
        success-callback))))
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;

&lt;p&gt;
  The deref is no longer needed because the nested fn will be called with the realized value once it&#39;s ready.
&lt;/p&gt;
&lt;p&gt;
  If we extract the details so that we have the same level of abstraction we had in the initial
  (synchronous) version, we end up with this.
&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4075541.js?file=abstracting_the_details.clj&quot;&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;
(defn get-fb-access-token [code redirect-uri]
  (run-pipeline (access-token-url code redirect-uri)
    {:error-handler (fn [_])}
    #(http-request {:url %})
    read-access-token-from-response))

(defn get-fb-user-with-token [access-token]
  (run-pipeline access-token
    {:error-handler (fn [_])}
    fb-user-url
    #(http-request {:url %})
    read-user-from-response
    #(combine-results % access-token)))

(defn fb-user-details-from-code [code redirect-uri success-callback error-callback]
  (run-pipeline (get-fb-access-token code redirect-uri)
    {:error-handler error-callback}
    get-fb-user-with-token
    success-callback))
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;

&lt;p&gt;
  Comparing that last fn to the synchronous version, we come out looking pretty good here!
  The only thing I find a little awkward is that the options to &lt;code&gt;run-pipeline&lt;/code&gt; are
  the second argument. It breaks up the flow when the first &quot;value&quot; is just a call to another
  pipeline.
&lt;/p&gt;

&lt;h3&gt;Assorted Thoughts&lt;/h3&gt;

&lt;h4&gt;Reusing the connection&lt;/h4&gt;
&lt;p&gt;
  Aleph&#39;s &lt;code&gt;http-client&lt;/code&gt; fn lets you reuse one connection for multiple requests to the same host.
  It mucks things up a bit, since you want to be sure the connection gets closed at the end.
&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4075541.js?file=sharing_the_connection.clj&quot;&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;
(defn fb-user-details-from-code [code redirect-uri success-callback error-callback]
  (let [client (http-client {:url &quot;https://graph.facebook.com&quot;})]
    (run-pipeline (get-fb-access-token client code redirect-uri)
      {:error-handler (fn [throwable]
                        (try (error-callback throwable)
                          (finally (lamina.connections/close-connection client))))}
      #(get-fb-user-with-token client %)
      #(do
         (try (success-callback %)
           (finally (lamina.connections/close-connection client)))))))
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;

&lt;p&gt;
  Not being able to just wrap a &lt;code&gt;(try ... (finally ...))&lt;/code&gt; around the whole thing is a bummer,
  but it&#39;s still not awful.
  Zach has said &lt;a href=&quot;https://groups.google.com/d/topic/aleph-lib/WDQ9vza-6Dw/discussion&quot;
    &gt;there will likely soon be a cleaner way to do &quot;finally&quot; in pipelines&lt;/a&gt;.
&lt;/p&gt;

&lt;h4&gt;The :error-handler&lt;/h4&gt;

&lt;p&gt;
  The empty &lt;code&gt;:error-handler&lt;/code&gt; fns on the &quot;get-fb-&quot; fns suppress &quot;unhandled exception&quot;
  logging from lamina. Since both of those pipelines are run as part of the outer pipeline, when either
  of them errors, the error-handler from the outer pipeline will be run, so that&#39;s the only place we
  need to use the error-callback.
  In my real app, those inner error-handlers just log,
  but you could just as easily leave the options out completely if you&#39;re ok with lamina&#39;s logging.
&lt;/p&gt;

&lt;h4&gt;The rest&lt;/h4&gt;
&lt;p&gt;
  I&#39;ve left out many details, like the FB-specific URL-templates and response parsing.
  If there&#39;s interest, I&#39;m happy to share those.
  I just thought the lamina/aleph stuff was the interesting part.
&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/730658286023948875/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/730658286023948875' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/730658286023948875'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/730658286023948875'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2012/11/using-alephs-asynchronous-http-client.html' title='Using Aleph&#39;s Asynchronous HTTP Client for OAuth'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-8973659247576244135</id><published>2012-11-05T17:46:00.000-05:00</published><updated>2012-11-05T17:47:06.154-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="clojure"/><category scheme="http://www.blogger.com/atom/ns#" term="heroku"/><title type='text'>How to Handle File Uploads from Clojure on Heroku (or anywhere else)</title><content type='html'>&lt;p&gt;
  I recently wanted to deploy a &lt;a href=&quot;http://clojure.org/&quot;&gt;Clojure&lt;/a&gt; web app that allowed users to upload a CSV and work with the data.
  &lt;a href=&quot;http://www.heroku.com/&quot;&gt;Heroku&lt;/a&gt; is as easy as it gets for web app deployment,
  but I figured I&#39;d have to jump through some extra hoops to do the file upload.
  It turns out there are no hoops.
  Thanks to &lt;a href=&quot;https://devcenter.heroku.com/articles/dynos#ephemeral-filesystem&quot;&gt;Heroku&#39;s ephemeral filesystem&lt;/a&gt;,
  we can handle file uploads just as we would on any other system,
  so long as we either process the file immediately or send it &lt;a href=&quot;https://devcenter.heroku.com/articles/s3&quot;&gt;someplace else&lt;/a&gt; to be stored.
&lt;/p&gt;
&lt;p&gt;
  Here&#39;s how to handle a file upload in a &lt;strong&gt;very&lt;/strong&gt; simple ring app.
&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4020886.js?file=app.clj&quot;&gt;&lt;/script&gt;

&lt;p&gt;
  The &#39;render-&#39; fns are left to you!
&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/8973659247576244135/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/8973659247576244135' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/8973659247576244135'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/8973659247576244135'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2012/11/how-to-handle-file-uploads-from-clojure.html' title='How to Handle File Uploads from Clojure on Heroku (or anywhere else)'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-3740351589651631328</id><published>2011-04-27T01:08:00.001-04:00</published><updated>2011-04-27T01:10:16.993-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="dotnet"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><title type='text'>FluentTime: a fluent interface in C# for working with dates and times</title><content type='html'>&lt;p&gt;
 After working fairly late last night I was too wound up to go to sleep when I got home. 
 Something possessed me to start creating a little 
 &lt;a href=&quot;http://en.wikipedia.org/wiki/C_Sharp_(programming_language)&quot;&gt;C#&lt;/a&gt; library 
 providing a fluent interface for working with 
 &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/system.datetime.aspx&quot;&gt;DateTime&lt;/a&gt;s, 
 &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/system.datetimeoffset.aspx&quot;&gt;DateTimeOffset&lt;/a&gt;s, 
 and &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/system.timespan.aspx&quot;&gt;TimeSpan&lt;/a&gt;s 
 inspired by ActiveSupport&#39;s 
 &lt;a href=&quot;https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/numeric/time.rb&quot; 
 &gt;active_support/core_ext/numeric/time&lt;/a&gt; and related extensions.
&lt;/p&gt;
&lt;p&gt;
 After about &lt;code&gt;3.Hours(30.Minutes())&lt;/code&gt; the straightforward part had come together.
 You can do stuff like this with it:
&lt;/p&gt;
&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_c&quot;&gt;&lt;span class=&quot;meta meta_block meta_block_c&quot;&gt;var toothBrushingTime = &lt;span class=&quot;constant constant_numeric constant_numeric_c&quot;&gt;10.&lt;/span&gt;&lt;span class=&quot;meta meta_function-call meta_function-call_c&quot;&gt;&lt;span class=&quot;support support_function support_function_any-method support_function_any-method_c&quot;&gt;Minutes&lt;/span&gt;(&lt;/span&gt;).&lt;span class=&quot;meta meta_function-call meta_function-call_c&quot;&gt;&lt;span class=&quot;support support_function support_function_any-method support_function_any-method_c&quot;&gt;Before&lt;/span&gt;(&lt;/span&gt;bedTime);
var lightsOutForReal = &lt;span class=&quot;constant constant_numeric constant_numeric_c&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;meta meta_function-call meta_function-call_c&quot;&gt;&lt;span class=&quot;support support_function support_function_any-method support_function_any-method_c&quot;&gt;Hour&lt;/span&gt;(&lt;/span&gt;&lt;span class=&quot;constant constant_numeric constant_numeric_c&quot;&gt;30.&lt;/span&gt;&lt;span class=&quot;meta meta_function-call meta_function-call_c&quot;&gt;&lt;span class=&quot;support support_function support_function_any-method support_function_any-method_c&quot;&gt;Minutes&lt;/span&gt;(&lt;/span&gt;)).&lt;span class=&quot;meta meta_function-call meta_function-call_c&quot;&gt;&lt;span class=&quot;support support_function support_function_any-method support_function_any-method_c&quot;&gt;After&lt;/span&gt;(&lt;/span&gt;bedTime);
...
Assert.&lt;span class=&quot;meta meta_function-call meta_function-call_c&quot;&gt;&lt;span class=&quot;support support_function support_function_any-method support_function_any-method_c&quot;&gt;That&lt;/span&gt;(&lt;/span&gt;actual, Is.&lt;span class=&quot;meta meta_function-call meta_function-call_c&quot;&gt;&lt;span class=&quot;support support_function support_function_any-method support_function_any-method_c&quot;&gt;LessThan&lt;/span&gt;(&lt;/span&gt;&lt;span class=&quot;constant constant_numeric constant_numeric_c&quot;&gt;50.&lt;/span&gt;&lt;span class=&quot;meta meta_function-call meta_function-call_c&quot;&gt;&lt;span class=&quot;support support_function support_function_any-method support_function_any-method_c&quot;&gt;Milliseconds&lt;/span&gt;(&lt;/span&gt;).&lt;span class=&quot;meta meta_function-call meta_function-call_c&quot;&gt;&lt;span class=&quot;support support_function support_function_any-method support_function_any-method_c&quot;&gt;After&lt;/span&gt;(&lt;/span&gt;expected)));
&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
 These built in .NET types are pretty easy to work with and made the implementation very easy 
 (except for the bit where I discovered that 
 &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/system.timespan.frommilliseconds.aspx&quot;&gt;TimeSpan.FromMilliseconds&lt;/a&gt; 
 takes a double but only has whole-millisecond precision).
&lt;/p&gt;
&lt;p&gt;
 This morning I put it up on Github. 
 &lt;a href=&quot;https://github.com/duelinmarkers/FluentTime&quot;&gt;You can find FluentTime here.&lt;/a&gt; 
&lt;/p&gt;
&lt;p&gt;
 The one more complex thing I&#39;d still like to add 
 is a variable-length variation on TimeSpan for handling units like months and years. 
 I&#39;m interested to see how clean I can keep things when my type will only take 
 on a specific value based on the date-time value it&#39;s being added to or subtracted from.
 That ought to be a good opportunity for exploring operator overloading. 
 (Unsurprisingly that hasn&#39;t come up in my day job just yet.)
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/3740351589651631328/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/3740351589651631328' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/3740351589651631328'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/3740351589651631328'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2011/04/fluenttime-fluent-interface-in-c-for.html' title='FluentTime: a fluent interface in C# for working with dates and times'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-5082503066298583475</id><published>2010-04-13T07:38:00.001-04:00</published><updated>2010-04-13T07:39:19.522-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="agile"/><title type='text'>My Favorite Analysis Artifact: Code</title><content type='html'>&lt;p&gt;On projects where you&#39;re integrating with legacy or vendor systems, it&#39;s often necessary to dedicate developers to technical analysis of the system before stories can be broken up reasonably and estimated by the team. Typically the output from that analysis is technical notes on a wiki page (or Mingle card). Those notes are helpful, but keep an eye out for the opportunity to do something far more useful: automated specs showing how to get the system to do what you need (or something close, if the details haven&#39;t been worked out).&lt;/p&gt;

&lt;p&gt;Since your specs are self-verifying, whoever picks up the story will know for sure that the system works the way the specs say, which is never the case for notes on a wiki.&lt;/p&gt;

&lt;p&gt;Code is a natural and unambiguous way to describe using a system, so it eliminates challenges of interpreting someone else&#39;s notes. It&#39;s also likely faster for developers to write code than to write prose.&lt;/p&gt;

&lt;p&gt;Resist the temptation to go beyond the spec and write production implementation code. If your requirements haven&#39;t been fully baked, you&#39;ll just be bloating the codebase. Your specs can be clearly labeled as analysis artifacts, and once the real integration has happened, they can be deleted.&lt;/p&gt;

&lt;p&gt;I&#39;d recommend using this approach any time you already have a test environment for the legacy database or application. Besides giving you more manageable and useful analysis artifacts, it&#39;s likely to keep the developers assigned to do technical analysis much happier.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/5082503066298583475/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/5082503066298583475' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/5082503066298583475'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/5082503066298583475'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2010/04/my-favorite-analysis-artifact-code.html' title='My Favorite Analysis Artifact: Code'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-3048560790116607983</id><published>2010-03-06T15:24:00.001-05:00</published><updated>2010-03-06T15:24:53.201-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="actionscript"/><category scheme="http://www.blogger.com/atom/ns#" term="gotchas"/><title type='text'>ActionScript const Gotcha</title><content type='html'>&lt;p&gt;I&#39;ve been working in ActionScript lately. It&#39;s a bizarre language. Java-style OO features are smashed right on top of JavaScript, with no regard for the prototype-based inheritance that the language has had all along. (As far as I can tell Adobe doesn&#39;t want you to think about prototypes. I haven&#39;t dug into how the Java-style ActionScript class model plays with the prototype model, but it might be worth exploring.)&lt;/p&gt;

&lt;p&gt;After a few years of full-time Ruby, the adjustment has been pretty painful. One of my biggest complaints is the verbosity of the static type declarations. Having recently spent some time playing around with Scala, I was incredibly impressed at how much noise can be removed from a statically typed language with a compiler that does good type-inference. The ActionScript compiler does absolutely zero type-inference, and the result makes for really nasty reading.&lt;/p&gt;

&lt;p&gt;One of the additions to JavaScript syntax that initially impressed me was the introduction of the &lt;code&gt;const&lt;/code&gt; keyword. I really appreciate how Scala&#39;s &lt;code&gt;val&lt;/code&gt; keyword makes declaration of immutable fields just as terse and clear as the &lt;code&gt;var&lt;/code&gt; keyword does for mutable fields. This is in contrast to Java, where making things immutable requires an extra keyword (&lt;code&gt;final&lt;/code&gt;) that makes code with immutables (i.e., better, safer code) extra noisy, a great disincentive to doing the right thing. Using &lt;code&gt;const&lt;/code&gt; in place of &lt;code&gt;var&lt;/code&gt; in ActionScript is much prettier than the Java way. Unfortunately I quickly discovered a couple of shortcomings in ActionScript &quot;constants&quot; that really bummed me out.&lt;/p&gt;

&lt;p&gt;The first issue I ran into very quickly: Whereas Java only allows final variables to be assigned once, it&#39;s smart enough to allow that assignment to happen in a constructor. The ActionScript compiler specifically requires them to be assigned at the point of declaration. This means you can&#39;t declare a constant field and assign it its value in a constructor, which is the primary reason I&#39;d want constant fields.&lt;/p&gt;

&lt;p&gt;Boo! But &lt;code&gt;const&lt;/code&gt; was still good for static constants (obviously) and seemed to be good for locals as well.&lt;/p&gt;

&lt;p&gt;The second issue has to do with using &lt;code&gt;const&lt;/code&gt; on locals and led to a pretty painful debugging session. We were taking advantage of one feature ActionScript holds high over Java&#39;s head: functions are closures, and closures are handy. I was looping over some data structure using a for loop. Inside that loop I declared a const, then declared a function I wanted for later that used that const. But at the end of the loop all the functions were referring to the last item in the data structure.&lt;/p&gt;

&lt;p&gt;Here&#39;s some sample code illustrating what I was seeing.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_js&quot;&gt;&lt;span class=&quot;comment comment_line comment_line_double-slash comment_line_double-slash_js&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_js&quot;&gt;//&lt;/span&gt; Maps an array to an array of functions that return
&lt;/span&gt;&lt;span class=&quot;comment comment_line comment_line_double-slash comment_line_double-slash_js&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_js&quot;&gt;//&lt;/span&gt; the values from the original array.
&lt;/span&gt;&lt;span class=&quot;comment comment_line comment_line_double-slash comment_line_double-slash_js&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_js&quot;&gt;//&lt;/span&gt; But doesn&#39;t work!
&lt;/span&gt;&lt;span class=&quot;meta meta_function meta_function_js&quot;&gt;&lt;span class=&quot;storage storage_type storage_type_function storage_type_function_js&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;entity entity_name entity_name_function entity_name_function_js&quot;&gt;identityFunctions&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_begin punctuation_definition_parameters_begin_js&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable variable_parameter variable_parameter_function variable_parameter_function_js&quot;&gt;items:Array&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_end punctuation_definition_parameters_end_js&quot;&gt;)&lt;/span&gt;&lt;/span&gt;:&lt;span class=&quot;support support_class support_class_js&quot;&gt;Array&lt;/span&gt; &lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;storage storage_modifier storage_modifier_js&quot;&gt;const&lt;/span&gt; functions:&lt;span class=&quot;support support_class support_class_js&quot;&gt;Array&lt;/span&gt; &lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;meta meta_class meta_class_instance meta_class_instance_constructor&quot;&gt;&lt;span class=&quot;keyword keyword_operator keyword_operator_new keyword_operator_new_js&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;entity entity_name entity_name_type entity_name_type_instance entity_name_type_instance_js&quot;&gt;Array&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;keyword keyword_control keyword_control_js&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;storage storage_type storage_type_js&quot;&gt;var&lt;/span&gt; i:&lt;span class=&quot;storage storage_type storage_type_js&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant constant_numeric constant_numeric_js&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;&amp;lt;&lt;/span&gt; items&lt;span class=&quot;meta meta_delimiter meta_delimiter_method meta_delimiter_method_period meta_delimiter_method_period_js&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;support support_constant support_constant_js&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;storage storage_modifier storage_modifier_js&quot;&gt;const&lt;/span&gt; item:&lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;=&lt;/span&gt; items&lt;span class=&quot;meta meta_brace meta_brace_square meta_brace_square_js&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;meta meta_brace meta_brace_square meta_brace_square_js&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt;
    functions&lt;span class=&quot;meta meta_brace meta_brace_square meta_brace_square_js&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;meta meta_brace meta_brace_square meta_brace_square_js&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;storage storage_type storage_type_js&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;()&lt;/span&gt;:&lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;keyword keyword_control keyword_control_js&quot;&gt;return&lt;/span&gt; item&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;storage storage_modifier storage_modifier_js&quot;&gt;const&lt;/span&gt; funcs:&lt;span class=&quot;support support_class support_class_js&quot;&gt;Array&lt;/span&gt; &lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;=&lt;/span&gt; identityFunctions&lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_square meta_brace_square_js&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_js&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_js&quot;&gt;&quot;&lt;/span&gt;a&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_js&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;meta meta_delimiter meta_delimiter_object meta_delimiter_object_comma meta_delimiter_object_comma_js&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_js&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_js&quot;&gt;&quot;&lt;/span&gt;b&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_js&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;meta meta_delimiter meta_delimiter_object meta_delimiter_object_comma meta_delimiter_object_comma_js&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_js&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_js&quot;&gt;&quot;&lt;/span&gt;c&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_js&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_square meta_brace_square_js&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt;
funcs&lt;span class=&quot;meta meta_brace meta_brace_square meta_brace_square_js&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant constant_numeric constant_numeric_js&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_square meta_brace_square_js&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment comment_line comment_line_double-slash comment_line_double-slash_js&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_js&quot;&gt;//&lt;/span&gt; =&amp;gt; &quot;c&quot;, as desired
&lt;/span&gt;funcs&lt;span class=&quot;meta meta_brace meta_brace_square meta_brace_square_js&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant constant_numeric constant_numeric_js&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_square meta_brace_square_js&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment comment_line comment_line_double-slash comment_line_double-slash_js&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_js&quot;&gt;//&lt;/span&gt; =&amp;gt; &quot;c&quot;, but I wanted &quot;b&quot;
&lt;/span&gt;funcs&lt;span class=&quot;meta meta_brace meta_brace_square meta_brace_square_js&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant constant_numeric constant_numeric_js&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_square meta_brace_square_js&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment comment_line comment_line_double-slash comment_line_double-slash_js&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_js&quot;&gt;//&lt;/span&gt; =&amp;gt; &quot;c&quot;, but I wanted &quot;a&quot;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;Here&#39;s something I either never knew or at some point forgot about JavaScript: &lt;strong&gt;variables are lexically scoped, but only function bodies introduce new lexical scopes&lt;/strong&gt;. So even though that &lt;code&gt;item&lt;/code&gt; const is declared inside the loop, it&#39;s really scoped to the entire function. It gets assigned a new value on each pass of the loop, so yes, the &lt;code&gt;const&lt;/code&gt; keyword is a bold-faced lie. Apparently the keyword tells the compiler not to let any other code assign to that reference, but it creates a mutable reference just like &lt;code&gt;var&lt;/code&gt;, and in this case it gets repointed several times.&lt;/p&gt;

&lt;p&gt;Basically the function definition above compiles into the same thing as this.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_js&quot;&gt;&lt;span class=&quot;comment comment_line comment_line_double-slash comment_line_double-slash_js&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_js&quot;&gt;//&lt;/span&gt; Comes right out and tells you it doesn&#39;t work.
&lt;/span&gt;&lt;span class=&quot;meta meta_function meta_function_js&quot;&gt;&lt;span class=&quot;storage storage_type storage_type_function storage_type_function_js&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;entity entity_name entity_name_function entity_name_function_js&quot;&gt;identityFunctions&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_begin punctuation_definition_parameters_begin_js&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable variable_parameter variable_parameter_function variable_parameter_function_js&quot;&gt;items:Array&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_end punctuation_definition_parameters_end_js&quot;&gt;)&lt;/span&gt;&lt;/span&gt;:&lt;span class=&quot;support support_class support_class_js&quot;&gt;Array&lt;/span&gt; &lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;storage storage_modifier storage_modifier_js&quot;&gt;const&lt;/span&gt; functions:&lt;span class=&quot;support support_class support_class_js&quot;&gt;Array&lt;/span&gt; &lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;meta meta_class meta_class_instance meta_class_instance_constructor&quot;&gt;&lt;span class=&quot;keyword keyword_operator keyword_operator_new keyword_operator_new_js&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;entity entity_name entity_name_type entity_name_type_instance entity_name_type_instance_js&quot;&gt;Array&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;storage storage_type storage_type_js&quot;&gt;var&lt;/span&gt; item:&lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;keyword keyword_control keyword_control_js&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;storage storage_type storage_type_js&quot;&gt;var&lt;/span&gt; i:&lt;span class=&quot;storage storage_type storage_type_js&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant constant_numeric constant_numeric_js&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;&amp;lt;&lt;/span&gt; items&lt;span class=&quot;meta meta_delimiter meta_delimiter_method meta_delimiter_method_period meta_delimiter_method_period_js&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;support support_constant support_constant_js&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;{&lt;/span&gt;
    item &lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;=&lt;/span&gt; items&lt;span class=&quot;meta meta_brace meta_brace_square meta_brace_square_js&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;meta meta_brace meta_brace_square meta_brace_square_js&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt;
    functions&lt;span class=&quot;meta meta_brace meta_brace_square meta_brace_square_js&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;meta meta_brace meta_brace_square meta_brace_square_js&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;storage storage_type storage_type_js&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;()&lt;/span&gt;:&lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;keyword keyword_control keyword_control_js&quot;&gt;return&lt;/span&gt; item&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;Certainly it&#39;s my bad not knowing that the &lt;code&gt;for&lt;/code&gt; loop doesn&#39;t introduce a lexical scope, but it&#39;s crazy for the compiler to allow declaration of a const inside a looping structure. If Adobe&#39;s really committed to all this static typing and compiler checking hooey, surely they&#39;d agree this is a nasty gotcha that the compiler should prevent.&lt;/p&gt;

&lt;p&gt;As far as workarounds, there are several. For the contrived example above, the simplest is to use one of the iteration methods Array has in ActionScript. There the &quot;body&quot; of the loop is a function, so it has its own scope. Here&#39;s the cleanest way.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_js&quot;&gt;&lt;span class=&quot;comment comment_line comment_line_double-slash comment_line_double-slash_js&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_js&quot;&gt;//&lt;/span&gt; Actually works.
&lt;/span&gt;&lt;span class=&quot;meta meta_function meta_function_js&quot;&gt;&lt;span class=&quot;storage storage_type storage_type_function storage_type_function_js&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;entity entity_name entity_name_function entity_name_function_js&quot;&gt;identityFunctions&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_begin punctuation_definition_parameters_begin_js&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable variable_parameter variable_parameter_function variable_parameter_function_js&quot;&gt;items:Array&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_end punctuation_definition_parameters_end_js&quot;&gt;)&lt;/span&gt;&lt;/span&gt;:&lt;span class=&quot;support support_class support_class_js&quot;&gt;Array&lt;/span&gt; &lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;keyword keyword_control keyword_control_js&quot;&gt;return&lt;/span&gt; items&lt;span class=&quot;meta meta_delimiter meta_delimiter_method meta_delimiter_method_period meta_delimiter_method_period_js&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;storage storage_type storage_type_js&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;(&lt;/span&gt;item:&lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;meta meta_delimiter meta_delimiter_object meta_delimiter_object_comma meta_delimiter_object_comma_js&quot;&gt;, &lt;/span&gt;i:&lt;span class=&quot;storage storage_type storage_type_js&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;meta meta_delimiter meta_delimiter_object meta_delimiter_object_comma meta_delimiter_object_comma_js&quot;&gt;, &lt;/span&gt;a:&lt;span class=&quot;support support_class support_class_js&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;)&lt;/span&gt;:&lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;keyword keyword_control keyword_control_js&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;storage storage_type storage_type_js&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;()&lt;/span&gt;:&lt;span class=&quot;keyword keyword_operator keyword_operator_js&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;keyword keyword_control keyword_control_js&quot;&gt;return&lt;/span&gt; item&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;meta meta_brace meta_brace_round meta_brace_round_js&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_terminator punctuation_terminator_statement punctuation_terminator_statement_js&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;meta meta_brace meta_brace_curly meta_brace_curly_js&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/3048560790116607983/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/3048560790116607983' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/3048560790116607983'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/3048560790116607983'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2010/03/actionscript-const-gotcha.html' title='ActionScript const Gotcha'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-4242424436065805306</id><published>2009-11-21T17:44:00.002-05:00</published><updated>2009-11-21T17:48:39.338-05:00</updated><title type='text'>RapidSMS at Away Day</title><content type='html'>&lt;p&gt;Last weekend at the &lt;a href=&quot;http://www.thoughtworks.com/&quot;&gt;ThoughtWorks&lt;/a&gt; US Away Day we were joined by &lt;a href=&quot;http://www.linkedin.com/pub/merrick-schaefer/4/641/8a1&quot;&gt;Merrick Schaefer&lt;/a&gt; from &lt;a href=&quot;http://unicefinnovation.org/&quot;&gt;UNICEF Innovation&lt;/a&gt; and a &lt;a href=&quot;http://github.com/czue&quot;&gt;couple&lt;/a&gt; &lt;a href=&quot;http://github.com/rowenaluk&quot;&gt;developers&lt;/a&gt; who&#39;ve worked extensively with &lt;a href=&quot;http://www.rapidsms.org/&quot;&gt;RapidSMS&lt;/a&gt;. RapidSMS is an open source project providing a set of libraries and apps for interacting with phones. It&#39;s been used for a variety of purposes, mostly in Africa, where cell phone coverage is far better than electricity or land-line availability. One deployment they talked through in detail allows (relatively) untrained medical workers to use SMS messages to sumbit a few measurements to assess childhood malnutrition. In addition to recording the information to keep accurate and instantly available statistics, the system performs an immediate diagnosis and texts it back to the worker in the field.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.linkedin.com/pub/jeff-wishnie/0/1/962&quot;&gt;Jeff Wishnie&lt;/a&gt; organized a RapidSMS code jam for those not in the mood to attend sessions, and &lt;a href=&quot;http://blog.brianguthrie.com/&quot;&gt;Brian Guthrie&lt;/a&gt; and I spent most of Saturday pairing on a simple feature addition to one of the support applications. It was the first time either of us had touched Python for more than a few minutes, and the first time we&#39;d touched Django at all, so it was an interesting learning experience. It was also extremely rewarding knowing that we were contributing to such a good cause.&lt;/p&gt;

&lt;p&gt;If you sometimes find yourself in the mood for some coding but don&#39;t always have a personal project burning to get out, I&#39;d highly encourage you to spend a little time familiarizing yourself with RapidSMS and maybe find something in their &lt;a href=&quot;http://github.com/rapidsms/rapidsms/issues&quot;&gt;issue tracker&lt;/a&gt; you could tackle. I promise you&#39;ll feel great about it.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/4242424436065805306/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/4242424436065805306' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/4242424436065805306'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/4242424436065805306'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/11/rapidsms-at-away-day.html' title='RapidSMS at Away Day'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-4365315854603904699</id><published>2009-10-16T13:44:00.002-04:00</published><updated>2009-10-16T13:47:48.225-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="appengine"/><category scheme="http://www.blogger.com/atom/ns#" term="clojure"/><title type='text'>Interactive Clojure-AppEngine Development</title><content type='html'>If you like (1) interactive programming in Clojure from Emacs and (2) deploying to Google AppEngine, but you haven&#39;t yet managed to bring these two together, you should take a look at &lt;a href=&quot;http://www.hackers-with-attitude.com/2009/08/intertactive-programming-with-clojure.html&quot;&gt;this post over at hackers-with-attitude&lt;/a&gt;, which walks you through setting up a pretty slick workflow.</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/4365315854603904699/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/4365315854603904699' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/4365315854603904699'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/4365315854603904699'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/10/interactive-clojure-appengine.html' title='Interactive Clojure-AppEngine Development'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-5525527280324307404</id><published>2009-10-14T13:38:00.001-04:00</published><updated>2009-10-14T13:42:49.495-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="iphone"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><title type='text'>SimURLator: Test Custom iPhone URL Schemes in the iPhone Simulator</title><content type='html'>&lt;p&gt;
  Over the last few months ThoughtWorks 
  &lt;a href=&quot;http://www.thoughtworks.com/press-releases/New-iPhone-App-from-ThoughtWorks-for-Agile-2009.html&quot;&gt;has&lt;/a&gt; 
  &lt;a href=&quot;http://www.westcoastgreen.org/sponsors-exhibitors/sponsor-directory2009.php&quot;&gt;implemented&lt;/a&gt; 
  &lt;a href=&quot;http://ca.rroll.net/2009/10/12/agile-australia-2009-iphone-app/&quot;&gt;several&lt;/a&gt; 
  &quot;Conference Connection&quot; iPhone applications. 
  They provide a view of the conference schedule, 
  the ability to save a list of sessions you plan to attend, 
  maps of the conferene site, 
  a view of relevant Twitter posts, 
  and some features specific to the various conferences.
&lt;/p&gt;
&lt;p&gt;
  The Twitter integration includes a feature to post, 
  launching your prefered Twitter client 
  (&lt;a href=&quot;http://www.atebits.com/tweetie-iphone/&quot;&gt;Tweetie&lt;/a&gt;, 
  &lt;a href=&quot;http://echofon.com/&quot;&gt;Echofon&lt;/a&gt;&amp;mdash;formerly &quot;TwitterFon&quot;&amp;mdash;or 
  &lt;a href=&quot;http://iconfactory.com/software/twitterrific&quot;&gt;Twitterrific&lt;/a&gt;) 
  using &lt;code&gt;UIApplication&lt;/code&gt;&#39;s &lt;code&gt;openURL:&lt;/code&gt; and the apps&#39; custom URL schemes.
&lt;/p&gt;
&lt;p&gt;
  Relatively late in the testing cycle for another upcoming conference, 
  we discovered an issue with selecting a Twitter client when you have more than one of the supported clients installed.
  Some of our testers were testing only in the iPhone Simulator, 
  and because you can&#39;t install third-party apps in the Simulator, that feature had been undertested.
&lt;/p&gt;
&lt;p&gt;
  That was no good, so we created a sort of workaround: 
  a stand-in app that takes the place of the third-party apps you can&#39;t install in the Simulator. 
  &lt;a href=&quot;http://github.com/duelinmarkers/SimURLator&quot;&gt;SimURLator is now available on Github&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
  You configure SimURLator to respond to whatever custom URL schemes you want 
  (by adding or removing URL schemes in SimURLator-Info.plist) 
  and deploy it to your Simulator alongside your app. 
  When SimURLator is launched via a URL by your app, it shows you the full URL. 
  Obviously that&#39;s not as satisfying as seeing the third-party app launch and do the right thing, 
  but it&#39;s a lot better than nothing. 
&lt;/p&gt;
&lt;p&gt;
  You can also test your app&#39;s handling of its own custom URL schemes. 
  Launch SimURLator and type in a URL that should launch your application. 
  One button lets you verify that the OS reports the URL can be opened (with &lt;code&gt;canOpenURL:&lt;/code&gt;); 
  the other launches your app (with &lt;code&gt;openURL:&lt;/code&gt;).
&lt;/p&gt;
&lt;p&gt;
  If there are other features you think would be useful, comment below. Better yet, &lt;a href=&quot;http://github.com/duelinmarkers/SimURLator&quot;&gt;fork it&lt;/a&gt; and show us.
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/5525527280324307404/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/5525527280324307404' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/5525527280324307404'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/5525527280324307404'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/10/simurlator-test-custom-iphone-url.html' title='SimURLator: Test Custom iPhone URL Schemes in the iPhone Simulator'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-435027297025248902</id><published>2009-09-03T10:55:00.000-04:00</published><updated>2009-09-03T10:56:57.566-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="clojure"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><title type='text'>Google Group for clj-record</title><content type='html'>&lt;p&gt;
  The title pretty much says it all. There&#39;s now a 
  &lt;a href=&quot;http://groups.google.com/group/clj-record-dev&quot;&gt;Google Group for discussion of clj-record: clj-record-dev&lt;/a&gt;.
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/435027297025248902/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/435027297025248902' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/435027297025248902'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/435027297025248902'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/09/google-group-for-clj-record.html' title='Google Group for clj-record'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-1531586123114611004</id><published>2009-07-07T18:11:00.000-04:00</published><updated>2009-07-07T18:12:43.385-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><category scheme="http://www.blogger.com/atom/ns#" term="ruby"/><title type='text'>Renum 1.1: pretty instance-specific method definition</title><content type='html'>&lt;p&gt;You could always define instance-specific behavior on &lt;a href=&quot;http://github.com/duelinmarkers/renum/tree&quot;&gt;Renum&lt;/a&gt; values the same way you can on any object in Ruby:&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_ruby&quot;&gt;enum &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;FakeWord&lt;/span&gt; &lt;span class=&quot;keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby&quot;&gt;do
&lt;/span&gt;  &lt;span class=&quot;variable variable_other variable_other_constant variable_other_constant_ruby&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;variable variable_other variable_other_constant variable_other_constant_ruby&quot;&gt;Bar&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;variable variable_other variable_other_constant variable_other_constant_ruby&quot;&gt;Baz&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby&quot;&gt;()&lt;/span&gt;
  
  &lt;span class=&quot;meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby&quot;&gt;&lt;span class=&quot;keyword keyword_control keyword_control_def keyword_control_def_ruby&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;entity entity_name entity_name_function entity_name_function_ruby&quot;&gt;extra_stuff&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby&quot;&gt;&quot;&lt;/span&gt;no extra stuff here&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby&quot;&gt;&lt;span class=&quot;keyword keyword_control keyword_control_def keyword_control_def_ruby&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;entity entity_name entity_name_function entity_name_function_ruby&quot;&gt;Foo.extra_stuff&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby&quot;&gt;&quot;&lt;/span&gt;here&#39;s the foo stuff&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;That works fine, but it&#39;s a little ugly.&lt;/p&gt;

&lt;p&gt;Starting this afternoon (with release 1.1) you can do this instead:&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_ruby&quot;&gt;enum &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;FakeWord&lt;/span&gt; &lt;span class=&quot;keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby&quot;&gt;do
&lt;/span&gt;  &lt;span class=&quot;variable variable_other variable_other_constant variable_other_constant_ruby&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby&quot;&gt;do
&lt;/span&gt;    &lt;span class=&quot;meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby&quot;&gt;&lt;span class=&quot;keyword keyword_control keyword_control_def keyword_control_def_ruby&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;entity entity_name entity_name_function entity_name_function_ruby&quot;&gt;extra_stuff&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby&quot;&gt;&quot;&lt;/span&gt;here&#39;s the foo stuff&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;variable variable_other variable_other_constant variable_other_constant_ruby&quot;&gt;Bar&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;variable variable_other variable_other_constant variable_other_constant_ruby&quot;&gt;Baz&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby&quot;&gt;()&lt;/span&gt;
  
  &lt;span class=&quot;meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby&quot;&gt;&lt;span class=&quot;keyword keyword_control keyword_control_def keyword_control_def_ruby&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;entity entity_name entity_name_function entity_name_function_ruby&quot;&gt;extra_stuff&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby&quot;&gt;&quot;&lt;/span&gt;no extra stuff here&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;FakeWord::Foo&lt;/code&gt; will have its own implementation of &lt;code&gt;extra_stuff&lt;/code&gt; while &lt;code&gt;FakeWord::Bar&lt;/code&gt; and &lt;code&gt;FakeWord::Baz&lt;/code&gt; will have the one defined inside the &lt;code&gt;enum&lt;/code&gt; block. This was implemented by just &lt;code&gt;instance_eval&lt;/code&gt;&#39;ing the given block against the instance just after creating it. A &lt;code&gt;def&lt;/code&gt; form in an &lt;code&gt;instance_eval&lt;/code&gt;&#39;d block ends up defining a singleton method, so it&#39;s equivalent to the uglier version above.&lt;/p&gt;

&lt;p&gt;This ends up looking almost exactly like the Java enum equivalent for defining constant-specific class bodies.&lt;/p&gt;

&lt;p&gt;Since the block is &lt;code&gt;instance_eval&lt;/code&gt;&#39;d, if you prefered you could do any initialization there instead of defining an &lt;code&gt;init&lt;/code&gt; method. Depending on what you&#39;ve got going on it may turn out to be more readable. Here&#39;s a contrived &lt;a href=&quot;http://github.com/duelinmarkers/renum/blob/130416f6c63224ecee517b490089bf87f006c65c/spec/renum_spec.rb#L64&quot;&gt;example from the spec&lt;/a&gt; translated to that style.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_ruby&quot;&gt;&lt;span class=&quot;comment comment_line comment_line_number-sign comment_line_number-sign_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby&quot;&gt;#&lt;/span&gt; init-with-args-style
&lt;/span&gt;enum &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;Size&lt;/span&gt; &lt;span class=&quot;keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby&quot;&gt;do
&lt;/span&gt;  &lt;span class=&quot;variable variable_other variable_other_constant variable_other_constant_ruby&quot;&gt;Small&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby&quot;&gt;&quot;&lt;/span&gt;Really really tiny&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;variable variable_other variable_other_constant variable_other_constant_ruby&quot;&gt;Medium&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby&quot;&gt;&quot;&lt;/span&gt;Sort of in the middle&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;variable variable_other variable_other_constant variable_other_constant_ruby&quot;&gt;Large&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby&quot;&gt;&quot;&lt;/span&gt;Quite big&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;description&lt;/span&gt;

  &lt;span class=&quot;meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby&quot;&gt;&lt;span class=&quot;keyword keyword_control keyword_control_def keyword_control_def_ruby&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;entity entity_name entity_name_function entity_name_function_ruby&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;variable variable_parameter variable_parameter_function variable_parameter_function_ruby&quot;&gt;description&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby&quot;&gt;@&lt;/span&gt;description&lt;/span&gt; &lt;span class=&quot;keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby&quot;&gt;=&lt;/span&gt; description
  &lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_ruby&quot;&gt;&lt;span class=&quot;comment comment_line comment_line_number-sign comment_line_number-sign_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby&quot;&gt;#&lt;/span&gt; instance-block-per-value-style
&lt;/span&gt;enum &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;Size&lt;/span&gt; &lt;span class=&quot;keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby&quot;&gt;do
&lt;/span&gt;  &lt;span class=&quot;variable variable_other variable_other_constant variable_other_constant_ruby&quot;&gt;Small&lt;/span&gt;  &lt;span class=&quot;punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block&quot;&gt; &lt;/span&gt;&lt;span class=&quot;variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby&quot;&gt;@&lt;/span&gt;description&lt;/span&gt; &lt;span class=&quot;keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby&quot;&gt;&quot;&lt;/span&gt;Really really tiny&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;variable variable_other variable_other_constant variable_other_constant_ruby&quot;&gt;Medium&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block&quot;&gt; &lt;/span&gt;&lt;span class=&quot;variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby&quot;&gt;@&lt;/span&gt;description&lt;/span&gt; &lt;span class=&quot;keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby&quot;&gt;&quot;&lt;/span&gt;Sort of in the middle&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;variable variable_other variable_other_constant variable_other_constant_ruby&quot;&gt;Large&lt;/span&gt;  &lt;span class=&quot;punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block&quot;&gt; &lt;/span&gt;&lt;span class=&quot;variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby&quot;&gt;@&lt;/span&gt;description&lt;/span&gt; &lt;span class=&quot;keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby&quot;&gt;&quot;&lt;/span&gt;Quite big&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;description&lt;/span&gt;
&lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;The latter is probably slightly easier to digest, but it&#39;s also an extremely simple case, so I&#39;d expect your mileage to vary quite a bit. (Note that you can do both an &lt;code&gt;init&lt;/code&gt; with arguments AND an associated block. Renum evals the block before calling &lt;code&gt;init&lt;/code&gt;, so any instance-specific behavior is in place before your init runs.)&lt;/p&gt;

&lt;p&gt;Anyway, comment, suggest, complain, fork, improve, and enjoy.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/1531586123114611004/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/1531586123114611004' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/1531586123114611004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/1531586123114611004'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/07/renum-11-pretty-instance-specific.html' title='Renum 1.1: pretty instance-specific method definition'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-80789562878302939</id><published>2009-07-05T13:30:00.002-04:00</published><updated>2009-07-05T13:33:33.928-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="agile"/><title type='text'>AR&amp;otimes;TA</title><content type='html'>&lt;p&gt;If you haven&#39;t already, you should check out &lt;a href=&quot;http://exampler.com/&quot;&gt;Brian Marick&lt;/a&gt;&#39;s Agile reboot: &lt;a href=&quot;http://arxta.com/&quot;&gt;Artisanal Retro-Futurism Crossed With Team Scale Anarcho-Syndicalism&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There&#39;s a &lt;a href=&quot;http://arxta.com/video&quot;&gt;video&lt;/a&gt; of his excellent presentation from Agile Roots if you don&#39;t feel like reading. There are also &lt;a href=&quot;http://arxta.com/gear&quot;&gt;stickers you can ask for&lt;/a&gt; if you feel like spreading the word.&lt;/p&gt;

&lt;p&gt;It&#39;s totally tangential to the point, but my favorite quote from the video is near the end of the questions: &quot;...in my observation agile teams care more about delivering business value, often, than the business they&#39;re delivering it to.&quot;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/80789562878302939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/80789562878302939' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/80789562878302939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/80789562878302939'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/07/ar.html' title='AR&amp;otimes;TA'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-687741166758297982</id><published>2009-07-01T11:12:00.004-04:00</published><updated>2009-09-07T12:38:32.005-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="appengine"/><category scheme="http://www.blogger.com/atom/ns#" term="cloud"/><title type='text'>A Quick Look at Azure, Microsoft&#39;s Cloud</title><content type='html'>&lt;p&gt;Last week at a &lt;a href=&quot;http://www.thoughtworks.com/&quot;&gt;ThoughtWorks&lt;/a&gt; home-office gathering in New York we had a presentation by one of Microsoft&#39;s Architecture Evangelists about their in-the-works cloud offering, &lt;a href=&quot;http://www.microsoft.com/azure/default.mspx&quot;&gt;Azure&lt;/a&gt;. I have to say I was pretty impressed. (If not for the whole Windows thing, I&#39;d probably be trying it out instead of writing about it right now. If you aren&#39;t as bothered by Windows as I am, &lt;a href=&quot;http://www.microsoft.com/azure/register.mspx&quot;&gt;you can register and try it yourself&lt;/a&gt;.) Here are some fairly random impressions, mostly based on comparison to the two other best-known cloud contenders.&lt;/p&gt;

&lt;p&gt;Azure conceptually leans more towards &lt;a href=&quot;http://code.google.com/appengine/&quot;&gt;Google AppEngine&lt;/a&gt;&#39;s &quot;scale me up; scale me down; hide the details&quot; approach than &lt;a href=&quot;http://aws.amazon.com/ec2/&quot;&gt;Amazon EC2&lt;/a&gt;&#39;s easy-to-manage-infrastructure-as-a-service model, though it currently sits somewhere between them. There&#39;s not (yet, at least) any automatic scaling up and down, which surprised me. However what you manually configure is just the number of instances of each application, which is far simpler than manually spinning up new instances of EC2 images and getting them into your cluster (or what-have-you). (Although if you&#39;ve set up your infrastructure right, your EC2 scaling activities could be just as simple. EC2 just gives you the freedom to do it badly.)&lt;/p&gt;

&lt;p&gt;There are two-and-a-half types of application: web apps, worker apps, and web+worker apps. The worker concept is very cool and not something you can do with AppEngine. (The closest thing is their &lt;a href=&quot;http://code.google.com/appengine/docs/java/config/cron.html&quot;&gt;support for scheduled tasks&lt;/a&gt;, but these are limited to scheduled URL hits no more often than once a minute with the same restrictions as a normal web request.) Apps all run on something pretty close to the normal .NET framework (minus normal file system access and presumably some other areas--I forgot to ask about threading.)&lt;/p&gt;

&lt;p&gt;There are three kinds of storage.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A &lt;strong&gt;file-system-like BLOB storage&lt;/strong&gt; API, which involves saving individual blocks and then telling the system in what order to stitch them together. It seemed clunky, but I could see writing a simple abstraction over it for small stuff (where you&#39;d usually only need one block) and for large stuff maybe being able to take advantage of already having some blocks uploaded when something gets interrupted and can resume later.&lt;/li&gt;
  &lt;li&gt;A &lt;strong&gt;BigTable-like &quot;table&quot; storage&lt;/strong&gt; API. It isn&#39;t as rich as AppEngine&#39;s BigTable-backed Datastore in that entity properties are restricted to very simple types (no lists, for example) and there&#39;s no built-in support for relationships of any kind (even the simple hierarchy of Datastore). (Note there&#39;s nothing to stop you from storing keys of other entities as property values and rolling your own awkward relationships.) Similar to Datastore on AppEngine, you have to think about how you want your data partitioned and make some under-informed guesses about slow queries across multiple partitions vs parallel queries each in their own partition. (On Azure, the key for each entity includes an app-generated token naming the partition in which it should reside, which is a bit simpler than Datastore&#39;s &lt;a href=&quot;http://code.google.com/appengine/docs/java/datastore/transactions.html#Using_Transactions&quot;&gt;parentage-based entity groups&lt;/a&gt;.) The &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/dd894038.aspx&quot;&gt;story on transactions&lt;/a&gt; (being limited to a single entity group) seems to be similar as well. There&#39;s also currently no indexing by anything other than key, though it sounded like they&#39;ll allow you to add at least one additional index per table before they go proper-live.&lt;/li&gt;
  &lt;li&gt;Most interestingly, &lt;strong&gt;damn-near plain old &lt;a href=&quot;http://www.microsoft.com/azure/sql.mspx&quot;&gt;SQL Server&lt;/a&gt; storage&lt;/strong&gt; in the form of SQL Data Services, with just about every feature of MS SQL Server any normal application would use (except distributed transactions). I believe this is a separate service that you&#39;ll need to sign up for and eventually pay for separately. Regardless, it&#39;s a key element of the offering, because it provides a huge boost to the suggestion that you can migrate existing .NET applications into the cloud.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are also &lt;strong&gt;persistent queues&lt;/strong&gt;, which web apps can use to communicate to worker apps asynchronously. (I guess this qualifies as a fourth kind of storage.) They feature guaranteed delivery (with possible repeated delivery) of messages. This is very cool and not something AppEngine offers. (Even if you rolled your own queue-ish thing in datastore, there&#39;s no facility to consume messages intelligently.)&lt;/p&gt;

&lt;p&gt;There are REST APIs (and SOAP as well, I think) for just about everything, so you have a lot of options for architectures (using heterogeneous technologies) that run partly on-premisis and partly in the Azure cloud.&lt;/p&gt;

&lt;p&gt;There are a bunch of other services you can hook into, including &lt;a href=&quot;http://www.microsoft.com/azure/netservices.mspx&quot;&gt;&quot;.NET Services&quot;&lt;/a&gt; (low-level technical stuff including a Service Bus, Access Control, and a Workflow Service) and &lt;a href=&quot;http://dev.live.com/&quot;&gt;&quot;Live Services&quot;&lt;/a&gt; (higher-level end-user-meaningful services such as Contacts, Windows Live ID, and assorted Bing stuff).&lt;/p&gt;

&lt;p&gt;There&#39;s also, surprisingly, the ability to run &lt;strong&gt;unmanaged code&lt;/strong&gt; in your .NET application, AND some form of &lt;strong&gt;support for non-.NET stuff&lt;/strong&gt; via IIS&#39;s FastCGI support.&lt;/p&gt;

&lt;p&gt;Anyway, it looks like it&#39;ll be a compelling offering, competitive with EC2-hosted Windows, and thanks to SQL Data Services, much closer to allowing you to put a normal .NET web app into the cloud than AppEngine can do for Java web apps. For any organization willing to trust Microsoft with its infrastructure, Azure is a good reason to be excited. For the rest of the world, carefully executed EC2 still looks like the best bet for the Enterprise in the cloud.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/687741166758297982/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/687741166758297982' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/687741166758297982'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/687741166758297982'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/07/quick-look-at-azure-microsofts-cloud.html' title='A Quick Look at Azure, Microsoft&#39;s Cloud'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-493145342382086086</id><published>2009-06-23T09:17:00.001-04:00</published><updated>2009-06-23T09:19:59.706-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><category scheme="http://www.blogger.com/atom/ns#" term="ruby"/><title type='text'>Renum Moved to Github</title><content type='html'>&lt;p&gt;Chris O&#39;Meara recently contributed a fix to make &lt;a href=&quot;http://github.com/duelinmarkers/constantize_attribute/commit/a2b13a10a043ec69ea9a56f214554e399ca6f0ba&quot;&gt;constantize_attribute handle nil values sanely&lt;/a&gt;. (Did you know &lt;code&gt;&quot;&quot;.constantize # =&gt; Object&lt;/code&gt;? I was sure surprised.)&lt;/p&gt;

&lt;p&gt;He&#39;s using constantize_attribute to persist renum enumeration values in ActiveRecord models, which is the reason I wrote it in the first place. The reminder that these things do get used here and there inspired me to give renum a little late Spring cleaning.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/duelinmarkers/renum/tree/master&quot;&gt;Renum now lives on github&lt;/a&gt; and is set up to be gem-packaged with &lt;a href=&quot;http://github.com/technicalpickles/jeweler/tree/master&quot;&gt;jeweler&lt;/a&gt;, which is a lot more elegant than the previous solution (at least in terms of how much &lt;a href=&quot;http://github.com/duelinmarkers/renum/commit/ca0fd0841043c1ba57347972ecf2dd422a922996&quot;&gt;extra stuff&lt;/a&gt; it puts into source control). Release 1.0.2 is tagged, but it&#39;s functionally the same as 1.0.1 (it just lives in a better SCM and has less packaging cruft).&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/493145342382086086/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/493145342382086086' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/493145342382086086'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/493145342382086086'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/06/renum-moved-to-github.html' title='Renum Moved to Github'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-8181341724503601627</id><published>2009-06-19T12:07:00.005-04:00</published><updated>2009-06-19T12:13:22.401-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="clojure"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><title type='text'>clj-record now has find-by-sql</title><content type='html'>&lt;p&gt;I don&#39;t know why I didn&#39;t do this 3 months ago (or 5), but this morning I &lt;a href=&quot;http://github.com/duelinmarkers/clj-record/commit/796d7e5ed1892cbcc56b5266e1c16911d0f62935&quot;&gt;extracted find-by-sql from find-records&lt;/a&gt; so you can do arbitrary queries and still have them run through the callbacks defined for your model.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/8181341724503601627/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/8181341724503601627' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/8181341724503601627'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/8181341724503601627'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/06/clj-record-now-has-find-by-sql.html' title='clj-record now has find-by-sql'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-1024104863603310715</id><published>2009-06-05T10:54:00.001-04:00</published><updated>2009-06-05T10:54:54.217-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="rails"/><category scheme="http://www.blogger.com/atom/ns#" term="ruby"/><title type='text'>Specifying Column Order in ActiveRecord Migrations</title><content type='html'>&lt;p&gt;ActiveRecord migrations don&#39;t allow you to control column order when adding or changing columns. You can always just&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_ruby&quot;&gt;execute &lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby&quot;&gt;&quot;&lt;/span&gt;ALTER TABLE ...&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;comment comment_line comment_line_number-sign comment_line_number-sign_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby&quot;&gt;#&lt;/span&gt; but it&#39;s ugly and hard to remember the details.&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;If you&#39;re willing to give up vendor neutrality, you can have the feature in pretty-Ruby-migration syntax with a small effort. Here&#39;s an implementation for MySQL that adds options &lt;code&gt;:first&lt;/code&gt; and &lt;code&gt;:after&lt;/code&gt; to &lt;code&gt;add_column&lt;/code&gt; and &lt;code&gt;change_column&lt;/code&gt;.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_ruby&quot;&gt;&lt;span class=&quot;support support_class support_class_ruby&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_other punctuation_separator_other_ruby&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;support support_class support_class_ruby&quot;&gt;ConnectionAdapters&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_other punctuation_separator_other_ruby&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;support support_class support_class_ruby&quot;&gt;MysqlAdapter&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby&quot;&gt;.&lt;/span&gt;class_eval &lt;span class=&quot;keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby&quot;&gt;do
&lt;/span&gt;  &lt;span class=&quot;meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby&quot;&gt;&lt;span class=&quot;keyword keyword_control keyword_control_def keyword_control_def_ruby&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;entity entity_name entity_name_function entity_name_function_ruby&quot;&gt;add_column_options!&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable variable_parameter variable_parameter_function variable_parameter_function_ruby&quot;&gt;sql&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby&quot;&gt;,&lt;/span&gt; options&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;keyword keyword_control keyword_control_pseudo-method keyword_control_pseudo-method_ruby&quot;&gt;super&lt;/span&gt;
    &lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;if&lt;/span&gt; options&lt;span class=&quot;punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;after&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby&quot;&gt;]&lt;/span&gt;
      sql &lt;span class=&quot;keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_augmented keyword_operator_assignment_augmented_ruby&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby&quot;&gt;&quot;&lt;/span&gt; AFTER &lt;span class=&quot;source source_ruby source_ruby_embedded source_ruby_embedded_source&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;#{&lt;/span&gt;quote_column_name&lt;span class=&quot;punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;after&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;elsif&lt;/span&gt; options&lt;span class=&quot;punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;first&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby&quot;&gt;]&lt;/span&gt;
      sql &lt;span class=&quot;keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_augmented keyword_operator_assignment_augmented_ruby&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby&quot;&gt;&quot;&lt;/span&gt; FIRST&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;(It&#39;s also &lt;a href=&quot;http://gist.github.com/124302&quot;&gt;available in this gist&lt;/a&gt; if you&#39;d like to suggest a cleaner way.)&lt;/p&gt;

&lt;p&gt;With that in place,

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_ruby&quot;&gt;a&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby&quot;&gt;.&lt;/span&gt;add_column &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;foos&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;bar&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;after&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_separator punctuation_separator_key-value&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;baz&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

will execute

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_sql&quot;&gt;&lt;span class=&quot;meta meta_alter meta_alter_sql&quot;&gt;&lt;span class=&quot;keyword keyword_other keyword_other_create keyword_other_create_sql&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;keyword keyword_other keyword_other_table keyword_other_table_sql&quot;&gt;TABLE&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;string string_quoted string_quoted_other string_quoted_other_backtick string_quoted_other_backtick_sql&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_sql&quot;&gt;`&lt;/span&gt;foos`&lt;/span&gt; ADD &lt;span class=&quot;string string_quoted string_quoted_other string_quoted_other_backtick string_quoted_other_backtick_sql&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_sql&quot;&gt;`&lt;/span&gt;bar`&lt;/span&gt; &lt;span class=&quot;storage storage_type storage_type_sql&quot;&gt;varchar&lt;/span&gt;(&lt;span class=&quot;constant constant_numeric constant_numeric_sql&quot;&gt;255&lt;/span&gt;) AFTER &lt;span class=&quot;string string_quoted string_quoted_other string_quoted_other_backtick string_quoted_other_backtick_sql&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_sql&quot;&gt;`&lt;/span&gt;baz`&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

and

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_ruby&quot;&gt;a&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby&quot;&gt;.&lt;/span&gt;add_column &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;foos&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;bar&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;first&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_separator punctuation_separator_key-value&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant constant_language constant_language_ruby&quot;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

will execute

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_sql&quot;&gt;&lt;span class=&quot;meta meta_alter meta_alter_sql&quot;&gt;&lt;span class=&quot;keyword keyword_other keyword_other_create keyword_other_create_sql&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;keyword keyword_other keyword_other_table keyword_other_table_sql&quot;&gt;TABLE&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;string string_quoted string_quoted_other string_quoted_other_backtick string_quoted_other_backtick_sql&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_sql&quot;&gt;`&lt;/span&gt;foos`&lt;/span&gt; ADD &lt;span class=&quot;string string_quoted string_quoted_other string_quoted_other_backtick string_quoted_other_backtick_sql&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_sql&quot;&gt;`&lt;/span&gt;bar`&lt;/span&gt; &lt;span class=&quot;storage storage_type storage_type_sql&quot;&gt;varchar&lt;/span&gt;(&lt;span class=&quot;constant constant_numeric constant_numeric_sql&quot;&gt;255&lt;/span&gt;) FIRST&lt;/span&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;Love that Ruby!&lt;/p&gt;

&lt;p&gt;Now this is a monkey-patch, but as monkey-patches go it&#39;s pretty innocuous. Rather than redefining a method that&#39;s already implemented in &lt;a href=&quot;http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html&quot;&gt;MysqlAdapter&lt;/a&gt; (and as with many monkey-patches copy-pasting the existing implementation so we can modify it), we&#39;re defining a new override of a method (albeit undocumented) inherited from the &lt;a href=&quot;http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html&quot;&gt;SchemaStatements&lt;/a&gt; module (via &lt;a href=&quot;http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/AbstractAdapter.html&quot;&gt;AbstractAdapter&lt;/a&gt;). If you wanted to, you could subclass MysqlAdapter and then configure Rails to use your own adapter, but I suspect that would be more expensive to own than just adding this method to MysqlAdapter. (Beware: there are many useful Rails-provided rake tasks that look at your adapter string to decide whether to be useful or to spit in your face.)&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/1024104863603310715/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/1024104863603310715' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/1024104863603310715'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/1024104863603310715'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/06/specifying-column-order-in-activerecord.html' title='Specifying Column Order in ActiveRecord Migrations'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-8683656436271262917</id><published>2009-04-08T00:37:00.004-04:00</published><updated>2013-09-11T12:00:37.422-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="appengine"/><category scheme="http://www.blogger.com/atom/ns#" term="clojure"/><category scheme="http://www.blogger.com/atom/ns#" term="cloud"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><title type='text'>Clojure on Google AppEngine</title><content type='html'>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; This post is quite old, and Compojure has changed (and gotten quite a bit bigger) since it was written. The basic steps for setting up a Clojure GAE app haven&#39;t changed much, but the Compojure bits may no longer be so simple. You can always just AOT-compile a Clojure HttpServlet to get a web app running.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The release of Java support for Google AppEngine means more than just Java: it means lots of cool JVM languages! My personal favorite is &lt;a href=&quot;http://clojure.org/&quot;&gt;Clojure&lt;/a&gt;, so when I and several colleagues got an opportunity to try out a pre-release version (thanks to a partnership between Google and ThoughtWorks) I immediately started trying it out. It turns out to be pretty easy and pretty great.&lt;/p&gt;

&lt;p&gt;I&#39;m going to walk you through using Clojure on Google AppEngine by stepping you through the creation of a Clojure/&lt;a href=&quot;http://github.com/weavejester/compojure/tree&quot;&gt;Compojure&lt;/a&gt; version of the Guestbook application from the Getting Started section of the AppEngine docs. As we go I&#39;ll introduce some bits of &lt;a href=&quot;http://github.com/duelinmarkers/appengine-clj/tree/master&quot;&gt;appengine-clj&lt;/a&gt;, a library I extracted along the way to keep anything that felt like boiler-plate code out of my app.&lt;/p&gt;

&lt;p&gt;First you&#39;ll need to &lt;a href=&quot;http://appengine.google.com/&quot;&gt;sign up&lt;/a&gt; (if you haven&#39;t already) and download the SDK.&lt;/p&gt;

&lt;h2&gt;Creating Your Application&lt;/h2&gt;

&lt;p&gt;To start, create a project directory and the basic project directory structure documented in the getting started section of Google&#39;s documentation. Here&#39;s an overview of what to create and what will go where.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;project-root/
  src/
    [Clojure source code]
  war/
    [static files]
    WEB-INF/
      [config files]
      classes/
        [compiled classes from ../../src]
      lib/
        [jar dependencies]&lt;/pre&gt;

&lt;p&gt;Copy clojure.jar, clojure-contrib.jar, and compojure.jar into WEB-INF/lib. You&#39;ll also need to add appengine-api-XXX.jar from the SDK. I&#39;m using fairly recent trunk versions of Clojure, clojure-contrib, and Compojure. (The easiest way to make sure you have compatible versions of these is to pull the latest Compojure source from github and then download their deps.zip and build Compojure against that.) If you want to use appengine-clj you can build it yourself from &lt;a href=&quot;http://github.com/duelinmarkers/appengine-clj/tree/master&quot;&gt;source&lt;/a&gt; or &lt;a href=&quot;http://cloud.github.com/downloads/duelinmarkers/appengine-clj/appengine-clj.jar&quot;&gt;grab a prebuilt jar from the downloads section on Github&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Hello, World!&lt;/h3&gt;

&lt;p&gt;The entry point to your application will be a servlet class. Create a Clojure source file in your src directory and include a :gen-class directive to extend HttpServlet. Use Compojure&#39;s &lt;code&gt;defroutes&lt;/code&gt; and &lt;code&gt;defservice&lt;/code&gt; to create a HelloWorld.&lt;/p&gt;

src/guestbook/servlet.clj
&lt;pre class=&quot;textmate-source&quot;&gt;(ns guestbook.servlet
  (:gen-class :extends javax.servlet.http.HttpServlet)
  (:use compojure.http compojure.html))

(defroutes guestbook-app
  (GET &quot;/&quot;
    (html [:h1 &quot;Hello, World!&quot;])))

(defservice guestbook-app)&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;defroutes&lt;/code&gt; above creates a routing function that will respond to an HTML GET for the path &quot;/&quot;, and the html function converts vectors to a string of HTML. If you&#39;re not familiar with Compojure, start &lt;a href=&quot;http://en.wikibooks.org/wiki/Compojure/Getting_Started&quot;&gt;here on the Compojure wiki&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next create a web.xml with a servlet-mapping sending /* to your servlet class. Since Compojure handles URL routing, your application will have just this one mapping.&lt;/p&gt;

war/WEB-INF/web.xml
&lt;pre class=&quot;textmate-source&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;ISO-8859-1&quot;?&gt;
&amp;lt;web-app 
    xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot; 
    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd&quot;
    version=&quot;2.5&quot;&gt;
  &amp;lt;display-name&gt;Clojure Guestbook&amp;lt;/display-name&gt;
  &amp;lt;servlet&gt;
    &amp;lt;servlet-name&gt;gb&amp;lt;/servlet-name&gt;
    &amp;lt;servlet-class&gt;guestbook.servlet&amp;lt;/servlet-class&gt;
  &amp;lt;/servlet&gt;
  &amp;lt;servlet-mapping&gt;
    &amp;lt;servlet-name&gt;gb&amp;lt;/servlet-name&gt;
    &amp;lt;url-pattern&gt;/*&amp;lt;/url-pattern&gt;
  &amp;lt;/servlet-mapping&gt;
&amp;lt;/web-app&gt;&lt;/pre&gt;

&lt;p&gt;Create an appengine-web.xml, putting your application ID into the &lt;code&gt;application&lt;/code&gt; element.&lt;/p&gt;

war/WEB-INF/appengine-web.xml
&lt;pre class=&quot;textmate-source&quot;&gt;&amp;lt;appengine-web-app xmlns=&quot;http://appengine.google.com/ns/1.0&quot;&gt;
  &amp;lt;application&gt;[your application ID]&amp;lt;/application&gt;
  &amp;lt;version&gt;1&amp;lt;/version&gt;
  &amp;lt;static-files /&gt;
  &amp;lt;resource-files /&gt;
&amp;lt;/appengine-web-app&gt;&lt;/pre&gt;

&lt;p&gt;Create an ant build.xml file that compiles your application to the classes directory.&lt;/p&gt;

build.xml
&lt;pre class=&quot;textmate-source&quot;&gt;&amp;lt;project name=&quot;guestbook-clj&quot; basedir=&quot;.&quot; default=&quot;compile&quot;&gt;
  ... here you&#39;ll need to define project.classpath ...
  ... see GitHub for the full working example file ...
  &amp;lt;target name=&quot;compile&quot; depends=&quot;...&quot;&gt;
    &amp;lt;java classname=&quot;clojure.lang.Compile&quot; classpathref=&quot;project.classpath&quot; failonerror=&quot;true&quot;&gt;
      &amp;lt;classpath path=&quot;${src.dir}&quot; /&gt;
      &amp;lt;sysproperty key=&quot;clojure.compile.path&quot; value=&quot;${classes.dir}&quot; /&gt;
      &amp;lt;arg value=&quot;guestbook.servlet&quot; /&gt;
    &amp;lt;/java&gt;
  &amp;lt;/target&gt;
&amp;lt;/project&gt;&lt;/pre&gt;

&lt;p&gt;I&#39;ve left out some of the details, but you can &lt;a href=&quot;http://github.com/duelinmarkers/guestbook-clj/tree/master/build.xml&quot;&gt;find a full working version on Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At this point you should be able to run your &quot;Hello, World&quot; iteration of the application locally using the development appserver and deploy to appspot using the appcfg executable (or an Ant task if you prefer). Now it&#39;s just a matter of building your application using the tools Google AppEngine, Clojure, and Compojure make available to you.&lt;/p&gt;

&lt;h3&gt;The User Service&lt;/h3&gt;

&lt;p&gt;AppEngine has a simple API for dealing with user accounts. Let&#39;s greet logged in users by name. You&#39;ll need to import &lt;code&gt;com.google.appengine.api.users.UserServiceFactory&lt;/code&gt;.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;(ns ...
  (:import
    (com.google.appengine.api.users UserServiceFactory)))

...
  (GET &quot;/&quot;
    (let [user-service (UserServiceFactory/getUserService)
          user (.getCurrentUser user-service)]
      (html [:h1 &quot;Hello, &quot; (if user (.getNickname user) &quot;World&quot;) &quot;!&quot;]))))&lt;/pre&gt;

&lt;p&gt;But we have to let users log in to see this work. The UserService also exposes methods for creating login and logout URLs.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;...
  (GET &quot;/&quot;
    (let [user-service (UserServiceFactory/getUserService)
          user (.getCurrentUser user-service)]
      (html
        [:h1 &quot;Hello, &quot; (if user (.getNickname user) &quot;World&quot;) &quot;!&quot;]
        [:p (link-to (.createLoginURL user-service &quot;/&quot;) &quot;sign in&quot;)]
        [:p (link-to (.createLogoutURL user-service &quot;/&quot;) &quot;sign out&quot;)]))))&lt;/pre&gt;

&lt;p&gt;Now you should be able to log into your application and be greeted by name. On the dev appserver, the login page will let you provide any username and check a box to indicate whether you should be logged in as an administrator for your application. On the appspot servers, you&#39;ll get a proper-looking Google Accounts login page. The argument to &lt;code&gt;createLoginURL&lt;/code&gt; and &lt;code&gt;createLogoutURL&lt;/code&gt; is the path or URL the user should be redirected to after logging in or out.&lt;/p&gt;

I&#39;ve extracted the basic user-lookup calls into a Ring middleware function and put it into the &lt;a href=&quot;http://github.com/duelinmarkers/appengine-clj/tree/master/src/appengine_clj/users.clj&quot;&gt;appengine-clj.users&lt;/a&gt; namespace in appengine-clj. Here&#39;s what our servlet looks like using that.

&lt;pre class=&quot;textmate-source&quot;&gt;(ns guestbook.servlet
  ... you no longer need to import UserServiceFactory ...
  (:require
    [appengine-clj.users :as users]))

(defroutes guestbook-app
  (GET &quot;/&quot;
    (let [user-info (request :appengine-clj/user-info)
          user (user-info :user)]
      (html
        [:h1 &quot;Hello, &quot; (if user (.getNickname user) &quot;World&quot;) &quot;!&quot;]
        [:p (link-to (.createLoginURL (user-info :user-service) &quot;/&quot;) &quot;sign in&quot;)]
        [:p (link-to (.createLogoutURL (user-info :user-service) &quot;/&quot;) &quot;sign out&quot;)]))))

(defservice (users/wrap-with-user-info guestbook-app))&lt;/pre&gt;

&lt;p&gt;It&#39;s about the same amount of code, it just looks a little more clojurey now. (I&#39;m sorry: that&#39;s not a word.)&lt;/p&gt;

&lt;h3&gt;Datastore&lt;/h3&gt;

&lt;p&gt;Next let&#39;s collect guestbook entries and put them in the Datastore. AppEngine for Java has support for a couple of standard Java persistence APIs, JDO and JPA. But we&#39;ll use the lower-level datastore API, which seems a better fit for a dynamic language like Clojure (not to mention it doesn&#39;t require us to implement Java classes to persist).&lt;/p&gt;

&lt;p&gt;The Java API for datastore is pretty simple, but conceptually it&#39;s different enough from a SQL database that it definitely takes some getting used to. (I for one am still figuring it out.)&lt;/p&gt;

&lt;p&gt;Here&#39;s the sixty-second rundown of the bare essentials. The basic unit of persistence is the &lt;dfn&gt;Entity&lt;/dfn&gt;, which has a Map of String-keyed &lt;dfn&gt;properties&lt;/dfn&gt;. An Entity has a &lt;dfn&gt;kind&lt;/dfn&gt;, which is a string denoting the type. (But keep in mind there&#39;s no schema here, so you can give any entity any properties.) An Entity is identified by a &lt;dfn&gt;Key&lt;/dfn&gt; which is something more than a normal DBMS identifier because it can hold an Entity&#39;s association with a parent Entity. Besides using the Key, you can retrieve Entities with a &lt;dfn&gt;Query&lt;/dfn&gt;, which searches either a single kind of entity or descendent entities of a single ancestor (or both, depending on which constructor you use) and can apply simple filtering and sorting.&lt;/p&gt;

&lt;p&gt;The natural Clojurized form of an Entity seemed to be a map, so what I&#39;ve started pulling out into &lt;a href=&quot;http://github.com/duelinmarkers/appengine-clj/tree/master/src/appengine_clj/datastore.clj&quot;&gt;appengine-clj.datastore&lt;/a&gt; is functions that allow Clojure code to work with an immutable map of keyword-keyed properties (plus &lt;code&gt;:key&lt;/code&gt; and &lt;code&gt;:kind&lt;/code&gt;) and have the library take care of translating into Entity objects. Currently there are just create and find methods, since that was all the basic guestbook needed. (But you know that Internet. I&#39;ll need a delete function before the week is out.)&lt;/p&gt;

&lt;p&gt;Using appengine-clj.datastore, functions for creating and retrieving guestbook greetings are extremely simple.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;(ns guestbook.greetings
  (:require [appengine-clj.datastore :as ds])
  (:import (com.google.appengine.api.datastore Query)))

(defn create [content author]
  (ds/create {:kind &quot;Greeting&quot; :author author :content content :date (java.util.Date.)}))

(defn find-all []
  (ds/find-all (doto (Query. &quot;Greeting&quot;) (.addSort &quot;date&quot;))))&lt;/pre&gt;

&lt;p&gt;Note the creation of a &lt;code&gt;com.google.appengine.api.datastore.Query&lt;/code&gt; (in &lt;code&gt;find-all&lt;/code&gt;) that pulls back all Greetings and orders them by date. I&#39;ve considered a couple of approaches for cleaning up creation of a query from Clojure, but I haven&#39;t decided between something that looks fairly idiomatic vs something that reads just like GQL. For the time being I&#39;m sticking with the Java-interop style since even that is nicely terse and readable. Take a look at &lt;a href=&quot;http://github.com/duelinmarkers/appengine-clj/tree/master/test/appengine_clj/datastore_test.clj&quot;&gt;the tests for appengine-clj.datastore&lt;/a&gt; for more examples.&lt;/p&gt;

&lt;p&gt;Speaking of which, this is a good time to mention that writing tests for datastore code is easy with &lt;a href=&quot;http://github.com/duelinmarkers/appengine-clj/tree/master/src/appengine_clj/test_utils.clj&quot;&gt;appengine-clj.test-utils&lt;/a&gt;, which provides functions to set up an in-memory datastore. The &lt;code&gt;dstest&lt;/code&gt; macro used there creates a fresh datastore for each test. If you&#39;re using a different testing framework or prefer different scoping, you can call &lt;code&gt;ds-setup&lt;/code&gt; and &lt;code&gt;ds-teardown&lt;/code&gt; yourself. (Do keep in mind that this is the development version of datastore, so we&#39;ll all need to keep an eye out for differences between that and the real Datastore service.)&lt;/p&gt;

&lt;h3&gt;HTML and Form Handling&lt;/h3&gt;

&lt;p&gt;Now that we&#39;ve got our persistence straight (&lt;a href=&quot;http://github.com/duelinmarkers/guestbook-clj/tree/master/test/guestbook/greetings_test.clj&quot;&gt;and tested&lt;/a&gt;), let&#39;s create a UI so users can sign the guestbook. At this point it&#39;s just plain Compojure code. We&#39;ll create one route to show the guestbook and a form to enter a greeting at &quot;/&quot; and another route for saving the greeting with a POST to &quot;/sign&quot;.&lt;/p&gt;

&lt;p&gt;Here&#39;s our function for signing the guestbook.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;(defn sign-guestbook [params user]
  (greetings/create (params :content) (if user (.getNickname user)))
  (redirect-to &quot;/&quot;))&lt;/pre&gt;

&lt;p&gt;You can see there&#39;s very little to it. It takes the request parameters and a user, calls our &lt;code&gt;greetings/create&lt;/code&gt; function, and redirects back to the guestbook.&lt;/p&gt;

&lt;p&gt;The function for showing the guestbook is quite a bit more to swallow, since it includes our entire user interface.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;(defn show-guestbook [{:keys [user user-service]}]
  (let [all-greetings (greetings/find-all)]
    (html [:html [:head [:title &quot;Guestbook&quot;]]
      [:body
        (if user
          [:p &quot;Hello, &quot; (.getNickname user) &quot;! (You can &quot;
            (link-to (.createLogoutURL user-service &quot;/&quot;) &quot;sign out&quot;)
            &quot;.)&quot;]
          [:p &quot;Hello! (You can &quot;
            (link-to (.createLoginURL user-service &quot;/&quot;) &quot;sign in&quot;)
            &quot; to include your name with your greeting when you post.)&quot;])
        (if (empty? all-greetings)
          [:p &quot;The guestbook has no messages.&quot;]
          (map (fn [greeting]
            [:div
              [:p (if (greeting :author) [:strong (greeting :author)] &quot;An anonymous guest&quot;) &quot; wrote:&quot;]
              [:blockquote (h (greeting :content))]])
            all-greetings))
        (form-to [POST &quot;/sign&quot;]
          [:div (text-area &quot;content&quot; &quot;&quot;)]
          [:div (submit-button &quot;Post Greeting&quot;)])]])))&lt;/pre&gt;

&lt;p&gt;It takes the user-info map, which it destructures to grab the user and UserService. It calls our &lt;code&gt;greetings/find-all&lt;/code&gt; function to get the items to show and then uses Compojure&#39;s html helpers to create the document. For any real application you&#39;d want to break the view down into smaller pieces to avoid such a huge nested chunk of vectors (or consider using another templating library like &lt;a href=&quot;http://github.com/cgrand/enlive/tree/master&quot;&gt;Enlive&lt;/a&gt;), but for this example I think it&#39;s easier to understand what&#39;s going on with the whole page in one function.&lt;/p&gt;

&lt;p&gt;Finally here are the routes that wire it all together.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;(defroutes guestbook-app
  (POST &quot;/sign&quot;
    (sign-guestbook params ((request :appengine-clj/user-info) :user)))
  (GET &quot;/&quot;
    (show-guestbook (request :appengine-clj/user-info))))&lt;/pre&gt;

&lt;p&gt;Here I&#39;m using the &lt;code&gt;:appengine-clj/user-info&lt;/code&gt; map that&#39;s been assoc&#39;d to the request by the Ring middleware.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/duelinmarkers/guestbook-clj/tree/master/src/guestbook/servlet.clj&quot;&gt;See the entire servlet file on GitHub&lt;/a&gt;, including some enhancements for styling and to see some other code to exercise Clojure features on AppEngine.&lt;/p&gt;

&lt;h2&gt;The Big Caveat&lt;/h2&gt;

&lt;p&gt;Two unusual aspects of the Google AppEngine environment create pretty major constraints on your ability to write idiomatic Clojure.&lt;/p&gt;

&lt;p&gt;First, an AppEngine application runs in a security context that doesn&#39;t permit spawning threads, so you won&#39;t be able to use Agents, the clojure.parallel library, or Futures.&lt;/p&gt;

&lt;p&gt;Second, one of the most exciting features of AppEngine is that your application will be deployed on Google&#39;s huge infrastructure, dynamically changing its footprint depending on demand. That means you&#39;ll potentially be running on many JVMs at once. Unfortunately this is a strange fit for Clojure&#39;s concurrency features, which are most useful when you have precise control over what lives on what JVM (and simplest when everything runs on one JVM). Since shared references (Vars, Refs, and Atoms) are shared only within a single JVM, they are not suitable for many of their typical uses when running on AppEngine. You should still use Clojure&#39;s atomic references (and their associated means of modification) for any state that it makes sense to keep global per-JVM, since there may be multiple threads serving requests in one JVM. But remember JVMs will come and go during the lifetime of your application, so anything truly global should go in the Datastore or Memcache.&lt;/p&gt;

&lt;h2&gt;More to Come&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;I&#39;ll try and expand on this in the future with more write-ups, including a discussion of special handling for static files (which as of the version of the SDK I&#39;m using works great on the appspot servers even with a /* servlet mapping but not on the local dev appserver, where servlet mappings win out over static files).&lt;/li&gt;
  &lt;li&gt;If you&#39;ll &lt;a href=&quot;http://guestbook-clj.appspot.com/&quot;&gt;sign my silly little guestbook on the appspot servers&lt;/a&gt;, I&#39;d like to publish information on how many requests I got and how they performed.&lt;/li&gt;
  &lt;li&gt;Google also provides Java APIs for caching, image manipulation, making HTTP requests, and email. I haven&#39;t even scratched the surface of those yet.&lt;/li&gt;
  &lt;li&gt;With fresh support in AppEngine for scheduled tasks and upcoming support for task queues, there&#39;s more Clojure fun to be had.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;

&lt;p&gt;
  &lt;strong&gt;Update 7 September 2009:&lt;/strong&gt; 
  Late last week, &lt;a href=&quot;http://googleappengine.blogspot.com/2009/09/app-engine-sdk-125-released-for-python.html&quot;&gt;the App Engine Team released version 1.2.5 of the SDK&lt;/a&gt;, including both &lt;a href=&quot;http://code.google.com/appengine/docs/java/xmpp/&quot;&gt;XMPP (jabber instant messaging)&lt;/a&gt; support, which is brand new, and &lt;a href=&quot;http://code.google.com/appengine/docs/java/taskqueue/overview.html&quot;&gt;Task Queues&lt;/a&gt;, which had been available in the Python SDK but are now available for Java (and Clojure) applications.
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/8683656436271262917/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/8683656436271262917' title='26 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/8683656436271262917'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/8683656436271262917'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/04/clojure-on-google-appengine.html' title='Clojure on Google AppEngine'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>26</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-6773643288401623233</id><published>2009-03-28T09:26:00.000-04:00</published><updated>2009-03-28T09:27:38.511-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="clojure"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><title type='text'>clj-record Cleans Up Real Nice</title><content type='html'>&lt;div style=&quot;float:right;margin:1em;&quot;&gt;&lt;a href=&quot;http://www.flickr.com/photos/patrick_q/384925207/&quot; title=&quot;Groom by Patrick Q, on Flickr&quot;&gt;&lt;img src=&quot;http://farm1.static.flickr.com/170/384925207_5fdfdb1ced_m_d.jpg&quot; width=&quot;180&quot; height=&quot;240&quot; alt=&quot;Groom&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;Since &lt;a href=&quot;http://github.com/duelinmarkers/clj-record/tree/master&quot;&gt;clj-record&lt;/a&gt; started out as a sandbox for learning about &lt;a href=&quot;http://clojure.org/macros&quot;&gt;Clojure macros&lt;/a&gt;, the project was a bit unstructured. Particularly weird was that the root source directory was the root of the project, so if you put that on your classpath, you also ended up with whatever random stuff was checked in at the top level on your classpath (for example TODO.txt and the script for running tests). That also gave me no good place to put dependencies, so you had to cobble together the right classpath on your own based on the instructions in the README.&lt;/p&gt;

&lt;p&gt;I&#39;ve now twisted the project into something more like a normal library layout. Source is rooted in /src. Dependencies are in /lib. Extra dependencies for running tests are in /test_lib. All this gets pulled together by &lt;a href=&quot;http://ant.apache.org/&quot;&gt;Ant&lt;/a&gt;, which will run tests and spit out clj-record.jar (with &lt;code&gt;ant all&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;It was a painful moment starting work on that build.xml. Clojure would be a far better language for the build script&amp;mdash;just about any language would be better than xml&amp;mdash;but practicality won out: Clojure developers will have Ant installed. It was only a couple minutes before not having a real programming language there was an annoyance, and it will probably happen again. Hopefully once &lt;a href=&quot;http://www.pragprog.com/titles/shcloj/programming-clojure&quot;&gt;Programming Clojure&lt;/a&gt; is published, Stu Holloway will push forward with &lt;a href=&quot;http://github.com/stuarthalloway/lancet/tree/master&quot;&gt;Lancet&lt;/a&gt; and we can all convert our build.xmls into build.cljs before long.&lt;/p&gt;

&lt;p&gt;The Ant script also knows how to run a Nailgun server for &lt;a href=&quot;http://kotka.de/projects/clojure/vimclojure.html&quot;&gt;VimClojure&lt;/a&gt; (with &lt;code&gt;ant vim&lt;/code&gt;) that will have the source and tests on the classpath. (VimClojure is so badass.)&lt;/p&gt;

&lt;p&gt;Certainly there&#39;s a lot to be said for the simplicity of just putting a thing on your classpath. You can still do that, just make it the project&#39;s /src directory instead of the root. At the moment the jar dependencies are checked into git, which could turn out to be a hassle for those who&#39;d prefer to just put /src on their classpath. I could go to a separate download (as &lt;a href=&quot;https://github.com/weavejester/compojure/tree&quot;&gt;Compojure&lt;/a&gt; recently did) to keep the repository light. Let me know what you&#39;d prefer.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/6773643288401623233/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/6773643288401623233' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/6773643288401623233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/6773643288401623233'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/03/clj-record-cleans-up-real-nice.html' title='clj-record Cleans Up Real Nice'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-5897360644275080230</id><published>2009-02-27T16:52:00.000-05:00</published><updated>2009-02-27T16:53:48.550-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="clojure"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><title type='text'>Attribute Serialization for clj-record</title><content type='html'>&lt;p&gt;I&#39;ve just pushed clj-record support for attribute serialization to &lt;a href=&quot;http://github.com/duelinmarkers/clj-record/tree/master&quot;&gt;github&lt;/a&gt;. This uses clj-record callbacks and clojure.core&#39;s &lt;a href=&quot;http://clojure.org/api#toc393&quot;&gt;pr&lt;/a&gt; and &lt;a href=&quot;http://clojure.org/api#toc426&quot;&gt;read&lt;/a&gt; functions to allow you to store many standard Clojure and Java types as record attributes in varchar columns of your database.&lt;/p&gt;

&lt;p&gt;I&#39;m not crazy about calling this feature &quot;serialization&quot; since it&#39;s not using Java&#39;s serialization mechanism (though I&#39;m very happy about Clojure&#39;s fairly human-readable pr/prn/read format). If you have thoughts on a better name, let me hear them.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/5897360644275080230/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/5897360644275080230' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/5897360644275080230'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/5897360644275080230'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/02/attribute-serialization-for-clj-record.html' title='Attribute Serialization for clj-record'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-2123050408402394237</id><published>2009-02-25T07:56:00.001-05:00</published><updated>2009-02-25T07:56:38.546-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="clojure"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><title type='text'>Fancy Queries for clj-record</title><content type='html'>&lt;p&gt;Thanks to another contribution from Tunde Ashafa, clj-record now has support for more advanced queries using functions in the new clj-record.query namespace.&lt;/p&gt;

&lt;p&gt;Previously you could do this:&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_clojure&quot;&gt;&lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;manufacturer&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;find&lt;/span&gt;-&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;records&lt;/span&gt; &lt;span class=&quot;meta meta_map meta_map_clojure&quot;&gt;{:&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;grade&lt;/span&gt; &lt;span class=&quot;constant constant_numeric constant_numeric_decimal constant_numeric_decimal_clojure&quot;&gt;98&lt;/span&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;meta meta_after-expression meta_after-expression_clojure&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;comment comment_line comment_line_semicolon comment_line_semicolon_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_clojure&quot;&gt;;&lt;/span&gt; or
&lt;/span&gt;&lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;manufacturer&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;find&lt;/span&gt;-&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;records&lt;/span&gt; &lt;span class=&quot;meta meta_map meta_map_clojure&quot;&gt;{:&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_clojure&quot;&gt;&quot;&lt;/span&gt;Humedai Motors&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_clojure&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;Now you can do this:&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_clojure&quot;&gt;&lt;span class=&quot;comment comment_line comment_line_semicolon comment_line_semicolon_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_clojure&quot;&gt;;&lt;/span&gt; having required [clj-record.query :as q]
&lt;/span&gt;&lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;manufacturer&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;find&lt;/span&gt;-&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;records&lt;/span&gt; &lt;span class=&quot;meta meta_map meta_map_clojure&quot;&gt;{:&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;grade&lt;/span&gt; &lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;q/&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;greater&lt;/span&gt;-&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;than&lt;/span&gt; &lt;span class=&quot;constant constant_numeric constant_numeric_decimal constant_numeric_decimal_clojure&quot;&gt;90&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;/span&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;meta meta_after-expression meta_after-expression_clojure&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;comment comment_line comment_line_semicolon comment_line_semicolon_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_clojure&quot;&gt;;&lt;/span&gt; or
&lt;/span&gt;&lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;manufacturer&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;find&lt;/span&gt;-&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;records&lt;/span&gt; &lt;span class=&quot;meta meta_map meta_map_clojure&quot;&gt;{:&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;q/&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;like&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_clojure&quot;&gt;&quot;&lt;/span&gt;Hume%&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_clojure&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;/span&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/duelinmarkers/clj-record/blob/master/clj_record/test/query_test.clj&quot;&gt;See the tests&lt;/a&gt; for more examples of what it does. &lt;a href=&quot;http://github.com/duelinmarkers/clj-record/blob/master/clj_record/query.clj&quot;&gt;See the source&lt;/a&gt; for how it&#39;s implemented. And please: recommend improvements.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/2123050408402394237/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/2123050408402394237' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/2123050408402394237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/2123050408402394237'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/02/fancy-queries-for-clj-record.html' title='Fancy Queries for clj-record'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-7044265702529700036</id><published>2009-02-10T15:48:00.002-05:00</published><updated>2009-02-10T15:52:40.371-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="clojure"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><title type='text'>clj-record support for MySQL and PostgreSQL</title><content type='html'>&lt;p&gt;Thanks to pull requests from a couple of helpful github users (&lt;a href=&quot;http://github.com/vishnu&quot;&gt;vishnu&lt;/a&gt; and &lt;a href=&quot;http://github.com/tashafa&quot;&gt;tashafa&lt;/a&gt;), &lt;a href=&quot;http://github.com/duelinmarkers/clj-record&quot;&gt;clj-record&lt;/a&gt; should now work with both MySQL and PostgreSQL.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/7044265702529700036/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/7044265702529700036' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/7044265702529700036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/7044265702529700036'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/02/clj-record-support-for-mysql-and.html' title='clj-record support for MySQL and PostgreSQL'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-5395379437769417751</id><published>2009-02-06T13:09:00.000-05:00</published><updated>2009-02-06T13:10:03.925-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="clojure"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><title type='text'>The Ins and Outs of clj-record Validations</title><content type='html'>&lt;p&gt;One of the first features I tackled in &lt;a href=&quot;http://github.com/duelinmarkers/clj-record/tree&quot;&gt;clj-record&lt;/a&gt; was a declarative validation API. Here I&#39;ll look at how it works, both in terms of how to use it and how it&#39;s implemented.&lt;/p&gt;

&lt;h3&gt;How to declare validations&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;init-model&lt;/code&gt; form can include an option-group that opens with the keyword &lt;code&gt;:validation&lt;/code&gt; like this.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source&quot;&gt;&lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;clj-record&lt;/span&gt;.&lt;span class=&quot;meta meta_symbol&quot;&gt;core&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol&quot;&gt;init-model&lt;/span&gt;
  &lt;span class=&quot;meta meta_expression&quot;&gt;(:&lt;span class=&quot;meta meta_symbol&quot;&gt;validation&lt;/span&gt;
    &lt;span class=&quot;meta meta_expression&quot;&gt;(:&lt;span class=&quot;meta meta_symbol&quot;&gt;nickname&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin&quot;&gt;&quot;&lt;/span&gt;Nickname is required.&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; #&lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;empty&lt;/span&gt;? %)&lt;/span&gt;)&lt;/span&gt;)&lt;/span&gt;)&lt;/span&gt;)&lt;span class=&quot;meta meta_after-expression meta_after-expression_clojure&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;Following &lt;code&gt;:validation&lt;/code&gt; you provide any number of list forms with the attribute name as a keyword, the message that should be reported if the validation fails, and the function that implements the validation. It must take one argument, the value being validated, and return logical true or false. The function can be defined inline, as shown above, or a reference to a function defined elsewhere. (That is, even though &lt;code&gt;init-model&lt;/code&gt; is a macro, your code will be evaluated as you expect.)&lt;/p&gt;

&lt;p&gt;Built-in validations are provided in &lt;code&gt;&lt;a href=&quot;http://github.com/duelinmarkers/clj-record/blob/master/clj_record/validation/built_ins.clj&quot;&gt;clj-record.validation.built-ins&lt;/a&gt;&lt;/code&gt;.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source&quot;&gt;&lt;span class=&quot;comment comment_line comment_line_semicolon&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment&quot;&gt;;&lt;/span&gt; assuming you&#39;ve required [clj-record.validation.built-ins :as valid]
&lt;/span&gt;&lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;clj-record&lt;/span&gt;.&lt;span class=&quot;meta meta_symbol&quot;&gt;core&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol&quot;&gt;init-model&lt;/span&gt;
  &lt;span class=&quot;meta meta_expression&quot;&gt;(:&lt;span class=&quot;meta meta_symbol&quot;&gt;validation&lt;/span&gt;
    &lt;span class=&quot;meta meta_expression&quot;&gt;(:&lt;span class=&quot;meta meta_symbol&quot;&gt;nickname&lt;/span&gt;
      &lt;span class=&quot;string string_quoted string_quoted_double&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin&quot;&gt;&quot;&lt;/span&gt;Nickname must be three to fifteen letters.&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;valid&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;string string_regexp&quot;&gt;#&quot;^[A-Za-z]{3,15}$&quot;&lt;/span&gt;)&lt;/span&gt;)&lt;/span&gt;)&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;clj-record.validation.built-ins/match&lt;/code&gt; is a higher order function that takes a regular expression pattern and returns a validation function that passes only if the value matches the pattern (in whole or in part, so use &lt;code&gt;^&lt;/code&gt; and &lt;code&gt;$&lt;/code&gt; in your pattern if you want to match the whole string).&lt;/p&gt;

&lt;p&gt;Some of the functions in &lt;code&gt;clj-record.validation.built-ins&lt;/code&gt; are, like &lt;code&gt;match&lt;/code&gt;, higher order functions that return validation functions. Others, for example &lt;code&gt;numeric?&lt;/code&gt;, are simple validation functions that you refer to directly.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source&quot;&gt;&lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;clj-record&lt;/span&gt;.&lt;span class=&quot;meta meta_symbol&quot;&gt;core&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol&quot;&gt;init-model&lt;/span&gt;
  &lt;span class=&quot;meta meta_expression&quot;&gt;(:&lt;span class=&quot;meta meta_symbol&quot;&gt;validation&lt;/span&gt;
    &lt;span class=&quot;meta meta_expression&quot;&gt;(:&lt;span class=&quot;meta meta_symbol&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin&quot;&gt;&quot;&lt;/span&gt;Age ain&#39;t nothing but a number.&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;meta meta_symbol&quot;&gt;valid&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol&quot;&gt;numeric&lt;/span&gt;?)&lt;/span&gt;)&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;Since they&#39;re just normal functions, the built-ins can be combined just like any other functions.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source&quot;&gt;&lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;clj-record&lt;/span&gt;.&lt;span class=&quot;meta meta_symbol&quot;&gt;core&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol&quot;&gt;init-model&lt;/span&gt;
  &lt;span class=&quot;meta meta_expression&quot;&gt;(:&lt;span class=&quot;meta meta_symbol&quot;&gt;validation&lt;/span&gt;
    &lt;span class=&quot;meta meta_expression&quot;&gt;(:&lt;span class=&quot;meta meta_symbol&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin&quot;&gt;&quot;&lt;/span&gt;Age ain&#39;t nothing but a number (or nil).&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      #&lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;constant constant_language constant_language_nil&quot;&gt;nil&lt;/span&gt;? %)&lt;/span&gt; &lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;valid&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol&quot;&gt;numeric&lt;/span&gt;? %)&lt;/span&gt;)&lt;/span&gt;)&lt;/span&gt;)&lt;/span&gt;)&lt;span class=&quot;meta meta_after-expression meta_after-expression_clojure&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;h3&gt;How to run validations&lt;/h3&gt;

&lt;p&gt;ActiveRecord&#39;s &lt;a href=&quot;http://api.rubyonrails.com/classes/ActiveRecord/Validations.html&quot;&gt;validation API&lt;/a&gt; is nice looking in the normal case but conceptually a bit nasty. The &lt;code&gt;valid?&lt;/code&gt; method looks like a pure predicate, but it mutates the model behind the scenes, clearing and re-populating its errors collection each time it&#39;s called. Since immutability is highly valued in functional languages, it was clear a similar approach wouldn&#39;t be appropriate for clj-record.&lt;/p&gt;

&lt;p&gt;Model validations in clj-record are run using the &lt;code&gt;validate&lt;/code&gt; function of the model namespace, which returns a validation-result. (If you can suggest a better name for that, please do.) The result can be inspected with the predicate &lt;code&gt;clj-record.validation/valid?&lt;/code&gt; and messages for an attribute can be retrieved with &lt;code&gt;clj-record.validation/messages-for&lt;/code&gt;, which takes the attribute name keyword and returns a collection of messages.&lt;/p&gt;

&lt;h3&gt;How validations work&lt;/h3&gt;

&lt;p&gt;The details of how validations are implemented shouldn&#39;t be relevant for normal use, but since clj-record is very young, it&#39;s pretty likely you&#39;ll need to look at the internals. If you&#39;re new to Clojure (or LISP macros in general) perhaps you&#39;ll also find this a useful case-study. I&#39;m also interested in suggestions for improvements (to both the internals and the public-facing API).&lt;/p&gt;

&lt;p&gt;First let&#39;s look at how option-groups in the &lt;code&gt;init-model&lt;/code&gt; form work. The &lt;code&gt;init-model&lt;/code&gt; macro uses the opening keyword of each option-group to look up a namespace. The validation option-group opens with &lt;code&gt;:validation&lt;/code&gt;, so the namespace will be &lt;code&gt;clj-record.validation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It then calls a function in that namespace called &lt;code&gt;expand-init-option&lt;/code&gt; once for each option-form in the option-group. (In the first example above, the only option-form is &quot;&lt;code&gt;(:nickname &quot;Nickname is required.&quot; #(not (empty? %)))&lt;/code&gt;,&quot; but there can be more.) &lt;code&gt;expand-init-option&lt;/code&gt; takes as arguments the model name (as a string) followed by whatever appeared in the option-form and returns a form that will appear in the expansion of &lt;code&gt;init-model&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As you can guess from the examples above, &lt;code&gt;clj-record.validation/expand-init-option&lt;/code&gt; takes as arguments the model-name followed by the (unevaluated) attribute name, message, and validation function. It returns a syntax-quoted form that calls &lt;code&gt;clj-record.validation/add-validation&lt;/code&gt; with those same arguments verbatim, which in turn adds a validation to the (mutable) model metadata of your model.&lt;/p&gt;

&lt;p&gt;So this:&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source&quot;&gt;&lt;span class=&quot;comment comment_line comment_line_semicolon&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment&quot;&gt;;&lt;/span&gt; in a model namespace called com.example.model.foo
&lt;/span&gt;&lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;clj-record&lt;/span&gt;.&lt;span class=&quot;meta meta_symbol&quot;&gt;core&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol&quot;&gt;init-model&lt;/span&gt;
  &lt;span class=&quot;meta meta_expression&quot;&gt;(:&lt;span class=&quot;meta meta_symbol&quot;&gt;validation&lt;/span&gt;
    &lt;span class=&quot;meta meta_expression&quot;&gt;(:&lt;span class=&quot;meta meta_symbol&quot;&gt;nickname&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin&quot;&gt;&quot;&lt;/span&gt;Nickname is required.&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; #&lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;empty&lt;/span&gt;? %)&lt;/span&gt;)&lt;/span&gt;)&lt;span class=&quot;meta meta_after-expression meta_after-expression_clojure&quot;&gt;
&lt;/span&gt;&lt;/span&gt;    &lt;span class=&quot;meta meta_expression&quot;&gt;(:&lt;span class=&quot;meta meta_symbol&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin&quot;&gt;&quot;&lt;/span&gt;Age ain&#39;t nothing but a number.&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;meta meta_symbol&quot;&gt;valid&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol&quot;&gt;numeric&lt;/span&gt;?)&lt;/span&gt;)&lt;/span&gt;)&lt;span class=&quot;meta meta_after-expression meta_after-expression_clojure&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;is equivalent to this:&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source&quot;&gt;&lt;span class=&quot;comment comment_line comment_line_semicolon&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_comment&quot;&gt;;&lt;/span&gt; in a model namespace called com.example.model.foo
&lt;/span&gt;&lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;clj-record&lt;/span&gt;.&lt;span class=&quot;meta meta_symbol&quot;&gt;core&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol&quot;&gt;init-model&lt;/span&gt;)&lt;span class=&quot;meta meta_after-expression meta_after-expression_clojure&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;clj-record&lt;/span&gt;.&lt;span class=&quot;meta meta_symbol&quot;&gt;validation&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol&quot;&gt;add-validation&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin&quot;&gt;&quot;&lt;/span&gt;foo&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  :&lt;span class=&quot;meta meta_symbol&quot;&gt;nickname&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin&quot;&gt;&quot;&lt;/span&gt;Nickname is required.&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; #&lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;empty&lt;/span&gt;? %)&lt;/span&gt;)&lt;/span&gt;)&lt;span class=&quot;meta meta_after-expression meta_after-expression_clojure&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;meta meta_expression&quot;&gt;(&lt;span class=&quot;meta meta_symbol&quot;&gt;clj-record&lt;/span&gt;.&lt;span class=&quot;meta meta_symbol&quot;&gt;validation&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol&quot;&gt;add-validation&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin&quot;&gt;&quot;&lt;/span&gt;foo&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  :&lt;span class=&quot;meta meta_symbol&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin&quot;&gt;&quot;&lt;/span&gt;Age ain&#39;t nothing but a number.&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;meta meta_symbol&quot;&gt;valid&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol&quot;&gt;numeric&lt;/span&gt;?)&lt;span class=&quot;meta meta_after-expression meta_after-expression_clojure&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;since the &lt;code&gt;init-model&lt;/code&gt; macro will expand to a form including those exact &lt;code&gt;add-validation&lt;/code&gt; calls.&lt;/p&gt;

&lt;p&gt;Notice that very little work is happening in the macro-expansion. Keeping the macro layer very thin yields a number of benefits.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;First, it makes it trivial to allow references in the option-forms to work as you&#39;d expect. In an earlier version of the implementation, model-metadata was built up at macro-expansion time, but it turned out I had to jump through extra &lt;code&gt;eval&lt;/code&gt; hoops to get symbols to resolve to the right thing.&lt;/li&gt;
  &lt;li&gt;Second, it keeps things working with AOT (ahead-of-time compilation). After jumping through the &lt;code&gt;eval&lt;/code&gt; hoops, I decided to test a pre-compiled model class and realized that the in-memory model-metadata built up at macro-expansion time didn&#39;t exist, because macros had been expanded back when I compiled. Oops!&lt;/li&gt;
  &lt;li&gt;Third, it makes testing much easier. I can define a funny validation for just one test and don&#39;t have to worry about keeping it passing in other tests. At the end of a test I just repoint a mutable ref back at the old value, and the model-metadata is back to its original shape. (I actually haven&#39;t yet used that technique for validation tests but will probably start soon. I do already use it in the &lt;a href=&quot;http://github.com/duelinmarkers/clj-record/blob/master/clj_record/test/callbacks_test.clj&quot;&gt;tests for callbacks&lt;/a&gt;.)&lt;/li&gt;
  &lt;li&gt;Finally, it reduces conceptual overhead. Macros are complicated. Anything you can do to make sure macro-expansion is just a simple conversion into something unmagical helps reduce head scratching.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Internally the validations are stored in model-metadata as a vector, since I wanted to run them in the order they&#39;re defined (for no real reason other than predictability). The validation result returned by validate is just a clojure hash map, but I provided &lt;code&gt;valid?&lt;/code&gt; and &lt;code&gt;messages-for&lt;/code&gt; so the implementation is abstracted. (Currently &lt;code&gt;valid?&lt;/code&gt; is just &lt;code&gt;empty?&lt;/code&gt; and &lt;code&gt;messages-for&lt;/code&gt; is just &lt;code&gt;get&lt;/code&gt;, but perhaps they&#39;ll get fancier later.)&lt;/p&gt;

&lt;p&gt;So much for validations. Next week, maybe callbacks ... or maybe I&#39;ll actually do some coding.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/5395379437769417751/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/5395379437769417751' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/5395379437769417751'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/5395379437769417751'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/02/ins-and-outs-of-clj-record-validations.html' title='The Ins and Outs of clj-record Validations'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-5470264375249374447</id><published>2009-01-24T14:52:00.002-05:00</published><updated>2009-01-24T14:55:54.532-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bdd"/><category scheme="http://www.blogger.com/atom/ns#" term="ruby"/><title type='text'>RSpec&#39;s New Implicit Subjects</title><content type='html'>&lt;p&gt;In case you missed &lt;a href=&quot;http://blog.davidchelimsky.net/2009/1/13/rspec-1-1-12-is-released&quot;&gt;the announcement&lt;/a&gt;, RSpec now supports this badassery.&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_ruby source_ruby_rails&quot;&gt;describe &lt;span class=&quot;variable variable_other variable_other_constant variable_other_constant_ruby&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby&quot;&gt;do
&lt;/span&gt;  describe &lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby&quot;&gt;&quot;&lt;/span&gt;born 19 years ago&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby&quot;&gt;do
&lt;/span&gt;    subject &lt;span class=&quot;punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block&quot;&gt; &lt;/span&gt;&lt;span class=&quot;support support_class support_class_ruby&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;birthdate&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_separator punctuation_separator_key-value&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant constant_numeric constant_numeric_ruby&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby&quot;&gt;.&lt;/span&gt;years&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby&quot;&gt;.&lt;/span&gt;ago &lt;span class=&quot;punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby&quot;&gt;}&lt;/span&gt;
    it &lt;span class=&quot;punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block&quot;&gt; &lt;/span&gt;should be_eligible_to_vote &lt;span class=&quot;punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby&quot;&gt;}&lt;/span&gt;
    it &lt;span class=&quot;punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block&quot;&gt; &lt;/span&gt;should be_eligible_to_enlist &lt;span class=&quot;punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby&quot;&gt;}&lt;/span&gt;
    it &lt;span class=&quot;punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block&quot;&gt; &lt;/span&gt;should_not be_eligible_to_drink &lt;span class=&quot;punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/5470264375249374447/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/5470264375249374447' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/5470264375249374447'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/5470264375249374447'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/01/rspecs-new-implicit-subjects.html' title='RSpec&#39;s New Implicit Subjects'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-5859988369563576764</id><published>2009-01-19T15:39:00.003-05:00</published><updated>2009-01-19T15:51:58.478-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="clojure"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><title type='text'>clj-record: ActiveRecord for Clojure</title><content type='html'>&lt;p&gt;I&#39;ve spent a chunk of the last month (and an absurd amount of the last four days) working on &lt;a href=&quot;http://github.com/duelinmarkers/clj-record/tree/master&quot;&gt;clj-record&lt;/a&gt;: an ActiveRecord-like library written in the &lt;a href=&quot;http://www.clojure.org/&quot;&gt;Clojure&lt;/a&gt; programming language. (Clojure is a LISP dialect with &lt;a href=&quot;http://clojure.org/concurrent_programming&quot;&gt;badass concurrency features&lt;/a&gt; for the JVM.) clj-record aims to provide an idiomatic &quot;functional&quot; API for persistence with many of the features we&#39;ve come to appreciate from ActiveRecord, currently including validation, (somewhat) convenient associations, and (some) callbacks.&lt;/p&gt;

&lt;p&gt;(Persistence requires side-effects, so it can&#39;t really be functional, but it can be idiomatic and leverage a great deal of power from being written in a functional language.)&lt;/p&gt;

&lt;p&gt;Here&#39;s what a model definition looks like:&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;source source_clojure&quot;&gt;&lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;ns&lt;/span&gt; &lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;clj&lt;/span&gt;-&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;record&lt;/span&gt;.&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;test&lt;/span&gt;.&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;model&lt;/span&gt;.&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;manufacturer&lt;/span&gt;
  &lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;:&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;clj&lt;/span&gt;-&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;record&lt;/span&gt;.&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;boot&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;meta meta_after-expression meta_after-expression_clojure&quot;&gt;
&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;clj&lt;/span&gt;-&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;record&lt;/span&gt;.&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;core&lt;/span&gt;/&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;init&lt;/span&gt;-&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;model&lt;/span&gt;
  &lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;:&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;associations&lt;/span&gt;
    &lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;has&lt;/span&gt;-&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;many&lt;/span&gt; &lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;products&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;meta meta_after-expression meta_after-expression_clojure&quot;&gt;
&lt;/span&gt;&lt;/span&gt;  &lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;:&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;validation&lt;/span&gt;
    &lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;:&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_clojure&quot;&gt;&quot;&lt;/span&gt;empty!&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_clojure&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; #&lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;empty&lt;/span&gt;? %&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;meta meta_after-expression meta_after-expression_clojure&quot;&gt;
&lt;/span&gt;&lt;/span&gt;    &lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;:&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;founded&lt;/span&gt; &lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_clojure&quot;&gt;&quot;&lt;/span&gt;must be numeric&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_clojure&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; #&lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant constant_language constant_language_nil constant_language_nil_clojure&quot;&gt;nil&lt;/span&gt;? %&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;meta meta_expression meta_expression_clojure&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_begin punctuation_section_expression_begin_clojure&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;re&lt;/span&gt;-&lt;span class=&quot;meta meta_symbol meta_symbol_clojure&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;string string_regexp string_regexp_clojure&quot;&gt;#&quot;\D&quot;&lt;/span&gt; %&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_expression punctuation_section_expression_end punctuation_section_expression_end_clojure&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;Coming soon:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Even more convenient associations&lt;/li&gt;
  &lt;li&gt;Even more callbacks&lt;/li&gt;
  &lt;li&gt;Serialization of data structures as attribute values&lt;/li&gt;
  &lt;li&gt;A fancy query API&lt;/li&gt;
  &lt;li&gt;Other cool stuff&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/duelinmarkers/clj-record/tree/master&quot;&gt;Check it out!&lt;/a&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/5859988369563576764/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/5859988369563576764' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/5859988369563576764'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/5859988369563576764'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/01/clj-record-activerecord-for-clojure.html' title='clj-record: ActiveRecord for Clojure'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23529072678094516.post-7400205924795857599</id><published>2009-01-16T15:03:00.001-05:00</published><updated>2009-01-16T15:04:51.406-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby"/><category scheme="http://www.blogger.com/atom/ns#" term="tools"/><title type='text'>Ack in Project Skipping Rake</title><content type='html'>&lt;p&gt;&lt;a href=&quot;http://github.com/protocool/ack-tmbundle/tree/master&quot;&gt;Ack in Project&lt;/a&gt; is probably the &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; bundle I use the most (after the Ruby bundle, I suppose, which I constantly use without even thinking about it). If you haven&#39;t already, you should install it immediately for very fast project searches (and recursive searches of just the selected directory, which on its own is hugely useful).&lt;/p&gt;

&lt;p&gt;What it took me a while to realize, though, is that my project&#39;s &lt;a href=&quot;http://rake.rubyforge.org/&quot;&gt;Rake&lt;/a&gt; files weren&#39;t being searched. It turns out this is because &lt;a href=&quot;http://petdance.com/ack/&quot;&gt;Ack&lt;/a&gt; will by default search every file of a known type and ignore everything else. Ack knows Ruby as .rb, .rhtml, .rjs, .rxml and .erb, but that&#39;s all by default. (You can see what extensions are associated with what types from the command line with &lt;code&gt;ack --help types&lt;/code&gt;. This assumes you&#39;ve got ack installed on your path somewhere.)&lt;/p&gt;

&lt;p&gt;To teach Ack about new extensions or entirely new types, create an .ackrc file in your home directory and fill it with things like this:&lt;/p&gt;

&lt;pre class=&quot;textmate-source&quot;&gt;&lt;span class=&quot;text text_plain&quot;&gt;&lt;span class=&quot;meta meta_paragraph meta_paragraph_text&quot;&gt;# Add the .rake extension to the existing ruby type.
--type-add
ruby=.rake
&lt;/span&gt;
&lt;span class=&quot;meta meta_paragraph meta_paragraph_text&quot;&gt;# Create a new type for Clojure source files.
--type-set
clojure=.clj&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;(&lt;code&gt;type-set&lt;/code&gt; can also be used to completely replace a built in type definition. Read about this and all sorts of other ack options from the command line with &lt;code&gt;ack --man&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;In addition to getting .ackrc in place, you also have to tell the TextMate bundle that you want to use it. From inside TextMate do an Ack in Project, hit the &quot;Advanced Options&quot; twisty, and check &quot;load defaults from .ackrc.&quot; You&#39;re all set.&lt;/p&gt;

&lt;p&gt;It may have occurred to you that your top-level Rakefile doesn&#39;t have an extension. Likewise for the executables in your Rails projects&#39; script directory. And your Capfile.&lt;/p&gt;

&lt;p&gt;Damn.&lt;/p&gt;

&lt;p&gt;Luckily, Ack knows unix-ey people like dropping extensions from executables and using a shebang line to tell the shell how to run them. So any file with an unrecognized extension (or none at all) will be checked for a shebang line that might qualify it for the perl, ruby, php, python, shell or xml types!&lt;/p&gt;

&lt;p&gt;So that takes care of RAILS_ROOT/script/*. As far as Rakefile and Capfile ... well, the shebang line would really be a lie, since they&#39;re not executable without the supporting Rake and Capistrano libraries loaded, but the workaround of adding the shebang will get them into your Ack results. I leave it to you to decide whether the lying shebang or excluded files is a lesser evil, and promise to let you know if I find a better way to get Ack to consider them Ruby.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://elhumidor.blogspot.com/feeds/7400205924795857599/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/23529072678094516/7400205924795857599' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/7400205924795857599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23529072678094516/posts/default/7400205924795857599'/><link rel='alternate' type='text/html' href='http://elhumidor.blogspot.com/2009/01/ack-in-project-skipping-rake.html' title='Ack in Project Skipping Rake'/><author><name>John Hume</name><uri>http://www.blogger.com/profile/00164870439569642842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://static.flickr.com/1/buddyicons/50377630@N00.jpg?1096547663'/></author><thr:total>3</thr:total></entry></feed>