<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN"
"http://p.moreover.com/xml_dtds/rss-0_91.dtd">
<rss version="0.91">

<channel>
<title>whateverblog.</title> 
<link>http://www.joecheng.com/blog/</link> 
<description>Joe Cheng's devblog.</description> 
<language>en-us</language> 



<item>
<title>This blog has moved</title>
<link>http://www.joecheng.com/blog/entries/Thisbloghasmoved.html</link>
<description>
<![CDATA[
<p>New URL: <a href="http://blog.joecheng.com">http://blog.joecheng.com</a><br />
New RSS: <a href="http://blog.joecheng.com/index.rdf">http://blog.joecheng.com/index.rdf</a></p>
<p>See you there!</p>
]]>
</description>
</item>



<item>
<title>Virtual Private Servers rock!</title>
<link>http://www.joecheng.com/blog/entries/VirtualPrivateServersrock.html</link>
<description>
<![CDATA[
<p>This blog is now coming to you from my brand new <a href="http://www.sw-soft.com/en/products/virtuozzo/ve/">virtual private server</a> (VPS), hosted by <a href="http://www.vpscenter.com">vpscenter.com</a>.</p>
<p>I'd never heard of VPS, but it seems to be all the rage in the web hosting business--and rightfully so. VPS gives you root access to your own virtual Linux machine, so you get just about all of the control and flexibility of running your own physical server: you can add your own Apache modules, install new languages (like Ruby!), run a J2EE app server, provide IMAP access, schedule jobs, create users... whatever.</p>
<p>Unlike running your own physical server, though, VPS is dirt cheap. We're talking $20/month cheap, compared to $10 for shared hosting and, say, $100 for a dedicated server (and that's not counting the cost of the server itself!). I'm guessing most VPS hosting companies deploy dozens if not hundreds of virtual Linux instances on each skinny little 1U server; the incremental cost of each additional VPS account follows the economies of shared hosting, not the economies of dedicated servers.</p>
<p>The downside, of course, is that you don't get a 2.6GHz of that Xeon and all 4GB of fancy ECC RAM all to yourself--you have to share it with all the other customers on that particular server. Really, though, how many sites need more than 2% (on avg) of a modern server's CPU time?</p>
<p>Every VPS provider I've seen also throws in friendly web-based control panel software that make it easy for you to play webhost to your friends or customers--just a few clicks through a wizard interface and the software automatically configures a new virtual host in Apache, creates the appropriate FTP, shell, and e-mail accounts, and makes the relevant changes to the VPS provider's DNS servers. Luckily, VPS providers generally don't charge you per virtual host, and many don't set any artificial limit on how many virtual hosts you can have.</p>
<p>As of tonight I'm running <a href="http://www.caucho.com">Resin</a> (with jikes and JDK 1.4.2) on my VPS. I'm planning to run an rsync daemon for backup purposes. mod_python came pre-installed, but not mod_ruby, which I'll have to remedy. It still amazes me that I can say all that about a $20/month account!</p>
<p>Frankly, I don't see why any serious web developer would get a traditional web hosting account these days--VPS gives you so much for so little, it's too good a deal to pass up!</p>
]]>
</description>
</item>



<item>
<title>Cryptography in .NET and Java</title>
<link>http://www.joecheng.com/blog/entries/Cryptographyin.NETandJava.html</link>
<description>
<![CDATA[
<p>Like most programmers, I'm far from a cryptography expert. That's why I'm so excited about the cryptographic services provided by both the .NET Framework (<a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconCryptographicServices.asp">System.Security.Cryptography</a>) and the Java platform (<a href="http://java.sun.com/products/jce/">Java Cryptography Extension</a>). Right out of the box, both give you easy and accessible libraries for doing symmetric and asymmetric encryption, one-way hashing, and digital signatures.</p>
<p>Thanks to these libraries, you don't need to have a very deep understanding of cryptography to begin using it to help secure your applications. You don't need to understand how or why one-way hashing or asymmetric encryption work; you just need to understand where and why you would want to use each one.</p>
<p>If you're a Java or .NET programmer and you're unfamiliar with (or intimidated by) cryptography, take a look at this <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconcryptographyoverview.asp">intro</a>&nbsp;at MSDN. Practically every serious developer should have an understanding of these concepts, especially since the tools are already sitting there in your classpath or GAC, just waiting to be used!</p>
]]>
</description>
</item>



<item>
<title>New toy</title>
<link>http://www.joecheng.com/blog/entries/Newtoy.html</link>
<description>
<![CDATA[
<p>My parents were nice enough to get me a <a href="http://www.liteonit.com/ODD/english/new_p_e/english--p-drw4440.htm">DVD burner</a> for Christmas. So far it's been working great; very fast with both DVD+RW and CD-R (the only two media I've tried so far). And as a bonus, it's far quieter than my aging PlexWriter 12/10/32.</p>
<p>Several other toys have caught my eye lately. One is the <a href="http://www.echoindigo.com/">Echo Indigo</a>, a CardBus sound card that supports 24/96 playback and has incredibly good <a href="http://www.echoindigo.com/content/info/techspecs.html">specs</a>. At work, I've got my poor&nbsp;<a href="http://www.sennheiserusa.com/pages/products/headphones/580.htm">580's</a>&nbsp;plugged directly into my laptop's headphone jack, and it sounds terrible--very audible line noise and distortion. Seems like the Indigo, on the other hand, would do justice to even <a href="http://www.sennheiserusa.com/pages/products/headphones/650.htm">very high quality</a> headphones. At $130, it's even a decent value, though I'm not sure I personally could justify buying it over the cheaper, less elegant alternatives [<a href="http://www.creative.com/products/product.asp?prodid=4991">1</a>, <a href="http://www.musiciansfriend.com/srs7/sid=040112224714066167184028703406/g=rec/search/detail/base_pid/701381/">2</a>].</p>
<p>The next is VIA's new(?) <a href="http://store.ituner.com/ituner/viaepiap4itx.html">P4-ITX</a> motherboard, which supports most P4 or Celeron processors and includes S-video out, USB 2.0 ports, optional FireWire support, network adapter, and a PCI slot, all in an area smaller than 7" squared! Stick it in one of <a href="http://www.mini-itx.com/reviews/enote/">these</a> and you've got the start of a book-sized PC with some serious grunt.</p>
<p>And finally, in the "Stuff I'll Never Own" category, Meridian, one of my favorite hi-fi manufacturers, has launched the <a href="http://www.meridian-audio.com/g_bro_system.htm">G Series</a> line of components--and wow. These things are <em>drop dead gorgeous</em>. The G Series is a step up from the already expensive <a href="http://www.meridian-audio.com/p_501.htm">500 Series</a>, which means these components are well into "Should I buy a lightly used BMW instead?" range. (Scroll to the bottom of <a href="http://www.audiorevolution.com/news/0803/29.meridian.shtml">this page</a> for the damage.)</p>
]]>
</description>
</item>



<item>
<title>Book: ANSI Common Lisp by Paul Graham</title>
<link>http://www.joecheng.com/blog/entries/BookANSICommonLispbyPaulG.html</link>
<description>
<![CDATA[
<p>Well, <a href="entries/Twobooks.html">Programming Language Pragmatics</a> was too heavy (physically, that is... it's a huge hardcover textbook) for me to read on my commute, so I'll have to try to work through it at work or home.</p>
<p>Now my commute is being spent learning Lisp, via Paul Graham's <a href="http://www.paulgraham.com/acl.html"><em>ANSI Common Lisp</em></a>. So far it's absolutely excellent. Paul Graham must be one of the most articulate voices in computing. Everything I've read by him (check out his <a href="http://www.paulgraham.com">website</a>) has been incredibly well written: interesting, clear, concise, and generally pretty convincing. So far <em>ANSI Common Lisp</em> is the most readable and enjoyable programming language book I've come across, eclipsing my previous favorite: <em><a href="http://www.rubycentral.com/book/index.html">Programming Ruby</a></em>. The Lisp community is lucky to have this guy as an advocate.</p>
]]>
</description>
</item>



<item>
<title>Learning Linux</title>
<link>http://www.joecheng.com/blog/entries/LearningLinux.html</link>
<description>
<![CDATA[
<p>Since my Java-based IMAP server project has been gathering dust since well before the wedding, I thought I'd take another crack at setting up and hosting a Linux mail server out of my apartment. I've never had much luck getting Linux to do my bidding; sure, installing any modern distro is easy and you can boot to a nicely equipped GNOME or KDE desktop without ever firing up a text editor. But once you need to start changing default settings, especially for server stuff... that's where the pain begins. I've always been able to get 80% there, but then something always hangs me up.</p>
<p>Lo and behold... I actually succeeded this time! I've finally got my own Linux mail server: hush.joecheng.com. (It's called "hush" because it's a near-silent machine, using a tiny <a href="http://www.viavpsd.com/product/epia_mini_itx_spec.jsp?motherboardId=21">VIA EPIA</a> board and a very quiet Seagate Barracuda hard drive.)</p>
<p>Unfortunately, my ISP blocks all traffic on port 25 unless it is going to their mail servers. So I actually have all of my mail delivered first to a POP account with my webhost. Hush polls the POP account for messages and then serves them up via IMAP. This is exactly the setup I wanted to support with my own Poorman IMAP server.</p>
<p>To anyone who is familiar with Linux administration, the following will sound like a plain-vanilla ho-hum mail setup. But I suspect it might seem a bit like a <a href="http://www.rube-goldberg.com/html/gallery.htm">Rube Goldberg machine</a> to anyone from the Windows camp! Here are all the programs involved.</p>
<ol>
<li><tt><a href="http://www.catb.org/~esr/fetchmail/">fetchmail</a></tt> - Polls the POP account at my webhost and delivers any mail it finds to localhost.
</li>
<li><tt><a href="http://www.sendmail.org/">sendmail</a></tt> - Accepts the mail for delivery and drops it off with my local account.
</li>
<li><tt><a href="http://www.ii.com/internet/robots/procmail/">procmail</a></tt> - Filters all mail received by my local account (I'm using it to keep separate mail folders for each mailing list I'm subscribed to), saving it to folders in my home directory. This is the "traditional" Unix mailbox system.
</li>
<li><tt><a href="http://www.washington.edu/imap/">imapd</a></tt> - Serves up IMAP sessions to mail clients, optionally over SSL.
</li>
<li><tt><a href="http://www.netfilter.org/">iptables</a></tt> - The built-in Linux "firewall". Port 993 (secure IMAP) was being blocked by default, so I had to figure out how to unblock it.
</li>
</ol>
<p>Luckily, my Red Hat 9 machine already had all of these programs installed! But I still needed to learn how to configure each of the services.</p>
<p>At first I was a little irritated that so many programs in the chain. But really, this is what the Unix philosophy is all about: chaining together small programs that each do one thing well. The downside is having to learn a ton of small programs. The upside is the incredible amount of flexibility and power you get in return.</p>
<p>I realized that configuring Linux has more in common with programming than it does with configuring Windows. With Windows, you can just click and hunt around until you find the checkbox you're looking for; if you have a general idea of what you're trying to do, it doesn't generally take long to figure out how to do it. With Linux, you really have to spend some time learning the program you're about to configure. Each program has its own set of config files; you probably wouldn't even be able to <em>find</em> them without the docs, much less figure out the configuration syntax. So before you even start touching anything, it's best to take a breath, sit down with a tutorial or the <tt>man</tt> page, and really spend some time learning about&nbsp; and understanding the program. As a programmer, you wouldn't want to jump in and start playing with the <a href="http://java.sun.com/j2se/1.4.2/docs/guide/2d/index.html">Java 2D API</a>, calling different methods until things work; it's the same with configuring <tt>sendmail</tt>.</p>
<p>I'm not just trying to present a familiar metaphor here. Traditional Unix programs really are more like APIs than like end user programs on other platforms, and using the shell is very much like interactive programming. So if you're starting out with Linux, understand that for good or ill, you must give system configuration tasks the same kind of attention and respect that you might give programming tasks.</p>
<p>With that in mind, I spent a few nights learning and configuring and learning and configuring. In most cases I only needed to add or modify perhaps a single line to a config file, and often the exact thing I was trying to achieve was specifically addressed somewhere in the documentation. (These programs have been around for a long time; anything you could want to do with them has probably been done thousands of times before.) So this time around, there was very little trial and error involved. I was able to make each change with confidence, and most of the time, it worked the first time.</p>
<p>Fortunately, after all that, it was very much worth the trouble to get it all working. I'm happily using Mozilla Thunderbird 0.4 at home and at work, and I have a consistent view of my mailbox wherever I go thanks to IMAP. I don't have to add mail filtering rules on both clients. I can be sure that when I delete a piece of spam, I'm not going to see it again when I log in from the other computer. And it's all extremely stable, secure, and... free!</p>
]]>
</description>
</item>



<item>
<title>Amy writes her first program!</title>
<link>http://www.joecheng.com/blog/entries/Amywritesherfirstprogram.html</link>
<description>
<![CDATA[
Last night, my wife wrote her first computer program:

<pre>
print 'how many meters: '
usernumber = gets
puts (usernumber.to_f * 3.28).to_s + ' feet'
</pre>

In just a few minutes, she learned about strings, ints, floats, variables, and input/output. I'm married to a genius! :)
]]>
</description>
</item>



<item>
<title>Using instant messaging as a human-software interface</title>
<link>http://www.joecheng.com/blog/entries/Usinginstantmessagingasah.html</link>
<description>
<![CDATA[
<p><a href="http://cephas.net/blog">AJ</a> and I attended <a href="http://ll3.ai.mit.edu">LL3</a> this past weekend and got to hear some pretty cool ideas.</p>
<p>The first talk (by Dana Moore and Bill Wright) presented <a href="http://www.jabber.org">Jabber</a> as a way to control distributed software agents. The user fires up a Jabber client and his "buddy list" consists of all the different agents that are floating around the system. He can send chat messages to interact with each one, getting info such as "help", "status", or directives such as "kill cpu" (their project involved agents that attack a distributed system to see how it holds up).</p>
<p>I really like the idea of using Jabber to communicate with "headless" software in general. The usual solution these days seems to involve embedding a web server into the program, or maybe telnet--and that's if the software has a user interface at all. More commonly, input is restricted to config files, environment variables, and command line switches, and the only output you get is a logfile.</p>
<p>Embedding a Jabber client in your program is much lighter weight than embedding a web server: all you need is a <a href="http://www.jabber.org/developer/librarylist.php">client library</a>&nbsp;(the Smack library for Java, for example, weighs in at 124KB, while Jabber4r for Ruby is less than 75KB) and maybe a thread to poll for messages (I assume--I'm not too familiar with the protocol yet). And sending warning or error messages to an administrator through Jabber is much more likely to get attention than if the messages are simply written to a logfile.</p>
<p>I'll refrain from going into any more detail until I've tried this with one of my own projects. If and when I resume development on my IMAP server, I'd like to use Jabber as the interface to add/remove users, reset passwords, and other administrative tasks.</p>
]]>
</description>
</item>



<item>
<title>Programming languages</title>
<link>http://www.joecheng.com/blog/entries/Programminglanguages.html</link>
<description>
<![CDATA[
<p><a href="http://www.amazon.com/exec/obidos/tg/detail/-/1558604421/qid=1065059858/sr=8-1/ref=sr_8_1/104-4981247-7927131?v=glance&amp;s=books&amp;n=507846"><em>Programming Language Pragmatics</em></a> has been a great read so far. I'm still in the opening chapters, but already I've gained a better appreciation for the amount of variety that exists in programming languages out there. Such a seemingly simple thing as binding values to variable names can differ quite a bit from one language to the next.</p>
<p>Take Python and Ruby, which are very similar in many important ways: interpreted, dynamically typed, "object oriented". Check out these two snippets:</p>
<pre>
# Python code
def test():
  print x

x = 1
test()


# Ruby code
def test()
  print x
end

x = 1
test()
</pre>
<p>Even though the code looks almost identical, you get two very different results. The Python code prints <code>1</code>; the Ruby code throws the error <code>NameError: undefined local variable or method `i' for main:Object</code>. The reason for this is that Python scopes are <em>dynamic</em> and Ruby scopes are <em>static</em>. Depending on your programming background, you may find one or the other very hard to get used to!</p>
<p>This is just a simple example, but it illustrates the kind of subtle differences that make one language "feel" right to some people but not others.</p>
<p>A more fundamental concept I've learned about is "programming without side-effects", which seems to come from the functional programming world. Programming without side-effects means that when you call a function, the only variables that matter are the inputs, and the only effect of the function is returning a value. In other words, given a set of inputs, you must always get the same answer--the answer can't vary over time, or based on the state of a database record, or whatever. It also means the function must not change the state of the world at all, so calling or not calling the function cannot change the behavior or result of some <i>other</i> function.</p>
<p>Why put these restrictions on functions? What's wrong with side-effects? Well, it turns out that if you can count on a function to be side-effect free, you can be less careful when using it. You can cache results without worrying about them becoming stale. You can skip executing it without having to prove the program is still correct. There is guaranteed to be no coupling between one function call and any other function calls that come before or after. For example, imagine your program contains this statement for logging:</p>
<pre>
if (DEBUG)
  print("DEBUG: Window handle: " + getHandle(window));
</pre>
<p>Let's say the <code>getHandle</code> function is not side-effect free. On the contrary, it will create a handle for the window if one does not exist. You should be nervous about removing this logging statement--what if the next line of code implicitly assumes that the window already has a handle? But if <code>getHandle</code> could be guaranteed side effect free, there is no way removing the logging call could change the behavior or your program. (Well, actually <code>print</code> itself is not side effect free.)</p>
<p>That's not to say I'm going to go to work tomorrow and start programming without side-effects; far from it. While there are functional languages that encourage or even require your code to be completely side effect free, it's hard to imagine trying to achieve that in a real world Windows app written in C#; most objects are inherently stateful. But recognizing the value in programming this way will help me write better libraries by avoiding side-effects where possible.</p>
<p>The bottom line is that there is great value in studying different programming languages, even if you never adopt them as your own.</p>
]]>
</description>
</item>



<item>
<title>Two books</title>
<link>http://www.joecheng.com/blog/entries/Twobooks.html</link>
<description>
<![CDATA[
<p>I'm on the last chapters of <em><a href="http://www.amazon.com/exec/obidos/tg/detail/-/0201526883/qid=1065059887/sr=1-1/ref=sr_1_1/104-4981247-7927131?v=glance&amp;s=books">High-Performance Computer Architecture</a></em> by Harold S. Stone, which describes and quantifies many different techniques for increasing the performance of computer systems. As a software programmer with only enthusiast-level knowledge of machine and systems architecture, I found find this book to be a challenging but enjoyable (and very worthwhile) read. If you're interested in the latest processors to come out of Intel, AMD, and IBM labs, but don't know what terms like "superscalar execution" or "8-way set associative cache" mean; or are curious about the difference between big Cray supercomputers and today's desktops; or wonder why 16-processor servers cost much more than 16 times a single-processor server; this book will answer those questions and more.</p>
<p>One thing about the book is that it's somewhat dated: the edition I read was ©1987. However, the principles and techniques taught in the book are still very relevant today. I really enjoyed this book and I imagine most hardcore programmers would too.</p>
<p>I've also just purchased <em><a href="http://www.amazon.com/exec/obidos/tg/detail/-/1558604421/qid=1065059858/sr=8-1/ref=sr_8_1/104-4981247-7927131?v=glance&amp;s=books&amp;n=507846">Programming Language Pragmatics</a></em> by Michael L. Scott, and I can't wait to get started on it. It's a very large and imposing volume that seems to give a pretty comprehensive treatment of the design and implementation of programming languages. Parsing techniques, type systems, variable scoping, instruction-level optimization, it's all in here. There's also a chapter on functional and declarative languages that should be interesting.</p>
<p>Hopefully I can keep my brain from exploding.</p>
]]>
</description>
</item>



<item>
<title>Can zeroconf .NET LAN services be this easy?</title>
<link>http://www.joecheng.com/blog/entries/Canzeroconf.NETLANservice.html</link>
<description>
<![CDATA[
<p>I haven't written about zeroconf services lately so I thought I'd follow up on <a href="entries/AppleRendezvous.html">this comment</a> I made about a month ago:</p>
<blockquote style="MARGIN-RIGHT: 0px">
<p><em><font size="2">I've got some ideas about how to create a nice high-level C# binding to this. You should be able to take a marshal-by-ref object and just make one API call that says "make this available to everyone on the network."</font></em></p>
</blockquote>
<p>Let's compare this idea to making an object available to .NET Remoting clients. Start with the following class:</p>
<pre>
public class MediaLibrary <i>: MarshalByRefObj</i>
{
  <i>///
  /// .NET Remoting housekeeping--make this a singleton
  ///
  public override object InitializeLifetimeService()
  {
    return null;
  }</i>

  ///
  /// Returns a list of media on this server that 
  /// matches the given pattern.
  ///
  public string[] ListMedia(string pattern)
  {
    // ...details, details...
  }

  ///
  /// Returns a stream that is the raw file data
  /// for the requested media.
  ///
  public Stream GetMediaStream(string path)
  {
    // ...details, details...
  }
}
</pre>
<p>If you want to make this a marshal-by-ref singleton object, you need to add the italicized text. As you can see, it's not very expensive in terms of extra lines of code you have to add (though having to derive from <code>MarshalByRefObj</code> could obviously be quite invasive if you're building off an existing class hierarchy).</p>
<p>So now we have a class that can be remoted. How do we get the CLR process to start listening for requests? Well, there are a lot of knobs you can turn here, depending on what kind of serialization you want (binary or XML) and what kind of communications protocol (bare TCP/IP sockets or HTTP) or if you want to customize/extend the process. Let's ignore all of that; here is a simple cut-and-paste template for basic binary TCP/IP communication (which is generally what you want anyway when you're not dealing with firewalls or interop):</p>
<pre>
ChannelServices.RegisterChannel(new TcpChannel(1234));
RemotingConfiguration.RegisterWellKnownServiceType(
    typeof(MediaLibrary),
    "MediaLibrary.binary",
    WellKnownObjectMode.Singleton);
</pre>
<p>The first line creates a <code>TcpChannel</code> on port 1234 and registers it with the runtime, which will from then on listen for remoting calls on that port. The second line takes our particular class and publishes an instance of it at the URL "/MediaLibrary.binary". Thus, these two lines make a media library available at <a onclick="alert('C\'mon, who actually clicks on a tcp:// link?'); return false" href="tcp://hostname:1234/MediaLibrary.binary">tcp://hostname:1234/MediaLibrary.binary</a>.</p>
<p>Clients connect to the server like so:</p>
<pre>
RemotingConfiguration.RegisterWellKnownClientType(
    typeof(MediaLibrary),
    "tcp://myserver:1234/MediaLibrary.binary");
MediaLibrary remoteLibrary = new MediaLibrary();
</pre>
<p>The first method call needs to be done just once (per CLR lifetime), and then any call to <code>MediaLibrary</code>'s constructor will result in the creation of a <code>MediaLibrary</code> remoting proxy that connects to our server, as demonstrated in the last line. You can bury the first call somewhere in your client startup routine, and after that it all looks like magic. Need a <code>MediaLibrary</code>? Just new one up, don't worry about how it is implemented, pay no attention to the man behind the curtain.</p>
<p>While remoting as I've presented it is convenient, it's not particularly dynamic. The client has to know the server hostname, the port, and the service URI in advance; I suppose this info is usually gleaned from a config file, registry key, or user dialog. That's pretty annoying for something like a media library (well, I don't mind having a static service URI; just the hostname and port). What I really want is to be able to automatically get references to any (and hopefully, something approximating "every") <code>MediaLibrary</code> service on the LAN. Furthermore, I'd like to be able to do it without the user ever explicitly setting up a directory server.</p>
<p>Using the same <code>MediaLibrary</code> class, I want the following API on the server:</p>
<pre>
void EasyZeroConf.Publish(MarshalByRefObj serviceObj, string serviceUri);
</pre>
<p>And the following on the client:</p>
<pre>
object[] EasyZeroConf.Find(Type desiredType, string serviceUri);
</pre>
<p>The <code>Publish</code> method is simple enough: Take <em>serviceObj</em> and make it available to anyone requesting the service identified by <em>serviceUri</em>.</p>
<p>The <code>Find</code> method is a little less obvious: Search the LAN for <em>serviceUri</em>, and return the results as an array of remoting proxies of type <em>desiredType</em>. Here's an example:</p>
<pre>
MediaLibrary[] libraries = 
    (MediaLibrary[])EasyZeroConf.Find(typeof(MediaLibrary), "MediaLibrary");
</pre>
<p>That should be literally the only two methods most library users should need to get simple zeroconf LAN services, though I can think of many scenarios where more customization would be very desirable. This post is already getting long though.</p>
<p>I'll also save design/implementation details for another time. My day job beckons...</p>
]]>
</description>
</item>



<item>
<title>Compile Ruby into .EXE!</title>
<link>http://www.joecheng.com/blog/entries/CompileRubyinto.EXE.html</link>
<description>
<![CDATA[
<p>Ever since I started playing with Ruby, I've been dreading the first time I release a Ruby application and have to tell potential users something like, "By the way, to use my little image resizing program, you need to install a complete programming platform." Sure, I've released programs that require the .NET Framework or a JRE, but at least those are pretty mainstream languages. I can at least pretend to believe that, someday, every Windows client will have a recent CLR and JVM.</p>
<p>Turns out there is a nifty free tool called <a href="http://exerb.sourceforge.jp/index.en.html">exerb</a> that compiles a Ruby script into an executable that contains not only the script, but also all dependencies from both the core Ruby interpreter and any Ruby modules you load (whether part of the Ruby standard library or not). The end result is a totally self-contained (albeit slightly bloated) .EXE that will Just Work on any reasonably configured Windows machine, without the end user ever having to know about Ruby.</p>
<p>About that bloat... a trivially simple .rb script under 1.8.0 compiled to an .EXE weighing in at almost 0.5 MB. But that's a small price to pay for the convenience of a single program file with no dependencies.</p>
]]>
</description>
</item>



<item>
<title>Blog software progress</title>
<link>http://www.joecheng.com/blog/entries/Blogsoftwareprogress.html</link>
<description>
<![CDATA[
<p>I've decided to call the blog software "whateverblog", since the first blog it will be used for is this one... and because I am not very creative.</p>
<p>Of the three major parts (GUI frontend for editing content; generate website by merging content with templates; checksum-based sync files with web server via FTP), the first part is done and the second part is about half done. That is, the eRuby part of the templating system is written and the C# front-end is set up to interop with it, but I don't currently have a way to manage or choose between different sets of templates.</p>
<p>Still, I'm really excited that the eRuby stuff was so easy to integrate. I simply wrote a standalone Ruby script that reads in the data file, turns it into a nice array of articles, reads the template into memory, then runs the eRuby processor on the template (making the array of articles available to the template). It's great; I can combine the power and expressiveness of eRuby templates with the easy wysiwyg editing of CityDesk (and Contribute and whatnot).</p>
<p>I have a feeling the template management problem could end up being surprisingly difficult. I don't have a clear picture at all about how it should work. Need to let that one stew a little...</p>
]]>
</description>
</item>



<item>
<title>Next project: blog software</title>
<link>http://www.joecheng.com/blog/entries/Nextprojectblogsoftware.html</link>
<description>
<![CDATA[
<p>Right now I'm using Fog Creek's <a href="http://www.fogcreek.com/CityDesk">CityDesk</a>&nbsp;to generate this blog. While CityDesk is a wonderful tool for its intended purpose--allowing the, shall we say, "technically challenged" to maintain website content--its limitations bother me a little. In particular, its proprietary <a href="http://www.fogcreek.com/CityDesk/help/Top_Level/fog0000000054.html">scripting mini-language</a> is way too specialized for my tastes; it can easily handle simple web scripting needs, but then it hits a brick wall.</p>
<p>So, of course, I've decided to write my own blog software. (Yes, I realize that the world needs yet another blog program like it needs a hole in the head. I just can't help myself.) Actually, I didn't decide to write it as a result of CityDesk letting me down, particularly. I was just messing around with Microsoft's DHTML Edit Control and the blog software grew up around it by accident.</p>
<p>Like CityDesk, a simple WYSIWYG interface will be the input method for individual articles. A static website will be generated on disk, using HTML templates impregnated with Ruby code. Said static website will then be synced over FTP to your web server. (Sorry for all the passive-tense. I haven't named the software yet.)</p>
<p>Though the mechanism is, at a high level, almost identical to CityDesk, I am in no way attempting to create a competitor or substitute. CD lets you do some important things that I am not interested in tackling, such as organize your entries in arbitrarily nested folders, create multiple templates, "intelligently" manage links between entities within your site, etc. I currently intend to punt on all of the above. So my program will only really be useful for blogs, or other one-dimensional content.</p>
<p>The first of the three major parts is done: you can create, edit, and delete posts through a Windows Forms (C#) interface. I am using my own simple file format to persist the posts to disk, to make it easy to marshal the data into the Ruby interpreter.</p>
<p>Screenshots coming soon (not that they'll be very exciting).</p>
]]>
</description>
</item>



<item>
<title>Ruby-style collection iterators in C# (Whidbey)</title>
<link>http://www.joecheng.com/blog/entries/Ruby-stylecollectionitera.html</link>
<description>
<![CDATA[
<p>I sent an e-mail to <a href="http://www.gotdotnet.com/team/dbox/">Don Box</a> asking if the collections API in Whidbey will have Ruby-style iterators over collections, to go with C#'s new support for anonymous methods (aka closures). His entire reply: "Yes it does."</p>
<p>Holy crap... between generics and this, the collection classes in Whidbey are going to <em>rock</em>. I can't wait to do this:</p>
<pre>List&lt;Employee&gt; employees = ...
List&lt;int&gt; employeeIds = employees.Collect(delegate(Employee e){return e.Id;});</pre>
<p>Now if only there was a C# IDE that could keep up with IntelliJ...</p>
]]>
</description>
</item>



<item>
<title>Using Ruby to write C#</title>
<link>http://www.joecheng.com/blog/entries/UsingRubytowriteC.html</link>
<description>
<![CDATA[
<p>Lately I've gotten used to using Ruby to generate particularly mind-numbing chunks of C# code. For example, if I had to write the following:</p>
<pre>
// Red Flag
flagRed.Name = "Red";
flagRed.Text = "Red";
flagRed.OnSelected += new FlagSelectionHandler(flagRed).Handler;
flagRed.Image = "flagRed.jpg";
flags.Add(flagRed);

// Blue Flag
flagBlue.Name = "Blue";
flagBlue.Text = "Blue";
flagBlue.OnSelected += new FlagSelectionHandler(flagBlue).Handler;
flagBlue.Image = "flagBlue.jpg";
flags.Add(flagBlue);

// ...and so on for green, yellow, orange, purple...
</pre>
<p>I can just fire up irb and type the following:</p>
<pre>template = &lt;&lt;TEMPLATE
// @ Flag
flag@.Name = "@";
flag@.Text = "@";
flag@.OnSelected += new FlagSelectionHandler(flag@).Handler;
flag@.Image = "flag@.jpg";
flags.Add(flag@);
TEMPLATE

['Red', 'Blue', 'Green', 'Yellow', 'Orange', 'Purple'].each do |color|
	puts template.gsub(/\@/, color)
end</pre>
<p>and cut and paste the result to Visual Studio. So easy.</p>
<p>I also wanted to share this little bit of Ruby that parses <a href="http://www.unicode.org/Public/UNIDATA/Scripts.txt">Unicode script data</a> and writes C# code with the result:</p>
<pre>require 'net/http'

# get latest script from web
h = Net::HTTP.new('www.unicode.org', 80)
resp, data = h.get('/Public/UNIDATA/Scripts.txt', nil)
if resp.code !~ /^200/
	raise "Error code: #{resp.code}"
end

list = []
scripts = []

# the full text is in 'data' var
data.each_with_index do |line, i|

	# skip comments and all-whitespace lines
	next if line !~ /[^\s]/ or line =~ /^#/

	# parse single-point
	if line =~ /^([0-9A-F]{4,})\s*;\s*(\w*)/
		range = [$1, $1]
		script = $2
	# parse range
	elsif line =~ /^([0-9A-F]{4,})\.\.([0-9A-F]{4,})\s*;\s*(\w*)/
		range = [$1, $2]
		script = $3
	else
		raise "Parse error on line #{i + 1}: #{line}"
	end
	
	list &lt;&lt; [range, script]
	scripts &lt;&lt; script
end

scripts.uniq!  # remove duplicates

# now print C#

list.each do |x|
	(low, high), script = x
	if (low == high)
		puts "scripts.Add(0x#{low}, Script.#{script});"
	else
		puts "scripts.Add(0x#{low}, 0x#{high}, Script.#{script});"
	end
end

puts
puts scripts.join(",\n")</pre>
<p>This is the kind of thing at which Ruby really excels: banging out one-off text processing apps.</p>
]]>
</description>
</item>



<item>
<title>My computer has a new case</title>
<link>http://www.joecheng.com/blog/entries/Mycomputerhasanewcase.html</link>
<description>
<![CDATA[
<p>There's something neat about the fact that you can plunk down $100 (or <a href="http://www.colorcase.com/11101.html">$250</a>, or <a href="http://www.soldam.com">$500</a>&nbsp;if you're a real high roller) and completely change the look of your computer. Too bad you can't do that with a car.</p>
<p>Yesterday I replaced my Lian-Li <a href="http://scasp.webnow.biz/upload/a0000010/en_US/prd_362.jpg">PC60</a>&nbsp;with a (just discontinued) Cooler Master <a href="http://www.coolermaster.com/case/p710.htm">ATC-710-GX2</a>&nbsp;in dark grey. The PC60 is sort of a classic. It was the case that, along with the Cooler Master <a href="http://www.coolermaster.com/case/p200.htm">ATC-200</a>, started the aluminum case trend that dominates enthusiast cases today (a whole, what, three years later). Until the PC60 and ATC-200 hit the scene, the aftermarket offered only <a href="http://www.arstechnica.com/reviews/1q00/pa-atcx/atcx-1.html">standard</a> <a href="http://us.enlightcorp.com/products/pc/detel.php?kind=pccase&amp;serial=70">issue</a> <a href="http://www.in-win.com.tw/home/english/pc_case/a_series_00.htm">beige</a> or <a href="http://www.colorcase.com/22502.html">cheap and cheerful</a>&nbsp;plastics. Now all the <em>über</em>-geeks go to LAN parties toting sleek aluminum cases, unfortunately usually ruined with <a href="http://www.overclockersclub.com/gallery/">big plexi-covered portholes brimming with neon light</a>. Yeesh... kids these days. I wonder what kinds of sick mods they'd inflict on a Porsche. They'd probably line the rims with blue LEDs and <a href="http://www.directron.com/quake3.html">etch a Quake 3 logo</a> into the rear window.</p>
<p>The ATC-710 is quite a nice case, especially considering it's only around a hundred bucks (if you can find one). Even though only the faceplate is aluminum (the rest is ordinary steel), it looks as slick from the outside as any of its pricier all-aluminum brothers. The (all-aluminum) PC60 was over $200 when I bought it, though I think it's now down to $120 or so, which qualifies it as a bargain as well. One nice thing about the ATC-710 is the door over the drive bays; it's hard enough finding a case that looks good, let alone finding drives that match.</p>
<p>My other weird computer fetish is noise control. It really bugs me if my computers are louder than a whisper, mainly because I invested a lot in my stereo system and they share a room. The ATC-710 is noticeably louder than the old PC60, in spite of having a quieter case fan; the new case seems to resonate sympathetically with vibrations from both the hard drive and the power supply, which the Lian-Li did not. Hopefully I'll be able to solve that with some isolation pads from <a href="http://www.directron.com/silence.html">Directron</a>.</p>
]]>
</description>
</item>



<item>
<title>LANMP3 source posted</title>
<link>http://www.joecheng.com/blog/entries/LANMP3sourceposted.html</link>
<description>
<![CDATA[
I've posted the source to the LANMP3, which I described in an <a href="entries/AnnouncingLANMP3.html">earlier post</a>, to my <a href="http://www.joecheng.com/code/">code page</a>.
]]>
</description>
</item>



<item>
<title>New features in C#</title>
<link>http://www.joecheng.com/blog/entries/NewfeaturesinC.html</link>
<description>
<![CDATA[
<P>I went with most of the rest of my company last night to&nbsp;see <A href="http://www.gotdotnet.com/team/dbox/">Don Box</A>&nbsp;give a talk. The conference he was speaking at was called "XML Web Services One", and none of us are particularly interested in web services, so we were relieved that the subject of his presentation turned out to be a preview of Whidbey, the next major version of .NET (current ETA is second half of 2004).</P>
<P>The big news to me was anonymous methods. I had expected them to be much like Java's implementation of anonymous classes, only at the method level; in other words, I could write something like this:</P><PRE>public void SomeMethod() {
  string label = "Foo";
  this.Changed += new EventHandler(object sender, EventArgs args) {
    MessageBox.Show(label + " detected change in " + sender.ToString());
  }
}</PRE>
<P>and the compiler would expand it to something like this: <PRE>public void SomeMethod() {
  string label = "Foo";
  __anon_1_label = label;
  this.Changed += new EventHandler(<i>__Anon_1</i>);
}

<i>private string __anon_1_label;
private void __Anon_1(object sender, EventArgs args) {
  MessageBox.Show(__anon_1_label + " detected change in " + sender.ToString());
}</i></PRE>
<P>While convenient, there's nothing really magic about what's going on here. It's just that the compiler would make up an opaque method name for you and move your method body there, while making copies of the caller's local variables for the method body to use. (OK, the code shown above wouldn't actually work because the same instance of <code>__anon_1_label</code> would be shared among potentially many instances of the anonymous method--let's just pretend.)</P>
<P>Well, it turns out that C# anonymous methods are not anything like the above. They're way better. They're real closures.</P>
<P>The difference between a closure and the above is that a closure is directly wired into the scope of the enclosing block of code. In the above example, it means that after <code>SomeMethod()</code> creates and registers the anonymous method, it could change the value of label and the anonymous method would "see" the new value when it gets called. Or, rather than just printing out a message, the anonymous method could change the value of label and the change would be reflected in the enclosing scope (assuming the enclosing scope is still around).</P>
<P>This opens up C# to all sorts of new styles of programming. For example, Ruby's beloved blocks and iterators will be possible:</P><PRE>List&lt;i&gt; list = new List&lt;i&gt;();
list.Add(1);
list.Add(3);
list.Add(5);

int total = 0;
list.each(new Visitor&lt;i&gt;(int i) {
  total += i;
});
Console.WriteLine(total);</PRE>
<P>It's not as pretty as Ruby's syntax, but it's about as powerful and totally typesafe.</P>
<P>More fun with anonymous methods to come...</P>
]]>
</description>
</item>



<item>
<title>Apple Rendezvous (aka Zeroconf)</title>
<link>http://www.joecheng.com/blog/entries/AppleRendezvous.html</link>
<description>
<![CDATA[
<P>Lately I've been very interested in distributed components and services (let's call them "nodes") that "discover" each other on a LAN, and gracefully handle individual nodes going up and down. The <A href="http://www.zeroconf.org">Zeroconf</A> initiative, which Apple&nbsp;dubs <A href="http://developer.apple.com/macosx/rendezvous/">Rendezvous</A>, tackles this problem by combining multicast messages and creative use of DNS (yes, <EM>that</EM> DNS). It seems to work well, as Mac types seem to really like iChat, which uses Rendezvous.</P>
<P>I've got some ideas about how to create a nice high-level C# binding to this. You should be able to take a marshal-by-ref object and just make one API call that says "make this available to everyone on the network."</P>
<P>More on this later.</P>
]]>
</description>
</item>



<item>
<title>Design of the Power Mac G5</title>
<link>http://www.joecheng.com/blog/entries/DesignofthePowerMacG5.html</link>
<description>
<![CDATA[
<P>I try to keep these entries strictly programming-related, but I can't resist commenting on the design of the new <A href="http://www.apple.com/powermac/">Power Mac</A>. This is not a machine I would describe as "beautiful", unlike pretty much any other machine or peripheral Apple has produced in recent memory. I would describe it as impersonal, brutal, utilitarian. Especially after seeing <A href="http://www.applematters.com/g5html/g5index.html">pictures</A> that aren't as varnished as the ones at apple.com.</P>
<P>Just admit it. It's not pretty. And yet, I still want one, and you probably do too.</P>
<P>The Power Mac G5 is to Apple what the&nbsp;<A href="http://www.ferrari.com/cgi-bin/fworld.dll/ferrariworld/scripts/gt/production/cars_oggi_intro.jsp?title=ENZO&amp;carIdx=0">Enzo</A>&nbsp;is to Ferrari. The Ferrari Enzo is a distinctly <EM>un-</EM>beautiful car; all angles and protrusions, with none of the sensuality that Ferrari--and Italian design in general--is known for. Yet this is the most expensive Ferrari in history, and the one model to bear the first name of their founder.</P>
<P>Unsurprisingly, the Enzo's appearance was roundly criticized by the automotive press... but they all sang a different tune once they finally got a chance to get behind the wheel. The Enzo was designed to be a no-compromises performance machine, and that meant the design was dictated purely by the wind tunnel, not the eye of the beholder. Once you come to grips with that fact, it's not hard to see a different kind of beauty in this street-legal race car: an intense sense of purpose, an unswerving dedication to function over form. You don't need to look at the spec sheet to know this is the most powerful roadgoing Ferrari ever--it's spelled out for you in carbon fiber.</P>
<P>And so it is with the Power Mac G5. This is the first Apple in years that can run with the best Intel desktops. While I personally don't believe the benchmark numbers&nbsp;Apple has posted on their site, there is no doubt that it is a <EM>seriously</EM> fast computer. And thus, the big-metal-cage-o'-fans look works. "The better to cool my monstrous Power4-derived processors with, my dear."</P>
<P>The Enzo was designed by the wind tunnel;&nbsp;the Power Mac G5&nbsp;<EM>is</EM>&nbsp;a wind tunnel.&nbsp;Impersonal, brutal, utilitarian... and very, very desirable.</P>
<P>Of course, I could be way off base. Maybe Ive&nbsp;and his band of elite designers&nbsp;actually think the G5 case is beautiful in the same way previous Macs have been. But I kind of doubt it.</P>
<P>(By the way, today's hot Mac comes with dual processors derived from the Power4, requires nine cooling fans, runs a UNIX-like operating system, and comes in a box that looks like a 4U <A href="http://www-132.ibm.com/content/home/store_IBMPublicUSA/en_US/eServer/pSeries/entry/6306C4_70286C4121C.html">rackmount</A>. Do they still show that friendly, smiling Mac icon when the OS boots up?&nbsp;If so, they should change it to a pitbull.)</P>
]]>
</description>
</item>



<item>
<title>LANMP3 requires .NET Framework 1.1</title>
<link>http://www.joecheng.com/blog/entries/LANMP3requires.NETFramewo.html</link>
<description>
<![CDATA[
Forgot to mention in my last entry that LANMP3 requires the .NET Framework 1.1 Redistributable (or SDK), which is available from Windows Update or you can download it directly from <A href="http://microsoft.com/downloads/details.aspx?FamilyId=262D25E3-F589-4842-8157-034D1E7CF3A3&amp;displaylang=en">this page</A>. It weighs in at an unfortunate 24MB, sorry.
]]>
</description>
</item>



<item>
<title>Announcing: LANMP3</title>
<link>http://www.joecheng.com/blog/entries/AnnouncingLANMP3.html</link>
<description>
<![CDATA[
<P>So, I never got around to building my <A href="entries/AJinidistributedMP3jukebo.html">Jini Jukebox</A>, nor does it look like I'll ever find the time to. Instead I threw together LANMP3 [<A href="http://www.joecheng.com/code/LANMP3.zip">download</A>], which is basically a rewrite of RemoteMP3, my original attempt at a client/server MP3 player.</P>
<P><SPAN><IMG style="MARGIN-BOTTOM: 15px; MARGIN-LEFT: 15px" height=558 alt="LANMP3 Client screenshot" hspace=4 src="entries/images/LANMP3-screenshot.png" width=406 align=right border=0 cd:pos="1"></SPAN></P>
<P>LANMP3 and RemoteMP3 let you have a music server running on your LAN that contains MP3 files and is hooked up to speakers, which you can control using any Windows machine on your LAN. This is not as nice as my Jini Jukebox pipedream, which would let you (sitting at any PC) direct any PC to play music streamed from any PC, but it does solve the problem of controlling my main workstation's tunes from my wirelessly-connected laptop.</P>
<P>The RemoteMP3 server was a headless Java program that wrapped the <A href="http://java.sun.com/products/java-media/jmf/">Java Media Framework</A>, while the&nbsp;RemoteMP3 client was a Windows desktop application written in C#. They communicated using a custom protocol over TCP/IP sockets. For LANMP3, I decided instead to write the server as a C# program that wraps the Windows Media Player ActiveX control. The client is still a desktop C# program, but now it communicates with .NET Remoting (similar to Java RMI).</P>
<P>There were several problems with RemoteMP3. The worst was that JMF had problems playing many real-world MP3s that sound fine in Winamp or Windows Media Player; it would stutter or completely stop on probably one in fifteen MP3s in my collection. Furthermore, after it finished the last song in a playlist, it would make low-level pulsing static noises, as if it were looping the last second of the last song (just my guess). LANMP3 exhibits none of these issues, and as a bonus, WMP exposes a much simpler API than JMF (no surprise there).</P>
<P>Secondly, there was an unacceptable amount of latency between the RemoteMP3 client and server, despite the fact that I was using my own, very compact, direct TCP/IP socket protocol. The lag was on the order of *seconds*, even on a LAN, so there was definitely something screwy going on in my code. I never was able to track it down. On the other hand, .NET Remoting performs more than fast enough, and was much, much easier to work with. You have to know the particular incantations to publish an object as a remoting target, but once you do, it Just Works (well, usually).</P>
<P>LANMP3 binaries can be downloaded from my <A href="http://www.joecheng.com/code/">code</A> page. Feedback is welcome.</P>
]]>
</description>
</item>



<item>
<title>Polyphonic C#</title>
<link>http://www.joecheng.com/blog/entries/PolyphonicC.html</link>
<description>
<![CDATA[
<P>Stumbled onto a pretty cool project at Microsoft Research: <A href="http://research.microsoft.com/~nick/polyphony/">Polyphonic C#</A>. Seems to be a set of extensions to the language that make it easier to write concurrent applications, and coordinate actions between various asynchronous threads.&nbsp;The <A href="http://research.microsoft.com/~nick/polyphony/intro.htm">introduction</A> gives some illustrative examples.</P>
<P>You can see a list of other MS Research projects <A href="http://research.microsoft.com/research/topics/">here</A>... they are definitely not sitting still.</P>
<P>(Just for symmetry... <A href="http://research.sun.com/projects.html">here's</A> a list of Sun Labs projects.)</P>
]]>
</description>
</item>



<item>
<title>Knoppix: Linux on a CD</title>
<link>http://www.joecheng.com/blog/entries/KnoppixLinuxonaCD.html</link>
<description>
<![CDATA[
<P>One of the coolest things I've seen lately is <A href="http://www.knoppix.net">Knoppix</A>. Just download an ISO from their website and burn it to a CD, then reboot--voila, instant Linux desktop, no installation required.&nbsp;The entire Linux installation is already on the CD itself in runnable form, including drivers, window managers, applications, and anything else you're likely to need. It even comes with Java, Python, and even Ruby... all out of the box!</P>
<P>If I understand correctly, Knoppix won't touch your hard drive unless you do something to them. In fact, you don't even need a hard drive at all. For those who just want to try out Linux without having to repartition their hard drive or otherwise fudge with their perfectly tweaked Windows machine, this is about the least-invasive experience there is.</P>
<P>This isn't news for the Linux set... "live CD distros" have been along for quite some time, apparently, and range from tiny text-only installs intented for disaster recovery to full-on desktop setups like Knoppix. In fact, even I had heard of them quite some time ago, but never got around to actually trying one until now. It's the kind of thing that doesn't sound too exciting until you actually see it happen... like pausing live TV with TiVo.</P>
<P>Anyway, I highly recommend Knoppix. You've got nothing to lose but a blank CD...</P>
]]>
</description>
</item>



<item>
<title>Ruby is sweet</title>
<link>http://www.joecheng.com/blog/entries/Rubyissweet.html</link>
<description>
<![CDATA[
<P>Before I start the Ruby lovefest... regarding my <A href="entries/GettingstartedwithRuby.html">previous post</A> on Ruby, I found this tidbit from a <A href="http://www.rubygarden.org/iowa/faqtotum/abq1ws7SMkUIk/b/1.11.7.2.5">FAQ entry</A>:</P>
<BLOCKQUOTE><EM>Ruby's syntax and design philosophy are heavily influenced by Perl. It has a lot of syntactic variability. Statement modifiers (if, unless, while, until, etc.) may appear at the end of any statement. Some key words are optional (the ``then'' in an ``if'' statement for example). Parentheses may sometimes be elided in method calls. The receiver of a method may usually be elided. Many, many things are lifted directly from Perl. Built in regular expressions, $_ and friends, here documents, the single-quoted / double-quoted string distinction, $ and @ prefixes to distinguish different kinds of names and so forth.</EM></BLOCKQUOTE>
<P>I knew it! Is it just me, or is Perl at the root of all things evil!? ;)</P>
<P>All kidding aside, I have been very impressed with Ruby overall.&nbsp;It is as convenient&nbsp;as Python, but feels more coherent--some of Python's features kind of seem arbitrary or grafted on, as opposed to Ruby where a smaller number of constructs are more widely applicable.</P>
<P>I like that Ruby uses&nbsp;backquotes to `execute any arbitrary system command`--that's one Perlism that I wish all scripting languages followed.</P>
<P>I like blocks and iterators a lot.</P>
<P>I like how almost every statement&nbsp;returns <A href="http://www.rubycentral.com/book/tut_expressions.html">some kind of expression</A>.</P>
<P>I simply love <A href="http://www.rubycentral.com/book/web.html#S2">eRuby</A>. I don't know how many web scripting languages I've used over the years, but as far as basic syntax goes, I think Ruby-impregnated HTML&nbsp;is by far the best solution for me. I have been longing for a language that is fairly powerful (i.e. not a stripped down "templating language" like Velocity or CityScript), doesn't result in ugly code (see looping over collections in JSP), lets you do includes, and can (optionally) be used at "compile time" to create a static set of HTML documents.</P>
<P>(Note: This is a pretty unfashionable set of requirements these days--the trend is to move the power out of the hands of web developers and into dedicated logic classes, thus enabling better role separation.&nbsp;By taking the real code out of the HTML, you can hire (cheap) HTML developers who don't know how to write real code. Well, I know how to code, and I often need to create websites. Sometimes it's just really nice to be able to declare variables, perform arithmetic operations, and create and manipulate lists from right within the presentation tier. So keep your flaccid, stripped-down templating language outta my face.)</P>
<P>eRuby does all that and more. It is so wonderful that I'm having trouble expressing it in words.</P>
<P>It's just the little things. Say you have an array of images and want to filter out the ones that don't actually exist on disk. You can do this in one line:</P><PRE>&lt;% imageList.delete_if { |img| File.exist?(img) } %&gt;</PRE>
<P>Contrast that to, say, JSP:</P><PRE>&lt;%
ArrayList tmpList = new ArrayList(imageList.size());
for (Iterator it = imageList.iterator(); it.hasNext();) {
  String thisFile = (String)it.next();
  if (new File(thisFile).exists())
    tmpList.add(thisFile);
}
imageList = tmpList;
%&gt;</PRE>
<P>or ColdFusion:</P><PRE>&lt;cfset tmpArray = ArrayNew()&gt;
&lt;cfloop index="image" list="imageArray"&gt;
  &lt;cfif FileExists(image)&gt;&lt;cfset ArrayAppend(tmpArray, image)&gt;&lt;/cfif&gt;
&lt;/cfloop&gt;
&lt;cfset imageArray = tmpArray&gt;</PRE>
<P>This is just one example of Ruby taking one line where most other languages need five. Furthermore, it seems like the general problem of taking data and turning it into richly formatted HTML involves many of the kinds of operations that Ruby makes very easy:</P>
<UL>
<LI>
<DIV>Manipulating and iterating over data structures</DIV>
<LI>
<DIV>String processing</DIV>
<LI>
<DIV>Dealing with potential nulls</DIV></LI></UL>
<P>It's almost as if Ruby was specifically designed to be embedded into HTML! (It wasn't.)</P>
<P>Also, I have found that static typing--while a huge boon in general--is less useful in this area, specifically because there is generally so much iterating and&nbsp;unmarshalling (from request parameters) going on, so you end up doing a lot of casting and parsing which defeats much of the static type checking anyway.</P>
<P>So anyway, there you have it.&nbsp;If nothing else, use Ruby for generating web pages. (I'm using&nbsp;<A href="http://www2a.biglobe.ne.jp/~seki/ruby/erb-2.0.3.tar.gz">erb</A> as my eRuby implementation because it is small and portable.)</P>
]]>
</description>
</item>



<item>
<title>An IDE for Jini!?</title>
<link>http://www.joecheng.com/blog/entries/AnIDEforJini.html</link>
<description>
<![CDATA[
Too bad I'm leaving for NYC for the weekend in about&nbsp;five minutes... I'd love to check <A href="http://www.incax.com">this</A> out.
]]>
</description>
</item>



<item>
<title>Getting started with Ruby</title>
<link>http://www.joecheng.com/blog/entries/GettingstartedwithRuby.html</link>
<description>
<![CDATA[
<P>Having enjoyed my experience with Python so far, I decided to jump in the Ruby waters as well. I picked up <EM>Programming Ruby </EM>by Thomas and Hunt (of <EM>The Pragmatic Programmer</EM> fame); the full text is freely available <A href="http://www.rubycentral.com/book/">online</A>.</P>
<P>Ruby seems fine... not a whole lot different than Python at first blush. I like that it has stronger OOP support; an object's instance variables can't be accessed from outside of the object (you must use accessors), and there are formal notions of public/protected/private methods. Contrast this to Python's laissez-faire approach (i.e., no access control whatsoever). And of course, Ruby's iterators are very nice.</P>
<P>On the other hand, there seem to be an uncomfortable number of things you have to "just know". For example, there are quite a few predefined, global variables; one controls the default separator pattern for the <CODE>String.split</CODE> method, another holds the last line read by <CODE>Kernel.readline</CODE> and is used as the default for most print operations, another is the exception in a catch clause. Here's an example:</P><PRE>$, = ','
$; = '\t'
while gets
  data = $_.split
  puts data.join
end</PRE>
<P>This block of code converts tab delimiters to commas, but you'd have to know what <CODE>$,</CODE> and <CODE>$;</CODE> mean to make that connection.</P>
<P>In all fairness to Ruby, there always seems to be a "clean" way to do it as well:</P><PRE>while input = gets
  data = input.split('\t')
  puts data.join(',')
end</PRE>
<P>I'm also a little sketched out by how little you can rely on parentheses if you want to; to these Java-tainted eyes, it can make for some difficult-to-read code. When you see a comma-separated list of identifiers, you have to pay attention to figure out what's going on. <PRE>min 0, 100                   &nbsp; // method call
min 0, max x, y&nbsp;               // nested method call
a, b = x, max x, y             // parallel assignment w/ method call
rescue SyntaxError, NameError  // catch clauses</PRE>
<P>Contrast this with Java, which forces you to use parentheses to on method calls and such. They cost you a&nbsp;few more keystrokes, but the resulting code is utterly unambiguous and easy to read.&nbsp;(Note that Ruby doesn't force you to leave off the parentheses; in fact, the book says you should use parens in all but the most trivial cases.)
<P>It just seems like there's a little bit of Perl's "There's More Than One Way To Do It" thinking going on here, moreso than Python. Personally, that's not a mantra that appeals to me when it comes to syntactic details like where parens go, whether to use <CODE>&amp;&amp;</CODE> or <CODE>and</CODE>, whether to make blocks with&nbsp;<CODE>do..end</CODE>&nbsp;or curly braces. I'd rather have an simple, consistent, easy-to-read language and sacrifice those freedoms.</P>
]]>
</description>
</item>



<item>
<title>A Jini distributed MP3 jukebox</title>
<link>http://www.joecheng.com/blog/entries/AJinidistributedMP3jukebo.html</link>
<description>
<![CDATA[
<P>You know what would be super fun?&nbsp;A distributed MP3 jukebox, using Jini.</P>
<P>Any machine on a LAN could run the Library service and/or Player service. Library would simply offer up a catalog of available MP3 files on that machine, and allow those files to be streamed to clients. Player would allow files to be queued up and played. All of this would be controlled by Swing clients from anywhere on the network.</P>
<P>With such a system, you could use your 802.11b-enabled laptop to have your HTPC start playing a song that is stored on a server in your basement. You'd never have to sync up your MP3 collection across multiple machines.&nbsp;And since it would all be based on Jini, it could all be very robust and decentralized, with no "central server" necessary. New Players and Libraries connecting to the network would be autodiscovered by everyone else.</P>
<P>All of this would be extremely easy to do; the <A href="http://java.sun.com/products/java-media/jmf/">Java Media Framework</A>&nbsp;makes it easy enough to play MP3s, and the rest is just pushing bytes and messages around.</P>
<P>I'd love to build this, if only to give Jini a try. Oh well, tack it on to the end of the "To Do (Maybe)" list...</P>
]]>
</description>
</item>



<item>
<title>Writing an inter-process Read/Write locking mechanism</title>
<link>http://www.joecheng.com/blog/entries/Writinganinter-processRea.html</link>
<description>
<![CDATA[
<P>Encountered a particularly interesting little problem today. The requirement was to create a read/write locking mechanism for .NET that behaved according to these rules:</P>
<UL>
<LI>Any number of read operations can happen simultaneously. 
<LI>Only one write can happen at a time. 
<LI>Read and write operations cannot happen simultaneously. (i.e., a write cannot begin until any outstanding read operations complete.) 
<LI>Write operations should not starve forever during read-heavy periods.</LI></UL>
<P>That's a description of a fairly standard read/write lock. What makes it more interesting is that this read/write lock must protect a resource <EM>across multiple processes</EM>. Thus, most of the standard .NET threading constructs are useless, since they're only good for coordinating multiple threads inside a single process.</P>
<P>Win32 does provide a few cross-process synchronization primitives, however: <A href="http://msdn.microsoft.com/library/en-us/dllproc/base/mutex_objects.asp">Mutexes</A>, <A href="http://msdn.microsoft.com/library/en-us/dllproc/base/semaphore_objects.asp">Semaphores</A>, and <A href="http://msdn.microsoft.com/library/en-us/dllproc/base/event_objects.asp">Events</A>. These can be used from .NET with relative ease (i.e.,&nbsp;relative to many other interop chores). Between my friend John and me, we figured out that you can actually satisfy the requirements using only Mutex and Semaphore.&nbsp;It sure isn't pretty, though.</P>
<P>Start by picking a number that is slightly higher than the number of concurrent reads you can reasonably expect. In this case, let's say it's 100. We will create a global semaphore with this number of permits. We'll also create a global mutex. (The code that follows should be considered pseudocode: for illustration purposes only.)</P><PRE>// create or open global mutex
GlobalMutex mutex = new GlobalMutex("IdOfProtectedResource.Mutex");
// create or open global semaphore
GlobalSemaphore&nbsp;semaphore = new GlobalSemaphore("IdOfProtectedResource.Semaphore", 100);

public void AcquireReadLock()
{
  mutex.Acquire();
  semaphore.Acquire();
  mutex.Release();
}

public void ReleaseReadLock()
{
  semaphore.Release();
}

public void AcquireWriteLock()
{
  mutex.Acquire();
  for (int i = 0; i &lt; 100; i++)
    semaphore.Acquire();
  mutex.Release();
}

public void ReleaseWriteLock()
{
  for (int i = 0; i &lt; 100; i++)
    semaphore.Release();
}</PRE>
<P>Thus,&nbsp;having read lock simply means holding <EM>one</EM> of the semaphore's permits, while having write lock means holding <EM>all</EM> of the semaphore's permits. It's a little bit nasty and inefficient, but it seems like it should work.</P>
<P>Tune in next week, when&nbsp;we'll be creating a FIFO monitor using only bubble gum and a pocket watch. ;)</P>
]]>
</description>
</item>



</channel>
</rss>

