IronRuby Code in C# Anwendungen ausführen ist eine wirklich einfache Aufgabe. Mit wenigen Handgriffen kann man seinen Anwendungen die nötige dynamik beibringen.
Den hier verwendeten Code kann man ohne weiteres auch auf IronPython anwenden, da die Grundprinzipien aus der Dynamic Language Runtime (DLR) und nicht aus IronRuby kommen.
Zunächst müssen Assemblyverweise auf die folgenden Libs (alle befinden sich im IronRuby Installationordner) hinzugefügt werden
- IronRuby
- IronRuby.Libraries
- Microsoft.Scripting
- Microsoft.Scripting.Core
Nun kanns auch schon ans eingemachte gehen. Als einfaches Beispiel mal ein “Hello from IronRuby”
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DotNetRocks
{
class HostingIronRubySample
{
public void SayHello()
{
var scriptEngine = IronRuby.Ruby.CreateEngine();
var scriptSource = scriptEngine
.CreateScriptSourceFromString(
"puts 'Hello from IronRuby'");
scriptSource.Execute();
}
}
}
Wie man sehen kann, bietet die Ruby Klasse im IronRuby Namespace die Methode die eine neue ScriptEngine erstellen kann. Im zweiten Schritt wird noch eine ScriptSource erstellt, mit deren Hilfe kann der Ruby Code ausgeführt werden.
Dies ist der einfachste Weg dynamischen Code auszuführen, jedoch viel mehr als “Hello from IronRuby” und ein paar einfache Beispiele lassen sich damit nicht realisieren… Denn in einem Anwendungskontext möchte man gerne Werte übertragen und mit Ergebnissen des Scripts arbeiten.
Hierfür muss ein ScriptScope vorhanden sein, der bei Bedarf einen “Container” für den dynamischen Code bietet, so dass frei definiert werden kann, welcher dynamische Code über welche Eingabevariablen verfügt.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DotNetRocks
{
class HostingIronRubySample
{
public int DynamicAdd(int a, int b)
{
var scriptEngine = IronRuby.Ruby.CreateEngine();
var scriptScope = scriptEngine.Runtime.CreateScope();
scriptScope.SetVariable("a",a);
scriptScope.SetVariable("b",b);
scriptEngine.Execute(
"c = a+b", scriptScope);
return scriptScope.GetVariable<int>("c");
}
}
}
Den ScriptScope habe ich in diesem Beispiel über die Runtime Property der ScriptEngine genommen und im Anschluss daran die beiden Methodeparameter aus C# in die IronRuby Context übergeben.
Ab diesem Zeitpunkt kann man in Ruby Scripten auf die beiden Parameter zugreifen. Die Scriptausführung übernimmt in diesem Fall die ScriptEngine, wobei hier wichtig ist, den zuvor befüllten ScriptScope mitzugeben.
Dank der generischen GetVariable Methode kann das Ergebnis der Berechnung direkt volltypisiert vom ScriptScope abgefragt und weiterverarbeitet werden.
Nach diesen einfachen Beispielen möchte ich noch kurz auf ein paar interessante Aspekte eingehen, die für den ein oder anderen wichtig sein könnten.
ScriptSource
Die Klasse ScriptSource ist die Repräsentation von dynamischen Code, sie bietet Methoden die uns beim Arbeiten mit dynamischen Code unterstützen. (SourceCode lesen, ausführen usw)
Ein nettes weiteres Feature ist das Kompilieren von IronRuby Code mit Hilfe der Klasse ScriptSource. DLR Sprachen wird immer nachgesagt dass Sie in der Ausführung langsamer sind als “normaler” managed C# Code. Schaut man sich die Architektur der DLR an oder verinnerlicht man sich mal was im Hintergrund alles geschieht ist dass auch klar.
Verfügt ein IronRuby Script über statische oder sich nicht änderbare Passagen, so kann man diese Vorkompilieren, was zur Ausführungszeit dann einen kleinen Boost mit sich bringt.
public void ExecuteSomeCompiledRubyCode()
{
var scriptEngine = IronRuby.Ruby.CreateEngine();
var scriptSource = scriptEngine
.CreateScriptSourceFromString(
"puts 'Dynamic Code starts here'");
scriptSource.Compile();
scriptSource.Execute();
}
Wie man sieht muss lediglich die Compile Methode der ScriptSource aufgerufen werden, damit der IronRuby Code kompiliert wird. Ausgeführt wird der kompilierte Code dann, durch einen Aufruf Execute Methode.
Türchen öffne dich
Private Methoden außerhalb der definierenden Klasse verwenden oder auf private Felder einer Klasse zugreifen. Eine leichte Aufgabe für eine DLR basierte Sprache.
Wenn man auf private Member einer Klasse zugreifen möchte muss man die ScriptRuntime-Einstellungen manuell definieren bevor man eine Runtime erstellt.
Das nachfolgende Codebeispiel zeigt wie der private Setter der Property IsValid einer Klasse Person durch IronRuby gesetzt werden kann.
public class Person
{
public String FirstName { get; set; }
public String LastName { get; set; }
public bool IsValid { get; private set; }
}
public class IronRubyValidator
{
public bool IsPersonValid(Person person)
{
var scriptRuntimeSetup =
new ScriptRuntimeSetup()
{
// enable private binding to access private members
PrivateBinding = true
};
scriptRuntimeSetup.LanguageSetups
.Add(IronRuby.Ruby.CreateRubySetup());
var runtime = IronRuby
.Ruby.CreateRuntime(scriptRuntimeSetup);
var scriptEngine = runtime.GetEngine("Ruby");
var scriptScope = runtime.CreateScope();
scriptScope.SetVariable("person",person);
scriptEngine
.Execute(
@"if(person.FirstName.to_s.eql?'')
person.IsValid = false
end",
scriptScope);
var validatedPerson = scriptScope.GetVariable<Person>("person");
return validatedPerson.IsValid;
}
}
Wie man sehen kann wird in diesem Fall auf dem ScriptRuntimeSetup das PrivateBinding aktiviert, welches den Zugriff auf private Member aktiviert.
Alternativ zum hier gezeigten Inplace Scripting besteht natürlich auch die Möglichkeit die Scripts in Files auszulagern und diese dann auszuführen. Entsprechende Überladungen der Methoden sind vorhanden.
In einem der nächsten Posts werde ich die einzelnen Bestandteile wie ScriptRuntime, ScriptScope oder auch ScriptEngine etwas genauer definieren und deren Aufgabengebiet erläutern.
Bis dahin viel Spaß mit IronRuby. :)
Technorati Tags:
IronRuby,
DLR,
Hosting