<?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: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-9905395</id><updated>2011-12-12T05:47:21.047-07:00</updated><category term='shoes'/><category term='ruby'/><category term='haml'/><category term='debug'/><category term='LaTeX geek'/><category term='scala'/><category term='java'/><category term='clojure'/><category term='erlang'/><category term='web'/><category term='bonehead'/><category term='ajax'/><category term='vmware'/><category term='apple'/><category term='metaprogramming'/><category term='implementation'/><category term='lisp'/><category term='language'/><category term='geek'/><category term='bash'/><category term='lift'/><category term='misc'/><category term='interpreter'/><category term='fxruby'/><category term='iphone'/><category term='pragmatic'/><category term='rails'/><category term='antlr'/><category term='slicehost'/><category term='mac'/><category term='IE'/><category term='eclipse'/><category term='jruby'/><category term='clj-json'/><category term='prototype'/><category term='json'/><category term='rant'/><category term='compiler'/><category term='humor'/><title type='text'>David Rupp's Blog</title><subtitle type='html'>Ruby, iOS Development, and Languages in General</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>77</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-9905395.post-7006637407817080197</id><published>2011-12-10T18:24:00.006-07:00</published><updated>2011-12-10T18:39:29.588-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='clj-json'/><category scheme='http://www.blogger.com/atom/ns#' term='clojure'/><category scheme='http://www.blogger.com/atom/ns#' term='json'/><title type='text'>clj-json breaks when running 'lein javac'</title><content type='html'>I'm working on a potential patch to Mark McGranaghan's excellent &lt;code&gt;clj-json&lt;/code&gt; library for converting Clojure data to JSON. Unfortunately, I can't test my changes right now, because of an incompatibility between Leiningen version 1.6.2 and &lt;code&gt;clj-json&lt;/code&gt; version 0.4.3, which causes &lt;code&gt;lein javac&lt;/code&gt; to throw an exception. &lt;br /&gt;&lt;br /&gt;It looks like &lt;code&gt;clj-json&lt;/code&gt; has a dev dependency on an old version of a &lt;code&gt;lein-javac&lt;/code&gt; plugin for Leiningen, which itself depends on &lt;code&gt;ant-launcher&lt;/code&gt; version 1.6.5. It also looks like modern &lt;code&gt;lein&lt;/code&gt; is smart enough to use its built-in &lt;code&gt;javac&lt;/code&gt; task in preference to the old plugin, but it gets tripped up on the old version of &lt;code&gt;ant-launcher.jar&lt;/code&gt;. The specific exception is:&lt;pre&gt;Exception in thread "main" java.lang.NoSuchMethodError: \&lt;br /&gt;org.apache.tools.ant.launch.Locator.fromJarURI(Ljava/lang/String;)\&lt;br /&gt;Ljava/lang/String; (NO_SOURCE_FILE:0)&lt;/pre&gt;&lt;br /&gt;I've &lt;a href="https://github.com/mmcgrana/clj-json/issues/14"&gt;submitted an issue&lt;/a&gt; on the &lt;code&gt;clj-json&lt;/code&gt; project, but for now a simple workaround is to delete the dev dependency from &lt;code&gt;clj-json&lt;/code&gt;'s &lt;code&gt;project.clj&lt;/code&gt; file, remove everything from its &lt;code&gt;lib/&lt;/code&gt; directory, and run &lt;code&gt;lein deps&lt;/code&gt; to refresh dependencies.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-7006637407817080197?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/7006637407817080197/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=7006637407817080197' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7006637407817080197'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7006637407817080197'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2011/12/clj-json-breaks-when-running-lein-javac.html' title='clj-json breaks when running &apos;lein javac&apos;'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-8612172757735677593</id><published>2011-11-13T16:58:00.002-07:00</published><updated>2011-11-13T17:02:20.669-07:00</updated><title type='text'>Pro Tip: Fullscreen-ness of Apps Seems to be Sticky in Mac OS Lion</title><content type='html'>I've had a lot of fun the past couple days working with the Terminal app in fullscreen mode. Having entered fullscreen, I can work without distraction, but I can also switch freely back to my desktop or other running apps. Best of all: when I quit Terminal and restart it, it automatically restarts in fullscreen mode. Good job, Apple!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-8612172757735677593?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/8612172757735677593/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=8612172757735677593' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8612172757735677593'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8612172757735677593'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2011/11/pro-tip-fullscreen-ness-of-apps-seems.html' title='Pro Tip: Fullscreen-ness of Apps Seems to be Sticky in Mac OS Lion'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-8625349523100347229</id><published>2011-11-11T09:30:00.003-07:00</published><updated>2011-11-11T09:43:58.532-07:00</updated><title type='text'>Installing Java on AWS EC2 Instances with Pallet</title><content type='html'>I have recently been spinning up a lot of t1.micro instances on Amazon's EC2 service. I'm deliberately using minimal / default images, and I need to install Java on them from scratch. There have been reports (and I have experienced myself) that the Java install process hangs indefinitely when installing either Oracle's JRE/JDK or OpenJDK on a t1.micro running Ubuntu. I have been able to install Java successfully by explicitly requiring a 64-bit version of the OS.&lt;br /&gt;&lt;br /&gt;Here's how you can do that in your Pallet node-spec:&lt;br /&gt;&lt;script src="https://gist.github.com/1358467.js?file=pallet_64bit_ubuntu.clj"&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-8625349523100347229?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/8625349523100347229/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=8625349523100347229' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8625349523100347229'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8625349523100347229'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2011/11/installing-java-on-aws-ec2-instances.html' title='Installing Java on AWS EC2 Instances with Pallet'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-3336762069596064318</id><published>2011-10-25T17:35:00.002-06:00</published><updated>2011-10-25T17:46:56.001-06:00</updated><title type='text'>Starting an EC2 Instance with an Admin User Using Pallet</title><content type='html'>At &lt;a href="http://lonocloud.com/"&gt;my new day job&lt;/a&gt; today, I learned the hard way that if you're using Pallet to create an EC2 instance, and you want to use its &lt;code&gt;automated-admin-user&lt;/code&gt; crate (which authorizes the user running the crate to login on the new instance via SSH using your RSA key), you really *really* want to run that crate during the &lt;code&gt;:bootstrap&lt;/code&gt; phase. Running during the &lt;code&gt;:configure&lt;/code&gt; phase does not seem happy using my current configuration of Pallet version 0.6.4 and jclouds version 1.0.0. Here's the relevant Clojure snippet:&lt;pre&gt;(require 'pallet.core)&lt;br /&gt;(require 'pallet.phase)&lt;br /&gt;(require 'pallet.crate.automated-admin-user)&lt;br /&gt;&lt;br /&gt;(def server-spec&lt;br /&gt;  (pallet.core/server-spec&lt;br /&gt;   :node-spec node-spec&lt;br /&gt;   :phases {:bootstrap (pallet.phase/phase-fn (pallet.crate.automated-admin-user/automated-admin-user))}))&lt;/pre&gt;P.S.: &lt;a href="http://lonocloud.com"&gt;LonoCloud&lt;/a&gt; is hiring Clojure developers! Join us!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-3336762069596064318?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/3336762069596064318/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=3336762069596064318' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3336762069596064318'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3336762069596064318'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2011/10/starting-ec2-instance-with-admin-user.html' title='Starting an EC2 Instance with an Admin User Using Pallet'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-6476101214808633685</id><published>2011-07-22T20:29:00.002-06:00</published><updated>2011-07-22T20:39:48.827-06:00</updated><title type='text'>Implementing Continuations in iOS Using Blocks</title><content type='html'>Came up with a neat use for blocks in iOS at &lt;a href="http://thoughtworks.com"&gt;my day job&lt;/a&gt; today: continuations!&lt;br /&gt;&lt;br /&gt;Like this: I'm working on a native iOS app that occasionally shows data from a Rails website in a &lt;code&gt;UIWebView&lt;/code&gt;. When it does so, it has to make sure the user of the iOS app is authenticated to the web app. This way we should never have to see the web app's login page in the native app, which has its own login screen.&lt;br /&gt;&lt;br /&gt;Of course, all of this reeks of asynchronicity. Which usually means callbacks. Which usually means implementing one or twelve protocols. We've tried to encapsulate all of the authentication logic in a custom class that's used by the controller that presents the &lt;code&gt;UIWebView&lt;/code&gt;s, but we've also recently come up with a need for multiple controllers, controlling multiple web views. And the code is already (ahem) not as attractive as it could be.&lt;br /&gt;&lt;br /&gt;Our original workflow looks something like this:&lt;br /&gt;&lt;br /&gt;* Render a &lt;code&gt;UIWebView&lt;/code&gt; and invoke its &lt;code&gt;loadRequest:&lt;/code&gt; method with some URL.&lt;br /&gt;* Have the controller implement the &lt;code&gt;UIWebViewDelegate&lt;/code&gt; protocol.&lt;br /&gt;** In particular, implement &lt;code&gt;webView:shouldStartLoadWithRequest:navigationType:&lt;/code&gt;.&lt;br /&gt;** Ask the authenticator object if we need to refresh authentication.&lt;br /&gt;*** If not, just load the request (happy path).&lt;br /&gt;*** If so, tell the authenticator object to refresh authentication and return NO.&lt;br /&gt;*** This may require collecting new authentication credentials from the user, which means another controller and view (and asynchronous request cycle) entirely.&lt;br /&gt;*** It certainly requires a separate asynchronous call to the web app, to verify authentication and set the timeout for this session.&lt;br /&gt;*** Oh yeah -- we need to have remembered the original request and restart it when the dust settles.&lt;br /&gt;* Insanity ensues, in the form of delegates with delegates and very hard-to-follow callback trails and state management.&lt;br /&gt;&lt;br /&gt;Enter: blocks.&lt;br /&gt;&lt;br /&gt;With blocks we can streamline, thusly (starting with &lt;code&gt;webView:shouldStartLoadWithRequest:...&lt;/code&gt;):&lt;br /&gt;&lt;br /&gt;* Tell the authenticator object to make sure we're authenticated, passing in a block to execute once we are.&lt;br /&gt;** This block closes over all the state we need to "do the next thing". It can refer to methods in the current controller and access the current controller's instance variables, even though it'll be executed by the authenticator object.&lt;br /&gt;* If the authenticator decides we're already authenticated, it simply executes the block (the happy path).&lt;br /&gt;* If we're not authenticated, but we already know our credentials, open a &lt;code&gt;NSURLConnection&lt;/code&gt; to the web app, using the usual callbacks. When that connection finishes successfully, execute the block we were given from the original controller.&lt;br /&gt;* If we're not authenticated and we need new credentials, push the login screen controller on the stack, passing *it* a block that does the &lt;code&gt;NSURLConnection&lt;/code&gt; stuff, followed by executing the block from the original controller.&lt;br /&gt;* The login controller presents a modal dialog, accepts the user's input for authentication credentials, then calls its block (the one passed from the previous step, which encapsulates "what to do next" at two levels).&lt;br /&gt;&lt;br /&gt;When we can implement this strategy pervasively, every entity will simply ask its collaborators to do whatever they need to do, however they need to do it (including asynchronously), and know that once the task of e.g., login, is accomplished, whatever is supposed to happen next actually does. &lt;br /&gt;&lt;br /&gt;This idea was inspired by Continuation Passing Style in compiler theory, in which every function is called with the data it needs to perform its own business, *plus* information about what to do next. When programs are expressed in this style, there is no recursion, no nesting of routines; there is only "do what you're supposed to do, then jump to the next thing". It's not a perfect analogy, but it has been surprisingly effective in making some pretty complex and ugly code easier to understand.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-6476101214808633685?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/6476101214808633685/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=6476101214808633685' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6476101214808633685'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6476101214808633685'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2011/07/implementing-continuations-in-ios-using.html' title='Implementing Continuations in iOS Using Blocks'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-8649376139867379049</id><published>2011-07-22T19:16:00.001-06:00</published><updated>2011-07-22T19:18:59.506-06:00</updated><title type='text'>You're Going Down, Emacs!</title><content type='html'>Time to get serious, yo:&lt;pre&gt;[david@davids-MacBook-Air-3 ~]$ cat .aliases&lt;br /&gt;alias vi='emacs'&lt;br /&gt;alias vim='emacs'&lt;br /&gt;alias nano='emacs'&lt;br /&gt;alias mate='emacs'&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-8649376139867379049?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/8649376139867379049/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=8649376139867379049' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8649376139867379049'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8649376139867379049'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2011/07/youre-going-down-emacs.html' title='You&apos;re Going Down, Emacs!'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-4358929652865953129</id><published>2011-07-16T11:56:00.004-06:00</published><updated>2011-07-16T12:32:04.163-06:00</updated><title type='text'>iOS Pro Tip: Know Thine API!</title><content type='html'>I was reading through some code at my current project (P.S.: &lt;a href="http://thoughtworks.com"&gt;ThoughtWorks&lt;/a&gt; is hiring! Contact me if you want to submit a résumé!), when a good old-fashioned C-style &lt;code&gt;for&lt;/code&gt; loop caught my eye. You know the kind:&lt;pre&gt;for (int i = 0; i &lt; someArray.length; i++) {&lt;br /&gt;  // do something with someArray[i]&lt;br /&gt;}&lt;/pre&gt;It turns out that if &lt;code&gt;someArray&lt;/code&gt; is an &lt;code&gt;NSArray&lt;/code&gt;, there is API defined to help with this:&lt;pre&gt;[someArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {&lt;br /&gt;  // do something with obj&lt;br /&gt;  // set stop to true if you want to skip the remaining objects for some reason&lt;br /&gt;}];&lt;/pre&gt;Note: Yes, I'm aware that there is also &lt;span style="font-weight:bold;"&gt;syntax&lt;/span&gt; for this in Objective-C 2.0, in the form of fast enumeration. The code I was converting was specifically interested in returning the index of a particular object in the array, which is why I went with this form.&lt;br /&gt;&lt;br /&gt;Anyway, further reading in the &lt;code&gt;NSArray&lt;/code&gt; API led me to another fun method: &lt;code&gt;indexOfObjectPassingTest&lt;/code&gt;. It also takes a block, which returns a boolean value indicating whether or not the current object passes the test. If it does, you get the index back and move on with your life. This turned out to be exactly what I needed to port the original bit of code to full modernity.&lt;br /&gt;&lt;br /&gt;I also came across an example of code that was using &lt;code&gt;NSString +stringWithFormat&lt;/code&gt;. It would create the original format string, conditionally modify it based on the presence of some optional data, then pass all available data in to format the final string. Something like this:&lt;pre&gt;NSString *formatString = @"required: %@";&lt;br /&gt;if (optionalData) {&lt;br /&gt;  formatString = [formatString stringByAppendingString:@", optional: %@"];&lt;br /&gt;}&lt;br /&gt;NSString *result = [NSString stringWithFormat:formatString, requiredData, optionalData];&lt;/pre&gt;This seemed potentially error-prone to me; I didn't like the idea of always passing in the (potentially nil) optional data, even if I was convinced there would be no placeholder for it in the format string.&lt;br /&gt;&lt;br /&gt;A quick read through the &lt;code&gt;NSString&lt;/code&gt; class reference led to the instance method &lt;code&gt;stringByAppendingFormat&lt;/code&gt;. This allowed for a workflow more like the following:&lt;pre&gt;NSString *result = [NSString stringWithFormat:@"required: %@", requiredData];&lt;br /&gt;if (optionalData) {&lt;br /&gt;  result = [result stringByAppendingFormat:@", and optional:%@", optionalData];&lt;br /&gt;}&lt;/pre&gt;This way I did not have to dance around getting the format string just right before populating it; I could tack on the extra bits on the fly, which just seems cleaner.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-4358929652865953129?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/4358929652865953129/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=4358929652865953129' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4358929652865953129'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4358929652865953129'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2011/07/ios-pro-tip-know-thine-api.html' title='iOS Pro Tip: Know Thine API!'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-6778955305371904012</id><published>2011-07-03T17:51:00.006-06:00</published><updated>2011-07-05T09:29:28.811-06:00</updated><title type='text'>iOS Pro Tip: textFieldShouldClear</title><content type='html'>If you're using &lt;code&gt;UITextFieldDelegate&lt;/code&gt; to manage the state of a save button (e.g., to disable saving if a field is empty), you're probably implementing the &lt;code&gt;textField:shouldChangeCharactersInRange:replacementString:&lt;/code&gt; delegate method and checking for the field being empty. This will handle the case where the user causes the text field to become empty by deleting all characters, or selecting and cutting them. In this case, you should probably also implement &lt;code&gt;textFieldShouldClear:&lt;/code&gt;, to handle the case where the user clicks on the text field clear widget (if it exists). It turns out that action does not call the ...&lt;code&gt;shouldChangeCharacters&lt;/code&gt;... callback.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-6778955305371904012?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/6778955305371904012/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=6778955305371904012' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6778955305371904012'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6778955305371904012'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2011/07/ios-pro-tip-textfieldshouldclear.html' title='iOS Pro Tip: textFieldShouldClear'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-3381992709111986396</id><published>2011-06-27T15:53:00.013-06:00</published><updated>2011-06-27T19:12:14.093-06:00</updated><title type='text'>Versioning Core Data Models and Unit Testing with Xcode 4</title><content type='html'>Today at &lt;a href="http://www.thoughtworks.com/"&gt;my day job&lt;/a&gt; I got to create a new version of a Core Data model. The Good News: automatic migration really does Just Work in the simple case of adding a new attribute (column) to an entity (table). The Bad News: Unit Tests do not necessarily automatically get the new version of the model.&lt;br /&gt;&lt;br /&gt;For reference, I'm using Xcode 4.0.2, build 4A2002a. Also using Google Toolbox for Mac and OCMock for unit testing.&lt;br /&gt;&lt;br /&gt;For some reason, my unit test target (we're using Google Toolbox for Mac and OCMock for unit testing) was originally set up with  the following files targeted for various build phases:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Compile Sources: MyModel.xcdatamodel (the original version, underneath the .xcdatamodeld directory)&lt;/li&gt;&lt;li&gt;Copy Bundle Resources: no model&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;After creating the new version of the data model, we really want the .xcdatamodeld (the whole directory structure that knows about all versions) to be what gets compiled / bundled.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Compile Sources: MyModel.xcdatamodeld&lt;/li&gt;&lt;li&gt;Copy Bundle Resources: MyModel.xcdatamodeld&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;So I had to delete MyModel.xcdatamodel from the Compile Sources build phase and add MyModel.xcdatamodeld. The really weird thing was I &lt;span style="font-weight:bold;"&gt;could not&lt;/span&gt; get Xcode to add the file using the add dialog in the "Choose items to add" dialog that the plus sign brings up.&lt;br /&gt;&lt;br /&gt;Note: to see the "Choose items to add" dialog I'm referring to in Xcode 4, do this:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Click on the Project name in the Project navigator&lt;/li&gt;&lt;li&gt;Click on a Target&lt;/li&gt;&lt;li&gt;Click on "Build Phases" at the top of the project info section&lt;/li&gt;&lt;li&gt;Expand the "Compile Sources" phase&lt;/li&gt;&lt;li&gt;Scroll to the bottom of "Compile Sources" and click on the "plus" sign (+)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Using this dialog, Xcode will allow you to select any of the .xcdatamodel files, but not the .xcdatamodeld. The only way I could get the .xcdatamodeld file in "Compile Sources" (and "Copy Bundle Resources") was by clicking on the file in the Project navigator and dragging it into the build phase. Then it worked like a charm.&lt;br /&gt;&lt;br /&gt;One other bit of weirdness: A lot of blogs that talk about versioning Core Data models don't mention how to set the selected version in Xcode 4. To do this:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Select the .xcdatamodeld file in the Project navigator&lt;/li&gt;&lt;li&gt;Open the Utilities pane (in the upper right corner of Xcode; see image)&lt;/li&gt;&lt;/ul&gt;&lt;a href="http://3.bp.blogspot.com/-k9yW98j7vt0/Tgj-FRaPAdI/AAAAAAAAAEI/elwjn0XMLtI/s1600/Screen%2Bshot%2B2011-06-27%2Bat%2B5.51.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 84px;" src="http://3.bp.blogspot.com/-k9yW98j7vt0/Tgj-FRaPAdI/AAAAAAAAAEI/elwjn0XMLtI/s200/Screen%2Bshot%2B2011-06-27%2Bat%2B5.51.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5623023501298303442" /&gt;&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Select the current version from the Versioned Data Model section of the File Inspector (see image)&lt;/li&gt;&lt;/ul&gt;&lt;a href="http://1.bp.blogspot.com/-4H_6zyZA37A/Tgj-MnZ0_jI/AAAAAAAAAEQ/b8s0dPgmhuI/s1600/Screen%2Bshot%2B2011-06-27%2Bat%2B5.53.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 150px;" src="http://1.bp.blogspot.com/-4H_6zyZA37A/Tgj-MnZ0_jI/AAAAAAAAAEQ/b8s0dPgmhuI/s200/Screen%2Bshot%2B2011-06-27%2Bat%2B5.53.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5623023627461262898" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Final note: The app pictured in the images, TripCents, is not my current client's app; it's one of mine.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-3381992709111986396?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/3381992709111986396/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=3381992709111986396' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3381992709111986396'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3381992709111986396'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2011/06/versioning-core-data-models-and-unit.html' title='Versioning Core Data Models and Unit Testing with Xcode 4'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-k9yW98j7vt0/Tgj-FRaPAdI/AAAAAAAAAEI/elwjn0XMLtI/s72-c/Screen%2Bshot%2B2011-06-27%2Bat%2B5.51.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-5710506357865995870</id><published>2011-06-23T17:14:00.002-06:00</published><updated>2011-06-23T17:19:10.882-06:00</updated><title type='text'>Mac Pro Tip: macerror</title><content type='html'>Ran across an obscure error code working on an iOS app at my day job. It turns out there's a utility on the Mac that will give you a little more to go on than some random negative integer: simply run &lt;pre&gt;macerror [error code]&lt;/pre&gt; to get a (slightly) more descriptive version. For instance, I was getting error code -25299 trying to add an entry to my app's keychain; &lt;code&gt;macerror&lt;/code&gt; reports that as &lt;code&gt;errKCDuplicateItem&lt;/code&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-5710506357865995870?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/5710506357865995870/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=5710506357865995870' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5710506357865995870'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5710506357865995870'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2011/06/mac-pro-tip-macerror.html' title='Mac Pro Tip: macerror'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-3304210161531736194</id><published>2011-06-23T16:46:00.006-06:00</published><updated>2011-06-23T17:09:54.667-06:00</updated><title type='text'>[app next]: TripCents is now in the iTunes store!</title><content type='html'>My latest app, &lt;a href="http://tripcents.ruppworks.com"&gt;TripCents&lt;/a&gt;, is now available. It lets you store a collection of named trips and calculates the approximate cost of gas, given the distance for each trip, your car's estimated gas mileage, and the current local cost of gas. I wrote it to help me gauge how much to budget for gas each month, and also to educate my kids as to how much money we actually spend shuttling them around town.&lt;br /&gt;&lt;br /&gt;I'm particularly happy about the chance this gave me to flex some new iOS developer muscles. Where &lt;a href="http://treadcalc.ruppworks.com"&gt;TreadCalc&lt;/a&gt; was essentially a single-screen app with no persistence, TripCents uses CoreData for persistence, has actual multi-page navigation and editable forms, and it renders its views using custom UITableViewCells. It also offers a lot of opportunity for incremental improvements, such as adding the ability to use Google's APIs for geocoding and directions, and MapKit to render geocoded destinations nicely.&lt;br /&gt;&lt;br /&gt;You can read more about TripCents on &lt;a href="http://tripcents.ruppworks.com"&gt;my RuppWorks, LLC website&lt;/a&gt;, or if you just can't wait you can go &lt;a href="http://itunes.apple.com/us/app/tripcents/id441026515?mt=8&amp;ls=1"&gt;straight to the store&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-3304210161531736194?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/3304210161531736194/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=3304210161531736194' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3304210161531736194'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3304210161531736194'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2011/06/app-next-tripcents-is-now-in-itunes.html' title='[app next]: TripCents is now in the iTunes store!'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-7912527403207553375</id><published>2011-06-03T15:15:00.003-06:00</published><updated>2011-06-03T15:30:49.189-06:00</updated><title type='text'>Mac Pro Tip: Preferences .plist files</title><content type='html'>I &lt;span style="font-weight:bold;"&gt;love&lt;/span&gt; my new MacBook Air 11". Used it for most of my flight from Atlanta to Chicago today (about 1.5 hours flying time), and for once didn't have to worry about the guy in front of me leaning his seat back all the way into my lap. Also had a reported ~5.5 hours of battery time left when I had to shut down. The only thing I would do differently next time is get maximum RAM (currently sporting 2GB versus a potential max of 4GB).&lt;br /&gt;&lt;br /&gt;When I'm at home, I run the MB Air in clamshell mode connected to a 20" cinema display (old school, baby; that's how I roll). Works great, everything displays where it should in either mode.&lt;br /&gt;&lt;br /&gt;Except ...&lt;br /&gt;&lt;br /&gt;There's this one app, RSA SecurID, that I occasionally need to connect to ThoughtWorks' sites. And it is decidedly &lt;span style="font-weight:bold;"&gt;not&lt;/span&gt; smart about repositioning itself. I usually have it dragged down out of the way, to the bottom right corner of the cinema display, but today while traveling I found out it always stays off the screen, even without the cinema display connected. Exposé will show it to me when I switch to the app and Show Application Windows, but when I click on it, it zips back off to the (now non-existent) bottom right corner of the cinema display. Restarting does not help, nor does gathering windows nor detecting displays in the Displays settings panel.&lt;br /&gt;&lt;br /&gt;Bummer.&lt;br /&gt;&lt;br /&gt;So I had to resort to opening the .plist file for the app (located for me at ~/Library/Preferences/com.rsa.Software Token\Desktop.plist) and manually updating its two properties: LastXPosition and LastYPosition. Supposedly, .plist files can be edited with any text editor (e.g., TextEdit), but this one seems to be in some binary format; I ended up have to use Xcode's plist editor to be able to make my changes.&lt;br /&gt;&lt;br /&gt;Further Pro Tip: Make sure to tab or click out each field that you've edited. If you just type your change in place and save, it will not take.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-7912527403207553375?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/7912527403207553375/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=7912527403207553375' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7912527403207553375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7912527403207553375'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2011/06/mac-pro-tip-preferences-plist-files.html' title='Mac Pro Tip: Preferences .plist files'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-2667096303154622257</id><published>2011-04-07T10:22:00.003-06:00</published><updated>2011-04-07T10:28:37.394-06:00</updated><title type='text'>First App: TreadCalc!</title><content type='html'>My first app for iPhone -- &lt;a href="http://treadcalc.ruppworks.com"&gt;TreadCalc&lt;/a&gt; -- is live in the iTunes store! It's a little utility I developed for when I have to run on a treadmill and want to know what speed to set to run a given distance in a given time. There are other apps in the store that do this, of course, but I think mine is the only one that does it all on one screen -- no settings, no info; nothing but you, a few buttons (for switching between miles, kilometers, and time entry), and a couple of dials. As a bonus, you can use it as miles-to-kilometers conversion calculator, at least for distances up to 26.9 miles (just over the length of a marathon).&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="itms://itunes.apple.com/us/app/treadcalc/id420569547?mt=8"&gt;Check it out, yo!&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-2667096303154622257?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/2667096303154622257/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=2667096303154622257' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/2667096303154622257'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/2667096303154622257'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2011/04/first-app-treadcalc.html' title='First App: TreadCalc!'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-7022963481252529957</id><published>2011-03-16T14:42:00.002-06:00</published><updated>2011-03-16T14:55:48.293-06:00</updated><title type='text'>Mac Tech Tip: chflags</title><content type='html'>I'm in the throes of my very-nearly-annual clean install of Mac OS X to my MacBook Pro (just to generally de-gunk; no other pressing reason). Of course this means I have to reinstall a lot of software (&lt;i&gt;note to self: Hey self! You still need to restore World of Warcraft from your backup, stat!&lt;/i&gt;). Dropbox is helping me a lot this year; this time I don't have to recreate e.g., all of my password manager entries. That would be bad.&lt;br /&gt;&lt;br /&gt;One of the more obscure things that I take for granted, always lose when I reinstall, and immediately miss is my symlink to &lt;code&gt;/usr&lt;/code&gt;, which I like to create so I can use Finder to browse it. This year I decided to see if there was some better, native way to expose &lt;code&gt;/usr&lt;/code&gt; to the Finder, and it turns out there is: &lt;code&gt;chflags&lt;/code&gt;. One quick call to &lt;code&gt;sudo chflags nohidden /usr&lt;/code&gt;, and I'm happily browsing. No muss, no fuss, no stretchmarks.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-7022963481252529957?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/7022963481252529957/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=7022963481252529957' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7022963481252529957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7022963481252529957'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2011/03/mac-tech-tip-chflags.html' title='Mac Tech Tip: chflags'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-5150835811159396179</id><published>2010-10-25T19:34:00.004-06:00</published><updated>2010-10-26T05:57:39.254-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='clojure'/><title type='text'>First Clojure Patch (ding)!</title><content type='html'>In celebration of the awesomeness that was ClojureConj The First, I just submitted my first patch to the Clojure core codebase! Still waiting for the Lumbering Wheels of Bureaucracy to turn the Massive Gears of Pending Assembla Membership to open the Gates to the Kingdom of Those Who Have Had a Patch Accepted. But the hardest part is done!&lt;br /&gt;&lt;br /&gt;I'm just giddy with excitement. Or sleep deprivation. Or both. Probably both.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-5150835811159396179?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/5150835811159396179/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=5150835811159396179' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5150835811159396179'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5150835811159396179'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2010/10/first-clojure-patch-ding.html' title='First Clojure Patch (ding)!'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-6730391293260517834</id><published>2010-05-02T15:23:00.003-06:00</published><updated>2010-05-03T05:23:17.253-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='haml'/><title type='text'>Using Haml with Rails 3.0</title><content type='html'>One of the things I [heart] about being a Ruby/Rails developer for ThoughtWorks is that I occasionally get asked to do something really cool on the side, like being a technical reviewer for &lt;a href="http://www.amazon.com/Rails-Way-Addison-Wesley-Professional-Ruby/dp/0321601661/ref=sr_1_3?ie=UTF8&amp;s=books&amp;qid=1272835414&amp;sr=8-3"&gt;an upcoming book on Rails 3.0&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;In this particular book, the author uses Haml instead of Erb for templating views in the sample code. When I'm reviewing a book, I like to actually exercise as much of the sample code as I reasonably can, as it tends to be among the hardest stuff to get right, and among the easiest ways to discourage a reader when it's not right. But Rails defaults to Erb for templating, and I'm not exactly Haml-enabled.&lt;br /&gt;&lt;br /&gt;What's a geek to do?&lt;br /&gt;&lt;br /&gt;Well, my friend Google had a thing or two to suggest, but the process is slightly non-trivial so I thought I'd collate it here, thusly:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;i&gt;NB: All commands are entered from your Rails 3.0 application's root directory.&lt;/i&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Edit &lt;code&gt;[your_app]/Gemfile&lt;/code&gt;, and add the line &lt;code&gt;gem 'haml'&lt;/code&gt; somewhere convenient.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Edit &lt;code&gt;[your_app]/config/application.rb&lt;/code&gt;, and add the line &lt;code&gt;g.template_engine :haml&lt;/code&gt; to the &lt;code&gt;config.generators&lt;/code&gt; block (uncommenting the block if necessary).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;gem install haml&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;bundle install&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;haml --rails .&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;git clone git://github.com/psynix/rails3_haml_scaffold_generator.git lib/generators/haml&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;That last bit is particularly important for allowing Rails' scaffold generators to Do The Haml Thang&lt;sup&gt;&lt;small&gt;(TM)&lt;/small&gt;&lt;/sup&gt; when generating view templates.&lt;br /&gt;&lt;br /&gt;Now back to my regularly scheduled tech review ...&lt;br /&gt;&lt;br /&gt;P.S.: The book is going to be awesome. Reserve your copy now!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-6730391293260517834?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/6730391293260517834/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=6730391293260517834' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6730391293260517834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6730391293260517834'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2010/05/using-haml-with-rails-30.html' title='Using Haml with Rails 3.0'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-1762481407381940715</id><published>2010-05-01T16:19:00.001-06:00</published><updated>2010-05-01T16:21:13.412-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='clojure'/><category scheme='http://www.blogger.com/atom/ns#' term='interpreter'/><category scheme='http://www.blogger.com/atom/ns#' term='implementation'/><title type='text'>Enter the Lich!</title><content type='html'>So I've finally spent a little bit of time unpacking and bookshelving my trove of geek books from my move last summer. Among many that I want/need to read Real Soon Now (TM), the book "Lisp in Small Pieces" by Christian Queinnec caught my eye and reminded me that I need to actually do something about learning Clojure.&lt;br /&gt;&lt;br /&gt;So I've set out to implement Lisp in Clojure, following Queinnec's book. I've put the shell of the project -- featuring 100% test coverage; go me! -- on GitHub, at http://github.com/davidrupp/lich. For some reason I haven't taken the time to obsess about (yet), &lt;code&gt;lein repl&lt;/code&gt; doesn't include the project's &lt;code&gt;src&lt;/code&gt; directory on the CLASSPATH, so you can't really &lt;code&gt;require&lt;/code&gt; your code and play with it in the REPL. So I've included my own in &lt;code&gt;script/repl&lt;/code&gt;. I suspect that once I really get the hang of unit testing I won't need/want to rely on it so much, but at least it's there for now.&lt;br /&gt;&lt;br /&gt;This should be fun ...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-1762481407381940715?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/1762481407381940715/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=1762481407381940715' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/1762481407381940715'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/1762481407381940715'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2010/05/enter-lich.html' title='Enter the Lich!'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-6252811762580021001</id><published>2010-03-21T18:11:00.004-06:00</published><updated>2010-03-21T18:19:36.538-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pragmatic'/><category scheme='http://www.blogger.com/atom/ns#' term='clojure'/><title type='text'>Review: Pragmatic Clojure Studio</title><content type='html'>Executive Summary: If you are a developer looking to get into Clojure programming, attend this studio.&lt;br /&gt;&lt;br /&gt;Executive Summary (for executives): If a developer who reports to you wants to attend this studio -- let him.&lt;br /&gt;&lt;br /&gt;Even if you're at a shop that doesn't allow development in anything as exotic as Clojure -- yet -- you will benefit from this view of a language that embraces the JVM-as-a-platform as richly and effectively as Clojure does. You will learn what Java-the-language could (should?) have been capable of. You will even learn what Java-the-language actually *is* capable of. Much of Clojure is implemented in Clojure itself (homoiconicity FTW!), but a good chunk of it is implemented in Java.&lt;br /&gt;&lt;br /&gt;The value of this studio is not the standard nuts-'n'-bolts / grammar / syntax / now-do-some-labs dance of most technical training. The value of this studio is that it's co-taught by Rich Hickey, the creator of Clojure. You will get to hear him talk about the design decisions he made in the evolution of Clojure. More importantly, you will get to hear him talk about the design decisions he *avoided*. You may not agree with all of those decisions, but you have to respect the man's process. Language philosophy aside, Rich's talks on "State and Identity", "Modeling State and Time", and "Protocols, Reify, and Datatypes" will blow your mind, and cause you to think differently about the act of programming whether or not you ever write a line of Clojure outside the studio.&lt;br /&gt;&lt;br /&gt;Other not-quite-as-mind-blowing-but-still-pretty-cool topics include functional programming, web development with Compojure, Java interoperability, macros, and method dispatch. &lt;br /&gt;&lt;br /&gt;And it's not just Rich who is fun to watch. The studio is co-taught by Stuart Halloway, owner of Relevance, LLC -- a working Clojure shop -- and author of the Pragmatic Programmers' "Programming Clojure". The two complement each other nicely. Stuart is very impassioned, a bundle of energy when it's his turn at the projector. Rich is much more laid back (though still impassioned), and expresses volumes with the arch of an eyebrow or a quirky half-smile at Stuart's antics. Both are eminently accessible and happy to answer questions and interact with students during breaks.&lt;br /&gt;&lt;br /&gt;Remember Java in 1995? Ruby in 2005? I went into this studio a skeptic, and I came out convinced that Clojure is going to have a place -- and ultimately a prominent one -- in the landscape of our profession. &lt;br /&gt;&lt;br /&gt;Go to this studio. Spend a few days learning from the masters. You'll be glad you did.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-6252811762580021001?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/6252811762580021001/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=6252811762580021001' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6252811762580021001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6252811762580021001'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2010/03/review-pragmatic-clojure-studio.html' title='Review: Pragmatic Clojure Studio'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-1622967161984086951</id><published>2010-01-18T09:53:00.004-07:00</published><updated>2011-06-24T20:24:52.814-06:00</updated><title type='text'>Why Should You Want To Work For ThoughtWorks?</title><content type='html'>Note to self: the next time you're watching Roy (Roy Singham, the founder of ThoughtWorks) give his "How I Founded ThoughtWorks" pep talk to a batch of candidates, stand far off to the side and far enough away that he can't read your name tag.&lt;br /&gt;&lt;br /&gt;What brings this up?&lt;br /&gt;&lt;br /&gt;I spent a couple days in Chicago this past weekend helping out with our Super Saturday hiring event. And I watched Roy give his pep talk too closely and too directly in his line of sight. Because in mid pep talk he looked straight at me and said, "David, you're a ThoughtWorker, what do you want to add? Why should these people want to work for ThoughtWorks?"&lt;br /&gt;&lt;br /&gt;Umm...&lt;br /&gt;&lt;br /&gt;I managed to blurt out an impromptu, off-the-cuff, early draft of something like what follows.&lt;br /&gt;&lt;br /&gt;Here, after time to think and embellish and re-word and clarify, is the full response I wish I'd given:&lt;br /&gt;&lt;br /&gt;Why should you want to work for ThoughtWorks?&lt;br /&gt;&lt;br /&gt;If you're like me, you've spent a fair amount of time (over 20 years, in my case) developing software for 'The Man' -- a series of nameless, faceless, soulless corporations. You've worked primarily for managers who fear what they don't understand, and what they don't understand is primarily two things: software development, and you.&lt;br /&gt;&lt;br /&gt;At each of the places you've worked, you were led to your cube; isolated, walled off -- but not so much that The Man and his lieutenants can't walk by and easily keep tabs on you. Penned up so that human interaction happens on The Man's terms, not nature's. Guaranteed that every human interaction will require a hard context-switch in interface from keyboard and mouse to whiteboard and mouth.&lt;br /&gt;&lt;br /&gt;And you're told 'you're Just a Java Developer'. By which The Man means: don't hassle me with your Ruby, or your Clojure, or your [cool technology here] that you've actually spent some time on your own learning about that could make a difference.&lt;br /&gt;&lt;br /&gt;The Man doesn't want you to make a difference. The Man wants you to sit in your cube and write code. It doesn't even have to be working code; at least not at first. The QAs will figure out whether it works in due time. You're Just a Developer. And The Man has a whole raft of inefficient and degrading processes to manage the interactions between the QAs and the Developers.&lt;br /&gt;&lt;br /&gt;When you chafe at being Just a Java Developer and you advocate for making a difference -- using force multipliers like Ruby, and Pair Programming, and Test Driven Development -- you're branded a troublemaker. And the guard on your cube doubles. And the degrading processes get more degrading, especially for you.&lt;br /&gt;&lt;br /&gt;And then you find out that ThoughtWorks is hiring, and that we need smart people. People who voluntarily spend their free time getting better at what they do, looking for ways to make a difference. People who understand that there is more to software development than Just Java and The Man's inefficient and degrading processes.&lt;br /&gt;&lt;br /&gt;And you realize that it's a win-win -- if you work for ThoughtWorks, ThoughtWorks gets another smart person, you get to be that smart person, and The Man fills your pen with a willing sheep and has one less troublemaker to guard.&lt;br /&gt;&lt;br /&gt;If you take the plunge and apply, you get a chance to convince us that you can make a difference. And when I say 'us' I mean ThoughtWorkers, people who do what we would be hiring you to do. Not ThoughtWorks HR. Not ThoughtWorks Management. The very same ThoughtWorkers who will be your co-ThoughtWorkers if you're hired. We are not easily convinced. But we are aching to be convinced.&lt;br /&gt;&lt;br /&gt;And if you make it through the gauntlet of code samples and assessment tests and ThoughtWorkers, within a couple days of signing the paperwork your butt is in a chair at a client site and you're pairing full-time, ten hours a day, four days a week, writing mission-critical code with people who are smarter than you ever will be.&lt;br /&gt;&lt;br /&gt;And here's the coolest part. Not only do you *get* to make a difference (you do). Not only are you *required* to make a difference (you are).&lt;br /&gt;&lt;br /&gt;The coolest part is this: you're *expected* to make a difference.&lt;br /&gt;&lt;br /&gt;Because you're a ThoughtWorker.&lt;br /&gt;&lt;br /&gt;That's why you should want to work for ThoughtWorks.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-1622967161984086951?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/1622967161984086951/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=1622967161984086951' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/1622967161984086951'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/1622967161984086951'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2010/01/why-should-you-want-to-work-for.html' title='Why Should You Want To Work For ThoughtWorks?'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-1602070060896531019</id><published>2009-11-22T12:32:00.003-07:00</published><updated>2009-11-22T12:36:33.628-07:00</updated><title type='text'>Learning Japanese, I Think I'm Learning Japanese ...</title><content type='html'>The gauntlet is thrown.&lt;br /&gt;&lt;br /&gt;In my prior blog post about the disappointing contents of some of the sessions at day one of RubyConf 2009 (which is a great conference overall, btw; try to go next year!), I mentioned that some of the presentations were below a standard that I would have expected at the premier international conference for this language/community. As it happens, all of the presenters of the sessions I called out speak English as a second language, and they are all in fact Japanese.&lt;br /&gt;&lt;br /&gt;In my defense, I was not talking about the presenters' English skills. Lord knows their English is better than my (heretofore non-existent) Japanese. As an American who has made a point of learning and trying to function in the native language of the countries I've visited (Germany, France, and Italy to-date), I have nothing but respect for anyone brave enough to go anywhere and speak in a non-native tongue to an audience of native speakers.&lt;br /&gt;&lt;br /&gt;That's why I've accepted John Mettraux's gentle challenge to gain full perspective by submitting a proposal to RubyKaigi 2010, Japan's version of RubyConf. John assures me that I would not be expected to present in Japanese, but that strikes me as being less than "full perspective". So I'm going to try to learn enough Japanese to not make a total fool of myself (assuming my proposal is accepted). If any of the three of you reading this blog (hi, Mom!) has any ideas for good learning resources, please do let me know.&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Ah, but a man's reach should exceed his grasp, Or what's a heaven for? -- Robert Browning&lt;br /&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-1602070060896531019?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/1602070060896531019/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=1602070060896531019' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/1602070060896531019'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/1602070060896531019'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2009/11/learning-japanese-i-think-im-learning.html' title='Learning Japanese, I Think I&apos;m Learning Japanese ...'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-8964405312588916668</id><published>2009-11-20T16:37:00.002-07:00</published><updated>2009-11-20T16:41:46.460-07:00</updated><title type='text'>RubyConf Day Two: I Have Seen The Future, And It Is ...</title><content type='html'>MacRuby.&lt;br /&gt;&lt;br /&gt;So day two of RubyConf went much better for me, content-wise. Caleb Clausen gave a subdued, but very interesting, presentation on Ocelot, his version of a Ruby compiler. Yehuda Katz brought down the house with his presentation "Polishing Rubygems", which was primarily about his project, Bundler. Andy Keep had a different take on the topic of partial evaluation to make Ruby more amenable to compiling. And Charlie Nutter was very engaging in his presentation with Thomas Enebo on the status of JRuby, including a cool demo of jirb running on an Android phone (simulator).&lt;br /&gt;&lt;br /&gt;But my favorite talk by far was Laurent Sansonetti of Apple, talking about MacRuby. Laurent, who is clearly not a native English speaker -- are you reading this, jmettraux? -- gave a professional, content-laden presentation on MacRuby, which is pretty much his baby at Apple. He had some cool demos -- one of which was nearly thwarted by the hotel's abyssmal internet setup -- of MacRuby's integration with Cocoa/Objective-C, which also demonstrated that MacRuby has already solved the problem of compiling Ruby. XCode will build executables with the MacRuby framework embedded, and it will ship the MacRuby code as binary .rbo files, not Ruby source files. No need to obfuscate or simply give your code away; MacRuby takes care of it all.&lt;br /&gt;&lt;br /&gt;Now if only we could get Apple to open up the iPhone to this. Or perhaps build a tablet of some sort ...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-8964405312588916668?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/8964405312588916668/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=8964405312588916668' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8964405312588916668'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8964405312588916668'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2009/11/rubyconf-day-two-i-have-seen-future-and.html' title='RubyConf Day Two: I Have Seen The Future, And It Is ...'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-5652918919026755317</id><published>2009-11-19T21:57:00.003-07:00</published><updated>2009-11-19T22:40:13.761-07:00</updated><title type='text'>RubyConf 2009: Day One</title><content type='html'>In a word: disappointing.&lt;br /&gt;&lt;br /&gt;Oh, the venue (the Embassy Suites at the San Francisco Airport) is nice enough. And yes, there are problems finding power and getting on the internet, but that's to be expected. Anyone who attends a gathering of more than about ten people and actually believes the claims of abundant power and speedy WiFi for all is asking for a let-down. So it's not that.&lt;br /&gt;&lt;br /&gt;Maybe it was just my bad luck in the sessions I chose to attend. I was very much looking forward to hearing some of the Ruby core team speak, so I made sure to catch Matz's keynote, "East meets West", and "Hacking parse.y", the latter being interesting to me in its own right since it was about Ruby implementation internals. &lt;br /&gt;&lt;br /&gt;Matz has decent presence as a speaker, and the crowd dutifully laughed at all his good-natured, pro-forma digs at other languages' foibles (Java is lame. Lisp has too many parentheses. Et cetera.). But he didn't have anything particularly new or interesting to say about Ruby or language design, just that there can be no "one true" programming language, that the best we can hope for is one that's "close enough", and in his opinion Ruby is currently the best candidate. Go figure. Still and all, the man did invent the language that I currently use to make a living, so major props to him.&lt;br /&gt;&lt;br /&gt;The other two were just not up to any reasonable standard for a conference at this level. &lt;br /&gt;&lt;br /&gt;Look -- I know the Ruby community prides itself on being edgy. I get that RubyConf is deliberately kept small and scruffy and non-RailsConf-y. But is it really asking so much that the people who present talks at The Premier Conference for their language/community have some basic skill in the art of talking to an audience?&lt;br /&gt;&lt;br /&gt;I did find the talk on using Ruby to write DSLs and code generation tools for hybrid systems simulation and scientific computing pretty interesting. And tomorrow's schedule looks good on paper. &lt;br /&gt;&lt;br /&gt;Here's hoping it goes better than today.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-5652918919026755317?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/5652918919026755317/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=5652918919026755317' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5652918919026755317'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5652918919026755317'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2009/11/rubyconf-2009-day-one.html' title='RubyConf 2009: Day One'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-7391898616658457976</id><published>2009-11-18T20:08:00.008-07:00</published><updated>2009-11-18T20:24:29.520-07:00</updated><title type='text'>Dear RVM: It's Not Me. It's You.</title><content type='html'>Dear Ruby Version Manager,&lt;br /&gt;&lt;br /&gt;It's over. I tried. But I just can't do it any more. I though I could let you back into my life after that unfortunate 'sudo gem install rvm' spat we had on our first date. That's on me. But even after I moved you into my home directory, you still couldn't make it work.&lt;br /&gt;&lt;br /&gt;I wanted it to work. Really, I did. I mean, a chance to have an open relationship with multiple Ruby's, all painlessly and seamlessly installed and switchable through you? Who &lt;strong&gt;wouldn't&lt;/strong&gt; want that?&lt;br /&gt;&lt;br /&gt;But you just couldn't handle my prior relationship with my self-compiled, self-installed /usr/local/bin/ruby, could you? I mean, you ... you ... it hurts just to type it ... you hijacked my &lt;strong&gt;$PATH&lt;/strong&gt;, RVM! You locked me out of my &lt;strong&gt;gems&lt;/strong&gt;! I mean, they weren't all switchable and fancy and all, they were just plain gems, but they were &lt;strong&gt;my&lt;/strong&gt; gems, you know? How was I supposed to trust you after that?&lt;br /&gt;&lt;br /&gt;Was it jealousy? Was it ignorance? I guess I'll never really know.&lt;br /&gt;&lt;br /&gt;But -- for now at least -- I have to give you up. I have to lash myself to the mast of my virtual Argo, and forego your sweet, seductive siren song.&lt;br /&gt;&lt;br /&gt;It's a pity. We could've been something, you and me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-7391898616658457976?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/7391898616658457976/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=7391898616658457976' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7391898616658457976'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7391898616658457976'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2009/11/dear-rvm-its-not-me-its-you.html' title='Dear RVM: It&apos;s Not Me. It&apos;s You.'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-8083874223488930079</id><published>2009-10-14T06:03:00.001-06:00</published><updated>2009-10-14T06:09:46.598-06:00</updated><title type='text'>Ruby's inject(): Putting the 'Fun' in 'Functional Programming' Since ... Oh, About 4:15 This Morning</title><content type='html'>Yesterday at &lt;a href='http://www.thoughtworks.com'&gt;work&lt;/a&gt;, I ran across the need to convert a number of seconds into its equivalent in days, hours, minutes, and seconds. The canonical imperative way to do this is something like:&lt;pre&gt;seconds = 356521&lt;br /&gt;days = seconds / (24 * 60 * 60)&lt;br /&gt;seconds = seconds % (24 * 60 * 60)&lt;br /&gt;hours = seconds / (60 * 60)&lt;br /&gt;seconds = seconds % (60 * 60)&lt;br /&gt;minutes = seconds / 60&lt;br /&gt;seconds = seconds % 60&lt;/pre&gt;N.B.: This relies on Ruby's integer division -- dividing an integer by an integer results in an integer, with any fractional remainder discarded.&lt;br /&gt;&lt;br /&gt;My pair and I ended up implementing something very much like this, but it left a bad taste in my mouth. I mean, it works and all, but it feels kind of ... non-functional. Ya know? &lt;br /&gt;&lt;br /&gt;So, after much fretting and sleeplessness last night ... behold:&lt;pre&gt;seconds = 356521&lt;br /&gt;&lt;br /&gt;days, hours, minutes, seconds = &lt;br /&gt;  [1.day, 1.hour, 1.minute, 1.second].inject([]) do |acc, unit|&lt;br /&gt;    quotient, seconds = seconds.divmod unit&lt;br /&gt;    acc &lt;&lt; quotient&lt;br /&gt;  end&lt;/pre&gt;This version needs to be run in a Rails &lt;code&gt;script/console&lt;/code&gt; rather than &lt;code&gt;irb&lt;/code&gt;, because it makes use of the Rails shortcut definitions of the number of seconds in various units of time. You could easily convert to plain &lt;code&gt;irb&lt;/code&gt;-able Ruby by replacing &lt;code&gt;1.day&lt;/code&gt; &lt;i&gt;et al&lt;/i&gt; above with their numeric equivalents.&lt;br /&gt;&lt;br /&gt;The resulting code is both (more) functional, and more quintessentially Ruby-ish. &lt;code&gt;divmod&lt;/code&gt; and multiple assignment let us figure out the quotient and the remainder of the division in one go, and &lt;code&gt;inject&lt;/code&gt; lets us accumulate the results and ultimately multiply assign them to their respective units. &lt;br /&gt;&lt;br /&gt;Neato.&lt;br /&gt;&lt;br /&gt;I'm a little bummed, though, that this version has the side effect of destroying the original contents of &lt;code&gt;seconds&lt;/code&gt;, as well as requiring &lt;code&gt;seconds&lt;/code&gt; to be defined outside the scope of the &lt;code&gt;inject&lt;/code&gt;. What would be really cool would be to have a version of &lt;code&gt;inject&lt;/code&gt; that allowed for multiple accumulators (or, really, a 'decumulator' in this case) such that all side effects could be contained within the &lt;code&gt;inject&lt;/code&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-8083874223488930079?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/8083874223488930079/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=8083874223488930079' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8083874223488930079'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8083874223488930079'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2009/10/rubys-inject-putting-fun-in-functional.html' title='Ruby&apos;s inject(): Putting the &apos;Fun&apos; in &apos;Functional Programming&apos; Since ... Oh, About 4:15 This Morning'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-5743269200030793999</id><published>2009-09-09T20:27:00.003-06:00</published><updated>2009-09-10T10:41:09.823-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='metaprogramming'/><title type='text'>Fun With Metaprogramming: hash_initializer()</title><content type='html'>Earlier this week I was doing a code review for a candidate for ThoughtWorks. One of the bits I ran across was an object constructor that looked something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class MyClass&lt;br /&gt;  def initialize(name, price, is_cool, is_neato, is_rad)&lt;br /&gt;    @name = name&lt;br /&gt;    # et cetera&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Not so bad, really, when you're looking right at the signature for the constructor. But when it's called from another file...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;MyClass.new("bob", "1.00", true, false, true)&lt;br /&gt;MyClass.new("fred", "2.00", false, true, true)&lt;br /&gt;# et cetera&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;... the purpose of the first two parameters may be easy to infer, but the last three? Which order were those descriptors in anyway? To really be sure, one has to keep referring to the class definition. In my review, I opined that perhaps the constructor should take a single argument -- a hash with keys named after the objects instance variables and their corresponding values. Thusly:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class MyClass&lt;br /&gt;  def initialize(args)&lt;br /&gt;    args ||= {} # in case MyClass#new is called without any args&lt;br /&gt;    @name = args[:name] || ""&lt;br /&gt;    # et cetera&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This frees things up a bit, and allows for parameters to be specified in any order, with reasonable defaults for any that might be left out. It also makes calls to the constructor a little more self-documenting.&lt;br /&gt;&lt;br /&gt;But what would really be nice would be not having to write out this boilerplate code for everything that we want this kind of constructor for.&lt;br /&gt;&lt;br /&gt;Enter metaprogramming, in the form of &lt;code&gt;hash_initializer&lt;/code&gt; -- a nifty little class extension I found in Brian Guthrie's &lt;a href="http://github.com/bguthrie/awsymandias"&gt;Awsymandias&lt;/a&gt; library (as used at my current ThoughtWorks gig). Behold:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; 1. module ClassExtension&lt;br /&gt; 2.  &lt;br /&gt; 3.  if !Class.respond_to?("hash_initializer")&lt;br /&gt; 4.    def hash_initializer(*attribute_names, &amp;block)&lt;br /&gt; 5.      define_method(:initialize) do |*args|&lt;br /&gt; 6.        data = args.first || {}&lt;br /&gt; 7.        data.symbolize_keys!&lt;br /&gt; 8.        attribute_names.each do |attribute_name|&lt;br /&gt; 9.          instance_variable_set "@#{attribute_name}", data[attribute_name]&lt;br /&gt;10.        end&lt;br /&gt;11.        instance_eval &amp;block if block&lt;br /&gt;12.      end&lt;br /&gt;13.    end&lt;br /&gt;14.  end&lt;br /&gt;15.&lt;br /&gt;16. end&lt;br /&gt;17.&lt;br /&gt;18. Class.send :include, ClassExtension&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Lines 5 through 12 correspond pretty much to my second version of MyClass#initialize above. Line 6 does the same defensive guard against a nil first parameter. Lines 8 through 10 pull each attribute, create an instance variable with the same name, and set it to the desired value. The extra bits? Line 7 does something I should have done in my "improved" version -- it forces the keys of the params has to be symbols, avoiding the awkwardness of blowing up when a key is a String when it should be a symbol or vice versa. Line 11 allows for the execution of a block along with initialization, if you're into that sort of thing. And the rest just opens up the class Class and makes Class#hash_initializer available to all objects.&lt;br /&gt;&lt;br /&gt;With Class thus extended, the boilerplate for MyClass becomes simply:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class MyClass&lt;br /&gt;  hash_initializer :name, :price, :is_cool, :is_neato, :is_rad&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And instantiating a MyClass stays what you would expect:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;MyClass.new :name =&gt; "bob", :price =&gt; "1.00" # et cetera.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I like it.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;irb(main):001:0&gt; Class#hash_initializer.is_neato?&lt;br /&gt;=&gt; true&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-5743269200030793999?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/5743269200030793999/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=5743269200030793999' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5743269200030793999'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5743269200030793999'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2009/09/fun-with-metaprogramming.html' title='Fun With Metaprogramming: hash_initializer()'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-7044727921303819118</id><published>2009-08-10T20:11:00.007-06:00</published><updated>2009-08-11T11:01:31.798-06:00</updated><title type='text'>Code Artistry: The Negative Space</title><content type='html'>So I've been reading &lt;span style="font-style:italic;"&gt;The Merb Way&lt;/span&gt; this week. I like it so far -- Foy Savas does a pretty good job of making his discussion of the technical internals interesting to read, and generally elevates the material above today's standard grind-it-out, dress-up-the-man-page-or-the-public-API-docs, tech book farce. Fare. I meant "fare".&lt;br /&gt;&lt;br /&gt;One of the standout features of this book, though, that I'm finding myself drawn to is a marked lack of comparison between Merb and Rails. I can't tell if Savas intended this, but I find reading about, say, Merb's routing architecture easier to follow than if it were interrupted every other paragraph by an "as compared to Rails, which does this..." interlude. I'm not sure many other authors would have avoided that trap as neatly.&lt;br /&gt;&lt;br /&gt;If you think about it, it's a sensible approach to take, although maybe not the most obvious one. If I'm reading a book on Merb, what I want to read about is ... you know ... Merb. I may already know some of the internals of Rails. I may not. If I do, chances are that I will make those comparisons myself. If I don't, chances are a constant barrage of interruptions would just turn me off of Merb, and possibly Rails too. Either way, interleaving a discussion of Rails within a discussion of Merb would be counterproductive.&lt;br /&gt;&lt;br /&gt;Reading this book makes me wish more of us technologists would learn the value of the artistic concept of the negative space -- the bizarre and intriguing shapes formed *between* the familiar ones that are the nominal focus of the picture. That what the artist leaves out can contribute as much to the picture as what he puts in. Sometimes more.&lt;br /&gt;&lt;br /&gt;I was going to write out a list of how this concept applies to and enhances other artistic and quasi-artistic disciplines like architecture, music, literature, and film-making. &lt;br /&gt;&lt;br /&gt;But now I'm not. :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-7044727921303819118?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/7044727921303819118/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=7044727921303819118' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7044727921303819118'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7044727921303819118'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2009/08/code-artistry-negative-space.html' title='Code Artistry: The Negative Space'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-7182272065542727530</id><published>2009-02-16T21:00:00.002-07:00</published><updated>2009-02-16T21:06:43.823-07:00</updated><title type='text'>Recommended Reading: Flex on Rails (Addison-Wesley)</title><content type='html'>&lt;span style="font-style:italic;"&gt;Disclaimer: Addison-Wesley sent me (unsolicited) a review copy of this book at no charge, with the tacit assumption being that I would read it and review it publicly. There are no conditions, however, on the content of said review. What you are about to read is my unbiased, unvarnished take on this book, written to inform you, not to curry favor with the publisher or the authors. Also, I do not have any business relationship with any of the parties involved in the publication of this book; I will not gain in any way if you choose to buy it. But, as it happens with this book, I believe you will gain if you choose to buy it. Here's why:&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The Good&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Flex on Rails, by Tony Hillerson and Daniel Wanja, does many important things right, tech-book-wise, which I find astonishing; more so given that it's the first book by both authors. The material strikes an effective balance between easy-to-absorb and useful-in-real-life, without spending too much time at either extreme, and -- mercifully -- no time at all in "Hello World". &lt;br /&gt;&lt;br /&gt;Aside to would-be (and a lot of already-are) tech book authors: Read Chapter 1 of this book -- all seven pages of it -- to see how it's done. Notice how much (read: "how little") time and print is expended on rehashing yet again the minute details of installing Ruby, RubyGems, Rails, and other requirements. This is not one of those books that has chosen to pad its page count by wallowing in the comfort zone of a twenty-page "Getting Started" tutorial.&lt;br /&gt;&lt;br /&gt;Chapter 1 sets the tone. Subsequent material flows naturally, and never overwhelms. Chapters 2 and 3 combine effectively to introduce the concepts of Flex and Rails' use of XML and REST to communicate betwixt themselves, but without the seemingly-obligatory over-exposition of the history and motivation of either XML or REST.&lt;br /&gt;&lt;br /&gt;The authors introduce testing early on (Chapter 4); not quite TDD, but not bad either, for a tech book. A brief detour into RubyAMF (Chapter 5) follows, and then we get another key topic, Debugging (Chapter 6), early enough to be useful but not so early as to be overwhelming.&lt;br /&gt;&lt;br /&gt;Throughout this first part of the book, the authors assume a fair amount of Rails knowledge/experience. For example, Chapter 2 ("Passing Data with XML") jumps right in with a sample Rails app, complete with (minimal) scaffolding and a simple database migration. Rather than bog the reader down with minutiae, as far too many other tech books do, this material is presented with an unforced, matter-of-fact tone that assumes reader familiarity, but doesn't condescend. Now I concede that I'm an experienced Rails developer, so I take this stuff for granted. But I believe that someone new to Rails would not be overwhelmed. And -- key point here -- &lt;span style="font-weight:bold;"&gt;there are other, better resources for learning about Rails' scaffolding and migrations&lt;/span&gt;. An ill-timed dissertation here would just turn off seasoned developers entirely and distract any new kids in the audience. Good for Tony and Daniel (and whatever wise editors they worked with) for avoiding this all-too-familiar trap.&lt;br /&gt;&lt;br /&gt;The second half of the book demonstrates another key concept that too many tech books don't bother with, and one that I think this team just nailed: Flex on Rails is not an introductory tutorial, and it's not an advanced recipe book: it's both! Each of Chapters 10 through 22 presents a solution to a problem that is very likely to crop up in any real-world usage of this technology. To name a few:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Source Control (Chapter 10), including Subversion and Git&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Deployment (Chapter 12), largely with Capistrano&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Authenticating (Chapter 15)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Communicating between Flex and JavaScript (Chapter 21)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;File Upload (Chapter 22)&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;I've saved mentioning my favorite chapter for last, and I think it is just way too cool that it's in the book, because it reflects a personal mantra of mine -- Chapter 13: "Read the Source!" I am constantly preaching this, whether it's Rails, or Capistrano, or JRuby, or even Ruby. Open source software is a treasure trove of HOWTO: Write Code in all its glory. It is a cornucopia, constantly replenished by the experience and learnings and mistakes and refactorings and holy wars of a horde of extremely bright and capable programmers. Is it sometimes flawed? Sure. But it's there. And it's the best source of education in computer programming -- both how to and how not to -- that you have available to you. Use it.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The Bad&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Not a lot to write here. I've long since ceased to be as outraged as I used to be about the computer publishing industry's contempt for (okay -- let's be charitable and call it "indifference towards") the English language. Let's just say I won't be letting my son use this or any tech book as a primer in either spelling or grammar. This book is not as bad as some, but it still has its share of sloppy copy editing -- typos here, unfortunate typesetting choices there (primarily inconsistent/inadequate use of fixed-width fonts or otherwise visually highlighting stuff that's best viewed as code and not prose). &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The Meh&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;For a book that seems to have been designed deliberately to respect its target audience's intelligence and skill level, Chapter 8 ("Flex MVC Frameworks") takes a bit of a detour into Probably Should Be Obvious Land in its coverage of the Model-View-Controller paradigm. Yes, it's in the context of describing how Cairngorm and PureMVC specifically do MVC, but come on -- it's MVC. Even here, though, the authors rise above the average tech book experience, by implementing the same app using both frameworks, so you can compare and contrast and maybe get a sense for which framework fits your needs better.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Gratuitous Name-Dropping Reference&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Tony Hillerson and I worked on a Rails project (my first) together a few years ago, and even then he was valiantly (but alas, in vain) trying to convince his partners to let us use Flex for the UI. I'm glad to see him realizing that dream and in fact thriving on it. I've met Daniel briefly, at the Pragmatic Studio's Cocoa Programming class in Denver, but I don't think he knows who I am. Other than that, I have no business relationship with either, and I do not stand to benefit in any way from the sales of their book. But, as I said in my disclaimer above, I believe that you will benefit from buying it. Let me know if you do. And let me know how it goes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-7182272065542727530?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/7182272065542727530/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=7182272065542727530' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7182272065542727530'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7182272065542727530'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2009/02/recommended-reading-flex-on-rails.html' title='Recommended Reading: Flex on Rails (Addison-Wesley)'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-8833556142881027050</id><published>2009-01-10T21:11:00.019-07:00</published><updated>2009-01-11T14:58:00.793-07:00</updated><title type='text'>A Gem Is Born: Announcing Gemviz</title><content type='html'>&lt;b&gt;Final (potentially) update:&lt;/b&gt; Now appearing on github:&lt;pre&gt;git://github.com/davidrupp/gemviz.git&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Update to first update:&lt;/b&gt; I realized that I did not specify a gem dependency on &lt;code&gt;ruby-graphviz&lt;/code&gt; in my &lt;code&gt;hoe&lt;/code&gt;-generated Rakefile. This is in place as of version 0.1.2. &lt;br /&gt;&lt;br /&gt;To quote Groundskeeper Willie in The Simpsons - Treehouse of Horror V: "Och, I'm bad at this!"&lt;br /&gt;&lt;br /&gt;We now return you to the first update.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt; Peter Cooper correctly points out in his comment that Gemviz has a hard dependency on Graphviz, which isn't installed by default on Macs. I tend to take it for granted because it's one of the first things on my checklist to install when breaking in a new Mac. I use &lt;a href='http://www.pixelglow.com/graphviz/'&gt;The Mac OS X Edition of Graphviz&lt;/a&gt;, as referenced on &lt;a href='http://www.graphviz.org'&gt;the Graphviz home page&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;We now return you to the regularly scheduled original post.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Original Post&lt;/b&gt;: I can't really call myself a hotshot Ruby programmer until I release a RubyGem, now can I? Well, I still won't call myself a hotshot Ruby programmer yet...but, as of today -- and the release of versions 0.1.0 and 0.1.1 (for documentation purposes) of my new gem &lt;code&gt;gemviz&lt;/code&gt; to rubyforge.org -- at least I'm one step closer!&lt;br /&gt;&lt;br /&gt;Check it out, yo:&lt;pre&gt;[sudo] gem install gemviz&lt;/pre&gt;Now you can do e.g.,&lt;pre&gt;gemviz rails&lt;/pre&gt;(assuming you have Rails installed as a gem which, if you don't by now: huh?), which produces this graph on my system:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_MuRSvkgEbss/SWmASeadzZI/AAAAAAAAACA/jur-WDbK4jU/s1600-h/rails.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 251px;" src="http://2.bp.blogspot.com/_MuRSvkgEbss/SWmASeadzZI/AAAAAAAAACA/jur-WDbK4jU/s400/rails.png" alt="" id="BLOGGER_PHOTO_ID_5289900292213427602" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;In fairness, I should disclose that Gemviz is only about 30 lines of code, and makes heavy use of other people's work to do its magic. Here's how it works:&lt;br /&gt;&lt;br /&gt;* it scrapes the output of the system command &lt;code&gt;gem dep &lt;i&gt;gem_name&lt;/i&gt; --pipe&lt;/code&gt;.&lt;br /&gt;* for each dependency thus listed, it adds that dependency to the list of dependent gems.&lt;br /&gt;* it does the same recursively for each dependent gem until it finds that the current gem has no dependencies, or that some (recursively) dependent gem is not installed on the system.&lt;br /&gt;* it builds a Graphviz graph from the graph so built.&lt;br /&gt;* it runs the awesome &lt;code&gt;tred&lt;/code&gt; utility on the resulting Graphviz graph, which translates the original messy graph into one that properly represents all of the transitive dependencies.&lt;br /&gt;&lt;br /&gt;If you're not sure what that last bit is about, don't feel bad: I didn't either at first. So here's the picture that's worth a thousand words (give or take):&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_MuRSvkgEbss/SWmE42FEu-I/AAAAAAAAACQ/2K4tLnR4nXU/s1600-h/temp.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 207px;" src="http://3.bp.blogspot.com/_MuRSvkgEbss/SWmE42FEu-I/AAAAAAAAACQ/2K4tLnR4nXU/s400/temp.png" alt="" id="BLOGGER_PHOTO_ID_5289905349447695330" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The difference is subtle, but telling. The second graph shows that the &lt;code&gt;rails&lt;/code&gt; gem has dependencies on &lt;code&gt;activesupport&lt;/code&gt; and &lt;code&gt;actionpack&lt;/code&gt;. Which is true, as far as it goes, and is reflected in the output of the &lt;code&gt;gem dep&lt;/code&gt; command thusly:&lt;pre&gt;gem dep rails&lt;br /&gt;Gem rails-2.2.2&lt;br /&gt; rake (&gt;= 0.8.3, runtime)&lt;br /&gt; activesupport (= 2.2.2, runtime)&lt;br /&gt; activerecord (= 2.2.2, runtime)&lt;br /&gt; actionpack (= 2.2.2, runtime)&lt;br /&gt; actionmailer (= 2.2.2, runtime)&lt;br /&gt; activeresource (= 2.2.2, runtime)&lt;/pre&gt;&lt;br /&gt;But it turns out that &lt;code&gt;gem dep&lt;/code&gt; doesn't tell the whole story. &lt;code&gt;rails&lt;/code&gt; does depend on those other gems, but only &lt;i&gt;through&lt;/i&gt; its dependencies on &lt;code&gt;activerecord&lt;/code&gt;, &lt;code&gt;actionmailer&lt;/code&gt;, and &lt;code&gt;activeresource&lt;/code&gt;, as shown in the first graph. That's what the &lt;code&gt;tred&lt;/code&gt; utility (part of the Graphviz package) does: reduce the second graph to its minimal equivalent representation in the first graph.&lt;br /&gt;&lt;br /&gt;Here's another more fun example, for &lt;code&gt;merb&lt;/code&gt;, prior to transitive reduction:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_MuRSvkgEbss/SWmH0_Unr3I/AAAAAAAAACg/VPlgDT8n65U/s1600-h/temp.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 66px;" src="http://2.bp.blogspot.com/_MuRSvkgEbss/SWmH0_Unr3I/AAAAAAAAACg/VPlgDT8n65U/s400/temp.png" alt="" id="BLOGGER_PHOTO_ID_5289908581744226162" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And post &lt;code&gt;tred&lt;/code&gt;:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_MuRSvkgEbss/SWmHKFehv2I/AAAAAAAAACY/M4YZpR-h8qI/s1600-h/merb.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 150px;" src="http://1.bp.blogspot.com/_MuRSvkgEbss/SWmHKFehv2I/AAAAAAAAACY/M4YZpR-h8qI/s400/merb.png" alt="" id="BLOGGER_PHOTO_ID_5289907844662017890" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;This implementation owes much to the existing &lt;code&gt;DepGraph&lt;/code&gt;, especially its use of Graphviz and &lt;code&gt;tred&lt;/code&gt;. I basically just extracted the simple use case of graphing a gem at a time, and neglected to implement a bunch of DepGraph's options. For instance, the output of &lt;code&gt;gemviz&lt;/code&gt; is always a single &lt;code&gt;.png&lt;/code&gt; file per requested gem, named after the gem and placed in the current directory.&lt;br /&gt;&lt;br /&gt;Keep in mind that this is a very simple initial release. Please let me know if you find it useful, and especially what improvements you can think of.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-8833556142881027050?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/8833556142881027050/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=8833556142881027050' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8833556142881027050'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8833556142881027050'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2009/01/gem-is-born-announcing-gemviz.html' title='A Gem Is Born: Announcing Gemviz'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_MuRSvkgEbss/SWmASeadzZI/AAAAAAAAACA/jur-WDbK4jU/s72-c/rails.png' height='72' width='72'/><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-5335749103234596131</id><published>2008-12-30T20:58:00.002-07:00</published><updated>2008-12-30T21:01:36.548-07:00</updated><title type='text'>QOTD: Hans Küng</title><content type='html'>From &lt;span style="font-style:italic;"&gt;The Catholic Church: A Short History&lt;/span&gt;:&lt;br /&gt;&lt;blockquote&gt;Those who deliberately step in all the puddles should not complain too loudly about how bad the road is.&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-5335749103234596131?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/5335749103234596131/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=5335749103234596131' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5335749103234596131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5335749103234596131'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/12/qotd-hans-kng.html' title='QOTD: Hans Küng'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-2465505815096281992</id><published>2008-12-22T20:18:00.003-07:00</published><updated>2008-12-23T19:29:01.044-07:00</updated><title type='text'>Bad RSpec! BAD RSPEC!!</title><content type='html'>My first gig as a consultant for ThoughtWorks has been a whirlwind of security lines, flights to Texas, and Ruby on Rails. I'm very pleased that they were able to make me billable so quickly; to be getting paid to work on Rails again after two years in the Java mines is an extra added bonus.&lt;br /&gt;&lt;br /&gt;My pair and I spent today upgrading one of our apps to Rails 2.2, as a sort of trial run for eventually upgrading all our apps. This involved the usual amount of concomitant upgrades to plugins, with no shortage of "great, this is broken...now something else is broken...not that too!" hijinks. The best part of the day, though, was the RSpec tests that kept failing no matter what we tried to throw at them.&lt;br /&gt;&lt;br /&gt;Long story short, we have a controller that uses a &lt;code&gt;rescue_from&lt;/code&gt; handler to redirect in the event of a connection error. Works like a charm when we're clicking through the app. But after upgrading RSpec and RSpec-Rails it simply refused to pass the specs that verified the redirect behavior.&lt;br /&gt;&lt;br /&gt;It turns out that the latest version of RSpec has some secret sauce that defeats &lt;code&gt;rescue_from&lt;/code&gt;, instead going directly to raising the exception that needed rescuing from in the first place. I guess I wouldn't mind so much if we didn't have several behavior-driven tests that, you know, &lt;b&gt;tested behavior&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Not too cool, Rudy.&lt;br /&gt;&lt;br /&gt;There is, of course, a workaround. Simply add &lt;code&gt;controller.use_rails_error_handling!&lt;/code&gt; to your test and you can defeat RSpec's defeating of &lt;code&gt;rescue_from&lt;/code&gt;. But, just as double negatives fail to not clutter English prose (see what I did there?), having to demand that RSpec get out of the way of Rails' standard and -- in this case -- reasonable behavior just uglies up my code. I would much rather the maintainers of RSpec had elected to make the ability to bypass the rescue handler and skip straight to the underlying exception an "opt-in" behavior.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-2465505815096281992?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/2465505815096281992/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=2465505815096281992' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/2465505815096281992'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/2465505815096281992'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/12/bad-rspec-bad-rspec.html' title='Bad RSpec! BAD RSPEC!!'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-9116200760088749574</id><published>2008-12-01T09:15:00.003-07:00</published><updated>2008-12-01T09:33:44.745-07:00</updated><title type='text'>One of These Things is Not Like the Others...</title><content type='html'>I (heart) Amazon's recommendations service, but...&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_MuRSvkgEbss/STQRQimj-8I/AAAAAAAAAB4/XZfmLy3j1pg/s1600-h/AmazonRecommendations.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 160px;" src="http://4.bp.blogspot.com/_MuRSvkgEbss/STQRQimj-8I/AAAAAAAAAB4/XZfmLy3j1pg/s400/AmazonRecommendations.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5274860039421819842" /&gt;&lt;/a&gt;&lt;br /&gt;...I'm not entirely sure what the message is here. For me or any other fans of programming languages and/or algorithms.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-9116200760088749574?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/9116200760088749574/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=9116200760088749574' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/9116200760088749574'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/9116200760088749574'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/12/one-of-these-things-is-not-like-others.html' title='One of These Things is Not Like the Others...'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_MuRSvkgEbss/STQRQimj-8I/AAAAAAAAAB4/XZfmLy3j1pg/s72-c/AmazonRecommendations.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-506322514259469150</id><published>2008-10-17T23:07:00.002-06:00</published><updated>2008-10-17T23:10:05.035-06:00</updated><title type='text'>QOTD: Donald Knuth</title><content type='html'>Ripped directly from &lt;a href="http://www.informit.com/articles/article.aspx?p=1193856"&gt;this interview:&lt;/a&gt;&lt;br /&gt;&lt;blockquote&gt;...I trust my family jewels only to Linux.&lt;/blockquote&gt;I'm a Mac guy myself, but...respect, brother. Respect.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-506322514259469150?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/506322514259469150/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=506322514259469150' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/506322514259469150'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/506322514259469150'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/10/qotd-donald-knuth.html' title='QOTD: Donald Knuth'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-1075645083184048411</id><published>2008-08-15T23:45:00.011-06:00</published><updated>2008-08-16T00:10:24.482-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Regarding Ruby, instance_of?, kind_of?, and ===</title><content type='html'>So I was reading through some Ruby source code tonight -- primarily because I haven't in a while, thanks to a busy stretch of &lt;a href="http://www.stratavia.com/datapalette.php"&gt;Java work&lt;/a&gt; -- and I ran across an idiom that I found a little confusing at first. The Ruby source code belongs to Why the Lucky Stiff's Shoes project, "a very informal graphics and windowing toolkit" (according to &lt;a href="http://code.whytheluckystiff.net/shoes/"&gt;the official website&lt;/a&gt;). &lt;br /&gt;&lt;br /&gt;One of the first files I looked at, lib/shoes.rb, opens right up with this interesting bit of interestingness:&lt;pre&gt;class Range &lt;br /&gt;  def rand &lt;br /&gt;    conv = (Integer === self.end &amp;&amp; Integer === self.begin &lt;br /&gt;        ? :to_i &lt;br /&gt;        : :to_f)&lt;br /&gt;    ((Kernel.rand * (self.end - self.begin)) &lt;br /&gt;        + self.begin).send(conv) &lt;br /&gt;  end &lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;Okay. So we're defining a &lt;code&gt;rand()&lt;/code&gt; method on the built-in Ruby class &lt;code&gt;Range&lt;/code&gt;, which will return a random value from within the &lt;code&gt;begin&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt; values of the &lt;code&gt;Range&lt;/code&gt;. Neato. And apparently the use of &lt;code&gt;conv&lt;/code&gt; is meant to produce a result of a float or an integer, depending on the nature of the endpoints of the &lt;code&gt;Range&lt;/code&gt;. Again: neato. But I hadn't seen the use of the &lt;code&gt;===&lt;/code&gt; operator in this context before. &lt;br /&gt;&lt;br /&gt;The docs and the Pickaxe book are a little obtuse on this, so I did some &lt;code&gt;irb&lt;/code&gt; spelunking:&lt;pre&gt;&gt;&gt; r = (1..27)&lt;br /&gt;=&gt; 1..27&lt;br /&gt;&gt;&gt; r.class&lt;br /&gt;=&gt; Range&lt;br /&gt;&gt;&gt; r.begin&lt;br /&gt;=&gt; 1&lt;br /&gt;&gt;&gt; r.begin.instance_of? Integer&lt;br /&gt;=&gt; false&lt;br /&gt;&gt;&gt; r.begin.class&lt;br /&gt;=&gt; Fixnum&lt;br /&gt;&gt;&gt; r.begin.kind_of? Fixnum&lt;br /&gt;=&gt; true&lt;br /&gt;&gt;&gt; r.begin.kind_of? Integer&lt;br /&gt;=&gt; true&lt;br /&gt;&lt;/pre&gt;Hmm. So &lt;code&gt;instance_of()&lt;/code&gt; doesn't mean quite the same in Ruby as, say, the &lt;code&gt;instanceof&lt;/code&gt; operator in Java does. (That, or &lt;code&gt;Fixnum&lt;/code&gt; doesn't truly inherit from &lt;code&gt;Integer&lt;/code&gt; in Ruby.)&lt;br /&gt;&lt;br /&gt;Also, as it turns out:&lt;pre&gt;&gt;&gt; Fixnum === r.begin&lt;br /&gt;=&gt; true&lt;br /&gt;&gt;&gt; Integer === r.begin&lt;br /&gt;=&gt; true&lt;br /&gt;&lt;/pre&gt;So it would seem that the &lt;code&gt;[class] === [value]&lt;/code&gt; syntax is syntactic sugar for &lt;code&gt;[value].kind_of? [class]&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;It's the bit about: &lt;pre&gt;&gt;&gt; r.begin.instance_of? Integer&lt;br /&gt;=&gt; false&lt;br /&gt;&lt;/pre&gt;that surprised me most, being the pathetic Java programmer that I am. And being that (according to the documentation for Integer on ruby-doc.org):&lt;pre&gt;&lt;code&gt;Integer&lt;/code&gt; is the basis for the two concrete classes that hold &lt;br /&gt;whole numbers, &lt;code&gt;Bignum&lt;/code&gt; and &lt;code&gt;Fixnum&lt;/code&gt;.&lt;br /&gt;&lt;/pre&gt; Apparently "is the basis for" != "is a superclass of".&lt;br /&gt;&lt;br /&gt;Or is that "!==="...?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-1075645083184048411?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/1075645083184048411/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=1075645083184048411' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/1075645083184048411'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/1075645083184048411'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/08/regarding-ruby-instanceof-kindof-and.html' title='Regarding Ruby, instance_of?, kind_of?, and ==='/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-8711821293599514078</id><published>2008-07-07T15:16:00.005-06:00</published><updated>2008-07-07T16:37:38.403-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><title type='text'>Fixing Eclipse's Workspace Dialog: A Unix One-Liner in Only Three (Okay, Four) Lines</title><content type='html'>&lt;pre&gt;&lt;br /&gt;cd /Applications/eclipse_3_3/configuration/.settings &lt;br /&gt;  &amp;&amp; sed '/RECENT_WORKSPACES/d' org.eclipse.ui.ide.prefs &lt;br /&gt;    &gt; org.eclipse.ui.ide.prefs.fixed &lt;br /&gt;  &amp;&amp; mv org.eclipse.ui.ide.prefs.fixed org.eclipse.ui.ide.prefs&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I usually (heart) Eclipse, but sometimes I just want to smack it upside its virtual head. One of those times is when it's starting up and I change focus to some other app and start typing something and Eclipse at some point grabs focus and inserts whatever I thought I was typing somewhere else and prepends it to its "which workspace do you want to use?" dialog and promptly creates a new workspace with a name of whatever nonsense text ended up in the dialog box. You know?&lt;br /&gt;&lt;br /&gt;So I now have this little bit of bash magic saved as &lt;code&gt;zapEclipseWorkspaceSelection.sh&lt;/code&gt; in my home directory. The downside is that it zaps &lt;b&gt;all&lt;/b&gt; of your prior workspace selections, not just the smack-Eclipse-upside-the-head ones. You could maybe modify it to, say, ask for input on what invalid workspace names it should delete. Not me, man. Subtlety is not one of my vices.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-8711821293599514078?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/8711821293599514078/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=8711821293599514078' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8711821293599514078'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8711821293599514078'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/07/fixing-eclipses-workspace-dialog-unix.html' title='Fixing Eclipse&apos;s Workspace Dialog: A Unix One-Liner in Only Three (Okay, Four) Lines'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-3620220659652872719</id><published>2008-06-20T11:57:00.018-06:00</published><updated>2009-02-12T11:53:50.416-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='vmware'/><title type='text'>HOWTO: Display a Custom Boot Message for Your VMWare Appliance</title><content type='html'>So I'm building a VMWare appliance for &lt;a href="http://stratavia.com"&gt;Stratavia's&lt;/a&gt; Data Palette data center automation application, and I want it to be like all the other &lt;a href="http://www.vmware.com/appliances/"&gt;cool appliances&lt;/a&gt; that print out some system-specific configuration information (e.g., IP address) when it finishes booting. This is useful for when, say, your appliance runs a webapp via an HTTP server like Apache or Mongrel or Tomcat and you want to tell the user where to point his browser to see it in action. &lt;br /&gt;&lt;br /&gt;I happen to be using the latest Ubuntu JEOS as the base OS for this appliance. As such, in order to hook in to the boot process, I am adding my changes to the &lt;code&gt;/etc/rc.local&lt;/code&gt; file. &lt;br /&gt;&lt;br /&gt;&lt;i&gt;(Author's Note: The exact location and name of this file vary -- sometimes wildly -- between Linuxen. Some flavors use &lt;code&gt;/etc/rc.d/rc.local&lt;/code&gt;. Gentoo uses &lt;code&gt;/etc/conf.d/local.start&lt;/code&gt;. I paraphrase the immortal Daffy Duck in "The Scarlet Pumpernickel": "Fancy, thy name is Linux. To coin a phrase.")&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Let's say you want your user to visit &lt;code&gt;http://[whatever the IP address ends up being]&lt;/code&gt; following bootup to get the virtual ball rolling. But you don't know what that IP address will be when you bundle your appliance; it's going to be assigned dynamically at bootup (as it should be). &lt;i&gt;No problema&lt;/i&gt;. Simply write a bit of script to scrape the IP address in your Linux flavor's version of &lt;code&gt;rc.local&lt;/code&gt;, thusly (Note: The following bit of awesome is directly ripped off from the &lt;code&gt;rc.local&lt;/code&gt; file of the &lt;a href="http://wiki.mindtouch.com/Deki_Wiki"&gt;MindTouch Deki Wiki&lt;/a&gt; appliance):&lt;pre&gt;IP=`ifconfig eth0 | grep "inet addr" | awk -F ' ' '{print $2}' &lt;br /&gt;  | awk -F ':' '{print $2}'`&lt;/pre&gt;If you're Richard Stallman or some other Unix guru and you already know what the heck all this does, you can just smile smugly and skip the next few paragraphs. For the rest of us:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;ifconfig eth0&lt;/code&gt; -- Run the system command &lt;code&gt;ifconfig&lt;/code&gt; to print out the general networking information for this box (specifically, the first ethernet interface), and send ("pipe") its output to the &lt;code&gt;grep&lt;/code&gt; command. The output for &lt;code&gt;ifconfig eth0&lt;/code&gt; will look something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;eth0      Link encap:Ethernet HWaddr 00:0c:29:2d:f7:73&lt;br /&gt;          inet addr:172.16.139.130  Bcast:172.16.139.255  Mask:255.255.255.0&lt;br /&gt;          inet6 addr: fe80::20c:29ff:fe2d:f773/64 Scope:Link&lt;br /&gt;          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1&lt;br /&gt;          &lt;i&gt;[et cetera]&lt;/i&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;There are several more lines, but all we care about is the second line. The next command in the chain, &lt;code&gt;grep "init addr"&lt;/code&gt;, will filter out all the stuff we don't want, leaving us with just the second line. This is then fed to the first of two &lt;code&gt;awk&lt;/code&gt; scripts. &lt;br /&gt;&lt;br /&gt;The first &lt;code&gt;awk&lt;/code&gt; script uses a field separator of "space" (&lt;code&gt;-F ' '&lt;/code&gt;); this splits the inet addr line into its constituent "words", like "inet" and "addr:172.16.139.130". We just want the "addr:" word, so we print it as the output of the first &lt;code&gt;awk&lt;/code&gt; script and send it to the second (&lt;code&gt;{print $2}&lt;/code&gt;). &lt;br /&gt;&lt;br /&gt;The second &lt;code&gt;awk&lt;/code&gt; script splits its input using the colon as its field separator (&lt;code&gt;-F ':'&lt;/code&gt;) and prints the second word, which is -- tada! -- the IP address of the virtual machine. Whew.&lt;br /&gt;&lt;br /&gt;The Rubyistas reading this blog (&lt;a href="http://davidrupp.blogspot.com/2007/10/last-language-war-language-trolling.html"&gt;assuming I have any left&lt;/a&gt;) may have downloaded Ezra Zygmuntowicz's way-cool &lt;a href="http://express.engineyard.com/"&gt;EngineYard Express VMWare image&lt;/a&gt;. Its bootup a little more involved, asking for input from the user on initial boot, and displaying several bits of information after every boot thereafter, such as the randomly-generated &lt;code&gt;root&lt;/code&gt; and &lt;code&gt;express&lt;/code&gt; user passwords. EY Express is running on Gentoo, so its startup hook is in &lt;code&gt;/etc/conf.d/local.start&lt;/code&gt;. &lt;br /&gt;&lt;br /&gt;EY being EY, they delegate all the heavy lifting to Ruby via some custom Ruby Gems: &lt;code&gt;ey_reporter&lt;/code&gt;, &lt;code&gt;config_system&lt;/code&gt;, and &lt;code&gt;system&lt;/code&gt;. You have to do some serious filesystem spelunking to get there, but you can find the Ruby code that figures out the IP address (the equivalent of the bash script above) in &lt;pre&gt;/usr/lib/ruby/gems/1.8&lt;br /&gt;  /gems/system-[version]/lib&lt;br /&gt;  /system/info/info.rb&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-3620220659652872719?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/3620220659652872719/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=3620220659652872719' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3620220659652872719'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3620220659652872719'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/06/howto-set-custom-boot-message-for-your.html' title='HOWTO: Display a Custom Boot Message for Your VMWare Appliance'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-1902314166335302916</id><published>2008-06-09T12:54:00.007-06:00</published><updated>2008-07-07T16:38:38.671-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='iphone'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>WWDC Keynote 2008: Are You Kidding Me?</title><content type='html'>iPhone and the .Mac replacement: About two full hours.&lt;br /&gt;&lt;br /&gt;Next-gen Mac OS (10.6 -- "Snow Leopard"): About 10 seconds (a brief announcement that it's being discussed in an afternoon session with Bertrand Serlet). &lt;br /&gt;&lt;br /&gt;New Mac hardware (e.g., the rumored Mac tablet): 0 seconds.&lt;br /&gt;&lt;br /&gt;Mac hardware updates (e.g., the rumored laptop redesign/s): 0 seconds.&lt;br /&gt;&lt;br /&gt;"One-More-Thing": 0 seconds.&lt;br /&gt;&lt;br /&gt;I'm just waiting for this announcement from Cupertino:&lt;blockquote&gt;Apple Inc. (formerly-and-clearly-no-longer Apple &lt;b&gt;Computer&lt;/b&gt; Inc.) proudly announces the obliteration of any &lt;b&gt;computer&lt;/b&gt;-related product from its catalog. Going forward, the laptop computer formerly known as the "MacBook Air" will be known as the "iPhone Maxi"&lt;sup&gt;*&lt;/sup&gt;. And the segment-dominating laptop computers formerly known as the "MacBook" and "MacBook Pro" will be renamed the "iPhone Big-n-Bulky"&lt;sup&gt;*&lt;/sup&gt; and "iPhone Bigger-n-Bulkier"&lt;sup&gt;*&lt;/sup&gt;.&lt;br /&gt;&lt;br /&gt;&lt;small&gt;&lt;i&gt;&lt;sup&gt;*&lt;/sup&gt;iPhone cell phone functionality not included.&lt;/i&gt;&lt;/small&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-1902314166335302916?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/1902314166335302916/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=1902314166335302916' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/1902314166335302916'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/1902314166335302916'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/06/wwdc-keynote-2008-are-you-kidding-me.html' title='WWDC Keynote 2008: Are You Kidding Me?'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-7577119155508037543</id><published>2008-05-21T22:32:00.005-06:00</published><updated>2008-05-22T09:57:49.348-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><title type='text'>[Mac] pbcopy == Brilliant</title><content type='html'>For Mac users only (as far as I know):&lt;pre&gt;cat &lt;i&gt;filename&lt;/i&gt; | pbcopy&lt;/pre&gt;This bit of magic, as I just discovered, will copy the contents of the &lt;code&gt;cat&lt;/code&gt;-ed file to the clipboard, for easy pasting wherever you need to paste it. &lt;br /&gt;&lt;br /&gt;Macs. Rule.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-7577119155508037543?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/7577119155508037543/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=7577119155508037543' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7577119155508037543'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7577119155508037543'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/05/mac-pbcopy-brilliant.html' title='[Mac] &lt;code&gt;pbcopy&lt;/code&gt; == Brilliant'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-6616684649887499653</id><published>2008-05-11T21:10:00.004-06:00</published><updated>2008-05-11T21:22:31.426-06:00</updated><title type='text'>[OT] Out of the Mouths of Babes...</title><content type='html'>I promise I'm not going to turn into one of those bloggers who turns his blog into a blog about blogging every precious blogging word that comes out of his three-year-old son's mouth. But I'm submitting this to the Interbits in the hopes that some day when I'm old and grey and he's graduating from high school I'll be to retrieve it and *really* get some peoples' eyes rolling.&lt;br /&gt;&lt;br /&gt;So we're celebrating Mother's Day and my wife gets to the card that "he" gave her. It's got a Winnie the Pooh theme, with a typical "you're the best mommy ever" verse. Inside is a picture of Kanga (also a mommy) giving Roo (also a little boy) an affectionate hug. So Angela points to the picture and says, "Look, Alex, it's me and you!". And he looks at her with his eyes wide, then he looks at the picture again, and says in a tone that implies that he's a little concerned about how she's not keeping current with her medication, "Yeeeaaahhhh........and we've turned into kangaroos!"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-6616684649887499653?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/6616684649887499653/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=6616684649887499653' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6616684649887499653'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6616684649887499653'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/05/ot-out-of-mouths-of-babes.html' title='[OT] Out of the Mouths of Babes...'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-1074192562865283792</id><published>2008-05-09T14:37:00.005-06:00</published><updated>2008-05-09T14:43:26.838-06:00</updated><title type='text'>The Java Process That Ate My MacBook Pro</title><content type='html'>Gotta love the Phenomenal Cosmic Power of virtual memory (the next-to-last column in the following ActivityMonitor screen capture):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_MuRSvkgEbss/SCS28FtGDUI/AAAAAAAAABc/rnJ0RUckv6Q/s1600-h/RogueJavaProcess.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_MuRSvkgEbss/SCS28FtGDUI/AAAAAAAAABc/rnJ0RUckv6Q/s400/RogueJavaProcess.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5198481013332118850" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-1074192562865283792?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/1074192562865283792/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=1074192562865283792' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/1074192562865283792'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/1074192562865283792'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/05/java-process-that-ate-my-macbook-pro.html' title='The Java Process That Ate My MacBook Pro'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_MuRSvkgEbss/SCS28FtGDUI/AAAAAAAAABc/rnJ0RUckv6Q/s72-c/RogueJavaProcess.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-5531488383650855243</id><published>2008-05-08T22:31:00.014-06:00</published><updated>2008-05-09T09:58:19.681-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='debug'/><title type='text'>Debugging a Remote Java VM with Eclipse</title><content type='html'>I (heart) the internet. Why? Well, among &lt;a href="http://store.apple.com"&gt;other&lt;/a&gt; &lt;a href="http://www.xkcd.com"&gt;reasons&lt;/a&gt;, I ran across &lt;a href="http://www.juixe.com/techknow/index.php/2006/12/13/java-remote-debug/"&gt;this blog post&lt;/a&gt; today, which totally saved me a good several hours of frustration and swearing whilst tracking down a thorny problem.&lt;br /&gt;&lt;br /&gt;You see, at &lt;a href="http://www.stratavia.com"&gt;my day job&lt;/a&gt;, I help to develop this webapp that points to a number of Java VMs, running in various combinations of in-Eclipse/not-in-Eclipse. The not-in-Eclipse bits are not run in Eclipse for a reason (read: "I don't care to jump through the hoops necessary to force them to."); however, lamentably, sometimes I get to (read: "have no choice but to") debug those bits too. And so, on the advice of &lt;a href="http://www.juixe.com/techknow/index.php/2006/12/13/java-remote-debug/"&gt;the aforementioned blog post&lt;/a&gt;, I added these options to my startup scripts for the to-be-remotely-debugged VMs: &lt;pre&gt;-Xdebug -Xrunjdwp:transport=dt_socket,address=8001,server=y,suspend=n&lt;/pre&gt; did some Eclipse magic ('Run | Open Debug Dialog... | (right-click) Remote Java Application | New | (fill in the details)') to point my debugger at the appropriate host and port, and I was off to the races.&lt;br /&gt;&lt;br /&gt;The moral of the story: some days you just &lt;span style="font-weight:bold;"&gt;can&lt;/span&gt; get rid of a bomb. &lt;span style="font-style:italic;"&gt;&lt;br /&gt;&lt;br /&gt;(Author's Note: Bonus points to any commenters who can identify &lt;span style="font-weight:bold;"&gt;that&lt;/span&gt; seriously obscure reference!)&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-5531488383650855243?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/5531488383650855243/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=5531488383650855243' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5531488383650855243'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5531488383650855243'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/05/debugging-remote-java-vm-with-eclipse.html' title='Debugging a Remote Java VM with Eclipse'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-2029134442070404051</id><published>2008-04-29T22:53:00.028-06:00</published><updated>2008-04-30T09:56:24.449-06:00</updated><title type='text'>In Which I Reconnect With Ruby Just in Time to Finish My Master's Degree</title><content type='html'>The fates have conspired to force me to end my graduate school career with a bang. Fortunately, the bang is not the sound of my head exploding, as I feared it would be at the beginning of this semester. I'm taking two classes -- Theory of Complexity (about P vs. NP, BPP, #P, complexity of quantum computing...you know the drill; if you don't know the drill, try &lt;a href="http://qwiki.stanford.edu/wiki/Complexity_Zoo"&gt;The Complexity Petting Zoo&lt;/a&gt; for an introduction) and Applied Graph Theory. Each is fascinating in its own right, but the graph theory class has really made an impression on me. So much so that I think if I'd taken it earlier on, I probably would have been tempted to do a thesis in that area. As it is, I'm stuck with my lame default choice of the "coursework-only" option for the Master's degree I'll be finishing come mid-May (the 17th, just in case anyone wants to &lt;a href="http://www.amazon.com/gp/registry/wishlist/33MXNXWJHHS4D"&gt;send a present&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;However, to my credit, I did manage to complete a significant semester-long project in graph theory, using Ruby as the implementation language. Why Ruby, and not Java (which &lt;a href="http://stratavia.com"&gt;I use professionally&lt;/a&gt; right now) or Scala, or any of the other possibilities I blather about on this blog? &lt;br /&gt;&lt;br /&gt;Well, if you're a seasoned Ruby user you'll know why -- there's not much else (that I'm accustomed to) that's suited to the kind of rapid development/iterability I needed to get this thing done on time. My primary concerns throughout were a) to be able to complete the implementation with enough time left over to write up my results and prepare my in-class presentation, and 2) see concern a) above. Sure, Java's static typing or Scala's functional orientation might have given me some (nominally) safer code, but here, speed of development was of the essence. Advantage: Ruby.&lt;br /&gt;&lt;br /&gt;The actual project? It's an implementation of an algorithm due to Hermann Stamm-Wilbrandt of the University of Bonn (Google is your friend...) to draw a maximal planar graph in linear time. Actually, the intent of the core algorithm is to "embed", not "draw" the graph; although I also implemented an optional enhancement to assign x-y coordinates to the vertices of the embedded graph for drawing purposes. Using Ruby, I was able to implement the entire project in less than 500 lines -- including blank lines, comments, debug, and other cruft -- of gloriously unoptimized, un-statically-typed, un-refactored, un-IDE'd-to-within-an-inch-of-its-life Ruby code. &lt;br /&gt;&lt;br /&gt;I shudder to think what the LOC count would have been for Java.&lt;br /&gt;&lt;br /&gt;Even in Ruby, the bulk of the code was either utilitarian in nature (e.g., a Line class and a Triangle class to help with the geometry of deciding where vertices should lie in the plane; some vector math for seeing if two points are on the same side of a line), or was wrapped up in simple one-to-two-liner convenience methods to support the main objective. This stuff accounted for about 120 lines. And the &amp;lt;500 lines of code also includes my from-scratch graph data structure (typically of the adjacency list variety), along with about 35 lines of code to emit the output in displayable format. The guts of the actual implementation of Stamm-Wilbrandt's embedding algorithm took about 130 lines of Ruby code (including blanks and comments), plus another 70 or so to handle a complicated special case of the optional x-y coordinate assignment.&lt;br /&gt;&lt;br /&gt;My only regret is that I don't know Python well enough to have done the entire implementation in that language. I say that primarily because I used the totally awesome &lt;a href="http://nodebox.net"&gt;NodeBox&lt;/a&gt; as my drawing solution. NodeBox is written using PyObjC (Mac only -- sorry Windows/*n[i|u]x users!), and uses Python as its scripting language. My Ruby implementation actually emits a Python script which I then open with NodeBox to draw the final graph. If it were implemented in Python, then it could just be a NodeBox library.&lt;br /&gt;&lt;br /&gt;The Good News: Now I have an excellent excuse to learn Python...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-2029134442070404051?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/2029134442070404051/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=2029134442070404051' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/2029134442070404051'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/2029134442070404051'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/04/in-which-i-reconnect-with-ruby-just-in.html' title='In Which I Reconnect With Ruby Just in Time to Finish My Master&apos;s Degree'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-6334894794111720158</id><published>2008-02-07T20:51:00.000-07:00</published><updated>2008-02-08T09:00:13.735-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IE'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='prototype'/><title type='text'>IE Misbehaving? Fix It With Prototype!</title><content type='html'>&lt;span style="font-style:italic;"&gt;Disclaimer: Since I specifically make reference to where I work in this post, I thought I should also specifically make reference to the fact that on this blog in general, and in this post in particular, I do not speak for my company in any way shape or form. My opinions are mine. Mine, mine, mine! If my company wants to express its opinions, it can get its own damn blog!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So at &lt;a href="http://www.stratavia.com"&gt;my day job&lt;/a&gt; we're converting the front end of our product from a Java thick client to a Struts 2 / JSP / several-other-buzzwords webapp. We officially support three browsers (in no particular order other than my blatant preference): Firefox, Safari, and Internet Explorer. We're using a fair amount of Ajax via Prototype and Scriptaculous. Again, no big surprise.&lt;br /&gt;&lt;br /&gt;One of the problems we've encountered with our Ajax stuff is that anything we return from a normal Ajax call is subject to caching by the browser. Now I know the standard tricks to defeat caching are to a) submit the request as a POST; and 2) submit as a GET, but append a random string (e.g., a timestamp) to the request string to force the browser to fail on the cache lookup.&lt;br /&gt;&lt;br /&gt;Unfortunately, being the curmudgeonly programmer and language maven that I am, I object to both of these approaches. Sometimes (most times) GET is the proper verb to use for my request; it's not changing state on the server, just retrieving it. POST should be used for changing state. The random string hack is just that -- a hack. If the browser is going to cache the results of a single request, it's probably going to cache the result of each "unique" request we create by tacking on this otherwise-irrelevant snippet of "data". I don't want all that unnecessary cruft in my customers' caches if I can help it.&lt;br /&gt;&lt;br /&gt;There must be a better way.&lt;br /&gt;&lt;br /&gt;Fortunately, there is a better way as of HTTP 1.1, in the form of the &lt;code&gt;Cache-Control&lt;/code&gt; response header. See &lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html"&gt;Section 14.9&lt;/a&gt; of the HTTP 1.1 spec for all the gory details. Suffice it to say, including a response header of &lt;code&gt;Cache-Control: no-cache&lt;/code&gt; with all of our Ajax responses worked to defeat caching of our Ajax-GET snippets.&lt;br /&gt;&lt;br /&gt;Except on IE.&lt;br /&gt;&lt;br /&gt;Are you surprised? I was, but only because &lt;code&gt;Cache-Control: no-cache&lt;/code&gt; actually works on IE, to a point. So it wasn't obvious at first that IE was screwing up. Fortunately, we have the world's best quality engineering team, so they caught the problem when we lowly developers weren't seeing it.&lt;br /&gt;&lt;br /&gt;Long story short: it turns out that (in our application at least; YMMV) the &lt;code&gt;Cache-Control: no-cache&lt;/code&gt; response header actually works to prohibit caching in IE. Until the response is &amp;gt; ~8K in size. At which point IE caches it anyway. Which leads to all kinds of fun "but I &lt;b&gt;saved&lt;/b&gt; my edits, I &lt;b&gt;know&lt;/b&gt; I did" debates between QE and development when the browser returns the cached results instead of the latest and greatest. &lt;br /&gt;&lt;br /&gt;This is where Prototype comes in. You see, Prototype already has a set of headers it adds to every Ajax request submitted through it. For example, it sets the header &lt;code&gt;X-Requested-With&lt;/code&gt; to &lt;code&gt;XMLHttpRequest&lt;/code&gt;. We use this in our app to distinguish between Ajax GETs and non-Ajax (NAjax?) GETs. Comes in handy sometimes. &lt;i&gt;(Note: See Prototype's source file &lt;code&gt;prototype.js&lt;/code&gt;, specifically the &lt;code&gt;Ajax.Request.setRequestHeaders()&lt;/code&gt; function for the details on this; ~ line 1241 in Prototype 1.6.0.2.)&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;So the trick is to get Prototype to include another header with its Ajax requests: &lt;code&gt;If-Modified-Since&lt;/code&gt;. Set &lt;code&gt;If-Modified-Since&lt;/code&gt; to some datetime in the past, and the browser should always go to the server instead of the (expired) cache. I could do this by hacking my prototype.js file to include this new header in &lt;code&gt;Ajax.Request.setRequestHeaders()&lt;/code&gt;, but then I'd have to remember to re-hack every time I upgrade Prototype. Not fun. I could also submit it as a patch, I suppose, but I don't know that everyone needs this behavior by default.&lt;br /&gt;&lt;br /&gt;Enter The Next Best Thing: Functional Programming! Prototype has a wonderful FP-ish function called &lt;code&gt;wrap()&lt;/code&gt;, which is defined on &lt;code&gt;Element.Methods&lt;/code&gt; (prototype.js, ~ line 1652 in 1.6.0.2). &lt;code&gt;wrap()&lt;/code&gt; lets me extend the existing &lt;code&gt;setRequestHeaders()&lt;/code&gt; function from the outside thusly:&lt;pre&gt;Ajax.Request.prototype.setRequestHeaders =&lt;br /&gt;Ajax.Request.prototype.setRequestHeaders.wrap(&lt;br /&gt;  function(original) {&lt;br /&gt;    // do my stuff first; e.g., set 'If-Modified-Since'&lt;br /&gt;    original() // then call the original version&lt;br /&gt;})&lt;/pre&gt;In case this isn't clear, I'm replacing the original definition of the &lt;code&gt;setRequestHeaders()&lt;/code&gt; function with a new (anonymous) function that does my stuff first, &lt;b&gt;then&lt;/b&gt; does whatever the original was defined to do. To Java programmers circa 2004, this looks kind of like "before advice" in Aspect-Oriented Programming. Without the new-language-compiler-tools-runtime-stack baggage. To functional programming types, it looks like function composition. Without the lambdas and general my-thesis-is-bigger-than-yours snootiness.&lt;br /&gt;&lt;br /&gt;To me it looks like a pretty nifty hack, one that plays by everyone's rules. &lt;br /&gt;&lt;br /&gt;Well. Except IE's. &lt;br /&gt;&lt;br /&gt;But I'm okay with that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-6334894794111720158?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/6334894794111720158/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=6334894794111720158' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6334894794111720158'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6334894794111720158'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/02/ie-misbehaving-fix-it-with-prototype.html' title='IE Misbehaving? Fix It With Prototype!'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-6375773432730182670</id><published>2008-02-01T12:06:00.000-07:00</published><updated>2008-02-01T12:16:33.969-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pragmatic'/><category scheme='http://www.blogger.com/atom/ns#' term='bonehead'/><title type='text'>From the Bonehead File: Pragmatic Programmers Books And "Save As PDF"</title><content type='html'>I (heart) my MacBook Pro.&lt;br /&gt;&lt;br /&gt;One of the main reasons I (heart) my MacBook Pro is Mac OS X's built-in "Print... | PDF | Save as PDF..." feature. I've been reading a lot of papers from various online sources recently for my schoolwork, and I love being able to "print" them to a file on my local filesystem for offline reading. HTML, postscript, whatever format you care to publish your paper in, if I can browse it, I can PDF it.&lt;br /&gt;&lt;br /&gt;Unfortunately, though, I've gotten so much in the habit of printing to PDF that I've unwittingly given away some of the functionality of some of my documents.&lt;br /&gt;&lt;br /&gt;You see, I buy &lt;a href="http://www.pragprog.com/titles/fr_arr"&gt;a&lt;/a&gt; &lt;a href="http://www.pragprog.com/titles/fxruby"&gt;lot&lt;/a&gt; &lt;a href="http://www.pragprog.com/titles/ajax"&gt;of&lt;/a&gt; &lt;a href="http://www.pragprog.com/titles/fr_r4j"&gt;PDF&lt;/a&gt; &lt;a href="http://www.pragprog.com/titles/tpantlr"&gt;versions&lt;/a&gt; of books from the excellent &lt;a href="http://www.pragmaticprogrammer.com"&gt;Pragmatic Programmers&lt;/a&gt; catalog. Their PDFs are, without serious exception, among the most informative resources I have at my disposal. But because my browser renders them, and because I'm so used to "printing" my PDFs, I've gotten into the habit of browsing to them and saving them as PDF from the browser. Which, of course, totally wrecks the internal (and external) hyperlinks they have built into the books. &lt;br /&gt;&lt;br /&gt;D'oh!&lt;br /&gt;&lt;br /&gt;The good news is, the Prags allow one to regenerate purchased PDFs at will &lt;span style="font-style:italic;"&gt;(Note: sorry for the spike in regeneration requests this morning, guys! Give the gerbils an extra pellet or two for me.)&lt;/span&gt;, so I'm back in business, pragmatic-hyperlink-wise.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-6375773432730182670?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/6375773432730182670/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=6375773432730182670' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6375773432730182670'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6375773432730182670'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/02/from-bonehead-file-pragmatic.html' title='From the Bonehead File: Pragmatic Programmers Books And &quot;Save As PDF&quot;'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-5992131857652813138</id><published>2008-01-19T21:50:00.000-07:00</published><updated>2008-01-19T22:17:28.848-07:00</updated><title type='text'>Fox/FXRuby/Leopard, Part the Second</title><content type='html'>As a follow-on to &lt;a href="http://davidrupp.blogspot.com/2008/01/installing-fox-and-fxruby-on-mac-os-x.html"&gt;my previous post&lt;/a&gt;, I discovered that there's a little more work to be done to actually be able to use my freshly installed FXRuby. I tried using &lt;a href="http://media.pragprog.com/titles/fxruby/tables.pdf"&gt;the listexample.rb program&lt;/a&gt; from &lt;a href="http://www.pragprog.com/titles/fxruby"&gt;the aforementioned Pragmatic Programmers book&lt;/a&gt;, and promptly found that the &lt;code&gt;'fox16'&lt;/code&gt; library couldn't be &lt;code&gt;require&lt;/code&gt;d. Grumble.&lt;br /&gt;&lt;br /&gt;Fortunately, &lt;a href="http://rubyforge.org/pipermail/fxruby-users/2006-July/000928.html"&gt;this post&lt;/a&gt; on the fxruby-users mailing list (again, courtesy of Lyle Johnson, lead programmer of FXRuby and author of the PragBook) was able to get me going:&lt;blockquote&gt;a workaround is to use the "gem env" command to identify your Gems installation directory (usually something like /usr/local/lib/ruby/gems/1.8) and switch to the "gems/fxruby-&lt;i&gt;[version]&lt;/i&gt;/ext/fox16" directory underneath that:&lt;pre&gt;cd /usr/local/lib/ruby/gems/1.8/gems/fxruby-1.6.0/ext/fox16&lt;/pre&gt;and then type:&lt;pre&gt;make&lt;/pre&gt;&lt;/blockquote&gt;&lt;i&gt;Note: Since RubyGems comes installed with Leopard, your gem installation directory is more likely to be something like "/Library/Ruby/Gems/1.8", at least if you did a fresh install as I did. YMMV.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;But here I encountered the same architecture problem I referenced in my previous post. This time I ended up editing the &lt;code&gt;Makefile&lt;/code&gt; and deleting all references to "&lt;code&gt;-arch ppc&lt;/code&gt;"; setting the &lt;code&gt;ARCHFLAGS&lt;/code&gt; environment variable before running &lt;code&gt;make&lt;/code&gt; didn't cut it for me.&lt;br /&gt;&lt;br /&gt;Once you've re-made the gem, you should be able to verify your installation in &lt;code&gt;irb&lt;/code&gt;:&lt;pre&gt;&gt;&gt; require 'fox16'&lt;br /&gt;=&gt; true&lt;/pre&gt;Two final caveats:&lt;ul&gt;&lt;li&gt;I had to modify the PragExample with the line "&lt;code&gt;require 'rubygems'&lt;/code&gt;" at the top of the file, right before the "&lt;code&gt;require 'fox16'&lt;/code&gt;" line. This was because I haven't yet set my &lt;code&gt;RUBYOPT&lt;/code&gt; environment variable in Leopard to require rubygems automatically for me.&lt;/li&gt;&lt;li&gt;The example program worked fine for me after all this (try holding the shift button to do multiple selections), until I quit, at which point I got:&lt;pre&gt;X Fatal Error.&lt;br /&gt;Abort trap&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;So this FXRuby thing is not an exact science yet. Sigh.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-5992131857652813138?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/5992131857652813138/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=5992131857652813138' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5992131857652813138'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5992131857652813138'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/01/foxfxrubyleopard-part-second.html' title='Fox/FXRuby/Leopard, Part the Second'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-4051572638664696642</id><published>2008-01-19T18:00:00.001-07:00</published><updated>2008-01-19T21:12:51.714-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='shoes'/><category scheme='http://www.blogger.com/atom/ns#' term='fxruby'/><title type='text'>Installing Fox and FXRuby on Mac OS X Leopard (Intel)</title><content type='html'>I'm planning to start playing with some of the GUI toolkits available for Ruby, and I know there's a &lt;a href="http://www.pragprog.com/titles/fxruby"&gt;Pragmatic Programmers book&lt;/a&gt; (currently in beta) for FXRuby, so I thought I'd get the fixin's in place to be able to play with it. Unfortunately, the standard &lt;code&gt;sudo gem install fxruby&lt;/code&gt; alone didn't get the job done for me. Even using MacPorts to install Fox first (&lt;code&gt;sudo port install fox&lt;/code&gt;) let me down. Here's what (eventually) did work:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;Download and Install Fox:&lt;/b&gt;&lt;br /&gt;    &lt;ul&gt;&lt;br /&gt;      &lt;li&gt;&lt;a href="http://www.fox-toolkit.org/download.html"&gt;Download the source&lt;/a&gt;. I picked the current stable version from the "Downloads" section, "Linux/Unix" subsection.&lt;/li&gt;&lt;br /&gt;      &lt;li&gt;Set the &lt;code&gt;LDFLAGS&lt;/code&gt; environment variable as described in Lyle Johnson's (author of FXRuby and the book) &lt;a href="http://lylejohnson.name/blog/2007/12/01/building-fox-and-fxruby-on-mac-os-x-leopard/"&gt;very helpful post&lt;/a&gt;:&lt;pre&gt;export LDFLAGS="-dylib_file \&lt;br /&gt;/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib:\&lt;br /&gt;/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib"&lt;/pre&gt;&lt;b&gt;Note&lt;/b&gt;: There's a typo in Lyle's instructions that will trip you up if you just cut and paste from there; there should be a space between the "&lt;code&gt;-dylib_file&lt;/code&gt;" and the first "&lt;code&gt;/System/Library/...&lt;/code&gt;". And, in case you're cutting and pasting from here, there should be &lt;b&gt;no space&lt;/b&gt; between the colon and the second "&lt;code&gt;/System/Library/...&lt;/code&gt;".&lt;/li&gt;&lt;br /&gt;      &lt;li&gt;&lt;code&gt;cd&lt;/code&gt; to the expanded Fox source folder.&lt;/li&gt;&lt;br /&gt;      &lt;li&gt;&lt;code&gt;./configure&lt;/code&gt;, with whatever options you would normally use. I didn't specify anything in particular.&lt;/li&gt;&lt;br /&gt;      &lt;li&gt;&lt;code&gt;sudo make install&lt;/code&gt;, again with your typical options. The vanilla command worked for me.&lt;/li&gt;&lt;br /&gt;    &lt;/ul&gt;&lt;br /&gt;  &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;Install the FXRuby gem:&lt;/b&gt;&lt;br /&gt;    &lt;ul&gt;&lt;br /&gt;      &lt;li&gt;&lt;code&gt;sudo env ARCHFLAGS="-arch i386" gem install fxruby&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;      &lt;li&gt;The extra bits about the &lt;code&gt;ARCHFLAGS&lt;/code&gt; are a workaround for a problem in &lt;code&gt;ld&lt;/code&gt; (and/or &lt;code&gt;lipo&lt;/code&gt;) trying to build this gem for both PPC and Intel architectures. As you might guess, this will work for you only if you're on an Intel Mac.&lt;/li&gt;&lt;br /&gt;      &lt;li&gt;From the Credit-Where-Credit-Is-Due department: I cribbed the "&lt;code&gt;env ARCHFLAGS&lt;/code&gt;" trick from the most excellent &lt;a href="http://hivelogic.com"&gt;Hivelogic website&lt;/a&gt;, specifically &lt;a href="http://hivelogic.com/articles/installing-mysql-on-mac-os-x/"&gt;this post&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;    &lt;/ul&gt;&lt;br /&gt;  &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;Write a cool GUI!&lt;/b&gt; &lt;i&gt;(Haven't gotten that far on this item yet...)&lt;/i&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Oh, and I'll be looking at &lt;a href="http://code.whytheluckystiff.net/shoes/"&gt;Shoes&lt;/a&gt; too. No weird Unix/Linux project dependencies for that one, thank goodness. Just a regular .dmg to download and install. Nice.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-4051572638664696642?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/4051572638664696642/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=4051572638664696642' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4051572638664696642'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4051572638664696642'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/01/installing-fox-and-fxruby-on-mac-os-x.html' title='Installing Fox and FXRuby on Mac OS X Leopard (Intel)'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-5236335782842618561</id><published>2008-01-17T13:57:00.000-07:00</published><updated>2008-01-17T14:27:39.463-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='language'/><title type='text'>Required Reading: The Role of the Study of Programming Languages...</title><content type='html'>Via the always excellent &lt;a href="http://patricklogan.blogspot.com/2008/01/more-on-stable-layers-regarding.html"&gt;Patrick Logan&lt;/a&gt; -- Daniel Friedman: &lt;a href="http://www.cs.indiana.edu/hyplan/dfried/mex.pdf"&gt;The Role of the Study of Programming Languages in the Education of a Programmer.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Some key quotes:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;On the lack of tail call support in Java: &lt;blockquote&gt;Guy Steele...was promised back in 1997 that this flaw would be fixed. &lt;b&gt;Here it is 2001&lt;/b&gt; and there is still no resolution of this problem on the horizon.&lt;/blockquote&gt;Emphasis mine. The more things change...&lt;/li&gt;&lt;br /&gt;&lt;li&gt;On Friedman's own background and educational goals: &lt;blockquote&gt;Later, I wanted to be able to implement a language per week.&lt;/blockquote&gt;Followed later by: &lt;blockquote&gt;I want the student to be able to implement (perhaps crudely) every language that they study...&lt;/blockquote&gt;Now &lt;b&gt;that's&lt;/b&gt; Thinking Different.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Friedman quoting Jonathan Sobel, a former student who successfully used (uses?) these concepts in practice: &lt;blockquote&gt;Efficiency comes from elegant solutions, not optimized programs. Optimization is just a few correctness-preserving transformations away.&lt;/blockquote&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Finally, and perhaps my favorite, Patrick Logan's cogent take: &lt;blockquote&gt;Probably programming in C itself (or Java or Scala) is a kind of premature optimization.&lt;/blockquote&gt;&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;WARNING:&lt;/b&gt; Refers to -- and poses some examples in -- the lambda calculus. Implementations in Scheme. Don't let either of those put you off, though. I don't understand every line of code (yet), but I've seen enough to be inspired to figure it out. &lt;br /&gt;&lt;br /&gt;Challenge yourself.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-5236335782842618561?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/5236335782842618561/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=5236335782842618561' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5236335782842618561'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5236335782842618561'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/01/required-reading-role-of-study-of.html' title='Required Reading: The Role of the Study of Programming Languages...'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-3971946698786756128</id><published>2008-01-04T12:46:00.000-07:00</published><updated>2008-01-04T13:58:37.344-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Java Generics Broken? We Report, You Decide.</title><content type='html'>Since I've been &lt;a href="http://members.capmac.org/~orb/blog.cgi/tech/java/The_null_pointer_sc.html"&gt;taken to task&lt;/a&gt; in the past for &lt;a href="http://davidrupp.blogspot.com/2007/08/autoboxing-cool.html"&gt;my cogent observations&lt;/a&gt; of Java's, um, shall we say "unintuitive" behavior, particularly in the era of generics and autoboxing, I'm just going to put this one out there without making any (public (explicit)) value judgments.&lt;br /&gt;&lt;br /&gt;So it seems that if you define a method thusly (note: requires Java 5 ("Gangly Geek") or &lt;s&gt;better&lt;/s&gt; &lt;s&gt;greater&lt;/s&gt; having a numerically higher version number):&lt;pre&gt;public void doSomethingGenerically(Collection&amp;lt;Supertype&amp;gt;){...}&lt;/pre&gt;and you attempt to call it thusly:&lt;pre&gt;List&amp;lt;SubtypeOfSupertype&amp;gt; bogus = new ArrayList&amp;lt;SubtypeOfSupertype&amp;gt;();&lt;br /&gt;doSomethingGenerically(bogus);&lt;/pre&gt;you get a compiler error saying you can't call that method with those arguments.&lt;br /&gt;&lt;br /&gt;So my question here is: why the heck not?&lt;br /&gt;&lt;br /&gt;Inheritance 101: a &lt;code&gt;List&lt;/code&gt; &lt;i&gt;isa&lt;/i&gt; &lt;code&gt;Collection&lt;/code&gt;, right? A &lt;code&gt;SubtypeOfSupertype&lt;/code&gt; &lt;i&gt;isa&lt;/i&gt; &lt;code&gt;Supertype&lt;/code&gt;, right? So why am I being told that an instance of type &lt;code&gt;List&amp;lt;SubtypeOfSupertype&amp;gt;&lt;/code&gt; &lt;i&gt;isn'ta&lt;/i&gt; &lt;code&gt;Collection&amp;lt;Supertype&amp;gt;&lt;/code&gt;?&lt;br /&gt;&lt;br /&gt;Paging Dr. Liskov...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-3971946698786756128?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/3971946698786756128/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=3971946698786756128' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3971946698786756128'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3971946698786756128'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2008/01/java-generics-broken-we-report-you.html' title='Java Generics Broken? We Report, You Decide.'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-4242973805284861407</id><published>2007-11-12T21:00:00.001-07:00</published><updated>2008-07-22T08:22:48.859-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='geek'/><title type='text'>Blast From the Past: HP 11C</title><content type='html'>Whilst doing some major cleaning up / throwing away of some of my accumulated junk this weekend, I came across my scientific calculator from college.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_MuRSvkgEbss/Rzo_WEmO9TI/AAAAAAAAABA/B7zfuj2y52Y/s1600-h/HP11C_1.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_MuRSvkgEbss/Rzo_WEmO9TI/AAAAAAAAABA/B7zfuj2y52Y/s320/HP11C_1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5132484373765879090" /&gt;&lt;/a&gt;&lt;br /&gt;"Big deal", you say? Maybe so. But this thing is 23-24 years old, which is about 2400 in computer years, which puts it close to the technical definition of "antique". &lt;i&gt;(Author's Note: We won't get into what that makes me...)&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_MuRSvkgEbss/Rzo_WUmO9UI/AAAAAAAAABI/ZlSGobbkRdE/s1600-h/HP11C_2.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_MuRSvkgEbss/Rzo_WUmO9UI/AAAAAAAAABI/ZlSGobbkRdE/s320/HP11C_2.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5132484378060846402" /&gt;&lt;/a&gt;&lt;br /&gt;Still not impressed? The thing still works. With the &lt;b&gt;original batteries&lt;/b&gt;. &lt;br /&gt;&lt;br /&gt;You read that right. After a year or so of use, every time I turned the thing on I would wonder if this was the day I was going to have to change the batteries. Nearly 25 years later, that day still hasn't come. Now granted, for the past several years I wasn't even sure I still had it. But when I came across it in my junk-cleaning I couldn't resist the temptation to try it out. I suppose I should've been surprised, but I wasn't really.&lt;br /&gt;&lt;br /&gt;I'm telling you, this is the Calculator That Time Forgot. This is the Calculator of Dorian Gray. I'm going to have to hold some kind of ceremony when it finally does give up the electronic ghost. But I'm not holding my breath.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-4242973805284861407?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/4242973805284861407/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=4242973805284861407' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4242973805284861407'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4242973805284861407'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/11/blast-from-past-hp-11c.html' title='Blast From the Past: HP 11C'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_MuRSvkgEbss/Rzo_WEmO9TI/AAAAAAAAABA/B7zfuj2y52Y/s72-c/HP11C_1.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-7770519173484518001</id><published>2007-11-06T08:26:00.000-07:00</published><updated>2008-01-04T13:59:30.932-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='humor'/><title type='text'>Strike!</title><content type='html'>Dear Striking Writers Guild of America People,&lt;br /&gt;&lt;br /&gt;First off, let me just say that I in no way want to impede your constitutionally protected right to effectively quit your jobs and be guaranteed that you'll get them back once your demands are met. That is some constitutionally protected right, right there. Anyways, God love you and I hope you get your residuals. Power to the proletariat, and all that.&lt;br /&gt;&lt;br /&gt;But I do have a favor to ask. You see, the only new thing on TV for a while is going to be news footage of your picketing, and I think you owe it to yourselves (not to us, don't get me wrong here; you're on strike, I get that) to punch up your picketing chants a little. Maybe it's just me, but I kind of expect your strike chants to be more...I don't know...pithy? Trenchant? Heart-wrenchingly but ultimately stirringly observational of the Human Condition?&lt;br /&gt;&lt;br /&gt;I mean (this is an actual WGA picket line chant): "Why make the viewers wait? Why won't you negotiate?". Really? That's the best the WGA -- the &lt;b&gt;WGA&lt;/b&gt;! -- could come up with? I realize you're doing what you do best -- rehash old material -- but come &lt;b&gt;on&lt;/b&gt;. You have so much better old material to work with. Some suggestions:&lt;blockquote&gt;We won't write another role! &lt;i&gt;*bleep*&lt;/i&gt; you, &lt;i&gt;*bleep*&lt;/i&gt;hole!&lt;/blockquote&gt;&lt;blockquote&gt;More new films for Jean-Claude Van Damme? Frankly, my dear, I don't give a damn!&lt;/blockquote&gt;&lt;blockquote&gt;Did Tony Soprano's family get whacked? We won't tell you, 'cause we're so hacked!&lt;/blockquote&gt;&lt;blockquote&gt;No more Die Hard on Blu-Ray! Mother &lt;i&gt;*bleep*&lt;/i&gt;er, yippie-ki-yay!&lt;/blockquote&gt;&lt;blockquote&gt;Won't give us the money we lack?! Then the Terminator will &lt;b&gt;never&lt;/b&gt; be back!&lt;/blockquote&gt;&lt;br /&gt;You could go the nostalgic route, and remind us of everything you've done for us in the past, and what we'd be missing without you:&lt;blockquote&gt;Star Trek! Numb3rs! Stand By Me! No Wil Wheaton on TV!&lt;/blockquote&gt; &lt;br /&gt;Or, and this is my favorite option, you could go beyond the traditional chant-based approach and actually stage your picket lines like the different genres you write for. You guys are writers, for pity's sake! You need to keep your chops up. Everyone who isn't actually walking a picket line at the moment could meet in the bullpen:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Fade In. Cue cutesy beginning-of-scene jingle. A group of writers are sitting around a table.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Head Writer:&lt;/b&gt; Okay, people, we've got a strike to write here. Who's got some ideas? Let's shoot it around the room!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Writer 1:&lt;/b&gt; We could get the cast of Friends to sit around on some comfy chairs on the picket line and make wisecracks about how lame it is that the producers won't give us any more money! David Spade could guest star, which will eventually get us Heather Locklear! And Jerry Stiller could walk in and start yelling his lines incoherently until Jason Alexander gets so worked up that he has to leave. Let's leave what's-his-name-the-guy-who-played-Kramer out of this one, though, huh?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Writer 2:&lt;/b&gt; Oooh! Crime drama! We could get David Caruso to do that thing he does in, like, every SINGLE SCENE where he has any dialogue! You know -- he stares moodily into the distance while delivering the line and then, at the very end, just when you think he's not going to, HE SWIVELS HIS HEAD to look directly at whoever he's talking to! It's brilliant, I tell you!&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Smash cut. David Caruso is standing at the counter at a Starbucks.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Perky Starbucks Babe:&lt;/b&gt; &lt;i&gt;(perkily)&lt;/i&gt; Can I take your order, sir?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;David Caruso:&lt;/b&gt; &lt;i&gt;(staring moodily into the distance)&lt;/i&gt; Yeeessssssss. I believe I'll have a &lt;i&gt;(swivel)&lt;/i&gt; latte.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Wipe transition. We are back in the bullpen.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Other Writers:&lt;/b&gt;&lt;i&gt; (all at once)&lt;/i&gt;...Caruso...brilliant...NYPD...movie "career"...hahahahahahahahahahahahahahahahaha!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Writer 3:&lt;/b&gt; How about a reality TV angle? We could have one of the picketing writers get ragged on by all the others until he snaps and puts Billy's hand in warm water while he's sleeping and then schemes with Maggie and Amber to get Chuck and Bruce voted out of the picket line only to double-cross them by "accidentally" tripping Maggie during their mambo and telling Amber she's too fat for him to marry her so she bursts into tears and can't do her Billy Idol tribute number so she runs off to build houses and stuff for the poor until she gets noticed by Donald Trump who makes her his new wigtender! Simon Cowell could be the judge!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Writer 4:&lt;/b&gt; I've got it! Let's get the writers for Saturday Night Live! They've been on strike for, like, eight years now, only no one told Lorne Michaels so they still get paid...&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Fade out. Mercifully.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Remember now, I'm asking this favor for your sake, not mine. I'm not going to be affected one way or the other. You think you're "hunkered down for a long one"? Kid, I've got all the Harry Potters &lt;b&gt;and&lt;/b&gt; the Lord of the Rings trilogy on DVD. Extended Director's Cut Version With Commentaries and Making-Of Featurettes. You won't be seeing me for &lt;b&gt;months&lt;/b&gt;!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-7770519173484518001?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/7770519173484518001/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=7770519173484518001' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7770519173484518001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7770519173484518001'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/11/strike.html' title='Strike!'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-7603587802251820285</id><published>2007-11-05T22:16:00.000-07:00</published><updated>2008-01-04T14:00:00.999-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='LaTeX geek'/><title type='text'>LaTeX For The Win!</title><content type='html'>Yeah, yeah -- I feel a little silly making a big deal about having just now become a convert to LaTeX. It's like wandering into a room of World-of-Warcraft-playing geeks and bragging about "this super-cool game I found; it's called 'NetHack'!"&lt;br /&gt;&lt;br /&gt;But dang! I can't believe I made it through two-thirds of a Master's degree in computer science without even trying this brilliant tool. I mean the quality of the output (okay -- the quality of the typsetting; the actual semantic content is still GIGO) makes even the most mundane homework assignment look like something that's going to be published in some major journal any day now.&lt;br /&gt;&lt;br /&gt;The key was in resolving to take the first step. I was dreading it, but I found that forcing myself to do that first assignment in LaTeX let me pick up probably 80% of the formatting tricks I'll ever need. Definitely worth the effort.&lt;br /&gt;&lt;br /&gt;LaTeX strikes me as a perfect example of what &lt;a href="http://headrush.typepad.com/"&gt;Kathy Sierra used to blog about&lt;/a&gt; (&lt;i&gt;Author's Note: Hi, Kathy! Come back! We miss you!&lt;/i&gt;): software that lets you (me) KICK A**! It's a software tool, but it has one key characteristic of the best physical tools as well -- it takes a modest force as input (your (my) typing), and amplifies and concentrates that force to shape an output that you (I) could not have achieved with your (my) bare hands. &lt;br /&gt;&lt;br /&gt;Simply put: LaTeX rocks.&lt;br /&gt;&lt;br /&gt;I just hope TeXShop -- which is the front-end I use on my Macs -- plays well with Leopard. There aren't a lot of issues I can think of that would keep me from upgrading, but losing LaTeX/TeXShop would definitely be a showstopper.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-7603587802251820285?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/7603587802251820285/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=7603587802251820285' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7603587802251820285'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7603587802251820285'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/11/latex-for-win.html' title='LaTeX For The Win!'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-7049400006660331502</id><published>2007-11-01T22:53:00.000-06:00</published><updated>2008-01-04T14:01:27.151-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lift'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Fun With Scala: Reading Scala</title><content type='html'>No, I haven't given up on my quest to learn more about Scala by perusing the guts of the liftweb source code. It's just that Iteration 3 at work, along with Hallowe'en with my two-year-old and Exam Two in Theory of Automata have all combined to kick my butt for the past couple of weeks. Not to mention all of the other life stuff that's intervening. &lt;br /&gt;&lt;br /&gt;But no excuses! Let's have a look at some specific Scala code and see what we can figure out in general from it.&lt;br /&gt;&lt;br /&gt;Today's snippet of Scala code is brought to you by &lt;code&gt;net.liftweb.util.Helpers.scala&lt;/code&gt;, for those of you following along in the book. I've elided a lot of it, for the purposes of this post. Here are the relevant lines of code, suitable for entering into your &lt;code&gt;scala&lt;/code&gt; interpreter:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import java.text.SimpleDateFormat&lt;br /&gt;import java.util.TimeZone&lt;br /&gt;val utc = TimeZone.getTimeZone("UTC")&lt;br /&gt;def internetDateFormatter = {&lt;br /&gt;  val ret = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z")&lt;br /&gt;  ret.setTimeZone(utc)&lt;br /&gt;  ret&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Besides the lack of semicolons (yea!) and the use of some standard Java classes (likewise yea!), one of the first things to notice is that &lt;code&gt;utc&lt;/code&gt; is declared as a &lt;code&gt;val&lt;/code&gt;, while  &lt;code&gt;internetDateFormatter&lt;/code&gt; is declared as a &lt;code&gt;def&lt;/code&gt;. You can probably intuit the difference, but we'll see some examples in a moment of just how they're different. Also, notice that neither &lt;code&gt;utc&lt;/code&gt; nor &lt;code&gt;internetDateFormatter&lt;/code&gt; is given an explicit type. Each &lt;i&gt;has&lt;/i&gt; a specific type, determined by the type of what's on the right hand of the assignment, but the compiler doesn't need to see the type declared on the left hand side. This has some consequences that we'll see too.&lt;br /&gt;&lt;br /&gt;Finally, the definition of &lt;code&gt;internetDateFormatter&lt;/code&gt; ends simply with the line &lt;code&gt;ret&lt;/code&gt;. This idiom will be familiar to the Ruby programmers in the audience, as it simply declares what the ultimate value is of the block that constitutes the right hand side of the assignment to the variable &lt;code&gt;internetDateFormatter&lt;/code&gt;. That is, in Scala, as in Ruby, the value of a block is the value of the last statement executed within the block.&lt;br /&gt;&lt;br /&gt;Try entering the above code, line by line, in a &lt;code&gt;scala&lt;/code&gt; interpreter. The imports aren't that interesting, but notice what you get as a result of entering the declaration of &lt;code&gt;utc&lt;/code&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;scala&amp;gt; val utc = TimeZone.getTimeZone("UTC")&lt;br /&gt;utc: java.util.TimeZone = sun.util.calendar.ZoneInfo[id="UTC",&lt;br /&gt;    offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Which says that &lt;code&gt;utc&lt;/code&gt; is of type &lt;code&gt;java.util.TimeZone&lt;/code&gt;, and in particular it is equal to a &lt;code&gt;sun.util.calendar.ZoneInfo&lt;/code&gt; with the corresponding id, offset, etc. Whereas when you finish declaring &lt;code&gt;internetDateFormatter&lt;/code&gt;, you simply get:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;scala&gt; def internetDateFormatter = {&lt;br /&gt;     | val ret = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z")&lt;br /&gt;     | ret.setTimeZone(utc)&lt;br /&gt;     | ret&lt;br /&gt;     | }&lt;br /&gt;internetDateFormatter: java.text.SimpleDateFormat&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now you can start using &lt;code&gt;internetDateFormatter&lt;/code&gt; as you would any instance of &lt;code&gt;SimpleDateFormat&lt;/code&gt;. For example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;scala&gt; internetDateFormatter.toPattern()&lt;br /&gt;res8: java.lang.String = EEE, d MMM yyyy HH:mm:ss z&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Even more fun is this: now you have a result (&lt;code&gt;res8&lt;/code&gt;, or whatever pops up for you), which is a &lt;code&gt;java.lang.String&lt;/code&gt;, and can be treated as such:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;scala&gt; res8.split("\\s")&lt;br /&gt;res9: Array[java.lang.String] = [Ljava.lang.String;@66718&lt;br /&gt;&lt;br /&gt;scala&gt; res9.toString()&lt;br /&gt;res10: String = Array(EEE,, d, MMM, yyyy, HH:mm:ss, z)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;i&gt;(Author's Note: Have you ever wanted an &lt;code&gt;irb&lt;/code&gt; (the interactive ruby interpreter) for Java? Download Scala, and get one for free today!)&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;So what about the difference between declaring with &lt;code&gt;val&lt;/code&gt; and declaring with &lt;code&gt;def&lt;/code&gt;? Let's find out:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;scala&gt; utc = TimeZone.getTimeZone("GMT-7")&lt;br /&gt;line19$object.$iw.$iw.utc = TimeZone.getTimeZone("GMT-7")&lt;br /&gt;&lt;console&gt;:6: error: assignment to non-variable &lt;br /&gt;  val res12 = {utc = TimeZone.getTimeZone("GMT-7");utc}&lt;br /&gt;                   ^&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Uh-oh. Looks like Scala considers &lt;code&gt;val&lt;/code&gt;s to be constant. Although:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;scala&gt; val utc = TimeZone.getTimeZone("GMT-7")&lt;br /&gt;utc: java.util.TimeZone = sun.util.calendar.ZoneInfo[id="GMT-07:00",offset=-25200000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It also looks like some constants are more constant than others. But before you start thinking you can just reassign &lt;code&gt;internetDateFormatter&lt;/code&gt; with impunity:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;scala&gt; internetDateFormatter = "impunity"&lt;br /&gt;&lt;console&gt;:5: error: value internetDateFormatter_= is not a member of object $iw&lt;br /&gt;  val res13 = {internetDateFormatter = "impunity";internetDateFormatter}&lt;br /&gt;               ^&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Whatever that means. I'm going to have to track that one down and report on it later. Right now it's time for more life (read: "sleep") to intervene.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-7049400006660331502?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/7049400006660331502/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=7049400006660331502' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7049400006660331502'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7049400006660331502'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/11/fun-with-scala-reading-scala.html' title='Fun With Scala: Reading Scala'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-4806450124823002788</id><published>2007-10-22T13:55:00.000-06:00</published><updated>2007-10-22T14:37:24.977-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lift'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Fun With Scala: Things I'm Learning From Lift #2</title><content type='html'>Thanks to astute reader &lt;a href="http://www.blogger.com/profile/11583717134076863159"&gt;Ken&lt;/a&gt; (&lt;i&gt;Author's Note: I have a reader! An astute one! Who knew?!&lt;/i&gt;), I learned something about a feature I was bragging about in my last post; namely, the ability to import members of a class. As it turns out, not only is this feature present in Java 5 -- see the &lt;a href="http://java.sun.com/j2se/1.5.0/docs/guide/language/static-import.html"&gt;documentation&lt;/a&gt; for static imports -- the Scala version of it is pretty much equivalent.&lt;br /&gt;&lt;br /&gt;Ken points out that my example of &lt;code&gt;lift&lt;/code&gt; importing the members of the &lt;code&gt;net.liftweb.util.Helpers&lt;/code&gt; "class" maps exactly to the corresponding Java import. I waved my hands around the fact that &lt;code&gt;Helpers&lt;/code&gt; is actually an &lt;code&gt;object&lt;/code&gt;, which is, simultaneously and at the same time, Scala's way of denoting a singleton and providing static members (all of the members of an &lt;code&gt;object&lt;/code&gt; are effectively static). But it turns out that the &lt;code&gt;import&lt;/code&gt; syntax I was bragging about works precisely &lt;i&gt;because&lt;/i&gt; &lt;code&gt;Helpers&lt;/code&gt; is an &lt;code&gt;object&lt;/code&gt; and not a &lt;code&gt;class&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Here's some lame, contrived, forced-example, somewhat-minimal Scala code to demonstrate:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;i&gt;Mixee.scala&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;package bogus;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;class&lt;/b&gt; Mixee {&lt;br /&gt; def weird: String = {"Weird Al!"}&lt;br /&gt; def twentySeven: String = {"27!"}&lt;br /&gt;}&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;i&gt;Mixer.scala&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;package bogus;&lt;br /&gt;&lt;br /&gt;import Mixee._;&lt;br /&gt;&lt;br /&gt;object Mixer {&lt;br /&gt;  def main(args: Array[String]): Unit = { Console.println(weird); Console.println(twentySeven) }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As described here, &lt;code&gt;Mixer.scala&lt;/code&gt; will refuse to compile, with the error: "not found: value Mixee". This is because Mixee is defined as a &lt;code&gt;class&lt;/code&gt;. Change &lt;code&gt;class&lt;/code&gt; to &lt;code&gt;object&lt;/code&gt; and it compiles (and runs) just fine.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Bonus Discovery:&lt;/b&gt; While researching this, I also learned that Eclipse with the Scala plugin is not as smart as it should be about building my bogus project. If I start with a clean compile (i.e., defining Mixee as an object), Eclipse is happy. If I then change the definition of Mixee from &lt;code&gt;object&lt;/code&gt; to &lt;code&gt;class&lt;/code&gt;, the error doesn't show up until I force a rebuild by selecting Project -&gt; Clean... If I then change &lt;code&gt;class&lt;/code&gt; back to &lt;code&gt;object&lt;/code&gt;, the error goes away without my forcing the rebuild.&lt;br /&gt;&lt;br /&gt;Anyone got a line on that? Ken? Anyone?&lt;br /&gt;&lt;br /&gt;Anyway, just for the record, my purpose with these posts is not to bury Java, but to praise Scala and learn how it's different. I'll leave the determination of whether Scala is "better" than Java up to you, my readers. Both of you (hi, Mom!). ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-4806450124823002788?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/4806450124823002788/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=4806450124823002788' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4806450124823002788'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4806450124823002788'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/10/fun-with-scala-things-im-learning-from_22.html' title='Fun With Scala: Things I&apos;m Learning From Lift #2'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-7927769598734174845</id><published>2007-10-16T21:55:00.000-06:00</published><updated>2007-10-22T14:37:45.659-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lift'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Fun With Scala: Things I'm Learning From Lift #1</title><content type='html'>So I'm starting somewhere around the very beginning with the Liftweb framework: &lt;code&gt;LiftServlet.scala&lt;/code&gt;, which is in the &lt;code&gt;net.liftweb.http&lt;/code&gt; package. This looks like Lift's answer to a Rails controller, or a Struts Action &lt;span style="font-style:italic;"&gt;(Author's Note: probably with some caveats that I haven't found yet; (pleasant) comments welcome if I'm wrong about this)&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Fun Thing #1:&lt;/span&gt; The very first import is &lt;br /&gt;&lt;pre&gt;import javax.servlet.http.{HttpServlet, HttpServletRequest , &lt;br /&gt;          HttpServletResponse, HttpSession}&lt;/pre&gt;&lt;br /&gt;How's that for interoperability with Java? I haven't gotten that far yet, but this tells me that whatever Lift uses for its request/response implementation is going to conform to the usual Java interfaces. And how do you like the syntax for this style of import? I like it a lot, because it lets you do the equivalent of:&lt;br /&gt;&lt;pre&gt;import javax.servlet.http.HttpServlet&lt;br /&gt;import javax.servlet.http.HttpServletRequest&lt;br /&gt;import javax.servlet.http.HttpServletResponse&lt;br /&gt;&lt;i&gt;...et cetera...&lt;/i&gt;&lt;/pre&gt;&lt;br /&gt;...on a single line (it's a single line in the original source; I've split it here for friendlier rendering). Without resorting to importing everything in the &lt;code&gt;http&lt;/code&gt; package with a wildcard. This way you get to see exactly what you need. Yeah, I know a good Java IDE will manage the imports for you, so it's not as much of a pain as it used to be, but I like the compactness of this syntax. As it happens, the current version of the Scala plugin for Eclipse doesn't have the equivalent of "Organize Imports", so this syntax is definitely a boon.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Fun Thing #2:&lt;/span&gt; There's a syntax for importing not just classes, but &lt;i&gt;members of a class&lt;/i&gt;. Like so:&lt;br /&gt;&lt;pre&gt;import net.liftweb.util.Helpers._&lt;/pre&gt;&lt;br /&gt;This basically says "make everything in the &lt;code&gt;Helpers&lt;/code&gt; class (actually it's a singleton &lt;code&gt;object&lt;/code&gt;, but think of it as a class for now) available to this class without my having to qualify it with '&lt;code&gt;Helpers.&lt;/code&gt;' all the time".&lt;br /&gt;&lt;br /&gt;For example, there's a bit of code in &lt;code&gt;LiftServlet&lt;/code&gt; that goes like this:&lt;br /&gt;&lt;pre&gt;val md = parseInternetDate(mod)&lt;/pre&gt;&lt;br /&gt;This makes it appear that &lt;code&gt;parseInternetDate()&lt;/code&gt; is defined in the LiftServlet class. But it's not. It's defined in &lt;code&gt;Helpers&lt;/code&gt;. If you're used to modules/mixins in Ruby, this should seem very familiar to you. If not, now might be a good time to get used to them, because they're way cool.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Fun Thing #3:&lt;/span&gt; No, the lack of semicolons at the end of the code samples is not a typo. The Semicolon is Dead! Long Live the Semicolon! No, scratch that -- Long Live &lt;span style="font-weight:bold;"&gt;Scala&lt;/span&gt;!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-7927769598734174845?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/7927769598734174845/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=7927769598734174845' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7927769598734174845'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7927769598734174845'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/10/fun-with-scala-things-im-learning-from.html' title='Fun With Scala: Things I&apos;m Learning From Lift #1'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-6515955258115107834</id><published>2007-10-14T22:25:00.000-06:00</published><updated>2007-10-22T14:38:15.349-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lift'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Fun With Scala: Liftweb Getting Started</title><content type='html'>Job one: checking out and Eclipsify-ing lift.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://blog.circleshare.com/index.php?/archives/62-lift-QuickStart.html"&gt;instructions at the CircleShare lift blog&lt;/a&gt; are still current up to a point, at least as far as the prerequisites are concerned. I'm running from trunk, so my source code checkout was a little different:&lt;br /&gt;&lt;pre&gt;svn co http://liftweb.googlecode.com/svn/liftweb&lt;/pre&gt;&lt;br /&gt;After that, the available instructions need a little (ahem) updating.&lt;br /&gt;&lt;br /&gt;For a quick look at the kinds of things you can do with lift, run &lt;code&gt;mvn install&lt;/code&gt; in the &lt;code&gt;liftweb&lt;/code&gt; (top level) directory to build everything. When that's done, &lt;code&gt;cd sites/&lt;i&gt;project_name&lt;/i&gt;&lt;/code&gt;, where &lt;code&gt;&lt;i&gt;project_name&lt;/i&gt;&lt;/code&gt; is one of &lt;code&gt;example&lt;/code&gt;, &lt;code&gt;hellolift&lt;/code&gt;, or &lt;code&gt;skittr&lt;/code&gt;. From there, run &lt;code&gt;mvn jetty:run&lt;/code&gt; to start a Jetty web server running on some port on localhost. At this writing, &lt;code&gt;example&lt;/code&gt; and &lt;code&gt;hellolift&lt;/code&gt; run on port 8888, while &lt;code&gt;skittr&lt;/code&gt; runs on 8889. Don't ask me why.&lt;br /&gt;&lt;br /&gt;You can also &lt;code&gt;mvn install&lt;/code&gt; individual projects if you want, but I don't see any harm in building them all in one shot.&lt;br /&gt;&lt;br /&gt;To Eclipsify, make sure you have &lt;a href="http://www.scala-lang.org/downloads/eclipse/"&gt;the Scala Development Tools plugin&lt;/a&gt; installed, then run &lt;code&gt;mvn eclipse:eclipse&lt;/code&gt; in the project you want to be able to work with in Eclipse. You can do this for the lift source code itself (under the &lt;code&gt;liftweb/lift&lt;/code&gt; subdirectory), or any of the example projects (&lt;code&gt;liftweb/sites/&lt;i&gt;project_name&lt;/i&gt;&lt;/code&gt; where &lt;code&gt;&lt;i&gt;project_name&lt;/i&gt;&lt;/code&gt; is as above).  &lt;br /&gt;&lt;br /&gt;Happy Lifting!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-6515955258115107834?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/6515955258115107834/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=6515955258115107834' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6515955258115107834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6515955258115107834'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/10/fun-with-scala-liftweb-getting-started.html' title='Fun With Scala: Liftweb Getting Started'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-8316440510463955168</id><published>2007-10-14T22:09:00.000-06:00</published><updated>2007-10-14T22:25:16.474-06:00</updated><title type='text'>Fun With Scala: Liftweb</title><content type='html'>In my last post, I poked some serious fun at a bunch of mainstream languages (actually, their Internet communities), but left Scala pretty much alone. I chose Ruby and Java as my main characters because those are the languages I read the most about, and whose pundits' positions I felt like I could parody most accurately.&lt;br /&gt;&lt;br /&gt;I've been interested in Scala since I first came across it, but I haven't really had to time to do much with it. I like its functional leanings, and I am intrigued by its ability to run on the JVM and interoperate seamlessly with existing Java libraries. Although when I say "interoperate", I have to acknowledge that it's a one-way street, as far as I know. Scala code can certainly invoke methods on Java classes. I have yet to figure out how to make the reverse happen (not that I've tried very hard). If I can, I will definitely try to make use of that capability.&lt;br /&gt;&lt;br /&gt;So I've decided to do something real with Scala, to force myself to get to know it better. I've chosen Liftweb as my target project, largely because it's there and it's the only substantive chunk of Scala code I've found. You may have &lt;a href="http://blog.circleshare.com/index.php?/archives/55-Prance-with-the-Horses,-Skittr-with-the-Mice.html"&gt;read about its claims&lt;/a&gt; to be able to implement a full-on Twitter clone in less than 1000 lines of code, with the capability of handling one million or more users on a single commodity PC.&lt;br /&gt;&lt;br /&gt;Attention has been paid.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-8316440510463955168?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/8316440510463955168/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=8316440510463955168' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8316440510463955168'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8316440510463955168'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/10/fun-with-scala-liftweb.html' title='Fun With Scala: Liftweb'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-5878521941389076223</id><published>2007-10-03T22:05:00.000-06:00</published><updated>2007-10-03T22:21:26.050-06:00</updated><title type='text'>The Last Language War / Language Trolling Post You'll Ever Need To Read (Hopefully)</title><content type='html'>&lt;b&gt;Moderator:&lt;/b&gt; Greetings, and welcome to the First-And-Possibly-Last-Ever Pan-Computer-Programming-Language Conference (FAPLEPCPLC). I am joined on stage tonight by many distinguished, high-profile computer programming languages. Each is highly regarded by its devotees, and I for one look forward to hearing what each has to say.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ruby &lt;i&gt;(grabbing the microphone)&lt;/i&gt;:&lt;/b&gt; Um so yeah I'd just like to kick this bad boy off by saying that THE REST OF YOU SUCK A**!!! Yeah, I said it! The A-word! A**! Oh yeah! Boom, baby! Woo! Ruby FTW!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Java &lt;i&gt;(rolling its eyes)&lt;/i&gt;:&lt;/b&gt; Oh, real mature. I, on the other hand, would like to state that I have important work to get done in the Enterprise, so let's not waste everyone's time. I suggest we proceed using the Computer Programming Languages Discussion Pattern as implemented in JSR-6942, the Java&amp;trade; Absolutely Void-of-Acronyms Talking About Languages at Konferences (JAVATALK&amp;trade; which by the way is not an acronym for anything) specification.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ruby:&lt;/b&gt; Dude! I just wrote a full working clone of Google while you were giving your riveting little speech there!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Moderator:&lt;/b&gt; Oh, bravo, Ruby! I'd like to see that. Where is it deployed?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ruby:&lt;/b&gt; Umm....&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Lisp:&lt;/b&gt; In the beginning, there was the Lambda. And John McCarthy saw the Lambda. And John McCarthy saw that the Lambda was very good.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ruby &lt;i&gt;(rolling its eyes)&lt;/i&gt;:&lt;/b&gt; Here we go...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Lisp:&lt;/b&gt; And John McCarthy spake, and lo! the tongue he spake was sexp...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ruby:&lt;/b&gt; He said sex! Heeheeheeheehee!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Erlang:&lt;/b&gt; If I may, I've been running some ideas by the other panelists, all at the same time of course, not that that's any big deal...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ruby &lt;i&gt;(rolling its eyes)&lt;/i&gt;:&lt;/b&gt; And here we go with the concurrency...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Java:&lt;/b&gt; Gosh darn it, Ruby! There's no need to pooh-pooh everything someone else says, eh?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ruby:&lt;/b&gt; He said poo-poo! Heeheeheeheehee! &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Java:&lt;/b&gt; Ruby, I swear, one of these days...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ruby:&lt;/b&gt; Hey, don't give me any of your static! Heeheeheeheehee! See what I did there?! "Static"?! 'Cause I'm so dynamic?! GET IT?!!! DAMN, where's my BAWLS Guarana...?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ruby &lt;i&gt;(seconds later)&lt;/i&gt;:&lt;/b&gt; I said balls! Heeheeheeheehee!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;C#:&lt;/b&gt; Developers! Developers! Developers! Developers!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Erlang:&lt;/b&gt; ...I'd share my results with you, but it's going to take a while to retrieve them from the filesystem...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;COBOL:&lt;/b&gt; &lt;i&gt;keels over, only to be frantically revived by about three banks.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Basic:&lt;/b&gt; Actually, I have a question for Ruby...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Haskell:&lt;/b&gt; Me too...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;ML:&lt;/b&gt; Hey, Ruby, what's your answer to...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ruby:&lt;/b&gt; Hey, hey, hey now! Not too many at a &lt;code&gt;[segfault]&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Java:&lt;/b&gt; &lt;i&gt;smiles&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;bash:&lt;/b&gt; &lt;code&gt;kill -9 self&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Lambda Calculus:&lt;/b&gt; Actually, if we could all just take a moment to reflect on the implications of the Church-Turing thesis...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Everyone Else:&lt;/b&gt; Oh, SHUT UP!!!!!!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Scala:&lt;/b&gt; &lt;i&gt;says nothing, but sits quietly observing, taking notes, and learning a lot.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Moderator:&lt;/b&gt; Well, I think it's about time to bring some closure to this, um, lively...discussion...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Java &lt;i&gt;(sharply)&lt;/i&gt;:&lt;/b&gt; Hey! We're working on it!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Lisp:&lt;/b&gt; ))))))))))))))))))))))&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-5878521941389076223?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/5878521941389076223/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=5878521941389076223' title='42 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5878521941389076223'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5878521941389076223'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/10/last-language-war-language-trolling.html' title='The Last Language War / Language Trolling Post You&apos;ll Ever Need To Read (Hopefully)'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>42</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-5694877039402274675</id><published>2007-10-03T18:13:00.000-06:00</published><updated>2007-10-03T18:29:22.183-06:00</updated><title type='text'>Java 1, Intuition 0</title><content type='html'>Pop quiz: If you were given the following snippet of code:&lt;pre&gt;"".split("\\s");&lt;/pre&gt;...what would you expect the result to be?&lt;br /&gt;&lt;br /&gt;If it helps, the contract for &lt;code&gt;String.split()&lt;/code&gt; is that it takes a regular expression (in this case &lt;code&gt;\s&lt;/code&gt; means "anything that could reasonably be considered whitespace, like spaces, tabs, et cetera"), and returns a &lt;code&gt;String[]&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;My intuition was that it would return an empty &lt;code&gt;String[]&lt;/code&gt;. &lt;i&gt;(Author's Note: which would have been &lt;a href='http://davidrupp.blogspot.com/2007/08/autoboxing-cool.html'&gt;better than a null&lt;/a&gt;, but even that's not what I got.)&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;I mean think about it: what does it mean to split the empty string, on &lt;b&gt;any&lt;/b&gt; character? The empty string has length 0, so what can you split it into? Two strings of length 0? But then you could do that &lt;i&gt;ad infinitum&lt;/i&gt;, which would be silly.&lt;br /&gt;&lt;br /&gt;So what Java &lt;b&gt;actually&lt;/b&gt; does is return an array with one string: the empty string, or &lt;b&gt;one&lt;/b&gt; string of length 0. Which implies that Java thinks the beginning of a string -- represented by the regex &lt;code&gt;\A&lt;/code&gt;, or &lt;code&gt;^&lt;/code&gt; if you know the string you're regex-ing doesn't contain newlines -- is significant, but the end of a string (&lt;code&gt;\Z&lt;/code&gt; or &lt;code&gt;$&lt;/code&gt;) is "whitespace". I guess you could make a case for this making sense, but it doesn't match up with my intuition. &lt;br /&gt;&lt;br /&gt;What do you think?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-5694877039402274675?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/5694877039402274675/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=5694877039402274675' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5694877039402274675'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/5694877039402274675'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/10/java-1-intuition-0.html' title='Java 1, Intuition 0'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-3895527362462828661</id><published>2007-09-22T22:01:00.000-06:00</published><updated>2007-09-23T08:25:33.054-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>There and Back Again: PHP to Rails to...PHP?</title><content type='html'>Well &lt;a href="http://www.oreillynet.com/ruby/blog/2007/09/7_reasons_i_switched_back_to_p_1.html"&gt;this&lt;/a&gt; is going to generate some controversy. CDBaby's adoption of Rails was touted as a major coup for for the switch-to-Rails, it's-not-just-for-green-field-projects movement.&lt;br /&gt;&lt;br /&gt;Please note that I don't have a dog in this particular fight. I spent last year developing Rails webapps, and I enjoyed it. I've never done any non-toy PHP development, but I would if the occasion were to arise. Right now I'm back squarely in the Java camp for the foreseeable future. If I were inclined to switch to anything else, it would be Scala, which is shaping up to be a (much) better Java than Java. &lt;br /&gt;&lt;br /&gt;While Java is playing catch-up to features the designers deliberately left out in early releases, Martin Odersky and team have quietly built up a nice little language that leverages all of the strengths of the Java platform, while neatly sidestepping the weaknesses of the Java language. It is easy to learn if you already know Java, and extremely powerful if you know functional programming. Toss in an abundance of well-written, well-thought-out documentation, and you have to start thinking that we just might be looking at the Next Big Thing in programming languages.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-3895527362462828661?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/3895527362462828661/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=3895527362462828661' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3895527362462828661'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3895527362462828661'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/09/there-and-back-again-php-to-rails-tophp.html' title='There and Back Again: PHP to Rails to...PHP?'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-8848912265329472593</id><published>2007-09-20T22:53:00.000-06:00</published><updated>2007-09-20T23:48:00.964-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Scala Alert: bootclasspath/a Considered Canadian</title><content type='html'>&lt;i&gt;Author's Note: Please forgive what is, essentially and unfortunately, a very bad pun. It's late, and I've had a long week, what with my Automata exam coming up and all. And, for the record, I have nothing against Canadians. James Gosling is Canadian. So is William Shatner. I once lived in Vancouver. Please don't hate.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;So I was looking at &lt;code&gt;bin/scalac&lt;/code&gt; and &lt;code&gt;bin/scala&lt;/code&gt;, the Scala toolset equivalents of &lt;code&gt;javac&lt;/code&gt; and &lt;code&gt;java&lt;/code&gt;, and I found myself learning some stuff. For instance, &lt;code&gt;scala&lt;/code&gt; (which executes a Scala, um, executable) is simply a shell script that sets a bunch of shell/environment variables and finally runs this command:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;${JAVACMD:=java} ${JAVA_OPTS:=-Xmx256M -Xms16M} -Xbootclasspath/a:"$BOOT_CLASSPATH" -cp "$EXTENSION_CLASSPATH" -Dscala.home="$SCALA_HOME" -Denv.classpath="$CLASSPATH" -Denv.emacs="$EMACS"  scala.tools.nsc.MainGenericRunner  "$@"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This points out some interesting interestingness. First off is: Scala executables are ultimately run by...&lt;code&gt;java&lt;/code&gt;! Or what passes for it on your system. Pretty neat, huh? Although it makes total sense if you think of &lt;code&gt;java&lt;/code&gt; as the command that starts up a JVM on some arbitrary &lt;code&gt;.class&lt;/code&gt; file, not the command that runs a Java(TM) language program.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;(Author's Other Note: Scala code gets compiled to JVM-compliant bytecode. Which is proof that real closures are theoretically possible in Java(TM). Although they are &lt;a href="http://davidrupp.blogspot.com/2007/05/closures-in-java-why-bother.html"&gt;not likely to happen&lt;/a&gt; in your lifetime. But I digress.)&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Second off is: buried amongst the options to &lt;code&gt;java&lt;/code&gt; is the non-standard &lt;code&gt;-Xbootclasspath/a:"$BOOT_CLASSPATH"&lt;/code&gt;. Unlike plain ol' &lt;code&gt;-Xbootclasspath&lt;/code&gt;, this option lets you &lt;i&gt;append&lt;/i&gt; some arbitrary jars to the regular bootclasspath (hence the &lt;code&gt;/a&lt;/code&gt;). In this case, what it appends is &lt;code&gt;$SCALA_HOME/lib/scala-library.jar&lt;/code&gt; if it exists, which gives the executable access to the Scala standard library. Which includes among other things the package that implements Scala Actors. &lt;br /&gt;&lt;br /&gt;P.S.: It turns out there's also a &lt;code&gt;-Xbootclasspath/p&lt;/code&gt;, which stands for "prepend", but is not as funny when applied to Canadians.&lt;br /&gt;&lt;br /&gt;P.P.S.: I like the option to provide a default value for some environment variable that may not exist on a given system (e.g., &lt;code&gt;${JAVACMD:=java}&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;P.P.P.S.: EMACS??? What the heck does that have to do with running a Scala executable? Time for some more research, eh?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-8848912265329472593?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/8848912265329472593/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=8848912265329472593' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8848912265329472593'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8848912265329472593'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/09/scala-alert-bootclasspatha-considered.html' title='Scala Alert: bootclasspath/a Considered Canadian'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-3754172417557229267</id><published>2007-09-07T10:07:00.000-06:00</published><updated>2007-09-07T13:21:44.719-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='language'/><title type='text'>Recommended Reading: The Linguist</title><content type='html'>Do yourself a favor: start reading &lt;a href="http://www.thelinguist.blogs.com/"&gt;The Linguist&lt;/a&gt;, by Steve Kauffman, who speaks 9 languages and founded thelinguist.com as a resource for those who want to learn a new language. I particularly like the following quote, from the entry &lt;a href="http://thelinguist.blogs.com/how_to_learn_english_and/2007/09/why-children-le.html"&gt;Why children learn languages better than adults&lt;/a&gt;:&lt;br /&gt;&lt;blockquote&gt;Children have not yet been converted from naturally curious language explorers into teacher-dependent grammar learners.&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;I took Spanish for three years in high school (Hola, Srta. Chambless!), and I was very fortunate that my teacher taught the class &lt;b&gt;in Spanish&lt;/b&gt;, and required us to participate by speaking Spanish. After the first six weeks of the first year, no English was allowed, and precious little during that time. It was brilliant. It forced us to learn Spanish the way kids in Spanish-speaking families learn it - by experiencing it and figuring things out, not by sitting at a desk with a vocab list and a dictionary.&lt;div&gt;&lt;br /&gt;I've often wondered why no one has developed a computer language training curriculum modelled after some of the more effective human language training methods. But that's beside the point of Kauffman's thesis above. Ultimately, we learn what we love. And we love what we find out for ourselves as the result of exploring the things we find interesting, not what is hammered into us by some random instructor.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-3754172417557229267?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/3754172417557229267/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=3754172417557229267' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3754172417557229267'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3754172417557229267'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/09/recommended-reading-linguist.html' title='Recommended Reading: The Linguist'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-3709002624538984035</id><published>2007-08-17T11:32:00.001-06:00</published><updated>2007-08-17T11:33:23.063-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Scala: The Next Next Java!</title><content type='html'>Yeah, yeah. I know. It's only been recently that &lt;a href="http://www.erlang.org/"&gt;Erlang&lt;/a&gt; has &lt;a href="http://www.cincomsmalltalk.com/userblogs/ralph/blogView?showComments=true&amp;amp;entry=3364027251"&gt;started being touted&lt;/a&gt; as "the next Java". I just thought I'd get a jump on crowning the &lt;i&gt;next&lt;/i&gt; next Java. Oh, sure, no one gets to &lt;i&gt;officially&lt;/i&gt; be The Next Anything without a &lt;a href="http://www.pragmaticprogrammer.com"&gt;Pragmatic Bookshelf&lt;/a&gt; book backing it, but I'm sure it's just a matter of time before a Scala book is announced. &lt;br /&gt;&lt;br /&gt;After all, &lt;a href="http://www.scala-lang.org"&gt;Scala&lt;/a&gt; shares a lot of the advantages of Erlang -- principally, its support for Actor-based concurrent programming and hence its powers of super-scaling. But it also has a secret weapon that Erlang doesn't have: it runs on the JVM! Yes, &lt;i&gt;that&lt;/i&gt; JVM! It also has access to the multitudinous multitudes of existing Java libraries out there, making for a (potentially) easier migration path.&lt;br /&gt;&lt;br /&gt;No need to wait for Java 7 (or (possibly much (much)) greater) for closures in Java! Write some Scala scaffolding around your existing Java classes and have them today!&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Le roi mort! Vive le roi!&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-3709002624538984035?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/3709002624538984035/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=3709002624538984035' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3709002624538984035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3709002624538984035'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/08/scala-next-next-java.html' title='Scala: The Next Next Java!'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-3289501465085242129</id><published>2007-08-09T09:21:00.000-06:00</published><updated>2007-08-09T10:28:59.343-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>The Good, The Snide, and The Ugly (More on Autoboxing)</title><content type='html'>Wow. You know you've hit the big time when famed Java geek author Norman Richards &lt;a href="http://members.capmac.org/~orb/blog.cgi/tech/java/The_null_pointer_sc.html"&gt;gets all snarky&lt;/a&gt; on your sh*t. I would like to point out, though, that I never said autoboxing was "evil". I just said &lt;a href="http://davidrupp.blogspot.com/2007/08/autoboxing-cool.html"&gt;it was !cool&lt;/a&gt;. And no amount of Richards' sarcastic aping of my (deliberately) satirical-yet-lighthearted take on it is going to convince me otherwise.&lt;br /&gt;&lt;br /&gt;The problem, of course, lies in the fact that, pre-autoboxing, there was no question of an assignment to a variable of type &lt;code&gt;int&lt;/code&gt; throwing an NPE. For the first 87% of Java's numerical-version-life you just couldn't do it. The concept had no meaning, like the questions "what is north of the North Pole?", or "what color is up?". Or "can I borrow your copy of 'XDoclet in Action'?" &lt;i&gt;(Author's note: because no one in my office has one)&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Pre-autoboxing, Eclipse would have flat out disallowed the assignment of an &lt;code&gt;Integer&lt;/code&gt; to an &lt;code&gt;int&lt;/code&gt;. And here's the important point: Eclipse would have disallowed it, because &lt;i&gt;the compiler&lt;/i&gt; would have disallowed it. Because you couldn't assign an object reference to a primitive type, inadvertently or advertently. It wouldn't be a legal assignment, so it wouldn't compile, so there would be no way for it to throw an NPE in production. That's what static typing is supposed to ensure.&lt;br /&gt;&lt;br /&gt;In true famed Java geek author style, though, Richards totally misses the pedagogical point when he uses as his sarcastixample the potential NPE-ness of code that deals with &lt;code&gt;String&lt;/code&gt;. Which is an object type. Which has since the beginning of Java time been subject to NPE's. As opposed to primitive types. Like &lt;code&gt;int&lt;/code&gt;. Which have not. Until autoboxing rocked our world and broke static typing.&lt;br /&gt;&lt;br /&gt;I've actually been waiting for someone to snark on my (deliberately) satirical-yet-lighthearted insinuation that I rely too much on my IDE. That was the first point I took away from this whole exercise, and believe me I will be more wary in the future. I'm pretty sure I'm not the first to be burned by such an over-reliance. But in this case, even if I were using Emacs and the command line, I would still have fallen victim to this problem. The fault lies in the language, not in my reliance on any tool.&lt;br /&gt;&lt;br /&gt;Bottom line: I don't think it should be too hard to excuse someone who, when seeing an NPE in his stack trace, doesn't leap immediately at the assignment to the &lt;code&gt;int&lt;/code&gt; variable.&lt;br /&gt;&lt;br /&gt;But maybe that's just me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-3289501465085242129?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/3289501465085242129/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=3289501465085242129' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3289501465085242129'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3289501465085242129'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/08/good-snide-and-ugly-more-on-autoboxing.html' title='The Good, The Snide, and The Ugly (More on Autoboxing)'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-4792608497074635003</id><published>2007-08-07T15:23:00.000-06:00</published><updated>2007-08-07T15:46:02.884-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Autoboxing == !Cool</title><content type='html'>So you're finally getting used to the idea of Java 5's "feature" of converting between primitive types and their corresponding object wrappers (e.g., int -- Integer). You're getting to the point where you kind of trust it. You're &lt;i&gt;even&lt;/i&gt; getting to the point where you kind of &lt;i&gt;rely&lt;/i&gt; on it. Life is good.&lt;br /&gt;&lt;br /&gt;Then you go too far and try something weird and exotic like, oh, I don't know:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;int sucker = thisMethodReallyReturnsAnIntegerNotAnInt();&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;No squiggly red line in your Eclipse editor. Cool! That must mean this will work! Because Java has static typing, and you can count on that! Yea you!&lt;br /&gt;&lt;br /&gt;The problem is, it will work. It will work very nearly all the time. It will work until your method returns a &lt;code&gt;null&lt;/code&gt; instead of an actual &lt;code&gt;Integer&lt;/code&gt;. At which point you'll get a NullPointerException buried in your Struts 2 / OGNL / JSP project's Eclipse console stack trace, followed by a two-hour detour into the debugger wondering what the heck is going on. I mean after all, your code compiled, right? It worked for the first hour, right? Static typing is good, right? Autoboxing works, right?&lt;br /&gt;&lt;br /&gt;Not that you're bitter.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-4792608497074635003?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/4792608497074635003/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=4792608497074635003' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4792608497074635003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4792608497074635003'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/08/autoboxing-cool.html' title='Autoboxing == !Cool'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-1571577008589015803</id><published>2007-07-09T10:16:00.001-06:00</published><updated>2007-09-23T13:24:36.432-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='slicehost'/><title type='text'>Starting up a VPS on Slicehost</title><content type='html'>&lt;i&gt;Author's Note: None of the following is intended to portray any of the companies named in an unflattering light. There were good things about all of them, along with the not-so-good. They all have their fans and their detractors. However, you know that phrase we geeks use when talking about our experiences with things technical? YMMV? Well, my mileage varied, considerably. To the point where I changed vehicles.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;My first experience with running &lt;a href="http://ruppconsulting.com/"&gt;Rupp Consulting Services, LLC&lt;/a&gt; on VPS (Virtual Private Server) was with &lt;a href="http://rimuhosting.com/"&gt;Rimuhosting&lt;/a&gt;. This was after some, shall we say, &lt;i&gt;sub-optimal&lt;/i&gt; experiences with, in chronological order, Yahoo!, Textdrive, and Dreamhost. I went to VPS primarily to relieve the pain of memory/resource constraints/contention, and the inevitable shared downtime that comes with running a shared server.&lt;br /&gt;&lt;br /&gt;Rimuhosting, I must say, were great about functionality and support. I left them only reluctantly, having decided to farm out my email and my blog to Google, and not seeing the need to pay for my web presence. Well, that lasted about a month, and I've decided to jump back in. I considered going back to Rimu, but something I read on a blog convinced me to give Slicehost a shot. So far I'm glad I did.&lt;br /&gt;&lt;br /&gt;For $20/month, I get root access to my very own "slice" with 256MB memory, 10GB storage, and 100GB bandwidth/month. This compares to 96-128MB / 4GB / 30GB bandwidth on Rimu for the same price. You have to move up to Rimu's $40 plan to even get close to these specs (224MB / 4-8GB / 60GB). So space and bandwith are not a problem. However I do expect to bump up against my memory limit once I start doing any serious Rails development, which is my primary reason for getting back in the VPS game.&lt;br /&gt;&lt;br /&gt;One of the reasons for this post, in fact, is to document some of the extra work I had to do to get Ruby and Rails going once my slice was up and running. First, I followed the instructions &lt;a href="http://www.fluidblog.com/articles/2007/04/14/securing-ssh-on-fedora-6-slice"&gt;here&lt;/a&gt; to lockdown logins and SSH ports and stuff.&lt;br /&gt;&lt;br /&gt;This is one of the first chances Slicehost had to shine. You see, I stupidly locked myself out after the last step (installing the &lt;i&gt;iptables&lt;/i&gt; firewall). Slicehost provides an Ajax-based console for just such an occasion. Unfortunately, that wasn't working for me (note to self: enable cookies in Safari next time!), so I needed an even more drastic solution: rebuilding my slice. Fortunately, I was only backing out 10 minutes of work, so this was a viable option. Again, Slicehost really shines here by letting you rebuild automatically from an option on their management screen. A few clicks of the mouse and five minutes of waiting and I got to start fresh, this time being more careful with &lt;i&gt;iptables&lt;/i&gt; and actually proofreading my changes and stuff. Amazing what that will do.&lt;br /&gt;&lt;br /&gt;Then it was on to installing Ruby, &lt;i&gt;et al&lt;/i&gt;. The &lt;i&gt;yum&lt;/i&gt; package manager is your friend here. I considered downloading and building everything from source, but that takes a lot of time, and I'm actually becoming a fan (thanks to MacPorts on my home machines) of the whole package management concept. So a quick &lt;code&gt;yum install -y ruby&lt;/code&gt; got me started.&lt;br /&gt;&lt;br /&gt;Apparently, though, Slicehost installs a &lt;i&gt;very&lt;/i&gt; slimmed-down version of Fedora, as I discovered when I tried to &lt;code&gt;yum install -y rubygems&lt;/code&gt;. I had problems with missing libraries, which I was able to cure with &lt;code&gt;yum install -y ruby-devel&lt;/code&gt;. Which exposed some other missing goodies. Rinse, lather, repeat.&lt;br /&gt;&lt;br /&gt;Long story short, here's what I ended up having to &lt;code&gt;yum install&lt;/code&gt; to be able to build native extensions for gems and do other *nix hackery I'm used to being able to do out of the box:&lt;code&gt;&lt;ul&gt;&lt;li&gt;wget&lt;/li&gt;&lt;li&gt;make&lt;/li&gt;&lt;li&gt;which&lt;/li&gt;&lt;li&gt;tar&lt;/li&gt;&lt;li&gt;gzip&lt;/li&gt;&lt;li&gt;gcc&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;I've also installed &lt;code&gt;nginx&lt;/code&gt; (vice Apache), &lt;code&gt;mongrel&lt;/code&gt;, &lt;code&gt;subversion&lt;/code&gt;, and &lt;code&gt;mysql&lt;/code&gt; - the usual suspects. &lt;code&gt;nginx&lt;/code&gt; works as advertised, and one Mongrel instance handles my Mephisto-generated site traffic nicely. Just that, though, is enough to get me close to 200MB, and that's without the Trac instance I plan to install later. So I may be upgrading my plan even sooner than I expected.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-1571577008589015803?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/1571577008589015803/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=1571577008589015803' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/1571577008589015803'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/1571577008589015803'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/07/starting-up-vps-on-slicehost.html' title='Starting up a VPS on Slicehost'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-194595870503314772</id><published>2007-06-27T11:32:00.001-06:00</published><updated>2007-06-27T12:58:01.152-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='language'/><title type='text'>On the Importance of Being Multi-Lingual</title><content type='html'>My day job is currently focused on porting an Eclipse RCP ("Rich Client Platform") Java application -- essentially a WORA desktop app -- to a Struts2/JSP/Hibernate webapp. We're using a fair amount of JavaScript (via Prototype and Scriptaculous, along with some home-grown code) for the things you'd normally use JavaScript for. Nothing out of the ordinary there.&lt;br /&gt;&lt;br /&gt;One of the home-grown JavaScript bits that I've been working with recently is a text-box filter. You type in the text you want to filter on in a text-box, and the table rows (or li's, or what-have-you) that don't contain that text get hidden, leaving only those that pass the filter. Again, pretty standard stuff.&lt;br /&gt;&lt;br /&gt;The fun started when I wanted to add the ability to filter not just on text, but on a time range chosen from a dropdown menu, like, say, "show me only the results that were posted within the last two hours". I wanted to re-use the existing filter code as much as possible, because a lot of it is exactly what I need, but I needed a way to specify comparing two times (the time of each post versus the time range selected) instead of the default behavior of checking for a match on the input text.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;(Author's Note: Those of you who are familiar with Ruby probably already know exactly where I'm going with this. Bear with me while I build the suspense a little longer).&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;If the filter were written in Java (pre-closures Java, anyway &amp;lt;wink/&amp;gt;), I would have no choice but to jump through some serious refactoring hoops -- implement the new behavior, probably as a new set of methods, and then add in some way to designate which behavior I wanted either by introspecting on my arguments, or by an explicit switch, or...yuck. And if I were only aware of Java idoms, there would be nothing stopping me from taking the same approach in JavaScript.&lt;br /&gt;&lt;br /&gt;Fortunately, I spent the bulk of last year doing some serious Ruby programming, which forced me to get used to the idea of blocks. Equally fortunately, I've taken some time to get familiar with idiomatic JavaScript programming, and learned that functions are first-class datatypes in JavaScript, which means they can work pretty much like blocks do in Ruby.&lt;br /&gt;&lt;br /&gt;And so I was able to solve my problem with minimal impact to the existing codebase, by adding a parameter to the constructor (an optional block/function, defaulting to the existing "match" behavior), and making the code that does the actual filtering call that anonymous function rather than explicitly calling "match". One optional parameter and one (tiny) layer of indirection later, and I've got a solution that will never need extending again. The next time I come up against a type of filter that I haven't already implemented, I can do so on-the-fly, by building a function that implements the exact comparison behavior I want and passing it to the generic filter.&lt;br /&gt;&lt;br /&gt;Do yourself a favor: stop hating on Ruby because you're irritated by all the attention it gets. Stop assuming that if you learn idiomatic Ruby you're going to have to turn in your "Java ROOLZ, Ruby DROOLZ" badge and your "I (heart) static typing" secret decoder ring. Free your mind, and the rest will follow. And whatever other inspirational pop lyric you care to apply here.&lt;br /&gt;&lt;br /&gt;Learning another language (doesn't have to be Ruby; could be Python, or Lisp, or Erlang, or Scala, or...) can only increase your problem-solving capability. And, after all, isn't that what we're all about? Solving problems?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-194595870503314772?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/194595870503314772/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=194595870503314772' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/194595870503314772'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/194595870503314772'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/06/on-importance-of-being-multi-lingual.html' title='On the Importance of Being Multi-Lingual'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-7105838207188315273</id><published>2007-06-25T09:26:00.001-06:00</published><updated>2007-06-25T09:37:56.684-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='humor'/><title type='text'>meme.lolcats.die.die.die</title><content type='html'>The title says it all, really. But I hate one-liner blog posts, so here are some other "memes" that I'd really, really like to see go away:&lt;h1&gt;&lt;code&gt;meme.edgy.job.posting.die.die.die&lt;/code&gt;&lt;/h1&gt;Whenever I see a job posting like this:&lt;pre&gt;SELECT * FROM applicants WHERE knows_php=1 AND has_life=0&lt;br /&gt;ORDER_BY tattoo_count DESC;&lt;/pre&gt;I simply:&lt;pre&gt;DELETE FROM jobs_i_am_interested_in WHERE employer=that_loser;&lt;br /&gt;&lt;/pre&gt;I mean think about it. If you're that darn clever, what do you need me for? And what happens if you do hire me and I turn out not to be clever enough for you? Why should I leave my current position (which is at a pretty awesome startup, btw) for the eventual:&lt;pre&gt;INSERT INTO ranks_of_the_unemployed VALUES(that_chump_who_answered_our_lame_job_posting);&lt;/pre&gt;Same goes for any company looking for any kind of "programming god", "rock star", or "l33t hax0r". In fact, if any part of your job post is ultra-snarky, ultra-hip, or smacks of leetspeak, there's a good chance I will sprain my finger deleting it. I know you're trying to be, like, all Web-Too-Oh and everything, but who do you think you're fooling? In RL &lt;i&gt;(author's note: "Real Life")&lt;/i&gt; you're in your late 40's, and the closest thing you have to "l33t cr3d" is the time you virtually hit on that brooding emo chick whose profile you happened across while prowling MySpace. &lt;i&gt;(Which, by the way -- ewww).&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;However, we both know that at the end of the day, you're still going to be a clock-watching, interchangeable-human-resource-administrating, two-martini-lunch-taking, office-chair-warming executive type with a pathological distrust of technology. And I'm still going to be a code monkey. So let's just call a spade a spade, shall we?&lt;h1&gt;&lt;code&gt;meme.verbing.die.die.die&lt;/code&gt;&lt;/h1&gt;"Incentivize". "Monetize". "Reify".&lt;br /&gt;&lt;br /&gt;Gagize me.&lt;blockquote&gt;&lt;i&gt;Verbing weirds language -- Calvin, of Calvin and Hobbes&lt;/i&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;h1&gt;&lt;code&gt;meme.meme.tag.die.die.die&lt;/code&gt;&lt;/h1&gt;Look, if I really want you to know about my third lung or the time I gave CPR to that baby seal in Alaska, chances are you're my wife or my physician and I've already told you. Otherwise, buzz off.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-7105838207188315273?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/7105838207188315273/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=7105838207188315273' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7105838207188315273'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7105838207188315273'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/06/memelolcatsdiediedie.html' title='meme.lolcats.die.die.die'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-8598252897715574823</id><published>2007-06-22T11:13:00.001-06:00</published><updated>2007-06-25T09:39:07.361-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='compiler'/><category scheme='http://www.blogger.com/atom/ns#' term='language'/><title type='text'>Suggested Reading: "Rich Programmer Food"</title><content type='html'>Steve Yegge: &lt;a href="http://steve-yegge.blogspot.com/2007/06/rich-programmer-food.html"&gt;"Rich Programming Food"&lt;/a&gt;, in which Steve explains why Compilers is the second most important course you can take as a computer science major, and should be required in any self-respecting computer science curriculum. I couldn't agree more.&lt;br /&gt;&lt;br /&gt;I had to fight the temptation to label this "Required Reading", because I know that some of you who follow this link will be turned off by Steve's style, which I appreciate but which even I find a bit rambly at times. And I expect that his sense of humor, which resonates with mine and I therefore appreciate, is not for everyone.&lt;br /&gt;&lt;br /&gt;But I do consider this worth a read, primarily because I whole-heartedly agree with Steve's contention that knowing how compilers work is key to understanding computer science. It is also, not coincidentally, key to getting better as a programmer.&lt;br /&gt;&lt;br /&gt;I used to think that those two concepts were largely orthogonal. I still believe it's possible to be a successful programmer in industry without a good understanding of computer science. I myself have managed nearly 20 years of gainful employment on the strength of a computer science degree, some native ability, and the credibility that naturally goes with that much experience.&lt;br /&gt;&lt;br /&gt;But it's been only recently that I've really started to &lt;i&gt;get&lt;/i&gt; it, to understand the why's and wherefore's. Understanding state machines, and parse trees, and Big-O notation, and ... all that stuff I couldn't be bothered with as an undergrad ... understanding it as a grad student has made my life as a professional programmer a lot easier.&lt;br /&gt;&lt;br /&gt;If you do follow the link, prepare to be challenged. And even if you don't make it all the way through the post, make sure to scroll down to the bottom for the punch line. Again -- I couldn't agree more.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-8598252897715574823?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/8598252897715574823/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=8598252897715574823' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8598252897715574823'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/8598252897715574823'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/06/suggested-reading-programmer-food.html' title='Suggested Reading: &amp;quot;Rich Programmer Food&amp;quot;'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-3350999630886197426</id><published>2007-06-19T15:08:00.001-06:00</published><updated>2007-06-25T09:38:36.692-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Hear, Hear! (A Postscript to My Take on Closures in Java)</title><content type='html'>From &lt;a href="http://www.schemers.org/Documents/Standards/R5RS/r5rs.pdf"&gt;Revised Report on the Algorithmic Language Scheme&lt;/a&gt;, Introduction, Paragraph 1, Sentence 1:&lt;blockquote&gt;Programming languages should be designed not by piling&lt;br /&gt;feature on top of feature, but by removing the weaknesses&lt;br /&gt;and restrictions that make additional features appear necessary.&lt;/blockquote&gt;&lt;br /&gt;P.S.: This was written 'way back in &lt;i&gt;1998&lt;/i&gt;. Another fun quote:&lt;blockquote&gt;Those who cannot remember the past are condemned to repeat it. -- &lt;i&gt;Santayana&lt;/i&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-3350999630886197426?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/3350999630886197426/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=3350999630886197426' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3350999630886197426'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3350999630886197426'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/06/hear-hear-postscript-to-my-take-on.html' title='Hear, Hear! (A Postscript to My Take on Closures in Java)'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-7026356567037459864</id><published>2007-06-13T14:05:00.001-06:00</published><updated>2007-06-13T14:07:49.806-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jruby'/><title type='text'>Hacking JRuby: More on Method Arguments</title><content type='html'>I closed &lt;a href='http://davidrupp.blogspot.com/2007/06/hacking-jruby-bigdecimal-and-ruby.html'&gt;my last post&lt;/a&gt; with this observation:&lt;br /&gt;&lt;blockquote&gt;"...JRuby does not have an equivalent for rb_scan_args(), or at least not one that is called on a per-method basis."&lt;/blockquote&gt;&lt;br /&gt;In the comments, Ola Bini -- ThoughtWorker and JRuby committer -- graciously pointed out that there are, in fact, &lt;i&gt;two&lt;/i&gt; static methods that we can use to simulate the function of &lt;code&gt;rb_scan_args()&lt;/code&gt;, both found in the &lt;code&gt;org.jruby.runtime.Arity&lt;/code&gt; class: &lt;code&gt;checkArgumentCount()&lt;/code&gt;, and &lt;code&gt;scanArgs()&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;checkArgumentCount()&lt;/code&gt; does just what it says. You pass it an array of argument values, along with the minimum and maximum number of all arguments, and it verifies that the actual number of arguments falls within that range (inclusive). So this provides some basic sanity checking of the number of arguments passed to the method you're implementing. If the number of arguments is valid, &lt;code&gt;checkArgumentCount()&lt;/code&gt; returns the actual number of arguments passed. I guess you could argue that this isn't very useful, since you already know the number of arguments you expect, as well as the number given to you. But it is used in a number of places in the JRuby source code.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;scanArgs()&lt;/code&gt; will do a little more work for you. It will do the same sanity checking (it actually calls &lt;code&gt;checkArgumentCount()&lt;/code&gt; to do so), although you specify the numbers slightly differently, passing the number of &lt;i&gt;required&lt;/i&gt; arguments along with the number of &lt;i&gt;optional&lt;/i&gt; arguments. If the actual number of arguments passed is valid, &lt;code&gt;scanArgs()&lt;/code&gt; then creates a new array of length &lt;i&gt;required + optional&lt;/i&gt;, copies the values of any arguments passed in (all required arguments plus any provided optional arguments), sets the values of any non-provided optional arguments to nil, and returns the new array. This actually &lt;i&gt;is&lt;/i&gt; useful, as it transforms a variable-length array of arguments into one of fixed length, where the fixed length is always equal to the maximum number of arguments your method will accept.&lt;br /&gt;&lt;br /&gt;As Ola points out, this still is not quite the equivalent of MRI &lt;code&gt;rb_scan_args()&lt;/code&gt;, in that it doesn't handle "rest" or "block" arguments. But it does offload from us some of the burden of handling variable length argument lists.&lt;br /&gt;&lt;br /&gt;I do plan to use &lt;code&gt;scanArgs()&lt;/code&gt; to finish up my implementation of &lt;code&gt;BigDecimal.mode()&lt;/code&gt;, but in order to so meaningfully I'm also going to have to change &lt;code&gt;mode()&lt;/code&gt;'s method definition from taking a fixed number of arguments to taking a variable number of arguments (the first argument is required, the second is optional). That's going to involve digging into JRuby's mechanism for defining Ruby methods, which I've researched and found to be pretty cool, but it's going to take another few blog posts to sort out. Stay tuned...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-7026356567037459864?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/7026356567037459864/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=7026356567037459864' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7026356567037459864'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/7026356567037459864'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/06/hacking-jruby-more-on-method-arguments.html' title='Hacking JRuby: More on Method Arguments'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-6084650803975560789</id><published>2007-06-12T12:45:00.001-06:00</published><updated>2007-06-12T20:55:43.942-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jruby'/><title type='text'>Hacking JRuby: BigDecimal and Ruby Internals</title><content type='html'>I've submitted another patch for JRuby (viewable &lt;a href='http://jira.codehaus.org/browse/JRUBY-1120'&gt;here&lt;/a&gt;), to implement the &lt;code&gt;BigDecimal.mode()&lt;/code&gt; class method. In doing so, I learned quite a bit about JRuby's implementation strategy, as well as the internals of the C source code for MRI (Matz's Ruby Implementation) Ruby.&lt;h2&gt;&lt;code&gt;BigDecimal.mode()&lt;/code&gt; explained&lt;/h2&gt;&lt;code&gt;BigDecimal.mode()&lt;/code&gt; is a funky little method in the &lt;code&gt;BigDecimal&lt;/code&gt; module, which is not part of the core API but part of the standard library that ships with Ruby. It's kind of multi-variate -- what it does, exactly, depends on how it's called. &lt;br /&gt;&lt;br /&gt;The first parameter to &lt;code&gt;BigDecimal.mode()&lt;/code&gt; is required, and it must be a Fixnum representing either the constant &lt;code&gt;BigDecimal::ROUNDING_MODE&lt;/code&gt; or the exception mode to be set (more on that later). If it's &lt;code&gt;BigDecimal::ROUNDING_MODE&lt;/code&gt; and there is no second argument, then &lt;code&gt;mode()&lt;/code&gt; just returns the current rounding mode. If a second argument &lt;i&gt;is&lt;/i&gt; present, it must also be a Fixnum, and it must equate to one of the seven rounding modes Ruby recognizes (e.g., &lt;code&gt;BigDecimal::ROUND_UP&lt;/code&gt;, &lt;code&gt;BigDecimal::ROUND_FLOOR&lt;/code&gt;, etc.). In this case, &lt;code&gt;mode()&lt;/code&gt; sets the rounding mode (for all &lt;code&gt;BigDecimal&lt;/code&gt;s, remember, since this is a class method) to the value of the second argument.&lt;br /&gt;&lt;br /&gt;If the first argument is a Fixnum that is not equal to &lt;code&gt;BigDecimal::ROUNDING_MODE&lt;/code&gt;, then it is expected to have one of its bits set to correspond to one of the known exception modes (e.g., &lt;code&gt;BigDecimal::EXCEPTION_INFINITY&lt;/code&gt;). Again, if there is no second argument, &lt;code&gt;mode()&lt;/code&gt; simply reports the current exception mode(s) (each bit in the returned value corresponds to a single exception mode set). If there is a second argument, it must be one of 'true' or 'false'. If 'true', &lt;code&gt;mode()&lt;/code&gt; sets the mode passed in the first argument. If 'false', &lt;code&gt;mode()&lt;/code&gt; &lt;i&gt;unsets&lt;/i&gt; (i.e., turns off) the mode passed in the first argument.&lt;br /&gt;&lt;br /&gt;Simple, huh?&lt;br /&gt;&lt;h2&gt;Not So Fast...&lt;/h2&gt;When I picked up this task, &lt;code&gt;mode()&lt;/code&gt; was just a default stub that printed a message to the console and returned &lt;code&gt;nil&lt;/code&gt;. Not a lot to go on there. So I turned to the MRI source code to figure out just what it was supposed to do. &lt;h2&gt;Introducing: &lt;code&gt;rb_scan_args()&lt;/code&gt;&lt;/h2&gt;One of the first things MRI does (in a &lt;i&gt;lot&lt;/i&gt; of methods, as it turns out) is to call the function &lt;code&gt;rb_scan_args()&lt;/code&gt;), which is implemented in the file &lt;code&gt;class.c&lt;/code&gt; with the following signature:&lt;pre&gt;int rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)&lt;/pre&gt;It takes the number of arguments passed, a pointer to a structure containing the values of those arguments, a format string of some sort, and...some other stuff. The number and values of the arguments are self-explanatory, but the format string and the trailing "other stuff" are decidedly not, so let's take a look at them.&lt;br /&gt;&lt;br /&gt;The format string consists, minimally, of two digits. The first digit is the number of required arguments, the second is the number of optional arguments. &lt;code&gt;rb_scan_args&lt;/code&gt; parses the format string to find these numbers, then it walks the list of argument values and stuffs each value into its corresponding reference (which is what the "other stuff" in the signature actually is: a group of references to store the values of the arguments in).&lt;br /&gt;&lt;br /&gt;For example, &lt;code&gt;BigDecimal.mode()&lt;/code&gt; makes this call to &lt;code&gt;rb_scan_args&lt;/code&gt;:&lt;pre&gt;if(rb_scan_args(argc,argv,"11",&amp;which,&amp;val)==1) val = Qnil;&lt;/pre&gt;In English:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;get one required argument and store its value in the variable &lt;code&gt;which&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;get the optional second argument if it exists and put its value in &lt;code&gt;val&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;if &lt;code&gt;rb_scan_args&lt;/code&gt; returned 1 (i.e., only one argument was provided), then set the value of the optional argument to its default of &lt;code&gt;nil&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;So this is how MRI Ruby (as implemented in C) handles variable/optional arguments in  a general way. There's more to it, of course, including an astonishing bit of hackery with C macros that actually implements putting the argument values in the right place for return. But I won't go into that until I understand it better. Also, the format string allows for the Ruby constructs of "rest args" (indicated by an '*' in the format string) and finally a "block arg" (indicated by an '&amp;').&lt;h2&gt;Meanwhile, Back in JRuby...&lt;/h2&gt;This has gone on a bit long, so I'll just close by saying that JRuby does not have an equivalent for &lt;code&gt;rb_scan_args()&lt;/code&gt;, or at least not one that is called on a per-method basis. The runtime is responsible for bundling arguments and calling the appropriate Java method based on the number of arguments actually present. This causes a bit of a problem right now for class methods that take optional arguments (as &lt;code&gt;BigDecimal.mode()&lt;/code&gt; does), but that's a subject for another post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-6084650803975560789?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/6084650803975560789/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=6084650803975560789' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6084650803975560789'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6084650803975560789'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/06/hacking-jruby-bigdecimal-and-ruby.html' title='Hacking JRuby: BigDecimal and Ruby Internals'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-3938901751273734020</id><published>2007-06-07T11:41:00.000-06:00</published><updated>2007-06-12T20:56:17.396-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='humor'/><title type='text'>Umm, What Exactly Is Google Trying To Tell Me Here?</title><content type='html'>Dear Google,&lt;br /&gt;&lt;br /&gt;Look, I know this is a weblog about programming and Java and stuff, and I know you're just trying to help out with the targeted, "relevant" ads. And I'm okay with that. Really, I am.&lt;br /&gt;&lt;br /&gt;However...&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh6.google.com/image/david.e.rupp/RmhBExZumQI/AAAAAAAAAAw/xVVZrsKzqJY/s288/GoogleAd.png" /&gt;&lt;br /&gt;&lt;br /&gt;I think the whole "help out the poor, socially inept Revenge of the Nerds rejects" vibe is a bit much, huh?&lt;br /&gt;&lt;br /&gt;Love,&lt;br /&gt;David&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-3938901751273734020?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/3938901751273734020/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=3938901751273734020' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3938901751273734020'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/3938901751273734020'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/06/umm-what-exactly-is-google-trying-to.html' title='Umm, What Exactly Is Google Trying To Tell Me Here?'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-4908815129753837848</id><published>2007-06-07T11:01:00.001-06:00</published><updated>2007-06-12T20:56:40.114-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='misc'/><title type='text'>Kickin' it Old School: Inspecting $CLASSPATH with sed and grep</title><content type='html'>Here's a fun &lt;code&gt;sed&lt;/code&gt; one-liner that I used today to break up the entries in my $CLASSPATH:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;echo $CLASSPATH | sed 's/:/\&lt;i&gt;&amp;lt;return&amp;gt;&lt;/i&gt;&lt;br /&gt;/g'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that the &lt;i&gt;&amp;lt;return&amp;gt;&lt;/i&gt; above means to actually hit the return key following the backslash. This bit of awkwardness is &lt;code&gt;sed&lt;/code&gt;'s way of specifying a literal newline as part of the substitution string (how literal can you get?). The net effect is to replace the colon characters with newlines, resulting in a display of my classpath with one entry per line.&lt;br /&gt;&lt;br /&gt;I needed this in the context of figuring out a broken Ant build while testing some changes I'm making to JRuby. Unfortunately, even the pretty-printed version of my Ant classpath was too long to sift through with the naked eye, so I turned to &lt;code&gt;grep&lt;/code&gt; to look for exactly what I needed:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;echo $CLASSPATH | sed 's/:/\&lt;i&gt;&amp;lt;return&amp;gt;&lt;/i&gt;&lt;br /&gt;/g | grep jruby.jar'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That is, break up the classpath into one line per entry, and show me only the entries for jruby.jar. With this, I was able to determine that I had an older version of jruby.jar on my classpath that is incompatible with the current trunk. Problem solved!&lt;br /&gt;&lt;br /&gt;I love a happy ending.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-4908815129753837848?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/4908815129753837848/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=4908815129753837848' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4908815129753837848'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4908815129753837848'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/06/kickin-it-old-school-inspecting.html' title='Kickin&amp;#39; it Old School: Inspecting $CLASSPATH with sed and grep'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-6730056279916456533</id><published>2007-06-05T14:49:00.001-06:00</published><updated>2007-08-07T16:00:57.152-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Rant: JSP + OGNL + Collections == Train Wreck</title><content type='html'>&lt;h2&gt;The Story So Far&lt;/h2&gt;In my day job I'm using Struts 2, with JSP as the view templating mechanism. I have a collection whose size I'd like to report on the page. Unfortunately for my sanity, I have recent experience with Ruby on Rails, in which such a thing is as simple as:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&amp;lt;%= @myCollection.size %&amp;gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;But no.&lt;h2&gt;Problem #1&lt;/h2&gt;Struts 2 exposes properties on the Action class via OGNL, which has a nice, clean, property-based syntax, much like RHTML (which is what Rails uses for its view templates). So I should be able to ask for something like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;${myCollection.size}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Assuming, that is, that I have a method &lt;code&gt;getMyCollection()&lt;/code&gt; defined on my action. Which I do. &lt;br /&gt;&lt;br /&gt;The problem here is that I also have to have a method called &lt;code&gt;getSize()&lt;/code&gt; defined on whatever &lt;code&gt;getMyCollection()&lt;/code&gt; returns. Which is a &lt;code&gt;java.util.Set&lt;/code&gt;. Which, for some reason, does not &lt;i&gt;have&lt;/i&gt; a &lt;code&gt;getSize()&lt;/code&gt; method. It has a &lt;code&gt;size()&lt;/code&gt; method instead. Apparently the designers of the Java 2 Collections API were feeling a mite saucy when they went a-designin', and were daring their overlords to punish them for ignoring the Java Beans method naming convention that, as it turns out, OGNL relies on heavily.&lt;br /&gt;&lt;br /&gt;D'oh!&lt;br /&gt;&lt;br /&gt;No problem, though. OGNL doesn't &lt;i&gt;require&lt;/i&gt; method names to be bean-compliant, it just &lt;i&gt;prefers&lt;/i&gt; it in its chain of figuring out what the heck you're asking for. You can invoke any method directly, as in:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;${myCollection.size()}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Problem solved! Let's save everything and reload:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;Struts Problem Report&lt;br /&gt;Struts has detected an unhandled exception:&lt;br /&gt;Messages: &lt;br /&gt;view.jsp(40,109) The function size must be used with a prefix when a default namespace is not specified&lt;br /&gt;org.apache.jasper.JasperException: view.jsp(40,109) The function size must be used with a prefix when a default namespace is not specified&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;What the...?! Oh. The JasperException must mean that the &lt;code&gt;${...}&lt;/code&gt; expression is being interpreted as JSP EL instead of OGNL. Bummer. Oh well. I'll just let the EL engine handle it.&lt;br /&gt;&lt;br /&gt;Which brings me to:&lt;h2&gt;Problem #2&lt;/h2&gt;The JSP EL engine can't handle it. Neither the property syntax nor the method syntax works. The property syntax wants to use the (non-existent) &lt;code&gt;getSize()&lt;/code&gt; method too (go figure). And the method syntax doesn't exist. &lt;br /&gt;&lt;br /&gt;So. Since it looks like JSP EL trumps OGNL when the page is rendered, I'll just turn off JSP EL evaluation (in web.xml) and let OGNL handle everything.&lt;br /&gt;&lt;br /&gt;Or not. That results in a ton of errors from pages in my app that rely on JSP EL.&lt;h2&gt;Solution&lt;/h2&gt;I'll try to wrap this up. It turns out that the answer is &lt;i&gt;JSP functions&lt;/i&gt;. "All" I have to do to get the number of items in my collection is write a public function class and implement a static method -- which I get to name anything I want! Just like the Collections API designers! -- that takes the collection as a parameter and returns its size. &lt;br /&gt;&lt;br /&gt;Oh, and I have to write a snippet of XML in the form of a .tld file that tells the JSP where to find this function.&lt;br /&gt;&lt;br /&gt;Oh, and I have to declare the .tld as a taglib at the top of the JSP. And that's all I have to do.&lt;br /&gt;&lt;br /&gt;Sheesh.&lt;h2&gt;Conclusion&lt;/h2&gt;Fortunately, it turns out that the hardest part of this work has been done for me, in the form of the JSTL implementation (documented &lt;a href='http://java.sun.com/products/jsp/jstl/1.1/docs/tlddocs/fn/tld-summary.html'&gt;here&lt;/a&gt;) of several useful JSP functions, including &lt;code&gt;length()&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;It doesn't help my mood any that in Ruby -- if the problem existed to begin with, which it doesn't -- I could have solved it simply by adding a &lt;code&gt;getSize()&lt;/code&gt; method, aliased to the &lt;code&gt;size()&lt;/code&gt; method of the &lt;code&gt;Set&lt;/code&gt; class, and everyone would be happy.&lt;br /&gt;&lt;br /&gt;Grumble.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-6730056279916456533?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/6730056279916456533/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=6730056279916456533' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6730056279916456533'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/6730056279916456533'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/06/rant-jsp-ognl-collections-train-wreck.html' title='Rant: JSP + OGNL + Collections == Train Wreck'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-4541762771086686992</id><published>2007-06-04T12:14:00.001-06:00</published><updated>2007-06-12T21:01:41.022-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='antlr'/><title type='text'>Required Reading: Enforcing Strict Model-View Separation in Template Engines</title><content type='html'>So I've been reading Terence Parr's excellent addition to the Pragmatic Programmers catalog: &lt;a href='http://www.pragmaticprogrammer.com/titles/tpantlr/index.html'&gt;The Definitive ANTLR Reference&lt;/a&gt;. This book is a lot of fun for me (I recommend it highly, btw), because it's gotten me thinking again about languages and language implementation issues in a way that I haven't had to since my first compilers class. Dr. Parr has a relaxed and informative writing style that can make you forget you're reading about some pretty hardcore computer science.&lt;br /&gt;&lt;br /&gt;I wanted to do some more reading on StringTemplate, the templating engine ANTLR uses to emit the results of its translation, because I read that it can be (is) used to build websites as well. My searching led me to Dr. Parr's paper from 2004: &lt;a href='http://www.cs.usfca.edu/~parrt/papers/mvc.templates.pdf'&gt;Enforcing Strict Model-View Separation in Template Engines&lt;/a&gt;. In it, Parr explains -- and most importantly &lt;i&gt;formalizes&lt;/i&gt; -- his ideas about the need for strict separation in view templating mechanisms, and how the vast majority of existing solutions (e.g., JSP, Velocity) fall far short, ironically because of the Turing-complete power they provide.&lt;br /&gt;&lt;br /&gt;We Java programmers who have been around since the introduction of JSP have already been informally exposed to some of these concepts, in the form of the Model 1 -&gt; Model 2 architecture evolution. One of Dr. Parr's key points, though, is that it's not enough simply to &lt;i&gt;recommend&lt;/i&gt; avoiding using the full computational power available in (for example) JSP scriptlets. The temptation to use that power, even in seemingly innocuous ways, is simply too strong to be resisted for long. Parr's insight that having full access to the Turing-complete power inherent in JSP/Velocity/what-have-you actually gets in our way as developers reminds me of the 37Signals take on &lt;a href='http://www.37signals.com/svn/archives2/constraints_breed_breakthrough_creativity.php'&gt;embracing constraint&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Mind you, I don't expect to be organizing a revolution around replacing my company's use of JSP with StringTemplate anytime soon. But I do plan to develop the personal discipline of following Dr. Parr's advice and embracing constraint by voluntarily avoiding the constructs that violate strict separation. That statement may seem to contradict the statement above about temptation being too strong, but I think that if enough individuals had the same resolve, we could improve the situation somewhat until a move to StringTemplate or its equivalent became politically feasible.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-4541762771086686992?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/4541762771086686992/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=4541762771086686992' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4541762771086686992'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4541762771086686992'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/06/required-reading-enforcing-strict-model.html' title='Required Reading: Enforcing Strict Model-View Separation in Template Engines'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-4033424697472742483</id><published>2007-06-04T06:00:00.000-06:00</published><updated>2007-06-12T20:57:36.356-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Closures in Java: Continued</title><content type='html'>In the comments section of &lt;a href='http://davidrupp.blogspot.com/2007/05/closures-in-java-why-bother.html'&gt;my last post&lt;/a&gt;, user &lt;a href='http://www.blogger.com/profile/08621739963082876297'&gt;sanity&lt;/a&gt; makes the following astute observation, re: my closing remark about Java being a Turing complete language:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Well, pretty-much every programming language is Turing complete, so by that argument, why bother adding any feature to any language?&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;To which I reply: "Bravo!" and "Well said." I'll even take it one step further: anything that can be done in a high-level programming language can be done in assembler language, or even machine code. I know from hard experience, having implemented an encapsulation/data-hiding scheme using IBM mainframe assembler language, and having patched code using the raw machine opcodes in the same environment. Ah, the good old days!&lt;br /&gt;&lt;br /&gt;So I'll go back to programming in assembler language, and the rest of you can either join me or take a well-earned break. Thanks for playing.&lt;br /&gt;&lt;br /&gt;All right -- as much fun as the &lt;i&gt;reductio ad absurdum&lt;/i&gt; game is, all it does is reduce the argument to an absurdity. It doesn't really point to a constructive resolution. Although, in this case, it does raise the question (&lt;i&gt;note: notice I did not say, "begs the question," which is a &lt;a href='http://en.wikipedia.org/wiki/Solecism'&gt;solecism&lt;/a&gt; that I will reserve for another post&lt;/i&gt;) of the degree to which the effort involved in constructing a higher-level language offsets the effort involved in writing code in a low-level language.&lt;br /&gt;&lt;br /&gt;I think we can all agree that programming in C or C++ is more pleasant and more efficient in terms of our time than programming in assembler language. You give up some flexibility (e.g., the ability to stuff a specific value in a specific register, direct access to all non-protected memory), but you gain recursion, stack management, register coloring -- all that good stuff. &lt;br /&gt;&lt;br /&gt;Move up to Java, or Objective-C (version 2.0; coming soon!) and you get the added relief of automatic memory management (allocation and garbage collection). With Java and its ilk, you also get checked exceptions, which you may or may not appreciate.&lt;br /&gt;&lt;br /&gt;Move up to Ruby (JRuby), or Python (Jython), or LISP, and you get dynamic typing and language/runtime-level support for first-class functions/blocks/closures.&lt;br /&gt;&lt;br /&gt;So switching between these flavors of Turing-completeness makes sense. If you need close-to-the-metal performance with some measure of portability, use C. If you need rapid prototyping, use Ruby or Python. If you need to justify spending $300+ on IntelliJ IDEA, use Java. (Sorry, couldn't resist). &lt;br /&gt;&lt;br /&gt;My point (and I do have one) is that the difference between Turing-complete-Java and Turing-complete-Java-now-with-closures! is just not worth the effort it looks like adding this nice-but-not-essential feature to the language will require. &lt;br /&gt;&lt;br /&gt;Closures in Java will muddy an already-dense syntax even further. They will require more code butchery and internal gymnastics on behalf of the compiler. They will be devilishly hard to add in a way that makes sense, given Java's baked-in typing restrictions and flow control. And, in the end, they're going to satisfy the demands of a vocal minority. The masses will still take the path of least resistance, and will probably avoid closures like the plague. And those of us who make a point of being aware of non-Java programming idioms will appreciate the differences between the many available languages, and will continue to strive to use the best tool for the job at hand.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-4033424697472742483?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/4033424697472742483/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=4033424697472742483' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4033424697472742483'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4033424697472742483'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/06/closures-in-java-continued.html' title='Closures in Java: Continued'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-4020867553629215391</id><published>2007-05-30T23:16:00.001-06:00</published><updated>2007-06-12T20:57:51.676-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Closures in Java: Why Bother?</title><content type='html'>Yes, I know Closures are Cool. All the "cool", "new" dynamic languages, like Ruby (which is actually older than Java, BTW), have them, so if Java is going to be "cool", Java needs closures.&lt;br /&gt;&lt;br /&gt;Or does it?&lt;br /&gt;&lt;br /&gt;My original title for this entry was "Closures in Java: Lipstick on a Pig?", but I decided against that because I don't want to imply any disdain for Java. I've programmed professionally in everything from IBM mainframe assembler language to (shudder) COBOL to Java to Ruby. I spent most of last year developing websites in Ruby on Rails. I currently develop client and server-side solutions in Java.&lt;br /&gt;&lt;br /&gt;Professionally, I can't afford to be a language - or even a language-feature - snob. There are things I used to be able to do in assembler language that I can't do in Java, or even (gasp) Ruby. Do you see me writing proposals to add BALR functionality to the Java language? Of course not. Java is not IBM mainframe assembler language. It was never intended to be.&lt;br /&gt;&lt;br /&gt;Nor was it intended to be Ruby. Or Python. Or ML. Or LISP. Or whatever your particular favorite non-Java programming language happens to be.&lt;br /&gt;&lt;br /&gt;I wish the Java Powers That Be would leave well enough alone, be happy with the success of Java and the JVM, and stop trying to be Everything To Everybody. &lt;br /&gt;&lt;br /&gt;Enough with the inferiority complex, already. &lt;br /&gt;&lt;br /&gt;So Java doesn't have first-class functions (which some would say aren't essential for closures, but if you're not going to have first-class functions, why bother?). Big deal. It has a large install-base. It has Tomcat. It has the JVM. It has static typing, which is great if you're into that kind of thing. In short, it has everything you need to develop useful, useable, functional programs.&lt;br /&gt;&lt;br /&gt;Could Java be a better language? Probably. But could it be worse? Certainly (yes, I'm talking to you, C++).&lt;br /&gt;&lt;br /&gt;So why not just let Java be Java? If you really need first-class functions/blocks/closures, use Smalltalk. Or Ruby. Or Python. Or Lisp. If you really need first-class functions/blocks/closures on the JVM, use Smalltalk/JVM. Or JRuby. Or Jython. Or Armed Bear Common Lisp. Or any of the (astonishingly large) number of non-Java languages that have been implemented to run on the JVM.&lt;br /&gt;&lt;br /&gt;Here's a thought: look at how JRuby or Jython implement blocks in Java code, and abstract that code into a new library for Java. Or use the existing Jakarta Commons Functor project. There are enough options available that hacking the language spec beyond recognition and bastardizing a decent language in the name of Keeping Up with the Language Jones-es should not have to be considered the only viable option.&lt;br /&gt;&lt;br /&gt;Java is a Turing complete language -- by definition, you can do anything in Java that you can in any other Turing complete language. Which means that adding closures to Java, while an interesting theoretical and mental challenge, is not necessary.&lt;br /&gt;&lt;br /&gt;So why bother?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-4020867553629215391?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/4020867553629215391/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=4020867553629215391' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4020867553629215391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4020867553629215391'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/05/closures-in-java-why-bother.html' title='Closures in Java: Why Bother?'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9905395.post-4755239537000992875</id><published>2007-05-29T17:04:00.000-06:00</published><updated>2007-06-12T20:58:06.593-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jruby'/><title type='text'>Hacking JRuby</title><content type='html'>I recently got to contribute to the JRuby project -- issue #JRUBY-914: "JRuby's BigDecimal needs to round". In doing so, I got to learn a bit about Java's BigDecimal -- particularly how it differs from Java 1.4 to Java 5, implement a bit of code in the JRuby codebase, and test my ideas conveniently and interactively using Jython. Not a bad way to spend a couple hours hacking and contributing to the community.&lt;br /&gt;&lt;br /&gt;In a nutshell, &lt;code&gt;java.math.BigDecimal&lt;/code&gt; allows you to specify a &lt;i&gt;scale&lt;/i&gt;, which is the number of digits to the right of the decimal place. So a BigDecimal with an unscaledValue of 1 and a scale of 1 would evaluate to 0.1. Java 5 allows the scale to take a negative value. So a BigDecimal with an unscaledValue of 1 and a scale of -1 would evaluate to 10. This is significant for rounding because it allows you to round a number to any digit, right or left of the decimal. But Java 1.4 does not allow a negative scale, so we need another approach to allow the same behavior on both versions.&lt;br /&gt;&lt;br /&gt;My solution was to use the &lt;code&gt;movePointLeft()&lt;/code&gt; and &lt;code&gt;movePointRight()&lt;/code&gt; methods of the BigDecimal class to achieve the same result. It turns out those methods &lt;b&gt;do&lt;/b&gt; permit a negative offset, so moving the decimal point "right" by a negative number actually moves it to the left, and vice versa. This turns out to be all we need to achieve the desired behavior: moving the decimal point to the right &lt;i&gt;scale&lt;/i&gt; times positions the digit to be rounded to just to the left of the decimal point. We can then round normally, using whichever Java rounding mode suits our needs. Then we shift the decimal point left &lt;i&gt;scale&lt;/i&gt; times to restore the (now rounded) number.&lt;br /&gt;&lt;br /&gt;Note that this algorithm works for both negative and positive values of &lt;i&gt;scale&lt;/i&gt;. It's a little bit of overkill when &lt;i&gt;scale&lt;/i&gt; is non-negative, though, so I just implemented it for the interesting case of &lt;code&gt;&lt;i&gt;scale&lt;/i&gt; &lt; 0&lt;/code&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9905395-4755239537000992875?l=davidrupp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://davidrupp.blogspot.com/feeds/4755239537000992875/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9905395&amp;postID=4755239537000992875' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4755239537000992875'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9905395/posts/default/4755239537000992875'/><link rel='alternate' type='text/html' href='http://davidrupp.blogspot.com/2007/05/hacking-jruby.html' title='Hacking JRuby'/><author><name>David Rupp</name><uri>http://www.blogger.com/profile/16410820024654313029</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/_MuRSvkgEbss/Sn3S2E9U7qI/AAAAAAAAACs/z7ZbTEGU7oY/S220/drupp_gravatar.png'/></author><thr:total>1</thr:total></entry></feed>
