Desenvolvimento de portlets com Rails - parte 2

Posted by Túlio Ornelas Wed, 10 Feb 2010 11:03:00 GMT

No último post apresentamos uma forma de desenvolver portlets em Rails, falamos sobre a variável de ambiente GEM_HOME, que define um path único para as gems de todas as versões do Ruby (incluindo o JRuby), dessa forma não precisamos replicar a instalação das gems nas outras versões e tudo fica muito bem, porém na prática a teoria é outra. Tivemos alguns problemas relacionados a gem PG. Em algumas máquinas o ruby ficava confuso sobre usar a versão jdbc ou a nativa da gem, mesmo configurado para usar uma ou outra. O erro era o seguinte:

NameError: uninitialized constant ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::PGconn

Para resolver esse problema tivemos que comentar a variável de ambiente GEM_HOME e instalar a
versão java com o jgem, mantendo então as gems nativas do JRuby em seu próprio diretório de gems.

Prometemos um post sobre o liferay_models, mas ainda não será dessa vez. Nós criamos o projeto no github com algumas classes básicas, mas boa parte dele ainda está presa ao nosso projeto, isso deverá ser resolvido nas próximas semanas. Mas para não passar em branco, podemos adiantar algumas coisas sobre o projeto, como por exemplo as dependências da classe user_ do liferay, representada na imagem abaixo:

 



No liferay, por mais simples que um usuário seja, ele necessita de todas as dependências apresentadas, mais algumas que ainda não foram adicionadas. É nesse cenário que o liferay_models entra em ação, abstraindo as tabelas e os comportamentos das entidades, o que torna mais fácil a integração de um portlet escrito em Rails com o portlet container.

O fato de não utilizar o ambiente de extensão e desenvolvimento do portal implica em alguns problemas, como por exemplo, a integração com o apache lucene. Este framework é responsável por realizar a indexação de todo o conteúdo do portal, agilizando as consultas e melhorando a performance. Como o liferay_models trabalha diretamente com o banco de dados, sem passar pelas apis do portal, o conteúdo persistido por ele não é indexado pelo lucene, acarretando em alguns problemas como a ausência deste conteúdo nas pesquisas feitas nos serviços do portal. Estamos trabalhando nesse problema utilizando o jruby-lucene, que é uma api para manipular o lucene em ruby. Uma outra solução seria utilizar o solr, mas isso também fica para um próximo post. 


Falamos também do projeto rails-portlet que é uma ponte entre o projeto rails e o portlet container. Durante o desenvolvimento nós tivemos a necessidade de realizar upload de arquivos, e descobrimos de uma forma pouco agradável que o projeto não o suportava. Dessa forma começamos a saga da adição deste suporte a forms multipart ao rails-portlet, e como já era esperado, tivemos problemas.

1 - Apache fileupload vs Liferay

Round 1:
Tentamos utilizar o projeto fileupload da apache para receber os arquivos no lado servidor, mas tivemos a infelicidade de descobrir que o liferay eliminava (o.O) esses parâmetros antes da execução do rails-portlet, o que inviabilizava o uso do projeto da apache.

Round 2:
Utilizamos uma api do próprio portal para recuperar os arquivos, mas eles eram apagados antes do rails-portlet chamar o projeto em rails.

Round 3:
Já que os arquivos eram apagados, decidimos armazená-los na sessão durante a montagem da requisição para a aplicação rails, resolvendo o problema.


2 - Rack vs request multipart

Round 1:
Uma vez resolvido os problemas com o liferay, foi a vez do Rack atrapalhar, tratando os parâmetros do tipo string também como arquivo.
Basicamente o que acontecia era o seguinte:

O servidor recebia isso:

Parameters: {

"pessoa_fisica"=>{

"cpf"=>#<File:/tmp/RackMultipart4952-35>,

"foto"=>#<File:/tmp/RackMultipart4952-37>

}

}

Ao invés disso:

Parameters: {

"pessoa_fisica"=>{

"cpf"=>"123.456.789-00",

"foto"=>#<File:/tmp/RackMultipart4952-37>

}

}

O campo cpf, que era um textfield, virava um arquivo ao invés de simplesmente uma string.

Round 2:
Para resolver esse problema, tivemos que alterar novamente o rails-portlet, definindo o content-type dos campos do tipo string para null, pois o Rack na versão atual (1.1.0) trata qualquer parâmetro com content-type como binário, gerando o resultado apresentado acima.

Apesar dos problemas, conseguimos adicionar o suporte. Já temos no backlog uma lista de melhorias e novas funcionalidades, mas isso continua no próximo episódio.

Equipe CFA.

 

Desenvolvimento de portlets com Rails 2

Posted by Túlio Ornelas Tue, 19 Jan 2010 13:08:00 GMT

No final do ano passado fechamos um contrato com o Conselho Federal de Administração (CFA) para o desenvolvimento do cadastro integrado de administradores do Brasil dentre outras funcionalidades. Além da implementação, o CFA também tinha a necessidade de uma ferramenta capaz de prover recursos de CMS, comunidades, fóruns, etc. Tendo em vista esse cenário ficou decidido que o Liferay iria ser a ferramenta de portal utilizada enquanto o desenvolvimento dos portlets seria feito em Ruby/Rails.

Como seria possível desenvolver um portlet Ruby em uma plataforma Java? o.O? Utilizando JRuby é claro! Dentre os projetos que viabilizavam uso do Ruby/Rails em portlets java, o rails-portlet foi o que apresentou a melhor proposta. E como ele funciona?

  • 1 portlet java
  • O projeto feito com o Rails empacotado com a gem warble, rodando com o JRuby
  • Comunicação HTTP entre o portlet e o projeto Rails
  • Rails inacessível sem o portlet


O portlet java, que é provido pelo projeto rails-portlet, é adicionado ao portlet container e irá conversar com a aplicação feita em Ruby, isso facilita muito o trabalho, pois conseguimos desenvolver tudo em Ruby e com alguns comandos geramos o .war do portlet.

O projeto feito com o Rails tem algumas particularidades, a mais proeminente é o mapeamento das rotas, pois no projeto original cada método de controller gerava um novo portlet, um mapeamento fixo, por exemplo:

map.portlet_exemplo(

‘portlet_exemplo’, :controller => :controller_1, :action => :method_1

)   

Com as contribuições que nossa equipe fez ao projeto, nós conseguimos criar rotas que abrangem N controllers, o que melhorou bastante o processo de reaproveitamento de código, exemplo:

map.portlet_exemplo(

‘portlet_exemplo/:controller/:action’, :controller => :controller_1

)

Nessa caso, utilizamos um namespace ‘portlet_exemplo’ para definir o portlet e definimos que o controller padrão será o :controller_1 e o método padrão será o :index, que no caso, não precisa ser informado. Os wildcards :controller e :action serão substituídos pelos  valores passados por parâmetro na URL, por exemplo:

http://…/portlet_exemplo
controller_1, método index


http://…/portlet_exemplo/meu_controller

meu_controller, método index


http://…/portlet_exemplo/outro_controller/outro_metodo
outro_controller, método outro_metodo

 

Outra necessidade é a utilização da gem caterpillar que é a responsável por gerar automaticamente os xmls de configuração do portlet e do liferay, além de empacotar utilizando o warbler e realizar o deploy. Essa gem tem como dependência a gem lportal, que contém as tabelas do liferay na forma de modelos do ActiveRecord. Infelizmente o lportal não atendeu as nossas necessidades pois ele apresentou algumas problemas básicos, talvez por inatividade do projeto. Dessa forma nós criamos o projeto liferay_models, que ainda não está público, e que é assunto para um outro post.

Após o panorama geral, vejamos como criar um portlet em Rails.

1) Instalar e configurar o GEM

criar variável de ambiente $GEM_HOME=/caminho/das/gems/sem/a/pasta/gems
* Atenção: O caminho das gems não inclui a pasta gems.
Por exemplo:
export GEM_HOME=/usr/lib/ruby/gems/1.8

1) Instalar e configurar o JRuby

download
http://jruby.kenai.com/downloads/1.4.0/jruby-bin-1.4.0.zip

criar variável $JRUBY_HOME=/… e adicionar no path $JRUBY_HOME:$JRUBY_HOME/bin

1.5)
    apt-get install ruby-dev
    
Ubuntu:
     apt-get install libpqxx3-dev
    Debian:
     apt-get install libpq-dev libpgsql-ruby


2) Instalar gems

gem install jruby-openssl
gem install jruby-jars
gem install hpricot
gem install warbler
gem install caterpillar
gem install lportal
gem install uuidtools
gem install rails -v=2.3.3 (Até o momento o caterpillar só funciona com essa versão do rails, estamos trabalhando nisso)

* no caso de utilizar postgres
gem install pg
gem install activerecord-jdbcpostgresql-adapter

3) Criar projeto

rails nome-projeto
cd nome-projeto

ruby script/generate controller example index

- Configure a rota

map.example(‘example/index’, {:controller => ‘example’, :action => ‘index’})

- Adicionar as dependências das gems no config/environment.rb
config.gem "caterpillar"
config.gem "lportal"

- Configurar config/database.yml
development:
adapter: postgresql
database: lportal
username: postgres
password: postgres
host: 127.0.0.1
timeout: 5000

production:
adapter: jdbcpostgresql
database:
lportal
username: postgres
password: postgres
host: 127.0.0.1
encoding: unicode

- Ativar caterpillar

ruby script/generate caterpillar

- No arquivo config/portlets.rb adicione
portlet.container.root = ‘/../Programas/liferay-portal-5.2.3/tomcat-6.0.18/’
portlet.category = ‘Rails-apps’ (ou um nome que você preferir)

- Deploy

* Teste a conexao com:
caterpillar portlets

- Gerar o arquivo warble.rb
warble config

- Adicionar essa configuração no warble.rb
config.gems << "activerecord-jdbcpostgresql-adapter"
config.gems << "lportal"

# Precisa fazer apenas uma vez
caterpillar jar:install (Coloca o portlet genérico que trata as requisições no liferay)

# Sempre que mudar alguma configuracao e for testar a aplicação no liferay
caterpillar xml
caterpillar warble (nesse momento não pode ter nenhuma gem nativa no environment.rb, exemplo pg)

# Utilizado para atualizar os arquivos no container
caterpillar deploy:xml
caterpillar deploy:war

Após realizar os passos acima, inicie o Liferay e acesse a aplicação. Selecione o portlet rails que se encontra na categoria que foi configurada. Pronto, nós temos nosso portlet rails rodando!

Esse foi o primeiro de uma série de posts sobre o assunto. Fiquem no aguardo.

Equipe CFA:
Túlio Ornelas, Pedro Dias, Bruce Rodrigues e Renan Mendes.

Dojo SEA 2010 1

Posted by BrunoPedroso Mon, 11 Jan 2010 13:43:00 GMT

Salve!

Vamos reiniciar as atividades do DojoSEA essa semana, com uma reunião aberta (estão todos convidados).

Quarta-feira, dia 13/jan/2010

Às 17:00

Na SEA (CLN 110 - em cima do Marvin)

 

Nossa atividade para essa primeira reunião do ano será:

- Assistir ao Screencast de JQuery do peepcode e discutir;

- Conversar sobre as atividades para esse ano;

 

Sejam todos muito bem vindos !

 

 

Customizando o Liferay 1

Posted by Alê! Mon, 07 Dec 2009 19:15:00 GMT

Customizando o Liferay Olá galera,

segue mais um capítulo do minicurso online de Liferay que estamos fazendo aqui no blog. Os primeiros capítulos estão disponíveis em http://tinyurl.com/LiferayNaSEA

Nesta edição, discutimos conceitos básicos para customização do portal. Não entramos ainda no nível de código, mas chegaremos lá. Baby stetps, baby. Baby steps…


Ainda não consegui gravar com 100% de acerto. Ficaram alguns errinhos que mereciam ser polidos, mas já ficou bem melhor que os anteriores, e isso é o que importa. Afinal, o ótimo é inimigo do bom.

Mini-curso online de Liferay 4

Posted by Alê! Wed, 18 Nov 2009 20:37:00 GMT

Mini-Curso Online de Liferay Ultimamente, tenho viabilizado minha ida a maioria dos eventos tecnológicos através da oferta de mini-cursos de Ruby/Rails e de testes nessas tecnologias. No #LinguAgil, entretanto, não tive a cara de pau de concorrer com o @danielvlopes, figura bastante conhecida do mundo Rails, e acabei optando por um mini-curso que teria certa utilidade pra SEA.

O conteúdo em si não foi muito profundo mas, de tão positivo que foi o feedback da turma, acabei resolvendo gastar um pouco mais de esforço para deixá-lo para prosperidade. O resultado foi uma sequência de slidecasts que hoje começo a disponibilizar.

Nesta primeira leva, teremos 2 apresentações. Ambas são bem introdutórias, mas essenciais para tratar com quem nunca ouviu falar no assunto. Nos próximos episódios, falarei um pouco mais de desenvolvimento de portlets, não apenas em Java, como também em outras tecnologias, como Ruby e PHP.

Foi minha primeira experiência na gravação de *casts. O resultado não ficou 100%, mas já quebra o galho. O importante é a melhoria contínua.

Feedbacks, como sempre, são mais que bem-vindos.



[]s

Maré de Agilidade e Oxente Rails

Posted by Alê! Mon, 20 Jul 2009 22:11:00 GMT

MareDeAgilidade e Oxente Rails

Você tem duas grandes razões pra visitar o Nordeste em agosto de 2009!

Dois eventos de singular relevância estão para acontecer entre os dias 6 e 10/08. 


Maré de Agildiade


Um deles, Maré de Agilidade, é conhecido antigo dos leitores de nosso blog. Começou como experimento local brasiliense, ganhou estrada e já segue para sua 3a. edição. Ocorrerá desta vez na terra do sol seguindo a mesma receita da edição soteropolitana.

Serão 3 dias de mini-cursos a preço de banana e um dia de palestras com figurinhas conhecidas da comunidade ágil. Check it out:

Mini-Cursos 06, 07 e 10/08
Gerenciamento Ágil de Projetos com Scrum Manoel Pimentel
eXtreme Programming (XP) na Prática Renato Willi e Bruno Pedroso
Desenvolvimento web ágil com RubyOnRails Alexandre Gomes
Gestão Ágil de Requisitos Manoel Pimentel
Teste de aplicações Rails Alexandre Gomes
Planejamento e estimativas em projetos ágeis Fabiano Milani


Palestras 08/08
Manifesto 2.0 Alexandre Gomes
Gestão Lean para desenvolvimento de Software Manoel Pimentel
A Agilidade está no ar Renato Willi e Bruno Pedroso
“Sou ágil, logo não planejo!” Fabiano Milani
Governança no desenvolvimento ágil Clavius Tales
Conhecendo o desenvolvimento guiado a testes e a comportamento(TDD e BDD) Christiano Milfont
Onde mora a produtividade do Ruby on Rails? Fabio Kung
Painel com todos os palestrantes: Agile na Real - Interoperabilidade, Mix e Adaptações @ALL

E é sempre bom lembrar que o Maré é um evento intinerante, da comunidade para a comunidade. Escreva para maredeagilidade no GMail e veja como levá-lo à sua cidade.

Oxente Rails

Bem próximo ao Maré, e na mesma época, acontecerá o Oxente Rails, realizado pela comunidade local e com a presença confirmada das maiores autoridades nacionais e internacionais sobre o assunto.

O Oxente Rails vai acontecer em Natal, RN, nos dias 07 e 08 de agosto. Na programação palestras sobre Ruby on Rails, Desenvolvimento Ágil e diversos outros temas – interessantes pra quem ainda não conhece e pra quem já trabalha na área. Voici:

Palestras 07 e 08/08
Ruby on Rails: Ecossistema e Comunidade Fábio Akita
Desenvolvimento Ágil Tapajós e Sylvestre Mergulhão
Design de Interface para Programadores Juarez Filho
BDD com Rails Cauê Guerra
Deploy de Aplicações Rails Dante Regis
A ciência por trás do Ruby Carlos Brando
Pragmatic Thinking and Learning Andy Hunt
Easy Rails: Ruby on Rails fácil no Windows e Linux Régis Pires
Case de sucesso – Adotando Ruby on Rails no Tribunal de Justiça de Sergipe Dante Régis
Scaling Rails: Redeparede.com servindo 7,5 milhões por mês Tapajós e Sylvestre Mergulhão
Empreendedorismo e Rails em Natal Paulo Fagiani e David William
The Hashrocket Way Obie Fernandez

Se você é ou quer ser um Railer, não perca esta chance de unir o útil (Rails) ao agradável (Natal).

Maré de Agilidade vs Oxente Rails

É provável que alguns estejam a esta altura do post imaginando de quem foi a brilhante idéia de fazer, na mesma semana, dois eventos tão interessantes e tão relacionados entre si. E, o pior, com empresas patrocinantes e apoiadoras em comum! Bem, realmente, foi uma lástima, mas explico.

A movimentação do Oxente Rails começou um pouco antes dos preparativos pro Maré de Fortaleza e, na época, oferecemos ajuda para criação da marca e confecção do site, que foi prontamente aceita pelo Elomar e companhia. Algum tempo depois, a turma do XPCE se empolgou na realização do Maré de Agilidade e, em parceria  com o JavaBahia e a Visão Ágil, iniciamos nosso apoio, contribuindo com experiências das edições anteriores.

Assim, a organização de ambos eventos correram de forma independente e surpreendentemente rápida ao ponto de que, quando alguém se tocou da possibilidade de conflito entre as iniciativas, já era tarde demais. Os locais de realização já haviam sido (ao custo de muito suor) reservados e a maior parte dos palestrantes já havia sido contactada. No caso específico do Maré, além do Oxente Rails, tínhamos até outras justificativas para mudança da data (como uma indisponibilidade na agenda do Alexandre Magno), mas ainda assim não foi possível :-(

Resta-nos então lamentar profundamente. Os eventos Maré de Agilidade e Oxente Rails são eventos amigos que se complementam totalmente. Seria um baita marco para o Nordeste se tivéssemos nos organizado melhor a fim de realizá-los em sequência. Mas enfim, ç’arrive. O Paulo Fagiani, um dos organizadores do Oxente é colega antigo do mundo Java e isso só piora as coisas. Mas uma razão pra não haver desculpas. Foi uma baita falta de comunicação *mesmo*.  Talvez seja a hora de revermos alguns valores do XP… Nossas sinceras desculpas, comunidade.

[]s

Selenium e XPath 5

Posted by Adam Brandizzi Mon, 06 Jul 2009 13:23:00 GMT

Quem já acompanha o blog deve ter percebido que usamos o Selenium. Primariamente, terminávamos versões preliminares da interface e usávamos o Selenium IDE para gerar scripts de testes.

Todavia, essa abordagem tinha complicações. Mudanças de design quebravam os testes. Para repará-los tínhamos de percorrer todos os caminhos através do Selenium IDE. Isso tomava tanto tempo que os testes de Selenium acabaram abandonados.

Depois de surpresas com algumas telas, retomamos os testes. Entretanto, estamos usando outra abordagem. Para compreendê-la, é preciso conhecer XPath.

XPath

XPath é uma pequena linguagem para selecionar elementos, atributos, textos etc. de um documento XML. Considere, por exemplo, o documento XML abaixo:

<library> <book id="war-peace"> <title>War and Peace</title> <edition>1</edition> <author>Leon Tolstoi</author> </book> <book id="devil-backcountry"> <title>The Devil Pays in the Backcountry</title> <edition>2</edition> <author>Guimarães Rosa</author> </book> <book id="art1"> <title>The Art of Computer Programming</title> <edition>1</edition> <volume>1</volume> <author>Donald Knuth</author> </book> <book id="mythical"> <title>The Mythical Man-month</title> <edition>2</edition> <author>Fred Brooks</author> </book> </library>

A expressão XPath /library/book[2] retorna o elemento

<book id="devil-backcountry"> <title>The Devil Pays in the Backcountry</title> <edition>2</edition> <author>Guimarães Rosa</author> </book>

Já expressão /library/book[@id='mythical'] retorna o elemento:

<book id="mythical"> <title>The Mythical Man-month</title> <edition>2</edition> <author>Fred Brooks</author> </book>

e a expressão /library/book[@id='mythical']/author/text() retorna

fred Brooks

Esses exemplos são só para instigar sua curiosidade. Para aprender mais sobre XPath, recomendamos o tutorial da W3Schools. O add-on XPath Checker do Firefox também é uma mão na roda. Se você utiliza Linux, ferramentas como o xgrep e xmlstarlet podem ajudar nos estudos e permitir scripts sofisticados.

Dando Nome aos Bois

Usando o Selenium IDE, nossos scripts pareciam com isso:

selenium.open("/admin/texts"); selenium.type("article_title", "Selenium e XPath"); selenium.click("Salvar"); selenium.waitForPageToLoad("30000"); selenium.click("Artigos"); selenium.waitForPageToLoad("30000"); selenium.click("link=Retornar"); assertFalse(selenium.isTextPresent("Erro"));

Porém, o cliente pedia para mudar coisas. “Artigos” passariam a ser ”posts”, “Retornar” viraria “Voltar”, e “Salvar” seria agora “Gravar”. O link inicial não seria mais /admin/texts, mas sim /administracao/conteudo. Haveria um texto de ajuda no final, explicando que “Erros podem ser problemas de login” - o que faria com que o texto “Erro” estivesse presente em todas as páginas.

Para evitar eses problemas, demos nomes (ou ids) a todos os links, input, div, td etc. Também passamos a utilizar menos o Selenium IDE, e a digitar caminhos XPath explícitos. Por exemplo, se o link referenciado por link=Artigos fosse gerado por:

<a href="artigos/listar.html">Artigos</a>

nós adicionaríamos um id ao link

<a id="listarArtigos" href="artigos/listar.html">Artigos</a>

e, no lugar de

selenium.click("link=Artigos");

utilizamos

selenium.click("//a[@id='listarArtigos']");

//a[@id='listar_artigos'] é o caminho XPath para o link, que agora é independente do texto apresentado e inequivocamente encontrável. O mesmo vale para os campos de formulários. Por exemplo, o botão submit referenciardo por Salvar seria algo como

<input type="submit" value="Salvar" />

Depois da “reforma”, nós adicionamos um atributo name ao submit:

<input name="salvarArtigo" type="submit" value="Salvar" />

Agora, poderemos referenciá-lo através do nome, que é independente da apresentação:

selenium.click("//input[@name='salvarArtigos']");

Segundo o novo padrão, nosso teste será algo como

selenium.open("/admin/texts"); selenium.type("//input[@name='artigo.titulo']", "Selenium e XPath"); selenium.click("//input[@name='salvarArtigos']"); selenium.waitForPageToLoad("30000"); selenium.click("//a[@id='listarArtigos']"); selenium.waitForPageToLoad("30000"); selenium.click("//a[@id='retornar']"); assertTrue(selenium.isTextPresent("//div[@id='Erro']"));

Ao final, note como o selenium.isTextPresent() também verifica a presença de um elemento, não de um texto.

Esse processo, embora exija um pouco mais de trabalho, permite gerar códigos mais robustos, fáceis de manter e reutilizáveis.