<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Keith's Blog &#187; Ruby</title>
	<atom:link href="http://kfahlgren.com/blog/category/ruby/feed/" rel="self" type="application/rss+xml" />
	<link>http://kfahlgren.com/blog</link>
	<description>Keith on XML, Publishing, Ruby, Birds, &#038; San Francisco</description>
	<lastBuildDate>Fri, 09 Apr 2010 15:59:26 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=abc</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>For heaven&#8217;s sake, please just install hoe!</title>
		<link>http://kfahlgren.com/blog/2008/02/21/for-heavens-sake-please-just-install-hoe/</link>
		<comments>http://kfahlgren.com/blog/2008/02/21/for-heavens-sake-please-just-install-hoe/#comments</comments>
		<pubDate>Thu, 21 Feb 2008 22:46:40 +0000</pubDate>
		<dc:creator>Keith</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Whining]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://kfahlgren.com/blog/2008/02/21/for-heavens-sake-please-just-install-hoe/</guid>
		<description><![CDATA[This is why people get annoyed about the silly gem install dependency mess (esp WRT hoe):
$ gem install heckle
Need to update 31 gems from http://gems.rubyforge.org   # fair enough, you're allowed
...............................
complete
Install required dependency ruby2ruby? [Yn]  Y    # Yeah, I know you need these
Install required dependency ParseTree? [Yn]  Y  [...]]]></description>
			<content:encoded><![CDATA[<p>This is why people get annoyed about the silly <code>gem install</code> dependency mess (esp WRT hoe):</p>
<pre>$ gem install heckle
Need to update 31 gems from http://gems.rubyforge.org   # fair enough, you're allowed
...............................
complete
Install required dependency ruby2ruby? [Yn]  Y    # Yeah, I know you need these
Install required dependency ParseTree? [Yn]  Y     # oh, and this one too
Select which gem to install for your platform (i686-linux)
 1. ParseTree 2.1.1 (ruby)                                   # yeah, just a vanilla ruby here
 2. ParseTree 2.1.1 (i386-mswin32)
 3. ParseTree 2.1.0 (ruby)
 4. ParseTree 2.0.2 (ruby)
 5. Skip this gem
 6. Cancel installation
> 1
Install required dependency RubyInline? [Yn]  Y   # This is also needed, I gather
Install required dependency hoe? [Yn]  Y             # Ha, hoe again, OK
Install required dependency rubyforge? [Yn] Y     # Don't care, don't understand why both
Install required dependency rake? [Yn]  Y            # There's no rake on this box, really?
Install required dependency hoe? [Yn]  Y             # WTF, yeah, I just said that
Install required dependency hoe? [Yn]  Y             # .. now you're just fucking with me
Install required dependency ZenTest? [Yn] Y        # Huh? I like ZenTest, but there's no reason...
Install required dependency hoe? [Yn]  Y             # **** YOU, hoe!
Successfully installed heckle-1.4.1
Successfully installed ruby2ruby-1.1.8
....
</pre>
<p>The fourth time it asked me, it decided to trust me and actually starting installing the gems&#8230;.</p>
]]></content:encoded>
			<wfw:commentRss>http://kfahlgren.com/blog/2008/02/21/for-heavens-sake-please-just-install-hoe/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>ISBN10 to ISBN13</title>
		<link>http://kfahlgren.com/blog/2008/01/24/isbn10-to-isbn13/</link>
		<comments>http://kfahlgren.com/blog/2008/01/24/isbn10-to-isbn13/#comments</comments>
		<pubDate>Thu, 24 Jan 2008 22:41:20 +0000</pubDate>
		<dc:creator>Keith</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://kfahlgren.com/blog/2008/01/24/isbn10-to-isbn13/</guid>
		<description><![CDATA[As of the beginning of 2007, ISBN10 is dead. Now we&#8217;re in a world that allows &#8220;979&#8243; prefixes, though the following code doesn&#8217;t expect them yet&#8230;
Here&#8217;s some stuff to turn your 10-digit ISBNs into 13-digit ISBNs, naively assuming &#8220;978&#8243;, following an the API post doing the same from LibraryThing. There&#8217;s another one at isbn.org for [...]]]></description>
			<content:encoded><![CDATA[<p>As of the beginning of 2007, <a href="http://www.bisg.org/isbn-13/">ISBN10 is dead</a>. Now we&#8217;re <a href="http://www.bisg.org/news/press.php?pressid=44">in a world that allows &#8220;979&#8243; prefixes</a>, though the following code doesn&#8217;t expect them yet&#8230;</p>
<p>Here&#8217;s some stuff to turn your 10-digit ISBNs into 13-digit ISBNs, naively assuming &#8220;978&#8243;, following an the <a href="http://www.librarything.com/thingology/2008/01/isbn-check-api.php">API post doing the same</a> from LibraryThing. There&#8217;s <a href="http://www.isbn.org/converterpub.asp">another one at isbn.org</a> for humans.</p>
<p>Code from O&#8217;Reilly&#8217;s internal stuff:</p>
<pre>module Isbn
  def self.dash_isbn(isbn)
    raise ArgumentError.new("ISBN argument must be string") unless isbn.is_a?(String)
    if isbn.length == 10
      return isbn[/^./] + "-" + isbn[1..3] + "-" + isbn[4..8] + "-" + isbn[/.$/]
    elsif isbn.length == 13
      return isbn[0..2] + "-" + isbn[3].chr + "-" + isbn[4..6] + "-" + isbn[7..11] + "-" + isbn[/.$/]
    else
      raise ArgumentError.new("ISBN must be 10 or 13 characters")
    end
  end

  def self.isbn10toisbn13(isbn)
    raise ArgumentError.new("ISBN argument must be string") unless isbn.is_a?(String)
    raise ArgumentError.new("ISBN must be of length 10") unless isbn.length == 10
    prefix = "978"
    isbn12 = prefix + isbn[0...-1]
    return isbn12 + check_digit_13(isbn12).to_s
  end  

  def self.check_digit_13(isbn_12)
    # http://www.barcodeisland.com/ean13.phtml
    # need to subtract remainder from 10
    # and do exemption for zero LMS 08.29.2006
    raise ArgumentError.new("ISBN must be of length 12") unless isbn_12.length == 12
    sum = 0
    odds = 0
    evens = 0
    isbn_12.scan(/\d/).each_with_index {|d, i|
      if (i % 2) == 0
        evens = evens + (d.to_i * 1)
      else
        odds = odds + (d.to_i * 3)
      end
    }
    sum = evens + odds
    digit = sum % 10
    if digit.zero?
      return 0
    else
      return 10 - digit
    end
  end
end # of module Isbns
</pre>
<p>The last test method relies on my database, which you won&#8217;t have. Replace it with something else you trust or drop it.</p>
<pre>#!/usr/bin/env ruby

require 'test/unit'
require 'pdb'
require 'isbn'

class IsbnTest < Test::Unit::TestCase
  def setup
    @isbn10 = "059610123X"
    @isbn13 = "978059610123X"
  end
  def test_dash_isbn
    # must be a String
    assert_raise ArgumentError do Isbn.dash_isbn(123) end
    # must be 10 or 13 characters
    assert_raise ArgumentError do Isbn.dash_isbn("123") end
    assert_equal("0-596-10123-X", Isbn.dash_isbn(@isbn10))
    assert_equal("978-0-596-10123-X", Isbn.dash_isbn(@isbn13))
  end
  def test_isbn10toisbn13
    # must be a String
    assert_raise ArgumentError do Isbn.isbn10toisbn13(123) end
    # must be 10 characters
    assert_raise ArgumentError do Isbn.isbn10toisbn13(@isbn13) end
    assert_equal("9780596101237", Isbn.isbn10toisbn13(@isbn10))
  end
  def test_check_digit_13
    # must be 12 characters
    assert_raise ArgumentError do Isbn.check_digit_13(@isbn13) end
    assert_equal(7, Isbn.check_digit_13(@isbn13[0..-2]))
    assert_equal(0, Isbn.check_digit_13("123456789018"))
  end
  def test_isbn13
    ["0596008627", "1565929470", "0596002734", "0596004001", "0596527357",
     "0596101635", "0596005059", "0596527063", "1565926374", "0596526946",
     "1565926420", "0596101805", "059652742X", "156592455X", "0596006446",
     "0596008473", "0596009607", "0596100582", "0596100493", "0596004427",
     "1565925890", "1565927141", "059651610X"].each {|isbn|
      puts "Testing #{isbn}"
      assert_equal(PDB::ProdDB.new(isbn).isbn13, Isbn.isbn10toisbn13(isbn), "Bad checksum!")
    }
  end
end
</pre>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://kfahlgren.com/blog/2008/01/24/isbn10-to-isbn13/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>RubyConf 2007 Second Day Afternoon</title>
		<link>http://kfahlgren.com/blog/2007/11/03/rubyconf-2007-second-day-afternoon/</link>
		<comments>http://kfahlgren.com/blog/2007/11/03/rubyconf-2007-second-day-afternoon/#comments</comments>
		<pubDate>Sat, 03 Nov 2007 17:48:35 +0000</pubDate>
		<dc:creator>Keith</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Conferences]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[RubyConf2007]]></category>

		<guid isPermaLink="false">http://kfahlgren.com/blog/2007/11/03/rubyconf-2007-second-day-afternoon/</guid>
		<description><![CDATA[Ed Borasky: Profiling and Tuning Ruby 1.8
Slides are available here. Cougar is the name of the project that this is coming from?
Is Ruby 1.8 Slow?
To benchmark: Collect a set of benchmark times, then normalize them, then compute the geometric mean of the ratios.
Alioth is a popular (if controversial) set of benchmarks. Using gcc as the [...]]]></description>
			<content:encoded><![CDATA[<h2>Ed Borasky: Profiling and Tuning Ruby 1.8</h2>
<p><a href="http://rubyforge.org/docman/view.php/977/2705/Slides.pdf">Slides are available here</a>. <a href="http://rubyforge.org/projects/cougar/">Cougar</a> is the name of the project that this is coming from?</p>
<h3>Is Ruby 1.8 Slow?</h3>
<p>To benchmark: Collect a set of benchmark times, then normalize them, then compute the geometric mean of the ratios.</p>
<p>Alioth is a popular (if controversial) set of benchmarks. Using gcc as the standard, java is about 3 times slower, python is about 10, and ruby is 19 times slower (slower than Python, PHP, and Perl).</p>
<p>&#8220;Hemibel thinking&#8221;: (half of a an &#8220;order of magintude&#8221; [factor of 10]). Look for hemibel improvements or difference. Any greater accuracy doesn&#8217;t really help. Hemibel ratios: <tt>log10(a/b) * 2</tt>. Now we can rethink java as ~1, perl ~2, ruby ~2.6 (in terms of hemibels). Now we can say &#8220;Ruby is sort of slow.&#8221; </p>
<p>But, of course, reducing the speed of a language to a single number is not really that interesting. Instead, we need to find the variation of the benchmarks. Cue Box and Whisker Plots (they show a lot of data in a small amount of space). Now yarv looks really nice, with a really limited variation over the different benchmarks (python and ruby do well, perl and php do poorly). Each language has big (upper) outliers. Ruby&#8217;s worst outlier is &#8220;spectralnorm 500&#8243; (also bad for yarv).</p>
<h3>OK, it&#8217;s slow, now what?</h3>
<p>What to do: Throw hardware? Wait for 1.9/JRuby/IronRuby/Rubinius? Tune for 1.8?</p>
<p>Let&#8217;s try tuning 1.8 against the 1.9 slightly-modified benchmark suite. First, check how much you can get of GCC optimizations. There&#8217;s something to be gained there, but it&#8217;s a pretty small difference (1/2 a hemibel). Next, check out gprof. gprof shows the top three most expensive methods were <tt>rb_eval</tt>, <tt>rb_call0</tt>, and <tt>rb_call</tt> (for slightly more than 50% of the time).  Another GNU tools is gcov (for coverage and profiling), and it can provide some<br />
more help.</p>
<h2>Phil Hagelberg: Tightening the Feedback Loop</h2>
<p>How can you become a more effective programer? First, read <a href="http://www.pragprog.com/the-pragmatic-programmer"><em>The Pragmatic Programmer</em><em></em></a>.  Next, test more (duh) and use Test::Unit or RSpec (don&#8217;t waste your time doing stuff by hand that the computer could automate). <tt>autotest</tt> takes away a bit of the discipline of good testing (it tells &#8220;you what you need to know when you need to know it whether or not you know you need it or not&#8221;). But, <tt>autotest</tt> still requires a lot of context switching between terminals. Consider a solution like Flymake for emacs (which does in-editor syntax checking).</p>
<p>Testing habits help show the general feedback principles nicely. Many other parts of software development could benefit from the same introspection, measuring, and automation. What else should we measure?</p>
<ul>
<li>Accuracy (syntax highlighting and automatic indentation): modern editors</li>
<li>Meta-Accuracy (especially when starting out, are your accuracy checks actually accurate?): heckle, rcov</li>
<li>Maintainability (hard to measure, but you can measure complexity): flog </li>
<li>Performance: write your own automated test for what you care about</li>
</ul>
<p>Now that you&#8217;re watching various things, track it over time so that you can chart personal/project progress.</p>
<h2>Eric Hodel: Maximizing Productivity</h2>
<p>(How to find time to contribute to more projects)</p>
<p>Fun is the key to high productivity. It&#8217;s easier, of course, to have more fun on stuff you want to work on rather that something that someone else wants you to work on. YAGNI &amp; KISS help break projects into tiny, implementable pieces. Doing a lot of tests up front will help other people write patches for you and will help you not make mistakes when adding new features. [More pimping for heckle and autotest.]</p>
<p>Document late in the game to capture the latest changes. README should provide a quick-start, links to the rest of the documentation, a bug tracker link, and a synopsis and feature overview.</p>
<p>Finally, before releasing anything have a partner review your code.</p>
]]></content:encoded>
			<wfw:commentRss>http://kfahlgren.com/blog/2007/11/03/rubyconf-2007-second-day-afternoon/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>RubyConf 2007 Second Day Morning</title>
		<link>http://kfahlgren.com/blog/2007/11/03/rubyconf-2007-second-day-morning/</link>
		<comments>http://kfahlgren.com/blog/2007/11/03/rubyconf-2007-second-day-morning/#comments</comments>
		<pubDate>Sat, 03 Nov 2007 14:15:38 +0000</pubDate>
		<dc:creator>Keith</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Conferences]]></category>
		<category><![CDATA[JRuby]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[RubyConf2007]]></category>

		<guid isPermaLink="false">http://kfahlgren.com/blog/2007/11/03/rubyconf-2007-second-day-morning/</guid>
		<description><![CDATA[John Lam: State of IronRuby

Photo by dwortlehock
Who works on IronRuby? The core is: John Lam, Hiabo Luo, Tomas Matousek, &#38; John Messerly.
Why did John move from Toronto to Seattle to start working at Microsoft on IronRuby? He was in love with RubyCLR and couldn&#8217;t turn down the opportunity to work on a &#8220;real&#8221; implementation. 
Goals
John [...]]]></description>
			<content:encoded><![CDATA[<h2>John Lam: State of IronRuby</h2>
<p><a href="http://flickr.com/photos/dwortlehock/1844335039/" title="John Lam"><img src="http://farm3.static.flickr.com/2355/1844335039_efcee0eb01.jpg" width="500" height="375" alt="John Lam" /></a></p>
<p><a href="http://flickr.com/photos/dwortlehock/1844335039/">Photo</a> by <a href="http://flickr.com/photos/dwortlehock/">dwortlehock</a></p>
<p>Who works on <a href="http://rubyforge.org/projects/ironruby">IronRuby</a>? The core is: John Lam, Hiabo Luo, Tomas Matousek, &amp; John Messerly.</p>
<p>Why did John move from Toronto to Seattle to start working at Microsoft on IronRuby? He was in love with RubyCLR and couldn&#8217;t turn down the opportunity to work on a &#8220;real&#8221; implementation. </p>
<h3>Goals</h3>
<p>John wants IronRuby to be a Ruby implementation but also in changing Microsoft&#8217;s approach to opensource (&#8220;change or die&#8221;), especially the &#8220;either or approach&#8221; to thinking about opensource. However, it&#8217;s really hard to change a company that&#8217;s doing pretty well. Unfortunately, after doing some announcements  about their intentions, the blogosphere decided that IronRuby was going to be the work of the devil. Today, the project is hosted on Rubyforge, has open SVN access, and has already recieved code contributions from the outside community.</p>
<p>Support Rails: it&#8217;s &#8220;the testbed&#8221; (dispite speculation about MS not wanting to threaten ASP.NET): it&#8217;s &#8220;the testbed&#8221; (dispite speculation about MS not wanting to threaten ASP.NET).</p>
<p>Run everywhere .NET runs (Mac, Linux, Windows). [John is doing the presentation on a Mac and does an irb demo (compiled on Windows) with CLR types (with nice integration between the CLR type's methods and pure Ruby methods) on Mono.]</p>
<h3>Under the covers</h3>
<p>[John runs the Rubinius spec suite in IronRuby in Windows on his Mac. A lot of things fail (373 failures of 1030 examples). He thanks the Rubinius team for all of their work.]</p>
<p>[John goes on to show a bunch of quick IronRuby demos, including some in-browser demos using IronRuby in Silverlight.] There&#8217;ll be more examples <a href="http://www.iunknown.com/">up later on his blog</a>.</p>
<p>If you&#8217;re interested in making a language on .NET, please come to the <a href="http://langnetsymposium.com">Lang.NET Symposium</a>.</p>
<h3>Q &amp; A</h3>
<p>Release schedule? Unlike &#8220;real projects&#8221;, IronRuby has a  &#8220;conference-driven schedule&#8221;. They want to do a push to have Rails working in IronRuby by RailsConf 2008.</p>
<p>Can the IronRuby developers look at other implementations source code? They have to be &#8220;extremely clean&#8221; on the DLR, but &#8220;IronRuby does not have the same restrictions&#8221; because it doesn&#8217;t ship with the OS. Explicitly, they can look at<br />
test and test frameworks.</p>
<h2>Charlie Nutter &amp; Tom Enebo: JRuby: Ruby for the JVM</h2>
<p><img src="http://kfahlgren.com/photos/logos/jruby_logo.jpg" alt="JRuby logo" /></p>
<p>Everyone in the audience knows already what JRuby is, so they won&#8217;t cover that. It&#8217;s a Java implementation of the Ruby 1.8 language. It&#8217;s available under a few opensource licenses, blah blah blah. They released 1.0.2 a couple of days ago (and 1.1b1 last night during Matz&#8217; questions).</p>
<p>[Tim Bray fairly incomprehensively announces that Sun has done a deal with University of Tokyo to work with ? on ? and give them "a bunch of money". Another implementation?]</p>
<h3>Design</h3>
<p>Installation: 1. unpack binary, 2. set PATH. Dependencies: Java 5+. <tt>jruby.jar</tt> contains the full runtime.</p>
<p>The lexer is a hand-written lexer ported from MRI. The parser is another port of the MIR YACC/Bison parser using Jay (Bison for Java). This parser is the key for the recent boom in IDE support for Ruby (like NetBeans). [Charlie does a little NetBeans demo showing auto-complete and variable renaming/highlighting, unused variable detection.] </p>
<p>The Core classes are all written in Java, and nearly all have a 1:1 correspondence (String is RubyString, Array is RubyArray, etc). </p>
<p>The interpreter is a &#8220;simple switch-based AST walker&#8221; that recurses for nesting. Code starts out interpreted but command-line scripts compile immediately. &#8220;<tt>eval</tt>&#8216;d code always interpreted (for now).&#8221;</p>
<p>JRuby 1.1 brings full bytecode compilation. &#8220;The compiler is basically done. This is the only complete 1.8 compiler in existence.&#8221; [Charlie shows a fib benchmark. Compiling makes it quite a bit bigger in filesize (oh well).  There's a _huge_ improvement when using <tt>java -server</tt>.] Typically, you want to use AOT mode, which avoids &#8220;JIT warmup time&#8221;. It may also use less memory in the future and perhaps start faster?</p>
<p><a href="http://flickr.com/photos/dwortlehock/1844321217/" title="Charlie and Tom"><img src="http://farm3.static.flickr.com/2178/1844321217_923a7dea5c.jpg" width="500" height="375" alt="Charlie and Tom" /></a></p>
<p><a href="http://flickr.com/photos/dwortlehock/1844321217/">Photo</a> by <a href="http://flickr.com/photos/dwortlehock/">dwortlehock</a></p>
<h3>Performance</h3>
<p>Performance optimizations are progressing in a few directions. The first is the obvious one of compilation. For real numbers, see <a href="ihttp://blog.nicksieger.com/articles/2007/10/25/jruby-on-rails-fast-enough">this blog post</a>. Comilation caches literals, uses Java local variables when possible, and uses &#8220;monomorphic inline method cache.&#8221; ObjectSpace is a more controversial<br />
optimization. ObjectSpace allows you to iterate all Objects in a system. This isn&#8217;t painful in MRI, but is not easy in the fully-concurrent JRuby (&#8220;it sucks 2-5 times as much&#8221;). Charlie asserts that it is almost never used (except for Test::Unit). It&#8217;s turned off by default but can be turned on with a flag. [Some in the audience believe that the reverse should be used.] Custom implementations of Array, String, and Hash (among others) have also boosted performance.</p>
<h3>Threading, Extensions, &amp; Integration</h3>
<p>JRuby supports only native OS threads but they&#8217;re parallel. They also emulate unsafe green thread operations (<tt>Thread#kill</tt>, <tt>Thread#raise</tt>, etc).</p>
<p>Ruby native (C) extensions are not supported. Some libraries may be accessible with JNA. If you&#8217;re looking for a good binding, consider a Java equivalent to the C. Most of the extensions that Rails uses have already been ported to Java/JRuby.</p>
<p>You can call Ruby from Java, you can call Java from Ruby. A popular integration is building GUIs with Swing. Ruby makes &#8220;Swing development fun&#8221; and is much less verbose than the Java version. Swing integration also brings a cross-platform GUI solution to Ruby (essentially for the first time). A direct approch:</p>
<pre>$ jirb
&gt;&gt; include Java
=&gt; Object
&gt;&gt; import javax.swing.JFrame
...
&gt;&gt; frame = JFrame.new("Hello")
&gt;&gt; frame.show
=&gt; nil
# there is a frame on the screen
&gt;&gt; frame.set_size(500, 500)
=&gt; nil </pre>
<p><a href="http://ihate.rubyforge.org/profligacy/">Profligacy</a> adds a new layout language to simply GUI creation. <a href="http://rubyforge.org/projects/monkeybars/">MonkeyBars</a> takes a GUI editor approach.</p>
<h3>Rails</h3>
<p>Rails works with JRuby. It uses a JDBC connector to ActiveRecord. It can generate .war files (via Warble). ActiveHibernate is coming soon, and provides a different persistence API. Ruvlets brings Servlets to JRuby.</p>
<h3>TDD/BDD</h3>
<p>You can use Test::Unit or RSpec for Java code. &#8220;It&#8217;s so much less code&#8221; that writing tests for Java directly.</p>
<h3>Takeaway</h3>
<ul>
<li>JRuby is ready</li>
<li>JRuby is more than just an implementation</li>
<li>JRuby needs your help</li>
</ul>
<h2>Evan Phoenix: Rubinius 1.0</h2>
<p><img alt="Rubinius logo"<br />
src="http://kfahlgren.com/photos/logos/3d_rubinius_logo.png" /></p>
<p><a href="http://rubini.us/">Rubinius</a>, like IronRuby and JRuby is aiming to bring &#8220;total world domination &#8230;. for Ruby!&#8221; It&#8217;s a Smalltalk-inspired VM: </p>
<pre>class Rubinius &lt; Smalltalk
  # form
  include Ruby::Syntax
  # function
  include Ruby::Behavior
  include Google.search("crazy cs papers")
end </pre>
<p>Rubinius debuted last year at RubyConf 2006. There&#8217;s been &#8220;enormous progress&#8221; over the course of the year.</p>
<p>A comparison of implementation langagues (slightly misleading):</p>
<pre>1.8
84,516 lines of C
     0 lines of Ruby

1.9
128,786 lines of C
     50 lines of Ruby

IronRuby
 48,282 of C#
      0 lines of Ruby

JRuby
113,508 lines of Java
  1,000 lines of Ruby

Rubinius
 25,398 lines of C
 13,946 lines of Ruby </pre>
<p><a href="http://flickr.com/photos/dwortlehock/1844312099/" title="Evan Phoenix"><img src="http://farm3.static.flickr.com/2242/1844312099_290461f2f2.jpg" width="500" height="375" alt="Evan Phoenix" /></a></p>
<p><a href="http://flickr.com/photos/dwortlehock/1844312099/">Photo</a> by <a href="http://flickr.com/photos/dwortlehock/">dwortlehock</a></p>
<p>Some &#8220;Junior High Analysis&#8221;:</p>
<ul>
<li>1.8 &amp; 1.9 is Ruby (core) for C programmers</li>
<li>JRuby is Ruby for Java programmers</li>
<li>IronRuby is Ruby for C# programmers</li>
<li>Rubinius is Ruby for Ruby programmers </li>
</ul>
<p>Rubinius finally allows Ruby programmers to &#8220;eat yummy dogfood.&#8221; It&#8217;s targeted at fellow Ruby programmers, not folks on the street. It also provides a much tighter feedback loop for language improvements &amp; clarification.</p>
<p>Following on that, they have 57 committers, with 17 of those with 20 or more commits (36 with 100+ lines changed). Commit bits have been &#8220;free flowing&#8221; and easy to get (if something you submit as a patch you get commit rights). </p>
<p>Props to EngineYard for funding Evan&#8217;s work on Rubinius.</p>
<p>Evan is shooting to have 1.0 out by ? (was: RubyConf 2007). Things change. Mistakes were made. They&#8217;re evolving.</p>
<h3>Q &amp; A</h3>
<p>Given the fact that Ola Bini and Avi Bryant should Rubinius replace MRI eventually? The chicken didn&#8217;t decide to replace the dinosaur [laughs], the environment did.   The ultimate goal (from the start) wasn&#8217;t &#8220;replace MRI&#8221;, it was &#8220;total world domination&#8221;.</p>
<p>Are you more interested in performance than conformance? Rubinius has some compiler flags to turn certain optimizations on and off.</p>
<p>How small do you think you can get the C? They&#8217;ll follow Squeak in that they&#8217;ll write a C generator. The final goal is hand-maintained lines of C==0.</p>
<p>Are you worried about incompatibilities? We&#8217;re all worried about that. We wouldn&#8217;t checking something that intentionally broke compatibility.</p>
<p>What are these specs you&#8217;re writing? Do you pass them? They&#8217;re as implementation-agnostic as possible and written for MiniRSpec<br />
(intended to sytnax-compatible with RSpec).  Compatibility is all centered on emulating MRI. We used to fail ~1100 specs a few weeks ago, today we&#8217;re closer to ~500. The improvement is all due to community involvement.</p>
<p>Have you made any enhancements? Yeah, we did some prettier backtraces andcapturing exceptions from C extension segfaults, for example.</p>
<p>Is Rubinius challenged by ObjectSpace, Continuations, &amp;Selector Namespaces? They have a problem with ObjectSpace like JRuby, continuations are &#8220;our shiny awesomeness&#8221; (thanks Smalltalk&#8217;s spaghetti stack) [shows continuation demo], and (no comment on SN). </p>
]]></content:encoded>
			<wfw:commentRss>http://kfahlgren.com/blog/2007/11/03/rubyconf-2007-second-day-morning/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>RubyConf 2007 First Day Afternoon</title>
		<link>http://kfahlgren.com/blog/2007/11/02/rubyconf-2007-first-day-afternoon/</link>
		<comments>http://kfahlgren.com/blog/2007/11/02/rubyconf-2007-first-day-afternoon/#comments</comments>
		<pubDate>Fri, 02 Nov 2007 17:57:55 +0000</pubDate>
		<dc:creator>Keith</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Conferences]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[RubyConf2007]]></category>

		<guid isPermaLink="false">http://kfahlgren.com/blog/2007/11/02/rubyconf-2007-first-day-afternoon/</guid>
		<description><![CDATA[Nathaniel Talbott: Why Camping Matters
&#8220;I don&#8217;t know about you, but I am totally psyched about this conference!&#8221; Nathaniel has spoken at every RubyConf.

Photo by laurafries.com
Every talk needs a metaphor, and this talk&#8217;s will be the bacon, egg, and cheese biscuit. 
Bacon
The bacon is the connection to the creator, and to chunky bacon. It&#8217;s a 4k [...]]]></description>
			<content:encoded><![CDATA[<h2>Nathaniel Talbott: Why Camping Matters</h2>
<p>&#8220;I don&#8217;t know about you, but I am totally psyched about this conference!&#8221; Nathaniel has spoken at every RubyConf.</p>
<p><a href="http://flickr.com/photos/laurafries/1449813970/" title=" bacon, egg &amp; cheese biscuit, west egg cafe "><img src="http://farm2.static.flickr.com/1144/1449813970_b1c26751af.jpg" width="500" height="375" alt=" bacon, egg &amp; cheese biscuit, west egg cafe " /></a></p>
<p><a href="http://flickr.com/photos/laurafries/1449813970/">Photo</a> by <a href="http://flickr.com/photos/laurafries/">laurafries.com</a></p>
<p>Every talk needs a metaphor, and this talk&#8217;s will be the bacon, egg, and cheese biscuit. </p>
<h3>Bacon</h3>
<p>The bacon is the connection to the creator, and to chunky bacon. It&#8217;s a 4k micro framework created by _why.</p>
<p>A whole Camping app goes in a single file and is a typical MVC.</p>
<pre>Camping.goes :Blog

module Blog::Controllers
  class Index &lt; R '/' # route to /
    def get
      "Hello Rubyconf!"
    end
  end
end

...
$ camping blog.rb
# blah blah, up on localhost:3301
</pre>
<p>Hooray, Hello World!. Let&#8217;s start doing more MVC:</p>
<pre>Camping.goes :Blog

module Blog::Controllers
  class Index &lt; R '/' # route to /
    def get
      render :index
    end
  end
  class Add &lt; R '/add'
    render :add
  end
end

module Blog::Views
  def index
    # this is markaby
    a "add post", :href =&gt; R(Add)
  end
  def add
    form :method =&gt; :post do
      fieldset do
        label "Title: "
        input :name =&gt; :title; br
        label "Body"
        textarea :name =&gt; :body; br
      end
    end
  end
end
</pre>
<p>Camping is nice when making a sketch of the app, and eliminates all the unnecessary crap. To that extent, it&#8217;s wonderful for rapid prototyping. We&#8217;ve already got most of a Blog skeleton. Now, time for M of MVC.</p>
<pre>Camping.goes :Blog

module Blog::Controllers
  class Index &lt; R '/' # route to /
    def get
      render :index
    end
  end
  class Add &lt; R '/add'
    def get
      render :add
    end
    def post
      Post.create!(@input)
      redirect Index
    end
  end
end

module Blog::Views
  def index
    # this is markaby
    a "add post", :href =&gt; R(Add)
  end
  def add
    form :method =&gt; :post do
      fieldset do
        label "Title: "
        input :name =&gt; :title; br
        label "Body"
        textarea :name =&gt; :body; br
      end
    end
  end
end  

# This is ActiveRecord under the covers, that's _not_ part of the 4k
module Blog::Models
  class Post &lt; Base; end 

  # Migrations go inline
  class CreatePost &lt; V 1
    def self.up
      create_table :blog_posts do |t|
        t.column :title, :string
        t.column :body, :text
      end
    end
  end
end

def Blog.create;
</pre>
<p>Cool, we&#8217;ve got something working. It&#8217;d be nice if we knew what was there&#8230;</p>
<pre>module Blog::Views
  def index
    @posts.each do |e|
      h2{e.title}
      p e.body
      hr
    end

    # this is markaby
    a "add post", :href =&gt; R(Add)
  end
end
</pre>
<p>Permalinks:</p>
<pre>class View &lt; R '/view/(\d+)'
  def get(id)
    @post = Post.find(id)
    render :view
  end
end  </pre>
<p>[As you can see, this all happened very fast, as we're only 17 minutes in so far.] Nathaniel adds comments in the next 4 minutes.</p>
<h3>Egg</h3>
<p>So, that&#8217;s really most of Camping. To keep the metaphor going, we need to break some eggs (over Rails). Some differences between Rails &amp; Camping: </p>
<ul>
<li>Convention over configuration vs. minimalizism (nothing to configure)</li>
<li>Opinionated vs. wackyness (&#8220;Camping isn&#8217;t so much opinionated as it is&#8230;  strange.&#8221;)</li>
<li>Boring vs. different</li>
<li>Just like always, but better vs. &#8220;More flexible than a wet noodle&#8221;</li>
<li>Lots and lots of helpers vs. diy</li>
<li>Rails vs. Ruby</li>
<li>Daunting to hack on vs. hackable (if odd)</li>
<li>Encourages conformance vs. encourages experimentation</li>
</ul>
<h3>Cheese</h3>
<p>Actually hacking on stuff is fun. That&#8217;s why Camping &amp; any other cool, out-there framework or language is there: &#8220;we need to feed our inner hacker!&#8221; Keys to feeding your inner hacker:</p>
<ul>
<li>Needs to be regular (daily would really be great)</li>
<li>Eat a variety of foods (different is good: Rails Monday, Camping Tuesday)</li>
<li>You have to feed on things that you&#8217;re passionate about</li>
</ul>
<h3>Biscuit</h3>
<p>The biscuit is the Community. Why do people who have been to both RailsConf and RubyConf prefer RubyConf? [A show of hands confirmed this.]  RubyConf is still small, and it&#8217;s the hobbyist conference. Both of those are not true of RailsConf. We, the Ruby community, need both conferences, with RailsConf bringing the momentum and RubyConf bringing the vitality.  </p>
<h3>Questions</h3>
<p>Testing: There&#8217;s a testing framework for Camping called Mosquito. </p>
<p>Production: Nathaniel does use it in production, but not for clients.<br />
Intranet apps would be great.</p>
<h2>Nathan Sobo: Treetop: Bringing the Elegance of Ruby to Syntactic<br />
Analysis</h2>
<p>Earlier in the day, a significant but small number of folks raised their hands when asked if they had ever written a parser. However, many of us have written ad-hoc parsers on other stuff (in regexes, usually). Why don&#8217;t regular programmers use the same tools as language designers for creating parsers? Because the tools for making parsers have a really high barrier to entry, even though regexes and loops make for brittle software. Hopefully, some new tools will lower this barrier to entry. <a href="http://rubyforge.org/projects/treetop/">Treetop</a> is an attempt at this.
</p>
<p>What is a Context Free (Generative) Grammar? A grammar is a program, a program that generates every possible string in a language. However, there&#8217;s a problem with this model of grammars, as sometimes there&#8217;s ambiguity (think of <tt>if/else</tt> with ambigous nesting). Instead, do Parsing Expression Grammars [PEG], which Treetop uses, and work on <em>recognizing</em> language rather than generating it. </p>
<p>PEGs are just a generalization of ur-regexes, but are more powerful because they can do recursion. Here&#8217;s something for <tt>(((a)))</tt>:</p>
<pre># Treetop, not Ruby
grammar ParenLanguage
  rule nested_parens
    '(' nested_parens ')' / [a-z]
  end
end  

# use in Ruby like so
load_grammar 'paren_language'
parser = ParenLanguageParser.new
tree = parser.parse("(((a)))")
</pre>
<p>The <tt>tree</tt> above is an OO view of the parse.</p>
<h3>Some livecoding</h3>
<p>Let&#8217;s parse the language of arithmetic.</p>
<pre>(5 + 2) * (10 - 5)</pre>
<p>First, draw a tree over the thing you want to parse.</p>
<p>Here&#8217;s what I captured from what he livecode:</p>
<pre>dir = File.dirname(__FILE__)
require "#{dir}/test_header"

load_grammar "#{dir}/aritmetic"

class ArithmeticGrammarTest &lt; Test::Unit::TestCase
  include GrammarTestHelper

  def setup
    @parser = ArithmeticParser.new
  end
  def test_numbers_simple
    assert @parser.parse('0').success?
    assert_equal 0, @parser.parse('0').eval
    assert @parser.parse('123').success?
  end

  def test_numbers_use_helpers
    assert 0, parse('0').eval
  end

  def test_variables
    assert_equal 2, ... something ..
  end

  test_multiplicative
    assert_equal 20,  parse('x * 10').eval({'x' =&gt; 2})
    assert_equal 4 * 3 * 2, parse('4 * 3 * 2 * 1').eval...
  end

  def test_additive
    assert_equal 5 + 2 * 10 - 5, parse('5 + x * 10 - y').eval({'x' =&gt; 2, 'y' =&gt; 5})
  end 

  def test_parentheses
    assert_equal (5 + 2) * (10 - 5), parse('(5 + x) * (10 - y)').eval({'x' =&gt; 2, 'y' =&gt; 5})
  end
end

# different file
grammar Arithmetic
  rule primary
    variable
    /
    number
    /
    '(' space additive space ')' {
      eval(...)
    }
  end  

  rule space
    ' '*
  end  

  rule additive
    operand_1:multiplicative space additive_op space operand_2:additive {
      def eval(env)
        additive_op.apply(operand_1.eval(env), operand_2.eval(env))
      end
    }
    /
    primary
  end

  rule additive_op
    '+'
  end

  rule multiplicative
    operand_1:primary space '*' space operand_2:multiplicative {
      def eval(env)
        operand_1.eval(env) * operand_2.eval(env)
      end
    }
    /
    primary
  end

  rule variable
    [a-z]+ {
      def eval(env)
        env[name]
      end
      def name
        text_value
      end
    }
  end  

  rule number
    ([1-9] [0-9]* / '0') {
      def eval(env)
        text_value.to_i
      end
    }
  end
end
</pre>
<p>Note that we didn&#8217;t lex anywhere above, and that the stuff above is composable. Grammars can be opened up an have other Grammars included (<tt>include Arithmetic</tt>, then override some part of it!)).</p>
<p>[Just showed up a Turing-complete Lambda Calculus language parser in 132 lines]</p>
<p>Imagine (using each others PEGs): </p>
<pre>grammar RubyWithSQLStrings
  include Ruby
  include SQL

  rule expression
    ruby_expression
  end  

  rule ruby_string
    quote sql_expression quote / super
  end
end </pre>
<p>We didn&#8217;t cover lookahead, but there&#8217;s both negative and positive. Here&#8217;s negative (a quote, a bunch of not quotes, followed by quote):</p>
<pre>'"' (!'"' .)* '"'</pre>
<p>Memoization makes all of this stuff work, although it wasn&#8217;t an option in the past.</p>
<h2>Ryan Davis: Hurting Code for Fun and Profit</h2>
<p>On Ruby Sadism, Asceticism, &amp; Introspection. </p>
<p>Start with a story: Once upon a time, a developer went to a New Place. The New Place had legacy code (any code that you didn&#8217;t write yourself). Every piece of legacy code reference 5 other files and everything is a rats nest. The developer is mad. He does what is &#8220;right&#8221; and kills all of the people responsible.</p>
<p>OR: Developer finds the dependencies, the rats nest, and gets angry. But, this time he pulls out tools and instead of maiming the people, he hurts the code. He shows the code who is boss.</p>
<p><a href="http://flickr.com/photos/candescence/1835500367/" title=""><img src="http://farm3.static.flickr.com/2325/1835500367_7c8946771f.jpg" width="500" height="375" alt=" People will press charges if you hurt them." /></a></p>
<p><a href="http://flickr.com/photos/candescence/1835500367/">Photo</a> by <a href="http://flickr.com/photos/candescence/">candescence</a></p>
<p>People will press charges when you hurt them, code won&#8217;t. </p>
<h3>Why Hurt Code</h3>
<p>Hurting code is fun, and may make your code cleaner, more readable, and easier to test. If you make fixing code fun, you&#8217;ll do it much more often. An obvious example of sadism is killing a bug by writing a new test.</p>
<p>For some reason, people love complexity. Asceticism is characterized by strict self-discipline. Test-first is an example of asceticism. YAGNI is an example of abstenstion. Resist indulgence! (needless complexity, overly-clever code, code that you don&#8217;t need right now, &#8220;technical debt&#8221;)</p>
<p>&#8220;A developer&#8217;s obligation is to make sure that the code as written makes the clearest possible statement as to how the solution was understood at the time of writing.&#8221; â€“Ward Cunningham</p>
<h3>Introspection-oriented development</h3>
<p>How to do it?</p>
<ul>
<li>Ask yourself constantly: How do I do better? How did I overlook that bug? Am I wrong?</li>
<li>Improve yourself: read 1 nerd book per month (which is 12x industry average)</li>
<li>c2.com &amp; other wikis with smart people</li>
<li>Get rid of high-flow mailing lists, meaningless blogs in feedreader, bad sites</li>
<li>Grow: Learn a language a year, learn your tools much better, examine your habits, study something wierd</li>
<li>Push yourself: Write lots; throw away; write more (they weren&#8217;t kidding when they said &#8220;practice makes perfect&#8221;)</li>
<li>Push yourself more: Be competetive, challenge the status quo</li>
<li>Feel: Have an opinion, have passion (zentest, flog &amp; heckle all came from love) (image_science came from hate)</li>
<li>Feedback: Figure out how to get better</li>
</ul>
<h3>Tools</h3>
<p>Flog can help find code that will be hard to test and understand.</p>
<p>Coverage tools are good at finding gaping holes, but not anything about quality.</p>
<p>Heckle (&#8220;the most sadistic tools I&#8217;ve written&#8221;) mutates your implementation to make sure that your tests are good.</p>
]]></content:encoded>
			<wfw:commentRss>http://kfahlgren.com/blog/2007/11/02/rubyconf-2007-first-day-afternoon/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>RubyConf 2007 First Day Morning</title>
		<link>http://kfahlgren.com/blog/2007/11/02/rubyconf-2007-first-day-morning/</link>
		<comments>http://kfahlgren.com/blog/2007/11/02/rubyconf-2007-first-day-morning/#comments</comments>
		<pubDate>Fri, 02 Nov 2007 14:29:28 +0000</pubDate>
		<dc:creator>Keith</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Conferences]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[RubyConf2007]]></category>

		<guid isPermaLink="false">http://kfahlgren.com/blog/2007/11/02/rubyconf-2007-first-day-morning/</guid>
		<description><![CDATA[I&#8217;m at RubyConf 2007 for the next few days. Here&#8217;s a stream-of-consciousness blog of the first morning&#8217;s talks. Apparently there will eventually be video of the talks online.

Photo by jremsikjr

David Black kicks it off
This year is bigger than ever, with attendance 15 times greater than the first one in 2001. New tracks have been added [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m at <a href="http://rubyconf.org/">RubyConf 2007</a> for the next few days. Here&#8217;s a stream-of-consciousness blog of the first morning&#8217;s talks. Apparently there will eventually <a href="http://news.confreaks.com/2007/8/23/hoedown-rough-cuts-finished-next-stop-rubyconf">be video</a> of the talks online.</p>
<p><a href="http://flickr.com/photos/jremsikjr/1830514071/" title="RubyConf Shirt"><img src="http://farm3.static.flickr.com/2024/1830514071_796eff2f4f.jpg" width="500" height="375" alt="RubyConf shirt" /></a></p>
<p><a href="http://flickr.com/photos/jremsikjr/1830514071/">Photo</a> by <a href="http://flickr.com/photos/jremsikjr/">jremsikjr</a>
</p>
<h2>David Black kicks it off</h2>
<p>This year is bigger than ever, with attendance 15 times greater than the first one in 2001. New tracks have been added &amp; the format has been changed with plenary sessions for the mornings and 3 tracks in the afternoon.</p>
<h2><a href="http://marcelmolina.com">Marcel Molina</a>: What Makes Code Beautiful?</h2>
<h3>Historical definitions of beauty</h3>
<p>Beautiful things according to the audience:</p>
<ul>
<li>My Wife</li>
<li>&#8220;His Wife&#8221;</li>
<li>Kids</li>
<li>Flowers</li>
<li>Expressiveness</li>
<li>Simplicity</li>
</ul>
<p><a href="http://flickr.com/photos/dwortlehock/1835208726/" title="Marcel Molina"><img src="http://farm3.static.flickr.com/2011/1835208726_0db09c5e7b.jpg" width="500" height="375" alt="Marcel Molina" /></a></p>
<p><a href="http://flickr.com/photos/dwortlehock/1835208726/">Photo</a> by <a href="http://flickr.com/photos/dwortlehock/">dwortlehock</a>
</p>
<p>&#8220;Unlike most of the room, I wasn&#8217;t doing awesome BASIC hacks when I was 5 [years old]. I was reading books.&#8221; Marcel was interested in language and semantics, especially the differences between very similar sentences &amp; constructions. There are good ways of constructing sentences and bad ways, especially for software.</p>
<p>If you make a really long sentence, with lots of relatively, if interesting, long clauses that don&#8217;t really do anything but keep the audience from knowing the important bits (because of huge delay and &#8220;suspension&#8221;), you suck.</p>
<p>Ruby appealed to Marcel almost immediately on some root level, although he wasn&#8217;t entirely aware of why. This is half of why &#8220;My Wife&#8221; is beautiful but you can&#8217;t explain why (&#8220;I just feel it&#8221; versus &#8220;her jawline is the golden ratio&#8221;).  This makes peoples assertions that Ruby is elegant interesting to try to quantify.</p>
<h3>Beauty for Software</h3>
<p>Three parts of beauty (from Aquinas):</p>
<ul>
<li>Proportion (you could make your hand 10 times bigger and it&#8217;d still be ok, but not if you didn&#8217;t preserve the ratio of sizes)</li>
<li>Integrity (a crystal hammer might be beautiful, but isn&#8217;t much of a hammer) </li>
<li>Clarity (&#8220;complicated in the perjorative sense&#8221;)</li>
</ul>
<p>Now, a case study in code. The background is a web service that reads in a huge chunk of XML and builds Ruby objects as strings, but it&#8217;d be really nice to coerce those strings into appropriate objects:</p>
<pre>
'true'                     =&gt; true
'false'                    =&gt; false
'42'                       =&gt; 42
'2007-08-01T23:55:35.000Z' =&gt; Wed Aug 01 23:55:35 UTC 2007
</pre>
<p>The basic attempt is just to try a bunch of different coercions, one after the other, in a special <tt>try {}</tt> block.</p>
<p>So, how beautiful is this <tt>CoercibleString</tt>? It&#8217;s fairly proportionate, but that doesn&#8217;t really matter, we&#8217;re more interested in the appropriate size measure of proportionality. In this case, it&#8217;s not the appropriate size, because he later<br />
refactored it to half the number of lines (from ~20 to 10). Does it have integrity, in that it is well suited to what it does? He uses the Generator library, which uses continuations in 1.8 (but threads in 1.9), and it was crazy slow and had a memory leak, so it doesn&#8217;t have integrity. Clarity? &#8220;Uh&#8230; yeah.&#8221;  It had to be explained to everyone (unlike the refactored). Anyway, it failed<br />
on all three.</p>
<p><a href="http://www.flickr.com/photos/johnnunemaker/1826735273/" title="Marcel Molina"><img src="http://farm3.static.flickr.com/2025/1826735273_6e5ef61a6f.jpg" width="500" height="375" alt="Short Code: WTF" /></a></p>
<p><a href="http://flickr.com/photos/johnnunemaker/1826735273/">Photo</a> by <a href="http://flickr.com/photos/johnnunemaker/">jnunemaker</a>
</p>
<p>Remember, all three parts of our definition are necessary (and no one can  excluded). You lose clarity if you go too far on shortness:</p>
<pre>expand(join("", (map { /\s+\w/ ? ( $_ ....</pre>
<h3>Does quality relate to beauty</h3>
<p>Many engineers seem to not always care about beauty (and &#8220;feelings&#8221;), great software and beauty go hand &amp; hand. For example, Kent Beck in <em>Smalltalk: Best Practice Patterns</em> is just an exploration of the best ways to design and write software. He might not use the word &#8220;beauty&#8221; or think about it in that way, but his ideas on rules to write great software is based on the same principles of beauty oulined above.</p>
<h3>Is any of this useful?</h3>
<p>This refactored coerce method may not be the most beautiful thing on its own, but compared to assembler, it&#8217;s stunning.</p>
<pre>class String
  def self.coerce(string)
    case string
    when 'true':          true
    when 'false':         false
    when /^[1-9]+\d*$/:   integer(string)
    when DATETIME_FORMAT: Time.parse(string)
    else
      string
    end
  end
end</pre>
<p>Ruby may not be the most beautiful thing in 20 years, but it certainly is today. If you&#8217;re not pleased with the beauty of the <tt>case</tt> statement above, consider the time before <tt>if</tt> was implemented in programming languages, then consider how beautiful <tt>if</tt> was when it was first added.</p>
<p>&#8220;Luckily for us, Ruby is optimized for beauty.&#8221; &#8220;When you&#8217;re working on software, try to imagine better modes of expression.&#8221; After giving it a try, make sure it doesn&#8217;t violate any of the three rules of beauty. Iterate until it passes all three and you&#8217;ll hopefully end up with something beautiful.</p>
<p>Hats off to Matz &amp; and the Rubycore team for making such a beautiful language.</p>
<p><!--An expert is a person who has made all the mistakes that can be made in a very narrow field.--></p>
<h2>Jim Weinrich: Advanced Ruby Class Design</h2>
<p>Jim&#8217;s history in programming and OO meant that while he&#8217;d used dynamic languages, his sense of OO was all from a strict paradigm. Coming from Java and C++ will give you a lot of good concepts, but there are parts of Ruby that are inconceivable in Java.</p>
<p><a href="http://flickr.com/photos/dwortlehock/1834373633/" title="Jim Weirich"><img src="http://farm3.static.flickr.com/2307/1834373633_970a8f86e7.jpg" width="500" height="375" alt="Jim Weirich" /></a></p>
<p><a href="http://flickr.com/photos/dwortlehock/1834373633/">Photo</a> by <a href="http://flickr.com/photos/dwortlehock/">dwortlehock</a>
</p>
<h3>Master of Disguise</h3>
<p>This is an example from Rake (Rake::FileList). </p>
<pre>RUBY_FILES = FileList['lib/**/*.rb']</pre>
<p>FileList is like an Array, except that it initializes with a GLOB from the filesystem, has a specialized <tt>to_s</tt> method, uses lazy evaluation (woot), and has some extra methods (<tt>ext</tt> (for file extension manipulation), <tt>pathmap</tt>).</p>
<p>The first pass at this took the similarity to Array and started with that explicitly:</p>
<pre>class FileList &lt; Array
 ...
end </pre>
<p>Java would suggest that you never inherit from concrete classes, which also is a good rule for Ruby <b>but for totally different reasons</b>. More on that later.</p>
<p>The lazy bits made direct Array access problematic (like <tt>index</tt>), so each Array-accessing method had to call the <tt>resolve</tt> method to unlazyify the FileList. This made some operations not work.</p>
<p>Instead of inheriting from Array, you should use <tt>to_ary</tt>, so that Ruby helps you when messing with other Arrays out in the world. FileList now is just a regular class not inheriting from anything special and Ruby will ask a FileList if it can behave as an Array (using <tt>to_ary</tt>).</p>
<p>As for all of the methods needing to call <tt>resolve</tt>, you can DRY this with a list of relevant methods and a <tt>class_eval</tt>.</p>
<p>Takeaway: Consider <tt>to_ary/to_str</tt> when you want to mimic a base class, rather than using inheritance.</p>
<h3>Doing nothing</h3>
<p>Jim built Builder for the pure fun of it. (Thanks, Jim, it&#8217;s a pretty nice library!) It uses block structure and <tt>method_missing</tt> to make writing XML much easier. However, because XML element names may conflict with builtin methods (<tt>class</tt> is a good example), we have to make sure <tt>xml.class("Intro to Ruby")</tt> doesn&#8217;t blow up. </p>
<p>Wouldn&#8217;t it be nice to inherit from Object without inheriting all the stuff from Object?  Introducing BlankSlate, which is really easy to write:</p>
<pre>class BlankSlate
  instance_methods.each do |name|
    undef_methods name
  end
end</pre>
<p>&#8230;but that&#8217;s a little too overzealous, because it removes <tt>/^__/</tt> methods (<tt>__id__</tt> is used by a lot of internal stuff, for example). That can be easily fixed with an <tt>unless</tt>.</p>
<p>Unfortunately, you&#8217;ve still got problems with global methods defined later (in Kernel, say). You can fix this by adding to the <tt>method_added</tt> hook in Kernel and Object:</p>
<pre>alias_method :original_method_Added, :method_added
def method_added(name)
  result = original_method_added(name)
  BlankSlate.hide(name) if self == Kernel
  result
end</pre>
<p>All set? Not quite, we&#8217;ve still got a similar bug that bypasses <tt>method_added</tt>:</p>
<pre>module Name
  def name
    "My Name"o
  end
end 

class Object
  include Name
end  

...
xml.name("jim")</pre>
<p>This can be fixed with the <tt>append_features</tt> hook (look at BlankSlate in Builder).</p>
<h3>Parsing without Parsing</h3>
<p>Consider:</p>
<pre>User.find(:all, :conditions =&lt; ["name = ?", "jim"])</pre>
<p>&#8230;which looks a lot like SQL code. Why can&#8217;t I just call <tt>select</tt>?</p>
<pre>user_list.select {|user|
  user.name == "jim"
}</pre>
<p>&#8220;Wouldn&#8217;t it be nice if there was a way we could use select on ActiveRecord models?&#8221; Let&#8217;s write it (naive first attempt):</p>
<pre>class User
  def self.select(&amp;block)
    find(:all).select(&amp;block)
  end
end  </pre>
<p>This, of course, is not effecient at all, and large tables will kill you. Databases do really have a purpose, of course, and we should be using their design. Here&#8217;s a magical method:</p>
<pre>def self.select(&amp;block)
  cond = translate_block_to_sql(&amp;block)
  find(:all, :conditions =&gt; cond)
end </pre>
<p>&#8230;however, not many people have written Ruby parsers, which is an &#8220;interesting language to parse&#8221;. You could use ParseTree, which uses ruby to parse Ruby then evicerates the result. Could we just execute the code? (huh?)</p>
<p>Here&#8217;s some curious code:</p>
<pre>$ irb -rnode1
&gt;&gt; user = TableNode.new("users")
&gt;&gt; result = user.name
&gt;&gt; puts result.to_s
users.name
&gt;&gt; result2 = user.age
&gt;&gt; puts result2.to_s
users.age
</pre>
<p>This allows for references to tables that helps build SQL code. Here&#8217;s the background (similar for MethodNode):</p>
<pre>class TableNode &lt; Node
  def initialize(table_name)
    @table_name = table_name
  end
  def method_missing(sym, *args, &amp;block)
    MethodNode.new(self, sym)
  end
  def to_s
    @table_name
  end
end</pre>
<p>OK, we&#8217;ve got field references down, but how do we do stuff like <tt>==</tt>?</p>
<pre>class Node
  def ==(other)
    BinaryOpNode.new("=", self, other)
  end
end</pre>
<p>&#8230;well we just capture the interesting method in the Node class then  translate the method to a SQL fragment (in BinaryOpNode)</p>
<pre>$ irb -rnode1res1 = (user.age == 50)
&gt;&gt; user = TableNode.new("users")
&gt;&gt; puts res1.to_s
(users.age = 50)
&gt;&gt; res2 = (user.name == "jim")
&gt;&gt; puts res2.to_s
(users.name = jim) # oops, no quotes
</pre>
<p>To quote strings, we need to differentiate between LiteralNodes and StringNodes, which just wraps with quotes (and probably does escaping). Getting the right kind of Node could depend on a <tt>case</tt> statement, but that&#8217;s not very OO. Every object should really know how to convert itself&#8230;</p>
<pre>class Object
  # be careful opening core classes, which is why we have a unique name
  def as_a_sql_node
    LiteralNode.new(self)the r
  end
end  

class String
  def as_a_sql_node
    StringNode.new(self)
  end
end</pre>
<p>Now we just need to call it:</p>
<pre>  def ==(other)
    BinaryOpNode.new("=", self, other.as_a_sql_node)
</pre>
<p>  end  </p>
<p>&#8230;and, as we&#8217;d hoped:</p>
<pre>&gt;&gt; res2 = (user.name == "jim")
&gt;&gt; puts res2.to_s
(users.name = 'jim') </pre>
<h4>Problems</h4>
<p>We haven&#8217;t handled commutativity [hey, Marcel had this problem too!]. <tt>"jim" == user.name</tt> will not work, although <tt>+</tt> gets help from <tt>coerce</tt> for mathematical operators.  A killer problem is that <tt>&amp;&amp;/||</tt> aren&#8217;t methods (by necessity, because of their shortcircutyness). <tt>!/!=</tt> also have predefined semantics and aren&#8217;t overridable. So, this technique really wouldn&#8217;t work for SQL. It&#8217;s a  &#8220;solution looking for a problem.&#8221;</p>
<h3>What did we learn?</h3>
<p>Programming languages shape the way you think, so make sure you&#8217;re thinking  about problems in a Ruby-ish way. Sometimes, the corners of a language will hold the keys to good, idiomatic design. Don&#8217;t be afraid of unusual solutions (some of the time).</p>
]]></content:encoded>
			<wfw:commentRss>http://kfahlgren.com/blog/2007/11/02/rubyconf-2007-first-day-morning/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>More Clever GMail Ads for Programmers</title>
		<link>http://kfahlgren.com/blog/2007/07/10/more-clever-gmail-ads-for-programmers/</link>
		<comments>http://kfahlgren.com/blog/2007/07/10/more-clever-gmail-ads-for-programmers/#comments</comments>
		<pubDate>Tue, 10 Jul 2007 15:30:16 +0000</pubDate>
		<dc:creator>Keith</dc:creator>
				<category><![CDATA[Clever]]></category>
		<category><![CDATA[Links]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://kfahlgren.com/blog/2007/07/10/more-clever-gmail-ads-for-programmers/</guid>
		<description><![CDATA[Just like the folks from Jane Street Capital, Swivel really knows how to write good, eye-catching ad copy (for some crazy subset of the population):

]]></description>
			<content:encoded><![CDATA[<p>Just like the folks from <a href="http://kfahlgren.com/blog/2007/03/26/jane-street-capital-is-on-to-me/">Jane Street Capital</a>, <a href="http://swivel.com/about/jobs#dev1">Swivel</a> really knows how to write good, eye-catching ad copy (for some crazy subset of the population):</p>
<p><a href="http://kfahlgren.com/photos/screenshots/swivel_ad.png"><img src="http://kfahlgren.com/photos/screenshots/swivel_ad.png" alt="Clever Swivel Rails Ad" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://kfahlgren.com/blog/2007/07/10/more-clever-gmail-ads-for-programmers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JRuby + Jetty</title>
		<link>http://kfahlgren.com/blog/2007/06/06/jruby-jetty/</link>
		<comments>http://kfahlgren.com/blog/2007/06/06/jruby-jetty/#comments</comments>
		<pubDate>Thu, 07 Jun 2007 00:32:51 +0000</pubDate>
		<dc:creator>Keith</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[JRuby]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://kfahlgren.com/blog/2007/06/06/jruby-jetty/</guid>
		<description><![CDATA[I finally figured out how to get JRuby to serve a Jetty servlet today (thanks to Charles). The key was flipping what I&#8217;d been trying to do for a while (getting Jetty to run JRuby). Here&#8217;s code that implements the AbstractHandler interface pretty trivially:
$ cat jetty_example.jrb
require 'java'
include_class 'javax.servlet.ServletException'
include_class 'javax.servlet.http.HttpServlet'
include_class 'javax.servlet.http.HttpServletRequest'
include_class 'javax.servlet.http.HttpServletResponse'

include_class 'org.mortbay.jetty.Server'
include_class 'org.mortbay.jetty.servlet.Context'
include_class 'org.mortbay.jetty.servlet.ServletHolder'
include_class 'org.mortbay.jetty.handler.AbstractHandler'

class [...]]]></description>
			<content:encoded><![CDATA[<p>I finally figured out how to get JRuby to serve a Jetty servlet today (thanks to <a href="http://blog.earstu.org/">Charles</a>). The key was flipping what I&#8217;d been trying to do for a while (getting Jetty to run JRuby). Here&#8217;s code that implements the AbstractHandler interface pretty trivially:</p>
<pre>$ cat jetty_example.jrb
require 'java'
include_class 'javax.servlet.ServletException'
include_class 'javax.servlet.http.HttpServlet'
include_class 'javax.servlet.http.HttpServletRequest'
include_class 'javax.servlet.http.HttpServletResponse'

include_class 'org.mortbay.jetty.Server'
include_class 'org.mortbay.jetty.servlet.Context'
include_class 'org.mortbay.jetty.servlet.ServletHolder'
include_class 'org.mortbay.jetty.handler.AbstractHandler'

class SimpleHandler &lt; AbstractHandler
  def handle(target, request, response, dispatch)
    response.setContentType("text/html")
    response.setStatus(HttpServletResponse::SC_OK)
    response.getWriter().println("&lt;h1&gt;Goodbye, cruel monoglot world!&lt;/h1&gt;")
    request.setHandled(true)
  end
end

handler = SimpleHandler.new
server = Server.new(8080)
server.setHandler(handler)
server.start()
</pre>
<p>To run, add Jetty to your classpath:</p>
<pre>$ export CLASSPATH="/path/to/jetty-6.1.3.jar:/.../jetty-util-6.1.3.jar:/.../servlet-api-2.5-6.1.3.jar"</pre>
<p>Then it&#8217;s just a normal JRuby invocation:</p>
<p><code>$ jruby jetty_example.jrb</code></p>
<p>It&#8217;s trivial code at this point (and doesn&#8217;t handle concurrent requests, maxing out at 6.47r/s across my network), but at least it&#8217;s got me started.</p>
<p>[UPDATE: I can get the non-concurrent request handling way down with just a few simple tweaks (mainly running JRuby in SERVER mode) and running ab locally ;-)]</p>
]]></content:encoded>
			<wfw:commentRss>http://kfahlgren.com/blog/2007/06/06/jruby-jetty/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Code Behind DocBook Elements in the Wild</title>
		<link>http://kfahlgren.com/blog/2007/05/01/the-code-behind-docbook-elements-in-the-wild/</link>
		<comments>http://kfahlgren.com/blog/2007/05/01/the-code-behind-docbook-elements-in-the-wild/#comments</comments>
		<pubDate>Wed, 02 May 2007 03:24:15 +0000</pubDate>
		<dc:creator>Keith</dc:creator>
				<category><![CDATA[AtomPub]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[DocBook]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Work]]></category>
		<category><![CDATA[XML]]></category>

		<guid isPermaLink="false">http://kfahlgren.com/blog/2007/05/01/the-code-behind-docbook-elements-in-the-wild/</guid>
		<description><![CDATA[[UPDATE: Added a link to the categorized CSV file below]
Here&#8217;s some of the nitty-gritty behind DocBook Elements in the Wild. We&#8217;re trying to get a count of all of the element names in a set of 49 DocBook 4.4 &#60;book&#62;s.
First, go ask the O&#8217;Reilly product database for all the books that were sent to the [...]]]></description>
			<content:encoded><![CDATA[<p>[UPDATE: Added a link to the <a href="http://kfahlgren.com/docbook/49books_categorized.csv">categorized CSV</a> file below]</p>
<p>Here&#8217;s some of the nitty-gritty behind <a href="http://www.oreillynet.com/xml/blog/2007/05/docbook_elements_in_the_wild.html">DocBook Elements in the Wild</a>. We&#8217;re trying to get a count of all of the element names in a set of 49 DocBook 4.4 <tt>&lt;book&gt;</tt>s.</p>
<p>First, go ask the O&#8217;Reilly product database for all the books that were sent to the printer in 2006. Because I&#8217;m better at XML than Unix text tools, ask for <tt>mysql -X</tt>. Now we&#8217;ve got something like:</p>
<pre>&lt;resultset statement="select..."&gt;
 &lt;row&gt;
        &lt;field name="isbn13"&gt;9780596101619&lt;/field&gt;
        &lt;field name="title"&gt;Google Maps Hacks&lt;/field&gt;
        &lt;field name="edition"&gt;1&lt;/field&gt;
        &lt;field name="book_vendor_date"&gt;2006-01-05&lt;/field&gt;
  &lt;/row&gt;
  &lt;row&gt;
        &lt;field name="isbn13"&gt;9780596008796&lt;/field&gt;
        &lt;field name="title"&gt;Excel Scientific and Engineering Cookbook&lt;/field&gt;
        &lt;field name="edition"&gt;1&lt;/field&gt;
        &lt;field name="book_vendor_date"&gt;2006-01-06&lt;/field&gt;
  &lt;/row&gt;
  &lt;row&gt;
        &lt;field name="isbn13"&gt;9780596101732&lt;/field&gt;
        &lt;field name="title"&gt;Active Directory&lt;/field&gt;
        &lt;field name="edition"&gt;3&lt;/field&gt;
        &lt;field name="book_vendor_date"&gt;2006-01-06&lt;/field&gt;
  &lt;/row&gt;
  ...
</pre>
<p>Next, fun with <a href="http://xmlstar.sourceforge.net/">XMLStarlet</a>:</p>
<pre>$ xml sel -t -m "//field[@name='isbn13']" -v '.' -n books_in_2006.xml
9780596101619
9780596008796
9780596101732
9780596009441
...</pre>
<p>Now, pull the content down from our <a href="http://www.ietf.org/internet-drafts/draft-ietf-atompub-protocol-14.txt">Atom Publishing Protocol</a> repository and make a big document with XIncludes:</p>
<pre>#!/usr/bin/env ruby
require 'kurt'
require 'rexml/document'
OUTFILE = "aggregate.xml"
files_downloaded = []
ARGV.each {|atom_id|
  entry = Atom::Entry.get_entry("#{Kurt::PROD_RESOURCES}/#{CGI.escape(atom_id)}")
  filename = atom_id.gsub(/\W/, '') + ".xml"
  File.open(filename, "w") {|f|
    f.print entry.content
  }
  files_downloaded &lt;&lt; filename
}

agg = REXML::Document.new
agg.add_element("books")
agg.root.add_namespace("xi", "http://www.w3.org/2001/XInclude")
files_downloaded.each {|file|
  xi = agg.root.add_element("xi:include")
  xi.add_attribute("href", file)
}
File.open(OUTFILE, "w") {|f|
  agg.write(f, 2)
}</pre>
<p>Resolve all of the XIncludes into one big file:</p>
<pre>$ xmllint --xinclude -o aggregate.xml aggregate.xml </pre>
<p>It&#8217;s now pretty huge (well, huge in my world):</p>
<pre>$ du -h aggregate.xml
102M    aggregate.xml</pre>
<p>At this point, we&#8217;re ready to do the real counting of the elements (slow REXML solution commented out in favor of a <a href="http://libxml.rubyforge.org/doc/">libxml</a>-based solution):</p>
<pre>#!/usr/bin/env ruby
require 'rexml/parsers/pullparser'
require 'rubygems'
require 'xml/libxml'
start = Time.now
ARGV.each {|filename|
  counts = Hash.new
#  parser = REXML::Parsers::PullParser.new(File.new(filename))
#  while parser.has_next?
#    el = parser.pull
#    if el.start_element?
#      element_name = el[0]
#      if counts[element_name]
#        counts[element_name] += 1
#      else
#        counts[element_name] = 1
#      end
#    end
#  end
  parser = XML::SaxParser.new
  parser.filename = filename
  parser.on_start_element {|element_name, _|
    if counts[element_name]
      counts[element_name] += 1
    else
      counts[element_name] = 1
    end
  }
  parser.parse

  File.open(filename + ".count.csv", "w") {|f|
    counts.each {|element_name, count|
      f.puts "\"#{element_name}\",#{count}"
    }
  }
}
</pre>
<p>(Hooray for steam parsing, as this 100MB file was cranked through in 27 seconds on a 700MHz box!)</p>
<p>Finally, we&#8217;ve got CSV and we can do some graphing. <a href="http://kfahlgren.com/docbook/49books.csv">Here&#8217;s the full CSV</a> and the <a href="http://kfahlgren.com/docbook/49books_categorized.csv">categorized CSV</a>. Rather than working on a code-based graphing solution, I just messed with Excel. The result:</p>
<p><a href="http://www.oreillynet.com/xml/blog/images/elements_in_49_books.png"><img src="http://www.oreillynet.com/xml/blog/images/elements_in_49_books.png" alt="DocBook Elements from 49 Books" /></a></p>
<p>Here&#8217;s my favorite, a drill-down based on a categorization I just made up (click through for the drill-down):</p>
<p><a href="http://kfahlgren.com/photos/docbook/49booksmap.html"><img src="http://kfahlgren.com/photos/docbook/elements_in_49_books_categorized.png" alt="DocBook Elements from 49 Books, Categorized" /></a></p>
<p>Books used:</p>
<ul>
<li><a href="http://www.oreilly.com/catalog/googlemapshks/">Google Maps Hacks, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/excelseckbk/">Excel Scientific and Engineering Cookbook, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/actdir3/">Active Directory, 3e</a></li>
<li><a href="http://www.oreilly.com/catalog/rfid/">RFID Essentials, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/vb2005ian3/">Visual Basic 2005 in a Nutshell, 3e</a></li>
<li><a href="http://www.oreilly.com/catalog/psphks/">PSP Hacks, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/baseballhks/">Baseball Hacks, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/mindperfhks/">Mind Performance Hacks, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/repairpc/">Repairing and Upgrading Your PC, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/websiteckbk/">Web Site Cookbook, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/flickrhks/">Flickr Hacks, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/accessannoy/">Fixing Access Annoyances, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/powerpointannoy/">Fixing PowerPoint Annoyances, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/progsqlsvr/">Programming SQL Server 2005, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/learncsharp2/">Learning C# 2005, 2e</a></li>
<li><a href="http://www.oreilly.com/catalog/photoshopraw/">Photoshop CS2 RAW, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/wdnut3/">Web Design in a Nutshell, 3e</a></li>
<li><a href="http://www.oreilly.com/catalog/googletmm2/">Google: The Missing Manual, 2e</a></li>
<li><a href="http://www.oreilly.com/catalog/dgbebay/">Don&#8217;t Get Burned on eBay, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/artofsql/">The Art of SQL, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/windowsxpannoy/">Fixing Windows XP Annoyances, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/iphotomm/">iPhoto 6: The Missing Manual, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/ipodtmm4/">iPod &#038; iTunes: The Missing Manual, 4e</a></li>
<li><a href="http://www.oreilly.com/catalog/ajaxhks/">Ajax Hacks, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/flash8tmm/">Flash 8: The Missing Manual, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/mysqlspp/">MySQL Stored Procedure Programming, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/flashprojects/">Flash 8: Projects for Learning Animation and Interactivity, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/xamlian/">XAML in a Nutshell, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/linuxannoygks/">Linux Annoyances for Geeks, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/progphp2/">Programming PHP, 2e</a></li>
<li><a href="http://www.oreilly.com/catalog/flash8ckbk/">Flash 8 Cookbook, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/learnsqlsvr05/">Learning SQL on SQL Server 2005, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/progexcel/">Programming Excel with VBA and .NET, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/imovie06tmm/">iMovie 6 &#038; iDVD: The Missing Manual, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/enterprisesa/">Enterprise SOA, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/perlhks/">Perl Hacks, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/javaio2/">Java I/O, 2e</a></li>
<li><a href="http://www.oreilly.com/catalog/entjbeans5/">Enterprise JavaBeans 3.0, 5e</a></li>
<li><a href="http://www.oreilly.com/catalog/web2apps/">Building Scalable Web Sites, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/mcsecoreian/">MCSE Core Required Exams in a Nutshell, 3e</a></li>
<li><a href="http://www.oreilly.com/catalog/dns5/">DNS and BIND, 5e</a></li>
<li><a href="http://www.oreilly.com/catalog/learnphpmysql/">Learning PHP and MySQL, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/csb2/">Computer Security Basics, 2e</a></li>
<li><a href="http://www.oreilly.com/catalog/activedckbk2/">Active Directory Cookbook, 2e</a></li>
<li><a href="http://www.oreilly.com/catalog/ubuntuhks/">Ubuntu Hacks, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/unicode/">Unicode Explained, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/digphototmm/">Digital Photography: The Missing Manual, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/ajaxdp/">Ajax Design Patterns, 1e</a></li>
<li><a href="http://www.oreilly.com/catalog/pythonian2/">Python in a Nutshell, 2e</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://kfahlgren.com/blog/2007/05/01/the-code-behind-docbook-elements-in-the-wild/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JRuby + JFreeChart = Sparklines</title>
		<link>http://kfahlgren.com/blog/2007/04/13/jruby-jfreechart-sparklines/</link>
		<comments>http://kfahlgren.com/blog/2007/04/13/jruby-jfreechart-sparklines/#comments</comments>
		<pubDate>Fri, 13 Apr 2007 22:43:31 +0000</pubDate>
		<dc:creator>Keith</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[JRuby]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://kfahlgren.com/blog/2007/04/13/jruby-jfreechart-sparklines/</guid>
		<description><![CDATA[Inspired by how easy it was to get JFreeChart working  and some code from former colleague Andrew Bruno, I thought it&#8217;d be nice to write some JRuby to  generate Edward Tufte&#8217;s Sparklines.
Here&#8217;s some simple example code on a semi-random dataset:
# Mostly inspired by
# http://left.subtree.org/2007/01/15/creating-sparklines-with-jfreechart/
# have JFreeChart in your classpath, obviously, as well as [...]]]></description>
			<content:encoded><![CDATA[<p>Inspired by how easy it was to get <a href="http://www.jfree.org/jfreechart/">JFreeChart</a> working <a href="http://kfahlgren.com/blog/2007/04/12/more-jruby-play-jfreechart/"> and some code from former colleague </a><a href="http://left.subtree.org/2007/01/15/creating-sparklines-with-jfreechart/">Andrew Bruno</a>, I thought it&#8217;d be nice to write some JRuby to  generate Edward Tufte&#8217;s <a href="http://www.edwardtufte.com/bboard/q-and-a-fetch-msg?msg_id=0001OR&amp;topic_id=1">Sparklines</a>.</p>
<p>Here&#8217;s some simple example code on a semi-random dataset:</p>
<pre><span style="color: #00ffff;"><strong># Mostly inspired by
# <a href="http://left.subtree.org/2007/01/15/creating-sparklines-with-jfreechart/">http://left.subtree.org/2007/01/15/creating-sparklines-with-jfreechart/</a></strong></span>
<span style="color: #00ffff;"><strong># have JFreeChart in your classpath, obviously, as well as jcommon.jar</strong></span>
<span style="color: #8080ff;"><strong>require</strong></span> <span style="color: #ff6060;"><strong>'</strong></span><span style="color: #ff40ff;"><strong>java</strong></span><span style="color: #ff6060;"><strong>'</strong></span>

<span style="color: #8080ff;"><strong>module </strong></span><span style="color: #00ff00;"><strong>Graph</strong></span>
<span style="color: #8080ff;"><strong>  class </strong></span><span style="color: #00ff00;"><strong>Sparkline</strong></span>
    include_class <span style="color: #ff6060;"><strong>'</strong></span><span style="color: #ff40ff;"><strong>java.io.File</strong></span><span style="color: #ff6060;"><strong>'</strong></span>
    include_class <span style="color: #ff6060;"><strong>'</strong></span><span style="color: #ff40ff;"><strong>org.jfree.chart.ChartUtilities</strong></span><span style="color: #ff6060;"><strong>'</strong></span>
    include_class <span style="color: #ff6060;"><strong>'</strong></span><span style="color: #ff40ff;"><strong>org.jfree.chart.JFreeChart</strong></span><span style="color: #ff6060;"><strong>'</strong></span>
    include_class <span style="color: #ff6060;"><strong>'</strong></span><span style="color: #ff40ff;"><strong>org.jfree.chart.axis.NumberAxis</strong></span><span style="color: #ff6060;"><strong>'</strong></span>
    include_class <span style="color: #ff6060;"><strong>'</strong></span><span style="color: #ff40ff;"><strong>org.jfree.chart.plot.XYPlot</strong></span><span style="color: #ff6060;"><strong>'</strong></span>
    include_class <span style="color: #ff6060;"><strong>'</strong></span><span style="color: #ff40ff;"><strong>org.jfree.chart.renderer.xy.StandardXYItemRenderer</strong></span><span style="color: #ff6060;"><strong>'</strong></span>
    include_class <span style="color: #ff6060;"><strong>'</strong></span><span style="color: #ff40ff;"><strong>org.jfree.data.xy.XYSeries</strong></span><span style="color: #ff6060;"><strong>'</strong></span>
    include_class <span style="color: #ff6060;"><strong>'</strong></span><span style="color: #ff40ff;"><strong>org.jfree.data.xy.XYSeriesCollection</strong></span><span style="color: #ff6060;"><strong>'</strong></span>
    include_class <span style="color: #ff6060;"><strong>'</strong></span><span style="color: #ff40ff;"><strong>org.jfree.chart.plot.PlotOrientation</strong></span><span style="color: #ff6060;"><strong>'</strong></span>

<span style="color: #8080ff;"><strong>    def </strong></span><span style="color: #00ffff;"><strong>initialize</strong></span>(width=<span style="color: #ff40ff;"><strong>200</strong></span>, height=<span style="color: #ff40ff;"><strong>80</strong></span>, data=[])
      <span style="color: #00ffff;"><strong>@width</strong></span> = width
      <span style="color: #00ffff;"><strong>@height</strong></span> = height
      dataset = create_sample_data() <span style="color: #ffff00;"><strong>if</strong></span> data.empty?
      <span style="color: #00ffff;"><strong>@chart</strong></span> = create_chart(dataset)
    <span style="color: #8080ff;"><strong>end</strong></span>

<span style="color: #8080ff;"><strong>    def </strong></span><span style="color: #00ffff;"><strong>render_to_file</strong></span>(filename, format=<span style="color: #ff6060;"><strong>"</strong></span><span style="color: #ff40ff;"><strong>png</strong></span><span style="color: #ff6060;"><strong>"</strong></span>)
      javafile = java.io.<span style="color: #00ffff;"><strong>File</strong></span>.new(filename)
      <span style="color: #00ffff;"><strong>ChartUtilities</strong></span>.saveChartAsPNG(javafile, <span style="color: #00ffff;"><strong>@chart</strong></span>, <span style="color: #00ffff;"><strong>@width</strong></span>, <span style="color: #00ffff;"><strong>@height</strong></span>)
    <span style="color: #8080ff;"><strong>end</strong></span>

    private
<span style="color: #8080ff;"><strong>    def </strong></span><span style="color: #00ffff;"><strong>create_sample_data</strong></span>
       series = <span style="color: #00ffff;"><strong>XYSeries</strong></span>.new(<span style="color: #ff6060;"><strong>"</strong></span><span style="color: #ff40ff;"><strong>Sparkline</strong></span><span style="color: #ff6060;"><strong>"</strong></span>)
      data = [<span style="color: #ff40ff;"><strong>20</strong></span>]
      (<span style="color: #ff40ff;"><strong>1..99</strong></span>).each {<span style="color: #00ffff;"><strong>|x|</strong></span>
        y = (data.last + (rand(x) + <span style="color: #ff40ff;"><strong>1</strong></span>)) / <span style="color: #ff40ff;"><strong>2</strong></span>
        data &lt;&lt; y
        series.add(x, y)
      }

      dataset = <span style="color: #00ffff;"><strong>XYSeriesCollection</strong></span>.new
      dataset.addSeries(series)
      <span style="color: #ffff00;"><strong>return</strong></span> dataset
    <span style="color: #8080ff;"><strong>end</strong></span>

<span style="color: #8080ff;"><strong>    def </strong></span><span style="color: #00ffff;"><strong>create_chart</strong></span>(dataset)
      x = <span style="color: #00ffff;"><strong>NumberAxis</strong></span>.new
      x.setTickLabelsVisible(<span style="color: #ff40ff;"><strong>false</strong></span>)
      x.setTickMarksVisible(<span style="color: #ff40ff;"><strong>false</strong></span>)
      x.setAxisLineVisible(<span style="color: #ff40ff;"><strong>false</strong></span>)
      x.setNegativeArrowVisible(<span style="color: #ff40ff;"><strong>false</strong></span>)
      x.setPositiveArrowVisible(<span style="color: #ff40ff;"><strong>false</strong></span>)
      x.setVisible(<span style="color: #ff40ff;"><strong>false</strong></span>)

      y = <span style="color: #00ffff;"><strong>NumberAxis</strong></span>.new
      y.setTickLabelsVisible(<span style="color: #ff40ff;"><strong>false</strong></span>)
      y.setTickMarksVisible(<span style="color: #ff40ff;"><strong>false</strong></span>)
      y.setAxisLineVisible(<span style="color: #ff40ff;"><strong>false</strong></span>)
      y.setNegativeArrowVisible(<span style="color: #ff40ff;"><strong>false</strong></span>)
      y.setPositiveArrowVisible(<span style="color: #ff40ff;"><strong>false</strong></span>)
      y.setVisible(<span style="color: #ff40ff;"><strong>false</strong></span>)

      plot = <span style="color: #00ffff;"><strong>XYPlot</strong></span>.new
      plot.setDataset(dataset)
      plot.setDomainAxis(x)
      plot.setDomainGridlinesVisible(<span style="color: #ff40ff;"><strong>false</strong></span>)
      plot.setDomainCrosshairVisible(<span style="color: #ff40ff;"><strong>false</strong></span>)
      plot.setRangeGridlinesVisible(<span style="color: #ff40ff;"><strong>false</strong></span>)
      plot.setRangeCrosshairVisible(<span style="color: #ff40ff;"><strong>false</strong></span>)
      plot.setRangeAxis(y)
      plot.setOutlinePaint(<span style="color: #ff40ff;"><strong>nil</strong></span>)
      plot.setRenderer(<span style="color: #00ffff;"><strong>StandardXYItemRenderer</strong></span>.new(<span style="color: #00ffff;"><strong>StandardXYItemRenderer</strong></span>::<span style="color: #00ffff;"><strong>LINES</strong></span>))

      chart = <span style="color: #00ffff;"><strong>JFreeChart</strong></span>.new(<span style="color: #ff40ff;"><strong>nil</strong></span>, <span style="color: #00ffff;"><strong>JFreeChart</strong></span>::<span style="color: #00ffff;"><strong>DEFAULT_TITLE_FONT</strong></span>, plot, <span style="color: #ff40ff;"><strong>false</strong></span>)
      chart.setBorderVisible(<span style="color: #ff40ff;"><strong>false</strong></span>);
      <span style="color: #ffff00;"><strong>return</strong></span> chart
    <span style="color: #8080ff;"><strong>end</strong></span>

  <span style="color: #8080ff;"><strong>end</strong></span> <span style="color: #00ffff;"><strong># class Sparkline  </strong></span>
<span style="color: #8080ff;"><strong>end</strong></span> <span style="color: #00ffff;"><strong># class Graph</strong></span>

sp = <span style="color: #00ffff;"><strong>Graph</strong></span>::<span style="color: #00ffff;"><strong>Sparkline</strong></span>.new
puts <span style="color: #ff6060;"><strong>"</strong></span><span style="color: #ff40ff;"><strong>Rendering sparkline</strong></span><span style="color: #ff6060;"><strong>"</strong></span>
sp.render_to_file(<span style="color: #ff6060;"><strong>"</strong></span><span style="color: #ff40ff;"><strong>sparkline.png</strong></span><span style="color: #ff6060;"><strong>"</strong></span>)</pre>
<p>And the resulting sparkline chart:<br />
<img src="http://kfahlgren.com/photos/work/sparkline.png" alt="An Example Sparkline Chart" /></p>
<p>Code: <a href="http://kfahlgren.com/code/sparkline.jrb">http://kfahlgren.com/code/sparkline.jrb</a></p>
<p>UPDATE: Removed some of the useless sample generation code</p>
]]></content:encoded>
			<wfw:commentRss>http://kfahlgren.com/blog/2007/04/13/jruby-jfreechart-sparklines/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss>
