Este artigo foi traduzido por máquina.

O programador

Introdução ao Oak: Validação e empacotamento de dados

Ted Neward

Ted NewardPara três colunas agora, estive explorando a abordagem de "dinâmica-y" objeto que carvalho traz para o espaço de aplicativo da Web, e tem sido uma jornada interessante, completa com alguns desafios às crenças antigas sobre como aplicações Web precisam ser construído (ou sobre a plataforma sobre a qual são construídos). Mas cada passeio tem que ter um fim um dia, e está na hora de embrulhar minha exploração de carvalho. Eu tenho que descobrir como certifique-se de dados colocados no sistema pelo usuário são realmente bom, para começar.

Mas primeiro...

Comentário

Se você volta e olha para o sistema como eu deixei da última vez, tentando adicionar um comentário produz outro desses erros útil, desta vez informando-lhe que "Blog.Controllers.Blog não contém uma definição para 'AddComment.'" Ao contrário do que poderia acontecer em um sistema estaticamente tipado — onde a falta deste método iria tropeçar um erro de compilação na frente do ciclo compilar/executar/implantar — em um sistema dinâmico, esses erros não serão vistos até que eles estão realmente tentados. Alguns defensores da linguagem dinâmica afirmam que isto é parte do charme das linguagens dinâmicas, e certamente não precisar se preocupar com mantendo tudo consistente em todo o sistema pode ser um grande benefício, quando a mente está em chamas com uma idéia e você só precisa sair essa idéia para o mundo. Mas a maioria dos desenvolvedores Ruby-on-Rails, eu sei que fiz um projeto maior do que a sua típica aplicação de lista de Todo será o primeiro a admitir que, em um aplicativo de linguagem dinâmica, testes abrangentes são essenciais para manter a qualidade do projeto alta e sanidade do desenvolvedor forte. Então teste tem que ser uma parte de qualquer esforço sério com carvalho.

Infelizmente, assim que eu começar a falar sobre o teste, eu começo a entrar em várias áreas de discussão (testes unitários, testes de comportamento, testes de integração, desenvolvimento controlado por testes e assim por diante) que pode facilmente consumir outra questões revista meia dúzia por conta própria, e não quero quebrar essa caixa de Pandora aqui. O que quer que sua metodologia de testes ou preferência, basta dizer que você deve ter algum tipo de teste de presença em um aplicativo de Carvalho (ou qualquer aplicativo, mas a necessidade é muito maior em qualquer ambiente dinamicamente digitado), no entanto que você escolher testar.

Enquanto isso, o método AddComment ainda está desaparecido.

Comentário fora

Neste caso, quando o usuário digita um comentário no modo de exibição, ele envia para o método de HomeController comentários, que se parece com isso:

[HttpPost]
public ActionResult Comments(dynamic @params)
{
  dynamic blog = blogs.Single(@params.BlogId);
  blog.AddComment(@params);
  return RedirectToAction("Index");
}

Como você pode ver, o controlador é primeiro obter o ID do blog escondido no parâmetro BlogId vindo do formulário e, em seguida, usá-lo para encontrar a entrada correspondente do blog via o método único sobre o DynamicRepository, depois chamar o blog.AddComment para armazenar o comentário. (Novamente, apenas para fazer os pontos, "pro" e "contras": Este código tem estado em vigor desde a segunda parte desta série, e eu só agora estou correndo para o fato de que o método AddComment ainda não existia até agora.)

Definindo este método é muito simples; na classe de Blog, adicione este método:

void AddComment(dynamic comment)
{
  // Ignore addition if the body is empty
  if (string.IsNullOrEmpty(comment.Body)) return;
  // Any dynamic property on this instance can be accessed
  // through the "_" property
  var commentToSave = _.NewComment(comment);
  comments.Insert(commentToSave);
}

O único verdadeiro ponto de interrogação neste método é o uso do sublinhado (_.NewComment(comment)), que é um espaço reservado para a referência de "presente". O sublinhado tem plena consciência da natureza dinâmica deste objeto, que não a "esta" referência; em vez de se preocupar com as diferenças, o sublinhado permite que você use tudo "isto" seria, mais mais.

Observe como a natureza dinâmica do sistema permite que você seja extremamente frugal no código. Os parâmetros de formulário são capturados em um pacote nomeado no "@params" entrando para o controlador, e aqueles são passados sem descompactar algum deles diretamente para adicionar­comentário, que por sua vez passa-los para Novo_comentário, que constrói um objeto dinâmico fora delas, e o objeto resultante fica inserido comentários DynamicRepository. Onde vêm os nomes das propriedades do objeto? Do formulário HTML que originou tudo isto.

Maluco, né? Quase parece que você está sendo preguiçoso ou algo assim.

De qualquer forma, dar-lhe uma corrida, e claro o suficiente, agora comentários estão sendo adicionados em.

Validar-Me

Como está escrito, porém, o sistema tem uma grande falha (bem, de acordo com exigências de usuário, de qualquer forma): É perfeitamente admissível para duas entradas de blog ter o exato mesmo título, e que não é OK. (Leitores podem ficar confusos sobre qual para ler, a um intitulado "LOL ruivas" ou o outro intitulado "LOL ruivas.") Assim, você precisa de uma maneira de impor algum tipo de exclusividade sobre o código, para que usuários não podem acidentalmente colocar no blog intitulado de duplicar entradas.

Em um quadro da Web tradicional, isso seria um trabalho de duas partes. Primeiro, o modelo de objeto (Blog) teria que definir algum tipo de método "perguntar-me se estou válido", que chamarei IsValid por falta de algo realmente original e uma definição de validação disse dentro do objeto, que chamarei valida. E, como se poderia suspeitar da forma como o método de associados trabalhou para ajudar a definir a relação entre Blog e comentário de objetos (ou seja, é um nome predefinido para o qual Carvalho sabe olhar), o método valida funciona da mesma forma, se um objeto define um método valida e, em seguida, carvalho chamará o método IsValid já definido no objeto, que por sua vez irá procurar por um método valida e pedir todas as condições que fazem com que este objeto válido.

No código, que se parece com isto:

IEnumerable<dynamic> Validates()
{
  // And define the association
  // For other examples of validations, check out the Oak wiki
  yield return new Uniqueness("Name", blogs);
}

Novamente, você vê o uso de um fluxo de objetos que descrevem os requisitos de validação, devolveu um IEnumerable < dinâmico > para o fluxo, gerado através do uso da facilidade "yield return" do c#. E, como com a classe de esquema da última vez, a maneira de estender esta é apenas aderência em elementos adicionais que são "rendimento retornado," da seguinte forma:

IEnumerable<dynamic> Validates()
{
  // And define the association
  // For other examples of validations check out the Oak wiki
  yield return new Uniqueness("Name", blogs);
  yield return new Length("Name") { Minimum=10, Maximum=199 };
}

O Wiki de carvalho define a lista completa e o uso de objetos de validação, mas alguns as notáveis, que são:

  • Presença: Um campo não é opcional e deve estar presente no objeto.
  • Aceitação: Um campo no objeto deve conter um valor específico, como um objeto LegalDocument que contém um campo de TypedOutAcceptance em que o usuário digitou a seqüência de caracteres "Eu aceito" para indicar que ele aceitou as restrições legais.
  • Exclusão: Um campo não pode incluir determinados valores.
  • Inclusão: Um campo deve ser parte de um conjunto de certos valores.
  • Formato: A validação da expressão regular de uso geral (usando o Microsoft .NET Framework Regex classe).
  • Numericality: Praticamente tudo a ver com valores numéricos, incluindo a marcação de um campo como "inteiro,"somente maior -­do que e menos-do que as restrições, ou simples testes par/ímpar.
  • Condicional: O pega-tudo "escotilha" para qualquer validação não abrangidos em outros lugares — um campo deve satisfazer uma condição descrita usando uma função lambda.

O ultimo, condicional, não é na verdade uma validação digite em e de si mesmo, mas uma característica presente na maioria (se não todos) dos outros tipos de validação e, portanto, merece um pouco mais de explicação. Imagine um objeto de ordem, de ordens em um sistema de comércio eletrônico tradicional. Para os sistemas ditos, um número de cartão de crédito só é necessário se o usuário deseja utilizar um cartão de crédito para pagamento. Da mesma forma, um endereço para o qual deseja enviar o produto só é necessário se o usuário comprou outra coisa senão um download digital. Estas duas contingências ordenadamente são expressos usando duas validações condicionais, conforme mostrado no Figura 1.

Figura 1 validação condicional

public class Order : DynamicModel
{
  public Order()
  {
  }
  public IEnumerable<dynamic> Validates()
  {
    yield return new Presence("CardNumber") {
      If = d => d.PaidWithCard()
    };
    yield return new Presence("Address") {
      Unless = d => d.IsDigitalPurchase()
    };
  }
  public bool PaidWithCard()
  {
    // Could use This().PaymentType instead
    return _.PaymentType == "Card";
  }
  public bool IsDigitalPurchase()
  {
    // Could use This().ItemType instead
    return _.ItemType == "Digital";
  }
}

Cada um dos objetos condicionais está fazendo uso de uma propriedade no objeto de presença — juntamente com um lambda que gera um valor de verdadeiro/falso — para indicar se a presença valida com êxito. No primeiro caso, a presença retorna true (pass) se d.PaidWithCard, um local método que retorna true se o campo PaymentType é igual a "Placa", retorna true. No segundo caso, presença retorna true a menos que isDigitalPurchase retorna true, que significa que, se for um item digital, nenhum endereço é necessário.

Todos estes estão prontos para uso com qualquer objeto derivado de carvalho DynamicModel e, como anotado na coluna anterior (msdn.microsoft.com/magazine/dn519929) e na introdução deste, o DynamicModel -­objeto derivado não precisa definir explicitamente os campos que fazem referência a estas validações. Estas validações não devem ser suficientes para a tarefa, a propósito, esses são todos definidos no arquivo Validations.cs dentro da pasta do projeto scaffolded de carvalho. É bastante simples para definir um novo, se desejado: Só herdam de Oak.Validation e definir no mínimo um Validate método que retorna verdadeiro/falso. A validação de exclusão, por exemplo, é o seguinte:

public class Exclusion : Validation
{
  public Exclusion(string property)
    : base(property)
  {
  }
  public dynamic[] In { get; set; }
  public bool Validate(dynamic entity)
  {
    return !In.Contains(PropertyValueIn(entity) as object);
  }
}

In Propriedade nesse código é o campo no qual são armazenados os valores excluídos; Além disso, é bastante simples. Se uma mensagem de erro descritiva precisa ser incluído, validação fornece uma propriedade base, ErrorMessage, no qual uma mensagem descritiva pode ser armazenada para uso se a validação falhar.

(Para aqueles que estão curiosos sobre as "associações" partir das discussões de banco de dados da última vez, estes são definidos no Association.cs na mesma pasta, derivam de Oak.Association, e — como se poderia esperar — são um pouco mais complicado de explicar. Felizmente, carvalho tem a maioria das associações relacionais tradicionais já definidas, então não deve haver muita necessidade para personalizar aqui.)

Pedaços de carvalho

Às vezes, partes de uma biblioteca parecem muito legais, mas obstáculos adotando a coisa toda, e deseja você vai arrancar uma pequena parte dele e seguir em frente. Enquanto é geralmente melhor usar Carvalho como um todo, ele suporta a idéia de arrancar pedaços do mesmo (como as porções de banco de dados dinâmico, ou talvez apenas as porções de objeto dinâmico, chamado Gemini, que abordei na edição de agosto de 2013, no msdn.microsoft.com/magazine/dn342877) e usá-los autônomos, sem o resto do sistema. A página do GitHub Carvalho sobre o assunto (bit.ly/1cjGuou) tem os pacotes NuGet para cada um do standalone Carvalho peças, reproduzidas aqui (como esta escrito) para sua conveniência:

  • Carvalho instalar o pacote: Esta é a suíte completa de carvalho, inclui fichários de modelo MVC, geração de esquema, o DynamicModel de carvalho e suporte a classes, uma versão alterada do Massive (DynamicRepository) e o núcleo de carvalho dinâmico construir Gemini.
  • pacote de instalação Carvalho-json: Esta é a parte de carvalho no que diz respeito a serialização JSON (pode ser usado em APIs REST).
  • cambium do pacote de instalação: Esta é a parte de carvalho que exclui os componentes específicos do MVC e exclui a geração de esquema. Cambium inclui o DynamicDb de carvalho, DynamicModel, uma versão alterada do Massive (Dynamic­repositório) e a dinâmica do núcleo Carvalho construir Gemini.
  • semente do pacote de instalação: Esta é a geração de esquema de carvalho. Este pacote NuGet também inclui a versão alterada do Massive (usado para inserir dados de amostra). Ele não contém qualquer um dos fichários do modelo MVC, ou DynamicModel de carvalho ou classes de suporte.
  • gemini instalar o pacote: Isto irá instalar apenas a núcleo dinâmico construção sobre a qual todos a bondade dinâmica em carvalho é construída.

Antes de experimentar qualquer um em pedaços, eu sugiro tentar toda a experiência para ter uma idéia de como cada um deles se encaixa a imagem maior.

Benefícios, custo e dor

Como pode ser inferida a partir dessas quatro colunas, existem benefícios definitivos para ser capaz de simplesmente "improvisar" e trabalhar com um sistema mais dinamicamente tipado. Sem dúvida, os custos e a dor irão disparar suas cabeças feias em tal sistema (particularmente para os incautos e aqueles não utilizados para escrever testes), mas mesmo aqueles que são os mais obstinados estaticamente tipados fanáticos podem aprender algumas idéias importantes de um sistema como o carvalho. Mais importante, carvalho pode ser uma ferramenta extremamente valiosa para prototipagem o início do desenvolvimento de um sistema, quando o modelo de objeto é ainda altamente mutável e indefinido. O melhor de tudo, graças a plataforma subjacente de Carvalho (ou seja, .NET), torna-se bastante viável para sugerir construindo um aplicativo MVC em carvalho nas fases iniciais, em seguida, lentamente capotar partes para uma mais estaticamente digitado (e, assim, compilador-verifiquei e aplicadas pelo compilador) abordar como os detalhes do aplicativo get mais firmemente fechado.

Pessoalmente falando, sem dúvida, o carvalho é um projeto legal. Na minha opinião, este é um daqueles tempos quando um monte de idéias e funcionalidade interessante sair um pacote muito pequeno (relativamente falando). Carvalho, definitivamente, entra em minha caixa de ferramentas pessoal de truques.

Boa codificação.

Ted Neward é o diretor da Neward & Associates LLC. Ele tem escrito mais de 100 artigos e autor e co-autor de vários livros, incluindo "Professional F # 2.0" (Wrox, 2010). Ele é um F # MVP e fala em conferências em todo o mundo. Ele consulta, mentores regularmente — contatá-lo em ted@tedneward.com se você está interessado em ter-lhe vir trabalhar com sua equipe, ou ler seu blog em blogs.tedneward.com.

Agradecemos ao seguinte especialista técnico pela revisão deste artigo: Amir Ribeiro (criador do projeto de carvalho)
Amir Ribeiro é o criador de carvalho. Ele é um membro ativo da comunidade de desenvolvimento e tem especialização em ASP.NET MVC, HTML5, arquiteturas de resto, Ruby, JavaScript/CoffeeScript, NodeJS, iOS/ObjectiveC e F #. Ribeiro é uma verdadeira poliglota com uma paixão inabalável para software. Ele está no Twitter em @amirrajan e na Web em github.com/amirrajan, amirrajan.net e improvingenterprises.com.