<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Depth-First: Metaprogramming with Ruby: Mapping Java Packages Onto Ruby Modules</title>
    <link>http://depth-first.com/articles/2006/10/24/metaprogramming-with-ruby-mapping-java-packages-onto-ruby-modules</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Walking the Web of Chemical Informatics</description>
    <item>
      <title>Metaprogramming with Ruby: Mapping Java Packages Onto Ruby Modules</title>
      <description>&lt;p&gt;&lt;img src="http://depth-first.com/files/ruby_logo_new.gif" align="right"&gt;&lt;/img&gt;&lt;a href="http://www.vanderburg.org/Speaking/Stuff/oscon05.pdf"&gt;Metaprogramming&lt;/a&gt; lets you define new constructs in your programming language. In &lt;a href="http://www.journalhome.com/codecraft/9003/"&gt;safety languages&lt;/a&gt; like Java, metaprogramming is not a standard feature, although &lt;a href="http://www.csg.is.titech.ac.jp/~chiba/javassist/"&gt;it can be done&lt;/a&gt;. Not surprisingly, the reaction most Java developers have to metaprogramming typically ranges from "useless" to "catastrophic". I was once in that category. But in the words of Paul Graham, sometimes doing the right thing involves &lt;a href="http://www.paulgraham.com/progbot.html"&gt;"changing the language to suit the problem"&lt;/a&gt;. This is a powerful concept that, when used with care, can greatly reduce development time. The Ruby language is &lt;a href="http://ola-bini.blogspot.com/2006/09/ruby-metaprogramming-techniques.html"&gt;especially well-equipped&lt;/a&gt; for metaprogramming. This article will show a simple metaprogramming technique that extends Ruby so that Java packages are mapped onto to Ruby modules.&lt;/p&gt;

&lt;h4&gt;Prerequisites&lt;/h4&gt;

&lt;p&gt;This tutorial uses &lt;a href="http://rjb.rubyforge.org/"&gt;Ruby Java Bridge&lt;/a&gt; (RJB). RJB can be &lt;a href="http://depth-first.com/articles/2006/10/12/running-ruby-java-bridge-on-windows"&gt;installed&lt;/a&gt; either on Windows or Linux using the &lt;a href="http://rubygems.org/"&gt;RubyGems&lt;/a&gt; packaging mechanism.&lt;/p&gt;

&lt;h4&gt;Defining the Problem&lt;/h4&gt;

&lt;p&gt;The large amount of existing Java code makes RJB one of Ruby's most useful integration tools. RJB provides a lightweight mechanism for working with Java classes from Ruby, while simultaneously allowing for the use of C-extensions and everything else the C implementation of Ruby offers.&lt;/p&gt;

&lt;p&gt;Let's say you'd like to do image processing with Java's &lt;tt&gt;ImageIO&lt;/tt&gt; class. This could be accomplished with RJB via:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;rubygems&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;require_gem&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;rjb&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;rjb&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;

&lt;span class="attribute"&gt;@ImageIO&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Rjb&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="ident"&gt;import&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;javax.imageio.ImageIO&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;

&lt;span class="attribute"&gt;@ImageIO&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;getReaderFormatNames&lt;/span&gt; &lt;span class="comment"&gt;# =&amp;gt; an array of format names&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, the instance variable &lt;tt&gt;ImageIO&lt;/tt&gt; holds a reference to the Java class of the same name. This isn't too bad, but can we do better?&lt;/p&gt;

&lt;p&gt;Imagine a situation in which many Java classes need to be imported. This would lead to many variable assignments like the one above. The resulting duplications of Java class names and variable names &lt;a href="http://www.dcs.gla.ac.uk/~hcp/psd/lectures/badsmells.pdf"&gt;doesn't smell especially good&lt;/a&gt;, nor does it scale well. In addition, the large number of these variable assignments would add a lot of mental overhead when reading and writing the code.&lt;/p&gt;

&lt;p&gt;What we'd really like is if Ruby had a way for us to map a Java package/class hierarchy onto Ruby module/class hierarchy. We could then forget about the differences between Ruby and Java, and just get to work. For example:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="comment"&gt;#...&lt;/span&gt;

&lt;span class="ident"&gt;jrequire&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;javax.imageio.ImageIO&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;

&lt;span class="comment"&gt;#...&lt;/span&gt;

&lt;span class="constant"&gt;Javax&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Imageio&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;ImageIO&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;getReaderFormatNames&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;A Solution&lt;/h4&gt;

&lt;p&gt;Our solution will involve translating nested Java package/class constructs into nested Ruby module/class constructs at runtime. We'll need the ability to create new module hierarchies in running code, one of the problems metaprogramming solves. We'll also have to deal with capitalization: Java package names are all lowercase, but Ruby module names start with a capital letter. So &lt;tt&gt;java.lang.System&lt;/tt&gt; will become &lt;tt&gt;Java::Lang::System&lt;/tt&gt;. By mapping the Java package namespace onto the Ruby module namespace, we'll reduce the odds of creating a Ruby/Java class name collision, such as with &lt;tt&gt;java.lang.String&lt;/tt&gt;.&lt;/p&gt;

&lt;p&gt;Let's create a small library to illustrate these points. The code will take advantage of Ruby's &lt;tt&gt;const_set&lt;/tt&gt; method, which allows new constants (and therefore new modules and classes) to be defined at runtime. Save the following code into a file called &lt;strong&gt;java.rb&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;rubygems&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;require_gem&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;rjb&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;rjb&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;

&lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;Kernel&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;jrequire&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;qualified_class_name&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;java_class&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Rjb&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="ident"&gt;import&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;qualified_class_name&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;package_names&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;qualified_class_name&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;split&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;.&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;
    &lt;span class="ident"&gt;java_class_name&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;package_names&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;delete&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;package_names&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;last&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;new_module&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="keyword"&gt;class

    &lt;/span&gt;&lt;span class="class"&gt;package_names.each&lt;/span&gt; &lt;span class="ident"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;package_name&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;module_name&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;package_name&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;capitalize&lt;/span&gt;

      &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="punct"&gt;!&lt;/span&gt;&lt;span class="ident"&gt;new_module&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;const_defined?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;module_name&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="ident"&gt;new_module&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;new_module&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;const_set&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;module_name&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;Module&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="keyword"&gt;else&lt;/span&gt;
        &lt;span class="ident"&gt;new_module&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;new_module&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;const_get&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;module_name&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;new_module&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;const_defined?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;java_class_name&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

    &lt;span class="ident"&gt;new_module&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;const_set&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;java_class_name&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;java_class&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

    &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Usage&lt;/h4&gt;

&lt;p&gt;Using this library consists of &lt;tt&gt;requiring&lt;/tt&gt; it, applying the new &lt;tt&gt;jrequire&lt;/tt&gt; command, and manipulating the resulting class:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;jrequire&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;javax.imageio.ImageIO&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;

&lt;span class="constant"&gt;Javax&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Imageio&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;ImageIO&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;getReaderFormatNames&lt;/span&gt; &lt;span class="comment"&gt;# =&amp;gt; [&amp;quot;BMP&amp;quot;, &amp;quot;jpeg&amp;quot;, &amp;quot;bmp&amp;quot;, &amp;quot;wbmp&amp;quot;, &amp;quot;gif&amp;quot;, &amp;quot;JPG&amp;quot;, &amp;quot;png&amp;quot;, &amp;quot;jpg&amp;quot;, &amp;quot;WBMP&amp;quot;, &amp;quot;JPEG&amp;quot;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can eliminate the need to use the fully-qualified module name by adding an &lt;tt&gt;include&lt;/tt&gt; statement, just as you would with any other Ruby module:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;jrequire&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;javax.imageio.ImageIO&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;include&lt;/span&gt; &lt;span class="constant"&gt;Javax&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Imageio&lt;/span&gt;

&lt;span class="constant"&gt;ImageIO&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;getReaderFormatNames&lt;/span&gt; &lt;span class="comment"&gt;# =&amp;gt; [&amp;quot;BMP&amp;quot;, &amp;quot;jpeg&amp;quot;, &amp;quot;bmp&amp;quot;, &amp;quot;wbmp&amp;quot;, &amp;quot;gif&amp;quot;, &amp;quot;JPG&amp;quot;, &amp;quot;png&amp;quot;, &amp;quot;jpg&amp;quot;, &amp;quot;WBMP&amp;quot;, &amp;quot;JPEG&amp;quot;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;One Possible Variation&lt;/h4&gt;

&lt;p&gt;An interesting variation on the approach given here would be to override Ruby's &lt;tt&gt;require&lt;/tt&gt; method itself to accept fully-qualified Java class names. Then something even more Rubyesque could be used:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;javax/imageio/ImageIO&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;

&lt;span class="constant"&gt;Javax&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Imageio&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;ImageIO&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;getReaderFormatNames&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Other Examples of Ruby-Java Metaprogramming&lt;/h4&gt;

&lt;p&gt;&lt;a href="http://ola-bini.blogspot.com/"&gt;Ola Bini&lt;/a&gt; has written an article on JRuby metaprogramming that takes &lt;a href="http://ola-bini.blogspot.com/2006/10/jruby-import.html"&gt;a slightly different approach&lt;/a&gt; than the one detailed here.&lt;/p&gt;

&lt;h4&gt;Conclusions&lt;/h4&gt;

&lt;p&gt;This tutorial has shown a simple and practical application of Ruby's built-in metaprogramming capabilities. The careful use of metaprogamming is a powerful way to reduce code complexity and build a more consistent programming environment. Look for more metaprogramming techniques to appear in future releases of the &lt;a href="http://depth-first.com/articles/2006/09/25/cdk-the-ruby-way-rcdk-0-2-0"&gt;Ruby CDK&lt;/a&gt; library.&lt;/p&gt;</description>
      <pubDate>Tue, 24 Oct 2006 14:11:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:db2201c3-26d3-462c-9b21-357888d87876</guid>
      <author>Rich Apodaca</author>
      <link>http://depth-first.com/articles/2006/10/24/metaprogramming-with-ruby-mapping-java-packages-onto-ruby-modules</link>
      <category>Tools</category>
      <category>metaprogramming</category>
      <category>ruby</category>
      <category>java</category>
      <category>rjb</category>
    </item>
  </channel>
</rss>
