2008-07-22

El día que Stallman apareció en infobae

Cada vez más, el software libre se hace mainstream. Y hoy pasó una vez más. Hoy entré a infobae.com y, sorpresa, sorpresa, al lado de los escándalos de la ex-novia de Ronaldo estaba una nota sobre ¡Richard Stallman! Acá les paso el link. ¡Y encima Stallman está peinado!

Hasta el premio Nobel de la Paz no para.

Happy hacking,
Aureliano.

2008-07-12

Criptografía cuántica - explicación simple.

Ayer a la tarde vino al laburo Andrés Rieznik a dar una charla sobre criptografía cuántica. Andrés es doctor en física, es especialista en fibras ópticas y actualmente está haciendo un post-doctorado en el ITBA (sí, donde yo estoy haciendo el doctorado). Y Andrés también iba a la división de al lado en la secundaria.
Bueno, volviendo al tema de la criptografía cuántica, primero nos explicó en que fenómenos de la física cuántica está basada.

Polarización, fotones y otras cosas que solo los físicos entienden


Como todos sabemos, la luz a veces es una onda. Cuando hace de onda, la onda puede oscilar en cualquier ángulo (por ejemplo: verticalmente, horizontalmente o diagonalmente). Y cuando pasa a través de un polarizador queda oscilando en la dirección que el polarizador la deja. Y si después ponemos otro a 90 grados, no pasa nada. Y más aún, si ponemos el segundo polarizador a 45 grados sale un cuarto de la luz original (se queda la mitad en cada polarizador) ¡polarizada en el ángulo del segundo polarizador!
Pero también, la luz a veces es muchas partículas. Cuándo actúa como partículas, decimos que está compuesta por fotones. Entonces ¿qué pasa cuando pasa un solo fotón por un polarizador?.
Según entendí de lo que dijo Andrés, los fotones tienen un ángulo de oscilación. Y pasan o no por un polarizador en función de ese ángulo. En particular, si el polarizador está perfectamente "alineado" con la oscilación del fotón pasa siempre y si está perfectamente cruzado con el fotón no pasa nunca. ¿Y qué pasa si está a 45 grados? Entonces pasa la mitad de las veces y, cuando pasa, cambia el ángulo de su oscilación al del polarizador por el que pasó.
Bueno, usando todo lo que está arriba, hace ya 24 años a unos cráneos se les ocurrió una idea para que 2 partes (digamos, para mantener las costumbres, Alice y Bob) compartan un "stream" de números al azar. O sea, que los 2 sepan los mismos números, pero que estos números no están definidos de antemano. Para los que sepan cripto, el resultado es muy parecido a Diffie-Hellman pero se llega por caminos muy distintos. A ese tipo de algoritmos se los llama de "intercambio secreto de claves". El primer algoritmo que hicieron se llama BB84 (son unos cráneos pero tienen menos onda para los nombres que un fotón).

El BB84


En el algoritmo BB84, cada número lo manda como un único fotón polarizado en un ángulo de 0, 45, 90 o 135 grados. 0 grados se corresponde a un 0 en base A, 45 grados a un 0 en base B, 90 grados a un 1 en base A y 135 grados a un 1 en base b.

El algoritmo consiste en lo siguiente:
Alice elige una base (entre A y B) y un nro (entre 0 y 1) y manda un fotón polarizado en el ángulo correspondiente. Bob elige una base al azar y detecta (en esa base) el número. Si los 2 eligieron la misma base, los 2 conocen el mismo número, sino hay un 50% de chances de que Bob tenga un número distinto del que tiene Alice. Este procedimiento se repite n veces.
Cuando Alice termina de mandar cada uno de los bits, Alice manda por un canal normal (no cuántico) que bases usó para mandar cada bit. Bob, con esa información, le dice cuáles de los bits leyó en la misma base que Alice transmitió (van a ser aproximadamente la mitad).
Para ver que efectivamente nadie cambió los bits en el camino se eligen algunos de los bits efectivamente transmitidos y se los manda por un canal no cuántico para verificar que la transmisión fue correcta.
Después de esto, los bits efectivamente transmitidos que no se publicaron son los que forman la clave conocida por Alice y Bob.

¿Y Eve?


¿Qué pasa si Eve interfiere un fotón? Bueno esta es LA gracia de todo el asunto. En mecánica cuántica existe una cosa que se llama "principio de incertidumbre" (lo siento, va sin link porque no me gustó el artículo de wikipedia). El principio de incertidumbre dice que es imposible obtener una medida en una partícula sin modificarla en otra aledaña. Por ejemplo, si medís la velocidad de una partícula modificás su posición, haciendo imposible saber la posición original.
Bueno, el número (0 o 1) y la base (A o B) que representa el ángulo de polarización de un fotón es como saber la posición y la velocidad. Si medís el número, como tiene que pasar por un polarizador, cambiás potencialmente la base del fotón (el 50% de los casos). O sea, que si Eve escucha un fotón, el 50% de los casos le cambia la base. De ese 50%, el 50% de los casos Bob va a escuchar el número incorrecto. Por lo tanto, en la verificación que se hace al final del algoritmo van a detectar que el 25% de los bits están mal. Entonces se detecta a Eve y no se usa esa clave para transmitir info segura.

Unas cositas más


El BB84 no es el único algoritmo conocido, hay variaciones, pero todas están basados en que para medir si un fotón pasa o no por un polarizador hay que cambiarle el ángulo de polarización.
Por último, las cosas que estén mal explicadas son culpa mía y las que estén bien culpa de Andres.

Happy hacking,
Aureliano.

2008-07-09

Problemas con el DNS de speedy

Update: Gracias a Manuel por llevar a mi atención esta vulnerabilidad del diseño del DNS. No está confirmado, pero es altamente probable que la inestabilidad de los servers de Speedy tengan que ver con esto. Les sugiero que como medida de seguridad usen los servidores DNSs que están mencionados abajo aunque tengan otro proveedor de internet, al menos por un tiempo. Estos servers aparentemente no son vulnerables.

Desde ayer que el DNS de speedy está inestable. Hoy llamé al soporte técnico y me lo confirmaron. Como no me dieron ningún servidor de DNS alternativo, me conecté a google x ip (estaba haciendoles ping para hacer troubleshooting) y leí algunos caches de páginas. Ahí encontré esta página donde sugieren usar Open DNS.

Así que configuré en mi router los servidores de nombres que dice ahí (son 208.67.222.222 y 208.67.220.220) y ahora puedo navegar nuevamente.

Happy hacking,
Aureliano.

2008-07-02

Cómo hacer que Telefónica arregle tu teléfono

Update: Incluye segundo corte de teléfono.

Según mi experiencia en reparaciones de mi línea telefónica, este es el algoritmo apropiado para lograr reparaciones medianamente rápidas:

  1. Llamar al 114 (te atiende el contestador automático)
  2. Llamar al 112. Nadie atiende.
  3. Llamar al 114 de nuevo. El contestador automático te informa que tu teléfono será reparado "en los plazos establecidos".
  4. Llamar al 112. Nadie atiende
  5. Llamar al 114 de nuevo. Poner otro número de teléfono. Te atiende el operador.
  6. El operador te corta antes de admitir que te está boludeando porque no puede abrir el ticket de la reparación del teléfono.
  7. Llamar al 112. Nadie atiende
  8. Llamar de nuevo al 114, poner de nuevo un número equivocado. Quejarse que te cortaron en la mitad de la queja anterior. Te explican que en realidad no pueden abrir tu ticket (operador buena onda, probablemente empezó a trabajar hoy).
  9. Llamar al 112. Atienden (¡milagro!). Abrís un ticket quejandote de que no hay plazos por la reparación y porque te cortaron cuando llamaste al 114.
  10. ????????????????????????????????
  11. A los 10' de la última llamada arreglan la línea.
  12. ????????????????????????????????
  13. A las 12 horas se vuelve a quedar sin tono.
  14. Habla mi suegra con Telefónica, dicen que vienen mañana antes de las 10 AM. Alegan que hay un problema en mi casa (¡pero pusieron a andar el teléfono 12 horas sin venir!).
  15. Me dejaron plantado. Segundo fin de semana sin teléfono ni internet.
  16. Llaman a mi suegra, que no está, y le dicen que van a venir cuando no haya nadie en el depto.
  17. Llamo a reparaciones de larga distancia de telefónica (al servicio técnico habitual no me puedo comunicar porque no te atienden cuando hay un arreglo en curso, seguro que esto es antirreglamentario). Por suerte toman los datos del teléfono de la oficina (o eso me hace creer la operadora.
  18. ?????????????????????????????????
  19. Llego a casa a la noche y anda todo (vamos a ver con cuanto tiempo).
Conclusión: Con Telefónica todo mal. Igual, llamen al 112 y rompan las bolas. Puntos extra si van o se quejan en Defensa del Consumidor.

Aureliano.

PD para la gente de codear: En cuanto tenga tiempo quiero hacer un post sobre mónadas en Haskell. Tenganme paciencia.

2008-06-27

Busco hogar para Amón

Estoy buscandole un nuevo hogar a Amón, mi gato. Es macho, super-juguetón, tiene 2 años y está castrado. Vivió siempre en depto, así que está acostumbrado a la compañía humana. A quién le interese ser su nuevo "papá" o "mamá" por favor comuníquese x acá o a aurelianocalvo at gmail.com (cambiando at por @).

Acá abajo pongo una foto así se van encariñando.



Muchas gracias,
Aureliano.

2008-06-22

Grandes ideas

Odio cuando se me ocurren ideas que creo "geniales" y "únicas" para después descubrir que alguien ya lo hizo antes. Esto me pasa todo el tiempo :(. Y hoy pasó de nuevo.
Cuando volvía caminando del fútbol del domingo al medio día se me ocurrió "estaría buenísima una página web que sirva para organizar los partidos de fútbol". Los quilombos de los mails solucionados. Nadie haría mucho laburo (después de todo, la página haría sola toda la coordinación). Así que me puse a hacerla, y mientras la hago chequeo el correo y veo otra página web que es para esto.
Bueno, parece que voy a tener más tiempo para estudiar para el doctorado.

Happy hacking,
Aureliano.

2008-06-18

El lock-out

A veces uno trata de explicar cómo son las cosas, pero viene un humorista y deja todo claro.

2008-06-12

Estudio de noche y Google Analytics

Ayer a la noche (u hoy a la madrugada, en función de lo que quieran pensar) fui como invitado al programa Estudio de Noche en la Radio de la Ciudad. Ahí estuvimos hablando con mi amigo Gustavo Fernandez Walker(el conductor) y mi amigo Martín Gonzalez(el otro invitado) sobre varias cosas, pero principalmente sobre el impacto de la Internet sobre la forma de generar eso a los que los abogados llaman propiedad intelectual. En particular estuvimos hablando del impacto en obras artísticas (y aún más en particular, de obras musicales). También tocamos tangencialmente el tema de los wikis y del desarrollo de software libre. Me divertí haciendo radio, así que probablemente vaya como invitado alguna otra vez.
También le mostré a Gustavo Google Analytics, que le gustó y lo puso en el blog del programa. Gustavo es filósofo y estas instrucciones le sirvieron, así que supongo que le servirán a cualquiera que tenga un blog en blogspot (y cambiandolas un poquito, cualquier página). Acá pego las instrucciones textuales:


Tenés que ir al panel de tu blog -> diseño -> Añadir un elemento de
página -> HTML/Javascript
y poner adentro de eso el cacho de código que te dicen que pongas en
google analytics. En mi caso es:
<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "ACA VA UN CODIGO TUYO";
urchinTracker();
</script>
Seguramente debe ser algo parecido en el tuyo. Una vez que tenés eso
agregado, cada vez que alguien se conecte a tu blog se va a bajar ese
código, que es el que hace que google pueda saber quienes se conectan
a tu página (así es en la mía). Es solo copiar el cacho de código en
blog como dice arriba y activar el trackeo en google (si no me acuerdo
mal).

Happy hacking,
Aureliano.

2008-06-08

Buscando hosting para mis proyectos personales

Hola a tod@s,
Estoy buscando un hosting para mi. La idea es poner ahí las cosas que haga para el doctorado (así tengo acceso desde todos lados, versionado y un backup en otro lado) y algún que otro prototipo de página web que se me ocurra (tenía algunos pensados que no hice porque no tenía dónde ponerlos).

Mis requerimientos principales son que tenga svn por ssh y https (para lo del doctorado) y que pueda correr ruby para hacer la página (y seguramente alguna base de datos).

También estaría bueno acceso por ssh para poder tocar todo, que sea barato y que si me paso del ancho de banda diario/ semanal/ mensual corten el site y me manden un mail para ver que hacer (para evitar la quiebra por slashdot). No creo que, al menos al principio, tenga mucho tráfico.

Por favor, cuéntenme cómo les fue con sus proveedores de hosting. Escucho sugerencias.

Happy hacking,
Aureliano.

2008-05-24

Wireless@home

Update: Más configuración por seguridad

Hoy configuré el modem ADSL/router/access point wireless (AKA: "el coso wireless") que me compré en la semana y lo hice andar completo (wireless con WAP2). Es un hermoso ZyXEL PRESTIGE 660R-61C.
Ahora les cuento que cosas tuve que hacer para hacerlo hacer andar.

Paso 1: conexión a speedy


Conecté una notebook con fluxbuntu al "coso wireless" con el cable ethernet que viene en la caja y enchufé el cable de teléfono (que también viene en la caja) entre el coso y la boca de teléfono. Configuré eth0 para que esté en 192.168.1.23 (creo que casi cualquier IP en la red 192.168.1.x debe andar) y me dirigí con un browser a 192.168.1.1. Por suerte le pegué (de re-pedo) y entré al menú de configuración del coso vía web.
Intenté setear de una la conexión pero no funcó :(. ¿Por qué? Los seteos de VPI y VCI correctos para speedy (8 y 35 respectivamente) no los aceptaba. Después de un rato de googleo, encontré la página de speedy donde explica como configurar el coso wireless. Lo único que hice distinto es que cómo ya había cambiado el password de administración desde la parte web, puse mi password nuevo. Seguí las instrucciones, setié como name servers a200.51.211.7 y 200.51.212.7 y ¡se conectó a speedy!

Paso 2: Compartir la conexión entre varias PCs


Con esto andando, el siguiente paso fue compartir la conexión entre 2 PCs. Esto fue re-fácil. Configuré mis 2 notebooks x DHCP y las enchufé al coso wireless con sendos cables ethernet. Una vez que las PCs estuvieron andando, ya tenía conexión a internet :D

Paso 3: El coso wireless es wireless (y se supone que yo sé de seguridad informática)


Bueno, esto ya estaba andando. Entonces me puse a configurar el acceso wireless. En cuanto miré, algún vecino "extra rápido" ya se había conectado a mi coso wireless y estaba leecheando internet. Así que empecé a cerrarlo. Mirando rápido el menú me pareció que había solo WEP, y "this is unacceptable" para un investigador en seguridad informática como yo (chiste). Así que apagué el wireless para que no leecheen más y empecé a mirar la configuración. Al final encontré la parte de WAP, que está en otro menú (parece que la amigabilidad no es parte de los objetivos de diseño del menú administrativo por web del coso wireless). Y empecé a probar.
Para hacer andar la parte wireless tuve un desafío extra, que es que lo hice andar en un Kubuntu Hardy (8.04). Por suerte, cuando compré mi notebook tuve la delicadeza de comprar una placa Intel, por lo que se me hizo más fácil. Después de varias vueltas descubrí que lo mejor era configurar el coso wireless en WPA2 con PSK (pre-shared key). Y solo queda explicar que hice para hacer que ande el Kubuntu. Probé un montón de cosas que no andaban hasta que encontré por internet (no tengo el link acá) que a veces reinstalando algunos paquetes la integración de las placas wireless. Así que hice
sudo aptitude reinstall knetworkmanager wpasupplicant network-manager
y santo remedio. Cargué el KNetwork Manager y cliquié el botón derecho, me conecté a mi red, puse el password que había configurado en el párrafo anterior y anduvo todo joya.

Paso 4: Ajustes finales


Por último, quiero seguir pudiendo conectarme a bit torrent bien para "bajar ISOs de Linux". Así que configuré que la MAC de mi notebook tenga IP fija (está en la parte de LAN del menú avanzado) y forwardié los puertos correspondientes (TCP y UDP) que tengo configurados en el KTorrent.
Y para que sea más seguro (mirando los ataques que postulan en GNU Citizen) le configuré distinto el SNMP. Me conecte x telnet al coso y cambié las opciones del menú 22, que quedó algo así:

SNMP:
Get Community= [passwd nuevo]
Set Community= [otro passwd nuevo]
Trusted Host= 0.0.0.0
Trap:
Community= [y otro pass más]
Destination= 0.0.0.0

En una mirada rápida, me pareció que el resto de los ataques requieren que el atacante esté conectado en la red interna, así que los dejo para después (supongo que requerirán un cambio de firmware).

Así llegué a la configuración que quería y me dediqué a escribir este post.
Happy hacking,
Aureliano.

2008-05-03

El último pelo

Este hilarante blog habla de los calvos. Sus problemas y soluciones. Envidias y resentimientos. Gracias y tristezas. Recomiendo ampliamente a este blog, que es el único que conozco que tiene su propia canción. En fin, tómense unos minutos y ríanse un rato. Yo, como buen calvo que soy, lo recomiendo.

Happy hacking,
Aureliano.

2008-05-02

Load balancer minimalista en ruby (parte 2)

En el post anterior mostré un load balancer que implementé en ruby. Ahora le agregué algo de manejo de errores y algo de fail-over para que sea más estable.

Este es el código nuevo:


#!/usr/bin/env ruby
require 'socket'

def usage
puts "Very simple balancer"
puts "balancer.rb port host1:port1 [host2:port2 ....]"
exit
end

def build_targets(argv)
argv.map do
|param|
result = param.split(":")
result[1] = result[1].to_i
result
end
end

class Balancer
def initialize(port, *targets)
@descriptors = []
@next_step = {}

@serverSocket = TCPServer.new( "", port )
@serverSocket.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
puts("Balancer started on port #{port}")

@descriptors << @serverSocket

@targets = targets
@current_out = 0
end

def next_out
out_socket = nil
until out_socket
begin
@current_out = (@current_out + 1) % @targets.length
descr = @targets[@current_out]
out_socket = TCPSocket.new(descr[0], descr[1])
rescue
# do nothing on purpose
end
end
out_socket
end

def accept_new_connection

incoming = @serverSocket.accept
outgoing = next_out

@next_step[incoming] = outgoing
@next_step[outgoing] = incoming

@descriptors += [ incoming, outgoing ]
end

def propagate(sock)
next_sock = @next_step[sock]
next_sock.write(sock.read_nonblock(1000 * 1000))
end

def finish_connection(sock)
next_sock = @next_step[sock]
[sock, next_sock].each do
|s|
begin
s.close
rescue Object => e
puts "Error closing socket: #{e.inspect}"
end
@descriptors.delete(s)
@next_step.delete(s)
end
end

def run
loop do
connections = select( @descriptors )
if connections
connections[0].each do
|sock|
if sock == @serverSocket then
accept_new_connection
else
begin
if sock.eof? then
finish_connection(sock)
else
propagate(sock)
end
rescue
finish_connection(sock)
end
end
end
end
end
end
end

trap("SIGINT") do
exit
end

usage if ARGV.length < 2
port = ARGV.shift.to_i
targets = build_targets(ARGV)

Balancer.new(port, *targets).run


Me parece que ahora pueden llegar a usarlo.

Happy hacking,
Aureliano.

2008-05-01

Load balancer minimalista en ruby

Update: Puse la versión más nueva en este post.

Ayer estuve inspirado y dediqué un poquito de mi tiempo a aprender un poco más sobre cómo manejar sockets. Hacía mucho tiempo que no programaba sockets en bajo nivel y nunca había necesitado usar select para manejar muchos al mismo tiempo. De hecho, la última vez que tuve que hacer algo parecido a un server que acepte muchas conexiones TCP lo hice en java 1.3, y en java 1.3 no hay select (agregaron algo parecido en java.nio, que apareció después).
Así que puse manos a la obra e hice un minicloncito de un load balancer en ruby.
Por supuesto que no hice todo. En particular, me comí el manejo de errores. Pero si hace round robin de conexiones y tiene menos de 100 líneas de código, lo que creo que lo hace un buen ejemplo de juguete sobre como manejar sockets en ruby.
Bueno, basta de cháchara, acá va el código completo:


#!/usr/bin/env ruby
require 'socket'

def usage
puts "Very simple balancer"
puts "balancer.rb port host1:port1 [host2:port2 ....]"
exit
end

def build_targets(argv)
argv.map do
|param|
result = param.split(":")
result[1] = result[1].to_i
result
end
end

class Balancer
def initialize(port, *targets)
@descriptors = []
@next_step = {}

@serverSocket = TCPServer.new( "", port )
@serverSocket.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
puts("Balancer started on port #{port}")

@descriptors << @serverSocket

@targets = targets
@current_out = 0
end

def next_out
@current_out = (@current_out + 1) % @targets.length
@targets[@current_out]
end

def accept_new_connection
out_descriptor = next_out

incoming = @serverSocket.accept
outgoing = TCPSocket.new(out_descriptor[0], out_descriptor[1])

@next_step[incoming] = outgoing
@next_step[outgoing] = incoming

@descriptors += [ incoming, outgoing ]
end

def propagate(sock)
next_sock = @next_step[sock]
next_sock.write(sock.read_nonblock(1000 * 1000))
end

def finish_connection(sock)
next_sock = @next_step[sock]
[sock, next_sock].each do
|s|
s.close
@descriptors.delete(s)
@next_step.delete(s)
end
end

def run
loop do
connections = select( @descriptors )
if connections
connections[0].each do
|sock|
if sock == @serverSocket then
accept_new_connection
else
if sock.eof? then
finish_connection(sock)
else
propagate(sock)
end
end
end
end
end
end
end

trap("SIGINT") do
exit
end

usage if ARGV.length < 2
port = ARGV.shift.to_i
targets = build_targets(ARGV)

Balancer.new(port, *targets).run

Para usarlo, copien todo el codigo a un archivo (como balancer.rb) y fijense en el cartel que explica los parámetros. El primer parámetro es el puerto donde escucha y los otros son pares host:port a los que tiene que conectarse alternativamente.

Bueno, eso es todo por ahora.

Happy hacking,
Aureliano.

2008-04-20

Reconstrucción del ligamento cruzado anterior de la rodilla derecha - Epílogo

Como estuve contando hace un tiempo por acá hace un poco más de un año y medio me operaron para reconstruir el ligamento cruzado anterior de mi rodilla derecha, que fue obliterado en una de mis tantas caídas que tengo cuando esquío (en realidad fue la última). Ahora dejé de esquiar, más que nada por falta de $$$$, y me reincorporé al resto de las actividades usuales de mi vida (incluyendo volver a jugar al "fútbol" los domingos).

Aunque hace muuuucho tiempo que no escribo nada de esto, sigue habiendo gente que se interesa en estos posts de mi blog y, extrañamente, si buscan reconstrucción de ligamentos en google este blog aparece en la primera hoja, me pareció pertinente contarles como fue mi recuperación y como llegué a estar 100% activo deportivamente nuevamente.

Así que les cuento.

Septiembre 2006


Me operaron, pueden ver los detalles en este blog.

Octubre 2006-Marzo 2007


A los 20 días de la operación (aprox) el traumatólogo me dijo "ahora tenés que hacer rehabilitación". La rehabilitación es larga (dura unos 6 meses) y laboriosa (hay que ir 3 veces x semana como 2 horas) pero si hacen las cosas bien lo más probable es que queden bien. Así que me puse las pilas.
Al principio era la típica kinesiología que pensás que es medio al dope donde te ponen esos aparatos que hacen "magnetoterapia", que son como unos cilindros por donde pasas la pierna y están enchufados a un aparato con un timer, que hace ruido cuando termina. También te torturan un poquito haciendo estimulación eléctrica de los cuádriceps de la pierna operada. Media hora y despachás.
A las 2 semanas decidieron que era hora que hiciera pesas y estuve haciendo diversos ejercicios pero solo con la pierna rota, previa magnetoterapia. Todo el chiste me llevaba como 2 horas cada vez, un embole. Y encima en el gimnasio no había una mina que estuviera buena. Solamente un montón de tipos recuperandose de roturas de ligamento cruzado anterior y viejos con dolencias variadas :(.
Mientras pasaba el tiempo me fueron agregando ejercicios y subiendo el peso de los mismos hasta que idearon otra forma de torturarme: estimulación eléctrica del cuádriceps mientras hacía pesas.

¡A la mierda!

Eso si que es feo. Aparte te tiran voltajes grosos. La máquina la usaban entre ¡80 y 120 volts! (posta) y parecía que me iban a electrocutar toda la pierna. Pero cumplió su objetivo.
Al final, me agregaron ejercicios de correr de costado y haciendo secuencias raras de paso y después de un verano agotador, me dieron el alta.

Abril 2007-Presente


Me dieron el alta pero todavía no estaba bien, bien. Me faltaban los últimos detallecitos. Así que todavía me dolía la rodilla de vez en cuando y se me inflamaba cada vez que hacía movimientos raros. Aparte mi sobrepeso tampoco ayudó mucho.
En mayo entré a laburar a mi laburo actual y hubo 2 actividades que me hicieron mierda la rodilla:

  1. Día de actividades "corporativas" de la empresa. Donde nos llevaron al Club de Amigos a que nos lavaran el cerebro haciéndonos creer que la empresa es lo más importante del universo. La actividad fue una mierda, pero había un castillo inflable que estaba buenísimo, pero que fue la perdición de mi rodilla.

  2. Basket. Esa misma semana fui a jugar al basket en cancha de cemento.


El combo fue letal. Estuve rengo por 3 semanas, me asusté, fui al traumatólogo. Me dijo que me haga una radiografía y lo vea de nuevo. Cuando fui a verlo de nuevo con la radiografía, empezó a sacarle fotos porque le parecía que había hecho una obra de arte con mi rodilla y estaba muy orgulloso, me dijo que no haga más boludeces y que estaba bien y se fue a un congreso con las fotos de mi rodilla.
Así que esperé a que se terminara de desinflamar la rodilla y empecé a hacer "deporte de bajo impacto". O sea, volví a nadar. La natación me vino bárbaro y mi rodilla se terminó de mejorar. A mitad de año pusieron clases de yoga en el laburo y empecé a ir (y sigo yendo). Así estoy terminando de recuperar la flexibilidad.
Por último, hará un mes que volví a las canchas de fútbol 5. No se nota ninguna secuela de la lesión, salvo que ahora juego con una rodillera puesta. Mi habilidad está intacta y hasta estoy jugando un poquito mejor que antes. Igual eso no quiere decir mucho, era desastroso y lo sigo siendo.
O sea, si se rompieron la rodilla, no sean boludos, opérense. Es mucho laburo pero pueden volver a las cosas que hacían antes. Una cosita más. Lo que me dijeron los médicos es que si no se operan y tienen los ligamentos rotos, van a tener una artitris grosa en unos años.
Espero que les sea de utilidad a todos los googleros que llegan por acá buscando información sobre la reconstrucción de los ligamentos.

Happy hacking,
Aureliano.

2008-04-02

Si PHP es KK, ¿por qué lo usan tanto?

Hace unos meses dije que PHP es KK en un post de este blog que generó bastante polémica (al menos en los comentarios del post). Y esa discusión me puso a pensar "¿qué cosas buenas tiene PHP?". Claramente, mucha gente lo usa y sirve para hacer un montón de cosas. Bueno, estas son algunas de las cosas que hacen que php y su entorno sean buenos (sin ningún orden en particular):

  • Está en todos lados. Es muy fácil conseguir hosting con php y gente que sepa programar en php. También hace que haya millones de soluciones para pequeños problemas disponibles. Y un montón de gente muy inteligente haciendo soluciones para problemas de php como ésta o ésta.
  • La documentación de php es buenísima. Siempre que busqué cosas ahí las encontré de toque.
  • El entorno es fácilmente instalable. En linux, generalmente viene con la distribución y en Windows es solo cuestión de bajar e instalar un WAMP.
  • El ciclo de desarrollo es muy rápido. Nada de compilar. Nada de reiniciar ningún server. Cambiás un toque un script y ves el resultado inmediatamente.
Estos son todos buenos motivos. Algunos de estos solo valen porque php ya es popular, pero otras no. En particular, el manejo de la documentación de php es algo que es buenísimo.

Happy hacking,
Aureliano.

2008-03-31

Mañana empiezo el doctorado

Mañana a las 9:30 empiezo a cursar mi primera materia del doctorado en ingeniería informática en el ITBA, que es "Teoría de Tipos". Así que voy a estar más ocupado. De todas maneras voy a tratar de mantenerme escribiendo algunas cositas en el blog de vez en cuando.

Deseenme éxitos,
Aureliano.

2008-03-09

Clases abiertas - Ejemplo práctico

Estoy mirando una biblioteca para acceder a bases de datos muy copada que se llama sequel. En la misma no hay que declarar nada y el mapeador solo sabe como hacer para acceder a todo (como debería ser). Y quise ver como hace el sql que va a la base, así que prendí el irb e hice un poquito de "aspect programming alla ruby". Primero importé la biblioteca y el driver para postgres y les agregué datos (todo usando la biblioteca sequel desde adentro del irb y sin escribir una línea de SQL).

Pero después me agarró la duda de cómo se generaba el SQL. ¿Tendría quizás alguna forma de hacer un SQL injection si uso la biblioteca para hacer una página web? Lo que siempre me gustó de bibliotecas como ActiveRecord e Hibernate, es que me quitan la responsabilidad por estas cosas. Así que puse manos a la obra.

Una mirada rápida por la documentación y el código no me llevó a la opción de mostrar todo el sql que se ejecuta, así que tuve que hacer un toque de magia.


irb(main):042:0> class Sequel::Postgres::Dataset
irb(main):043:1> alias_method :select_sql_old, :select_sql
irb(main):044:1> def select_sql(*p)
irb(main):045:2> sql = select_sql_old(*p)
irb(main):046:2> puts(sql)
irb(main):047:2> sql
irb(main):048:2> end
irb(main):049:1> end


¿Qué hice? Hice "aspect programming alla ruby". Cambié cómo se hace para generar el sql de las consultas en postgres para que lo muestre por pantalla (solo en mi sesión de irb).

A partir de este momento (y hasta que lo vuelva atrás o salga del irb) me muestra el SQL de cada SELECT que haga a la base.

Así que ahora puedo mirar fácil que pasa si trato de inyectar SQL:

irb(main):053:0> user_name = "aaaa ' or 1=1"
=> "aaaa ' or 1=1"
irb(main):054:0> users.where(:name => user_name).all
SELECT * FROM users WHERE ("name" = 'aaaa '' or 1=1')
=> []


Soy feliz, y veo que la biblioteca parece estar bien pensada (al menos para esto).

Happy hacking,
Aureliano.

2008-03-08

Nueva versión del bottle invaders

Estoy muy contento de anunciar la nueva versión del "Bottle invaders", en la cual el Señor Destapador (AKA: Mr Opener) defiende su planeta de una invasión de botellas extraterrestres rechochas de vino tinto.

En este release, le bajé los fps a 5 para que se parezca al space invaders original un poco más (y porque me pareció que queda más divertido) y cambié la lógica de colisiones del misil para que sea consistente con esa cantidad de fps.

Si quieren bajarselo está disponible acá.

Happy hacking,
Aureliano.

2008-03-07

Requisitos renovación pasaporte

Hoy fui a renovar mi pasaporte y hacer mi cédula nueva (la anterior la perdí). No pude hacerlo porque me faltó un papel (/$%/#/$&%***+++###). Eso no impidió que me coma como 2 horas de cola al dope, así que les voy a contar cuáles son mis requisitos, que no tienen porqué ser los de ustedes (la burocracia suele ser así).
Cosas para llevar:

  • Pasaporte viejo (si no tenés eso podés declararlo perdido, creo)
    • Fotocopias de la 1a y 2a hoja)
  • DNI
    • Fotocopias 1a, 2a, 3a hojas y donde están tus votaciones.
  • Libreta de casamiento si estás casado (esto es lo que yo no llevé y no me pidieron hasta 2 horas después de iniciar el trámite).
Las fotocopias las podés sacar durante el tiempo de espera, pero los documentos los tenés que llevar sí o sí.

Lo único bueno es que durante la espera pude scriptear con rake para que se armen el .tar.gz y el .zip del Bottle invaders.

Happy hacking,
Aureliano.

2008-03-04

Bottle invaders - primer release

Hola,
acabo de subir a rubyforge el primer release del Bottle Invaders. En el mismo muestro al Señor Destapador luchando contra unas botellas rechonchas de vino que le disparan corchos. Este juego también lo hice usando la biblioteca Rubygame. Si quieren bajarlo pueden ir acá

Y por último, como una imagen vale más que mil palabras, les dejo un screenshot del juego en pleno juego.



Happy hacking,
Aureliano.

2008-03-03

Problemitas con speedy

Empecé a notar que speedy está andando muy "lento", pero que las descargas largas andan bien. Así que dejé un pinga google corriendo un rato y estos son los resultados:


$ ping www.google.com

(un monton de carteles con los round-trips)

--- www.l.google.com ping statistics ---
1927 packets transmitted, 1679 received, 12% packet loss, time 4652040ms
rtt min/avg/max/mdev = 346.690/969.515/1856.932/147.656 ms, pipe 2


Aparentemente está algo muy saturado porque se pierden el 12% de los paquetes y hay round trips de 2 segundos (¡y de promedio es casi un segundo!)

¿Les está pasando lo mismo?
¿Tengo que unirme a speedy apesta?

Happy hacking,
Aureliano.

2008-03-01

Un adelantito

Como ustedes ya saben, empecé a hacer jueguitos usando la biblioteca Rubygame. Como el primer juego ya está (SEUO), empecé el segundo. Es un clon del space invaders, que muestra al Señor Destapador defendiendonos de una forma muy particular de unos invasores que son como botellas de vino.
Como una imagen vale más que mil palabras, acá les muestro un snapshot:

Happy hacking,
Aureliano.

2008-02-25

Compilando LaTeX con rake

En el laburo estoy escribiendo un paper (es parte de las cosas que tengo que hacer como investigador en seguridad informática) y, como soy un geek que se precia, lo estoy escribiendo en LaTeX.

El temita es que las figuras que tiene el paper las hice en MS Visio y que quiero tener un script automático para generar el .dvi a partir del .tex, el .bib y los archivos de Visio. Pero LaTeX no acepta archivos de Visio como imágenes, solo .eps (encapsulated postscript) y Visio a lo sumo escupe .wmf. Así que instalé el wmf2eps y entonces tuve una idea, ¿por qué no generar todo usando rake y uso win32ole para hacer automation con el Visio?.

Este es el resultado (que anda) del rakefile que uso para generar el paper:


require 'rake/clean'
require 'win32ole'

VIS = FileList['*.vsd']
EPS = VIS.ext('eps')
MAIN = "paper.tex"
DVI = "paper.dvi"
BIB = "paper.bib"

CLEAN.include(FileList["*.wmf", "*.aux", "*.bbl", "*.blg", "*.log"] )
CLOBBER.include(FileList[DVI], VIS.ext("eps"))

def windows_name(file)
File.expand_path(file).gsub("/","\\").gsub(" ", "\\ ")
end

def visio()
begin
return WIN32OLE.connect("visio.application"), false
rescue WIN32OLERuntimeError # Visio is not running
return WIN32OLE.new("visio.application"), true
end
end

def open_doc(visio, filename)
begin
return visio.Documents(filename), false
rescue WIN32OLERuntimeError # file not open
return visio.Documents.open(filename), true
end
end

def latex
sh "latex", "-halt-on-error", MAIN do
|ok, res|
unless ok
puts "Error compiling LaTeX file (status=#{res.exitstatus})\n"
exit
end
end
end

def bibtex
sh "bibtex", "paper" do
|ok, res|
unless ok
puts "Error compiling bibtex file (status=#{res.exitstatus})\n"
exit
end
end
end

file DVI => EPS + [MAIN, BIB] do
latex
bibtex
latex
latex
end

rule '.wmf' => '.vsd' do
|t|
visio_filename = windows_name(t.source)
wmf_filename = windows_name(t.name)
visio, close_visio = visio()
document, close_document = open_doc(visio, visio_filename)
document.Pages(1).Export(wmf_filename)
document.Close if close_document
visio.Quit if close_visio
end

rule '.eps' => '.wmf' do
|t|
wmf_filename = windows_name(t.source)
dirname = windows_name( File.dirname(t.source) )
sh "wmf2eps\\wmf2eps -o #{dirname} #{wmf_filename}"
end

desc "Generates dvi for paper"
task :dvi => DVI

desc "Generates images for paper from Visio files"
task :images => EPS

desc "Generates images for paper from Visio files"
task :default => :images


Espero que les sirva esto, y que ruby se vaya acercando a la academia.

Happy hacking,
Aureliano.

2008-02-24

Mr Opener en rubyforge

Estoy muy contento de anunciar que el código de Mr Opener (anunciado en el post anterior) está disponible para todos los que lo quieran en rubyforge.

La página del proyecto es http://rubyforge.org/projects/mropener/.

Espero a futuro:

  • Hacer un gem con este programa
  • Hacer otro programita, que va a ser un clon del space invaders pero usando al Señor Destapador como personaje principal.
  • Seguir con más programas con el Señor Destapador como personaje principal.

Happy hacking,
Aureliano.

Mr Opener

Ultimamente estuve instalando Rubygame en mi PC.

Una vez que lo logré, puse manos a la obra. Así que me copé y en menos de una semana escribí un jueguito para mi sobrina. El mismo es una copia del Potato Guy, que viene con el KDE.

Como protagonista del juego puse al Señor Destapador. Al señor destapador hay que vestirlo, armarle la cara, y darle cosas para que tenga.

Acá les muestro un snapshot del programa andando.



Estoy tramitando un proyecto en rubyforge para hostear el proyecto y lo voy a licenciar con la GPL, ya que Rubygame es LGPL y no quiero tener quilombos de licencia al dope.

Les cuento que el código al final quedó bastante chiquito y fácil de leer, y que lo que más me costó fue editar las imágenes para que encajaran (más o menos) en el juego.

Hasta la próxima y happy hacking.

PD: Me olvidaba, si a alguno de ustedes les interesa tener el código fuente antes de que lo publique en ruyforge, no tienen más que pedirmelo.

2008-02-17

Rubygame en ubuntu, no hay 2 sin 3

Todavía hay más quilombos para hacer correr rubygame en kubuntu gutsy. La demo de sonido no anda y da este error:


$ ./demo_music.rb
ALSA lib pcm_dmix.c:864:(snd_pcm_dmix_open) unable to open slave
./demo_music.rb:19:in `open_audio': Error initializing SDL_mixer:
No available audio device (Rubygame::SDLError)
from ./demo_music.rb:73:in `join'
from ./demo_music.rb:73

Para arreglarlo hay tuve que desinstalar la biblioteca libpulse0, como sugieren en este post.

Happy hacking.

Rubygame en ubuntu, más soluciones

En el post anterior mostré como solucioné un problema que me impedía usar la biblioteca rubygame en mi kubuntu. Bueno, ese no es el único problema. Cuando traté de correr la demo de rubygame me daba otro error:


$ ./demo_rubygame.rb
./demo_rubygame.rb:28: SDL_gfx is not available. Bailing out. (RuntimeError)


Así que puse manos a la obra. El problema es que la versión de la biblioteca SDL_gfx que viene con ubuntu es vieja. En ubuntu gutsy viene la versión 1.2 y rubygame requiere la 2.0. Así que desinstalé la versión que viene con ubuntu, me bajé la última versión de acá y la instalé. La instalación es medio "tricky" porque la típica ./configure;make;make install no anda. Así que tuve que mirar el archivo INSTALL. Para ahorrarles el laburo de mirarlo ustedes, les cuento acá. Para instalarlo tienen que hacer:

$ ./autogen.sh
$ ./configure
$ make
$ sudo make install
$ sudo ldconfig
$ make distclean


Una vez que todo esto funcó, desinstalé y reinstalé el gem de rubygame y me anduvo la demo.

Bueno, espero que no haya más quilombos.

Happy hacking.

Rubygame en ubuntu

Estoy con ganas de probar rubygame porque tengo ganas de programar un poco en ruby (en el laburo me obligan a programar en python y php). Rubygame es la versión para ruby de pygame.

Pero me encontré con un problemita con el que quizás se encuentren ustedes también. Tengo kubuntu 7.10 (gutsy) y la instalación de ruby de ubuntu (al menos en esta versión) hace que el shared object de ruby tenga un nombre distinto del standard. Por eso la librería de rubygame no lo encuentra.

Este es un ejemplo del problema:


$ irb -rubygems -rrubygame
/usr/lib/ruby/gems/1.8/gems/rubygame-2.2.0-x86-linux/ext/rubygame/rubygame_core.so:
libruby18.so.1.8: cannot open shared object file: No such file or directory -
/usr/lib/ruby/gems/1.8/gems/rubygame-2.2.0-x86-linux/ext/rubygame/rubygame_core.so (LoadError)
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
from /usr/lib/ruby/gems/1.8/gems/rubygame-2.2.0-x86-linux/lib/rubygame.rb:25
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:32:in `gem_original_require'
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:32:in `require'
from /usr/lib/ruby/1.8/irb/init.rb:252:in `load_modules'
from /usr/lib/ruby/1.8/irb/init.rb:250:in `each'
from /usr/lib/ruby/1.8/irb/init.rb:250:in `load_modules'
from /usr/lib/ruby/1.8/irb/init.rb:21:in `setup'
from /usr/lib/ruby/1.8/irb.rb:54:in `start'
from /usr/bin/irb:13


Para arreglarlo, hay que hacer un link más en /usr/lib así: $ sudo ln -s libruby1.8.so libruby18.so.1.8.

La pista de como emparchar mi sistema me lo dio este post en la lista de ruby.

Aparentemente, es un bugcito de rubygame, pero no estoy del todo seguro.

Bueno, hasta la próxima y happy hacking.

2008-02-13

Un hecho, dos noticias

Esto lo encontró Natalia, una gran amiga mía.

La noticia, agarraron a un represor de la última dictadura militar, que boleteó a 19 personas. Comparen por ustedes mismos y miren.
Esta es la versión de La Nación y esta la de Página 12.

La verdad, indignante.

2008-02-10

No vayan al Hostal Azul (Humahuaca)

Hola,
Les cuento porqué. La habitación que me dieron tenía un olor a cigarrillo insoportable, impregnado por todos lados. Se notaba que no lavaron bien las sábanas ni la colcha y no me cambiaron el colchón aunque se los pedí explícitamente. Así que en plena temporada alta tuve que salir en una gira desesperada por Humahuaca para conseguir otra habitación en otro hotel. Por suerte conseguí una sin ese olor asqueroso que tenía la del hotel. Otro motivo para no ir es que tardan 45 minutos (aproximadamente) en servir el desayuno (medido con reloj, promediado en los 3 desayunos que estuvimos ahí).

Repito. No vayan al Hostal Azul en Humahuaca, Jujuy, Argentina.

Igual vayan a Humahuaca que está buenísimo, y si pueden haganse una escapada a Iruya.

2008-01-04

Nuevo blog

José (AKA: Termo) presenta su nuevo blog. En su segundo artículo (el primero es un test del blog) habla sobre como calcular el page rank en python. Espero que les guste.

2007-12-30

Dual head en mi notebook

Estoy muy contento de como quedó la configuración de la pantalla de mi Inspiron 1520.

Después de un toque de googleo, encontré un post donde explican cómo configurar una notebook con placa Intel para que soporte 2 monitores (el de la notebook y otro más) en KUbuntu Gutsy. Así que seguí los pasos (que son bastante simples) y los retoqué un bati-toque (santas redundancias batman) para que se configure solo.

Primero agregué a la subsección "Screen"/"Display" del archivo la entrada:

virtual         2560    1024

La onda de esta entrada es que el virtual desktop alcance para los 2 monitores.

Después me agregué en $HOME/bin 2 scripts. Uno para activar el segundo monitor y configurarlo bien y otro para desactivarlo. El que activa y configura el segundo monitor (dual-head) es:
#!/usr/bin/env bash

xrandr --output VGA --auto
xrandr --output VGA --right-of LVDS


El que la apaga es:
#!/usr/bin/env bash

xrandr --output VGA --off


Por último, linkié en ~/.kde/Autostart el script dual-head, así se setea automágicamente la dualidad onda-monitor. Si el monitor no está enchufado, el script no hace nada, así que lo dejé así:
ln -s ../../bin/dual-head


Así que ahora tengo todo listo y cuando estoy en casa puedo usar mi otro monitor también.

2007-12-24

Papá Noel

Ayer a la noche hice de Papá Noel en el complejo de departamentos en el que vivo. Fue un quilombo de niñ@s y adult@s. Impresionante el efecto que genera el disfraz de Papá Noel en la gente. L@s chic@s se me quedaban mirando (se quedarían preguntándose, ¿es de verdad?). En el evento repartí bolsitas con regalitos tontos para l@s chic@s y recibí algunas cartitas. Entre las cartas que recibí está la de mi sobrina.


Y acá está una foto con ella (que nunca supo que Papá Noel era yo) y de fondo el batifondo (santas redundancias batman) del evento.

La verdad, lo disfruté mucho.

2007-12-17

PHP es KK. Un ejemplo práctico

Hoy mi amigo Ernesto Alvarez me mostró un pequeñísimo ejemplo de porqué PHP es una mierda. La semántica del lenguaje deja muuuucho que desear.

El programa:

<?php
echo count == 0;# . "\n";
?>


Muestra "1" en la salida por standard output.

En cambio si dejamos el enter comentado no muestra nada. Este es el código que no muestra nada:

<?php
echo count == 0 . "\n";
?>


Bueno, una cosa más para hacer leña del árbol caído.
Hasta la próxima, amigos.

Update: Escribí una segunda parte de esto en mi artículo Si php es KK, ¿por qué lo usan tanto?.

2007-11-28

51 x 10^9 (parte 2)

En mi mi último post, mostré un problema que usan en ITA para reclutar gente. Parece que anda mal porque el resultado que encuentra no coincide con el fin de un número, como dice en el enunciado. Pero como me quedé desvelado una noche haciendolo, les mandé un mail con mi solución. Mientras tanto aprovecho y les pregunto a ustedes.

¿Le ven algún quilombo al código de abajo?


#!/usr/bin/env ruby

class Result
attr_accessor :letters_advanced
attr_accessor :letters_missing
attr_accessor :sum
attr_accessor :number
attr_accessor :name

def initialize(letters_missing=0)
self.letters_missing = letters_missing
self.sum = 0
self.letters_advanced = 0
self.number = -1 # not set
end

def << (other_result)
self.letters_advanced += other_result.letters_advanced
self.sum += other_result.sum
self.letters_missing -= other_result.letters_advanced
self.number = other_result.number
self.name = other_result.name
end

def to_s
string = "name:#{name} - number:#{number} - " +
"letters_advanced:#{letters_advanced} - " +
"letters_missing:#{letters_missing} - sum:#{sum}"
string += " - letter:#{name[name.length-1+letters_missing,1]}" if letters_missing <= 0
end
end

class Block

attr_reader :value
attr_reader :name
attr_accessor :next_block
attr_reader :position # 0 for ones, 1 for thousands, 2 for millions

def initialize( name, number, position )
@name = name
@value = number * (1000 ** position)
@position = position
end

# Inserts a block somewhere after this block, hopefully close (if not, takes a long time)
def insert_block(new_block)
before = self
after = self.next_block
while( after != nil && new_block.name > after.name )
before = after
after = before.next_block
end
before.next_block = new_block
new_block.next_block = after
end

def advance(min_position, max_position)
current = self.next_block
while !current.nil? && !current.position.between?(min_position, max_position)
current = current.next_block
end
current
end

ONES = [ "", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" ]
TEENS = ["ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen",
"seventeen", "eighteen", "nineteen" ]
ORIG_TENS = [ "twenty", "thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety" ]

tens = Array.new
ONES.each { |n| tens << n }
TEENS.each { |n| tens << n }
ORIG_TENS.each do
|ten|
ONES.each do
|one|
tens << ten + one
end
end

TENS = tens

HUNDREDS = ONES.map do
|hundred|
root = hundred == "" ? "" : hundred + "hundred"
numbers = tens.map do
|ten|
root + ten
end
numbers
end.flatten

number = 0
NAMED_HUNDREDS = HUNDREDS.map do
|name|
value = { :number => number, :name => name }
number += 1
value
end.sort_by { |pair| pair[:name] }.reverse

last = nil
NAMED_HUNDREDS.each do
|pair|
new = Block.new(pair[:name], pair[:number], 0)
new.next_block = last
last = new
end

@last = last.next_block # Ignore zero

#Add higher level blocks (thousand and millions)
current = last
while( current != nil )
if current.position == 0 then
current.insert_block( Block.new(current.name + "thowsand", current.value, 1) )
current.insert_block( Block.new(current.name + "million", current.value, 2) )
end

current = current.next_block
end

def self.first( position=0 )
first = @last
while (first.position != position)
first = first.next
end
first
end
end

class Number

def initialize( pos2, pos1, pos0 )
@blocks = [pos0, pos1, pos2]
end

def name
@blocks.reverse.inject("") do
|acum, block|
acum += block ? block.name : ""
end
end

def value
@blocks.inject(0) do
|acum, block|
acum += block ? block.value : 0
end
end

def description
r = Result.new
r.letters_advanced = self.name.length
r.name = self.name
r.number = r.sum = self.value
r
end

def next_number(min_pos=0)
max_pos = min_pos + 1
while @blocks[max_pos].nil? && max_pos < 3
max_pos += 1
end
max_pos -= 1
new_block = @blocks[min_pos] ? @blocks[min_pos].advance(min_pos, max_pos) : Block.first(min_pos)
@blocks[min_pos] = nil
if (new_block) then
@blocks[new_block.position] = new_block
else #carry
self.next_number(min_pos + 1)
end
self
end

unit_letter_count = 0
unit_accumulated_values = 0
block = Block.first(0)
while( block )
unit_letter_count += block.name.length
unit_accumulated_values += block.value
block = block.advance(0,0)
end
UNIT_LETTER_COUNT = unit_letter_count
UNIT_ACCUMULATED_VALUES = unit_accumulated_values

def step(result)

if (@blocks[1] && !@blocks[0])
big_step_char_count = self.name.length * 1000 + UNIT_LETTER_COUNT
if big_step_char_count < result.letters_missing then
step_result = Result.new
step_result.letters_advanced = big_step_char_count
step_result.number = step_result.sum = self.value * 1000 + UNIT_ACCUMULATED_VALUES
result << step_result
self.next_number(1)
return
end
end
# Big step is not taken
result << self.description
self.next_number
end

def self.first
self.new( nil, nil, Block.first )
end

def self.result( letter_position)
result = Result.new(position)
number = Number.first
while( result.letters_missing > 0 )
result << number.description
number=number.next_number
end
result
end

end


position = ARGV[0].to_i
result = Result.new(position)
number = Number.first
count = 0
while( result.letters_missing > 0 )
number.step(result)

count += 1
if (count % 100000) == 0 then
puts "Partial result"
p result
end
end

puts "Final result"
puts result

2007-11-25

51 x 10^9

Como estuve con un poquito de tiempo libre me puse a programar un toque este sábado a la noche (sí que soy divertido :p). Y encontré un problemita intereseante en el blog de una .com. El problema es el mismo que proponen en ITA para tomarte como programador.

El mismo consiste en saber cuál es la letra número 51 x 10^9 (o sea 51000000000) de la secuencia de agarrar todos los números entre 1 y 1000000000 y transformarlos en letras, saber a que número pertenece y cuanto vale la suma de todos los números por los que pasaste para llegar a esa letra desde el principio. La solución del problema es un bardo, así que no lo hice todo, pero escribí un programa para obtener la letra en la posición x en la secuencia de números que va del 1 al 1000, a qué número pertenece y cuál fue la suma acumulada hasta ahí.

Acá les pongo a continuación el enunciado del problema:

"If the integers from 1 to 999,999,999 are written as words, sorted alphabetically, and concatenated, what is the 51 billionth letter?"

To be precise: if the integers from 1 to 999,999,999 are expressed in words (omitting spaces, 'and', and punctuation[1]), and sorted alphabetically so that the first six integers are

  • eight
  • eighteen
  • eighteenmillion
  • eighteenmillioneight
  • eighteenmillioneighteen
  • eighteenmillioneighteenthousand
and the last is
  • twothousandtwohundredtwo
then reading top to bottom, left to right, the 28th letter completes the spelling of the integer "eighteenmillion".

The 51 billionth letter also completes the spelling of an integer. Which one, and what is the sum of all the integers to that point?

[1] For example, 911,610,034 is written "ninehundredelevenmillionsixhundredtenthousandthirtyfour"; 500,000,000 is written "fivehundredmillion"; 1,709 is written "onethousandsevenhundrednine".

Y este es mi programa en ruby. Una generalización trivial para correrlo con los números que vayan desde 1 a 1000000000 requeriría (según estimo) unos 100GB de espacio libre en el disco rígido, varios días de tiempo de procesamiento para correr e implementar una rutina de external sorting. Pero me parece que los números tienen suficiente estructura como para cortar camino, así que si se me ocurre algo mejor lo postiaré por acá también.



#!/usr/bin/env ruby

class Result
attr_accessor :letters_advanced
attr_accessor :letters_missing
attr_accessor :sum
attr_accessor :number

def initialize(letters_missing=0)
self.letters_missing = letters_missing
self.sum = 0
self.letters_advanced = 0
self.number = -1 # not set
end

def << (other_result)
self.letters_advanced += other_result.letters_advanced
self.sum += other_result.sum
self.letters_missing -= other_result.letters_advanced
self.number = other_result.number
end
end

class Hundred

attr_reader :value
attr_reader :name
attr_reader :next_number

def initialize( name, value, next_number )
@name = name
@value = value
@next_number = next_number
end

def description
r = Result.new
r.letters_advanced = self.name.length
r.sum = self.value
r.number = self.value
r
end

ONES = [ "", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" ]
TEENS = ["ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" ]
ORIG_TENS = [ "twenty", "thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety" ]

tens = Array.new
ONES.each { |n| tens << n }
TEENS.each { |n| tens << n }
ORIG_TENS.each do
|ten|
ONES.each do
|one|
tens << ten + one
end
end

TENS = tens

HUNDREDS = ONES.map do
|hundred|
root = hundred == "" ? "" : hundred + "hundred"
numbers = tens.map do
|ten|
root + ten
end
numbers
end.flatten

number = 0
NAMED_HUNDREDS = HUNDREDS.map do
|name|
value = { :number => number, :name => name }
number += 1
value
end.sort_by { |pair| pair[:name] }.reverse

last = nil
NAMED_HUNDREDS.each do
|pair|
last = Hundred.new(pair[:name], pair[:number], last)
end

@last = last

def self.first
@last
end
end

position = ARGV[0].to_i
result = Result.new(position)
number = Hundred.first
while( result.letters_missing > 0 )
result << number.description
number=number.next_number
end

p result

2007-11-18

Viaje boliviano

Mi cuñada está de viaje (tipo mochilera) por Bolivia desde junio. Éstas son las consecuencias del viaje, tal como lo relata en un mail:

"cerca de alli esta el ojo del inca, que es una fuente de aguas termales segun dicen con grandes propiedades curativas.... y creo que es lo uqe estoy necesitando en este momento porque no paro de tener problemas de salud.... nada grave no te preocupes, pero la verda es que me siento unav ieja tomando antibioticos, poniendome pomadas n diversos lugares del cuerpo, tomando toda clase de hierbas y yendo de medico en medico, a ver si alguna vez alguno la pega..... por suerte la quemadura ya cicatizo, pero tendre una enorme mancha en la pierna por el resto de mis dias.... de cualquier manera estoy probando con aloe vera y mayonesa, a ver si se borra un poco, pero por ahora no veo mucho resultado....

despues esta el tema del quiste, que creo que te habia contado, que me salio hace unos meses en el gluteo y me esta molestando por demas, ahora estoy probando con un nuevo antibiotico y si no funciona tendre que ir a un especialista porque eso si me dijeron que no lo deje pasar....
y de la selva me traje algo que parece ser un excema, cosa que nadie supo explicarme que es, pero todavia ono encuentro cura y no solo me duele sno que tengo la piel que pareciera pudrirse, justo detras de la rodilla....
por si fuera poco tengo parasitos que me hacen descomponerme cada dos por tres.... ahora estoy probando con gotitas de ajenjo y bardana que me regalo un español, el problema es que deberia continuar el tratamiento pero no encuentro esas hierbas por aqui......"

¿Sobrevivirá para traerme el charango que le pedí?

2007-11-08

¿Qué estudio?

Estoy con ganas de estudiar algo el año que viene (y quizás algunos más, o no). Mis opciones son bastante amplias, así que quería preguntarles que les parecen, si se les ocurre alguna otra, si alguna les parece que no vale la pena (por ejemplo, porque lo estudiaron y no les parece que lo vale) o si hay alguna de éstas que sea la posta. Es claro que no puedo estudiar todo esto, así que tengo que elegir.

Estas son las opciones que se me ocurrieron (sin ningún orden particular) y algunos pros y contras:

  • Lenguaje chino (¿mandarín?)
    • Pros: 1/6 del mundo lo habla. Tiene una forma re conceptual de definir las ideas.
    • Cons: mucha gente lo estudia. ¿No me puedo comunicar en inglés? Aparentemente es re-dificil
  • Lenguaje coreano
    • Pros: menos gente lo estudia. La comunidad coreana argentina es bastante grande.
    • Cons: menos gente lo habla.
  • Matemática
    • Pros: es perenne.
    • Cons: descolgado del mundo. Carrera exigente y larga.
  • Física
    • Pros: el mejor ejemplo de modelado que hay.
    • Cons: Carrera exigente y larga. ¿Hay gente más pedante que los físicos?
  • Algo relacionado con el diseño gráfico
    • Pros: Una de mis falencias más evidentes es la falta de criterio estético.
    • Cons: Una de mis falencias más evidentes es la falta de criterio estético.
  • Doctorado en computación
    • Pros: Seguir avanzando en el mismo camino.
    • Cons: Los doctorados son algo demasiado intensivo y no tengo forma de aguarlo (en las carreras de grado cursaría menos materias y listo).
Bueno, eso es todo.
Escucho ideas, sugerencias y críticas.

2007-10-26

El voto geek

El voto geek está complicado en estas elecciones.

Por un lado están los pingüinos K, que en su plataforma tienen Linux con KDE.

Por otro lado está UNA, representante de una gran tradición, el acrónimo recursivo.

Por lo tanto, el voto geek estará fuertemente polarizado. ¿A quién apoyaremos en estas elecciones?

PD: Si ya sé, estoy rompiendo la veda. Si quieren denúncienme al COMFER.
PD2: El voto geek puede o no estar correlacionado con el voto del resto de la gente.

2007-10-24

El botellón del dispenser

Hace un tiempo que estuve pensando en cómo explicar cuál es el mejor momento para hacer un refactoring, y cómo explicar cuál es el momento para hacer el refactoring, y cómo explicar cómo explicar cuál es el momento para hacer el refactoring....
Bueno, ustedes captan la idea.
Y esta es la explicación que elaboré.
Hacer un refactoring es como cambiar el botellón del dispenser de agua de la oficina.
¿Por qué?

  • No vas a ganar más plata por cambiar el bidón.
  • Generalmente cambiar el bidón es un dolor de huevos.
  • Pero peor es no cambiarlo (y quedar sediento).

Bueno, ¿y cuándo conviene cambiar un bidón del dispenser?
Definitivamente no cuando todavía queda agua en el mismo.

Tampoco es una buena idea cambiarlo después de que deje de salir agua del dispenser, porque cuando quieras sacar agua caliente va a estar tibia y cuando quieras sacar agua fría va a estar tibia.

¿Entonces cuándo hay que cambiarlo?
Cuando se vació el bidón, pero queda agua en el tanque que está adentro del dispenser (y las cañerías). Entonces no desperdiciamos agua y el agua sale fría y caliente cuando lo queremos.

Hacer refactorings es algo parecido. Hacer un refactoring antes de tiempo es hacer "boiler-plate", y por lo tanto una pérdida de tiempo. Hacer un refactoring tarde (o no hacerlo) que suframos un diseño feo y que apenas lo hagamos tengamos que arreglar todas las cosas que se hicieron mal porque el diseño anterior no lo permitía de otra manera. En cambio, hacer el refactoring justo antes de necesitar esa flexibilidad en el código por primera vez, es como cambiar el botellón cuando queda agua dentro del dispenser pero no en el botellón.
¿Y cómo nos damos cuenta cuándo estamos por necesitar una flexibilidad en el diseño? La verdad es que soluciones mágicas no conozco (si alguien sabe alguna, por favor que avise). Pero en mi experiencia programar test-first ayuda mucho.

Eso es todo, y espero que hayan disfrutado de las ilustraciones,
Aureliano.

2007-09-17

El cambio recién empieza




¿Siguen las cirujías? ¿Cómo va a terminar? ¿Como Moria Casán? ¿Cómo Zulema Yoma? ¿Como Michael Jackson?

2007-09-10

Un poquitín de Opera

Ayer a la noche fui a ver "Iphigénie en Tauride", de Gluck, interpretada por mi querida Compañía de las Luces. Como siempre (y aún más desde que dejé de cantar ahí) hicieron un espectáculo espectacular (redundantemente). Y están de suerte. Todavía quedan 2 funciones más, así que vayan.

Las dos funciones que quedan son el próximo sábado 15 y domingo 16 de septiembre a las 19:20hs en el Museo Nacional de Arte Decorativo. El mismo está en Libertador y Pereyra Lucena. Las entradas salen solamente $30.

2007-09-05

PHP GRASP

En mi laburo, Core Security, acaban de relesear un proyecto open source (licencia: Apache 2) que sirve para detectar SQL injections en PHP 5.2. El proyecto se llama GRASP y lo que hicieron mis compañeros de laburo es que se puede taintear cada caracter de un string y cuando se hace la llamada a la base de datos chequea las marcas en el string del query y controla si hubo un SQL injection o no.

En la versión actual chequea injections en mysql.

Me parece que se podría hacer algo para Rails. Así que si insisten lo suficiente (¡suscribanse a la lista y pidanlo!) en una de esas me dejan hacerlo. ¡Pidanlo ahora!

El site de GRASP es: http://grasp.coresecurity.com.

2007-08-31

Bug en rubygems 0.9.4

Hoy me crucé con un bug en la versión 0.9.4 de rubygems que hace que no tome de la línea de comando el proxy http, lo que hacía que no ande desde el laburo. Este bug está reportado y, por lo que dice en el bug tracker ya está arreglado en el repositorio de svn.

Pero si no quieren esperar a un nuevo release, se arregla muy fácil.

Busquen el archivo config_file.rb y pongan el siguiente código dentro de la clase ConfigFile (arriba de donde dice private):


def []=(key,value)
@hash[key.to_s] = value
end


Enjoy!
Aureliano.

2007-08-29

2007-08-24

Pequeña alegría

Es bueno cuando el universo confirma algunas cosas, como que ruby es "1337".

Esto apareció hoy cuando miré mi gmail a la mañana:

2007-07-24

Una nación avanzada

Ayer a la noche caí que el partido de Lavagna tiene un acrónimo
recursivo. Siguiendo la tradición informática de "Joe's own editor",
"Sine is not emacs", "LAME Ain't an MP3 Encoder", "Wine Is Not an
Emulator" y "GNU's not UNIX", se viene "Una Nación Avanzada".

¿Lo habrán hecho apropósito?

¿Están buscando el voto nerd?

2007-07-07

Nitro

Como algunos de ustedes saben (y otros no), a mi me gusta Ruby como lenguaje de programación. Y, aparte, se me ocurrió (de nuevo) una idea para hacer una página web y llenarme de $$$$. Por supuesto que no les voy a contar la idea ;), porque no me gusta programar apurado.

Pero rails no me termina de gustar. Es medio fascista, como python. Tiene un montón de cosas que están buenas, como python, pero no me gusta que sea tan estructurado, como python. Tampoco me da hacerla en PHP (eso sí que es feo). Y como es algo que hago porque tengo ganas y con mis propios tiempos decidí probar algo nuevo. Así que me puse a revisar un framework web que viene creciendo hace rato, a la sombra de Rails.

El framework se llama nitro y en gems va por la versión 0.43 (o sea, tiene 43 releases, creo). Como hace 6 meses que no releasean nada, pero están laburando a full decidí agarrar la última versión del repositorio de desarrollo. Así que me instalé darcs e hice un "get" del repositorio. Después de un rato, encontrar un bug, reportarlo y que lo arreglen y con ayuda por IRC de uno de los tipos que lo desarrollan, hice andar el ejemplo que viene con nitro, un blog.

Por lo que estuve leyendo, el framework es más desestructurado que rails y te deja poner las cosas donde quieras y aparte el mapping relacional-objeto que tienen es copado (googleen "ruby og").

Les cuento que las gems de las que depende son: facets, xml-simple, RedCloth y uuidtools.

Bueno, por último les dejo algunos links:
* http://www.arnebrasseur.net/2007/07/01/the-daily-nitro/en/ (explica como "setupear" nitro para hacerlo funcar y de que gems depende, aunque les faltó incluir una dependencia, uuidtools)
* http://rubyforge.org/pipermail/nitro-general/2006-August/005832.html (en el medio del post explica cómo agarrar nitro del repositorio darcs en el que está la última versión "oficial").
* http://www.nitroproject.org/ (home page de nitro, todavía no está terminada).
* http://oxyliquit.de/ (site con tutoriales y preguntas y respuestas sobre nitro y og).
* http://blog.interlinked.org/tutorials/darcs.html (tutorial sobre como usar darcs).

Eso es todo amigos, me voy a hacer lo que todo geek que se precie hace un sábado a la noche, programar.

Aureliano.

2007-06-11

Charla de metaprogramación en Ruby

El viernes pasado di una charla de metaprogramación en Ruby en mi laburo. La misma duró algo así como una hora y media y me faltó mostrar algunas cosas (tenía que tardar una hora) y estuvo interesante (creo). Una hora y media a puro irb, sin slides y casi sin usar el pizarrón.

Acá pongo la planificación de la misma y algunos comentarios:

A continuación está la preparación de la charla:

Historia
Creado por Yukiro Matsumoto (AKA:Matz) en 1994 porque Perl no soportaba Kanji (encoding japonés). Se usó solo en Japón hasta que en el 2000 se publicó el libro: "Programming Ruby", primer libro de Ruby en inglés. Después lo agarró DHH e hizo Rails y se hizo famoso.

Comparaciones
Ruby tiene muchas de las cosas buenas de Perl, como soporte nativo con sintaxis propia para regex.
/\d+(\.\d+)?/ reconoce números (por ejemplo). Y como a Matz le gustaba Smalltalk, todo es un objeto.
También, como en Smalltalk, hay metaclases, las clases son objetos, todas las variables son referencias (no hay tipos primitivos) y se pueden pasar closures a los métodos.

Hands on
(las líneas que empiezan con >> las tipeo en el irb, las que empiezan con => son lo que escribe el irb solo, las que están en corchetes las pongo en un archivo)
En ruby podés hacer cuentas:

>> 1+1
=> 2

asignar a variables, usar strings:

>> var = "hola"
=> "hola"
>> var
=> "hola"

usar expresiones regulares para hacer cosas:

>> "5 mas 95 es 100".gsub( /\d+/, "numerito" )
=> "numerito mas numerito es numerito"

tambien hay una cosa que se llaman símbolos (que son como los símbolos de Lisp) y se escriben así:

>> :mi_simbolo

a muchos objectos (incluidos los simbolos) les puedo pedir que se muestren como strings:

>> :mi_simbolo.to_s
=> "mi_simbolo"

la diferencia entre los símbolos y los strings es que puede haber muchos strings con el
mismo texto pero solo un symbol con ese texto (object_id es un número único del objecto,
o sea que si dos objectos tienen el mismo número son el mismo objeto):

>> :mi_simbolo.object_id
=> 359538
>> :mi_simbolo.object_id
=> 359538

el object id es el mismo.

>> "mi_simbolo".object_id
=> 24163140
>> "mi_simbolo".object_id
=> 24160510

el object id es distinto.

También, como en Perl, hay interpolación de strings

>> "1 mas 1 es #{1+1}"
=> "1 mas 1 es 2"

Y como lo que devuelve es un string posta:

>> "1 mas 1 es #{1+1}".gsub( /\d+/, "numerito" )
=> "numerito mas numerito es numerito"

podés aplicarle todas las operaciones de string.

Arrays:

>> [1,2,3]
=> [1, 2, 3]

Hashes:

>> h = { :a => "aa", :b => "bb" }
=> {:b=>"bb", :a=>"aa"}
>> h[:a]
=> "aa"

Bueno ahora vamos a definir una clase. Edito en el gvim:

[
class A
def initialize( param )
puts "Inicializando A con #{param}"
@inst_var = param
end

def hace_algo( param )
"#{@inst_var} #{param}"
end
end
]

mostrar que no tengo que pasar self como en python
y uso la clase en el irb (después de cargarla):

>> a = A.new( "toto" )
Inicializando A con toto
=> #

esto guarda "toto" en la variable de instancia

>> a.hace_algo( "parametro" )
=> "toto parametro"

y acá lo muestra.

La herencia es herencia simple igual a otros lenguajes, así que no la muestro.

Otra cosa medianamente distintiva de Ruby es el uso de módulos como mixin:
Mostrar todos los métodos de instancia que tienen "algo" en su nombre:

>> A.instance_methods.select { |m| m.include? "algo" }
=> ["hace_algo"]

escribo en un archivo:

[
module M
def otro_algo( param )
"otro " + hace_algo( param )
end
end
]

lo cargo y después:

>> class A; include M; end
=> A

eso pone los métodos definidos en M en la clase A

>> A.instance_methods.select { |m| m.include? "algo" }
=> ["hace_algo", "otro_algo"]

Esto inclusive modifica a los métodos de las instancias vivas:

>> a.methods.select { |m| m.include? "algo" }
=> ["hace_algo", "otro_algo"]

Y los ejecuto para mostrar como llamar desde el módulo a la clase:

>> a.otro_algo( "aaaaaaaaaaa" )
=> "otro inicial aaaaaaaaaaa"

Como acaban de ver cuando hice el include, las clases de ruby son abiertas. Alguien se acuerda de VB? Algo que siempre me gustó es que en vez de escribir "self" o "this" usamos "me".

Abro un archivo y escribo:

[
class Object
def me
self
end
end
]

Lo cargo (load ....) y lo uso:

>> me
=> main
>> me.class
=> Object

También lo puedo usar dentro de otras clases:

>> class A; def klass; me.class; end; end
=> nil
>> a.klass
=> A

Otro feature interesante es el de los bloques de código (AKA: closures)
Ya vimos un ejemplo del uso cuando miramos los nombres de los métodos definidos en A y a.
Mirando un toque más en detalle:

>> [1,2,3,4,5].each { |n| puts "Numero #{n}" }
Numero 1
Numero 2
Numero 3
Numero 4
Numero 5
=> [1, 2, 3, 4, 5]

También puedo hacer mis propios métodos que reciban bloques de código, recordando que las clases son abiertas, modifico Integer para iterar. (Acá nos desviamos un toque de la planificación y mostramos también como usar parámetros del método definido dentro del closure. Dejo la planificación original):

>> class Integer; def veces( &block ); (1..self).each { |n| block[n] }; me; end; end
=> nil
>> 3.veces{ |n| puts n }
1
2
3
=> 3

Los métodos se pueden definir en clases y en objetos, por ejemplo:

>> b = [1,2,3,4,5,6]
=> [1, 2, 3, 4, 5, 6]
>> def b.sarasa(toto); "sarasa #{toto}";end
=> nil
>> b.sarasa
ArgumentError: wrong number of arguments (0 for 1)
from (irb):71:in `sarasa'
from (irb):71
from :0

(ups, me olvidé de pasarle el parámetro).

>> b.sarasa( "aaaaaaaaaaaa" )
=> "sarasa aaaaaaaaaaaa"

En ruby puedo definir properties "tipo Delphi" (o C#). Para esto, hay varios métodos
definidos en la clase Module. Acá muestro uno:

>> class A; attr_accessor :sarasa; end
=> nil
>> a.sarasa = "SARASASASA"
=> "SARASASASA"
>> a.sarasa
=> "SARASASASA"

Ahora bien, attr_accessor es un método del lenguaje (no una palabra reservada) así que voy
a implementarlo de nuevo.

[
class Module
def my_attr_accessor( symbol )
instance_var = "@#{symbol}"
define_method( symbol ) { instance_variable_get instance_var }
define_method( symbol.to_s + "=" ) { |value|
instance_variable_set instance_var, value }
end
end
]


Hago load y lo uso:

>> class A; my_attr_accessor :toto; end
=> #
>> a.toto = 5
=> 5
>> a.toto
=> 5

Salvo por cuestiones de performance my_attr_accessor (y que le falta funcionalidad) es equivalente
al original. Toda la funcionalidad se puede implementar, pero tardaría más y no tengo tiempo ni ganas.

También se pueden hacer métodos de clase que son solo para una clase específica, usando la metaclase.

>> class A; def self.klass_method; "klass_method";end;end
=> nil
>> class A; klass_method; end
=> "klass_method"
>> class B; klass_method; end
NameError: undefined local variable or method `klass_method' for B:Class
from (irb):75
from :0

Notar que lo que hice es definir un método para una sola instancia de la clase Class.

>> class C < A; klass_method; end
=> "klass_method"

También vale para las clases que heredan de la misma. Esto es lo que hacen en Rails para poder hacer:

class Entity < ActiveRecord::Base
has_many :other_entity
end

Como en Smalltalk, se puede hacer override de method_missing para hacer proxies dinámicos y esas cosas (en la charla tuve que saltear esta parte porque venía atrasado de tiempo, así que se perdieron mi DSL de diálogos de pelis porno):

[
class A
def method_missing( method_id, *args, &block )
action = "action_#{method_id}"
(respond_to? action) ? send(action) : super( method_id,*args, &block )
end

def action_a
puts "AAAAAAhhhh"
end

def action_o
puts "OOooohhhh"
end
end
]

Y lo uso:

>> load "bdlv.rb"
=> true
>> a.a
AAAAAAhhhh
=> nil
>> a.o
OOooohhhh
=> nil
>> a.metodo_que_no_existe
NoMethodError: undefined method `metodo_que_no_existe' for #
from ./bdlv.rb:17:in `method_missing'
from (irb):107
from :0


Por último, mostré markaby:

>> require 'markaby'
=> true
>> mab = Markaby::Builder.new
=> #<Markaby::Builder:0x2ef2160 @auto_validation=true, @assigns={}, @output_meta
_tag=true, @builder=<inspect/>, @indent=0, @streams=[["<inspect", "/", ">"]], @h
elpers=nil, @tagset=Markaby::XHTMLTransitional, @output_xml_instruction=true, @o
utput_helpers=true, @elements={}>

>> mab.html do
?> head { title "Core se la banca" }
>> body do
?> h1 "Productos de Core"
>> ul do
?> li "Sarasa $500000000"
>> li "Sarasa2 $100000000"
>> li "Consulting priceless"
>> end
>> end
>> end
=> "<html><head><meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-
Type\"/><title>Core se la banca</title></head><body><h1>Productos de Core</h1><u
l><li>Sarasa $500000000</li><li>Sarasa2 $100000000\n</li><li>Consulti
ng priceless</li></ul></body></html>"

Notar que es código ruby! Por ejemplo, puedo iterar:

>> mab = Markaby::Builder.new
=> #<Markaby::Builder:0x2ecec9c @auto_validation=true, @assigns={}, @output_meta
_tag=true, @builder=<inspect/>, @indent=0, @streams=[["<inspect", "/", ">"]], @h
elpers=nil, @tagset=Markaby::XHTMLTransitional, @output_xml_instruction=true, @o
utput_helpers=true, @elements={}>
>> mab.html {
?> body {
?> ul {
?> [ "Sarasa $500000", "Sarasa2 $100000", "Consulting priceless" ].each do
?> |item| li item
>> end
>> }
>> }
>> }
=> "<html><body><ul><li>Sarasa $500000</li><li>Sarasa2 $100000</li><li>Co
nsulting priceless</li></ul></body></html>"


Por supuesto, este html es básico pero esto es CÓDIGO. Por ejemplo, haciendo un form:

>> mab.html { body { form( :target => "http://sarasa" ) { input( :type => :submit ) } } }
=> "<html><body><form target="http://sarasa"><input type="submit"/></form></body></html>"


También mostré como yapa el ejemplo del tutorial de rake.

Y me faltó el ejemplo de como manipular Excel, pero lo dejo acá:

require 'win32ole'
# -4100 is the value for the Excel constant xl3DColumn.
ChartTypeVal = -4100;

# Creates OLE object to Excel
excel = WIN32OLE.new("excel.application")

# Create and rotate the chart

excel['Visible'] = TRUE;
workbook = excel.Workbooks.Add();
excel.Range("a1")['Value'] = 3;
excel.Range("a2")['Value'] = 2;
excel.Range("a3")['Value'] = 1;
excel.Range("a1:a3").Select();
excelchart = workbook.Charts.Add();
excelchart['Type'] = ChartTypeVal;

30.step(180, 10) do |rot|
excelchart['Rotation'] = rot
end

excelchart2 = workbook.Charts.Add();
excelchart3 = workbook.Charts.Add();

charts = workbook.Charts
charts.each { |i| puts i }

excel.ActiveWorkbook.Close(0);
excel.Quit();

2007-04-17

Mirando el largo plazo

Encontré este comentario en slashdot que me pareció buenísimo, así que quería compartirlo. Lo que leerán a continuación es mi traducción del mismo.


Las características que nuestra sociedad capitalista premia (la excelencia en algunos campos da una gran riqueza y la competencia en ciertas habilidades en el trato interpersonal son más importantes que la competencia en cuestiones técnicas, etc.) son, aparentemente, lo que queremos como sociedad. Si la paga es mejor, la oferta debería incrementar. Sin embargo, las cosas que están correlacionadas con una mayor tasa de nacimientos son: religión (más => más hijos), región del país, etc. Características que están negativamente correlacionadas son educación (una mayor educación, especialmente para las mujeres, reduce la natalidad), ingreso, etc.

Aún más interesante es el nivel de diversidad genética. Los más despreciados por la gente que destaca los valores "familiares" (madre soltera con muchos hijos de diferentes padres) son los que generan más diversidad genética. Caballeros como K-Fed producen múltiple descendencia con múltiples mujeres, asegurando la diversidad genética de su prole.

Esto es interesante porque después de varias generaciones con decreciente religiosidad, creciente educación y gente sana viviendo más, parece que estas mismas fuerzas biológicas están achicando estas características. Hace unos años salió un divertido editorial sugiriendo que Roe vs. Wade destruyó el partido demócrata (N. de A.: de EEUU) no porque los abortos fueran impopulares sino por todo lo contrario. Debido a la alta correlación de la opinión política de la gente con la de sus padres, y la correlación entre ser demócrata con abortar (es 2 o 3 veces más probable tener un aborto si la mujer es demócrata que si es republicana), hacen que 18 años después de Roe vs. Wade haya una reducción en el pool genétcio de los demócratas.

También, las mayores tasas de natalidad de los fundamentalistas de todas las religiones está causando una lenta regresión de las reformas y cambios de la "post-iluminación" en las comunidades religiosas. Las "iglesias protestantes tradicionales" están perdiendo gente, los católicos se mantienen, pero el crecimiento es en Sudamérica y Africa (N. de A.: espero que no tenga razón) mientras que su presencia en Europa se achica y los católicos americanos son porcentualmente cada vez más latinos, en el judaísmo está creciendo su ala ortodoxa (desde el 8% hace 20 años hasta el 15% ahora) que se está tirando a la derecha y las tasas de crecimiento de los musulmanes están ganándoles a todo lo demás.

Basicamente, nuestra cultura secular ateísta encontró tal grado de autoindulgencia y libertad que puede llegar a achicarse a si misma. La izquierda norteamericana constantemente acusa a Bush pero los cambios culturales en EEUU muestran que la demografía, y no la demagogia, es lo que causa este empuej político reaccionario.

Es sumamente interesante, pero yo lo encuentro en más alto grado de ironía (N. de A.: yo también), que las alas "bíblicas" "anti-evolucionarias" de todas las religiones, que eran marginales hace una generación, de golpe están ganando, mientras que las seculares, culturalmente basadas en la ciencia y la razón, están en retirada. Y la razón es que la gente "anti-evolucionaria" está exparciendo su material genético y haciendo crías para hacer avanzar su agenda, y la gente pro-ciencia pro-evolución está acotando su material genético con familias de 0 a 2 hijos.

De hecho, más molesto es que los hombres que se manejan de las forma socialmente más irresponsable -- engaños permanentes, divorcio, etc., generalmente son padres de muchos más niños que aquellos que "juegan respetando las reglas". Así que si el material genético influencia el comportamiento, vamos a encontrar que cada generación es un poquito más adúltera.....

Si está tendencia continúa, que por supuesto no va a pasar, las cosas se vana ir para el otro lado, pero ironicamente en 4 o 5 generaciones, vamos a tener una población general no-blanca, muy religiosa que va a la iglesia/sinagoga/mesquita regularmente mientras tienen relaciones adúlteras durante la semana.

:) Pero seriamente, ¿piensan que la derecha religiosa es mala ahora? En 50 o 60 años, si siguen duplicando en su tasa de natalidad al EEUU secular... van a ser la mayoría.

Divertidas observaciones demográficas amateurs...
Alex



A mi me asusta que no veo ningún motivo para pensar que esto no pueda ser así. Como la gente que me conoce sabe, me preocupa mucho más el crecimiento demográfico de los religiosos que de los adúlteros. ¿Estamos por entrar en otra Edad Media? ¿Nos salvarán los adúlteros? ¿Cómo evitarían este futuro?