Markdown: Linguagem e Software

10:59 Vinicius Knob 0 Comments



Em projetos criados com GitHub, um popular sistema de controle de versão on-line, muitas vezes usei um arquivo chamado README.md que contém o readme para o projeto. Esses arquivos podem ser comparados com revisões históricas em texto legível (enquanto que um formato binário não pode ser facilmente comparado). A extensão .md era uma incógnita para mim que estava acostumado com XMLs e TXTs, foi assim que conheci Markdown.

Markdown


A definição para Markdown se dá em dois ambitos: como linguagem e como software. Ambos foram desenvolvidos por John Gruber, com importantes créditos para Aaron Swartz. Sua filosofia baseia-se na facilidade de leitura e escrita, pois isso é viável, valorizando a legibilidade acima de tudo. Markdown é destinada a uma finalidade: um formato de escrita para web, mas sem substituir HTML, pois apenas abrange um pequeno subconjunto de tags.

A linguagem


A linguagem Markdown é de marcação leve e permite que as pessoas escrevam em um formato de texto simples. A idéia principal é que seres humanos possam ler sem perceber as marcações no meio do texto e facilmente converter isso para HTML. Uma das extensões de arquivo para linguagem Markdown é a .md, mas também é reconhecida a extensão .markdown. Um arquivo Markdown deve ser publicado como está, como texto simples.

A sintaxe recebeu influências de vários filtros de texto para HTML, como Setext, atx, Textile, reStructuredText, Grutatext e EtText, porém a maior inspiração para a sintaxe de Markdown é o formato de texto para e-mail. Markdown é composta inteiramente por caracteres de pontuação que definem como formatar o texto sem perceber que foi formatado. Abaixo pode-se conferir alguns exemplos:


Cabeçalhos
Título h1    # Título h1
=======
Título h2    ## Título h2
-----------


Ênfase
*itálico*   **negrito**
_itálico_   __negrito__


Link em linha
Um [exemplo](http://url.com/ "Título")


Listas
* Primeiro item
* Segundo item
* Terceiro item
ou
+ Primeiro item
+ Segundo item
+ Terceiro item

O software


John Gruber criou um programa Perl para converter do formato de texto simples para HTML. O programa, chamado "Markdown", exige Perl 5.6.0 ou posterior e também o módulo Digest::MD5. Markdown está disponível no site Daring Fireball e possui uma versão online chamado de Digus [http://daringfireball.net/projects/markdown/dingus].

Fonte


0 comentários:

Dev In PF: Gramática, Vocabulário e Pragmática

10:00 Vinicius Knob 1 Comments


No dia 06 de abril de 2013 ocorreu o 1º encontro do Dev In PF. Pessoas da área de desenvolvimento de software da cidade de Passo Fundo agora contam com esse grupo que vem com a intenção de proporcionar eventos relacionados a área, fortalecer os laços de amizades e parcerias e estimular o conhecimento. Ver esse grupo nascendo e crescendo é algo espetacular para Passo Fundo e eu estive no 1º encontro apresentando um material "light" sobre boas práticas.



A experiência adquirida no mini-curso da SenaInfo me possibilitou apresentar com calma e confiança. Essa foi a segunda apresentação para um público da área que eu atuo. O público ajudou bastante, pois haviam pessoas que já trabalham na área, o que facilitou a apresentação de boas práticas de programação. Mas de novo esse assunto Vinícius? Novamente eu volto a apresentar boas práticas de programação, pois acredito ser um assunto fundamental e eternamente presente na vida de um profissional do desenvolvimento de software.

Nessa apresentação minimizei a teoria e apliquei mais exemplos, pois isso possibilita prender a atenção do público ao mesmo tempo que facilita o entendimento da mensagem final da apresentação: boas práticas não são uma tarefa de desenvolvimento, mas uma cultura, uma atitude profissional.



Acredito ter acertado no título, no assunto e no público. Dev In PF já possuía outras boas apresentações como TDD, HTML5 e Linguagens e seus ecossistemas, sendo que boas práticas são atitudes empregadas em todas as práticas e linguagens. A apresentação pode ser conferida logo abaixo.

1 comentários:

Dummy SMTP: JavaMail API + DevNullSMTP

18:17 Vinicius Knob 0 Comments

O que quero mostrar é uma forma de testar o envio de e-mails em ambiente de desenvolvimento simulando um falso servidor SMTP com o framework DevNull SMTP. Pretendo mostrar duas formas de fazer isso: uma utiliza JndiHelper (para usuário de WebLogic Server) e outra utiliza Session (classe integrante da API JavaMail).

Com JndiHelper


1. Crie uma pasta no sistema (exemplo: \devNullSMTP)

2. Baixar o jar do framework DevNull SMTP e coloque nessa pasta.
Link: http://www.aboutmyip.com/AboutMyXApp/DevNullSmtp.jar

3. No console do WebLogin Server vá em Mail Session e crie um novo com os seguintes atributos:

Name: SMTPMailSession
JNDI Name: SMTPMailSession
JavaMail Properties:
mail.debug=True
mail.smtp.port=25
mail.transport.protocol=smtp
mail.smtp.host=127.0.0.1

4. Crie dentro da pasta \devNullSMTP a pasta para receber os emails (exemplo: \emails)

5. Executar em linha de comando o seguinte:

java -jar DevNullSmtp.jar -s emails

Para mais comandos ver http://www.aboutmyip.com/AboutMyXApp/DevNullSmtpUsage.jsp

6. No console que irá abrir, clique em Start Server.

7. Pronto. Observações: Todo e-mail enviado vai para pasta \emails com a terminação EML e pode ser visualizada no Notepad++. O console do DevNull SMTP loga todo o processo em 4 abas disponíveis, incluindo o próprio EML. O console do Eclipse efetua logs também, mas nada além do console do DevNull SMTP.

Com Session


O mesmo processo para usuário de JndiHelper com excessão do passo 3, pois as propriedades são setadas em código da seguinte maneira:

Properties props = new Properties();
props.put("mail.debug", "true");
props.put("mail.smtp.port", "25");
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.host", "127.0.0.1");
Session session = Session.getInstance(props, null);

O restante do processo segue da mesma forma.

Conclusão


Esse conhecimento permitiu a independência de desenvolvimento de funcionalidades que utilizam envio de e-mail. Tive muitos problemas no passado com funcionalidades desse tipo e sempre dependia de ambiente configurado para os testes, quando encontrava um erro não era apenas uma correção, mas também tinha que esperar por um deploy em ambiente de homologação. Espero que isso também seja útil para você.

Fonte



0 comentários:

Um conselho...

06:45 Vinicius Knob 0 Comments


Codifique pensando que outras pessoas devem entender seu código, 
por mais que ninguém vá ver, 
pois isso aumenta consideravelmente as chances de no futuro 
você mesmo entender esse código.

0 comentários:

Refatoração na prática

04:01 Vinicius Knob 0 Comments


"Pensar é o trabalho mais pesado que existe. 
Talvez seja por isso que tão poucos o exercitem."
(Henry Ford)

Ser pragmático tem seu valor. Sempre me importei com o código, gosto de ser assim, pois conheço o real valor dessa ação, ou melhor, dessa conduta. Ser pragmático envolve muitos conceitos e acima de tudo, prática. Nesse post quero demonstrar uma prática muito conhecida de quem é pragmático que é a refatoração de código.

Refatorar código não é tão simples assim, exige conhecimento da lógica de negócio e pode ter resultados catastróficos se for feito por alguém que não possua uma visão macro do projeto e possa visualizar todas as dependências possíveis (lembrando que mesmo com os mecanismos de busca das IDEs muita informação está em locais inacessíveis para estas).



O código a seguir envolve um caso onde um controller é responsável por receber dados para o envio de um e-mail. Esse controller deve validar os dados de entrada, montar o e-mail e enviá-lo. Vou mostrar o código “cru” e então começarei a refatorá-lo. Passo a passo pretendo descrever conceitos que estão sendo atendidos e outros que possivelmente estão sendo violados. Será utilizada a linguagem Java para isso.

Case 1
import java.util.Date;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class Case1 {

    public void abstractController() throws Exception {
        
        try {
            // dados recebidos de uma fonte qualquer
            String de = "eu@email.com";
            String para = "qualquer@email.com";
            String assunto = "Refactoring na Prática";
            String corpo = "Corpo da Mensagem";
            
            // validacao de email
            Pattern p = Pattern.compile(".+@.+\\.[a-z]+");  
            
            Matcher m1 = p.matcher(de);  
            if (!m1.matches())  
                throw new IllegalArgumentException("Email address is invalid: \"" + de + "\"");
            
            Matcher m2 = p.matcher(para);  
            if (!m2.matches())  
                throw new IllegalArgumentException("Email address is invalid: \"" + para + "\"");
            
            Properties props = new Properties();
            props.put("mail.host", "127.0.0.1");
            Session session = Session.getInstance(props);
            MimeMessage msg = new MimeMessage(session);
            try {
                msg.setFrom(new InternetAddress(de));
                InternetAddress[] to = {new InternetAddress(para)};
                msg.setRecipients(Message.RecipientType.TO, to);
                msg.setSentDate(new Date());
                msg.setSubject(assunto);
                msg.setText(corpo);
                Transport.send(msg);
            } catch (MessagingException e) {
                // omitido
            }
        } catch (Exception e) {
            // omitido
        }
        
    }

}
A classe Case1 é o primeiro caso que quero mostrar. Em seu método abstractController ela executa a lógica de receber os atributos que identificam um e-mail, validar alguns deles, montar o e-mail utilizando a biblioteca javax.mail, então por fim, enviar o e-mail. Um método com muitas responsabilidades.

Isso funciona?

Sim (se você tiver o SMTP configurado em sua máquina como um servidor local). Acredito que nesse ponto se você não for alguém pragmático esse código é algo normal, mas para mim é um monstro. Quando estudei software a primeira coisa que aprendi lá em Algoritmos é que devo pegar um problema e fragmentá-lo, então resolver os problemas menores sendo que ao final terei resolvido o problema todo. O código acima para mim é um problema e que necessita de fragmentação.

Case 2

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class Case2 {
    
    /**
     * Refactoring:
     * - Criacao da classe EmailAddress
     */
    public void abstractController() {
        
        EmailAddress de = new EmailAddress("eu@email.com");
        EmailAddress para = new EmailAddress("qualquer@email.com");
        
        String assunto = "Refactoring na Prática";
        String corpo = "Corpo da Mensagem";
        
        Properties props = new Properties();
        props.put("mail.host", "127.0.0.1");
        Session session = Session.getInstance(props);
        MimeMessage msg = new MimeMessage(session);
        try {
            msg.setFrom(de.toInternetAddress());
            InternetAddress[] to = {para.toInternetAddress()};
            msg.setRecipients(Message.RecipientType.TO, to);
            msg.setSentDate(new Date());
            msg.setSubject(assunto);
            msg.setText(corpo);
            Transport.send(msg);
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }
}
Nesse ponto do código, uma classe extra foi criada com a intenção de centralizar o e-mail bem como sua validação. A classe EmailAddress tem a responsabilidade de fazer apenas isso, representar um e-mail. Vale lembrar que em um refactoring não se pode alterar o fluxo que a aplicação já possui, pois isso seria tarefa em uma correção ou manutenção. A classe EmailAddress segue o padrão Tiny Type, que são pequenos tipos de objeto que encapsulam pouca lógica.

EmailAddress

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;

/**
 * Tiny Type Pattern
 *
 * @author Vinicius Martins Knob
 */
public final class EmailAddress {

    private final InternetAddress _emailAddress;

    /**
     * Cria um EmailAddress a partir de um email
     * @param emailAddress
     * @throws AddressException
     */
    public EmailAddress(String emailAddress) throws AddressException {
        validar(emailAddress);
        _emailAddress = new InternetAddress(emailAddress);
    }

    private void validar(String emailAddress) {
        Pattern p = Pattern.compile(".+@.+\\.[a-z]+");  
        Matcher m = p.matcher(emailAddress);  
        if (!m.matches())  
            throw new IllegalArgumentException("Email address is invalid: \"" + emailAddress + "\"");
    }
    
    @Override
    public String toString() {
        return _emailAddress.getAddress();
    }
    
    public InternetAddress toInternetAddress(){
        return _emailAddress;
    }
    
    /**
     * Retorna uma lista imutavel de EmailAddress a partir de um array de emails.
     * @param emails
     * @return
     * @throws AddressException
     */
    public static List<EmailAddress> asList(String... emails) throws AddressException {
        if (emails.length == 0)
            return Collections.emptyList();
        
        List<EmailAddress> foo = new ArrayList<EmailAddress>();
        for (String bar : emails) {
            foo.add(new EmailAddress(bar.trim()));
        }
        return Collections.unmodifiableList(foo);
    }
}
O que ganho com isso?

Em um projeto grande, ter aquele trecho de código responsável por validar o e-mail espalhado por toda parte seria inadmissível. Uma alternativa encontrada muitas vezes por mim em outros projetos é uma classe com métodos estáticos onde exista um método validarEmail(), não que seja errado, mas quando criei a classe EmailAddress estive pensando em conceitos muito discutidos pela comunidade de software como coesão, responsabilidade única, legibilidade e baixo acoplamento. EmailAddress é uma classe que quando você vê tem plena noção de seus limites e responsabilidades, pois ela é simples e clara.

Case 3
import java.util.Date;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class Case3 {

    /**
     * Refactoring:
     * - Criacao da classe Email
     */
    public void abstractController() {

        EmailAddress de = new EmailAddress("eu@email.com");
        EmailAddress para = new EmailAddress("qualquer@email.com");

        Email email = Email.create()
                .from(de)
                .to(para)
                .subject("Refactoring na Prática")
                .body("Corpo da Mensagem")
            .build();

        Properties props = new Properties();
        props.put("mail.host", "127.0.0.1");
        Session session = Session.getInstance(props);
        MimeMessage msg = new MimeMessage(session);
        try {
            msg.setFrom(de.toInternetAddress());
            InternetAddress[] to = {para.toInternetAddress()};
            msg.setRecipients(Message.RecipientType.TO, to);
            msg.setSentDate(new Date());
            msg.setSubject(email.getSubject());
            msg.setText(email.getBody());
            Transport.send(msg);
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }
}
No caso 3 externalizei uma segunda parte da lógica, a montagem do e-mail. A montagem consiste na coleta dos dados e suas respectivas validações. Para isso criei a classe Email. Ela segue o padrão Builder.

Email

import java.text.MessageFormat;
import java.util.List;

/**
 * 
 * Esta classe segue o Builder Pattern
 *
 * @author Vinicius Martins Knob
 */
public class Email {
    
    private EmailAddress _from;
    private List<EmailAddress> _to;
    private String _subject;
    private String _body;
    
    private Email() {}
    
    public EmailAddress getFrom() {
        return _from;
    }
    public List<EmailAddress> getTo() {
        return _to;
    }
    public String getSubject() {
        return _subject;
    }
    public String getBody() {
        return _body;
    }
    
    @Override
    public String toString() {
        String pattern = "[from: {0}, to: {1}, subject: {2}, body: {3}]";
        Object[] data = {_from, _to.toString(), _subject, _body};
        return MessageFormat.format(pattern, data);
    }
    
    @Override
    public boolean equals(Object obj) {
        return this.toString().equals(obj.toString());
    }

    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result * (_from==null?0:_from.hashCode());
        result = 31 * result * (_to==null?0:_to.hashCode());
        result = 31 * result * (_subject==null?0:_subject.hashCode());
        result = 31 * result * (_body==null?0:_body.hashCode());
        return result;
    }
    
    /**
     * 
     * Builder Method - Builder Pattern
     * 
     */
    public static EmailBuilder create() {
         return new EmailBuilder();
    }
    
    /**
     * 
     * Builder Class - Builder Pattern
     * <br>Inner Class
     *
     */
    public static class EmailBuilder{
        private final Email _email = new Email();
        
        private EmailBuilder(){}
        
        public EmailBuilder from(EmailAddress from){
            _email._from = from;
            return this;
        }
        public EmailBuilder to(List<EmailAddress> to){
            _email._to = to;
            return this;
        }
        public EmailBuilder subject(String subject){
            _email._subject = subject;
            return this;
        }
        public EmailBuilder body(String body){
            _email._body = body;
            return this;
        }
        public Email build(){
            return _email;
        }
        
    }
    
}
A classe Email contempla o padrão Builder, padrão aplicado em virtude da quantidade de parâmetros utilizados, e a tendência dessa classe, dependendo do sistema, é aumentar a quantidade desses parâmetros. Cada método poderia conter a validação respectiva ao dado inserido, centralizando toda a lógica apenas em uma classe, para todo sistema. A classe Email faz a coleta e validação de dados de um e-mail, e só isso. Os atributos atendidos pela classe EmailAddress são também atendidos aqui: coesão, responsabilidade única, legibilidade e baixo acoplamento.

Case 4

import java.util.Arrays;

public class Case4 {
    
    /**
     * Refactoring: 
     * - Criacao da classe Sender
     */
    public void abstractController() {
        try {
            EmailAddress de = new EmailAddress("eu@email.com");
            EmailAddress para = new EmailAddress("qualquer@email.com");

            Email email = Email.create()
                    .from(de)
                    .to(Arrays.asList(para))
                    .subject("Refactoring na Prática")
                    .body("Corpo da mensagem")
                .build();

            Sender.send(email);
        } catch (Exception e) {
            // log
        }

    }
}

No caso 4, uma terceira lógica foi movida do controller para uma classe específica, a classe Sender. Essa classe passa a ser responsável pelo envio do e-mail, e somente isso. Ela encapsula boa parte da API de envio de e-mail do java (javax.mail) e possui apenas um método estático, send(email). O parâmetro de send é um objeto do tipo Email, a classe criada no caso 3. O contrutor de Sender lança uma UnsupportedOperationException, pois não existe motivos para instanciar essa classe, ela foi projetada para apenas enviar um e-mail, não armazenando dado algum. O seu método private getArrayAddress efetua uma conversão da lista de EmailAddress para um array de InternetAddress, exigência esta imposta pela API javax.mail.

Sender

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.naming.NamingException;

/**
 * <p>
 * Classe responsavel por enviar um email.
 * </p>
 * <p>
 * Classe nao-instanciavel, seu unico proposito eh enviar um email,
 * funcionalidade essa fornecidade pelo metodo {@link #send(Email)}.
 * </p>
 * <p>
 * <b>OBS:</b> Classe nao projetada para heranca.
 * </p>
 * 
 * @author Vinicius Martins Knob
 * 
 */
public final class Sender {
    
    private Sender() {
        throw new UnsupportedOperationException("classe nao-instanciavel");
    }
    
    public static void send(Email email) throws NamingException {
        Properties p = new Properties();
        p.put("mail.host", "127.0.0.1");
        Session session = Session.getInstance(p);
        MimeMessage msg = new MimeMessage(session);
        try {
            msg.setFrom(email.getFrom().toInternetAddress());
            msg.setRecipients(Message.RecipientType.TO, getArrayAddress(email.getTo()));
            msg.setSentDate(new Date());
            msg.setSubject(email.getSubject());
            msg.setText(email.getBody());
            Transport.send(msg);
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }
    
    private static InternetAddress[] getArrayAddress(List<EmailAddress> emails){
        List<InternetAddress> internetAddresses = new ArrayList<InternetAddress>();
        for (EmailAddress address : emails) {
            internetAddresses.add(address.toInternetAddress());         
        }
        return internetAddresses.toArray(new InternetAddress[internetAddresses.size()]);
    }
}

Conclusão

Acredito que com essa refatoração o controller ficou muitas vezes mais legível, fácil de efetuar uma alteração já que temos as partes do processo bem definidas. As classes geradas por esta refatoração são totalmente reutilizáveis, flexíveis e coesas. Nem sempre é possível efetuar uma refatoração de código, pois esta ação exige conhecimento amplo. Um ambiente preparado para testes seria ideal para manter uma rotina de refatoração diminuindo significamente partes repetidas de um sistema que utiliza uma linguagem orientada a objetos. Conceitos aplicados aqui, e muitos deles posso ter passado por cima, deveria sempre ser aplicados, desde o inicio.

0 comentários: