¿Por qué Sinatra?
Es cierto que hay entornos más completos y funcionales que Sinatra, pero la pega está en que los entornos populares como Rails requieren una pila de dependencias enorme y están orientados para aplicaciones web un poco más complejas y extensas. Sinatra es la opción ideal debido a su simplicidad y su cantidad mínima de dependencias. más información
¿Por qué Redis?
Si bien se podría usar un Hash interno, que de manera más eficiente lleve el registro de todo. El uso de Redis agrega unas cuantas ventajas muy atractivas, como la persistencia de datos, tiempo de vida para los datos y la capacidad de acceder externamente y realizar métricas sobre los datos. más información
El Algoritmo
De toda la aplicación, esta es la parte mas fácil de implementar, pero sin embargo, es la que requiere un poco más de cuidado de nuestra parte. Una mala elección en el algoritmo interno podría resultar en procesamiento innecesario, perdidas de tiempo y por que no, problemas de mantenimiento. A continuación listo algunos acercamientos junto con sus pros y contras.
- Strings Aleatorios
- PROS
- Fácil de implementar.
- CONS
- Se debe comprobar mucho.
- Se podría necesitar generar varias cadenas aleatorias para obtener una disponible.
- Strings Secuenciales
- PROS
- Fácil de implementar.
- No requiere comprobaciones.
- CONS
- Cualquier usuario podría predecir direcciones válidas. - Permutación De Carácteres
- PROS
- Fácil de implementar.
- No requiere comprobaciones.
- Las direcciones no se pueden predecir.
- CONS
-
class Url def initialize(n=1) @n = n @seq = mkseq(@n) end def next begin @seq.next.join rescue StopIteration @n += 1 fail 'No more resources available!' if @n > CHARS.length @seq = mkseq(@n) self.next() end end private def mkseq(n, random=true) if random CHARS.shuffle.permutation(n) else CHARS.permutation(n) end end CHARS = [*(0..9), *('A'..'Z'), *('a'..'z')] endEs muy importante señalar que es una mala idea convertir el resultado del método Array#permutation en un array, pues podría resultar en una perdida de tiempo del orden de minutos o días. Bueno, implementado ya el corazón de la aplicación, solo nos queda manejar las peticiones con Sinatra y unir todo.
La mecánica es la siguiente: Cuando un usuario hace una petición a nuestro servidor a través de una URL corta, se toma la segunda parte de la dirección — el recurso o path — y se hace una petición a Redis, si dicho recurso existe, el usuario es redirigido al enlace devuelto por Redis utilizando el método redirect, si no existe, entonces en vez de redirigir al usuario, se presenta un aviso con información al respecto.
#!/usr/bin/env ruby #encoding: UTF-8 require './url.rb' require 'redis' rd_child, wr_parent = IO.pipe() rd_parent, wr_child = IO.pipe() if fork() rd_child.close wr_child.close resources = Url.new loop do IO.select([rd_parent]) rd_parent.read(1) wr_parent.write(resources.next) end else rd_parent.close wr_parent.close require 'sinatra' redis = Redis.new set :static, true set :public_folder, File.join(File.dirname(__FILE__), 'static') get '/' do erb :index #template end get '/:resource' do @url = redis.get(params[:resource]) if @url redirect(@url) else halt 400, '<html><head><title>Error</title></head><body><h1>Not Found</h1></body></html>' end end post '/create' do begin URI.parse params[:url] rescue halt 400, '<html><head><title>Error</title></head><body><h1>Bad Url</h1></body></html>' end wr_child.write('g') IO.select([rd_child]) @resource = rd_child.sysread(512) redis.set(@resource, params[:url]) erb :index # tamplate end endHay un pequeño inconveniente del que no se habla mucho en la documentación oficial de Ruby. Los Fibers no se pueden utilizar a través de múltiples hilos, por ejemplo si intentas ejecutar el siguiente código, producirá un error:
fiber = nil Thread.new do fiber = Fiber.new do { Fiber.yield 1 } end fiber.resume # FiberError: fiber called across threads
Página Web
Bueno, como realmente el diseño es para los diseñadores, me haré de una página que genere una interfaz más o menos aceptable para luego descargar el código y modificarlo a gusto.
Para lograr una interfaz parecida a la de esta entrada, visita http://www.phpform.org/ y selecciona a gusto los elementos de la página principal, descarga el código y personifícalo, recuerda, la página principal debe quedar lo más simple posible.
Una cosa más, para mostrar la nueva url al usuario, se utiliza el valor almacenado por la aplicación en la variable de instancia @resource. Cada vez que se acorta un enlace, dicha variable es accesible desde el view, en mi caso el fichero index.erb — el cual elaboré a partir del código generado por la página phpform —. El siguiente es un fragmento del código necesario para mostrar la URL recién creada en el pie (footer) de la página desde el view index.erb.
<div id="footer">
<% if @resource %>
<% url = "http://localhost:4567/#@resource" %>
<p><a href="<%=url%>"><%=url%></a></p>
<% else %>
<p><a href="">Submit new url</a> </p>
<% end %>
</div>
Descargar Aplicación De La Entrada
Actualización 9/12/12
Uso del método Array#permutation en lugar del método Array#combination.
No hay comentarios:
Publicar un comentario