O problema
Passei um bom tempo ontem tentando trabalhar com localização numa aplicação Rails 2.1 na qual estou trabalhando. Na verdade o que eu queria fazer era manter todas as mensagens da minha aplicação (que não é muito grande) num arquivo e chamar as mensagens dentro dele desde a view. Preferi utilizar uma solução de L10n porque ia resolver meu problema e ia me outorgar soluções a uns possíveis problemas que poderia ter no futuro, como utilizar a ferramenta em diversas linguagens.
Pesquisei sobre L10n em Rails mas somente achei soluções para Rails 1.2. Na pagina de plugins do Rails existem diretórios para diferentes plugins, mas os links não estão funcionando agora. Felizmente cheguei nuns posts que falavam sobre L18n em Rails com JRuby e assim apos bater cabeça um pouco, consegui solucionar meu problema.
A solução
Antes de implementar e demostrar a solução começarei criando uma aplicação Rails 2.1 de teste:
jruby -S rails teste
Como o arquivo de mensagens ira ser carregado pelo método java.util.ResourceBundle.getBundle, o arquivo devera estar no classpath do Java. O arquivo de mensagens estará localizado na pasta app/views do nosso projeto Rails e por tal motivo criaremos um script que ira incluir a pasta no classpath e seguidamente inicializar o servidor. O script esta dentro da pasta raiz do nosso projeto; chamarei ele de start_server:
export CLASSPATH="app/views:$CLASSPATH"
jruby script/server
Para executar o script executamos:
./start_server
e possivelmente obteremos um output similar a esse:
=> Booting WEBrick...
JRuby limited openssl loaded. gem install jruby-openssl for full support.
http://wiki.jruby.org/wiki/JRuby_Builtin_OpenSSL
=> Rails 2.1.0 application started on http://0.0.0.0:3000
=> Ctrl-C to shutdown server; call with --help for options
[2008-07-03 13:36:37] INFO WEBrick 1.3.1
[2008-07-03 13:36:37] INFO ruby 1.8.6 (2008-07-02) [java]
[2008-07-03 13:36:37] INFO WEBrick::HTTPServer#start: pid=21025 port=3000
Criaremos um arquivo chamado messages_pt_BR.properties na pasta app/views do nosso projeto. O arquivo, como já sabemos, ira conter as mensagens da nossa aplicação para o idioma português e o maravilhoso pais Brasil, no seguinte formato:
chave=valor
app.title=MouseOver Studio
Criaremos agora a implementação. Para isso iremos gerar um controller chamado Localizacao:
require "java"
class ApplicationController < ActionController::Base
...
before_filter :localizate
def localizate
locale = java.util.Locale.new("pt", "BR")
@messages = java.util.ResourceBundle.getBundle("messages", locale)
end
...
end
Basicamente o que fizemos foi incluir as bibliotecas do Java com require ‘java’, criar um filtro chamado localizate e nele definir a variável messages que contem as mensagens indicadas. No exemplo eu coloquei pt e BR estaticamente, mas isso poderia vir da base de dados ou de algum arquivo de configuração.
Vou modificar agora a view, vou apagar o conteúdo do arquivo app/views/localizacao/index.html.erb e colocar um texto besta, somente para testar:
< %= @messages.get_string "app.title" %>
Tudo pronto, vamos agora testar a aplicaçao. Nao se esquecam que o servidor tem que ser inicializado com o script que criamos porque o Java precisa carregar o arquivo de mensagens.
Testamos nossa aplicaçao acessando http://localhost:3000/localizacao e o resultado é:
O bonus
MissingSourceFile in LocalizacaoController#index
no such file to load -- sqlite3
Opa! O que aconteceu? Rails trabalha por padrão com bancos de dados e a não temos nenhuma base configurada, e não queremos configurar nenhuma, queremos trabalhar com Rails sem depender de um banco de dados. Possivelmente já leram em outros blogs que a solução a esse problema é adicionar a seguinte linha no arquivo config/environment.rb:
config.frameworks -= [ :active_record ]
mas em Rails 2.1 se reiniciarmos o servidor apos a alteração iremos obter o seguinte erro:
/Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:278:in `load_missing_constant': uninitialized constant ActiveRecord (NameError)
from /Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:467:in `const_missing'
from /Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:479:in `const_missing'
from /Users/jesper/Projects/rails/test_new_rails_features/config/initializers/new_rails_defaults.rb:5
from /Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:502:in `load'
from /Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:502:in `load'
from /Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:354:in `new_constants_in'
from /Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:502:in `load'
from /Library/Ruby/Gems/1.8/gems/rails-2.1.0/lib/initializer.rb:475:in `load_application_initializers'
... 32 levels...
from /Library/Ruby/Gems/1.8/gems/rails-2.1.0/lib/commands/server.rb:39
from /Library/Ruby/Site/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
from /Library/Ruby/Site/1.8/rubygems/custom_require.rb:27:in `require'
from script/server:3
Ja foi criado um ticket para esse erro e o mesmo foi corregido por DHH. A solução consiste em alterar o arquivo config/initializers/new_rails_defaults.rb com esse código aqui, o qual vai vir como parte do Rails 3.
Reiniciamos nossa aplicaçao, facemoz o teste de novo e agora sim o resultado é:
MouseOver Studio
É nois! Qualquer duvida não se esqueçam de perguntar que eu responder rapidamente e muito contente. Agora ire a continuar meus estudos com Rails, falo!
Tags: jruby · rails
Como algumas pessoas ja sabem, nessas dois ultimas semanas entrei fortemente no Ruby e no Rails. Comecei lendo os excelentes livros Programming Ruby e Agile Web Development With Rails. Como o livro do DHH fala sobre Rails 1.2, eu atualizei o aprendido com esses dois tutoriais do Akita. Finalmente lapidei um pouco meu conhecimento com o livro de Carlos Brando.
O problema
Mas agora, sem muito papo, o motivo do post. Estava brincando com scaffolding e por algum motivo a inserção do meu modelo não estava funcionando. Quando intentava inserir alguma coisa a view era redirecionada para a lista de itens do modelo. Supostamente deveria ser chamada a action create do meu controller, mas era chamada a action index. Comprovei o mencionado analisando o log:
Processing PostsController#index (for 127.0.0.1 at 2008-07-01 21:02:10) [POST]
Outra coisa, o scaffolding no Rails 2.1 é RESTful e por tanto chamar a action index com o método POST acho que não tem sentido algum porque index da a entender uma consulta e POST um envio de informação.
A solução
Não sei se seja a melhor solução mas eu corregi o problema reiniciando o servidor. Talvez o Mongrel não consiga pegar todas as rotas dinamicamente, pode ser. Registro essa solução aqui porque não á achei em nenhum lugar, teve que experimentar muitas coisas.
Experiencias adquiridas
Quando tenham um problema considerem entrar no canal rubyonrails no IRC. Existem no canal muitas pessoas dispostas a ajudar. Eu acessei o canal para tentar achar uma solução à meu problema, não consegui achar ela no canal mas a experiencia foi muito boa, o povo deu um monte de dicas e sugestões.
Uma rapidinha agora. Quando tenham duvidas sobre as rotas da sua aplicação Rails, executem o seguinte comando:
rake routes
e ele mostrara o resultado das configurações das rotas, estilo:
formatted_posts GET /posts.:format {:action=>”index”, :controller=>”posts”}
E é isso ae, Rails é incrível!
Tags: rails · ruby
Tudo começou com uma discussão sobre o classloader do Java. A discussão surgiu porque existiam duas aplicações que queriam compartilhar dados de uma classe estática localizada numa biblioteca x. Nesse momento surgiu a seguinte pergunta:
Como isolar aquelas duas aplicações de modo que possam utilizar uma biblioteca compartilhada que não possa ser utilizada por uma terceira aplicação?
Para responder a pergunta era necessário entender o conceito de classloader e também como trabalham os classloaders do servidor envolvido, que nesse caso era o JBoss.
Caso alguem não entenda muito bem o que são os classloaders e como eles trabalham, pode ler esse excelente post e lapidar o aprendizado com esse feedback muito bom do Rodrigo Kumpera.
Vou dizer que menti faz um pouco. Na verdade não é necessário entender o conceito de classloader para poder isolar aplicações devido a que JBoss torna o processo muito simples e transparente, mas eu acho conveniente para ter uma idéia de como funciona o negócio, o que é realizado por trás.
Mas então, o que deve ser feito? Existem varias alternativas mas a mais simples é criar um arquivo .ear contendo os projetos desejados e alterar o arquivo ear-deployer.xml localizado na pasta deploy do servidor JBoss. No arquivo mencionado devera ser trocado o valor do atributo Isolated para true:
<attribute name=\"Isolated\">true</attribute>
O JBoss utilizara um classloader para cada deploy ear, pelo que as aplicações dentro dele ficaram num contexto isolado.
Agora, o que acontece se eu tenho somente uma aplicação e quero que ela fique totalmente isolada, preciso por ela num ear? Não, nesse caso não, seria meio que uma gambiarra. No caso que queiramos um classloader para uma única aplicação e não um grupo delas o que devemos fazer é colocar as bibliotecas que desejamos sejam carregadas pelo classloader na pasta WEB-INF/lib da aplicação. Não tenho certeza se isto é uma especificação mas o JBoss Web e o Tomcat entendem que o que esta dentro da pasta WEB-INF é somente visível para a própria aplicação pelo que não teria sentido se as bibliotecas dentro da pasta lib fossem carregadas por um classloader de alto nível.
Finalmente, eu quero que minhas bibliotecas sejam visíveis para tudo mundo, onde devo colocar elas? Nesse caso a resposta é mais fácil, nesse caso basta colocar a biblioteca na pasta lib do servidor ou dentro de uma pasta (lib de preferencia) dentro do projeto ear, somente não esquecer de declarar ela no arquivo application.xml:
<library-directory>lib</library-directory>
Isso e tudo, não se esqueçam que agora a pagina conta com um formulário de contato e toda duvida sera respondida com plácer =)
Tags: Tutoriales · Uncategorized · java · jboss · tomcat
Quando trabalhamos com aplicações web é muito comum salvar dados nas sessões dos usuários. No caso que estejamos trabalhando também com cluster, a melhor opção na maioria das vezes é replicar as sessões em todas as instancias do nosso servidor de modo que os usuários possam se conetar em qualquer nodo do cluster e a sessão este disponível para eles em tudo momento.
Talvez tenham se perguntado: qual é a outro opção? Bom, a outra opção e utilizar “sticky sessions”, de modo que o balanceador sempre se conete com o mesmo membro do cluster, o qual possui a sessão do usuário. O ruim das sticky sessions é que ao cair algum nodo do cluster, os dados que se encontravam neles serão perdidos dado que não foram replicados.
Nesse artigo vamos configurar nossa aplicação web para que o JBoss 4.2.2.GA replique a sessão HTTP ao longo de todos os nodos do cluster cade vez que ela sofrer alguma alteração. A configuração é realizada no JBoss Web que utiliza o Tomcat, pelo que a configuração num Tomcat independente não deveria fugir muito do que ira ser mostrado aqui.
Mãos na obra
A configuração é bem simples, são necessários somente dois passos.
O primeiro passo é indicar que nossa aplicação é distribuída. Possivelmente esta configuração já foi feita devido a que estamos supondo que a aplicação já foi configurada para rodar num cluster. De qualquer modo, a configuração é realizada no arquivo web.xml da aplicação; devemos inserir o seguinte trecho dentro do nodo web-app:
<distributable />
O segundo e último passo é configurar um arquivo chamado jboss-web.xml, que sera localizado na pasta WEB-INF. Esse arquivo indicara ao JBoss como e quando replicar a sessão da aplicação:
<jboss-web>
<replication-config>
<replication-trigger>SET</replication-trigger>
<replication-granularity>SESSION</replication-granularity>
</replication-config>
</jboss-web>
O nodo replication-trigger indica quando o JBoss deve replicar os dados. Nesse caso estamos indicando que os dados devem ser replicados cada vez que seja chamado o método setAttribute da sessão (HttpSession).
O nodo replication-granularity indica o que devera ser replicado. Se a sessão não for muito grande, SESSION é uma boa opção porque sera replicada a sessão inteira. Quando a sessão é grande demais, a opção ATTRIBUTE resulta num ganho de performance devido a que somente serão replicados os atributos da sessão que sofreram alteração.
Testando a configuração
Fiz uma aplicação web bem simples para poder testar e comprovar que o JBoss esta replicando os dados da sessão dela. A aplicação guarda na sessão o número de acesos e printa o valor na tela. Cada vez que acessarmos a aplicação, deveria aparecer quantas vezes realizamos a operação, independente de qual nodo do cluster o balanceador escolher para servir.
A aplicação pode ser descarregada de aqui .
Para finalizar, agora o blog possui uma pagina de contato. Qualquer duvida ou pergunta que não quiserem colocar nos comentários pode ser enviada mediante o formulário e eu responderei assim chegar na minha caixa de email.
Tags: Uncategorized
Vou tentar rodar uma aplicação Ruby no JBoss. Se eu conseguir meu objetivo possivelmente poderei utilizar os excelentes recursos que o JBoss oferece.
Abri o NetBeans 6.1, selecionei para criar uma aplicação Ruby on Rails e ao dar Next fiquei surpreso ao ver a opção Add Rake Targets to Support App Server Deployment (.war). Com um arquivo .war o trabalho já esta quase feito, parece que vai ser mais fácil do que pensei! Somente uma coisa, o que é Rake? Pesquisei ao respeito e Rake é o Make para Ruby, algo similar ao Ant do mundo Java. Vou selecionar a opção, escolher como plataforma JRuby e dar um Finish.
O projeto foi criado e desenvolvi uma pequena aplicação de teste, mas agora o que importa. Se acionarmos o click contextual no nosso projeto e seguidamente escolhermos a opção Run Rake Task > war > standalone > create um arquivo nome_do_projeto.war sera gerado na pasta raiz do mesmo.
Agora o único passo restante é publicar aquele arquivo .war no nosso servidor preferido e pronto, temos nossa aplicaçao Rails rodando sobre um servidor Java!
Tags: java · jruby · rails · ruby
Estou querendo puxar o código fonte do JRuby que se encontra no GitHub, pelo que preciso do Git.
Após pesquisar um pouco, parece que o melhor jeito de instalar o Git no OS X é mediante o MacPorts. Para instalar o MacPorts basta baixar o arquivo .dmg de aqui e executa-lo.
Uma vez instalado o MacPorts consegui instalar o Git executando:
sudo port install git-core
A partir de aqui toda a informação pode ser encontrada no tutorial do Akita.
Na pagina do projeto encontramos o endereço do repositório, chamado de Clone URL. Vou ir no meu workspace e executar o seguinte comando, que ira criar uma pasta chamada jruby e colocar os arquivos dentro dela:
git clone git://github.com/nicksieger/jruby.git
Finalmente vou seguir o conselho do Akita e criar um branch local com o comando:
git checkout -b working
Agora estou pronto para começar a estudar o JRuby, proximamente noticias!
Tags: Tutoriales · java · osx · programação · ruby
Se você trabalha com Firefox 3 no Ubuntu e esta tendo algum problema com o Firebug então você esta na mesma situação que eu antes de ler esse post .
Sem perder muito tempo, desinstale o add-on Firebug no Firefox e instale o pacote firebug:
sudo apt-get install firebug
Pronto! De volta ao desenvolvimento!
Tags: Firebug · Firefox · ubuntu
Um pouco de historia
Faz umas semanas decidi junto a dois amigos meus que moram e trabalham comigo começar um projeto web. Depois de conversar um pouco decidimos que o framework MVC seria o Struts 2. Diariamente quando chegávamos em casa abríamos algo para beber e começávamos a desenvolver um pouco do projeto.
Depois de três dias, uma quarta feira, sugeri trocar o Struts 2 por outro framework. Domingo da semana anterior tinha ido para o Falando em Java 2008 e após a palestra do Paulo Silveira fiquei com muita vontade de experimentar o VRaptor 2.
Meus dois amigos concordaram na hora. O motivo? O motivo era que chegávamos em casa um pouco cansados e não tínhamos muito tempo na noite, sempre havia um programa ou filme legal para assistir, pelo que ficar configurando os arquivos .xml sempre que fossemos fazer alguma coisa com o Struts era muito chato e pouco produtivo.
Ja passaram alguns dias desde que começamos a trabalhar com o VRaptor 2 e a verdade e que estamos surpresos com a produtividade que temos ganhado e a facilidade que o VRaptor 2 nos tem dado para desenvolver algumas coisas. E por tal motivo que decidi criar esse artigo explicando como configurar o VRaptor 2 com o Freemarker e Sitemesh e assim tudo mundo possa começar a desenvolver seu projeto web de forma ágil e sem muitas complicações.
Para os apressadinhos
Criei um projeto blank no Google Code. Para os que não tem muito tempo é somente baixar ele e começar a desenvolver.
Mãos na obra
Antes de começar devemos criar a estrutura de pastas e arquivos de um projeto web. Eu utilizo o Eclipse e consigo isso rapidamente criando um projeto web dinâmico.
Os jars que precisaremos são freemarker-2.x.jar, sitemesh-2.x.jar, vraptor-2.x.jar, log4j-1.x.jar, xstream-1.x.jar e picocontainer-1.x.jar. Os tres últimos jar são utilizados pelo VRaptor 2 e vem junto com ele. O PicoContainer é um container IoC e o XStream é uma biblioteca para trabalhar com arquivos XML.
No arquivo web.xml situado na pasta WEB-INF, dentro do nodo web-app, devemos incluir o seguinte código:
<filter>
<filter-name>sitemesh</filter-name>
<filter-class>
com.opensymphony.module.sitemesh.filter.PageFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>sitemesh-freemarker</servlet-name>
<servlet-class>
com.opensymphony.module.sitemesh.freemarker.FreemarkerDecoratorServlet
</servlet-class>
<init-param>
<param-name>TemplatePath</param-name>
<param-value>/</param-value>
</init-param>
<init-param>
<param-name>default_encoding</param-name>
<param-value>ISO-8859-1</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>sitemesh-freemarker</servlet-name>
<url-pattern>*.dec</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>vraptor2</servlet-name>
<servlet-class>org.vraptor.VRaptorServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>vraptor2</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
Acabamos de configurar nossa aplicação para mapear os recursos .action com o VRaptor e os recursos .dec com o Freemarker, além de indicar que todos os recursos deveram ser filtrados pelo Sitemesh (talvez de para melhorar isso aqui). O url-pattern padrão do VRaptor é .logic mas prefiro chamar minhas actions como XptoAction que como XptoLogic e por isso o .action .
O VRaptor trabalha por padrão com views .jsp pelo que teremos que mudar isso. Criaremos um arquivo chamado vraptor.xml que estará ubicado na pasta raiz do nosso código fonte (src no meu caso). Indicaremos também nesse arquivo que desejamos que todas as views sejam procuradas na pasta views, assim teremos uma melhor organização:
<vraptor>
<regex-view-manager>
/views/$component/$logic.$result.dec
</regex-view-manager>
</vraptor>
Antes de continuar é importante mencionar que se temos uma classe chamada AdminAction e um método chamado login, $component equivale a Admin, $logic a login e $result à string que o método login retornar. Caso login seja um método do tipo void, $result sera equivalente por padrão à ok.
Por ultimo (sim, por ultimo!) devemos criar um arquivo chamado decorators.xml e coloca-lo na pasta WEB-INF. O conteúdo dele sera o seguinte:
<decorators defaultdir="decorators">
<decorator name="main" page="main.dec">
<pattern>/*<pattern>
</decorator>
</decorators>
Nesse arquivo acabamos de indicar que nossos decoradores do Sitemesh estarão localizados na pasta decorators, que teremos um decorador chamado main que ira ser o arquivo main.dec e estará mapeado a todos os arquivos filtrados pelo Sitemesh.
O teste
Vamos testar se nossa aplicação funciona. Vou fazer o teste no Tomcat 6 mas isso não deveria ter muita diferença.
Começaremos criando uma classe chamada de TestAction com o seguinte conteúdo:
import org.vraptor.annotations.Component;
@Component
public class TestAction {
private String text;
public String sayHelloWorld() {
text = "Hello World!";
return "ko";
}
public String getText() {
return text;
}
}
O metodo poderia ser um void mas preferi retornar alguma string somente para demostrar o poder do VRaptor, KO!
Seguidamente criaremos um arquivo chamado sayHelloWorld.ko.dec que ficara na pasta test do nosso conteúdo web:
${text}
Finalmente criaremos uma pasta chamada decorators e nela o arquivo main.dec :
Sitemesh antes do body
${body}
Sitemesh depois do body
Ao subir nosso container web e acessar o recurso test.sayHelloWorld.action deveriamos ter como resultado a mensagem Sitemesh antes do body Hello World! Sitemesh depois do body.
Concluindo
O legal do VRaptor é que exige configuração minima e é fortemente baseado em anotações. Podem aprender mais sobre ele aqui. O Freemarker oferece uma linguagem dinâmica e muito poderosa, mesmo não tinha sido demostrada nesse artigo. Finalmente, o Sitemesh é muito útil porque ele infiltra as views de modo que quem cria elas não precisa ficar sabendo nem se preocupando com os decoradores. Sitemesh é algo similar à inversão de controle na view.
Espero que o artigo seja de utilidade e como sempre qualquer duvida ou critica é bem vinda. Ate a próxima!
Tags: Tutoriales · freemarker · java · programação · sitemesh · vraptor
Um post rapidinho somente para informar dessa nova promoção da Sun.

Tags: comunidade · java
Constantemente quando estou desenvolvendo software lembro desse post realizado por Phillip Calçado aka Shoes.
Confesso que quando li o título pensei “Vixi o Shoes deve ter bebido demais naqueles barzinhos em Melbourne”
mas quando terminei de ler o post meu pensamento foi “PQP que foda!”.
O post esta em inglês mas a idéia de forma simplificada é que os video games de um tempo atrais são comparáveis com código confuso e os video games modernos com código simples de entender.
Para explicar tal comparação Shoes começa mostrando uma imagem do jogo Pong:

Ele explica que o jogo representa um tennis de mesa mas pode ser confuso identificar isso, que pode ser confundido com gol a gol ou com um jogo de naves. O mesmo acontece com alguns códigos. Quantas vezes ja vimos códigos que são muito difíceis de ser analisados e/ou podem parecer que estão fazendo uma coisa quando na realidade estão fazendo outra? Foi mostrado o seguinte exemplo:
public void sendEmailWithCsvFile(String from, String to, String subject,
String body, List csvFileLines) {
Properties props = new Properties();
props.put("mail.smtp.host", "localhost");
Session session = Session.getDefaultInstance(props);
MimeMessage msg = new MimeMessage(session);
try {
msg.setFrom(new InternetAddress(from));
InternetAddress[] address = { new InternetAddress(to) };
msg.setRecipients(Message.RecipientType.TO, address);
msg.setSubject(subject);
msg.setSentDate(new Date());
MimeBodyPart part1 = new MimeBodyPart();
part1.setText(body);
MimeBodyPart part2 = new MimeBodyPart();
StringBuffer buffer = new StringBuffer();
for (String line : csvFileLines)
buffer.append(line + "\n");
part2.setContent(buffer.toString(), "text/csv");
part2.setFileName("file1.csv");
Multipart mp = new MimeMultipart();
mp.addBodyPart(part1);
mp.addBodyPart(part2);
msg.setContent(mp);
Transport.send(msg);
} catch (AddressException e) {
throw new RuntimeException(e);
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
A simples vista fica difícil de entender o que o código esta fazendo, o que ele representa. Esse código poderia ser bem mas claro e explicito, como o seguinte:
public void sendEmailWithCsvFile(String from, String to, String subject,
String body, List csvFileLines) {
StringBuffer buffer = new StringBuffer();
for (String line : csvFileLines)
buffer.append(line + "\n");
Properties props = new Properties();
props.put("mail.smtp.host", "localhost");
MailService mailService = new MailService(props);
mailService.newMessage()
.from(from)
.to(to)
.subject(subject)
.body(body)
.attachTextFile("file3.csv", buffer.toString())
.send();
}
É esse tipo de código, simples de entender, que se compara com os video games modernos, como Rockstar Table Tennis:

Ao ver aquele jogo não cabe duvida que se trata de um jogo de tennis de mesa.
Quando estou escrevendo código lembro do entendido nesse post e tento fazer meu código comparável com os jogos modernos, tentando que eles representem ao máximo a intenção deles. Quem sabe algum dia a experiência de ler meu código seja tão próxima ao domínio como a dos jogos de Wii com alguns esportes.
Tags: ddd · programação · qualidade