Archive for the ‘Work’ Category

APP Interop Pictures

Tuesday, April 17th, 2007

I took a couple of quick shots of the group:

The assembled crowd at the APP interop

and the grid:

The interop grid at the APP interop

More details on my xml.com blog post.

Tim Bray has better photos here.

More JRuby Play: JFreeChart

Thursday, April 12th, 2007

I’ve been messing around at work trying to make some automated scheduling charts (basically Gantt-like) in Ruby. I’ve implemented it a couple of times using SVG::Graph, which is close to what I need, but I end up having to rewrite a lot of methods whenever I really start using it. It occurred to me today that I might be able to co-opt a sexy Java library to do my dirty work. JFreeChart to the rescue!

As before, I’m generally amazed at how little work goes into integrating Java and JRuby these days. It’s a testament to the JRuby team and to the wealth of well-written, well-documented Java libraries out there.

Here’s some toy code that makes a simple Gantt chart and saves it as a PNG to a file:

# have jfreechart.jar in your classpath, obviously, as well as jcommon.jar
# and use a recent jruby
require 'java'
module Gantt
  class Simple
    include_class 'org.jfree.chart.ChartFactory'
    include_class 'org.jfree.chart.ChartUtilities'
    include_class 'org.jfree.chart.JFreeChart'
    include_class 'org.jfree.data.gantt.Task'
    include_class 'org.jfree.data.gantt.TaskSeries'
    include_class 'org.jfree.data.gantt.TaskSeriesCollection'
    include_class 'org.jfree.data.time.SimpleTimePeriod'
    include_class 'java.lang.System'
    include_class 'java.io.File'

    MILLIS_IN_A_DAY = 86400000

    def initialize(title="Chunky Bacon", width=700, height=400, data=[])
      @width = width
      @height = height
      @title = title
      dataset = create_sample_data() if data.empty?
      @chart = create_chart(dataset)
    end

    def render_to_file(filename, format="png")
      javafile = java.io.File.new(filename)
      ChartUtilities.saveChartAsPNG(javafile, @chart, @width, @height)
    end

    private
    def create_sample_data
      # dates as milliseconds seems the easiet
      now = System.currentTimeMillis
      tomorrow = now + (MILLIS_IN_A_DAY * 1)
      day_after_tomorrow = now + (MILLIS_IN_A_DAY * 2)
      week_from_today = now + (MILLIS_IN_A_DAY * 7)

      s1 = TaskSeries.new("JRuby")
      s1.add(Task.new("Download JRuby",
                      SimpleTimePeriod.new(now, tomorrow)))
      s1.add(Task.new("Write Code",
                      SimpleTimePeriod.new(tomorrow, day_after_tomorrow)))
      s1.add(Task.new("Setup CLASSPATH",
                      SimpleTimePeriod.new(day_after_tomorrow, week_from_today)))

      s2 = TaskSeries.new("Java")
      s2.add(Task.new("Read Comics",
                      SimpleTimePeriod.new(now, tomorrow)))
      s2.add(Task.new("Write Code",
                      SimpleTimePeriod.new(tomorrow, day_after_tomorrow)))
      s2.add(Task.new("Setup CLASSPATH",
                      SimpleTimePeriod.new(day_after_tomorrow, week_from_today)))

      collection = TaskSeriesCollection.new
      collection.add(s1)
      collection.add(s2)
      return collection
    end

    def create_chart(dataset)
      opts = {
              :title => @title,
              :domain_axis_label => "Task",
              :range_axis_label => "Date",
              :data => dataset,
              :include_legend => true,
              :tooltips => false,
              :urls => false
             }
      chart = ChartFactory.createGanttChart(
                                              opts[:title],
                                              opts[:domain_axis_label],
                                              opts[:range_axis_label],
                                              opts[:data],
                                              opts[:include_legend],
                                              opts[:tooltips],
                                              opts[:urls]
                                            )
      return chart
    end

  end # class Simple
end # module Gantt

chart = Gantt::Simple.new("Gantt Chart Demo")
puts "Rendering chart"
chart.render_to_file("simplegantt.png")

Example PNG:

A Simple Gantt Chart Example

Code: http://kfahlgren.com/code/simple_gantt.jrb

Borrowing Java’s XSLT Support for Ruby

Friday, March 2nd, 2007

Well, I finally caught up with the crowd and got JRuby running on one of my dev boxes. The reason I’d been interested in it from the getgo was because Ruby lacks any support for internal XSLT processing. All those system()s were starting to get me down, especially as I’m trying to get a DocBook->PDF rendering webservice to be a lot faster. Much to my surprise, I was able to get simple transforms working in almost no time (thanks in part to lots of help). Without further ado, here’s a simple library for XSLT transforms using either Xalan-J or Saxon (make sure you have the jars for both in your CLASSPATH):

require 'java'
module JXslt
  include_class "javax.xml.transform.TransformerFactory"
  include_class "javax.xml.transform.Transformer"
  include_class "javax.xml.transform.stream.StreamSource"
  include_class "javax.xml.transform.stream.StreamResult"
  include_class "java.lang.System"

  class XsltProcessor
    def transform(xslt,infile,outfile)
      transformer = @tf.newTransformer(StreamSource.new(xslt))
      transformer.transform(StreamSource.new(infile), StreamResult.new(outfile))
    end
  end # XsltProcessor
  class Saxon < XsltProcessor
    TRANSFORMER_FACTORY_IMPL = "net.sf.saxon.TransformerFactoryImpl"
    def initialize
      System.setProperty("javax.xml.transform.TransformerFactory", TRANSFORMER_FACTORY_IMPL)
      @tf = TransformerFactory.newInstance
    end
  end
  class Xalan < XsltProcessor
    TRANSFORMER_FACTORY_IMPL = "org.apache.xalan.processor.TransformerFactoryImpl"
    def initialize
      System.setProperty("javax.xml.transform.TransformerFactory", TRANSFORMER_FACTORY_IMPL)
      @tf = TransformerFactory.newInstance
    end
  end
end 

# if you wanted to run this from the command line, do something like
# $ jruby lib/jxslt.rb a.xsl in.xml out.xml
xalan = JXslt::Xalan.new
xalan.transform(*ARGV)
#saxon = JXslt::Saxon.new
#saxon.transform(*ARGV)

Big props to Charles for helping me get going and writing the first version of the above.

darcs get http://kfahlgren.com/code/lib/jxslt/ or jxslt.rb

Exploiting FrameMaker MIF as XML, Reading Bookfiles

Sunday, February 25th, 2007

[Read this for an introduction to what I'm talking about].

Now that we’ve got our FrameMaker documents in XML, how can we exploit their new format? One of the first things I did was to create new ways of reading (eventually changing) the simple data stored within them. This isn’t all that earth-shattering, but when you consider how difficult it is to find and change some values in the FrameMaker UI this is a big win. Where to start? Bookfiles.

To be able to apply stylesheets or data-collection tools to books (rather than individual files), I need to be able to collect a books components. So, convert your bookfile to MX (yeah, it works on bookfiles as well as chapter files), and search through it for one of the filenames you know is a part of the book (you’ll probably want to pretty-print the XML first). I get something like this:

  <BookComponent>
    <FileName>`&lt;c\&gt;ch01'</FileName>
    <Unique>27107</Unique>
    <StartPageSide>StartRightSide</StartPageSide>
    <PageNumbering>Restart</PageNumbering>
    <PgfNumbering>Continue</PgfNumbering>
    <PageNumPrefix>`'</PageNumPrefix>
    <PageNumSuffix>`'</PageNumSuffix>
    <DefaultPrint>Yes</DefaultPrint>
    <DefaultApply>Yes</DefaultApply>
  </BookComponent>

MX in this case has a pretty comprehensible structure, so we’ll need to grab a BookComponent/FileName, do a little text processing to remove the funky characters, and potentially append our MX file extension (I chose “.mx”). Here’s a very simple stylesheet to do just that:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="@*|node()">
    <xsl:apply-templates/>
  </xsl:template>  

  <xsl:template match="/">
    <xsl:element name="components">
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="//BookComponent/FileName">
    <xsl:param name="extension" select="'.mx'"/>
    <xsl:variable name="str-after">
      <xsl:value-of select="substring-after(., '>')"/>
    </xsl:variable>
    <xsl:element name="component">
      <xsl:value-of select="substring($str-after,
                                      1,
                                      string-length($str-after) - 1)"/>
      <xsl:value-of select="$extension"/>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

When you run that on a MX bookfile, you should see an output like this (note that the file extension is customizable above):

<?xml version="1.0"?>
<components>
  <component>svcTOC.fm.mx</component>
  <component>foreword.mx</component>
  <component>ch00.mx</component>
  <component>ch01.mx</component>
  <component>ch02.mx</component>
  <component>ch03.mx</component>
  <component>ch04.mx</component>
  <component>ch05.mx</component>
  <component>ch06.mx</component>
  <component>ch07.mx</component>
  <component>ch08.mx</component>
  <component>ch09.mx</component>
  <component>appa.mx</component>
  <component>appb.mx</component>
  <component>appc.mx</component>
  <component>appd.mx</component>
  <component>appe.mx</component>
  <component>svcIX.fm.mx</component>
  <component>svcAPL.fm.mx</component>
  <component>svcLOR.fm.mx</component>
</components>

That’ll give us a nice structure to direct other processes to the individual component files.

The code is also available here or darcs get http://kfahlgren.com/code/mx/.

Ruby and the Atom Publishing Protocol

Saturday, February 24th, 2007

I gave a short talk at the first North Bay Ruby Users Group last Thursday (Feb 15, 2007) about my recent work implementing an Atom Publishing Protocol library in Ruby. Here’s the presentation:

Thumbnail of my Ruby APP talk

Exploiting FrameMaker MIF as XML, Back into MIF

Saturday, February 3rd, 2007

[Read this for an introduction to what I'm talking about].

The first step of doing anything useful with MX is the ability to get back out into MIF. Thankfully, this is an entirely trivial job in XSLT.

[This code thanks to my boss, Andrew Savikas.]

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <!-- author: Andrew Savikas, O'Reilly Media -->

  <xsl:output method="text" encoding="ascii"/>
  <xsl:strip-space elements="_facet"/>

  <xsl:template match="/|MIF_ROOT">
    <xsl:apply-templates/>
  </xsl:template>

<!-- This template needs to remain flush left for correct output -->
<xsl:template match="_facet">
<xsl:text>
</xsl:text>
<xsl:apply-templates/>
</xsl:template>

  <xsl:template match="*">
    <xsl:text>&lt;</xsl:text>
      <xsl:value-of select="name()"/>
    <xsl:text> </xsl:text>
    <xsl:apply-templates/>
    <xsl:text>&gt;</xsl:text>
    <xsl:text> </xsl:text>
  </xsl:template>

</xsl:stylesheet>

The code is also available here or darcs get http://kfahlgren.com/code/mx/.

Exploiting FrameMaker MIF as XML, Introduction

Saturday, February 3rd, 2007

My O’Reilly colleague Andy Bruno has just written a pair of posts on converting FrameMaker’s MIF (link may be old/die) format into XML (henceforth ‘MX’). I’ll be writing a few posts outlining the ways in which we’ve leveraged MX at O’Reilly.

[Update: Series continues here with getting back into MIF, and reading bookfiles.]

(more…)

North Bay Ruby Users Group Formed

Wednesday, January 24th, 2007

I’m pleased to announce a new Ruby Users Group for folks north of San Francisco (or who like to go to Sebastopol, CA): the North Bay Ruby Users Group with a first meeting on February 15th, 2007 at 7:30pm 7:00pm. O’Reilly has graciously offered us their meeting space, so we’ll be holding the meetings at the office in Sebastopol, CA (directions). The initial schedule for meetings is the third Thursday of each month, but we can have a discussion about whether other times work better at the first meeting. If you’re interested in learning more, please sign up for the mailing list.

[Full disclosure: Both Rob Orsini (the author of the recently released Rails Cookbook) and I are both employees of O'Reilly Media, so I can't pretend that holding the meetings here was a total accident.]

The agenda, as well as everything else, for the first meeting is still in flux, but Rob and I are planning to do a presentation on how O’Reilly uses Ruby internally, from small internal Rails sites to Atom Publishing Protocol libraries to XML processing. We can spend the rest of the time getting to know each other, eating pizza, and planning upcoming meetings. If you’d like to make a presentation at a future meeting or know someone who should, please join the mailing list and make suggestions (even if you can’t make it every month).

Looking forward to seeing you there!

PS: Yeah, the website is pretty crappy right now, we’ll be making it more useful in the coming days, adding a wiki and all that. For now, just be happy it isn’t this.

Sometimes, a Picture Speaks Louder Than Words

Tuesday, January 23rd, 2007

Especially a picture of SVN commit messages:
Nasty Commit Logs

PS: That’s an RSS feed via Trac, a decent tool if you can manage to set it up…

BDD, rspec Worth the Fuss?

Friday, January 19th, 2007

So, I’ve been hearing a lot of buzz lately in the Ruby community about BDD and rspec. Like this person on ruby-talk, I didn’t really understand what all the fuss was about. However Dave Astels’ great Google Tech Talk video (worth watching in full rather than just a part, thanks James Britt) has at least inspired me to download rspec and start playing around.

I’m pleased to see that rspec has support for both rcov and heckle built-in, as I’ve been enjoying both recently.