Question

I am using IronRuby to do some scripting in my app.

I have lots of commands (CLR objects) I need to create and execute, but most of the commands need properties set on them and I want to avoid having to assign the command to a variable just so I can set the properties.

Is there a way to do it like the C# object initializer syntax?

Was it helpful?

Solution

There is no built-in way of setting properties when constructing CLR objects, as it is not a features of Ruby itself. However, though Python does not support this either, IronPython does support it by allowing named parameters to be supplied to constructors. Since Ruby does not support named parameters, we did not want to enforce a named parameter pattern, be it a Hash as the last argument, or a block which is instance_eval'd against the constructing object, or something else people come up with.

That being said, both strategies of implementing object initializers can be written in pure Ruby without any special support in IronRuby. I'll describe both listed above, but feel free to experiment if they are not exactly the syntax you were looking for.

Assuming the following CLR class (written in C#):

namespace TestLib {
  public class TestObj1 {
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
  }
}

You could imagine initializing properties with a block passed to the constructor:

testobj = TestObj1.new do
  self.prop1 = "Prop1 Value"
  self.prop2 = "Prop2 Value"
end

And here's how you could override the TestObj1.new method to support that:

class TestObj1
  def self.new(*args, &block)
    obj = super(*args)
    obj.instance_eval &block
    obj
  end
end

Since this actually evals the block against the newly created object, you can execute any Ruby code inside the block. This pattern is popular when creating DSLs and more natural APIs in Ruby.

Or, if you prefer using Ruby's lenient Hash syntax in method arguments:

testobj = TestObj1.new :prop1 => "Prop1 value", :prop2 => "Prop2 value"

Then this is how you could override the .new method:

class TestObj1
  def self.new(*args)
    last_arg = args[-1]
    if last_arg.kind_of?(Hash)
      first_args = args[0..-2]
      obj = super(*first_args)
      last_arg.each do |prop, val|
        obj.send("#{prop}=", val)
      end
      return obj
    end
    super(*args)
  end
end

The Hash option is definitely a bit more complicated, but a bit more performant (as it avoids eval) and is a more common pattern in Ruby to expose named parameters.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top