IronRuby

Agora o C# traz diversas características dinâmicas, na sua versão 4. Falei semana passada como usar uma delas semana passada, com um leitor de XML dinâmico. Além disso, dá pra fazer diversos outros malabarismos com C# 4 e o dinamismo. Já falei disso aqui, aqui, aqui e aqui.

Mas tem uma coisa que não dá pra fazer ainda de maneira muito simples, que é compilar código C# dinamicamente. Até dá pra fazer, mas dá um trabalho violento. Você tem que usar as classes do compilador do C# disponível em Microsoft.CSharp, como a CSharpCodeCompiler, e fazer todo o trabalho de referências, usings, namespaces, e dá um trabalhão que simplesmente não se paga.

O problema vem do fato de o C# não ser uma linguagem que nasceu para ser compilada dinamicamente, aberta desta forma, apesar de estar caminhando para isso. O ideal seria trabalhar uma linguagem que funciona melhor com scripts, mais dinâmica, como o VB. Infelizmente com VB dá o mesmo trabalhão, já que ele é irmão (quase siamês) do C#. Isso não é mais um problema: o .Net agora tem uma linguagem mais adaptada para isso, que é o IronRuby, implementação de Ruby para rodar nativamente no runtime do .Net, assim como o JRuby é a implementação do Ruby para o runtime do Java.

O IronRuby está em versão release candidate e já compila quase toda a especificação do Ruby, rodando os principais frameworks de Ruby, como o Rails (um MVC para web) e o RSpec (framework de BDD). Você pode pegar a versão que funciona com o .Net 3.5, que é o RC1, aqui. Essa versão é numerada como 0.9.3. A versão que vou usar neste post é a 0.9.1, a que tem como o .Net framework 4 Beta 2 (fique atento, há uma versão 0.9.1 que foca no .Net 3.5 também):

Versão do IronRuby

Não vou ficar explicando aqui a sintaxe do Ruby, que é super simples, e tenho certeza que vocês, que gostam de ponto e vírgula e chaves, vão entender. Pra quem quiser ver mais de perto dê uma olhada no tutorial oficial aqui. Eu gosto muito deste tutorial também, e ele é bem mais direto que o oficial.

Vamos ver o que podemos fazer com o Ruby integrado no .Net. Pra começar, baixe a versão que apontei, a que compila com o .Net 4, e baixe, se ainda não baixou (não??), o VS 2010 Beta 2.

Descompacte o zip em C:\ruby. Seu diretório deve ficar assim:

Diretório do ruby

Não invente de colocar no D:, não invente de colocar debaixo de MyDocuments, ou em qualquer outro lugar. A instalação padrão do Ruby é no c:\ruby, e o Ruby é sensível. Amanhã se você quiser se integrar com a versão original do Ruby, feita em C (chamada de MRI), você vai ter problemas se o IR não estiver neste diretório. Não que não seja impossível colocar em outro lugar, mas se você não gosta de dor de cabeça, acredite em mim, e coloque ele lá, ok? Todos os exemplos do mundo de Ruby em Windows assumem que ele está lá.

Crie um projeto de testes com C# no Visual Studio 2010. Sim, testes. Porque você quer testar o IronRuby, lembra? Nós não testamos com projeto de console, ou Winforms, ou WPF, ou ASP.Net, nós testamos com projetos de testes automatizados, pra ter feedback constante e rápido se fizemos alguma coisa errada. Lembre-se disso, vamos em frente.

Referencie as seguintes dlls do IronRuby e do DLR que vão estar em c:\ruby\bin:

  1. IronRuby.dll
  2. IronRuby.Libraries.dll
  3. Microsoft.Dynamic.dll
  4. Microsoft.Scripting.dll

Em tempo, DLR é o que dá suporte às linguagens dinâmicas e agora faz parte do .Net Framework. O código é aberto e está no Codeplex.

Referencie também Microsoft.CSharp.dll, que deve estar nas suas referências de DLLs .Net. Ele é o assembly que permite usar a palavra chave dynamic, e fornece a infraestrutura de contato com a DLR, entre outras coisas. (Estou esperando desde o Beta 1 essa DLL sumir para dentro do BCL, ou do CLR, mas até agora nada…)

Pronto, infra resolvida. Agora basta fazer os testes.

Crie uma classe de testes, e crie a inicialização do contexto, que basicamente vai consistir em criar o runtime que vai rodar o Ruby. É código padrão, vai ser praticamente sempre igual em toda aplicação. Basicamente a inicialização cria um ScriptRuntime, que define o ambiente de scripting, e então, a partir dele, cria um ScriptEngine, que é quem executa nosso código fonte Ruby. O resto é detalhe. Criei um ScriptEngine estático, que será o padrão, usado por todos os testes. Precisando, podemos criar outro a partir do runtime, se fosse o caso. Ficou assim:

[TestClass]
public class Dado_Um_Contexto_Ruby
{

    private static ScriptRuntime _scriptRuntime;
    private static ScriptEngine _defaultScriptEngine;

    [ClassInitialize]
    public static void E_Um_ScriptRuntime_e_ScripEngine_Padrao_Inicializados(TestContext testContext)
    {
        CriarScriptRuntimeEEnginePadrao();
    }

    public static void CriarScriptRuntimeEEnginePadrao()
    {
        var setup = new ScriptRuntimeSetup();
        setup.LanguageSetups.Add(
            new LanguageSetup(
                "IronRuby.Runtime.RubyContext, IronRuby",
                "IronRuby 1.0",
                new[] { "IronRuby", "Ruby", "rb" },
                new[] { ".rb" }));
        _scriptRuntime = ScriptRuntime.CreateRemote(AppDomain.CurrentDomain, setup);
        _defaultScriptEngine = _scriptRuntime.GetEngine("Ruby");
    }
}

Com isso, precisamos criar algum código fonte Ruby que execute. Se você abrir a console do IronRuby, disponível no executável c:\ruby\bin\ir.exe, vai poder digitar 1+2 e digitar enter. Vai dar o resultado, assim:

Console do IronRuby

Essa é a instrução mais simples do mundo, uma soma simples.

Que tal testar esse essa instrução? Fica assim:

[TestMethod]
public void Consigo_Somar()
{
    var source = "1 + 2";
    var scriptSource = _scriptEngine.CreateScriptSourceFromString(source);
    var resultado = (int)scriptSource.Execute();
    Assert.AreEqual(3, resultado);
}

Mais simples impossível! Na primeira linha criamos o complicadíssimo fonte, “1 + 2”, que é nosso código Ruby (depois vamos ver códigos mais interessantes, mas esse serve por enquanto). Na segunda linha chamamos o método “CreateScriptSourceFromString” no ScriptEngine e passamos nosso código fonte. Depois executamos, pegamos o resultado e comparamos com o esperado. Rode o teste, vai passar.

Esse é o básico. Nos próximos posts eu vou mostrar pra vocês como criar, com Ruby, um objeto a partir de uma classe definida no próprio Ruby e devolvê-la para o C# manipular, como criar um objeto a partir de uma classe definida em C# e usar ela no Ruby e devolver pro C#, e como chamar métodos de C# para Ruby e vice-versa.

Em tempo, todo esse exemplo rodaria no C# 3. A partir do próximo não vai rodar mais.

E se você gostou do Ruby, e está sentindo falta de uma IDE para te ajudar, dê uma olhada na RubyMine da JetBrains, que é a empresa que faz o Resharper. Já vi o Uncle Bob dizendo que usa, e o Brian Marick, do manifesto ágil, com quem pareei no Ágiles 2009, também usa. Eu tenho usado, e, apesar de não ser um Visual Studio (longe disso), é melhor que o Notepad, ou seus amigos anabolizados como Notepad++, Textmate, etc (que continuam mirrados).

Gostaria de saber se vocês se interessam pelo assunto, ou se encerro ele mais cedo por falta de interesse. Garanto que vou chegar em uma aplicação viável de negócio, mas só mais para o final…

Giovanni Bassi

Arquiteto e desenvolvedor, agilista, escalador, provocador. É fundador e CSA da Lambda3. Programa porque gosta. Acredita que pessoas autogerenciadas funcionam melhor e por acreditar que heterarquia é mais eficiente que hierarquia. Foi reconhecido Microsoft MVP há mais de dez anos, dos mais de vinte que atua no mercado. Já palestrou sobre .NET, Rust, microsserviços, JavaScript, TypeScript, Ruby, Node.js, Frontend e Backend, Agile, etc, no Brasil, e no exterior. Liderou grupos de usuários em assuntos como arquitetura de software, Docker, e .NET.