A linguagem Ruby vem ganhando muito espaço no mercado, talvez a linguagem dinâmica mais badalada atualmente, mas o objetivo do artigo não é ensinar Ruby, vuo mostrar como podemos integrar o C# com o Ruby.

Como não vou ensinar Ruby aqui, vou deixar aqui uma dica de quem considero referência quando o assunto é .NET + Ruby, que é o site do Vinicius Quaiato, ele também está estudando bastante o assunto, e sempre conversamos a respeito. Muito do que mostrarei aqui ele postou no blog dele, e claro tenho autorização do mesmo para utilizar alguns códigos dele aqui.

Eu sempre pensei que algumas partes dos softwares que desenvolvemos, deveriam ser mais flexíveis, o que eu quero dizer como flexível é o usuário, ou alguém de um setor de suporte, ou mesmo desenvolvedores, alterarem uma regra de negócio da aplicação sem precisar recompilar o software, abrir o Visual Studio e por ai vai.

Não vou entra no mérito da questão se o usuário vai entender o código, vai saber o que escrever, não vem ao caso, criar um parser de ruby ou outra linguagem para algo mais simples não é tão complicado.

Chega de falar, e vamos ao que interessa.

O primeiro passo é instalar o IronRuby.

Depois de instalado, referênciar as 3 .dll´s abaixo, que ficam no diretório bin da instalação do IronRuby.

  • IronRuby.dll
  • IronRuby.Libraries.dll
  • Microsoft.Scripting.dll

A primeira classe que temos é chamada de EngineRuby que utiliza o padrão Singleton para armazenar uma instância da classe ScriptEngine que está disponível no namespace Microsoft.Scripting.Hosting.

A classe EngineRuby também tem um método ObterObjeto que recebe como parâmetro o nome do arquivo Ruby que contém a classe feita em Ruby que vamos utilizar, e o nome da classe feita com Ruby. Esse método retorna um tpo dinâmico do C#, atenção a keyword dynamic.

Para facilitar o entendimento, a idéia é ter uma pasta chamada Ruby no diretório da aplicação onde vamos armazenar os arquivos .rb do Ruby.

public static class EngineRuby
{
    private static ScriptEngine rubyEngine;

    private static ScriptEngine CriarEngine()
    {
        if(rubyEngine == null)
            rubyEngine = Ruby.CreateEngine();

        return rubyEngine;
    }

    public static dynamic ObterObjeto(string nomeDoArquivo, string nomeDaClasse)
    {
        var diretorioDaAplicacao = Path.GetDirectoryName(typeof (EngineRuby).Assembly.Location);
        var arquivo = Path.Combine(diretorioDaAplicacao, String.Format("Ruby\\{0}.rb", nomeDoArquivo));

        CriarEngine().ExecuteFile(arquivo);
        dynamic objeto = CriarEngine().Runtime.Globals.GetVariable(nomeDaClasse);
        return CriarEngine().Operations.CreateInstance(objeto);
    }
}

Bom, antes de continuar vamos falar do arquivo Ruby, para desenvolvedor códigos em Ruby, eu utilizo o RubyMine da JetBrains. É o melhor editor que encontrei até o momento.

O código é simples, uma classe chamada Boletim que possui um método Media. Passo para o método nota para os 4 bimestres, e divido a soma por 4, uma média simples, o que faremos no decorrer do artigo é alterar essa regra de calculo da média diretamente pelo aplicativo de exemplo.

Segue o código.

class Boletim
  def Media(primeiroBimestre, segundoBimestre, terceiroBimestre, quartoBimestre)
    (primeiroBimestre + segundoBimestre + terceiroBimestre + quartoBimestre) / 4
  end
end

Agora teremos uma classe Boletim escrita em C#, que vai armazenar algumas informações como o nome do arquivo Ruby referente, e o nome da classe em Ruby.

public class Boletim
{
    private static string NomeDaClasse = "Boletim";
    private static string NomeDoArquivo = "Boletim";

    private static dynamic Objeto
    {
        get
        {
            return EngineRuby.ObterObjeto(NomeDoArquivo, NomeDaClasse);
        }
    }

    public static double Media(double primeiroBimestre, double segundoBimestre, double terceiroBimestre, double quartoBimestre)
    {
        return Objeto.Media(primeiroBimestre, segundoBimestre, terceiroBimestre, quartoBimestre);
    }
}

Bom para quem viu o artigo do Vinicius Quaiato, eu modifiquei a propriedade Objeto, no caso dele, novamente ele utilizar o padrão Singleton eu não faço isso aqui por que quero alterar o código Ruby rodando meu aplicativo, e a próxima vez que o mesmo for executado quero executar as alterações no código.

Agora a parte fácil, crio uma tela onde consigo informar as notas para cadas bimestre, um botão para calcular a média e mostro o restultando.

Boletim

O código que é executado quando o botão Calcular é pressionado é simples.

private void button1_Click(object sender, RoutedEventArgs e)
{
    txtMedia.Text = Boletim.Media(
                                    double.Parse(txtPrimeiroBimestre.Text),
                                    double.Parse(txtSegundoBimestre.Text),
                                    double.Parse(txtTerceiroBimestre.Text),
                                    double.Parse(txtQuartoBimestre.Text)
                                 ).ToString();
}

Reparem na imagem acima, que ao lado do botão Calcular tem outro botão, clicando nesse botão uma tela será aberta mostrando o código do arquivo Ruby que possui a regra para a média do boletim.

O código dessa tela é simples, segue o código completo, mas basicamente só possui leitura e escrita de arquivo Smile.

public partial class EditorDeCodigo : Window
{
    public string NomeDoArquivo { get; set; }

    public EditorDeCodigo()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        LerArquivo();
    }

    private void btnCancelar_Click(object sender, RoutedEventArgs e)
    {
        Close();
    }

    private void btnGravarAlteracoes_Click(object sender, RoutedEventArgs e)
    {
        SalvarArquivo();
        Close();
    }

    private string ObterArquivo()
    {
        var diretorioDaAplicacao = System.IO.Path.GetDirectoryName(typeof(EngineRuby).Assembly.Location);
        return System.IO.Path.Combine(diretorioDaAplicacao, String.Format("Ruby\\{0}.rb", NomeDoArquivo));
    }

    private void LerArquivo()
    {
        using (var textReader = new StreamReader(ObterArquivo()))
        {
            txtCodigo.Text = textReader.ReadToEnd();
            textReader.Close();
        }
    }

    private void SalvarArquivo()
    {
        using (var textWriter = new StreamWriter(ObterArquivo()))
        {
            textWriter.Write(txtCodigo.Text);
            textWriter.Close();
        }
    }
}

Abaixo imagem da edição do código Ruby durante a execução do aplicativo.

EditarCodigo

Acho que um vídeo é o ideal para mostrar a idéia funcionando, então vejam abaixo um pequeno vídeo com a execução do aplicativo.

Integração C# + Ruby from Márcio Fábio Althmann on Vimeo.

Essa foi uma pequena amostra de como podemos utilizar essa integração do C# + Ruby, o download da solução pode ser feito abaixo.

Abraços.

  • http://viniciusquaiato.com Vinicius Quaiato

    Ah muleeeeeeeque!

    Show hein! Vamos que vamos difundindo o Ruby/IronRuby pra galera!

    Esse é um exemplo bacana, é só usar a criatividade. Imagine os cálculos fiscais, que variam bagarai, podendo ser abstraídos assim com uma linguagem dinâmica, estando a parte.
    “Puts, mudou o cálculo!!!”
    “Ah ok, fizemos isso em Ruby, basta alterar o script”
    Lindo!

    Parabéns!
    \o/

    Att,
    Vinicius Quaiato.

    • Márcio Fábio Althmann

      É isso ai, quando pensei foi logo em calculos de imposto e por ai vai hehe, mas boletim é mais fácil de explicar heueheue.

      E vamos que vamos com .net + ruby

      Abraços

  • http://www.mbanagouro.net/blog Michel Banagouro

    Show de bola!
    Muito bacana o artigo e mostra claramente a facilidade de integrar as duas linguagens.
    Parabéns!

  • http://www.fabriciosanchez.com.br Fabrício Sanchez

    Grande Márcio…

    Parabéns cara!
    Não manjo de IronRuby mas foi possível entender perfeitamente a idéia… show!

  • Pingback: IronPyton, IronRuby, DLR e o que isso influencia na minha vida:??: « Felipe Pedroti Raymundo

  • Oops Twitter isnt working at the moment