Marcio Trindade

Ruby on Rails

Problemas com campos inet no Rails

A partir do Rails 4 podemos utilizar campos específicos do PostgreSql com está descrito no Rails Guide. Um destes campos que resolvi utilizam ontem foi o campo inet que representa um host ou uma rede com IPv4 ou IPv6. Sendo assim podemos armazenar registros de range de IPs como por exemplo 192.168.0.0/24.

Como podemos ver no Pull Request #6196 o Rails converte este tipo de campo no objeto IPAddr do ruby então vamos entender como este objeto funciona. Vamos começar pelo range de IPs 192.168.0.0/32 que podemos entender que o 192.168.0.0 representa o IP e o /30 representa o prefixo. Quando instanciamos um objeto IPAddr com este range de IP o mesmo deixa de utilizar o prefixo e passa a utilizar uma representação com máscara 255.255.255.252, veja o exemplo abaixo.

ruby
IPAddr.new('192.168.0.0/30')
# => #<IPAddr: IPv4:192.168.0.0/255.255.255.252>

Depois que instanciava o Objeto não consegui mais representar o mesmo através de ip/prefixo, assim quando criei um formulário eu podia entrar com o valor de um range, mas ao editar o mesmo ele me trazia somente o IP e ignorava o prefixo. Olhando o código do Rails vi que como é feito a representação ip/prefixo que é salva no banco e é como eu precisaria mostrar no formulário de novo, então passei a utilizar o mesmo código na minha aplicação.

Após alguns testes observei um outro problema, quando eu altero somente o prefixo de um range o Rails não salva esta alteração no banco e entendi que o problema está no jeito como o IPAddr trata a comparação entre os objetos. Para este tipo de objeto a comparação é feita somente com o IP e ignorado o prefixo como podemos ver no código abaixo.

ruby
IPAddr.new('192.168.0.0/24') == IPAddr.new('192.168.0.0/30')
# => true

Tendo isso em mente e entendendo o Dirty Models do Rails ficou fácil perceber por que quando altero somente o prefixo o novo valor não é salvo no banco de dados. Então pra solucionar este segundo problema utilizo o método attribute_will_change! (no meu caso address_will_change!) para informar o Rails que precisa salvar este no banco sempre.


Rails 5 - redirect_back

Com a chega do Rails 5 se aproximando vamos ver uma das muitas novidades que estão por vir.

Estou falando do método redirect_back que nos permite redirecionar o usuário para a URL anterior, você deve estar pensando "Nossa mas já havia isso no Rails 4, por que é novidade?" Pois bem a grande diferença é como isso funciona.

Para redirecionar para o URL anterior a mesma precise estar no request.headers["Referer"] por que se não houver uma exception do tipo ActionController::RedirectBackError é lançada, então nós tinhamos que tratar isso no nosso código. Veja exemplo abaixo.

ruby
class UsersController < ApplicationController
  rescue_from ActionController::RedirectBackError, with: :redirect_to_default

  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to :back
    else
      render :new
    end
  end

  private

  def redirect_to_default
    redirect_to users_path
  end
end

Rails + Unicorn + Upstart + Monit

Hoje vou mostrar como estou utilizando o deploy deste blog que é um projeto Rails com o código fonte aberto. O mesmo está rodando em um servidor linux "Ubuntu 14.04.1 LTS (Trusty Tahr)". Não vou entrar em detalhes aqui de como preparei a máquina, mas deixo a dica de que estou utilizando Ansible pra isso.

Unicorn

A configuração do unicorn que eu estou utilizando não tem muito segredo, é uma configuração simples que você encontra parecidas no github como exemplo.

/etc/unicorn/blog.rb
listen            "/tmp/blog-unicorn.sock"
logger            Logger.new($stdout)
pid               "/var/run/blog/unicorn.pid"
preload_app       true
timeout           10
worker_processes  2
working_directory "/var/www/blog/current"

before_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
end

after_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection

  child_pid = server.config[:pid].sub(".pid", "#{worker.nr}.pid")
  system("echo #{Process.pid} > #{child_pid}")
end

A nova sintaxe de mocks do rspec

No último artigo escrevi sobre a nova sintaxe do rspec, agora vou falar sobre a nova sintaxe para os mocks do rspec. Veja a documentação.

Apartir do rspec 2.14.0 as duas sintaxes estão presentes e você pode especificar qual deseja usar no spec_helper.

spec_helper.rb
RSpec.configure do |config|
  config.mock_with :rspec do |c|
    # c.syntax = [:expect, :should]
    # c.syntax = :should
    c.syntax = :expect
  end
  config.expect_with :rspec do |c|
    # c.syntax = [:should, :expect]
    # c.syntax = :should
    c.syntax = :expect
  end

A nova sintaxe do rspec

O Rspec agora possui uma nova sintaxe que ao meu ponto de vista ficou um pouco mais coeso com o que realmente o teste está testando.

Pra configurar é bem simples, basta você setar qual modo deseja usar no seu spec_helper.

spec_helper.rb
RSpec.configure do |config|
  config.expect_with :rspec do |c|
    # c.syntax = [:should, :expect]
    # c.syntax = :should
    c.syntax = :expect
  end
end

Acredito que o código acima é auto explicativo e não se faz necessário explicações.


Utilizando gems locais em desenvolvimento

Hoje encontrei uma dica muito útil no site The Life of a Radar.

Quem utiliza alguma gem na qual está trabalhando constantemente sabe como é chato ficar alterando o arquivo Gemfile sempre que vai fazer um deploy. Este é o meu caso que tenho os models do meu projeto separado em uma gem (Rails Engine) a fim de reutiliza-los em todos os projetos, tanto front-end quanto workers.

Como a maior parte do tempo estou desenvolvendo, mantenho o meu Gemfile configurado desta forma:

Gemfile
gem 'projet-models', path: "../project-models"

Começando com Mongoid

Hoje quero mostrar algo muito simples, mas é pra encorajar as pessoas que ainda não usaram o MongoDB como banco de dados de seus projetos.

É importante você conhecer pelo menos um pouco do mongodb, saber como instalar, iniciar, parar, reiniciar, fazer uma query e inserir e editar documentos.

Depois que já tiver este background então vale a pena dar uma olhada na página do Mongoid, que é uma ótima gem criada pelo Durran Jordan pra trabalhar com mongodb no rails.


Pjax

Não faz muito tempo que escutei falar de pjax uma técnica que une ajax + pushState e que o Chris Wanstrath do github difundiu criando o jquery-pjax, recentemente o Ryan Bates do railscts.com publicou um epsódio mostrando como você pode utiliza-la com o rails.

Você já deve ter visto a arvore de códigos (source tree) que o github utiliza, quando você clica em um arquivo ou diretório ele atualiza o conteúdo via ajax e altera a url pra que você possa copiar e mandar pra outras pessoas sem o uso de ancoras que hoje em dia é o uso comum. Isso mantém o histórico de navegação dos navegadores funcionando.


Nginx + Unicorn

Desde que a Phusion lançou o passenger eu sempre o utilizei em conjunto com o apache que já era familizarizado.

Quando trabalhei na Digital Pages me deparei com o pessoal utilizando o passenger com nginx, pra falar a verdade eu só tinha escutado falar deste servidor web e nem dei muita bola, além de que a aplicação caia de tempos em tempos e o log ficava poluído com algumas mensagens que o passenger adicionava, mostrei para o pessoal como utilizar o apache no lugar do nginx, a aplicação acabou ficando bem estável e optamos por colocar em produção.


Introdução ao Guard

Dando continuidade aos últimos artigos resolvi escrever sobre a Guard, uma gem que complementa de forma incrível o uso do spork e automatiza seus testes.

A Guard tem uma função parecida com a Watcher, que praticamente fica monitorando alterações em seus arquivos e pode executar deterinadas ações quando isso ocorre, porém ele é bem estruturado e aceita outras gems complementares pra facilitar nosso dia a dia, uma destas é a guard-rspec e a guard-spork.