Wipeando discos en el tercer mundo
Tuve que wipear un disco rígido y las herramientas de wipeo que tenía a mano no lo reconocían.
La solución:
# yes > /dev/sda
¿Se les ocurre alguna solución más corta? (wipe-golfing)
Happy hacking,
Aureliano.
Blog de Aureliano Calvo. Aureliano Calvo's blog.
Tuve que wipear un disco rígido y las herramientas de wipeo que tenía a mano no lo reconocían.
La solución:
# yes > /dev/sda
Como les conté en el post anterior, estoy en VisWeek 2009. En esta conferencia hay gente de alrededor de todo el mundo, pero el grueso es de Europa y EEUU. Y hay algunas cosas que me llamaron la atención (positivamente) sobre el comportamiento de la gente por acá.
La primera cosa es que las charlas empiezan y terminan en horario (cronométricamente). El primer día de la conferencia me fui a comer y pensé que la sesión de la tarde iba a empezar algo así como media hora más tarde y en vez de eso empezó en horario y me perdí una charla. Comparado con la laxitud de la Eko, que terminó 2 horas y media después del horario planificado, es mucho mejor. Así que aprendí y empecé a llegar en horario. No como en Argentina que las cosas se van atrasando cada vez más y llegamos a situaciones ridículas donde el horario de entrada a un boliche es las 3 AM.
Pero lo que más me asombró es como se arman colas automáticamente cuando se requiere acceso a un recurso escaso. Es en todos lados. Cuando hay que entrar a una sala grande y la puerta es un cuello de botella. Cuando sacamos comida de las mesas de la conferencia. Esperando el ascensor (!). Si prestás atención, se arman colas en todos lados y todo el mundo las respeta.
¿Será realmente más eficiente hacer las cosas más ordenadamente?,
Aureliano.
El viernes pasado partí a Atlantic City para participar de Visweek, enfocándome especialmente en Vizsec. Como el avión llegó a la mañana del sábado a Filadelfia y la conferencia empezó el domingo, dediqué parte del sábado a caminar por Filadelfia. Y me gustó bastante. Es una ciudad linda, con alma. A pesar de que me mojé porque llovía de a ratos y estaba bastante mareado porque el vuelo desde Washington fue en un avión diminuto que se movía mucho lo disfruté un montón.
A las 13:47 me tomé el tren a Atlantic City, y colapsé por falta de sueño. Alrededor de las 16:30hs llegué a Atlantic City y fui al hotel. El hotel de Atlantic City es una mole gigantesca y el casino es increíblemente grande (no me creen, fíjense).
Y la posta es que no me gusta, pero tiene todas las comodidades que hacen falta para la conferencia, que viene super-interesante. Estoy tomando notas de las charlas a las que voy, así que probablemente las ponga en el blog una vez que las muestre en Core (ellos pagan el viaje así que me parece bien que las vean primero).
Acá estoy empezando a conocer gente y hay un grupo de alemanes que son buena onda y me adoptaron. Estoy contento de que estoy aprendiendo un montón y que estoy conociendo gente super-interesante. Espero saber mucho más sobre visualización cuando termine la conferencia. En particular, estoy buscando herramientas para hacer visualizaciones que sean medianamente razonables (para una definición mía y absolutamente arbitraria de razonable). Por ahora no encontré nada :( y los investigadores con los que hablé todos implementaron sus propias herramientas para hacer visualizaciones.
Por último, cuando termine la conferencia me voy 3 días a Nueva York. ¿Qué me sugieren que haga por allá? Lo que tengo pensado es ir al Central Park, subir al Empire State, cruzar caminando el puente de Brooklin, ir a Wall Street, ir a Broadway, ir a la Quinta Avenida (y quizás hacerme el muerto donde mataron a John Lennon), tomarme el ferry a Staten Island para ver la estatua de la Libertad e ir al Museo de Arte Moderno.
Cuando pueda subo fotos de la conferencia, el viaje, etc.
Happy hacking,
Aureliano.
Etiquetas:
atlantic city,
conferencia,
filadelfia,
new york,
viaje,
visualización
Encontré un XSS persistente en una aplicación de ejemplo de Jetty. Si quieren ver el advisory, pueden verlo acá.
Jetty es un container de servlets (como Tomcat) que mantiene la Apache foundation (como Tomcat) pero que intenta ser liviano (no como Tomcat).
Happy hacking,
Aureliano.
Update: Mi cuenta de facebook está deshabilitada. Mandé un mail para preguntar porqué la deshabilitaron, porque hasta donde yo sé no violé ninguna de las reglas del servicio. Los voy a mantener al tanto de la respuesta.
Hola a tod@s,
quería contarles que tengo un nuevo proyecto. El mismo se llama "Destruyendo facebook desde adentro" y consiste en ver cómo hacer para que en vez de que haya una sola empresa que maneje la red social de amigos y conocidos cada persona pueda tener sus datos donde quiera e igualmente pueda relacionarse con otra gente. Un poco como el mail, se puede mandar correos a personas que tienen un proveedor de correo distinto del tuyo.
Para eso, hay algunas actividades que pueden hacer:
Etiquetas:
facebook
Leanlo, ¡no tiene desperdicio!
from Phanida_Roidoung
reply-to allaccountuser@live.com
to
date Thu, Aug 6, 2009 at 2:20 PM
subject Dear dc.uba.ar Owner
hide details 2:20 PM (34 minutes ago)
Reply
Follow up message
Dear dc.uba.ar Owner,
We are currently carrying-out a mentaiance
process to your dc.uba.ar account, to
complete this process you must reply to
this email immediately,and enter your User
Name here (******) And Password here(*****)
if you are the rightful owner of this account.
This process we help us to fight against
SPAM MAILS.
Failure to summit your password, will render
your email address n-active from our database.
You can also confirm your email address by
logging into your dc.uba.ar
account at:http://dc.uba.ar
NOTE: You will be send a password reset
messenge in next two (2)
working days after undergoing this process
for security reasons.
Thank you form dc.uba.ar!
THE dc.uba.ar TEAM
to
complete this process you must reply to
this email immediately,and enter your User
Name here (******) And Password here(*****)
if you are the rightful owner of this account.
Cosas buenas:
Etiquetas:
python
En rapidito los tests siguen creciendo. Ahora le agregué un test al modelo de la base de datos (que por ahora tiene una sola tabla, pero espero que crezca bastante) y otro a los views y controllers (que también tienen que crecer).
Todo está agregado en el rakefile:
$ rake -T
(in /home/aure/devel/rapidito)
rake db:initial # Generate the initial pages for the wiki
rake db:migrate # Migrate the database
rake default # Run all tests
rake test # All tests
rake test:functional # Test controllers and views
rake test:lib # Library tests
rake test:model # Model tests
~/devel/rapidito$ find test -name "*.rb" | xargs wc -l
26 test/lib/test_lang_hacks.rb
60 test/lib/elem_stack_test.rb
35 test/lib/rapidito_data_test.rb
187 test/lib/rapidito_html_test.rb
81 test/lib/state_test.rb
48 test/lib/nodes_test.rb
110 test/lib/tokenizer_test.rb
55 test/model/pages_test.rb
49 test/functional/start_test.rb
651 total
Hola,
puse un repo de rapidito (mi proyecto de base de datos basado en wikis) en github. Está disponible en http://github.com/aurelianito/rapidito/tree/master.
Si quieren usarlo, solo hay que hacer en el directorio clonado:
rake db:migrate
rake db:initial
rackup
Problema: Quiero ejecutar un comando desde otro directorio en rake (por ejemplo, ejecutar un makefile que está en un subdirectorio).
Solución: Agrego al rakefile la posibilidad de ejecutar comandos en otro directorio. Para eso, al principio de mi rakefile puse:
require 'fileutils'
def sh_in_dir( dirname, *args, &block )
old_path = pwd
FileUtils.chdir( dirname )
sh( *args, &block )
FileUtils.chdir( old_path )
end
Problema: Tengo un firefox corriendo en X11 y quiero correr otro firefox remotamente por X11 en otro host (usando el mismo display). Si lo ejecuto directamente, haciendo ssh -X -n host firefox en vez de ejecutar el Firefox en la otra máquina, abre otra ventana del firefox que ya tengo corriendo.
Solución: Usando la opción -no-remote evito que el firefox busque ventanas de firefox abiertas para abrir una ventana nueva y se ejecuta remotamente. La línea de comando entera es: ssh -X -n host firefox -no-remote.
Gracias Pedro Varangot por contarme del parámetro -no-remote
Happy hacking,
Aureliano.

Esta versión del logo de rapidito se viene con texto y fondo. Y me parece que se "mueve" un poco más. De a poco voy aprendiendo a usar el inkscape.

Estoy pensando en un logo para la base de datos/wiki que estoy haciendo. ¿Qué les parece este? La idea es ponerlo arriba a la izquierda en todas las páginas y como ícono en la barra de navegación del browser.
Siguiendo mi proyecto de hacer mi wiki en Ruby, encontré un comportamiento muy raro.
Generé un hash (que se llama @rules) que no tiene un elemento que tiene. O sea, @rules[@rules.keys[2]] da nil, pero @rules.values[2] devuelve el objeto asociado a la clave @rules.keys[2]. Como este hash tiene como claves un montón de expresiones regulares, me imaginé que había un problema con el hash y el eql? de Regexp, así que los implementé de nuevo y monkeypatchié.
Este es el código:
class Regexp
alias_method :old_rapidito_inspect, :inspect
def inspect
@inspect = old_rapidito_inspect if @inspect.nil?
@inspect
end
def eql?( other )
false if other.class != Regexp
self.inspect == other.inspect
end
alias_method :"==", :eql?
def hash
self.inspect.hash
end
end
$ ruby --version
ruby 1.8.7 (2008-08-11 patchlevel 72) [i486-linux]
Etiquetas:
monkeypatch,
regexp,
ruby
En ruby no se calculan los cambios minúscula mayúscula para las letras acentuadas. Por ejemplo:
irb(main):002:0> puts "áa".upcase
áA
=> nil
class String
alias_method :old_rapidito_upcase, :upcase
def upcase
self.gsub( /\303[\240-\277]/ ) do
|match|
match[0].chr + (match[1] - 040).chr
end.old_rapidito_upcase
end
alias_method :old_rapidito_downcase, :downcase
def downcase
self.gsub( /\303[\200-\237]/ ) do
|match|
match[0].chr + (match[1] + 040).chr
end.old_rapidito_downcase
end
end
upcase y downcase para que manejen los acentos, sumando o restando 32 (40 en octal) al segundo byte. Pongo este código en lang_hacks.rb y sale todo con fritas. Mirando el irbirb(main):001:0> require 'lang_hacks'
=> true
irb(main):002:0> "aÁ".upcase
=> "A\303\201"
irb(main):003:0> puts "aÁ".upcase
AÁ
=> nil
Etiquetas:
monkeypatch,
regexp,
ruby
Entra enfermo de dengue
Mosquito pica enfermo
Sapo se come mosquito
¡No hay dengue!
Alfredo planta soja
Alfredo fumiga veneno
Veneno mata sapito
Sapo muerto no come mosquito
¡Hay soja!
¡Hay veneno!
¡No hay sapo!
¡Hay dengue!
¡Hay mosquito!
Hormiga atómica fumiga mosquito
Sapo vivo se caga de hambre
Año que viene
¡Se muere!
¡No hay sapo!
¡Hay dengue!
¡Hay malaria!
¡Hay peste!
¡Hay mosquito!
Nonsanto reparte los sobres
Al periodismo in-the-pendiente
¡Hay sobre!
¡No hay noticia!
¡Hay soja!
¡Hay veneno!
¡No hay sapo!
¡Hay dengue!
¡Hay mosquito!
Nonsanto no garpa bloguero
Bloguero publica noticia
¡Nonsanto se cae de culo!
¡Cristina prohibe la soja!
¡No hay sobre!
¡Hay sapo!
¡No hay dengue!
¡No hay mosquito!
Vía: artepolitica.com
Comprobado en Vmware Server 2.0.1.
Host: KUbuntu 9.0.4. 64 bits.
Guest: KUbuntu 9.0.4. 32 bits.
¿Estoy haciendo algo mal?
Aureliano.
Etiquetas:
vmware
Como les estuve contando, sigo escribiendo mi wiki. Ya parsea un subconjunto interesante del lenguaje definido por trac.
Siguiendo la tradición, les cuento como está avanzando el tokenizer. Al tokenizer lo simplifiqué para que devuelva la expresión regular que matcheo junto con el match (en vez del "tipo"). Esto hizo que la interfase para definir las reglas para tokenizar sea más simple. Si no hay ninguna regla que matchee sigue devolviendo ["string", :text].
Sin más, acá el código:
module Rapidito
class Tokenizer
def initialize( *delimiters )
@delimiter_list = delimiters + [/\z/]
@match_cache = nil
end
def source
valid_cache? ? @match_cache[0].to_s + @source : @source
end
def source=(s)
@match_cache = nil
@source = s
end
def has_next?
!@source.empty? || valid_cache?
end
def valid_cache?
(!@match_cache.nil?) && (@match_cache[0].to_s.length > 0)
end
def next_match
@delimiter_list.map {|regex| [regex.match(@source),regex]}.reject {|p| p[0].nil?}.inject do
|better,new|
better_pos = better[0].pre_match.length
new_pos = new[0].pre_match.length
if better_pos < new_pos
better
elsif new_pos < better_pos
new
elsif better[0].to_s.length > new[0].to_s.length
better
else
new
end
end
end
def next_token
if @match_cache #cached delimiter
rv = @match_cache
@match_cache = nil
return rv
end
match = next_match
p = match[0].pre_match.length
@source = @source[p + match[0].to_s.length, @source.length]
if p == 0 #delimiter
match
else #text
@match_cache = match
[match[0].pre_match, :text]
end
end
def all_tokens
tokens = []
while has_next?
tokens << next_token
end
tokens
end
end
end
require 'test/unit'
require 'rapidito/tokenizer'
include Rapidito
class TokenizerTest < Test::Unit::TestCase
def test_no_token
tok = Tokenizer.new
tok.source = "aaaa"
assert_equal true, tok.has_next?
assert_equal ["aaaa", :text], tok.next_token
assert_equal false, tok.has_next?
end
def assert_all_tokens( expected, tokenizer )
assert_equal expected,
tokenizer.all_tokens.map { |token, kind| [token.to_s, kind] }
end
def test_two_delimiters
tok = Tokenizer.new(
/\|/, /;;/
)
tok.source = "aa|bbb;;;;cccc"
assert_all_tokens \
[ ["aa", :text], ["|", /\|/], ["bbb", :text],
[";;", /;;/], [";;", /;;/], ["cccc", :text] ],
tok
tok.source = "aa;;bbb||cccc"
assert_all_tokens \
[ ["aa", :text], [";;", /;;/], ["bbb", :text],
["|", /\|/], ["|", /\|/], ["cccc", :text] ],
tok
end
def test_choose_longest_match
tok = Tokenizer.new(
/aa/, /aaa/
)
tok.source = "aaaa"
assert_all_tokens [ ["aaa", /aaa/], ["a", :text ] ], tok
end
def test_reset_precache
tok = Tokenizer.new(
/\|/, /,/
)
tok.source = "original start|original end"
tok.next_token
tok.source = "new start,new end"
assert_equal ["new start", :text], tok.next_token
end
def test_almost_finished
tok = Tokenizer.new( /!/ )
tok.source = "bang!"
tok.next_token
assert_equal true, tok.has_next?
tok.next_token
assert_equal false, tok.has_next?
end
def test_carriage_return_ending
tok = Tokenizer.new( /!/ )
tok.source = "bang!\n"
tok.next_token
assert_equal true, tok.has_next?
tok.next_token
assert_equal true, tok.has_next?
assert_equal "\n", tok.next_token[0].to_s
assert_equal false, tok.has_next?
end
def test_transparent_caching
tok = Tokenizer.new( /!/ )
tok.source = "bang!pum"
tok.next_token
assert_equal "!pum", tok.source
end
def test_match_klass
tok = Tokenizer.new( /!/ )
tok.source = "!bang!pum"
assert_equal \
[MatchData, String, MatchData, String],
tok.all_tokens.map { |tok, kind| tok.class }
end
end
Hice una nueva versión del bouncer que permite bouncear a varios targets desde un mismo host:
#!/usr/bin/env ruby
# == Synopsis
#
# bouncer.rb: Redirects TCP connections to distant machines. Handles simultaneously many connections.
#
# == Usage
#
# ruby redirect.rb [OPTION]
#
# -h, --help:
# Show help
#
# --ip ip, -i ip:
# Accept connections from ip (default 127.0.0.1)
#
# --port port, -p port:
# Listen on port (default 12345)
#
# --target ip:port, -t ip:port
# Connect to ip:port (default 127.0.0.1:23456)
#
require 'getoptlong'
require 'rdoc/usage'
require 'socket'
class Bouncer
def self.for_host( source_ip, start_port, targets )
rules = {}
targets.each_with_index do
|target, index|
rules[ start_port + index ] = target
end
self.new( source_ip, rules )
end
def initialize( source_ip, port_target_map )
@port_target_map = port_target_map
@source_ip = source_ip
@servers = @port_target_map.keys.map do
|port|
ss = TCPServer.new( "", port )
ss.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
ss
end
@descriptors = @servers.clone
@next_step = {}
end
def handle_new_connection( ss )
incoming = ss.accept
if incoming.peeraddr[3] == @source_ip
begin
outgoing = TCPSocket.new( *@port_target_map[incoming.addr[1]] )
@next_step[ incoming ] = outgoing
@next_step[ outgoing ] = incoming
@descriptors += [ incoming, outgoing ]
rescue
silent_close( incoming )
end
else
silent_close( incoming )
end
end
def silent_close( sock )
begin
sock.close
rescue
#do nothing intentionally
end
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|
silent_close(s)
@descriptors.delete(s)
@next_step.delete(s)
end
end
def run
loop do
connections = select( @descriptors )
connections[0].each do
|sock|
if @servers.include? sock then
handle_new_connection( sock )
else
begin
sock.eof? ? finish_connection(sock) : propagate(sock)
rescue
finish_connection(sock)
end
end
end
end
end
end
if $0 == __FILE__ then
opts = GetoptLong.new(
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
[ '--ip', '-i', GetoptLong::REQUIRED_ARGUMENT ],
[ '--port', '-p', GetoptLong::REQUIRED_ARGUMENT ],
[ '--target', '-t', GetoptLong::REQUIRED_ARGUMENT ]
)
ip = '127.0.0.1'
port = '12345'
target = '127.0.0.1:23456'
opts.each do
|opt, arg|
case opt
when '--help'
RDoc::usage
exit
when '--ip'
ip = arg
when '--port'
port = arg
when '--target'
target = arg
end
end
port = port.to_i
target = target.split(":")
trap("SIGINT") do
exit
end
Bouncer.new(ip, port => [target[0], target[1].to_i]).run
end
Encontré como hacer para que ande bien la ruedita del mouse adentro de un Guest de VMWare con Kubuntu 9.04. Hay que instalar el paquete xserver-xorg-input-vmmouse.
Happy hacking,
Aureliano.
Etiquetas:
vmware
El comando xrandr sirve, entre otras cosas para cambiar la resolución de X desde un shell. Primero listo las resoluciones disponibles:
$ xrandr
Screen 0: minimum 320 x 200, current 1280 x 1024, maximum 1280 x 1280
VGA connected 1280x1024+0+0 (normal left inverted right x axis y axis) 338mm x 270mm
1280x1024 60.0*+ 75.0 60.0*
1152x864 75.0
1024x768 75.0 60.0
800x600 75.0 60.3
640x480 75.0 59.9
720x400 70.1
-s. Por ejemplo:
$ xrandr -s 800x600
Como les conté acá y acá, estoy escribiendo un tokenizador para un wiki que estoy programando. Y hoy me encontré con una cosa muy extraña de las expresiones regulares.
En ruby la función match sirve para buscar el primer match de una regex dentro de un string. Por ejemplo (usando el irb):
irb(main):001:0> m = /a/.match "babab"
=> #<MatchData "a">
irb(main):002:0> m.pre_match
=> "b"
irb(main):003:0> m[0]
=> "a"
pre_match es lo que está antes del match en el string. También según había entendido (mal) /\Z/ matchea con el final del string. Por ejemplo:
irb(main):004:0> m = /\Z/.match "hola"
=> #<MatchData "">
irb(main):005:0> m.pre_match
=> "hola"
/\Z/ tiene un comportamiento muy extraño, aunque documentado, cuando el último caracter antes del final es un \n. Lo que pasa es que el pre_match queda ¡sin el \n del final!. Lo muestro en el irb:
irb(main):006:0> m = /\Z/.match "\n"
=> #<MatchData "">
irb(main):007:0> m.pre_match
=> ""
\n, hay que usar /\z/ (¡en minúscula!):
irb(main):008:0> m = /\z/.match "\n"
=> #<MatchData "">
irb(main):009:0> m.pre_match
=> "\n"
def initialize( delimiters )
@delimiter_list = [[/\z/, :finish]] +
delimiters.to_a.map { |k,arr| arr.map { |re| [re, k] } }.inject([]) { |ac,ps| ac + ps }
@match_cache = nil
end
def test_carriage_return_ending
tok = Tokenizer.new( :a_kind => [/!/] )
tok.source = "bang!\n"
tok.next_token
assert_equal true, tok.has_next?
tok.next_token
assert_equal true, tok.has_next?
assert_equal "\n", tok.next_token[0].to_s
assert_equal false, tok.has_next?
end
Como les conté hace un par de posts, estoy haciendo un wiki y blogueando sobre el tokenizer
Hoy les cuento de la segunda versión del tokenizer. A esta versión (que no es compatible con la anterior) le agregué la posibilidad de asociar un tipo (kind) a cada regexp que define un token. Por lo tanto, el método next_match ahora devuelve un par [match, kind]. Los matches de texto común (no delimitadores) tienen kind :text. Los matches de los delimitadores vienen con la MatchData entera, para poder usarla si interesan partes del match a la hora de procesar el token. También dejé de usar =~ para matchear regexps, ya que el
Me quedaron un par de cosas con dudas después de implementar esto.
/.{34,}(regex de verdad)/ no vale.module Rapidito
class Tokenizer
attr_reader :source
def initialize( delimiters )
@delimiter_list = [[/\Z/, :finish]] +
delimiters.to_a.map { |k,arr| arr.map { |re| [re, k] } }.inject([]) { |ac,ps| ac + ps }
@match_cache = nil
end
def source=(s)
@match_cache = nil
@source = s
end
def has_next?
!@source.empty? || valid_cache?
end
def valid_cache?
(!@match_cache.nil?) && (@match_cache[0].to_s.length > 0)
end
def next_match
@delimiter_list.map {|p| [p[0].match(@source), p[1]]}.reject {|p| p[0].nil?}.inject do
|better,new|
better_pos = better[0].pre_match.length
new_pos = new[0].pre_match.length
if better_pos < new_pos
better
elsif new_pos < better_pos
new
elsif better[0].to_s.length > new[0].to_s.length
better
else
new
end
end
end
def next_token
if @match_cache #cached delimiter
rv = @match_cache
@match_cache = nil
return rv
end
match = next_match
p = match[0].pre_match.length
@source = @source[p + match[0].to_s.length, @source.length]
if p == 0 #delimiter
[match[0].to_s, match[1]]
else #text
@match_cache = match
[match[0].pre_match, :text]
end
end
def all_tokens
tokens = []
while has_next?
tokens << next_token
end
tokens
end
end
end
require 'test/unit'
require 'rapidito/tokenizer'
include Rapidito
class TokenizerTest < Test::Unit::TestCase
def test_no_token
tok = Tokenizer.new( {} )
tok.source = "aaaa"
assert_equal true, tok.has_next?
assert_equal ["aaaa", :text], tok.next_token
assert_equal false, tok.has_next?
end
def assert_all_tokens( expected, tokenizer )
assert_equal expected,
tokenizer.all_tokens.map { |token, kind| [token.to_s, kind] }
end
def test_two_delimiters
tok = Tokenizer.new(
:a_kind => [/\|/, /;;/]
)
tok.source = "aa|bbb;;;;cccc"
assert_all_tokens \
[ ["aa", :text], ["|", :a_kind], ["bbb", :text],
[";;", :a_kind], [";;", :a_kind], ["cccc", :text] ],
tok
tok.source = "aa;;bbb||cccc"
assert_all_tokens \
[ ["aa", :text], [";;", :a_kind], ["bbb", :text],
["|", :a_kind], ["|", :a_kind], ["cccc", :text] ],
tok
end
def test_choose_longest_match
tok = Tokenizer.new(
:a_kind => [/aa/, /aaa/]
)
tok.source = "aaaa"
assert_equal [ ["aaa", :a_kind], ["a", :text ] ], tok.all_tokens
end
def test_reset_precache
tok = Tokenizer.new(
:a_kind => [/\|/, /,/]
)
tok.source = "original start|original end"
tok.next_token
tok.source = "new start,new end"
assert_equal ["new start", :text], tok.next_token
end
def test_almost_finished
tok = Tokenizer.new( :a_kind => [/!/] )
tok.source = "bang!"
tok.next_token
assert_equal true, tok.has_next?
tok.next_token
assert_equal false, tok.has_next?
end
end
Tomando como base lo que dice en este post, hice un script en ruby que genera html con clases que se pueden poner coloritos con css:
#!/usr/bin/env ruby
require 'rubygems'
require 'syntax/convertors/html'
class Syntax::Convertors::HTML
def convert( text, klass="" )
html = "<pre class=\"#{klass}\">"
regions = []
@tokenizer.tokenize( text ) do |tok|
value = html_escape(tok)
case tok.instruction
when :region_close then
regions.pop
html << "</span>"
when :region_open then
regions.push tok.group
html << "<span class=\"#{tok.group}\">#{value}"
else
if tok.group == ( regions.last || :normal )
html << value
else
html << "<span class=\"#{tok.group}\">#{value}</span>"
end
end
end
html << "</span>" while regions.pop
html << "</pre>"
html
end
end
convertor = Syntax::Convertors::HTML.for_syntax "ruby"
puts convertor.convert( $stdin.read, "ruby" )
<style>
pre.ruby {
background-color: #ffffcc;
color: #000000;
padding: 10px;
font-size: 1.1em;
overflow: auto;
margin: 4px 0px;
width: 95%;
border: thin dashed;
}
.ruby .normal {}
.ruby .comment { color: #005; font-style: italic; }
.ruby .keyword { color: #A00; font-weight: bold; }
.ruby .method { color: #077; }
.ruby .class { color: #074; }
.ruby .module { color: #050; }
.ruby .punct { color: #447; font-weight: bold; }
.ruby .symbol { color: #099; }
.ruby .string { color: #944; }
.ruby .char { color: #F07; }
.ruby .ident { color: #004; }
.ruby .constant { color: #07F; }
.ruby .regex { color: #B66; }
.ruby .number { color: #D55; }
.ruby .attribute { color: #377; }
.ruby .global { color: #3B7; }
.ruby .expr { color: #227; }
</style>
Etiquetas:
ruby
Después de mirar un rato el estado de las bibliotecas para hacer wikis en Ruby, y descubrir que ninguna me servía, Decidí que tenía que poner cartas en el asunto y hacer la mía. Mi idea es implementar el markup de Trac, haciéndolo extensible, y agregarle un par de cositas que por ahora son un secreto :-p.
Lo importante del asunto es que puse manos en el asunto. Al final, después de un intento fallido de hackear mi camino al andar, decidí que lo mejor es armar un tokenizer y un parser que use esos tokens para generar el árbol del que extraeré el HTML.
Así que me puse a programar. Como no encontré ningún tokenizer en ruby, programé uno. El tokenizer se contruye con un montón de expresiones regulares que definen cada delimitador. Después se le setea una fuente de caracteres (un string) y separa el string en los delimitadores de arriba (que se devuelven como símbolos) y cadenas que no matchean con ninguno de los delimitadores (que devuelve como strings).
Bueno, basta de cháchara, acá tá el código:
module Rapidito
class Tokenizer
def initialize( *delimiters )
@regexp = Regexp.union( *delimiters + [/$/] )
end
attr_accessor :source
def has_next?
! @source.empty?
end
def next_token
p = (@source =~ @regexp)
if p == 0 #delimiter
token = nil
@source.sub!( @regexp ) { |match| token=match.to_sym; "" }
token
else #text
token = @source[0,p]
@source = @source[p,@source.length]
token
end
end
def all_tokens
tokens = []
while has_next?
tokens << next_token
end
tokens
end
end
end
require 'test/unit'
require 'rapidito/tokenizer'
include Rapidito
class TokenizerTest < Test::Unit::TestCase
def test_no_token
tok = Tokenizer.new
tok.source = "aaaa"
assert_equal true, tok.has_next?
assert_equal "aaaa", tok.next_token
assert_equal false, tok.has_next?
end
def test_two_delimiters
tok = Tokenizer.new( /\|/, /;;/ )
tok.source = "aa|bbb;;;;cccc"
assert_equal [ "aa", :"|", "bbb", :";;", :";;", "cccc" ], tok.all_tokens
tok.source = "aa;;bbb||cccc"
assert_equal [ "aa", :";;", "bbb", :"|", :"|", "cccc" ], tok.all_tokens
end
def test_choose_first_match
tok = Tokenizer.new( /aa/, /aaa/ )
tok.source = "aaa"
assert_equal [ :aa, "a" ], tok.all_tokens
end
end
En el ruby quiz pusieron como problema de la semana hacer un quine en ruby. Un quine es un programa que genera como salida el código fuente del mismo (ver wikipedia).
Así que puse manos a la obra. Agarre el último quine en C de la página de wikipedia:
main() { char *s="main() { char *s=%c%s%c; printf(s,34,s,34); }"; printf(s,34,s,34); }
s="s=%c%s%c;printf(s,34,s,34)";printf(s,34,s,34)
s="s=%c%s%c;printf(s,34,s,34,13,10)%c%c";printf(s,34,s,34,13,10)
Etiquetas:
ruby
Yo no soy un fan de Perl, pero a veces aparecen cosas increíbles. Por ejemplo, este es un programa válido en perl:
''=~( '(?{' .('`' |'%') .('[' ^'-')
.('`' |'!') .('`' |',') .'"'. '\\$'
.'==' .('[' ^'+') .('`' |'/') .('['
^'+') .'||' .(';' &'=') .(';' &'=')
.';-' .'-'. '\\$' .'=;' .('[' ^'(')
.('[' ^'.') .('`' |'"') .('!' ^'+')
.'_\\{' .'(\\$' .';=('. '\\$=|' ."\|".( '`'^'.'
).(('`')| '/').').' .'\\"'.+( '{'^'['). ('`'|'"') .('`'|'/'
).('['^'/') .('['^'/'). ('`'|',').( '`'|('%')). '\\".\\"'.( '['^('(')).
'\\"'.('['^ '#').'!!--' .'\\$=.\\"' .('{'^'['). ('`'|'/').( '`'|"\&").(
'{'^"\[").( '`'|"\"").( '`'|"\%").( '`'|"\%").( '['^(')')). '\\").\\"'.
('{'^'[').( '`'|"\/").( '`'|"\.").( '{'^"\[").( '['^"\/").( '`'|"\(").(
'`'|"\%").( '{'^"\[").( '['^"\,").( '`'|"\!").( '`'|"\,").( '`'|(',')).
'\\"\\}'.+( '['^"\+").( '['^"\)").( '`'|"\)").( '`'|"\.").( '['^('/')).
'+_,\\",'.( '{'^('[')). ('\\$;!').( '!'^"\+").( '{'^"\/").( '`'|"\!").(
'`'|"\+").( '`'|"\%").( '{'^"\[").( '`'|"\/").( '`'|"\.").( '`'|"\%").(
'{'^"\[").( '`'|"\$").( '`'|"\/").( '['^"\,").( '`'|('.')). ','.(('{')^
'[').("\["^ '+').("\`"| '!').("\["^ '(').("\["^ '(').("\{"^ '[').("\`"|
')').("\["^ '/').("\{"^ '[').("\`"| '!').("\["^ ')').("\`"| '/').("\["^
'.').("\`"| '.').("\`"| '$')."\,".( '!'^('+')). '\\",_,\\"' .'!'.("\!"^
'+').("\!"^ '+').'\\"'. ('['^',').( '`'|"\(").( '`'|"\)").( '`'|"\,").(
'`'|('%')). '++\\$="})' );$:=('.')^ '~';$~='@'| '(';$^=')'^ '[';$/='`';
Etiquetas:
perl
En el consorcio donde vivo estamos cambiando de administración. Para ello se recibieron propuestas escritas de un montón de administradoras. Después de ser analizadas, elegimos 4 para entrevistar. De esas 4, si sale todo bien, deberíamos elegir a la brevedad cuál de ellas será nuestra próxima administración.
Por lo tanto, quería pedirles que me cuenten si tienen referencias (tanto buenas como malas) de estas administraciones. Las mismas son:
Etiquetas:
consorcio