Jekyll: Category and Tag Paging and Feeds

Jekyll is a very popular and very powerful static blog generator. Out of the box it’s able to generate sophisticated site structures, and has a ton of configurability. One of the areas where I feel that Jekyll lacks some sophistication is around the handling of categories and tags; these are two data-sets that are core to Jekyll, but there isn’t a lot of functionality actually built around them. This is in contrast to dynamic blogging platforms like WordPress, or possibly Drupal, where these two data points are used to drive a lot of central navigation for the site.

To be fair, Jekyll is really intended to be a framework for expansion into larger degrees of customization and sophistication, and thankfully it has a very powerful plugin model. Higher-level frameworks like Octopress and Jekyll Bootstrap have shown what you can do with a little extra tweaking - as have the long list of Jekyll plugins.

When I set out to move my site over to Jekyll, one of my key goals was to still support all of the key navigation my site was capable of with my custom platform code, and Wordpress before it. That pretty much amounts to:

  • A date descending paging root for all blog entries /index.html.
  • A matching Atom feed for the root index.
  • Static pages like /about.html and /contact.html
  • Individual blog pages (I suppose this one is obvious).
  • Date desceding paging indexes for all categories and tags I use (for example: /category/article/ and /tag/jruby/.
  • Matching atom feeds for each of the paging indexes above (for example: /category/article/atom.xml and /tag/jruby/atom.xml.

It was the last two where I hit a hurdle when converting over. Jekyll simply doesn’t have built-in support for this. What surprised me was that I couldn’t find an open source plugin that did it either. The closest I found was over at marran.com, where Keith Marran built category pagination using a Jekyll plugin. It wasn’t exactly what I was looking for, but by far the closest - so props to the author for leading me down the path to understanding a little better.

So long story short, I chose to build my own - and it turns out to be quite simple to do, with a significant amount of added navigation as the end result.

Let’s recap a bit what we want to achieve:

  • A page in the form /category/[category name]/index.html for every category, with a Jekyll style paginator available to the page at render time.
  • Subsequent pages in the form /category/[category name]/pageN/index.html for every extra page of entries for that category, again with a paginator available at render time.
  • A top-ten feed list for the category at /category/[category name]/atom.xml.
  • Similar support for every tag via /tag/[tag name]/index.html and /tag/[tag name]/pageN/index.html.

The first step to generating new pages that Jekyll will output is to extend the Jekyll Generator class. We can do this in the Jekyll module so we have access to all of the context we need:

1module Jekyll
2  class CatsAndTags < Generator
3    def generate(site)
4      # Generate pages here!
5    end
6  end
7end

Next, we simply need to orient ourselves and actually iterate over what we want to generate. Turns out that’s quite easy - the passed in site object has everything we need:

 1def generate(site)
 2  site.categories.each do |category|
 3    build_subpages(site, "category", category)
 4  end
 5
 6  site.tags.each do |tag|
 7    build_subpages(site, "tag", tag)
 8  end
 9end
10
11# Do the actual generation.
12def build_subpages(site, type, posts)
13  posts[1] = posts[1].sort_by { |p| -p.date.to_f }     
14  atomize(site, type, posts)
15  paginate(site, type, posts)
16end
17
18def atomize(site, type, posts)
19  # TODO
20end
21
22def paginate(site, type, posts)
23  # TODO
24end

So here you can see we’re iterating over all of the sites categories and tags, and for each one calling the new method build_subpages. What the site actually has on it for each category and tag is a two-position array, with position 0 being the category or tag name, and position 1 being the posts associated with that category or tag.

Now we can narrow down into what the subpage generation actually looks like - as you can see we have three main things we do: sort the pages by descending date, call atomize, and then call paginate.

Creating the atom pages is a little bit easier, so we’ll start there:

 1def atomize(site, type, posts)
 2  path = "/#{type}/#{posts[0]}"
 3  atom = AtomPage.new(site, site.source, path, type, posts[0], posts[1])
 4  site.pages << atom
 5end
 6
 7class AtomPage < Page
 8  def initialize(site, base, dir, type, val, posts)
 9    @site = site
10    @base = base
11    @dir = dir
12    @name = 'atom.xml'
13
14    self.process(@name)
15    self.read_yaml(File.join(base, '_layouts'), "group_atom.xml")
16    self.data[type] = val
17    self.data["grouptype"] = type
18    self.data["posts"] = posts[0..9]
19  end
20end

Let’s break this down:

  • We generate a path for the new page based on the passed in type, and position-0 of the posts array. This will generate our /category/[cat name] and /tag/[tag name] as desired.
  • We create a new AtomPage, which is a custom class we’ll get to momentarily.
  • We add the new atom page to the site’s pages collection.

The custom atom page we have created has a few specific goals. Since it’s not backed by an actual file (like most Jekyll pages would be), we need to mix and match pieces and parts to generate our page into existence:

  • The file name is hard-coded (if you were to use/extend this you may wish to change this or pull it from the site config)
  • The YAML front-matter is read from a layout file we expect to find at _layouts/group_atom.xml (again, the location of this atom layout could be something that comes from the site config if you desire).
  • We bind the tag or category name (val) to the page’s data hash, so the page source can know what it actually represents.
  • We bind the type of group we’re dealing with (“category” or “tag”) to the grouptype data element.
  • We bind the actual list of posts we want to render to the output - in this case the first 10 items in the list. How many this actually renders could also come from the site configuration if desired.

Now we just need to look at the actual atom layout page itself - group_atom.xml:

 1{% raw %}
 2---
 3title: nil
 4---
 5<?xml version="1.0" encoding="UTF-8" ?>
 6
 7<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
 8  <channel>
 9   {% if page.grouptype == 'tag' %}
10   	<title>RealJenius.com - Tag: {{page.tag}}</title>
11   {% elsif page.grouptype == 'category' %}
12    <title>RealJenius.com - Category: {{page.category}}</title>
13   {% else %}
14    <title>RealJenius.com</title>
15   {% endif %}
16   <link>http://realjenius.com</link>
17   <description>I'm a software developer in the game industry, and have been (for better or worse) coding on the Java platform for the last decade. I also do all my own stunts.</description>
18   <language>en-us</language>
19   <managingEditor>R.J. Lorimer</managingEditor>
20   <atom:link href="rss" rel="self" type="application/rss+xml" />
21
22    {% for post in page.posts %}
23	  <item>
24        <title>{{ post.title }}</title>
25        <link>http://realjenius.com{{ post.url }}</link>
26		<author>R.J. Lorimer</author>
27		<pubDate>{{ post.date | date_to_xmlschema }}</pubDate>
28		<guid>http://realjenius.com{{ post.url }}</guid>
29		<description><![CDATA[
30		   {{ post.content | expand_urls : site.url }}
31		]]></description>
32	  </item>
33    {% endfor %}
34  </channel>
35</rss>
36{% endraw %}

(Yes, I know this is RSS 2.0 and not Atom 1.0 - so sue me. I plan to fix it some day!)

In this case, all we do is layout the main feed layout, and then iterate over the posts attached to the data set and print out item content for each.

Note that the title is switched based on the value of the page.grouptype element.

Note: This snippet uses the expand_urls Liquid filter, which is a custom filter to make URLs absolute in the body of the post since feeds are not rendered on your site, so relative links won’t work. I’m not going to go into detail about it here, but it’s available in my site source, and also available in a varied form in the Octopress platform.

So that’s it for generating our feeds for every category and tag - not too tricky! Next, we need to look at how we do the paged indexes in the paginate method above:

 1def paginate(site, type, posts)
 2  pages = Pager.calculate_pages(posts[1], site.config['paginate'].to_i)
 3  (1..pages).each do |num_page|
 4    pager = Pager.new(site.config, num_page, posts[1], pages)
 5    path = "/#{type}/#{posts[0]}"
 6    if num_page > 1
 7      path = path + "/page#{num_page}"
 8    end
 9    newpage = GroupSubPage.new(site, site.source, path, type, posts[0])
10    newpage.pager = pager
11    site.pages << newpage
12
13  end
14end
15
16class GroupSubPage < Page
17	def initialize(site, base, dir, type, val)
18	  @site = site
19	  @base = base
20	  @dir = dir
21	  @name = 'index.html'
22
23	  self.process(@name)
24	  self.read_yaml(File.join(base, '_layouts'), "group_index.html")
25	  self.data[type] = val
26	end
27end

This is borrowed closely from Keith Marran’s plugin. Basically it does this:

  • Ask the paging system to count our pages based on the core pagination configuration value.
  • For each page build a new Pager object and calculate a base path.
  • For all pages but the base page, append an additional element to the path so we get that pageN in the URL.
  • All sub-pages will be called “index.html”, and will use the file in the _layouts folder with the name group_index.html (look familiar to the atom stuff?? It should!)

I won’t bore you with a full wall of HTML for my article list, but here are the relevant bits for doing the output in group_index.html.

 1{% raw %}
 2{% if page.grouptype == 'tag' %}
 3 <!-- Print tag title here -->
 4{% elsif page.grouptype == 'category' %}
 5 <!-- Print category title here -->
 6{% endif %}
 7
 8{% for post in paginator.posts %}
 9  <!-- Print post entry short stuff -->
10{% endfor %}
11
12{% if paginator.next_page %}
13  <!-- Render next page link -->
14{% endif %}
15
16{% if paginator.previous_page %}
17  <!-- Render previous page link -->
18{% endif %}
19
20{% endraw %}

If you’re familiar with doing a normal paginated index in Jekyll this should look pretty familiar; all of the mechanics are identical at the Liquid+HTML level.

That’s about it for the custom page generation. Not too bad!

If you’d like to see the implementation I use on my actual size, you can see it over on GitHub here: RealJenius.com - cat_and_tag_generator.rb.

jekyll  ruby  site 

Clean it Up: Article Series List With Jekyll, Part 2

Previously I showed how you could build an article series list with Jekyll by scrapping together some Liquid scriptlets and some clever looping. The implementation certainly works, but it’s a little bit ugly, inefficient, and hard to maintain. The main goal was to see how far we could stress it using only Liquid.

This time I’d like to show how you could achieve the same by actually implementing a proper Liquid tag; implementing some Ruby code to achieve the same goal.

There are a lot of ways you can extend Jekyll, including custom generators, filters, and converters. Another way is to add custom tags to Liquid, since it’s the foundation for Jekyll. In fact, Jekyll adds a couple out of the box, like the include and highlight tags.

We’ll create a new tag called series, and the end result would be that we can simply put it in our article like this:

Welcome to my article about Fish in the United States. This is the first entry in a series about fish throughout the world!

{% raw %}
{% series_list %}
{% endraw %}

Obesity rates in the Fish in US have hit epidemic proportions...

In practice, not a whole lot different than what we had in part one, but the code will be oh-so-much-more-rewarding. So how do we get there?

First, we need to implement a new Jekyll tag - we’ll start by getting all of the declaration boiler-plate out of the way:

 1module Jekyll
 2
 3  class SeriesTag < Liquid::Tag
 4    def initialize(tag_name, params, tokens)
 5      super
 6    end
 7
 8    def render(context)
 9      ""
10    end
11  end
12end
13
14Liquid::Template.register_tag('series_list', Jekyll::SeriesTag)

We can put this new class definition in our Jekyll _plugins folder as _plugins/series_tag.rb. In reality, you can put any Ruby code in the plugins folder that you want to load on Jekyll page-generation time; in practice it’s going to extend Jekyll in some way, or there probably isn’t much point to having it.

All we’re doing here is creating a new Liquid::Tag, and registering it in Liquid as series_list. The render method is expected to generate replacement markup that will be processed by the next stages of the generation process (markdown/textile, and then final HTML output).

Now we need to figure out how to get a handle on a few things:

  • Our current page so we know which post we’re currently rendering.
  • The series we’re supposed to be rendering.
  • All posts for the site, so we can generate the list.

Here’s what that looks like:

1site = context.registers[:site]
2page_data = context.environments.first["page"]
3series_name = page_data['series']
4if !series_name
5	puts "Unable to find series name for page: #{page.title}"
6    return "<!-- Error with series tag -->"
7end

We get the site object out of the “context.registers” hash. The page is more cryptic - in this case we’re collecting the page data out of the environments, and then simply fetch the series off of it. There isn’t a lot of documentation about what is available where, but there are some good examples on the web already, and you always have access to the Jekyll source!

We also do a simple series value-set check, and fail fast if it’s not there.

Next, we need to get our filtered list of posts, and make sure they’re ordered as we need:

1all_entries = []
2  site.posts.each do |p|
3    if p.data['series'] == series_name
4      all_entries << p
5    end
6  end
7
8  all_entries.sort_by { |p| p.date.to_f }
9

Now that we have the subset of posts as well as our current post, it’s really just a matter of looping and rendering a bunch of HTML:

 1text = "<div class='seriesNote'>"
 2  list = "<ul>"
 3  all_entries.each_with_index do |post, idx|
 4    list += "<li><strong>Part #{idx+1}</strong> - "
 5    if post.data['title'] == page_data['title']
 6      list += "This Article"
 7      text += "<p>This article is <strong>Part #{idx+1}</strong> in a <strong>#{all_entries.size}-Part</strong> Series.</p>"
 8    else
 9      list += "<a href='#{post.url}'>#{post.data['title']}</a>"
10    end
11    list += "</li>"
12  end
13  text += list += "</ul></div>"

And that’s it! We now have our finished HTML generating series list, fresh from Ruby code instead of a bunch of Liquid scripts.

You can get the entire tag by forking or downloading from Github.

jekyll  ruby 

Dirty Tricks: Building an Article Series List With Jekyll

Jekyll is one of the most popular “static blogging” tools available right now, and is the foundation for a number of popular tools at a more sophisticated level, including OctoPress, and Jekyll Bootstrap. Since the end result of a built Jekyll site is plain, vanilla HTML, it allows for fairly complex sites to be built with a bare minimum of hosting requirements, and it’s also pretty easy to secure and make perform in the process!

That said, there are some features Jekyll just doesn’t have that big dynamic content management systems do; but that doesn’t mean they can’t be built. For the more sophisticated enhancements, you will likely need to look at implementing custom Jekyll plugins, tags, and filters – but for some features, you can get away with wrangling Liquid scripts into the shape you need. Liquid is a templating engine, and like many templating engines, it has a little bit of programming support mixed in with its ability to generate dynamic markup, and sometimes you can leverage that to hit your goal.

One of the features that RealJenius.com has is a “series” list - you can see this on my Distilling JRuby articles:

An article count, an ordered list, and links to all neighbors.

An article count, an ordered list, and links to all neighbors.

Obviously, if you’re running a site with server code behind it, you can simply fetch by an index in the database, or possibly iterate over relevant entries in memory, or something similar when you’re rendering the blog entry, and easily generate something like this. But if you’re generating a blog at build time, how can you fill this in?

It turns out it’s not all that different in Jekyll. One of the things Jekyll makes available at the global level when performing blog generation is a list of all posts, ordered by their post time in descending order, and with those you can get all sorts of data about the post. From that, I imagine you can probably see how to do this with a plugin to Jekyll; just:

  • Iterate the post list with some Ruby code
  • Pluck out the right entries
  • Sort them
  • … and render some HTML

But let’s say we want to take the role of “site designer”, and not write any Ruby code. How can we translate this raw list of posts into a series list, with all of the information above?

First off we need to identify the posts. When you write a post in Jekyll, you have to fill in the YAML front-matter. It’s just a block of YAML leading the entry that has property-like details for the post, like the title, the page layout to use, etc. You can add any custom fields you want as well. So let’s tag our articles as belonging to a particular series:

 1---
 2title: The Fish of the United States
 3layout: post
 4series: fish-series
 5---
 6{% endhighlight %}
 7
 8Next, we need to create a reusable chunk of Liquid+HTML for the logic. We can simply use an include in the `_includes` directory for this:
 9
10**`_includes/series.html`**:
11
12```html
13<div class="seriesNote">
14<!-- A series block will go here -->
15</div>

Now, in our article, we can simply include the series like this:

Welcome to my article about Fish in the United States. This is the first entry in a series about fish throughout the world!

{% raw %}
{% include series.html %}
{% endraw %}

Obesity rates in the Fish in US have hit epidemic proportions...

Finally, we just need to implement our series block. There are a number of things we need to render the example above:

  • A count of all of the articles.
  • An index for the current article.
  • A list of posts in chronological order for the series.
  • The URL for each post.

Here is a big wall of hack-y liquid tags to achieve just that.

 1{% raw %}
 2{% assign count = '0' %}
 3{% assign idx = '0' %}
 4{% for post in site.posts reversed %}
 5	{% if post.series == series %}
 6		{% capture count %}{{ count | plus: '1' }}{% endcapture %}
 7		{% if post.url == page.url %}
 8			{% capture idx %}{{count}}{% endcapture %}
 9		{% endif %}
10	{% endif %}
11{% endfor %}
12
13<div class="seriesNote">
14	<p>This article is <strong>Part {{ idx }}</strong> in a <strong>{{ count }}-Part</strong> Series.</p>
15	<ul>
16	{% assign count = '0' %}
17	{% for post in site.posts reversed %}
18	{% if post.series == series %}
19		{% capture count %}{{ count | plus: '1' }}{% endcapture %}
20		<li>Part {{ count }} -
21		{% if page.url == post.url %}
22			This Article
23		{% else %}
24			<a href="{{post.url}}">{{post.title}}</a>
25		{% endif %}
26		</li>
27	{% endif %}
28	{% endfor %}
29	</ul>
30</div>
31
32{% assign count = nil %}
33{% assign idx = nil %}
34{% endraw %}

You can download or fork this source directly from Github.

Let’s work through this. The script is roughly broken into three parts:

  1. The first part iterates over the total post list counting all entries that match the series key, and also finds the index of the post.
  2. The second part generates the series summary text, and then iterates over the total post list again, generating links for each post.
  3. Finally will clear out any variables we used since they all float around in a global namespace.

A few interesting things to note:

  • Properly incrementing and tracking a count when you’re doing filtering in a for loop is pretty nasty in liquid. This is done by using the “plus” filter and recapturing the value: {% raw %}{% capture count %}{{ count | plus: '1' }}{% endcapture %}{% endraw %}. This is effectively like saying count = count + 1.
  • We use the special for loop keyword reversed because the posts in Liquid are reverse-chronological, and we want to list them in true chronological order.
  • We have to iterate twice because the series summary text comes before the actual list, and there aren’t any liquid constructs (that I know of) for creating a new array off of the first iteration.

And that’s it! Could this be done cleaner and probably easier via some judicious use of Jekyll plugins? Absolutely! And what would be the fun of that?

jekyll  ruby  site  hacks 

Distilling Mirah: Type Inference

Recently, I’ve been watching the work of a handful of developers on a new programming language: Mirah. As a fan of the Ruby programming language and a slave to the Java overlords, Mirah offers instant appeal to me, as it borrows the core Ruby syntax almost verbatim, but creates true Java class files runnable on any Java VM. This decision makes perfect sense, considering the originator of Mirah is Charlie Nutter, one of the key developers of JRuby, a language heavily invested in both Ruby and Java. (Mirah actually reuses the JRuby parser at the time of this writing, if that gives you any indicator how similar the syntax is).

Because of my interest in the development of Mirah, I’ve decided to begin spelunking into the implementation as it stands today, sharing with you what is going on internally. Many of you are probably familiar with my “Distilling JRuby” series, and while these articles will likely read similarly, I suspect they will be more brief and hand-wavy. This is partially out of a desire to cover more topics over a short period of time, but also because the implementation for Mirah is very fluid, and is likely to change, rendering these articles invalid or at least out-dated.

Without further ado - let’s kick this pig. On to Mirah’s type-inferencing!

Article Series

  1. Distilling Mirah: Type Inference

Mirah Overview

There are a few key concepts that need to be discussed regarding Mirah before we get started:

  • Mirah is not Ruby! Mirah looks like Ruby at first glance, but that is only superficial in nature. We will see why over the next series of topics.
  • Unlike JRuby, Mirah is not implemented in Java (well, mostly not). It is actually implemented in Ruby - this is going to make the way we traverse the code in these articles very different than the JRuby series.
  • While I say that Mirah borrows the Ruby syntax, it has to modify and add certain structures to fit the mold which has been carved for it. So while it is possible to write some programs that are almost true Ruby syntax, most Mirah programs will have a few extra sprinkles.
  • Mirah is statically typed, and compiles to standard Java bytecode. This is one of the key reasons that Mirah is not 100% Ruby-grammar compatible.
  • Mirah is designed from the ground up to be a language specification that can be implemented on several platforms (.NET is a perfect example). This introduces indirection in the code that may, at first, seem confusing.
  • One of the key principals of Mirah is to avoid runtime encumbrances if at all possible. What this means is that all features in Mirah as it currently stands are implemented by either a compiler plug-in, or by using existing core libraries of the underlying platform (or a combination, of course). This goal is to hopefully avoid the 3-5 MB ball-and-chain that many languages (i.e. Scala, Clojure, JRuby) hang around your neck to run deployed code. The idea being that, if you want runtime features, you can bring Mirah modules in per your own decision, but if you want to fit a Mirah program on a micro-controller that can run Java bytecode (or Dalvik cough), you should be able to by forgoing some of those features that require large runtime libraries.

The Mirah site can be found at http://www.mirah.org, and the official Mirah ‘master’ repo is available at github: http://github.com/mirah/mirah. Feel free to checkout and follow along, although one last disclaimer - the code is changing quickly, so my articles are bound to fall out of the times.

I’d suggest before proceeding you familiarize yourself with the language syntax - I don’t plan to stop along the way.

A Background on Type Inference

Most JVM language replacements that are garnering attention right now in one way or another avoid explicit typing in the syntax to some degree. Languages that are compiled to normal byte-code with some degree of implicit typing, must involve some form of type inference. This is the process of statically analyzing code to determine the runtime types the code is using by inferring from the use of variables and parameters. Statically compiled languages on the VM must do this, because Java bytecode (and the VM) expects to work with types - and if the compiler can’t figure it out, it can’t compile the bytecode.

Consider this Java statement:

1HashMap<String,String> myMap = new HashMap<String,String>();

There is really no reason you need to define the left-hand side (the declaration) so explicitly, considering that the right-hand side (the assignment) has already told you exactly what the variable is. Surely this should be sufficient:

1var myMap = new HashMap<String,String>();

Anyone familiar with C# will likely recognize this syntax convenience. Of course, this is simple example, because you only have to examine this one line to infer the variable type. Things get much more complex when there are control structures, methods, and other language features in the way.

That being said, type inferencing is a well-tread path - it’s certainly not unique to JVM languages; far from it. There are different levels of type inference, with the most complete often using something like Hindley-Milner to deduce types recursively (excellent description of Hindley-Milner by Daniel Spiewak on his blog).

Mirah’s Type Inferencing

As it stands today, Mirah currently implements a variant of type inference somewhere between true “local” type inference, and fully recursive type inference like Hindley-Milner. Mirah’s inference uses a multi-pass infer process, where the first phase does simple local inference (or line-by-line inference), and then subsequent passes are made, looking for new type resolutions from those higher constructs. For example, consider these two Mirah methods:

1def method_a()
2  return method_b(5) + method_b(6)
3end
4
5def method_b(x:int)
6  return x * -1
7end

In this case, ‘method_a’ is obviously dependent upon ‘method_b’ - but if ‘method_a’ is inferred first, it will have no way to know what it’s return type is, because method_b hasn’t been inferred yet. In this case, ‘method_a’ is ‘deferred’ for a later inference pass. Shortly thereafter, ‘method_b’ will be processed, and since it can be completely analyzed through local inference, it will resolve to return an int. At that point, method_a can look at the two invocations that are involved in the return statement, and can in turn determine that it should also return an int.

The Algorithm

From an implementation standpoint, Mirah does this inference by utilizing the ASTs generated from the source. Each AST knows individually how to infer itself based on its recursive contents - this is something we’ll investigate in more detail shortly.

Mirah defines a namespace and class called Typer that is used to orchestrate this entire process. The Typer is asked to analyze each AST tree parsed by Mirah individually, and then to iteratively resolve:

1typer = Typer.new
2asts.each { |ast| typer.infer(ast) }
3typer.resolve

The infer method for an individual AST node is pretty straightforward:

1class Typer
2  def infer(node)
3    node.infer(self)
4    # error handling business
5  end
6end

Notice that the typer passes itself into the node - this allows the nodes to callback into the typer for a variety of reasons. For example, each node has to decide for itself whether or not it has enough information to infer. If it doesn’t, it will tell the typer that it needs to be ‘deferred’, meaning it doesn’t yet have enough information. All this effectively does is record the node for later:

1class Typer
2  def defer(node)
3    @deferred_nodes << node
4  end
5end

So the typer calls infer on the top level AST node, at which point the AST hierarchy will recurse, inferring and deferring nodes as appropriate. After the first recursive inference pass, the typer is then asked to resolve AST nodes iteratively until all nodes are inferred, or until no progress is made:

 1class Typer
 2  def resolve
 3    old_len = @deferred_nodes.length
 4    while true
 5      @deferred_nodes.each do |node|
 6        type = infer(node)
 7        if type != nil
 8          @deferred_nodes.remove(node)
 9        end
10      end
11
12      if @deferred_nodes.length == 0
13        break
14      elsif old_len == @deferred_nodes.length
15        raise # can't infer error!
16      end
17    end
18  end
19end

AST Working Together

Understanding the concept of the AST recursively inferring is the key component to understanding the typer. Consider, for example, the statement x = method_b(5) - this is represented by a tree of AST nodes. For those of you with experience in parsers, or experience with my previous JRuby articles, it probably won’t be too hard to derive the types of nodes involved - it’s basically this:

LocalDeclaration
|
.-- LocalAssignment (type_node)
    |
    .-- FunctionalCall (value)
        |
        .-- Fixnum (parameters)
            |
            .-- "5" (literal)

The idea is that the declaration will ask the assignment, which will in turn ask the call being made with the parameter types in play, and will then return the type of the call return type. Here is a sketch of the various infer methods for these nodes:

 1class LocalDeclaration
 2  def infer(typer)
 3    type = @type_node.infer(typer)  #type_node is the local assignment
 4    if(!type)
 5      typer.defer(self)
 6    end
 7    return type
 8  end
 9end
10
11class LocalAssignment
12  def infer(typer)
13    type = @value.infer(typer) #value is the "functional" call.
14    if(!type)
15      typer.defer(self)
16    end
17    return type
18  end
19end
20
21class FunctionalCall
22  def infer(typer)
23    @parameters.each { |param| param.infer(typer) }
24    if #all parameters inferred, and method with params and scope is known
25      return typer.method_type(@method_name, method_scope, @parameters)
26    else
27       typer.defer(self)
28       return nil
29    end
30  end
31end
32
33class FixNum
34  def infer(typer)
35    return typer.fixnum_type(@literal) #literal is '5'
36  end
37end

A few things to note here:

  • This is totally pseudo code - the actual code has all kinds of branches for caching and other good bits.
  • The one literal we have, Fixnum, calls back into the typer to get the actual fixnum type - we’ll see this come in to play momentarily.
  • The typer has the ability to look up a method type by a signature - when methods are scanned during type inference, they record themselves in the typer for other nodes, like this one, to use when inferring since they are one case of node “hopping”, where one AST can be linked to another by reference.
  • We’re dodging how the functional call determines things like ‘method scope’ for now.

Resolving Literals

As noted above, the Fixnum node is asking the typer to give it back a fixnum type. This is done for all of the literal types. It’s done this way so that the platform implementation (in this particular case, Java) can plug in a particular type. So in this particular case, the Java implementation, in the JVM::Types module, provides a FixnumLiteral that looks at the provided value, and determines where in the hierarchy it belongs (for you Java folks, that’s byte, short, int, long, etc). When asked to actually compile, these AST nodes actually know how to generate the ultra-fast JVM bytecode-ops for primitives.

Type Annotations

As seen in one of the snippets above, Mirah supports type definitions for places where typing is either required (due to a lack of inference) or desired (widening a type, for example). Forgoing the fact this is a contrived implementation for a moment, consider this method:

1import java.util.Map
2import java.util.HashMap
3class SomeClass
4  def singleton_map(a:string, b:string):Map
5    map = HashMap.new
6    map.put(a,b)
7    return map  
8  end
9end

Here we are declaring both variable types so we can control inputs, and then we are declaring the return type. The reason you might want to declare a return type like this is so that the compiled method doesn’t expose too narrow of an implementation. Remember, we’re compiling to Java class files here - so if the compiled type inferred that the method returned a HashMap, that is a contraint we may never be able to change in the future. By changing it to ‘Map’, we can adjust the API like we would in the Java world to avoid tying ourselves to an implementation. To see this in action, here’s the output from mirahc when asked to generate Java code for this with and without the return type:

 1// With:
 2public class SomeClass extends java.lang.Object {
 3  public java.util.Map singleton_map(java.lang.String a, java.lang.String b) {
 4    java.util.HashMap map = new java.util.HashMap();
 5    map.put(a, b);
 6    return map;
 7  }
 8}
 9
10// Without:
11public class SomeClass extends java.lang.Object {
12  public java.util.HashMap singleton_map(java.lang.String a, java.lang.String b) {
13    java.util.HashMap map = new java.util.HashMap();
14    map.put(a, b);
15    return map;
16  }
17}

Individual AST nodes know about these definitions (sometimes known as forced types), and will respect those over the corresponding inferred types. That’s not to say that it will just take them for granted; the type inference still occurs. In the example above, the method body is still inferred to ensure it returns a type that can be widened to ‘java.util.Map’ - otherwise the code will cause runtime errors in the VM. Here’s a snippet of the method definition AST analysis:

 1class MethodDefinition
 2  def infer(typer)
 3    forced_type = @return_type
 4    inferred_type = @body.infer(typer)
 5    actual_type = if forced_type.nil?
 6      inferred_type
 7    else
 8      forced_type
 9    end
10
11    if !actual_type.is_parent(inferred_type)
12      raise "inference error"
13    end
14    return actual_type
15  end
16end

The return_type field will be set by the parser if provided, and takes precedent so long as it’s still able to be used in place of the actual inferred type of the method body.

Uncovered Topics

So this was a quick spin through Mirah-land, but even for the inference engine, a lot was left on the table if you’d like to explore from here:

  • Finding “native” types (in this case, calls into and returning Java types)
  • Tracking class/method scope when inferring
  • Inferring against intrinsics (such as ‘+’, and ‘||')
  • Dealing with multi-node inference - several nodes, like ‘MethodDefinition’ are expected to infer several parts, including arguments, return type, throws types, etc. This increases the complexity of the implementation, but doesn’t have much impact on concept.
  • Superclasses, invocation of ‘super’, overriding, overloading, etc.
  • Framework vs. Implementation (i.e. JVM) Responsibilities

Stay tuned as the Mirah story unfolds!

JRuby and Sinatra in 2 Minutes

While at RubyMidwest I decided to explore Sinatra in more detail. I’ve spent a lot of time with Rails, and while I love it, there is something alluring about the simplicity of Sinatra (and, well… ooh shiny). Being a recovering Java developer (Hi, I’m R.J., and I haven’t developed in Java for 18 hours) I have a server that runs Java, and would like to be able to use Sinatra to build my fancy-awesome web-apps. On those lines, I want all of the shiny benefits of JRuby’s multi-threading awesome-ness, as opposed to just trying to use WEBrick, which does not a powerful server make. So here is a 2 minute tutorial (well, depending on the performance of your computer, and how fast you type) startup with Sinatra, JRuby, Bundler, and Glassfish

I’m cheating already by assuming you already have JRuby installed as your default Ruby installation. No? Go get it!

Next step is to get bundler:

1gem install bundler

Now we need to make a home for our application, and prep it for Bundler:

1mkdir testapp
2cd testapp
3edit Gemfile

Here I’m creating a new file in testapp called ‘Gemfile’ in your favorite editor. This is where we will sketch out our dependencies for Bundler to do all the hard work for us - here are the contents for this example:

1source :rubygems
2gem "sinatra"
3gem "glassfish"

Frankly, that’s it. We tell Bundler to look for gems in RubyGems core repo, and then we ask it to make sure we have Sinatra and Glassfish. Now we can create the program - create the file ‘hello.rb’, and use these contents:

1require "rubygems"
2require "bundler"
3Bundler.setup
4
5require "sinatra"
6
7get '/hi' do
8	"Hello World!"
9end

So what’s special for JRuby? Absolutely nothing. We do have special sauce for Bundler, (by calling Bundler.setup prior to the require for ‘sinatra’) but trust me - you’ll be happy you used it. You’ll also make @wycats happy.

And - that’s it! Now, if you were to start this file the standard (well, bundler-standard) way, we’ll see this:

1realjenius$ bundle exec hello.rb
2== Sinatra/1.0 has taken the stage on 4567 for development with backup from WEBrick
3[2010-07-17 11:24:46] INFO  WEBrick 1.3.1
4[2010-07-17 11:24:46] INFO  ruby 1.8.7 (2010-06-06) [java]
5[2010-07-17 11:24:46] INFO  WEBrick::HTTPServer#start: pid=44490 port=4567

…and we can visit this URL: http://localhost:4567/hi. But, recall that our goal was to work with Glassfish, not WEBrick. All that has to change (and for folks who has done Glassfish/Rails before, this won’t be a surprise) is to run this startup instead

1realjenius$ bundle exec glassfish
2Log file /Users/realjenius/Projects/testapp/log/development.log does not exist. Creating a new one...
3Starting GlassFish server at: 0.0.0.0:3000 in development environment...
4Writing log messages to: /Users/realjenius/Projects/testapp/log/development.log.
5Press Ctrl+C to stop.
6
7Running sinatra

This time, we’ll visit this URL: http://localhost:3000/hi, and if all worked as desired, Sinatra will be crooning away. Boom goes the dynamite.

JRuby "IO.foreach" Performance

/img/articles/jruby/logo.png

I’ve been spending some time dipping my toes in patch contribution for JRuby recently. I started with a few easy, isolated, spec issues, and have since been working my way into more entrenched problems. The past few weeks I spent a good bit of time toying with solutions to JRUBY-2810: “IO foreach performance is slower than MRI”. The exercise was interesting enough, that I thought it might be worth posting here. This isn’t meant to be a study of the JRuby code in particular, but more-so in the thought process of diagnosing a performance problem in foreign code.

Proof is in the Benchmark

Performance is a very multi-faceted thing - there are so many measuring sticks (CPU, memory, I/O, startup time, scalability, ‘warm up’ time, etc). This makes quantifying a performance problem hard.

Furthermore, improvements for most performance problems typically involves making some kind of trade-off (unless you’re just dealing with bad code). The goal is to trade-off a largely-available resource for a sparse one (cache more in memory to save the CPU, or use non-blocking IO to use more CPU rather than waiting on the disk, etc).

JRuby always has a few open, standing performance bugs. It’s the nature of the beast that it is compared to MRI (the “reference” implementation), and anywhere it performs less favorably is going to be considered a bug (fast enough be damned). The performance measurement is up to the beholder, but CPU timings are generally the most popular.

JRUBY-2810 is an interesting case. IO line looping was proving to be slower than MRI Ruby; in some cases much slower. In this particular case, CPU was the closely-watched resource.

The first step I took to analyzing the problem was reproducing it. With Ruby this is usually pretty easy, as arbitrary scripts can just be picked up and executed, as opposed to Java, where all too often you have to build a special harness or test class just to expose the problem. Scripts are very natural for this, and in this particular case, the user had already provided one in the benchmarks folder that ships with the JRuby source.

Having run that file, I quickly saw the performance discrepancy reported in the bug. At this point in my experimenting, I was running inside an Ubuntu VM through VirtualBox on my Windows machine, so I think that level of indirection exasperated the numbers, so I checked my Macbook Pro as well. In both cases, the differences were significant: on Ubuntu, MRI Ruby was running the code in under 10 seconds, where JRuby was taking 30 seconds to a minute; the Macbook was still twice as slow in JRuby (12 seconds) as compared to MRI (6.5 seconds).

When faced with a big gap like this, I generally start by profiling. Running the entire process under analysis will generally grab some hotspots that need some tuning. I’m enamored with how low the barrier to entry on profiling has become on modern Java VMs (something that I think is actually a big selling point for JRuby as compared to other Ruby implementations; but I digress). To do my work here, I simply ran the benchmark, and popped open VisualVM. From there, I simply connected and performed CPU profiling (which automagically connects and injects profiling code into the running system).

In this particular case, the first problem was quickly uncovered:

Great Odin's Raven!

Great Odin's Raven!

Clearly, a very large amount of time is being spent in ByteList.grow. I felt fairly fortunate at this point, as rarely is it this straightforward; having a performance problem reported with this singular of a hot-spot. When nearly 80% of the processing time is spent in a single method, it brings up several questions: What is ByteList? Why does IO.foreach use it? Why must it keeping ‘growing’? Did I leave the iron on? To answer these questions (most of them, anyway) you simply have to get your feet wet in the code.

Coding for Crackers

At its heart, IO.foreach (and the close counterpart, each/each_line) is simply a line iterator that hands off each line of text off to a receiving block - there are a number of caveats and subtleties built into that idea, but at its core, it allows developers to write code like this:

1io = #...
2io.each_line do |line|
3  puts line
4end

Deceptively, simple - isn’t it? It turns out that a lot of wrangling has to occur to make this so simple - much of it having to do with how files are encoded, and the variety of line separators that may exist. Thankfully, the good folks at JRuby have cemented this in the code fairly decently - for my part, I mostly had to draw boxes around the complex encoding and line termination algorithms, and focus on the loop and data-reading itself. Most of this was occurring in a single method (for the code-eager, this was in RubyIO#getline and its derivatives). This method is used in a number of scenarios: the traditional looping algorithms, the 1.9 inverted enumerator stuff (handing the ownership of “next” off to the caller) as well as basic calls to ‘IO.gets’. Internally, each call to getline allocates a new ByteList and copies data from the input stream into it.

This is where the high-CPU numbers started. ByteList is simply an easier-to-use wrapper around a byte[]. It backs several JRuby data structures - the most notable probably being RubyString (the Java peer for String objects in JRuby). In fact, the ByteList allocated in this routine is eventually given to a String object, and returned at the end of the call. The ‘grow’ method on ByteList (the offending code-point) is the automatic capacity increase mechanism, and does this via an an array-allocation and copy (much like ArrayList); this method uses a fairly standard 1.5x grow factor.

It’s easy to see how ByteList would be central to the benchmark since it represents the primary data structure holding the bytes from the input source, but it seemed suspicious that ‘grow’ was the offending hotspot. I would expect it to be one of the copy methods, like ‘append’, which is really where the algorithm should be spending its time (that, and ‘read’ from the input source). To understand why ‘grow’ was so cranky, I had to look more closely at the code I was invoking: the benchmark.

Understanding the Benchmark

The original benchmark used to test the ‘foreach’ performance in JRuby when 2810 was first opened performed something like 10,000 line iterations on a file with relatively short lines. Halfway through the life of this bug, those values were adjusted in this original benchmark in a way that exposed a short-coming in the JRuby line-read routine - by generating only 10 lines that were very, very long instead.

For any Ruby implementation, reading a file with particularly long lines using foreach is prohibitively expensive, as the entire line has to be read into memory as a single string object that is then shared with the code block. Normally, you wouldn’t want to read data this way if you knew that the file was structured so wide, and should probably consider a streamed-read instead. That being said, MRI Ruby performed much more admirably in this scenario, so it was something to be analyzed.

The root of the problem was this: JRuby was starting with an empty ByteList, and was then creating subsequently larger byte[]s indirectly (via ByteList.grow) - the 1.5x factor wasn’t enough, as the chunks were being read 4k at a time, and these files were significantly wider than 4k. For that reason alone, the ByteList was having to grow a number of times for each line, and when we’re talking about a byte[] several kilobytes in size, array copies are simply going to be expensive - all those together combine to make this an unfriendly performance proposition.

As I mentioned previously, the benchmark used to be a very different performance model. I decided at this point it was good to split the benchmark so that both could be run side by side, and I could see both the ‘wide’ scenario and the ‘tall’ scenario at the same time. It turned out via profiling that the tall file was experiencing pains from ‘grow’, but not nearly so badly. Even at 10,000 lines the amount of adverse memory allocation and churn was much smaller, as a single 4k allocation on each line was more than sufficient.

For reference, here is what the ‘tall’ benchmark looks like:

 1require 'benchmark'
 2
 3MAX  = 1000
 4BLOCKSIZE = 16 * 1024
 5LINE_SIZE = 10
 6LINES = 10000
 7FILE = 'io_test_bench_file.txt'
 8
 9File.open(FILE, 'w'){ |fh|
10  LINES.times{ |n|
11    LINE_SIZE.times { |t|
12      fh.print "This is time: {t} "
13    }
14    fh.puts
15  }
16}
17
18stat = File.stat(FILE)
19(ARGV[0] || 5).to_i.times do
20  Benchmark.bm(30) do |x|
21    x.report('IO.foreach(file)'){
22      MAX.times{ IO.foreach(FILE){} }
23    }
24  end
25end
26
27File.delete(FILE) if File.exists?(FILE)

The only difference in the wide benchmark is the tuning parameters:

1LINE_SIZE = 10000
2LINES = 10

So ‘tall’ can be read as ‘10000 lines, 10 sentences long’, and ‘wide’ can be read as ‘10 lines, 10000 sentences long’.

Also for reference, here is what it looks like to run a benchmark using this framework - 5 iterations are run (as defined in the file), and the various aspects of CPU usage are measured. Generally, the most important number is the ‘real’ column when measuring performance between Ruby and JRuby, as the two report user/system CPU usage very differently.

 1# Running with JRuby
 2realjenius:~/projects/jruby/bench$ jruby --server bench_io_foreach_wide.rb
 3                                     user     system      total         real
 4IO.foreach(file)                63.970000   0.000000  63.970000 ( 63.764000)
 5                                     user     system      total         real
 6IO.foreach(file)                30.212000   0.000000  30.212000 ( 30.212000)
 7                                     user     system      total         real
 8IO.foreach(file)                30.973000   0.000000  30.973000 ( 30.973000)
 9                                     user     system      total         real
10IO.foreach(file)                30.768000   0.000000  30.768000 ( 30.767000)
11                                     user     system      total         real
12IO.foreach(file)                32.813000   0.000000  32.813000 ( 32.813000)
13
14#Running with MRI Ruby
15realjenius:~/projects/jruby/bench$ ruby bench_io_foreach_wide.rb
16                                     user     system      total         real
17IO.foreach(file)                 0.200000   9.500000   9.700000 (  9.982682)
18                                     user     system      total         real
19IO.foreach(file)                 0.230000   9.430000   9.660000 (  9.889992)
20                                     user     system      total         real
21IO.foreach(file)                 0.560000   9.340000   9.900000 ( 10.232858)
22                                     user     system      total         real
23IO.foreach(file)                 0.520000   9.270000   9.790000 ( 10.054699)
24                                     user     system      total         real
25IO.foreach(file)                 0.600000   9.350000   9.950000 ( 10.348258)

After splitting the benchmarks, here is a breakdown of my two configurations:

Environment ‘wide’ MRI ‘wide’ JRuby ‘tall’ MRI ‘tall’ JRuby
Ubuntu VM 10 seconds 30 seconds 6 seconds 11 seconds
Macbook Pro 6.5 seconds 12 seconds 8 seconds 15 seconds

Keep in mind I’m just rounding here; not really trying to be exact for this blog post. Check the bugs for more exact numbers.

A Solution Lurks

So, we have performance problems on tall files, and a whole lot more performance problems on wide files, particularly depending on the environment. Because of the environmental discrepencies, I spent some more time comparing the two test environments. It turned out that the Macbook Pro was simply working with a more resource-rich environment, and as such wasn’t hitting the wall as badly when allocating the new immense byte[]s. The implementation in JRuby was not degrading as well on older (or more restricted) hardware as MRI.

(It’s probably good to note here the value of testing in multiple environments, and from multiple angles)

My first pass at a solution to this problem was to consider a byte[] loan algorithm. Basically, at the start of foreach, I effectively allocated a single ByteList (byte[] container), and for each iteration of the loop, I just reused the same ByteList – eventually the byte[] being used internally would be sufficient to contain the data for each line, and would not need to grow any more (yay recycling!).

I encapsulated most of this ‘unsafe’ byte[] wrangling and copying into a small inner class called ByteListCache - at the start of the loop, the ByteListCache is created, and then it is shared for each iteration, being passed down into ‘getline’ as an optional parameter, the side effect being that the first call to ‘getline’ manually allocates a large byte[] (just like it did pre-patch), and each subsequent call can simply reuse the previously allocated byte[] that is already quite large. If the need arises to grow it more, it can, but it becomes increasingly less likely with each line.

Once the iteration is completed, the ByteListCache is dropped out of scope, ready for garbage collection. The number of calls to ‘grow’ drops dramatically with this implementation, and so did the impacts to the performance:

Environment ‘wide’ MRI ‘wide’ JRuby ‘wide’ JRuby (v1) ‘tall’ MRI ‘tall’ JRuby ‘tall’ JRuby (v1)
Ubuntu VM 10 seconds 30 seconds 7 seconds 6 seconds 11 seconds 8 seconds
Macbook Pro 6.5 seconds 12 seconds 7 seconds 8 seconds 15 seconds 9 seconds

Unfortunately, they were only this fast because the implementation was now thoroughly broken.

Stop Breaking Crap

Okay, so I had amazing performance numbers. Except. Now over 50 ruby spec tests were failing. Oh yeah, that might be a problem. Needless to say the problem was obvious the minute I realized what I had done (I actually woke up at 6:00am realizing this, which if you know me, is a bad sign). Remember how earlier I said that the ByteList was used as a backing store for the String? Well, at the time I implemented this, that point had eluded me. I was (accidentally) creating strings with my shared bytelist, so you can probably see where that would end up creating some significant issues with data integrity.

To fix this, the solution was simple - create a perfectly-sized ByteList at the end of the line-read the exact size necessary for the String, copying into it from the shared bytelist, and then passing it in to the String constructor. Obviously this cut into my performance numbers by a percentage on each, but it also fixed the data corruption, which is nice.

Environment ‘wide’ MRI ‘wide’ JRuby ‘wide’ JRuby (v2) ‘tall’ MRI ‘tall’ JRuby ‘tall’ JRuby (v2)
Ubuntu VM 10 seconds 30 seconds 14 seconds 6 seconds 11 seconds 10 seconds
Macbook Pro 6.5 seconds 12 seconds 10 seconds 8 seconds 15 seconds 13 seconds

The lesson learned here, obviously, is that you need to run a variety of tests (a full suite of specs if you have them) when considering bug fixes. For JRuby, that means (at a minimum) running the specs, which is easy with the Ant script:

1ant spec # or ant spec-short to just run interpreted tests

A Word on Limited Application

Note that I isolated the use of this construct to the foreach and each_line algorithms, as these had deterministic, single-threaded behavior, and would benefit from the overhead of dealing with this additional object. The new Ruby 1.9 enumerator stuff does not use it, as there is no guarantee of single-threaded usage of the enumerator, so we can’t reuse a single byte list. Similarly, individual calls to ‘gets’ do not currently use it, for the same general reason.

Changes could be made to make this byte[] re-use more long-lasting/global - but the changes required felt a little too overwhelming for a first pass, even if they did offer potentially larger benefits.

Rinse and Repeat

Now that I had two tests, and I had seen some improvements (but not quite in the range of MRI), it was time to revisit. Re-running the benchmarks, it was fascinating to see a new prime offender - incrementlineno. It turns out that a global variable was having to be updated through a very indirect routine that contains a fixnum representing the line number in the file, and all of this heavy-weight variable updating (going through call-sites and arg file lookups) was very expensive in comparison to the rest of the iteration.

At this point, I’d spend a lot of time explaining how I improved the performance of this little gem, however the truth be told once I hit this, I simply had to inform the powers-that-be, and back up. You see, I couldn’t figure out (for the life of me) why this method was doing what it was doing; why it was so important for this line number to be set. This is one of the perils that I have verbally discussed with folks about consuming foreign code-bases. You can’t assume secret sauce is a bad thing - I had to assume it is there for a reason, even if I don’t know what it is.

It turns out, the JRuby folks didn’t know the reason either. Well, that’s not exactly true; it didn’t take long for Charles Nutter to figure out why it was there, but it was clear it was only for rare file execution scenarios, and not appropriate for the more general looping scenarios I was debugging. To follow his efforts on how he optimized that code path, you can reference his commit here: JRUBY-4117.

After his optimizations, the numbers boosted again:

Environment ‘wide’ MRI ‘wide’ JRuby ‘wide’ JRuby (v3) ‘tall’ MRI ‘tall’ JRuby ‘tall’ JRuby (v3)
Ubuntu VM 10 seconds 30 seconds 11 seconds 6 seconds 11 seconds 8.5 seconds
Macbook Pro 6.5 seconds 12 seconds 6.3 seconds 8 seconds 15 seconds 9.5 seconds

Summary

I think it’s fascinating how varied the numbers are depending on the platform. This is a complicated benchmark, and as Charles Nutter mentioned to me, one problem we’ll continue to face is that we have no control element in this benchmark. You can get consistency through repetition, but there are simply too many variables to predict exactly what the outcome will be on any given platform. I find it interesting how well the Macbook handles the wide files compared to the Ubuntu VM, which just dies a slow death in comparison - this has to be a side-effect of resource starvation in the VM; but whatever the case, it’s an interesting dichotomy.

On average, the new numbers are much more competitive with MRI, even if they don’t beat it in most cases. As I learned from working with others on this bug, your mileage may vary significantly, but it’s clear from the implementation that we’re causing a lot less resource churn for very little cost (the trade off here is retained memory), and that’s generally a good sign things are going in the right direction. Certainly, profiling has shown that the effort is much more focused on reading from the input channel.

That being said, I’m sure there is more performance to be found - MRI is just a hop-skip-and-jump away!

java  ruby  jruby 

Did You Know: Ruby Instance Methods

Did you know that in Ruby you can add new methods to a particular object, as opposed to adding them to the entire class?

Ruby makes it easy to re-open any class to add new methods - that’s almost always covered early on in Ruby books that talk through using the IRB; it’s a great tool for being able to evolve code incrementally as you are scripting the solution. Less common, however is discussion over how to add methods to individual objects; something that feels foreign to a lot of Java developers like myself. It turns out, it’s just as easy as adding methods to the class.

For example, let’s say you have created a class to represent a dog:

 1class Dog
 2 def bark
 3  puts 'Woof!'
 4 end
 5end
 6
 7pug = Dog.new
 8basset = Dog.new
 9
10pug.bark
11>> Woof!
12
13basset.bark
14>> Woof!

Adding a method to all objects of the class dog is very simple. Simply re-declare the class, defining the new methods in place:

1class Dog
2 def growl
3  puts 'Grrr!'
4 end
5end

To show that this new method works on all instances of that class, we can now call it on both of our existing dog objects:

1pug.growl
2>> Grrr!
3
4basset.growl
5>> Grrr!

There can be cases where a particular object should be given special functionality without manipulating all objects of that class; generally speaking this is a case where the object needs to be adapted. There are different ways to achieve this, such as using a proxy object wrapping the original; however one way to solve this involves adding the method to just one object. This isn’t always the “right” solution for the job, but there are definitely cases where it can streamline a block of code, by simply being able to adapt an existing object to fit a different API.

In Ruby this is done simply by prefacing the method name with the variable name of the object you want to modify (obviously, that object has to be in scope at the time you make the modification). Beyond that, the syntax is the same as any method definition:

1def pug.snort
2 puts 'SNGRRHT!'
3end

Note the ‘pug.’ prefix. The best way to read this is “define the method snort on the object pug, and assign this code block to it”. Simple testing will show that our pug now has “snort” behavior, but our basset, on the other hand, does not:

1pug.snort
2>> SNGRRHT!
3
4basset.snort
5>> NoMethodError: undefined method `snort' for <Dog:0x1de8aa8>

Taking Advantage of Java in JRuby

For those who may not be familiar, Tom Enebo and Charles Nutter were full-time Sun employees until late July when they switched companies to Engine Yard. As many others have said, it’s a different experience (and in my opinion, a very good one) seeing JRuby posts show up on the EngineYard blog.

You have to make a decision at some point when developing a JRuby application as to whether or not you are going to hide the ‘Java’ in your JRuby application from the main bulk of your code. Obviously, if you are developing a Swing GUI, it’s pretty much a foregone conclusion that it’s a JRuby app, and JRuby alone. On the other hand, it’s very possible now-a-days to code a Rails app that can run concurrently on JRuby, MRI, and any other compliant platform.

No answer is ‘right’. Each approach has its own virtues, and it really depends on your goals. A Java developer who simply wants to make coding Java apps easier could easily switch to JRuby and sprinkle Java references throughout their app. On the flip-side, you may just choose to deploy an existing Ruby app on JRuby for it’s compelling performance characteristics and its alternative deployment/scalability options; or perhaps your company would rather manage a Glassfish cluster than a Mongrel cluster - there are a variety of possible reasons.

Tom’s article (part 2) delves a little deeper into the influence of Java from an API perspective (as opposed to the basic Java-integration language constructs). His comments on delegation are compelling:

Delegation as a concept is not specific to Ruby, but is worth bringing up. Why decorate a Java class and get all of that Java class’s methods exposed when you can hide them behind another Ruby class that only exposes what you want? This is especially powerful when you want to write a common API that works across multiple Ruby implementations (e.g. JRuby, MRI, and Rubinius), but has different native backends.

In practice, even if you have committed to JRuby as your platform and have Java libraries referenced all over the place, it can still be very valuable to abstract APIs around the original Java implementation - if for no other reason than Java APIs needing some TLC to feel natural in Ruby. Likewise, there are cases where using Java libraries as the driver for a particular component of your application may give you a competitive advantage on JRuby.

In that vein, I was recently working on a high-concurrency component of a JRuby application. In my opinion, the Java libraries for concurrency are much further evolved than those in Ruby; particularly after the advent of java.util.concurrent in Java 5. One of those areas where Ruby is lacking is the existence of a read-write lock. Jonah Burke previously blogged about implementing a read-write lock using the standard Ruby mutex objects. His implementation is simple, well-implemented, and should be quite reliable. However, Java’s ReentrantReadWriteLock is fully integrated into the underlying Java platform, including in the instrumentation libraries (detailed briefly here). To me, if you plan to run on a Java platform, being able to independently monitor locks held by the Ruby runtime is a Good Thing.

Here is a simple JRuby implementation of a ReadWriteLock that uses a Java lock via delegation:

 1require 'java'
 2
 3class ReadWriteLock
 4    include_package 'java.util.concurrent.locks'
 5    def initialize
 6        @java_lock = ReentrantReadWriteLock.new
 7    end
 8
 9    def read(&block)
10        read_lock = @java_lock.read_lock
11        read_lock.lock
12        begin
13            block.call
14        ensure
15            read_lock.unlock
16        end
17    end
18
19    def write(&block)
20        write_lock = @java_lock.write_lock
21        write_lock.lock
22        begin
23            block.call
24        ensure
25            write_lock.unlock
26        end
27    end
28end

As you can see, JRuby makes using Java objects and extending them very straightforward.

This particular implementation is not 100% API-compatible with the one provided by Jonah, however with a little manipulation it could be easily (I’ll leave it as an exercise to the reader). This is really more of an example of how Java can provide the underlying engine for Ruby libraries when running on JRuby (this is how the majority of JRuby is implemented, after all) - and if this were API compatible with Jonah’s, it’d simply be a matter of constructing this in an isolated factory method, and switching implementations then becomes simply a matter of flipping a flag.

Note that instead of delegation, you could also simply extend the existing Java object with more goodies. Here is an alternate implementation of these same block methods that simply appends these methods to the Java class itself:

 1require 'java'
 2
 3class java::util::concurrent::locks::ReentrantReadWriteLock
 4    def read(&block)
 5        read_lock = self.read_lock
 6        read_lock.lock
 7        begin
 8            block.call
 9        ensure
10            read_lock.unlock
11        end
12    end
13
14    def write(&block)
15        write_lock = self.write_lock
16        write_lock.lock
17        begin
18            block.call
19        ensure
20            write_lock.unlock
21        end
22    end
23end

In practice this works almost identically to the first example. This implementation has some API-leakage (the Java methods on ReentrantReadWriteLock are available to call by the client code), but also has one less object involved, as the methods are added to the Java lock class itself, rather than provided by a proxy object. Which approach you would use for your particular use-case is really dependent upon the scenario in question, and your goals for the API.

Either way for this example, the core usage of this library is identical irrespective of which implementation you choose:

 1lock = ReadWriteLock.new
 2# Alternatively, the second example would be created this way:
 3# require 'java'
 4# import java.util.concurrent.locks.ReentrantReadWriteLock
 5# lock = ReentrantReadWriteLock.new
 6
 7# Usage is identical
 8lock.read do
 9    puts 'Executing with read lock'
10end
11
12lock.write do
13    puts 'Executing with write lock'
14end
java  ruby  jruby