Thursday, June 16, 2011

A Ruby DSL for creating entity system components in Java

Have you ever been working with entity system architectures in Java and wished for a quicker way to prototype components? Well, you've come to the right place.

After working with entity systems for about year, I have an idiosyncratic way of defining any of my components. Keep in mind that components are just dumb data objects, not proper OO-objects, and I'm roughly following Adam Martin's approach. Here's the way I would define a position component for two-dimensional floating-point space:


public class Position {

    private float x;
    private float y;
    
    public float x() { return x; }
    public float y() { return y; }
    
    public Position x(float x) { this.x=x; return this; }
    public Position y(float y) { this.y=y; return this; }
    
}


I've skipped documentation for the sake of brevity. I like to keep my data private, even though it's exposed through accessors and mutators—I just can't bring myself to make public instance fields in Java. The naming convention for accessors and mutators is a Smalltalk approach, using the same name for both accessor and mutator, which can lean designs towards fluent API design instead of pushbutton (see Fowler's DSL book for more on this). The mutator returns code to promote fluent API design as well, to permit code like the following.


Position p = new Position().x(10).y(15);

Now if I'm feeling really spiffy, I'll use a builder:

Position p = new Position.Builder().x(10).y(15).build();

but that's not my point.

My point is that this is almost all boilerplate code. All I really want to say is that there's a component called Position and it has two floats, x and y. So maybe what I really want to be able to write is something like this:

Component.build :Position do
  float :x
  float :y
end

The title of the post gives it away, but this is a little Ruby DSL that I hacked together this afternoon, just to see if I could. I've been meaning to learn Ruby for some time. I have read quite a bit about Ruby and its use in DSLs, but never actually created one. This time around, I used Jonathan Magen's tutorial as a reference.

The Component module is the starting point, and it uses what I take to be a fairly standard approach, sending blocks of code from the context to be executed within the object context:

module Component
  def self.build(name, &block)
    builder = ComponentBuilder.new(name)
    builder.instance_eval(&block)
    builder.build()
    return builder
  end
end

The one custom piece is the build method on the ComponentBuilder. What this does (perhaps not clearly indicated by the name, in retrospect) is tell the constructed object to dump out a Java source file based on the content of the block. The ComponentBuilder looks like this:

class ComponentBuilder 
  def initialize(name)
    @name = name
    @floats = []
  end

  def float(x)
    @floats << x
  end

  def build
    File.open(@name.to_s + ".java", "w") do |theFile|
      theFile.syswrite("public class #{@name.to_s} implements Component {\n")
      unless @floats.nil? 
        @floats.each do |var|
          theFile.syswrite <<BLOCK
    private float #{var};
    public float #{var}() { return #{var}; }
    public #{@name.to_s} #{var}(float #{var}) {
        this.#{var} = #{var};
        return this;
    }
BLOCK
        end
      end
      theFile.syswrite("}")
    end
  end

end


Here's a very quick walkthrough for those readers who do not know Ruby. The initialize method is the constructor, and it's setting the instance variable @name to the argument's value and initializing an empty array for @floats. The next method is called float, which is not a reserved word in Ruby, and it appends its argument to the @floats array. The build method opens the appropriately-named file, writes the class declaration, and then iterates through the @floats field, dumping out the field, accessor, and mutator definition for each one.

There were two specific pieces of Ruby syntax that I had to learn to make this work: here documents and expression substitution in strings. The oddly named "here documents" are multiline string literals. In my program, they run from <<BLOCK to the recurrence of BLOCK. This is a really useful feature for a program that is essentially filling templates and dumping them out. C# has something similar, but Java—my usual production language— does not. Note that you can do it with Groovy, though it appears the proposal to put this in 1.7 hasn't been approved. Regardless, expression substitution in strings is simple and elegant: within a string literal, put an expression within #{...}, and it is evaluated and interpolated into the string at runtime.

In order to make my program a bit more robust, I added configuration information to control the Java package and path to the destination as well as per-class and per-field embedded Javadoc, passed as optional parameters (via variable-length argument lists). The biggest problem I had with this was creating directories automatically from relative paths, but in the end, I pulled it off with a little custom method shown below.

def defensive_makedir(dir) 
  array = dir.split('/')
  accum = '.'
  array.each do |partial| 
    accum = accum + "/" + partial
    unless File.directory? accum
      Dir.mkdir accum
    end
  end
end

To integrate this into Eclipse, I wrote a simple ant build script. Honestly, I wrote this very early in the experiment, but the process of debugging Ruby through Eclipse and Ant was quite cumbersome, and once I hopped over to a console and emacs, I was able to iterate much faster. To incorporate this into the build process in Eclipse, hop over to your project preferences and check out the "Builders" section. This requires the JRuby jar to be on the build path.


<project name="FunWithComponents" default="make_components">
    
    <property name="lib" location="${basedir}/vendor/lib"/>
    <property name="jruby.jar" location="${lib}/jruby.jar"/>
    <property name="ruby.src" location="${basedir}/ruby"/>
    <property name="generated.src" location="${basedir}/gen"/>
    
    <target name="make_components">
        <java jar="${jruby.jar}" fork="true" dir="${ruby.src}">
            <arg value="${ruby.src}/make_components.rb"/>
        </java>
    </target>
        
</project>

I enjoyed working on this and learned a bit about Ruby, both in terms of the language and how to express myself within it. I acknowledge that I do so much work in Java, it can be hard to switch paradigms or idioms, and so it's good to actually make something useful from time to time. However, it also makes me wonder if all that trouble is actually worth it when I can immediately imagine how to support a Java-based solution like the following.

 public static void main(String[] args) {
    ComponentFactory.instance().build("Position")//
        .withFloat("x")//
        .withFloat("y")//
        .build();
    }

1 comment:

  1. very cool. I too now that I've almost completed the component breakdown on the Java side with my efforts am looking toward various JVM languages. I'll likely mostly be focusing on Scala first due to the type-safe & joint compilation aspects as that will work well for Android / J2SE cross-platform concerns. I suppose this being a move towards a firm component-functional direction emphasizing COP over OOP except for a bit of inheritance between components and the listener pattern for event distro and timing / clocking callbacks. Should be interesting to see what entity system goodness Scala can reveal on the DSL level or otherwise. IE should be possible to produce a Scala based clock source for anonymous callbacks. I'm very excited to start working with Scala as that'll be some fresh territory after 15 years of mostly Java. Of course I'll get my Groovy on via Gradle integration on the build side of things.

    ReplyDelete