Criando seu próprio encurtador de URLs
Leia em 2 minutos
Um dia desses eu estava dando uma olhada na lista de domínios que eu tenho e vi que o domínio http://fnando.me estava sem uso. Aí, decidi criar um encurtador de URL usando Sinatra e Redis.
Criar encurtadores de URL é uma coisa bastante simples. Normalmente, utilizo o id
do registro que identifica a URL original em base 36, por exemplo. Com Ruby isso é moleza.
id = 12345
short = id.to_s(36)
puts short
#=> 9ix
puts short.to_i(36)
#=> 12345
Uma das vantagens de se usar a base 36 como identificador de URLs é que você tem um limite bastante grande com poucos caracteres. O id 9999999 gera o identificador 5yc1r
com apenas 5 caracteres.
Para armazenar estas URLs você pode usar o Redis. Fácil de usar e muito rápido, ele é perfeito para esta tarefa. No Ruby, você pode usar a gem redis.
Primeiro, precisamos definir como iremos armazenar a URL. Você usar uma chave que contenha uma identificação da URL original, além do identificador curto desta URL. Algo como urls:<url>:<id curto>
.
Para saber qual o próximo id disponível para a URL você pode usar o comando INCR
, que incrementa um número inteiro em uma chave.
redis = Redis.new
sid = redis.incr("urls._id").to_s(36)
Para armazenar uma representação da URL, vamos gerar um hash MD5.
require "digest/md5"
url = "http://codeplane.com.br"
hash = Digest::MD5.hexdigest(url)
Agora podemos armazenar a URL no Redis.
key = "urls:#{hash}:#{sid}"
redis.set(key, url)
Antes de armazenar a URL você pode verificar se esta URL já foi salva com o comando KEYS
, passando o hash da URL.
unless redis.keys("urls:#{hash}:*").first
redis.set(key, url)
end
Para pegar a URL original, você pode fazer uma consulta semelhante, passando apenas o id curto.
key = redis.keys("urls:*:#{sid}").first
url = redis.get(key) if key
Agora, como fazer tudo isso rodar em produção? Simples! Eu já peguei tudo isso e empacotei em uma gem ridiculamente simples de usar.
Usando o Dogo
A gem dogo faz basicamente tudo isso e ainda disponibiliza um servidor Sinatra que já lida com as requisições.
Crie um arquivo Gemfile
com as dependências de nosso projeto. Em desenvolvimento, gosto de usar o Thin.
source :rubygems
gem "dogo"
gem "unicorn"
group :development do
gem "pry"
gem "awesome_print"
gem "thin"
end
Nós vamos precisar do arquivo config.ru
, com as informações do Dogo.
require "bundler/setup"
require "dogo"
Dogo.host = "http://fnando.me"
Dogo.api_key = "abc"
Dogo.default_url = "http://nandovieira.com.br"
run Dogo::Server.new
Agora é só iniciar o servidor! Em modo de desenvolvimento, basta executar rackup
.
O servidor Sinatra responde a alguns endpoints. O caminho raíz irá redirecionar para a URL definida em Dogo.default_url
.
Para encurtar uma URL, você pode executar o comando curl 'http://fnando.me/shorten?api_key=abc&url=http://codeplane.com
. Note que estou usando o verbo HTTP GET
para criar a URL (OMG! REST! OHNOES!). Isso é necessário para funcionar com o Tweetbot, cliente de Twitter que uso tanto no Mac, quanto no iPhone.
Finalmente, a URL original pode ser acessada através do endereço http://fnando.me/<id>
.
O Dogo faz a contagem de cliques em cada URL. Estes contadores são armazenados em uma chave clicks.<id curto>
. No futuro, quero adicionar um dashboard que irá exibir isso com alguns gráficos e coisas do tipo, mas tudo vai depender da preguiça (ou da falta dela).