Home
Atom Feed

IronRuby and Umbraco

Oct 21, 2010

A few days ago, a colleague asked me to do a presentation of Umbraco. Since I knew that he was in love with Ruby, I thought I could include a IronRuby/Umbraco demo to win his heart.

As it turns out, I was sailing into uncharted waters. IronRuby support has only been included in Umbraco sinde version 4.5 (summer 2010), and it seems like noone has tried to use it until now (at least not blogged about it).

I started out with one of the included Ruby scripts, which lists out all children of the current node. I wanted to make a simple Ruby alternative to EPiServer PageList, so I needed to pass the id of the starting node as a parameter.

And at that point, I sailed onto the first rock. IronRuby refused to accept the following line (nodeId is the alias of the parameter):

parent = Node.new(System::Int32.Parse(nodeId))

The parser choked with the following error message:

Error loading python script (file: RubyTest.rb, Type: ''
uninitialized constant Object::Node
 at Microsoft.Scripting.Interpreter.ThrowInstruction.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.LightLambda.Run2[T0,T1,TRet](T0 arg0, T1 arg1)
 at IronRuby.Runtime.RubyScriptCode.Run(Scope scope, Boolean bindGlobals)
 at umbraco.scripting.MacroScriptEngine.Execute()
 at umbraco.scripting.MacroScriptEngine.ExecuteFile(String path)
 at umbraco.scripting.MacroScript.ExecuteFile(String path, Hashtable variables)
 at umbraco.macro.loadMacroDLR(macro macro, Hashtable attributes, Hashtable pageElements)
 at umbraco.macro.renderMacro(Hashtable attributes, Hashtable pageElements, Int32 pageId)

After trying and failing for some time, Aaron Powell mentioned on Twitter that it might be a namespace problem. Googling IronRuby and namespaces gave me this article by IronShay.

It turns out that IronRuby wants all class names and namespaces PascalCased, and just plainly refuses to see classes and namespaces starting with a lowercase character (Umbraco classnames are camelCased). I applied the workaround described by IronShay, which is to get the class with "const_get" and assign it to a PascalCased variable.

Here is the code I ended up with:

Umbraco = Object.const_get("umbraco")
Library = Umbraco.const_get("library")
NodeFactory = Umbraco.const_get("presentation").const_get("nodeFactory")

# Set the node id you would like to fetch pages from here
# You can also set it as a macro property with the alias 'nodeId' instead

parent = NodeFactory::Node.new(System::Int32.Parse(nodeId))


# Start writing out the list
result = "<ul>"

parent.Children.each do |child|
  result += "<li><a href='" + Library.NiceUrl(child.Id) + "'>" + child.Name + "</a></li>"
end

result += "</ul>"

puts result

And voila!
Everything worked as expected!

I hope this article will help anyone trying to do IronRuby+Umbraco in the future.

EDIT: Jonas Eriksson also has some nice revelations.

 

UPDATE:
Here is a version that also filters out hidden nodes:

Umbraco = Object.const_get("umbraco")
Library = Umbraco.const_get("library")
NodeFactory = Umbraco.const_get("presentation").const_get("nodeFactory")

# Set the node id you would like to fetch pages from here
# You can also set it as a macro property with the alias 'nodeId' instead

parent = NodeFactory::Node.new(System::Int32.Parse(nodeId))


# Start writing out the list
result = "<ul>"

parent.Children.find_all{|f| f.GetProperty("umbracoNaviHide")!="1"}.each do |child|
  result += "<li><a href='" + Library.NiceUrl(child.Id) + "'>" + child.Name + "</a></li>"
end

result += "</ul>"

puts result