image[1]

Este é o quinto post sobre IronRuby. Veja os anteriores:

  1. Rodando Ruby com C#: Parte 1
  2. Rodando Ruby com C#: Parte 2 – chamando uma classe Ruby no C#
  3. Rodando Ruby com C#: Parte 3 – chamando uma classe C# a partir do Ruby
  4. Rodando Ruby com C#: Edição especial: ASP.Net MVC 2.0, .Net 4.0 e Visual Studio 2010

Este é o post que eu queria escrever desde o começo. Nele vou mostrar um exemplo de uso real do IronRuby na sua aplicação, vou jogar uma idéia, que vai dar a vocês muitas outras idéias.

A idéia é que tenho uma aplicação feita com C#, mas tenho um pequeno pedaço da minha regra de negócios que quero deixar o usuário editar. Uma opção alternativa seria criar um formulário onde o usuário poderia tentar compor uma regra com textboxes, checkboxes e outros controles, e funcionaria, só que eu limitaria a liberdade do usuário na criação da regra. Com IronRuby posso dar o potencial do cliente escrever a regra em código.

Observação: A princípio eu ia fazer com ASP.Net MVC, usando C#. Por algum motivo não está rodando. Os testes funcionam, e funciona com uma aplicação console, mas não funciona com ASP.Net, seja MVC ou Webforms. Se alguém quiser testar, e não funcionar, votem lá no Codeplex para que o time possa olhar. O bug está aqui. Clique aqui para baixar a solution para testar. O projeto original com MVC está lá, assim como o de WebForms.

Como ficou o projeto:

Criei uma camada de apresentação em console app, só com C#, uma camada de domínio com C# e IronRuby, e os testes, só em C#, e às vezes passando código em IronRuby para testar.

Criei um sistema de pedidos. O cliente pode solicitar um pedido. Vejam as entidades:

Dessas, vale a pena ver a de pedido:

public class Pedido
{
    public Cliente Cliente { get; set; }

    public Pedido(Cliente cliente)
    {
        if (cliente == null)
            throw new InvalidOperationException("Cliente não pode ser nulo.");
        Cliente = cliente;
    }

    private List<ItemPedido> _itens = new List<ItemPedido>();

    public void Adicionar(ItemPedido itemPedido)
    {
        _itens.Add(itemPedido);
    }

    public IEnumerable<ItemPedido> Itens { get { return _itens; } }

    public decimal Valor
    {
        get
        {
            return _itens.Sum(i => i.Quantidade * i.Produto.Preco);
        }
    }

    public int Id { get; set; }
}

As classes do projeto são simples, sem grandes comportamento, praticamente só getters e setters. A classe de pedido é mais esperta e expõe apenas os itens como um Enumerable, mas só permite incluir itens através de seu método.

Há dois serviços também:

O ServicoCriacaoDePedido cria pedidos com o método Criar, passando a eles um objeto de valor chamado de CriarPedido:

Ele então retorna um pedido criado.

O serviço mantém ainda na propriedade RegrasPosCriacaoDoPedido uma lista estática de regras que acontecem após a chamada de criação do pedido.  Você pode criar regras chamando o método Incluir, e pode limpá-las chamando o método Limpar.

Quando o serviço criar um pedido, ele submete o pedido ao método privado RodarRegraSobreOPedido. É lá que as regras são aplicadas. As regras podem ser envio de e-mails, descontos, etc. Vejam a classe de regras:

public abstract class AposCriacaoPedido
{
    public AposCriacaoPedido()
    {
        Id = Guid.NewGuid();
    }
    public abstract void Rodar(Pedido pedido);
    public string Codigo { get; set; }
    public Guid Id { get; set; }
}

Tem um método rodar, que recebe um pedido e é abstrato, ou seja, vai ter que ser implementado por clases herdeiras. É neste método que acontece a regra. A classe tem ainda um Id para identificação. A parte onde você percebe que há algo diferente é na propriedade Codigo. É lá que fica o código do IronRuby. Ele fica lá apenas para referência, já que, vocês verão em breve, teremos classes IronRuby filhas desta classe.

É aí que entra o serviço de criação de regras, a outra classe de serviços, a da direita. Ela tem somente um método público chamado CriarRegraPosCriacaoPedido. Bamos vê-lo:

public AposCriacaoPedido CriarRegraPosCriacaoPedido(string codigo)
{
    const string templateDoMetodo =
        @"
        include RegrasDinamicas::Model
        class {0} < AposCriacaoPedido
            def Rodar(pedido)
                {1}
            end
        end";

    var nomeClasseTemp = "AposCriacaoPedido_" + Guid.NewGuid().ToString().Replace("-", string.Empty);
    var codigoCompleto = string.Format(templateDoMetodo, nomeClasseTemp, codigo);
    _engine.Execute(codigoCompleto, _scope);
    var classe = _runtime.Globals.GetVariable<RubyClass>(nomeClasseTemp);
    var retornoCriacao = _operations.CreateInstance(classe);
    var aposCriacaoPedido = (AposCriacaoPedido)retornoCriacao;
    aposCriacaoPedido.Codigo = codigo;
    return aposCriacaoPedido;
}

Notem que este método cria com código Ruby uma classe com nome aleatório. O template desta classe é uma string, que possui dois placeholders. O zero é o nome da classe, gerado via Guid, e colocado na variável nomeClasseTemp para depois ser substituído no template. O placeholder 1 é o corpo do método da regra, passado pelo usuário, e que fica na variável codigo. O código completo fica na variável codigoCompleto. A classe gerada herda da classe de regras, e portanto pode ser instanciada e feito cast dela de uma classe dinâmica do Ruby para a classe do C#. A partir daí, a classe de regra está gerada. Ela recebe na propriedade Codigo o código passado pelo usuário, e é então repassada para o chamador, que a coloca na coleção estática de regras do serviço de criação de pedidos, que a aplica sempre que um pedido é criado.

O resto desta classe de serviço é código de infra que já vimos nos outros posts.

Tem mais uns negócios interessantes no projeto, como repositórios, e um padrão MVC para escrita na console. Vale a pena dar uma olhada.

A partir daí é só utilizar. Vejam o controller de regras (um pouco resumido):

class RegrasController : IRegrasController
{
    private IList<AposCriacaoPedido> _regras;
    private IRegrasView _regrasView;
    private readonly ServicoCriacaoDeRegras _servicoCriacaoDeRegras = new ServicoCriacaoDeRegras();

    public RegrasController(IRegrasView regrasView)
    {
        _regrasView = regrasView;
    }

    public void ExibirRegras()
    {
        if (_regras == null)
            _regras = ServicoCriacaoDePedido.RegrasPosCriacaoDoPedido.ToList();
        var proximaAcao = _regrasView.Listar(_regras);
        proximaAcao(this);
    }

    public void CriarNova()
    {
        var proximaAcao = _regrasView.ExibirNova();
        proximaAcao(this);
    }

    public void CriarNova(string codigo)
    {
        var regra = _servicoCriacaoDeRegras.CriarRegraPosCriacaoPedido(codigo);
        ServicoCriacaoDePedido.IncluirRegraPosCriacaoDoPedido(regra);
        _regras = null;
        ExibirRegras();
    }

    public void ExibirRegra(int posicaoSelecionada)
    {
        if (posicaoSelecionada == -1)
            return;
        var pedido = _regras[posicaoSelecionada];
        var proximaAcao = _regrasView.ExibirRegra(pedido);
        proximaAcao(this);
    }
}

Posso listar as regras, que passa pelo método ExibirRegras:

Posso listar uma regra:

Notem no método CriarNova, que o serviço de regras é utilizado. Isso me permite criar uma regra (com multi line):

E entao executá-la ao inserir um pedido:

Note a mudança no preço. Ao alterarmos o preço de um produto, todos os pedidos mudam (pequeno erro de modelagem…).

O código está disponível para vocês baixarem. Depois vou postar sobre o MVC que usei no projeto, que achei que ficou interessante.


Postado na(s) categoria(s) IronRuby pelo Giovanni Bassi em 2 de fevereiro de 2010 às 03:00 | Tags: , , ,

Comentários


fevereiro 2. 2010 08:02
tucaz
Massa! Mas e aquele ponto que conversamos na última reunião? Em todos os seus exemplos até agora você usou código Ruby dentro de strings em C#. E com o uso de arquivos.rb? Quero ver um exemplo desses Smile

http://blog.tucaz.net/http://blog.tucaz.net/


fevereiro 2. 2010 09:08
Ari C. Raimundo
Muito bom Giovanni !
Agora é só esperar o bug com o ASP.NET ser corrigido... Smile
Um abraço!

http://araimundo.blogspot.com/http://araimundo.blogspot.com/


fevereiro 2. 2010 11:36
Giovanni Bassi
Tuca, na segunda parte eu mostrei como faz isso. E na edição de integração com MVC também.
Smile

http://unplugged.giggio.net/http://unplugged.giggio.net/

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

«  julho 2010  »
seteququsedo
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678
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