Fork me on GitHub

Viagens, opiniões e afins

by Andre Fonseca


Usando SOLR parte 2 – Testes e Cucumber

Se você já leu a primeira parte dessa série, você até o momento tem as gems necessárias instaladas e parte do seu ambiente resolvido.  Uma coisa acabou ficando esquecida, e quem tentou implementar algo seguindo os passos que falei e fez algum teste deve ter enfrentado bastantes erros, dentre eles o de que não foi possivel encontrar constantes e o método searching.

Isso tudo acontece pois esquecemos de fazer o require das libs dentro das nossas classes. Isso porque, como não fizemos dentro do enviroments(isso só vale para apps Rails) as chamadas das libs, o Rails ainda não sabe como achar as coisas.

Bem vamos lá. Imaginemos que você quer que a classe Minha seja indexa e posteriormente busca pelo SOLR, basta colocar no top o require:

require ‘sunspot’

require ‘sunspot_rails’

Feito isso e mais os passos já descritos, creio que agora já temos algo funcional.

Bem vamos para a parte de testar a coisa. Claro que todos nós somos adeptos do TDD (Test Driven Development – Desenvolvimento Orientado a Testes) e por isso vamos sempre querer escrever nossas especificações antes. Falei especificações pois considero que você seja uma pessoa experta e esteja usando o RSPEC.

Para isso, como disse na parte 1, é preciso que você use alguns matchers que irão facilitar em muito a sua vida. Eles estão dentro da gem sunspot_matchers. Dentro do seu spec_helper (isso já vale para todo projeto ruby que esteja usando o Rspec), coloque as linhas:

 

require ‘sunspot_matchers’

config.include SunspotMatcher

config.before do

Sunspot.session = SunspotMatchers::SunspotSessionSpy.new(Sunspot.session)

end

 

config.after do

Sunspot.session = Sunspot.session.original_session

end

Estamos fazendo aqui (acredito que já tenho dito isso) é colocando uma interceptador das chamadas da DSL a sessão com o SOLR para ver se tudo está ocorrendo do jeito que queremos. Seria algo como o nosso mocka, para quem conhcece.

Segue abaixo um exemplo de um código de teste. Veja como fica bem simples testar se a busca, no caso, está incluindo os itens que preciso para fazer a filtragem:

it “Deve filtrar pela data de finalizacao maior que” do

data = DateTime.now.strftime(“%d/%m/%Y”)

encontrados = Inscricao.busca({:campanha_id => “1″, :start_date => data})

data = Date.strptime(data, ‘%d/%m/%Y’)

Sunspot.session.should be_a_search_for(Inscricao)

Sunspot.session.should have_search_params(:with, Proc.new { with(:finalizada_em).greater_than(data)})

end

 

it “Deve filtrar pela data finalizacao menor que” do

data = DateTime.now.strftime(“%d/%m/%Y”)

encontrados = Inscricao.busca({:campanha_id => “1″, :end_date => data})

data = Date.strptime(data, ‘%d/%m/%Y’)

Sunspot.session.should be_a_search_for(Inscricao)

Sunspot.session.should have_search_params(:with, Proc.new { with(:finalizada_em).less_than(data)})

end

 

it “Deve filtrar pela data de nascimento maior que” do

data = Date.today.years_ago(20)

encontrados = Inscricao.busca({:campanha_id => “1″, :idade_final => “20″})

Sunspot.session.should be_a_search_for(Inscricao)

Sunspot.session.should have_search_params(:with, Proc.new { with(:data_nascimento).greater_than(data)})

end

 

A coisa fica bem próxima as expectations que estamos acostumados a usar.  Com isso temos nossos testes de especificação prontos e podemos então passar para os teste de aceitação.

Antes que alguém comece a dizer que estou “roubando” pois estou fazendos os testes de aceitação depois de já ter algo implementado, quero dizer que sou adepto da abordagem emergente. Vou implementado os testes a medida da necessidade – isso é o que chamam de inside out.

Imagino que você que esteja lendo e desenvolvendo junto comigo deva usar o Cucumber para fazer os seus testes de aceitação. Também imagino que, ao contrário dos testes de especificação ou microtestes, você agora queira que as coisas estejam rodando a vera.

Para ter um SOLR funcionando para você executar os seus testes de Cucumber, vá no arquivo, features/support/env.rb e coloque as seguintes linhas:

….

 

::Sunspot::Rails::Server.new.start

 

Before do

Inscricao.reindex

end

 

After do

Inscricao.remove_all_from_index!

end

 

at_exit do

puts “Saindo do solr”

::Sunspot::Rails::Server.new.stop

end

 

Essas linhas irão iniciar um SOLR; a cada teste pedir para reindexar os dados neles e por fim, quando tudo terminar, ele irá parar o solr que você iniciou.  Uma breve ressalva:  tive um mega problema… quando fiz isso, meus teste de jeito algum passavam. Isso porque, por motivos que ainda não sei, quando criamos na mão nossos arquivos, precisamos pedir que sejam indexados, ou seja,  ele não indexa automaticamente. Pelo oque li a gem faz isso pelos controllers. Assim que tiver certeza sobre isso escrevo algo. A questão é que tive que escrever um maldito passo para indexar e chamá-lo toda vez que algo era criado. Algo como abaixo:

 

Dado /ˆque preciso reindexar minhas coisas$/ do

Minhas.reindex

end

 

… Em um features da vida

Dado que crio uma coisa minha

E que preciso reindexar minhas coisa

Eu vejo essa coisa na busca

 

Bem pessoal por hoje é só. Em outras oportunidades pretendo escrever sobre como você pode colocar tudo isso de forma assíncrona e outros truques bem legais, bem como buscas complexas e montagens de indices.

 

Published by Andre, on março 31st, 2011 at 8:56 pm. Filled under: atualidades,ruby Tags: , , , 3 Comments

Usando o SOLR na sua app Rails – parte 1

Num projeto que estou trabalhando atualmente, dentro da parte de admin, existe uma funcionalidade de busca textual e filtrada que tivemos que implementar. A princípio tentamos fazer ela buscando dentro do próprio banco de dados, montando os filtros da query a partir dos parametros que o usuário vai definindo na tela. Isso até que funcionou bem por um tempo, porém, como, espero, todos devem saber, toda vez que a gente usa um “like”, o banco de dados ignora o fato de existir qualquer indice e faz um busca por todos os dados da tabela.  Imaginem que agora temos um banco de dados com 10 milhões de linhas… Agora pega essas 10Mi de linhas e multiplica por 100 mil usuários e você terá a dimensão do nosso problema atual.

Nosso cliente diz que a funcionalidade  de busca é fundamental para o seu dia a dia. Logo criamos uma história de prioridade alta para resolver esse pepino. Antes de mais nada deixa eu contextualizar quando aos outros detalhes desse projeto. Esse projeto é uma aplicação Rails (versão 2.3.4), rodando em cima de um REE (Ruby Enterprise Edition), num servidor CentOS e nosso banco de dados o MySQL.

A primeira conclusão que chegamos foi que precisamos tirar esse busca do banco de dados. Banco de dados são excelentes desde que mantenha sua busca navegando pelos indices das tabelas. Tente um fulltext search e verá o quanto será penoso, até mesmo em bases pequenas.  Para resolver isso pensamos em alguma solução de indexação e a melhor que vimos no momento foi o SOLR.:

Solr is the popular, blazing fast open source enterprise search platform from the Apache Lucene project. Its major features include powerful full-text search, hit highlighting, faceted search, dynamic clustering, database integration, and rich document (e.g., Word, PDF) handling. Solr is highly scalable, providing distributed search and index replication, and it powers the search and navigation features of many of the world’s largest internet sites.

Solr is written in Java and runs as a standalone full-text search server within a servlet container such as Tomcat. Solr uses the Lucene Java search library at its core for full-text indexing and search, and has REST-like HTTP/XML and JSON APIs that make it easy to use from virtually any programming language. Solr’s powerful external configuration allows it to be tailored to almost any type of application without Java coding, and it has an extensive plugin architecture when more advanced customization is required.

Traduzindo o SOLR é uma app WEB construída em cima do Lucene (ferramenta em  java para indexação e busca) que permite que a gente fale com ela via HTTP (busque e indexe).  Roda em cima de um container servlet. Aqui no nosso caso usamos o jetty ao invés do Tomcat.

O seu funcionamento básico é bem simples. Você manda para ele um XML com os dados da sua entidade a ser indexada.  Ele internamente a partir de um schema.xml vai ler esse XML e montar os indices para que busque depois.  Claro que estou sendo bem simplório quanto ao seu funcionamento por isso recomendo muito uma leitura da documentação do produto.

Escolhido a ferramenta falta implementarmos a integração entre o SOLR e nossa app Rails.  Para isso usamos a gem Sunspot. Ela é excelente e torna esse trabalho simples e elegante. Ela “cria” uma DSL dentro de nossa classes que nos permite definir o XML a ser enviado para o SOLR indexar e sua busca posterior.  Ele serve tanto para app Rails quanto qualquer outra. Com rails a coisa fica “mágica”: a integração é total. Com outros tipos só muda as chamadas dos métodos. Veja o exemplo que está na própria documentação do projeto:

  class Post
    #...
  end

  Sunspot.setup(Post) do
    text :title, :description, :stored => true
    string :author_name
    integer :blog_id
    integer :category_ids
    float :average_rating, :using => :ratings_average
    time :published_at
    string :sort_title do
      title.downcase.sub(/^(an?|the)\W+/, ''/) if title = self.title
    end
  end

No exemplo acima vocês podem ver um classe criada e logo em seguido sua definição de dados que serão indexados. Para buscar e tão simples quanto:

Sunspot.search(Post) do
    keyword "teste"
    with :author_name, "andre fonseca"
    with :average_rating, 10.3
  end

Abaixo segue um exemplo de um ActiveRecord sendo preparado para a busca:

class MinhaClass << ActiveRecord
    searchable do
      text :nome, do |minha_classe|
        minha_classe.outro.nome
      end
    end
end

A documentação (WIKI) do projeto é muito bem feito e tem bastante informação que irá ajudar, por isso, não vou perder tempo explicando os primeiros passos. Embora aqui cabe a primeira ressalva ou pulo do Gato:  Ao configurar a gem para instalar, caso esteja usando uma App Rails, no seu enviroments use as linhas abaixo ao invés da recomendada na documentação:

Sunspot.search(Post) do
    config.gem "sunspot"
    config.gem "sunspot_rails"
end

Além disso, não esquecer que no arquivo Rakefile você irá fazer um require das taks do sunspot para poder ter os recursos disponíveis. Uma recurso bastante util é que ele vem com um solr embutido. Assim com o comando rake sunspot:solr:start e rake sunspot:solr:stop você poder iniciar e parar um solr para seus testes locais.
Outra dica muito válida é que você provavelmente não vai querer ter um SOLR real rodando para rodar seus testes unitários ou rspec. Para isso, vá na pasta spec, e adicione as seguintes linhas ao spec_helper:

config.include SunspotMatchers
 
config.before do
  Sunspot.session = SunspotMatchers::SunspotSessionSpy.new(Sunspot.session)
end
 
config.after(:each, :type => type) do
  ::Sunspot.session = ::Sunspot.session.original_session
end

Com certeza isso dará um erro pois esqueci de avisar que você tem que instalar uma outra gem que te ajudará para caramba com os teste: sunspot_matchers. Ele te dará um monte de matcher no rspec para que teste tanto a busca quanto a indexação. Além disso com a configuração acima, ao rodar seus teste, essa gem vai “interceptar” as chamadas ao solr e montar um spy/mock.
Bom chega por enquanto. Na segunda parte pretendo escrever sobre outras coisas que descobri implementando a funcionalidade. Até mesmo uma forma de chamada assincrona.

Published by Andre, on março 30th, 2011 at 7:15 pm. Filled under: atualidades3 Comments

Usando Sinatra para evitar overdesign

Muita gente que  deseja começar um projeto de sistema web e quer ser rápido e produtivo, logo pensa em usar Rails. Rails é um excelente framework, desenvolvido pelo pessoal da 37signals, que é realmente muito produtivo no que diz respeita a entrega de funcionalidade. Porém até o Rails pode ser um overdesign e um má escolha, dependendo do que deseja realizar.

Ruby on Rails, ou RoR ou somente Rails é uma framework de desenvolvimento de aplicações web, cuja a filosofia gira em torno de evitar ao máximo desperdícios com configurações, repetições, etc. Para ser bem objetivo, em Rails tudo é baseado em convenção e tudo é voltado para se escrever o mínimo de código para se implementar um comportamento. Caso queira saber mais peça ajuda ao grande oráculo (google)… para ajudar um pouco você pode começar pelos Railscast que são gratuítos e ajudam muito a entender.

Mas sendo tão orientado a produtividade como o Rail é, ele também pode se tornar um overdesign – um escolha que tem mais recursos e estruturas do que precisa – e uma má escolha para quem deseja fazer um sitema web muito simples e com poucos recursos.

 

Imagine que você deseja fazer um sistema web que simplesmente irá receber uma requisição do usuário, tratá-la (sem necessariamente criar algo num banco de dados, por exemplo) e renderizar uma resposta. Um bom exemplo disso foram alguns projetos que foram feitos durante o evento FISL 2010, pela galera #horaextra.  No meio do evento, decidimos fazer um site onde teríamos ao invés de um horóscopo, teríamos um desoróscopo, com mensagens nada animadoras. Assim surgiu o desoroscopo.

A princípio todo ele foi feito usando Rails. Mas logo nos primeiros releases, fomos vendo que não precisávamos de muitos recursos, principalmente de um banco de dados. Sendo assim, ter ali um activerecord, activecontroller, routes e etc era algo que não precisávamos. Era um overdesign para a solução que desejamos.  Foi então, que o Marcos Tapajós, tomou a iniciativa de reescrever todo o sistema usando outro framework ruby  baseado em RACK que é mega simples : SINATRA.  Vale ressaltar que essa reescrita foi feita em menos de uma hora(a se levar em conta o fato do Tapajós dominar bem ruby e o próprio Sinatra) .

A questão que o site está até hoje no ar e tem números bem expressivos de visitas e em nenhum momento não sentimos falta de algum recurso que rails tem e o Sinatra não.

Sinatra é um framework web feito em Ruby baseado em Rack. Em sua própria documentação, seus criadores, vão além e o chamam de DSL ressaltando sua simplicidade. Todo o seu poder está no fato de precisar de quase nada para estar funcionando. Chega ao limite de precisar apenas a gem instalada e um script com as definições de rotas e seus tratamento in line. Veja exemplo abaixo retirado do site da documentação.

# myapp.rb
require 'sinatra'

get '/' do
  'Hello world!'
end

Note que com apenas 3 linhas de código o sistema ao acessar a url padrão (localhost:4567) já está respondendo. Isso é realmente impressionante.  Entretanto, tudo tem um preço. Caso precise fazer algo que vá além do muito simples, estará por sua própria conta e risco. Claro que pode lançar mão de uma gem para ajudá-lo (isso é algo muito comum no ecossistema Ruby) mas nem tudo será resolvido.

Carrego comigo uma regra que sempre me ajuda muito.  Se meu sistema precisa de mais do que uma entidade, entidades que se relacionam, vai além do que uma 4 ou 5 rotas… já começo a tender para usar o Rails do que qualquer d’outra coisa. Mas se meus sistema não fala com banco de dados, tem poucas rotas, sempre que posso farei com usando Sinatra.

Tenho mais dois exemplos sobre isso:  Sou um dos organizadores do DevinRio, e na edição de 2010, tínhamos os desafio de fazermos um site. Na hora optamos por fazê-lo toda usando Sinatra. Isso porque sinatra é simples e aproveitamos a facilidade de deploy e gestão do produto heroku.  O sistema aguentou requisição para caramba e foi um sucesso, tanto que vamos usar a mesma infra/tecnologia para a edição deste ano.

O segundo exemplo vem do meu trabalho:  precisávamos criar um sistema que iria receber uploads de mídias (fotos, vídeo, etc), gravá-las no disco e processá-las de forma assíncrona.  Muitos poderiam pensar que esses requisitos nos levariam facilmente a querer fazer isso com rails ou até com outra tecnologia/framework(scala, java, etc).  Entretanto, resolvemos fazê-lo em Sinatra. Foi a melhor decisão.  A aplicação ficou simples, limpa e coesa.  E com ajuda de algumas outras gems, o Sinatra está dando conta do recado com maestria.

Enfim, até os mais famosos podem não ser as melhores escolhas. Por isso, fica a dica: fique atento. Leia bastante, converse com outros colegas, amigos e profissionais. Esteja aberto as novidades. Elas podem acabar salvando o seu dia. Evite resolver tudo sempre da mesma forma. Diversifique. Mesmo que seja apenas para testar.  Pluralidade é a palavra da vez.

Published by Andre, on março 20th, 2011 at 10:31 pm. Filled under: agil,atualidades,ruby Tags: , , , , 2 Comments

Processo por processo não resolve. Aceite e mude.

A gente sabe quando está mais consciente de algo quando é capaz de ver as falhas dele. Hoje está na moda dizer que é ágil ou que sua empresa usa algum “método ágil”.  E por aí segue as diversas sopas de letrinhas e nomes pomposos.  A pouco tempo atrás a tábua de salvação era o Scrum que agora já é “jogado a fogueira” pelos seus antigos defensores e em seu lugar ouço muito de Kanban, lean, etc.  Eu pessoalmente não gosto de modas, prefiro seguir aquilo que me facilite a vida e torne-me mais produtivo, feliz, etc.

Antes de mais nada, deixem eu dizer qual é o significa, para mim, de ser ágil: Pelo dicionário, ser ágil é alguém que se move com facilidade e preteza; para mim ser ágil é ser rápido e resiliente as mudanças; é ir além de aceitar as mudanças… é entender que ela é uma característica da vida.  Quando falamos de TI isso fica mais focado ainda na questão de ser rápido. Gosto de usar a figura de barcos: ser ágil é ser como um pequeno veleiro que pode mudar de direção facilmente.

Frente a isso fica mais fácil entender o que vou dizer a seguir: acredito que nem 1% das empresas que dizem ser ágeis se encaixam na “minha definição”.  Penso, sinceramente, que elas apenas trocaram de nomes, no fim é exatamente as mesmas coisas só que trocaram suas roupas. Antes eram cronogramas, agora são cartões com bluetaks pelas paredes, burns downs e quadro brancos.  Antes tinhamos gerentes, agora temos os Scrum Masters.

Uma outra coisa que considero ruim é que as pessoas não conseguem entender que devemos fazer aquilo que nos torna mais feliz e produtivos. Se controle em cascata deixa a nossa equipe mais produtiva será isso que irei usar. Simples assim. Seguir os processos ips letri é bom quando a gente não está acostumado e ainda não está maduro nele. Entretanto, depois de certo tempo seguir o processo e fazer as coisas só por que dizem que deve ser feito não é nada inteligente.

Uma vez ouvi uma história dos macacos que são colocados numa jaula. Cada vez que algum deles tentava pegar uma banana, todos tomavam uma descarga elétrica. Assim, com o tempo, foram trocando os macacos um a um. Eles desenvolveram um comportamento interessante de bater naquele que tentasse acessar a banana para evitar os choques.  Mesmo tempos depois tendo desligado os choques, os novos habitantes da gaiola batiam naquele que tentava acessar as bananas reproduzindo o comportamente sem nem mais saber porque.

Isso acontece o tempo todo em nosso meio. As pessoas fazem daily meetings e nem sabem mais porque… fazem por que tem que ser assim e ponto final. Cara se na sua equipe já existe a comunicação o daily pode ser uma grande perda de tempo. Pense nisso.  Reviews e retrospectivas também podem ser perdas de tempo. O próprio XP diz que é interessante que os conflitos sejam resolvidos o quanto antes e que a comunicação deve ser permanete e fluída. Assim ter uma reunião para conversar, para mim, por si só é um contra senso.

Ultimamente, o meu time tem usado algo que se assemelha mais ao kanban: kanban é a ferramente visual e não o processo. O processo é algo mais Lean – tentamos sempre manter o fluxo de entregas de forma constante e sem interrupções.  Procuramos sempre o que é melhor para nossa produtividade. Eliminar desperdícios mesmo que isso seja parte do próprio processo como se diz no livro. Inteligência é usar algo e adaptá-lo a sua necessidades. Seguir processos sem questioná-los é escolher não evoluir e correr o risco de acertar.

Colar post it na parede não diz que você é ágil. Ser ágil, para mim, é ser adaptativo. Mudar e procurar sempre o gradiente máximo.  Toda vez que leio o manifesto ágil, vejo ali um pedido para sermos mais inteligentes e produtivos. Usar processos não é ser ágil.. trocar waterfall por scrum não vai magicamente fazer entregar mais. É algo mais perto da cultura que deve ser mexida.

 

Published by Andre, on março 15th, 2011 at 12:07 am. Filled under: agil,atualidades,scrum Tags: , , , 5 Comments