El mundo según San Francisco
Fuente: acá
Blog de Aureliano Calvo. Aureliano Calvo's blog.
Update: Parece que se viene esto como parte de paver 1.1
Hice un pequeño hack y ahora puedo usar el API de fabric cuando escribo tasks de paver y que la tarea se ejecute una vez para cada host.
Para hacer eso, hice un decorator, que se llama multihost y es así:
from fabric import state, network
def multi_host(f):
"""\
Run the decorated function for each host in options.get("host_strings").
Will use the private key set in options.pk if set.
"""
def decoratee(options):
for host_string in options.get("host_strings"):
network.interpret_host_string(host_string)
pk = options.get("pk", None)
state.env.key_filename = [pk] if pk else None
f(options)
decoratee.func_name = f.func_name
return cmdopts((
("pk=", None, "Private key file"),
))(decoratee)
from paver.easy import task, needs
from fabric import api
@task
def find_hosts(options):
options["host_strings"] = ["host1", "user@host2", "user2@host3:2222"]
@task
@needs("find_hosts")
@multi_host
def remote_hello_world(options):
api.run("echo hello world")
En el laburo desde hace un tiempo estoy haciendo el experimento de correr varias VMs con los distintos proyectos en los que trabajo. Por lo tanto tengo varias VMs distintas (guests) corriendo que se conectan usando X a mi host.
En una de ellas tengo eclipse instalado con pydev. Y como host tengo un Windows 7 con X-Ming como server X. Y anda todo bastante bien salvo porque algunas ventanas de diálogo se van agrandando de formas extrañas hasta que se vuelve imposible usarlas. Y aparte, no sé por qué, no se pueden resizear usando el mouse (que sí se puede si lo corrés en Windows, gnome o kde normalmente).
Por lo tanto, busqué y encontré un workaround al problema, y edito a mano algunos archivos de configuración del workspace de eclipse para cambiar estos tamaños a una medida razonable para mi y para que quede documentado lo dejo anotado acá.
En el archivo ./org.eclipse.ui.ide/dialog_settings.xml sección DialogBoundsSettings setié:
<item value="1024" key="DIALOG_WIDTH"/>
<item value="768" key="DIALOG_HEIGHT"/>
<item value="1024" key="DIALOG_WIDTH"/>
<item value="768" key="DIALOG_HEIGHT"/>
<item value="1024" key="DIALOG_WIDTH"/>
<item value="768" key="DIALOG_HEIGHT"/>
<item value="1280" key="CommitDialog.size.x"/>
<item value="768" key="CommitDialog.size.y"/>
Hola,
estuve ocupado últimamente así que no pude darle mucha pelota a mi blog. Hoy suspendo este silencio para contarles que estuve haciendo en el laburo los últimos meses. Lo que hicimos es un servicio para pentestear instancias y páginas web que estén hosteadas en AWS. Aunque mucho el nombre no me gusta (lo eligió la gente de marketing), se llama Core CloudInspect y está acá.
La idea es hacer pentests en 5 clicks y 20 dólares y, los primeros 3 son gratarola!.
Happy hacking,
Aureliano.
Etiquetas:
cloud,
cloud computing,
pentest,
seguridad
Hace unas horas presenté en Hack.lu mi charla sobre aureliax. Aureliax es un software que sirve para mostrar diferencias entre diferentes versiones de una misma función, que es mostrada mediante un grafo de basic blocks. En cuanto pueda voy a poner on-line el white paper que explica cómo funciona y cuál es la base teórica que lo sustenta.
Por ahora estoy contento y cansado (acá en Luxemburgo son las 2 menos 20), y mañana tengo un largo día por delante.
Happy hacking,
Aureliano.
Etiquetas:
hack,
hacklu,
reversing,
seguridad,
visualización

Les recomiendo que vayan a ver esto. Yo fui la vez anterior, en abril, estuvo buenísimo y voy a ir de nuevo.
Etiquetas:
música
Qué valor debería dar el siguiente código:
"aDb".replace("D","$$")"aDb".replace("D",function() { return "$$" })
Etiquetas:
javascript
Me encontré con que no hay ningún método para dibujar flechas en raphaelJS, así que me puse las pilas y programé uno:
/**
* Returns a path in form of an arrow
* @param arrowSize is the size of the arrow end. Defaults to 10 pixels.
*/
Raphael.fn.arrow = function(x1, y1, x2, y2, arrowSize) {
var arrowSize = arrowSize || 10
var l = Math.sqrt( Math.pow( x2 - x1, 2 ) + Math.pow( y2 - y1, 2 ) )
var xStep = (x2 - x1) / l
var yStep = (y2 - y1) / l
// arrow point ends
var arrX1 = x2 + arrowSize * (yStep - xStep) / 2
var arrY1 = y2 + arrowSize * (-xStep - yStep) / 2
var arrX2 = x2 + arrowSize * (-yStep - xStep) / 2
var arrY2 = y2 + arrowSize * (xStep - yStep) / 2
var path = ["M", x1, y1, "L", x2, y2, //main line
"M", arrX1, arrY1, "L", x2, y2, "L", arrX2, arrY2] //arrow end
return this.path(path.join(" "))
}
var r = Raphael(....)
.....
var arrow = r.arrow(150,100,250,300)
Etiquetas:
javascript,
raphaeljs,
svg
Esto es lo que tuve que hacer para que la webapp en java que estoy desarrollando con eclipse 3.5 y web tools platform ande en el context-path root:
Saqué cuenta en twitter y voy a empezar a twittear (¿se escribe así?). Si quieren seguirme mi usuario es aurelianito.
En cuanto tenga alguna idea de lo que opino de twitter la mando por acá.
Happy hacking,
Aureliano
Etiquetas:
twitter
Update: si quieren pueden escuchar algunas canciones del show en su página de myspace
Este sábado 10 de abril a las 23:30 hs mi amiga y gran cantante Natalia Iñón presenta su espectáculo "Bajo el farol" en la Casona del Teatro, que queda en Corrientes 1975 (Ciudad de Buenos Aires). El repertorio está compuesto por canciones de Edith Piaf, Marlene Dietrich y algunos tangos. Les sugiero que vayan a escucharla ya que es una cantante exquisita.
Etiquetas:
música
Hoy configuré el lyx para que use la clase de LaTeX que piden para los proceedings de ACM y fue bastante más molesto de lo que esperaba. Esto lo hice en un ubuntu 9.10 casi pelado (con lyx y no mucho más). Acá les cuento los pasos:
Etiquetas:
acm,
latex,
lyx,
proceedings
El otro día estaba cenando con unos amigos y estabamos hablando de como me echaron de FB. Y un amigo hizo una analogía re-buena.
¿Qué pasa si te ponés a bailar en los pasillos de un shopping? Lo más probable es que venga alguien de seguridad y te pida "amablemente" que te retires del lugar. En cambio si te ponés a bailar en el medio de una plaza, más allá de algunas miradas raras no debería pasarte nada.
¿Y por qué? El asunto es que nos olvidamos que el shopping no es un espacio público sino un espacio privado al que te dejan entrar cuando quieren, como quieren y en el que pueden aplicar toda clase de reglas arbitrarias. En cambio, una plaza es un lugar mucho más público.
Y con facebook pasa lo mismo. Es un espacio que nos olvidamos que es privado, pero lo es. Entonces nos molesta que nos apliquen reglas raras.
Ojalá estas cosas que me pasaron a mi (y a otros) sirvan para que entendamos la diferencia.
\usepackage{tikz}\begin{figure}
\centering
\input{archivo_exportado_de_dia.tex}
\caption{Acá va el caption}
\label{aca_va_la_label}
\end{figure}
Ayer salió mi segundo advisory. Lo que encontramos con Manuk es que parte administrativa de la aplicación LanDesk Manager tiene un cross-site request forgery que hace que si enganchás a un administrador se pueda hacer un cross-site scripting no persistente y, aún más divertido, hacer un OS command injection.
Aún más divertido, es que se puede usar modprobe y cargar módulos de kernel de linux que uplodee usando el command injection.
Más detalles en la página de Core.
Happy hacking,
Aureliano
Etiquetas:
advisory,
command_injection,
csrf,
seguridad,
xss
Ayer me di cuenta que no necesito rehacer los classloaders de java para cargar scripts de JavaScript, ya que puedo usarlos y son todo lo flexibles que necesito (y más). Inclusive, si por una de esas casualidades llegara a hace falta, creo que puedo hacer uno en rhino y todo. Por lo tanto decidí que mi lógica para cargar módulos tenía una indirección de más (la lista de loaders) y lo cambié para que use el classloader y listo. Fíjense en la nueva implementación de loadMod y como se simplificó el uso de los loaders.
En la versión del código que estoy usando ahora, también hice que los módulos se carguen un toque distintos para que haya mejores stack-traces (ese cambio está en la función require). Bueno, abajo pongo el código:
/*
* Does not support relative paths (yet?).
* Can be invoked by several threads.
*
* Uses the classloader to fetch js modules.
*/
var require = function (id) {
try {
require.lock.lock() // Avoid threading issues while loading modules.
if (!require.modules[id]) {
var modCode = require.loadMod(id)
var exports = {}
require.modules[id] = exports
var module = {
id : id
}
var context = {
exports: exports,
module: module,
}
if(!modCode) {
throw new require.RequireError(id, "module not found")
}
org.mozilla.javascript.Context.getCurrentContext().evaluateString(
context,
modCode,
id,
1,
null
)
/*
Old style invocation, should work in the browser but has worst error messages.
var f = new Function("require", "exports", "module", modCode)
f.call(context, require, exports, module) */
}
return require.modules[id]
} catch (x) {
if (x instanceof require.RequireError) {
throw x
} else {
throw new require.RequireError(id, x)
}
} finally {
require.lock.unlock()
}
}
require.loaders = []
require.modules = {}
require.lock = new java.util.concurrent.locks.ReentrantLock()
var RequireErrorProto = {}
require.RequireError = function(moduleId, cause) {
this.moduleId = moduleId
this.cause = cause
}
require.RequireError.prototype = RequireErrorProto
require.RequireError.prototype.toString = function() {
return "Error loading " + this.moduleId + ( this.cause ? ". Cause: " + this.cause : "" )
}
require.RequireError.prototype.name = "RequireError"
require.load = function(path) {
return java.lang.Class.forName("java.lang.String").getResourceAsStream(path)
}
require.loadMod = function(id) {
function path(id) {
return "/" + id + ".js"
}
function readFromStream(stream) {
var io = java.io
var reader = new io.BufferedReader( new io.InputStreamReader(stream) )
try {
var stringBuffer = new java.lang.StringBuffer()
var line = ""
while( line = reader.readLine()) {
stringBuffer.append(line)
stringBuffer.append("\n")
}
return stringBuffer.toString()
} finally {
reader.close()
}
}
var stream = require.load(path(id))
if (!stream) { return null }
return readFromStream(stream)
}
/**
* Resets the module cache.
*
* It will reload all the modules. Old modules referenced will still be working.
*/
require.reset = function() {
try {
require.lock.lock()
require.modules = {}
} finally {
require.lock.unlock()
}
}
Etiquetas:
java,
javascript,
rhino
Para parsear un CSV con datos numéricos hay que hacer esto:
text.split("\n").map( function(r){
return r.split(",").map( function(d) {
return parseFloat(d)
} )
} )
Array.prototype.map = Array.prototype.map || function( f ) {
var r = []
for (var i=0; i<this.length; i++) {
r[i] = f(this[i])
}
return r
}
Etiquetas:
csv,
javascript
Primero hay que setear el namespace por default:
default xml namespace = "http://www.w3.org/1999/xhtml"
function elem(name) { return <{name} /> }function attr(elem, key, value) { elem["@"+key] = value }
Etiquetas:
e4x,
javascript,
rhino
Si estás logueado en Google, podés setear Google Scholar para que te muestre las entradas de bibtex.
Para eso tenés que ir a Scholar Preferences -> Bibliography Manager y elegir Show links to import citations into BibTeX.
Happy hacking,
Aureliano
(Sacado de acá)
Hoy estuve hackeando un toque y modifiqué el RhinoServlet que les mostré en este post para poder tener módulos en Javascript. El API que hice es un subset de lo definido en CommonJS. En particular, soporta require pero no los paths relativos de módulos (o sea, los que empiezan con . o ..). También le puse a todo un lock para que pueda usarse en un entorno concurrente, pero si un módulo tarda mucho en cargarse puede llegar a ser un problema.
Lo que me parece más interesante de mi prototipo es que se pueden agregar fácilmente nuevos loaders de módulos de JavasScript. Ya vienen por defecto loaders para cargar desde el classpath y desde la webapp, pero si quieren poder poner sus módulos en /WEB-INF/example, alcanza con agregar una línea en init.js:
require.loaders.push( require.stdLoader(servlet.servletContext, "/WEB-INF/example/"))
package aure.jslib;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
public class RhinoServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private Scriptable initialScope = null;
public void runJs(Context cx, String scriptLocation, Scriptable scope) throws IOException {
InputStream is = this.getClass().getResourceAsStream(scriptLocation);
if( is == null) {
is = this.getServletContext().getResourceAsStream("/WEB-INF/js/" + scriptLocation);
}
Reader jsReader = new InputStreamReader(is);
cx.evaluateReader(scope, jsReader, scriptLocation, 1, null);
}
public static void addJavaObjectToScope(Scriptable scope, String name, Object obj) {
ScriptableObject.putProperty(scope, name, Context.javaToJS(obj, scope) );
}
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
Context cx = Context.enter();
try {
Scriptable scope = cx.initStandardObjects();
addJavaObjectToScope(scope, "servlet", this);
addJavaObjectToScope(scope, "config", config);
this.runJs(cx, "initRequire.js", scope);
this.runJs(cx, "init.js", scope);
this.initialScope = scope;
} catch (IOException e) {
throw new ServletException(e);
} finally {
Context.exit();
}
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Context cx = Context.enter();
try {
Scriptable scope = cx.initStandardObjects();
scope.setParentScope(this.initialScope);
addJavaObjectToScope(scope, "request", request);
addJavaObjectToScope(scope, "response", response);
this.runJs(cx, "start.js", scope);
} finally {
Context.exit();
}
}
}
/* Based on http://www.davidflanagan.com/demos/require.js
* but heavily modified.
*
* Does not support relative paths (yet?).
* Can be invoked by several threads.
*
* Uses the servletContext and the classloader to fetch js modules.
*/
var logger = java.util.logging.Logger.getLogger("sarasa")
var require = function (id) {
try {
require.lock.lock() // Avoid threading issues while loading modules.
if (!require.modules[id]) {
var modText = require.loadMod(id)
var context = {}
var exports = {}
require.modules[id] = exports
var module = {
id : id
}
var f = new Function("require", "exports", "module", modText)
f.call(context, require, exports, module)
}
return require.modules[id]
} catch (x) {
throw new Error("Can't load module: " + id + ": " + x)
} finally {
require.lock.unlock()
}
}
require.loaders = []
require.modules = {}
require.lock = new java.util.concurrent.locks.ReentrantLock()
require.loadMod = function(id) {
var modText = null
var i = 0
while (modText == null && i < require.loaders.length) {
modText = require.loaders[i](id)
i++
}
return modText
}
// Setup loaders for files in the classpath, files in WEB-INF and files in the
// web-app (usually served to the client)
require.stdLoader = function(source, prepend) {
// Support functions
function readFromStream(stream) {
var io = java.io
var reader = new io.BufferedReader( new io.InputStreamReader(stream) )
var stringBuffer = new java.lang.StringBuffer()
var line = ""
while( line = reader.readLine()) {
stringBuffer.append(line)
stringBuffer.append("\n")
}
return stringBuffer.toString()
}
function path(id) {
return prepend + id + ".js"
}
var f = function(id) {
var stream = source.getResourceAsStream(path(id))
return stream ? readFromStream(stream) : null
}
return f
}
require.initStdLoaders = function() {
// classpath loader
require.loaders.push( this.stdLoader(servlet["class"], "/") )
// web-app context loader
require.loaders.push( this.stdLoader(servlet.servletContext, "/") )
}
require.initStdLoaders()
var f = new Function("require", "exports", "module", modText)
f.call(context, require, exports, module)¿Por qué te negás a usar dependencias de otros proyectos de eclipse?
¿Por qué no recompilás bien cuando está corriendo el web-server en modo debug y uso resources de adentro del classpath?
Bueno, por ahora son 2 problemas que creo poder "workarroundear".
Happy hacking,
Aureliano.
Hoy estuve configurando una VM con ubuntu que tiene una placa en modo bridged por DHCP y otra host-only estática. El problema que tuve fue que no encontraba la forma que la ruta por default sea la ruta por default que informa el dhcp. La solución que encontré fue hacer que la métrica de la ruta por default de la ip estática sea más grande.
Este es el archivo /etc/network/interfaces que terminé usando:
auto lo eth1 eth0
iface lo inet loopback
iface eth0 inet dhcp
iface eth1 inet static
address 172.17.17.16
netmask 255.255.255.0
network 172.17.17.0
broadcast 172.17.17.255
gateway 172.17.17.1
metric 200
Estuve con algo de tiempo y me puse a probar como hacer para ejecutar JavaScript en el server. E hice 2 implementaciones (o sea, 2 servlets programados en Java). La primera que hice usa javax.script (que viene por defecto en Java 6) y me parece que el API es más cómoda pero tiene 2 limitaciones que no me gustaron, no se puede heredar de clases Java en JavaScript y no encontré cómo hacer para bindear un método de Java a JavaScript. Este es el código:
package aure;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class JsServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private ScriptEngine jsEng = new ScriptEngineManager()
.getEngineByName("js");
private Bindings initBindings = this.jsEng
.getBindings(ScriptContext.ENGINE_SCOPE);
public JsServlet() {
super();
}
@Override
public void init(ServletConfig config) throws ServletException {
try {
super.init(config);
this.initBindings.put("config", config);
this.initBindings.put("servlet", this);
this.runJs("init.js", this.initBindings);
} catch (ScriptException e) {
throw new ServletException("Error running init.js", e);
}
}
public void runJs(String scriptLocation, Bindings bindings)
throws ScriptException {
Reader jsReader = new InputStreamReader(this.getServletContext()
.getResourceAsStream(scriptLocation));
this.jsEng.eval(jsReader, bindings);
}
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
try {
Bindings bindings = jsEng.createBindings();
bindings.putAll(this.initBindings);
bindings.put("request", request);
bindings.put("response", response);
this.runJs("start.js", bindings);
} catch (ScriptException e) {
throw new ServletException("Error running javascript", e);
}
}
}
package aure;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
public class RhinoServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private Scriptable initialScope = null;
public void runJs(Context cx, String scriptLocation, Scriptable scope) throws IOException {
Reader jsReader = new InputStreamReader(this.getServletContext()
.getResourceAsStream("/WEB-INF/js" + scriptLocation));
cx.evaluateReader(scope, jsReader, scriptLocation, 1, null);
}
public static void addJavaObjectToScope(Scriptable scope, String name, Object obj) {
ScriptableObject.putProperty(scope, name, Context.javaToJS(obj, scope) );
}
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
Context cx = Context.enter();
try {
Scriptable scope = cx.initStandardObjects();
addJavaObjectToScope(scope, "servlet", this);
addJavaObjectToScope(scope, "config", config);
this.runJs(cx, "/init.js", scope);
this.initialScope = scope;
} catch (IOException e) {
throw new ServletException(e);
} finally {
Context.exit();
}
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Context cx = Context.enter();
try {
Scriptable scope = cx.initStandardObjects();
scope.setParentScope(this.initialScope);
addJavaObjectToScope(scope, "request", request);
addJavaObjectToScope(scope, "response", response);
this.runJs(cx, "/start.js", scope);
} finally {
Context.exit();
}
}
}
Etiquetas:
java,
javascript,
rhino,
servlet
Para editar html con javascript con algo más de onda en gvim adopté el siguiente seteo:
:set inde=
:set ai
Etiquetas:
html,
indent,
javascript,
vim
¿Querés una invitación para Google Wave?
Avisame y te la mando (tengo 16 para repartir).
Esto le pasó ayer a un compañero de trabajo. Espero que sirva para que sepamos que no hay que creer todo lo que pasa en la tele:
Ayer fue al super (disco) a buscar la compra que habia hecho antes de
ayer ya que me avisaron que los empleados del delivery estaban de paro
por un reclamo sindical. cuando estoy llegando veo que en la esquina hay
una especia de muchedumbre de 20-30 personas y una camara de TV. No doy
bola y paso de largo hasta el super, que encuentro cerrado, en las
puertas de vidrio habia pagadas que decia qu ella empres ales mando la
policia a los que estaban reclamando para "apretarlos" y por ese se
metieron adentro y cerraron. Digo.. "uh que mal" y me voy pensando que
la muchedumbre de la esquina tendra algo que ver con eso. cuando llego a
la esquina y presto atencion a lo que pasa me doy cuenta que en realidad
hay 20-30 energumenos en chomba, camisa rosa+sweater al cuello y/o
vestido largo y ollas de fondue gritandole a la camara "seguridad!
seguridad!" mientras esgrimen cartelitos escritos prolijamente con
marcador con la palabra en cuestion ("seguridad"). Detras de todos
ellos, sentado en el piso esta durmiendo un señor de entre 50-60 años
que vive en la calle en esa esquina desde hace una semanas. Los
protestantes lo ignoran olimpicamente, pero el señor se despierta y
percibe que hay algun quilombo y desde el piso se une a los canticos al
tiempo que ademas aplaude (!?)
Me abro paso vocifereando que son todos unos payasos mediaticos (como
osvaldo pagani) pero a nadie parece importarle o molestarle lo que digo
o hago (que lastima! ya que estan todos hipnotizados cantando su mantra
a la camara.
Minutos despues cuando llego a casa me dicen que en el noticiero de
America transmitian en vivo una "protesta de los vecinos de palermo"
demandando mas seguridad o algo asi.
Media hora mas tarde cuando paso otra vez por la esquina paseando al
perro "los vecinos de palermo" no estan mas (el noticiero de america ya
termino) pero sigue estando el señor que vive en la esquina condiciones
de "seguridad" bastante precarias (hasta tanto no se entere la UZZEP).
Alguien vio algo de esto en el noticiero de America ayer? que dijeron?
Si dejo el firewall de Windows 7 activado filtra las conexiones entrantes a las VMs del VMWare Server que están conectadas en modo "bridged" a la red. No sé el ip del guest ya que se obtiene por DHCP. Si apago el firewall, las conexiones entrantes llegan hasta las VMs.
Saben como hacer para dejar el firewall prendido pero que las conexiones que van a los guests en modo bridged pasen sin ser filtradas? Qué reglas tengo que agregar/sacar?
Muchas gracias,
Aureliano
Vía slashdot.
Una mina con tratamiento por depresión en Canadá le niegan el tratamiento porque tiene fotos en la playa y en una fiesta en facebook (en una cuenta "lockeada"). Más detalles ver acá.
Esperemos que no se mate por esto,
Aureliano.
En este post prometí que iba a contar que cosas vi en VisWeek 2009. Y como lo prometí, aunque un poco tarde acá pongo mis notas de la conferencia (tienen mezcla con inglés):
Domingo 11:
8:30am
* Keynote: Bill Cheswick
*"Visualization is about coolness"
* Lots of developments. But almost nothing in the industry
Problemas:
* Layout arbitrario
* La evolución no es clara
* Las cosas que se sacan no se muestran
* Destruyen el contexto
Usa minimum distance spanning trees todo el tiempo para mostrar cosas aunque descarta el 30-40% de las conexiones.
How do you measure security?
* Generals and CIOs want to know.
Trusecure. Always and human in a step.
Network Access Security: contar cantidad de puertos abiertos
privilege scalation: programas que corren como root desde un usuario normal.
Parte difícil, como hacer los reportes.
2D sin tiempo -> 3D para el tiempo?
Ninguna buena visualización buena de IPv6
--------------------------------------------------------------------
Sesión de visualización de redes
10:30am
Visualization of complex attacks.
* Desde el pto de vista del sysadmin.
* Ataque simple: Una acción
* Ataque complejo: combinación de los anteriores
* DDNS
* "Multi-step"
* Worm propagation
* Interesante: las líneas hacen un "fade-out" con el tiempo. Las cosas nuevas son menos transparentes.
* El IDS informa que ataques simples pertenecen al mismo ataque complejo.
OverFlow: An Overview Visualization for network analisis
FloVis -> http://www.flovis.net
Security visualization tools and ipv6 addresses
* Demasiadas direcciones (128 bits)
* La mayoría de las visualizaciones no bancan ipv6
* muestra cambios en headers 4->6
* Se enfoca en "process and reformat" y "display"
* Muestra las comunicaciones con un scatter plot
* Le da un índice ordenado a cada dirección
* Usa la estructura de la dirección para agrupar direcciones
* Treemap (no me gusta, parece muy "cluttereado":
* Color: tipo de paquete (tcp, udp, etc)
* Tamaño: volumen de tráfico
1:00p
Malware and forensics
A visual Analytic framework for Exploring Relationships in Textual Content of Digital Forensics Evidence
Muestra como indexan el disco rígido y después filtran para ir buscando cosas en el disco rígido
Visual Analysis of Malware Behaviour Using Treemaps and Thread Graphs
CWSandbox.org
* Analisis automático de malware porque les mandan como 4000 ejemplos todos los días.
* Hacen un treemap con syscalls clasificados por tipo c/ color y cantidad de llamadas en el tamaño.
* El análisis es dinámico (o sea, ejecutan el malware).
* Por eso no los jode tanto la ofuscación
* Pueden detectar pdfs "malvados" mirando los treemaps (o clusterizando los datos).
Visualizing Compiled Executables for Malware Analysis
http://www.offensivetechnology.net (VERA)
* Dibuja los basic blocks que se ejecutan (todos, también los que se generan dinámicamente)
* Arma el grafo de los basic blocks
* les da más peso a los basic blocks que más se ejecutan
* por eso los loops que más se ejecutan quedan más grandes.
* pone diferentes colores a los basic blocks en función de cómo se generan
(ej: en el ejecutable en una sección de código,
en el ejecutable en una sección de datos,
generado dinamicamente,
etc)
2:30p
Users and Usability
Visualizing Cyber Security: Usable Workspaces
* Los analistas se quejan de que no pueden interactuar con los datos.
* Usan muuuuuuuuuuucha definición (10240x3200) y pueden mostrar simultáneamente los datos y los patrones que se forman en
alto nivel.
* History trees. Parecen como los branches en un control de versiones o algo así.
Visualization is Better! A Comparative Evaluation
http://tnv.sf.net vs ethereal en newbies
* Controlled experiment comparing two tools
* http://vizsec.org/datasets/
4:45p
Visualizing Keyboard Pattern Passwords
* Muestra como mostrar que teclas apretaron con una secuencia de reglas sobre como dibujarlos.
* Encontraron patrones mirando los passwds de a 25 (500 en total)
* Generaron diccionario con algunos patrones (500K passwords)
* Con ese diccionario crackeron passwds de verdad que John The Ripper no encontró
Visualizing Firewall Configurations Using Created Voids
Espacio:
5 dimensiones escalares (host1, port1, protocol, host2, port2) y un valor categorico (allow, deny).
regla:
(min_max de (host1, port1, protocol, host2, port2), (allow|deny))
Usa paralell coordinates :( (problemas de oclusión)
Panel (Visualization + Security = Science?):
Fue una pedorrada
Lunes 12:
Keynote VAST.
Storytelling.
Contó como es la estructura de un cuento clásico (introducción, nudo y desenlace), el camino del héroe (los 12 pasos que siguen casi todos los libros y películas) y dio una heurística para darse cuenta si algo es una buena historia o no. La misma consiste en si es posible responder estas 4 preguntas facilmente:
1. Quién es el héroe?
2. Qué quiere el héroe?
3. Qué impide que el héroe lo consiga?
4. Qué hace el héroe para solucionar el problema?
El chabón afirma que todos tenemos una habilidad natural para entender cuentos, y que por eso es bueno organizar las visualizaciones y presentaciones en forma de cuento.
VAST
Multidimentional Data session
A framework for uncertainty-aware visual analytics
Proponer manejar la incertidumbre todo a lo largo del pipeline de procesamiento de datos. Puede ser que los datos vengan con "incertidumbre" o que las transformaciones agreguen incertidumbre. Al final muestra la incertidumbre de varias maneras. Ej: color (segun que variable introduce mas incerteza), transparencia (para esconder datos menos confiables), o desagregando (pero solo para pocos datos)
Combining automated analysis and visualization techniques for efective exploration of high-dimentional data
Intenta detectar automáticamente que visualización de los datos es mejor. Creo que puede servir para mostrarle al usuario automáticamente vistas de datos que tengan algún sentido (muestran en scatter plots y paralell coordinates). También dicen que vistas muestran mejor una categorización dada.
Two-stage framework for visualization of clustered high dimentional data.
Hace algo parecido que la charla de arriba pero proyectando muchas dimensiones en 2 o 3.
Model space visualization for multivariate linear trend discovery.
Muestra una técnica manual para encontrar correlaciones lineales de dimensiones cuando hay muchas dimensiones
Martes 13:
Vast. Text analytics session.
LSAView: A tool for visual exploration of latent semantic modelling.
Sirve para clasificar documentos en función del tema. Visualiza el documento para que un analista decida si el clustering está bien.
Parallel tag clouds to explore and analyze faceted text corpora.
Analizan fallos de cortes EEUUenses. POnen las palabras que son más comunes en un documento y menos comunes en el resto (Dunning's log likehood). Ponen los tags con tamaño en gráfico de paralell coordinates. Parece interesante.
Describing story evolution from dynamic information streams.
No me gustó. Muestra como intentan detectar historias que se extienden a lo largo de diferentes artículos periodísticos.
What's being said near "Martha"? Exploring name entities in literature.
Muestra varias herramientas usando como ejemplo el análisis de literatura "rara":
* FeatureLens
* Wordle (Dunning's log likehood)
* POSvis
Necesitaban los Stop-words para analizar el texto.
VAST contest dataset use in education.
Hay un montón de datos de los diferentes VAST contest. Muestran como los usan para dar clases.
VARIAS DE LAS PRESENTACIONES DESTACARON LA NECESIDAD DE PODER IR AL TEXTO ORIGINAL QUE GENERÓ LOS DATOS QUE ESTÁN MOSTRANDO LAS HERRAMIENTAS. Posiblemente pase lo mismo con gráficos sumarizados de ataques.
Vast. Analysis Process & Graph Analytics
Connecting the dots in visual analysis.
Integran notas y flujos de trabajo en el proceso de analisis de los datos. Hacen todo un manejo bastante complicado de los flujos de trabajo (action trails?) del analista. También sugiere a los analistas que miren cosas que ya hicieron. Les preguntaron a 2 analistas sobre si les gustó el enfoque y dijeron que sí.
Capturing and supporting the analysis process.
Otro paper que explica como soportar el proceso del analista. Ejemplo con data set del VAST challenge 2008.
Evaluating visual analytics systems for interactive analysis: Deriving design principles from a case study.
Estudian como un analista usa jigsaw y otras herramientas para hacer tareas de analisis de datos.
A multi-level middle-out cross-zooming approach for large graph analytics.
Muestra una parte del grafo con una "resolución" y otra parte con otra "resolución". Cuando hace zoom-in en algún lado hace zoom-out en otro para mantener + o - constante la cantidad de cosas que muestra. Sirve para grafos de tipo small-worlds (algo parecido a esparsos). La implementación es un bolonqui porque quieren que todo sea interactivo (quién no?).
http://www.pnl.gov/wong
Visual analytics of graphs with multiple connected components.
Share-holder networks == Weighted directed acyclic graph.
Buscan estructuras comunes en estos grafos. Para eso hacen un proceso iterativo donde buscan los componentes.
Tutorial: Exploring design decisions for effective information visualization
Usando datos de las últimas 8 elecciones norteamericanas hicimos un montón de treemaps distintos analizando que se ve en cada uno, que no se ve y buscando la mejor forma de mostrar cosas.
Miércoles 14:
8:30a Keynote: Visual thinking and visual thingking tools
En esta charla Collin Ware estuvo hablando sobre como percibimos las cosas y como esto afecta como nos relacionamos con las herramientas de visualizacion. En particular mostró como se compara una interfase zoomeable con una que abre ventanitas para mostrar detalles y dio algunas formas muy simples de calcular que esfuerzo requiere hacer tareas en ambas interfaces. Usando el mismo ejemplo, también mostró porqué en otro estudio que él hizo convino usar imagenes en vez de video para mostrar movimiento. Esta charla también le da sustento teórico a la necesidad de tener más espacio disponible en el/los monitor(es).
4:15p Models and theories
A nested model for visualization design and validation
* problem
* data/op abstraction
* enc/interact technique
* algorithm
Intenta ordenar cómo saber si una visualización es "buena" o no. Divide en 4 diferentes "aspectos" a una visualización.
Conjunctive visual form.
Hace una especie de query by example pero en vez de poner un ejemplo pone un conjunto para cada dimensión (cross-filtered views, paper del año pasado).
Jueves 15:
Interaction Techniques for Selecting and Manipulating Subgraphs in Network Visualizations
Explica un montón de diferentes formas de operar sobre conjuntos de nodos en un grafo y elegir que conjunto de nodos. Están buenos pero son un quilombo.
Software: Navigator.
ActiviTree: Interactive Visual Exploration of Sequences in Event-Based Data Using Graph Similarity
Buscar patrones en eventos. ¿Puede servir para analizar logs?
“Search, Show Context, Expand on Demand”: Supporting Large Graph Exploration with Degree-of-Interest
Enfoque bottom-up para explorar grafos. Search, show context, expand on demand.
cada nodo tiene un "interes". Con eso, la query que hace el usuario y la estructura del grafo hacen una cuenta y definen el "degree of interest" (DOI) de un nodo. El contexto es el subgrafo conexo que contiene a ese nodo y tiene mayor interés. Para hacer que ande mejor un algoritmo greedy "difunden" el interés en los nodos vecinos. Lo usan para buscar fallos judiciales y como se relacionan.
A Comparison of User-Generated and Automatic Graph Layouts.
Probaron layouts generados a mano por usuarios (usando Surface, de MS) para las tareas que iban a pedir a otros participantes. Después agarraron esos layouts y 3 layouts automáticos más e hicieron que otra gente haga esas cosas. Al final, el mejor fue el force directed layout.
Smooth Graphs for Visual Exploration of Higher-Order State Transitions.
En vez de tomar A-B como "edge" se fijan en secuencias más largas (A-B-C-D, por ejemplo). Usaron esto para analizar el comportamiento de pingüinos.
Harnessing the Web Information Ecosystem with Wiki-based Visualization Dashboards.
Muestra un wiki que sirve para armar dashboards con algunos tipos de visualizaciones.
Viernes
code swarm: A Design Study in Organic Software Visualization
MOstró codeswarm.
Towards Utilizing GPUs in Information Visualization: A Model and Implementation of Image-Space Operations
Mostró un prototipo que usa la GPU para hacer visualizaciones (basado de shaders) y un programa que sirve para implementar los shaders uniendo cajas con flechas.
A Multi-Threading Architecture to Support Interactive Visual Exploration
Arquitectura para hacer visualizaciones:
1 thread de eventos
n threads para n visualizaciones
Los threads de las visualizaciones son interrumpidos cuando los datos que usan para calcular cosas cambian.
Hay un cache de cachos de dibujos y datos intermedios
Protovis: A Graphical Toolkit for Visualization
Toolkit para hacer visualizaciones en JavaScript de los autores de Flare y Prefuse
http://protovis.org
Etiquetas:
visualización
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