IronRuby

Feliz ano novo, pessoal! Vamos começar o ano acelerando.

Volto a falar de IronRuby com C#. Este é o segundo post sobre o assunto, e não vou voltar no básico. Para isso leia o primeiro post, onde mostro como configurar o básico dos testes, baixar e configurar o IronRuby, e preparar o resto do ambiente:

  1. Rodando Ruby com C#: Parte 1

Nesse post vou mostrar como criar uma classe no IronRuby e chamar ela no C# 4 com Visual Studio 2010.

A classe Ruby que quero rodar contém apenas um método, e não faz nada de mais:

class Teste2
    def Chamar(flag, texto)
        if !flag
            texto
        else
            texto + 'sufixo'
        end
    end
end 

A idéia é criar uma instância dessa classe e após declará-la, retorná-la, tudo no mesmo script. No Ruby, a última linha retorna o resultado. Pra isso, então basta incluir:

Teste2.new 

Ok, para fazer isso funcionar, é igualzinho o que é feito no post anterior, que fazia uma soma simples:

[TestMethod]
public void ConsigoCriarUmObjetoRubyEChamarUmMetodoDinamicamente()
{
    var source = @"
        class Teste2
            def Chamar(flag, texto)
                if !flag
                    texto
                else
                    texto + 'sufixo'
                end
            end
        end
        Teste2.new";

    var scriptSource = _defaultScriptEngine.CreateScriptSourceFromString(source);
    var teste = scriptSource.Execute();
    //continua...

A linha 17 está iluminada porque ela é quem executa o código. Adivinham qual o tipo da variável “teste”? Um Ruby de pelúcia pra quem disse “dynamic” (mais sobre ela aqui). No exemplo anterior eu fazia cast para inteiro, porque o resultado era um inteiro, produto da soma de 1 mais 2. E agora o resultado é uma classe que só existe no Ruby, e não existe em lugar nenhum no C#, e portanto não pode ser chamada estaticamente. E o C# é uma linguagem estaticamente tipada, ele não conhece a classe Teste2 declarada no Ruby. Só que na versão 4 ele consegue realizar Dynamic Dispatch, enviando a chamada ao método dinamicamente (mais informação sobre Dynamic Dispatch em uma discussão que tivemos no .Net Architects).

Isso significa que consigo chamar quaisquer métodos e propriedades que existam no objeto Ruby. E é isso que faço:

    //continuando...
    var resultado = teste.Chamar(true, "ttt");
    Assert.AreEqual("tttsufixo", resultado);
} 

Na linha 2 estou executando o método “Chamar”, via dynamic dispatch, que existe na classe Teste2, declarada no IronRuby, e executando seu código. Na linha 3 eu verifico se o resultado era o esperado. O código todo do teste fica assim (apenas repetindo para deixar mais claro):

[TestMethod]
public void ConsigoCriarUmObjetoRubyEChamarUmMetodoDinamicamente()
{
    var source = @"
        class Teste2
            def Chamar(flag, texto)
                if !flag
                    texto
                else
                    texto + 'sufixo'
                end
            end
        end
        Teste2.new";

    var scriptSource = _defaultScriptEngine.CreateScriptSourceFromString(source);
    var teste = scriptSource.Execute();
    var resultado = teste.Chamar(true, "ttt");
    Assert.AreEqual("tttsufixo", resultado);
}

E se eu não quiser escrever o corpo do método Ruby no C#, mas defini-lo em outro arquivo? Não tem problema, o ScriptSource sabe executar arquivos também, além de texto puro. Veja este outro teste, onde faço exatamente isso:

[TestMethod]
public void ConsigoCriarUmObjetoRubyAPartirDeUmArquivoEChamarUmMetodoDinamicamente()
{
    var scriptSource = _defaultScriptEngine.CreateScriptSourceFromFile(
        ObterCaminhoArquivo("RubyTeste.rb"),
        Encoding.UTF8);
    var teste = scriptSource.Execute();
    var resultado = teste.Chamar(true, "ttt");
    Assert.AreEqual("tttsufixo", resultado);
} 

O método CreateScriptSourceFromFile lê o arquivo e executa. O arquivo RubyTeste.rb contém praticamente o mesmo código utilizado no teste anterior. Coloquei ele na estrutura do projeto:

image 

Marquei nas propriedades do arquivo o parâmetro “Copy to Output Directory” como “Copy if newer”, o que garante que o arquivo vai estar no mesmo diretório da dll de testes. O método ObterCaminhoArquivo sabe que deve procurar os arquivos Ruby no diretório especificado, olhando na pasta onde a dll está:

private static string ObterCaminhoArquivo(string arquivo)
{
    return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), arquivo);
}

(Fique atento para colocar o encoding correto no arquivo, no meu caso, UTF8, ou vai dar erro na hora de rodar.)

Para executar o script de definição da classe, mas só criar ela quando quisermos é fácil. Aqui está um teste que faz exatamente isso:

[TestMethod]
public void ConsigoExecutarUmArquivoRubyECriarOObjetoDinamicamente()
{
    var scriptCriaClasse = _defaultScriptEngine.CreateScriptSourceFromFile(
        ObterCaminhoArquivo("RubyTeste2.rb"),
        Encoding.UTF8);
    scriptCriaClasse.Execute();
    var scriptInstanciaObjeto = _defaultScriptEngine.CreateScriptSourceFromString("Teste4.new");
    var teste = scriptInstanciaObjeto.Execute();
    var resultado = teste.Chamar(true, "ttt");
    Assert.AreEqual("tttsufixo", resultado);
}

O arquivo RubyTeste2 tem a definição da classe, mas não cria e retorna, ou seja, o retorno é nulo:

class Teste4
    def Chamar(flag, texto)
        if !flag
            texto
        else
            texto + 'sufixo'
        end
    end
end

Na linha 7 do teste eu executo este arquivo, e na linha 9 eu executo a criação da classe. Como o código está executando sob o mesmo contexto, o mesmo ScriptEngine, a definição da classe ainda está no escopo, ele cria uma instância da classe na linha 9 e retorna.

Pelo que andei pesquisando, há outras maneiras de fazer isso, mas só como o método Execute do ScriptSource, e os métodos CreateScriptSourceFromString e CreateScriptSourceFromFile do ScriptEngine você já faz quase toda a interação básica.

Interessante, certo? Já podemos definir arquivos Ruby e executá-los dinamicamente. Será que o IronRuby consegue ele mesmo criar e interagir com objetos .Net? Não perca as cenas do próximo capítulo.


Postado na(s) categoria(s) C# , IronRuby pelo Giovanni Bassi em 4 de janeiro de 2010 às 08:17 | Tags: , ,

Comentar


(Vai mostrar seu Gravatar)

  Country flag

biuquote
  • Comentário
  • Pré-visualização
Loading



Quem é Giovanni Bassi

Giovanni Bassi Sou uma pessoa apaixonada por tecnologia e especificamente por .Net. Sou consultor independente especialista em .Net, focado em arquitetura e melhores práticas. Tenho dezenas de artigos publicados na .Net Magazine, revista da qual sou editor técnico. Ministro palestras e cursos de vez em quando, e quando dá tempo eu respiro um pouco. Mais detalhes nesta página.

Busca

Selos

Eu vou ao TechEd Brasil 2010, e você?

MVP

MCPD

MCSD

.Net Magazine

Abaixo ao if!

Calendário

«  setembro 2010  »
seteququsedo
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910
Ver detalhamento de posts no calendário

Blogs interessantes

    OPMLDownload OPML file

    Postagens recentes

    Comentários recentes

    Disclaimer / Aviso
    As opiniões colocadas neste blog são minhas e pessoais e não expressam necessariamente as opiniões de meus empregadores, pareceiros e amigos. Da mesma forma, os comentários feitos por leitores do blog não expressam a minha opinião.

    © Copyright 2010 .Net Unplugged
    Log in