Ruby and Code Generation

I've been playing with Ruby the latest couple of weeks (and I'm not the only one ;).

We are starting a new project and I'm trying to evangelize the rest of the team to use code generation throughout the build process.

While reading Code Generation in Action, I became intrigued about Ruby, as it was used for the sample code. I've been hearing about Ruby from a long time but I was never intrigued enough to learn more about it, and I never had a need for it in my day to day job.

I knew the Pragmatic Programmers had a book on Ruby so I went to their site, and found an online copy of their Ruby book. The intro is enough to get a grasp of it. For a quick intro, go straight to . There is also a full Ruby setup for Windows with all the important libraries and a text editor from where you can run Ruby scripts.

The Code Generation in Action book the ERB template engine is used for the samples, and it's not included in the Windows distribution mentioned above. You can find it in http://raa.ruby-lang.org/list.rhtml?name=erb

I liked Ruby a lot, but I'm still not sure if I should push it inside our team or not.

Below are the CodeSmith/C# template and the ERB/Ruby template to acomplish the same task:

--- CodeSmith / C#

<%@ Property Name="XmlFileName" Type="System.String"%>
<%@ Assembly Name="System.Xml"%>
<%@ Import Namespace="System.Xml" %>

<%
XmlDocument doc = new XmlDocument();
doc.Load(XmlFileName);
string objectName = doc.SelectSingleNode("/UIObject/Name").InnerText;
%>

public class <%= objectName %>
{
<% foreach (XmlNode node in doc.SelectNodes("/UIObject/Members/Member"))
   {
   string memberType = node.SelectSingleNode("Type").InnerText;
   string memberName = node.SelectSingleNode("Name").InnerText;
%>
   private <%= memberType %> m_<%= memberName %> <%= (node.Attributes["Initialize"] != null && node.Attributes["Initialize"].Value == "yes")?("= new " + memberType + "()"):""%>;
<% } %>

<% if (doc.SelectSingleNode("/UIObject/DefaultConstructor") != null)
   { %>
   public <%= objectName %>()
   {
   }
<% } %>

}


--- ERB / Ruby
<%
require "rexml/document"

doc = REXML::Document.new File.new(xml_file_name)
object_name = doc.elements["/UIObject/Name"].text
%>

public class <%= object_name %>
{    
<%
doc.elements.each("/UIObject/Members/Member") { |element|
     member_type = element.elements["Type"].text
     member_name = element.elements["Name"].text
%>   private <%= member_type %> m_<%= member_name %>  <%= "= new " + member_type + "()" if element.attributes["Initialize"] == "yes"%>;
<% }

if doc.elements["/UIObject/DefaultConstructor"]
%>
   public <%= object_name %>()
   {
   }
<%
end%>
}

This generates something like:

public class EditBox
{
    private string m_Text;
    private EditBoxHelper m_Helper = new EditBoxHelper();

    public EditBox()
    {
    }
}

As you can see, the Ruby code is more compact. Some little Ruby features help you to write less code:

  • As it's a dynamically typed language, you don't need to declare variables. I'm not sure if I would use a dynamically typed language for writing a big complex application, but in this case it's more convenient.
  • The XML library lets you write element.attributes["Initialize"], and it returns the value, instead of having to ask for the .Value explicitly, so you can write

if element.attributes["Initialize"] == "yes"

instead of

if (node.Attributes["Initialize"] != null && node.Attributes["Initialize"].Value == "yes")

  • Expressions that result in a null value evaluate to False in conditions, so you can write:

if doc.elements["/UIObject/Parent"]

instead of

if (doc.SelectSingleNode("/UIObject/Parent") != null)

  • The 'if' statement modifier lets me write:

"= new " + member_type + "()" if element.attributes["Initialize"] == "yes"

instead of

node.Attributes["Initialize"].Value == "yes"?("= new " + memberType + "()"):""

  • For processing XML, ruby code blocks provide pretty much the same functionality as C# foreachs, so there is no big advantage there.

Even if the code is more compact, I don't feel I have a solid argument to convince our team to move to Ruby. C# skills are more common than Ruby skills, and the improvements are not as big to force the team to learn a new language.

I keep reading comments about being able to 'think in the Ruby way', and I'm sure I still think with my C# head, so perhaps there's a better way to write this in Ruby that will make the differences bigger...

1 Comment

  • Actually it gets better than:



    &quot;= new &quot; + member_type + &quot;()&quot; if element.attributes[&quot;Initialize&quot;] == &quot;yes&quot;



    You can say:



    &quot;= new #{member_type}()&quot; if element.attributes[&quot;Initialize&quot;] == &quot;yes&quot;



    Which is something along the lines of the 'Ruby Way'.



    I picked Ruby for the book because it was easy to learn and understand and I was sure that people like you would find value in the concepts and not be particularly concerned about the implementation language.

Comments have been disabled for this content.