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?
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 eval
s 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.