Marcio Trindade

Março de 2016

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