I have an application that implements several objects in C# and then expects those objects to be usable from IronRuby by adding them to a Microsoft.Scripting.Hosting.ScriptScope. This works for the most part: I've learned how to implement the various members of DynamicObject to make things act correctly. But how do I make an object compatible with Ruby's for loops?
My C# object looks like this:
public class TrialObject : DynamicObject, IEnumerable<int> {
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) {
if (indexes.Length != 1) return base.TryGetIndex(binder, indexes, out result);
try {
var index = (int)indexes[0];
result = index;
return true;
} catch { return base.TryGetIndex(binder, indexes, out result); }
}
public int Length { get { return 3; } }
public IEnumerator<int> GetEnumerator() {
yield return 0;
yield return 1;
yield return 2;
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
Then I try to use the object from ruby, like so.
trial.Length # returns 3
trial[0] # returns 0
trial[2] # returns 2
for t in trial do <stuff>; end #error: no block given
How do I adjust the trial object to allow it to work in a for loop? I know I could use "i in 0..trial.Length-1" as a hack around, but I'd rather fix the problem than introduce a hack.
After some trial and error, I learned that I could get the desired effect by adding an "each" method to my class.
public class TrialObject : DynamicObject, IEnumerable<int> {
...
public IEnumerable<BuildableObject> each() { return this; }
...
}
It seems as if ironruby uses the "each" method to implement its for. So by adding an each method, I can get this dynamic object to act like the IEnumerable it's trying to be.