Limpando CSS com Ruby
Essa dica é para quem trabalha com um ou vários arquivos CSS. Geralmente esquecemos de remover as classes criadas para teste, etc. Há uma maneira simples e eficaz para verificar as classes que não estão sendo utilizadas no projeto. A gem deadweight permite executar uma Rake Task que faz essa verificação.
Instalação:
gem install deadweight
Task:
require 'deadweight' Deadweight::RakeTask.new do |dw| dw.stylesheets = %w( /stylesheets/style.css ) dw.pages = %w( /page/1 /about ) end
Execução:
rake deadweight
Primeiro informamos ao deadweight onde está localizado o arquivo css e depois as páginas que serão verificadas. Para executar essa task é necessário que você inicie o WEBrick (ou outro servidor que você esteja usando).
Ao executar a task, é exibido um relário completo de classes por página e um relatório contendo as classes que não são utilizadas.
Rodando processos do sistema com Ruby
Após pesquisar um pouco sobre automatização, resolvi criar alguns scripts para me ajudar a obter maior produtividade no dia-a-dia. Comecei criando um script para me ajudar a realizar merge de branchs com o GIT. O primeiro passo foi descobrir como executar comandos com o Ruby. Encontrei os métodos: Kernel.system e Kernel.exec.
Há uma diferença na utilização desses métodos. Ao utilizar o método Kernel.exec só conseguimos rodar um comando, pois perdemos o controle do processo corrente. Felizmente existe o método Kernel.system. Esse método permite executar comandos em um sub shell, e então continuar processando os comandos seguintes.
O script de automatização ficou assim:
require 'optparse'
class GitUtil
REMOTE = "origin"
MASTER_BRANCH = "master"
def parsed_options
options = {}
OptionParser.new do |opts|
opts.on("-m [BRANCH]") do |branch|
list_local_branches
merge branch
push_remote_branch MASTER_BRANCH
delete_local_branch branch
end
end.parse!
end
def list_local_branches
puts "\nListing all local branches"
system "git branch"
end
def push_remote_branch branch
puts "\nPushing -> #{branch}"
system "git push #{REMOTE} #{branch}"
end
def merge branch
puts "\nMerging master and #{branch}"
system "git checkout master"
system "git merge #{branch}"
end
def delete_local_branch branch
puts "\nDeleting local branch -> #{branch}"
system "git branch -D #{branch}"
end
if __FILE__ == $PROGRAM_NAME
g = GitUtil.new
g.parsed_options
end
end
Esse script executa os seguintes comandos:
- Lista todos os branches
- Faz o merge entre o branch master e o branch selecionado
- Faz o push do branch master
- Deleta o branch local selecionado
Para rodar o script basta executar a seguinte linha:
ruby gitutil.rb -m [BRANCH]
#Tip 2 – Merge de branches com o GIT no Windows
Essa dica é para quem utiliza o GIT no windows através do msysgit. Ao realizar o merge de branchs, frequentemente encontramos alguns conflitos. Para ajudar a resolver esses conflitos, você pode utilizar o KDiff3, uma ferramenta de diff e merge.
Configuração
Para utilizar o KDiff3, basta executar a seguinte linha:
git config --global merge.tool kdiff3
E colocar o executável do KDiff3 no diretório: Git/bin
Após realizar a configuração, já é possível executar o KDiff3 como ferramenta de diff e merge:
git mergetool
Mensagens flash dinâmicas no Rails
Uma funcionalidade interessante nas aplicações rails é a utilização do objeto Flash. Ele fornece uma forma de passar objetos entre ações. Essa é uma boa opção para enviar mensagens de alerta, erros, sucesso, etc.
Para criarmos uma mensagem notificando determinada operação na aplicação, definimos um controller da seguinte forma:
class PostsController < ActionController::Base
def create
flash[:notice] = "Successfully created post"
redirect_to posts_path(@post)
end
end
[/code]
Para exibir a mensagem no template:
[code language="ruby"]
<% if flash[:notice] %>
<div class="notice"><%= flash[:notice] %></div>
<% end %>
Podemos melhorar a forma como essas mensagens são exibidas para o usuário. A melhor forma de fazer isso, é utilizar o JQuery.
O primeiro passo é criar um helper para exibir as mensagens:
FLASH_NOTICE_KEYS = [:error, :notice, :warning]
def flash_message
messages = ""
FLASH_NOTICE_KEYS.each {|type|
if flash[type]
messages += "<div id='flash_messages'>"
messages += "<p class=\"#{type}\">#{flash[type]}</p>"
messages += "</div>"
end
}
messages
end
Para exibir as mensagens no template:
<%= flash_message %>
Agora podemos tornar a exibição das mensagens mais interativa utilizando os efeitos que o JQuery disponibiliza:
<script type="text/javascript">
$(document).ready(function() {
setTimeout(
function() {
$("#flash_messages").fadeOut(800)
},
2500);
});
</script>
O código javascript criado é responsável por realizar um efeito fadeOut no DIV que contém a mensagem. Dessa forma, a mensagem de erro é exibida para o usuário e depois de 8ms o DIV é escondido automaticamente.
Se você estiver utilizando o Prototype, pode criar o mesmo efeito assim:
<script type="text/javascript">
document.observe('dom:loaded', function() {
setTimeout(hideFlashes, 2500);
});
var hideFlashes = function() {
$$('.notice', '.warning', '.error').each(function(e) {
if (e) Effect.Fade(e, { duration: 1.5 });
})
}
</script>
Utilizando JQuery no Rails
Atualmente estou trabalhando em uma aplicação Rails que utiliza o framework javascript Prototype através dos helpers. Decidimos migrar essa aplicação para o JQuery:
jQuery is a fast, concise, JavaScript Library that simplifies how you traverse HTML documents, handle events, perform animations, and add Ajax interactions to your web pages. jQuery is designed to change the way that you write JavaScript.
Essa mudança casou um problema: Não queremos perder toda a flexibilidade e facilidade dos helpers do Prototype.
A solução encontrada foi o jRails:
jRails is a drop-in jQuery replacement for Prototype/script.aculo.us on Rails. Using jRails, you can get all of the same default Rails helpers for javascript functionality using the lighter jQuery library.
Instalação
script/plugin install git://github.com/aaronchi/jrails.git
Após instalar o plugin, copie os arquivos javascript do plugin para o diretório plublic/javascripts da sua aplicação.
Configuração
Adicione a seguinte linha no arquivo application.html.erb:
<%= javascript_include_tag :defaults %>
Essa linha adicionará os arquivos do jRails em vez dos arquivos do prototype/script.aculo.us. Após realizar os passos descritos, você já pode utilizar os helpers normalmente. O jRails faz todo o trabalho sujo pra você. 😉
O jRails também adiciona o JQuery UI ao projeto:
jQuery UI provides abstractions for low-level interaction and animation, advanced effects and high-level, themeable widgets, built on top of the jQuery JavaScript Library, that you can use to build highly interactive web applications.
Em breve vou postar sobre as facilidades que o JQuery UI fornece para tornar as aplicações Web mais interativas.
#Tip 1 – Editor Ruby/Rails para o Windows
Essa dica é para quem desenvolve aplicações Ruby/Rails no Windows. Se você está procurando um editor leve, simples e customizável, dê uma olhada no Intype.
O Intype é bem parecido com o famoso editor para MacOs, o TextMate. O Intype possui suporte a temas e bundles de diversas linguagens (PHP, Python, Ruby, etc).
Customização
É possível converter temas do TextMate para o modelo de temas do Intype através de um script Python. Todas as informações para realizar a conversão estão disponíveis aqui.
O site http://www.tmthemes.com/ possui diversos temas do TextMate para download.
É possível também utilizar a mesma fonte do Editor do TextMate, a fonte Monaco. Você pode fazer o download da fonte aqui.
WeatherFinder – Biblioteca para previsão de tempo
Há um tempo atrás, eu estava envolvido na construção de uma funcionalidade que precisava de informações relacionadas com previsão do tempo. Pois bem, fiz algumas pesquisas atrás de algumas bibliotecas para previsão do tempo com Ruby e nenhuma me agradou totalmente.
Como um bom curioso, coloquei a mão na massa e resolvi implementar uma biblioteca para previsão do tempo acessando o serviço do site Weather.com. O site permite ter acesso a previsão do tempo através de um feed XML.
O WeatherFinder foi criado utilizando basicamente o Hpricot para fazer o parser do XML e o MemCached para operações de cache.
Instalação
gem install weatherfinder
gem install hpricot
Para utilizar o WeatherFinder, é preciso obter o partner_id e license_key do Weather.com. Acesse o seguinte endereço para obter os dados: http://www.weather.com/services/xmloap.html
Após se registrar, você receberá um email contendo o partner_id e license_key que serão utilizados no WeatherFinder.
Após a instalação da biblioteca já é possível recuperar informações sobre a previsão do tempo:
Condições atuais
require "weatherfinder"
service = Weather::WeatherFinder.new
# Partner ID and License Key provided by Weather.com
service.partner_id = your partner id
service.license_key = your license key
# Fetching current forecast from Rio de Janeiro
forecast = service.load_forecast("BRXX0201", 2)
puts "Current Description: " + forecast.current.description
puts "Current Temperature: " + forecast.current.temp
puts "Current Icon: " + forecast.current.icon
puts "Current Wind Speed: " + forecast.current.wind.speed
Previsão do tempo para amanhã
puts "Tomorrow's High Temperature: " + forecast.tomorrow.high puts "Tomorrow's Low Temperature: " + forecast.tomorrow.low puts "Tomorrow's Sunrise: " + forecast.tomorrow.sunrise puts "Tomorrow's Sunset: " + forecast.tomorrow.sunset puts "Tomorrow's Wind Speed" + forecast.tomorrow.wind.speed
Previsões futuras
Previsões futuras podem ser recuperadas através do método day() da classe Forecast. Esse método aceita um parâmetro indicando o número de dias que serão recuperados. Essa funcionalidade depende do parâmetro days, que foi definido no método load_forecast() da classe WeatherFinder.
puts "High Temperature 2 days from now: " + forecast.day(2).high puts "High Temperature 2 days from now: " + forecast.day(2).low puts "Sunrise 2 days from now: " + forecast.day(2).sunrise puts "Sunset 2 days from now: " + forecast.day(2).sunset
Cache
O WeatherFinder usa o Memcached para suportar o cache de dados. Essa funcionalidade é muito interessante quando há uma grande quantidade de acessos realizados pelos clientes.
Instalando o Ruby-MemCache
gem install Ruby-MemCache
Após a instalação do Ruby-MemCache, é preciso habilitar o cache:
service.enable_cache service.cache.servers = "localhost:11211" service.cache_expiry = 10
Para mais informações, dê uma olhada no código que está hospedado no Github e na página do projeto no RubyFourge.
Validação customizada no RoR
Quando um formulário possui erros, o ActionView adiciona um div envolvendo o campo que contém o erro. Esse div possui uma classe chamada “fieldWithErrors”:
<p>
<div class="fieldWithErrors">
<input id="user_name" name="user[name]" type="text" value="" />
</div>
</p>
A classe CSS correspondente:
.fieldWithErrors {
border: 2px solid red;
display: inline;
padding: 2px;
}
Nós podemos melhorar a forma como a validação é exibida nos formulários. O primeiro passo é criar o arquivo config/initializers/field_with_errors.rb com o seguinte conteúdo:
require 'hpricot'
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
doc = Hpricot(html_tag)
elem = (doc/"/").first
css_class = elem.attributes['class']
css_class ||= ""
css_array = css_class.split(" ")
css_array << "fieldWithErrors"
elem.attributes['class'] = css_array.uniq.join(" ")
doc.to_s
end
[/source]
Repare que no codigo acima nós utilizamos o <a href="http://github.com/why/hpricot/tree/master">hpricot</a> para customizar a tag HTML. O hpricot é um parser HTML bastante flexível e simples de utilizar. Se você não possui a gem hpricot instalada, execute o seguinte comando:
gem install hpricot
Após essa pequena customização, o campo que contém o erro será exibido da seguinte forma:
<p> <input id="user_name" name="user[name]" type="text" value="" class="fieldWithErrors" /> </p>
O formulário contendo erros será exibido assim:

Restful Authentication + RoleRequirement
Uma funcionalidade importante nos sistemas que desenvolvemos é a autenticação baseada em roles. Para quem utiliza plugin Restful Authentication, existe o plugin RoleRequirement que fornece toda a infra-estrutura necessária.
Instalação Restful Authentication
script/plugin install git://github.com/technoweenie/restful-authentication.git script/generate authenticated user sessions
Instalação RoleRequirement
script/plugin install git://github.com/timcharper/role_requirement.git script/generate roles Role User
O Generator do plugin RoleRequirement executa as seguintes modificações:
- Adiciona o método has_role? ao arquivo user.rb
- Gera uma migration com as modificações necessárias no banco de dados
- Escaneia o ApplicationController e insere as linhas “include AuthenticatedSystem“, e “include RoleRequirementSystem“, se ainda não foram incluídas.
Restrição no Controller
Para informar que somente administradores podem acessar determinado controller:
class Users < ApplicationController require_role "admin" ... end[/source] Para informar que readers podem acessar tudo e somente administradores podem acessar a action destroy: [source language="ruby"]class Blog < ApplicationController require_role "reader" require_role "admin", :for => :destroy # não permite readers deletarem posts ... end
Restrição na View
Para restringir trechos de códigos na view, basta utilizar o helper has_role?:
<% if current_user.has_role?("admin") %>
...
<% end %>
Testando envio de emails com Cucumber
A maioria dos sitemas que construímos utiliza algum mecanismo para o envio de emails. Em certos cenários, precisamos testar se o usuário recebeu um email de ativação de cadastro ou um email informando que sua conta no sistema vai expirar em determinado dia.
Para realizar esses testes com o Cucumber, é necessário utilizar o plugin email-spec do Ben Mabey. Esse plugin possui uma série de steps que podem ser incluídos nos cenários:
Given no emails have been sent When I open the email When I follow "[link]" in the email When "[address]" opens the email with subject "[some text]" When "[address]" opens the email with text "[some text]" When I click the first link in the email Then I should receive an email Then "[address]" should receive n emails Then "[address]" should have n emails Then "[address]" should not receive an email Then I should see "[some text]" in the subject Then I should see "[some text]" in the email
Instalação
script/plugin install git://github.com/bmabey/email-spec.git
O próximo passo é adicionar a seguinte linha no arquivo features/support/env.rb. Adicione após a linha: “require ‘cucumber/rails/world”
require 'email_spec/cucumber'
Execute o seguinte comando:
script/generate email_spec
Esse comando criará o arquivo features/step_definitions/email_steps.rb. Esse arquivo contém uma série de steps que podem ser utilizados na descrição dos cenários.
Um cenário que utiliza envio de emails pode ser escrito da seguinte forma:
Scenario: Register new user
Given I am on the login page
Then I follow "Cadastre-se"
And I fill in "Nome" with "Carlos"
And I fill in "Login" with "carlos"
And I fill in "Email" with "carlos@gmail.com"
And I fill in "Senha" with "123456"
And I fill in "Confirmação da Senha" with "123456"
And I press "Sign up"
Then I should see "Obrigado por se registrar! Você receberá um email com o seu código de ativação."
And I should receive an email
When I open the email
Then I should see "Por favor ative sua nova conta." in the subject
When I click the first link in the email
Then I should see "Registro completo! Por favor, efetue o login."
Configurando o current_email_address
O arquivo email_steps.rb possui um método chamado current_email_address. Esse método define o endereço de email que será utilizado para testar o envio de emails.
Por exemplo, essa é a configuração que utilizo em meus projetos:
def current_email_address @email || (@current_user && @current_user.email) || "guilherme.ruby@gmail.com" end
Você também pode criar seus próprios steps combinando os helpers que o plugin email-spec fornece.