viernes, noviembre 27, 2009

object oriented erlang

el titulo suena sacrilegioso pero es algo asi.

ultimamente estaba pensando como agregarle una construccion mas poderosa que los "records" de erlang a mi lenguaje (http://marianoguerra.com.ar/efene) y me parece que llegue a una version que me cierra:

-module(person).
-compile(export_all).

-record(person, {firstname, lastname, mail}).

person(Firstname, Lastname, Mail) ->
Person = #person{firstname=Firstname, lastname=Lastname, mail=Mail},
wrapper(Person).

wrapper(Person) ->
fun(getfirstname) -> Person#person.firstname;
(getlastname) -> Person#person.lastname;
(getmail) -> Person#person.mail;
({has, Field}) -> lists:member(Field, [firstname, lastname, mail]);
({setfirstname, Value}) -> wrapper(Person#person{firstname=Value});
({setlastname, Value}) -> wrapper(Person#person{lastname=Value});
({setmail, Value}) -> wrapper(Person#person{mail=Value});
(string) -> io_lib:format("person(~p ~p ~p)",
[Person#person.firstname, Person#person.lastname,
Person#person.mail]);
(record) -> Person;
(fields) -> {firstname, lastname, mail};
(name) -> person end.

test() ->
% helper function
Print = fun(X) -> io:format("~p~n", [X]) end,
% create an "object"
P = person("mariano", "guerra", "mail"),
% get firstname
Print(P(getfirstname)),
% get lastname
Print(P(getlastname)),
% get mail
Print(P(getmail)),
% return the "object" as string
Print(P(string)),
% get the "object" as an erlang record
Print(P(record)),
% get the fields of the "object"
Print(P(fields)),
% get the name of the "object"
Print(P(name)),

% check if the "object" has an attr called firstname
Print(P({has, firstname})),
% check if the "object" has an attr called address
Print(P({has, address})),

% create a new "object" changing the firstname attribute
P1 = P({setfirstname, "Mariano"}),
% print the new "object"
Print(P1(string)).


la idea es generar la function person (y poner wrapper adentro) automaticamente en base a una expresion como

person = record(firstname lastname mail)

la cual genera la descripcion del record y la funcion para construir el "objeto"

esto agrega cosas dinamicas que los records no tienen, ya que son transformados a tuplas en tiempo de compilacion y toda la informacion del mismo se pierde, por ejemplo con un record en erlang no se puede saber sus campos, no se puede saber su nombre, no se puede manipular sin incluir el .hrl que lo define, no se puede saber si tiene un atributo y muchas cosas mas como tener que especificar el tipo de record del que estoy hablando en cada expresion (mirar el codigo de wrapper para darse una idea).

basicamente le agregue duck typing a los records en erlang :P

se podria tener una lista de estos "objetos" que en realidad son funciones con closures adentro, y checkear si tienen un atributo y hacer algo con ellos, cosa que no se podria hacer con los records de erlang.

en unas semanas cuando ande con tiempo lo agrego a mi lenguaje y ya casi estaria cerrado para una 0.1 :D

la sintaxis en efene va a quedar algo asi (reimplementando la function test)

person = record(firtname lastname mail)

main = fn () {
P = person("mariano" "guerra" "mail")
# helper function
Print = fn (X) { io.format("~p~n", [X]) }
# create an "object"
P = person("mariano" "guerra" "mail")
# get firstname
Print(P(getfirstname))
# get lastname
Print(P(getlastname))
# get mail
Print(P(getmail))
# return the "object" as string
Print(P(string))
# get the "object" as an erlang record
Print(P(record))
# get the fields of the "object"
Print(P(fields))
# get the name of the "object"
Print(P(name))

# check if the "object" has an attr called firstname
Print(P((has, firstname)))
# check if the "object" has an attr called address
Print(P((has, address)))

# create a new "object" changing the firstname attribute
P1 = P((setfirstname, "Mariano"))
# print the new "object"
Print(P1(string))
}


los parentesis extras en los set* y el has son necesarios porque es una unica function que recibe un solo argumento (se debe mantener la aridad en todos los pattern matching de una misma function) y preferi que los get y los "metodos" sean simples y complicar los sets.

se aceptan criticas

jueves, noviembre 26, 2009

Panic - The smiths



Burn down the disco
Hang the blessed DJ
Because the music that they constantly play
IT SAYS NOTHING TO ME ABOUT MY LIFE
Hang the blessed DJ
Because the music they constantly play

lunes, noviembre 23, 2009

some quote

Finland is sometimes given as an example of a prosperous socialist country, but apparently the combined top tax rate is 55%, only 5% higher than in California. So if they seem that much more socialist than the US, it is probably simply because they don't spend so much on their military.

-- Paul Graham

sábado, noviembre 21, 2009

no caere tanto del cielo

llega un momento en la vida de toda persona en la que se ve saltando de un avion y cayendo al vacio a 200 km/h desde 3000 metros de altura.

ese dia fue para mi hoy :)

algunas fotos y un video

el album completo aca










viernes, noviembre 20, 2009

webless prototipo final

si viste el post anterior encontraste un poco de codigo de mas, para que lo querria uno?

bue, la idea era hacer algo asi:

uno entra a una pagina, pone una url, escribe un poco de javascript/jquery, aprieta probar y nos trae la pagina de resultado renderizandola en el server en webkit y aplicando el codigo que le dimos.

si el snippet que hicimos nos es util lo podemos guardar y nos da una url que podemos usar para obtener el resultado de esa transformacion cada vez que queramos.

no entendes nada? un ejemplo?

* obtener solo los links de los resultados de la primera pagina de google al buscar X
* sacar la cotizacion del dolar de la pagina de algun diario

es interesante tenerlo limpito y nos puede interesar consumirlo periodicamente o usarlo como entrada a otro programa, incluso podemos combinar varias entradas y armar un mashup en una pagina usando los resultados de varios scripts.

todavia no se entiende? ok, vamos con un par de screenshots...

lo que hacemos es poner esta url: www.google.com/search?q=sublime
y este script:


var page = $("html");
page.html($(".l"));
$('.l').wrap('<li></li>');
page.wrapInner('<ul></ul>');


y darle test, el resultado es:



luego elegimos send y lo guarda y nos da una url permanente para accederlo, al hacerlo vemos lo siguiente:



si alguien tiene ganas de seguir jugando con esto o lo hostea en algún lado publico el código por ahí :D

inject fun

el post anterior lo escribi como calentamiento para este que es la realizacion de una idea que tuve hoy (y que se va a completar en el post siguiente).

ya hicimos un browser, ahora estaria bueno manosear el contenido de las paginas no?

la mejor forma que conozco en cualquier lenguaje de manipular html es jquery, asi que estaria bueno poder jugar con el en todas las paginas no? lastima que no todas lo incluyen...

pero si lo inyectamos si lo van a tener :)

entonces tenemos una pagina, un campo para poner codigo js y ejecutarlo y podemos modificar el contenido de la pagina, algo asi:



por si no se ve corri el siguiente script: $('a').html('lol');

ahora vamos a ver un uso mas interesante:



casi me olvido el codigo :)

import os
import sys
import gtk
# https://bugs.launchpad.net/bugs/480398
gtk.gdk.threads_init()
import webkit

class Webless(object):
def __init__(self, url=''):
self.view = webkit.WebView()
self.open(url)

def inject_file(self, path):
'''run a script in a file'''
if os.path.isfile(path) and os.access(path, os.R_OK):
self.inject_script(file(path).read())
return True

return False

def inject_script(self, script):
'''run a script'''
self.view.execute_script(script)

def get_code(self):
'''return the text of the widget'''
self.view.execute_script('oldtitle=document.title;document.title=document.documentElement.innerHTML;')
html = self.view.get_main_frame().get_title()
self.view.execute_script('document.title=oldtitle;')
return html

def open(self, url):
if not url.startswith('http://') and not url.startswith('https://'):
self.url = 'http://' + url
else:
self.url = url

self.view.open(self.url)

class Browserless(gtk.Window, Webless):
def __init__(self, url=''):
gtk.Window.__init__(self)
Webless.__init__(self, url)
self.add(self.view)

class Browser(gtk.Window, Webless):
def __init__(self, url=''):
gtk.Window.__init__(self)
Webless.__init__(self, url)
self.set_title('Browser')
self.set_default_size(640, 480)

scroll = gtk.ScrolledWindow()
scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scroll.set_shadow_type(gtk.SHADOW_IN)

scroll.add(self.view)

vbox = gtk.VBox()
hbox = gtk.HBox()

entry = gtk.Entry()
entry.connect('activate', self._on_url_changed)

if self.url:
entry.set_text(self.url)

code = gtk.Entry()
code.connect('activate', self._on_run_code)

getcode = gtk.Button("html to clipboard")
getcode.connect('clicked', self._on_get_code)

hbox.pack_start(code, True, True)
hbox.pack_start(getcode, False)

vbox.pack_start(entry, False)
vbox.pack_start(scroll, True, True)
vbox.pack_start(hbox, False)
vbox.show_all()

self.add(vbox)

self.connect('delete-event', lambda *args: sys.exit(0))

def _on_get_code(self, button):
'''copy the content of the html window to the clipboard'''
code = self.get_code()
gtk.clipboard_get().set_text(code)

def _on_url_changed(self, entry):
'''called when the url changes'''
url = entry.get_text()
self.open(url)

def _on_run_code(self, entry):
'''called when the url changes'''
code = entry.get_text()
self.inject_script(code)

def process_events():
while gtk.events_pending():
gtk.main_iteration(True)

if __name__ == '__main__':
browser = Browser('www.google.com/search?q=emesene')

def inject_fun(view, frame):
browser.inject_file('js/jquery.js')
browser.inject_file('js/json2.js')

browser.view.connect('load-finished', inject_fun)

browser.show()

gtk.main()

Browser en pygtk con webkit en unas cuantas lineas de codigo


import sys
import gtk
# https://bugs.launchpad.net/bugs/480398
gtk.gdk.threads_init()
import webkit

class Browser(gtk.Window):
def __init__(self, url=''):
gtk.Window.__init__(self)
self.set_title('Browser')
self.set_default_size(640, 480)

self.url = url

scroll = gtk.ScrolledWindow()
scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scroll.set_shadow_type(gtk.SHADOW_IN)

self.view = webkit.WebView()
scroll.add(self.view)

vbox = gtk.VBox()

entry = gtk.Entry()
entry.connect('activate', self._on_url_changed)

if self.url:
entry.set_text(self.url)
entry.activate()

vbox.pack_start(entry, False)
vbox.pack_start(scroll, True, True)
vbox.show_all()

self.add(vbox)

self.connect('delete-event', lambda *args: sys.exit(0))

def _on_url_changed(self, entry):
'''called when the url changes'''
self.url = entry.get_text()

if not self.url.startswith('http://') and not self.url.startswith('https://'):
self.url = 'http://' + self.url

self.view.open(self.url)

if __name__ == '__main__':
browser = Browser('www.google.com')
browser.show()
gtk.main()

domingo, noviembre 15, 2009

/etc/hosts

127.0.2.1 reader.google.com
127.0.2.1 slashdot.org
127.0.2.1 reddit.com
127.0.2.1 clarin.com
127.0.2.1 criticadigital.com
127.0.2.1 lanacion.com.ar
127.0.2.1 lavoz.com.ar
127.0.2.1 ole.com.ar


veamos cuanto duro

viernes, noviembre 13, 2009

travlr.in

Algunos saben, otros no, pero con mi hermano y un amigo estamos trabajando en un proyecto llamado travlr in hace ya casi un año..


Si bien no esta 100% completo, ya se puede usar, asi que pensamos que es hora de empezar a pasar verguenza! :P


El proyecto se llama travlr.in (traveler in - viajero en) y pueden encontrarlo en www.travlr.in


Travlr.in pretende responder a las típicas preguntas: que hacer? donde? cuando? y tambien mostrarte que cosas hay a tu alrededor, que puede ser desde negocios, plazas, bares y hospitales hasta fiestas para el fin de semana y eventos historicos que sucedieron en cualquier parte del mundo.


Si sos de Córdoba vas a ver que el centro de la ciudad esta lleno de informacion, ya que nos tomamos el trabajo de salir y tomar nota de todos los negocios, telefonos publicos, puestos de diarios y revistas, paradas de colectivos y un largo etcetera (faltan un par de cuadras y el centro estaria completo).


El sistema no tiene mucha informacion actualmente, ya que la idea es que cada uno ponga lo que le interese de su alrededor para compartirlo con el resto. Algo así como una wikipedia de lugares y eventos, mantenida por la comunidad..
Si tenes un negocio podes cargarlo en travlr.in, si haces una fiesta tambien! cualquier cosa es posible, no pretendemos imponer ninguna estructura sobre lo que se puede poner en el mapa y lo que no, simplemente queremos que les sea una herramienta util a cada uno de ustedes, sin importar si estan marcando todos los arboles de córdoba o quizas puntos de avistamiento de ovnis, no nos importa! a mas información y mas variada mejor!


Como se usa?

Cualquiera puede buscar contenido en travlr.in sin necesidad de una cuenta, pero si deseas crear contenido, solo tenes que entrar a travlr.in, clickea en “ingresar” y luego en “Registrarse”, create una cuenta y ya podes crear cualquier cosa!


Para buscar, solo tenes que posicionar el mapa en donde sea que te interese buscar algo, escribir en la barra de busqueda y darle click a la lupa.
Para crear, solo tenes que clickear en + y luego hacer click en donde sea que esto se encuentre para localizar lo que sea que estes creando, luego agrega informacion sobre el elemento creado y listo! Ya podes crear eventos sobre ese lugar, agregar fotos, videos, comentarios, etc

Mucha mas informacion y en mas detalle en www.wiki.travlr.in

Se aceptan criticas de cualquier tipo, a no abstenerse que las necesitamos!

martes, noviembre 10, 2009

como subir attachments a couchdb desde un formulario HTTP (plus 300 posts)

algo que costo poner a andar fue subir attachments a un documento desde una pagina web, termine leyendo como lo hace futon (linea 886, el frontend de couchdb) y quedo algo asi:

en el html

   <div id="upload">
<form id="upload-form" method="POST" action="" enctype="multipart/form-data">
<p id="upload-msg"></p>
<label for="_attachments">Archivo</label>
<input type="file" name="_attachments" id="image-file"/>
<div class="buttons">
<input type="hidden" value="" name="_rev"/>
<button onclick="return ui.upload();">Adjuntar</button>
</div>
</form>
</div>
</div>


en el codigo javascript:

// en algun lado del init correr esto
$('#upload-form').ajaxForm();

ui.upload = function () {
var id = $("input[name='_id']").val(), form = $('#upload-form'), field = $('#image-file');

if (field.val().trim() === '') {
ui.error("Archivo no seleccionado");
return false;
}
else {
form.ajaxSubmit({
url: req.base + id,
success: function(resp) {
field.val('');
ui.message('Imagen adjuntada');
}});
}
};


para hacerlo hace falta jquery y el plugin ajaxsubmit

PD: este es el post 300, 300 posts intrascendentes de una persona intrascendente, keep poluting the interwebs

PD1: vino ian curtis con una pepsi de litro y medio para festejar (la de dos litros esta cara)

PD2: no, no voy a poner una imagen de la pelicula 300 :P

como extender couchdb

que sucede si queres hacer un redirect en couchdb?
que sucede si queres hacer un request a otro host desde js y debido al same origin policy no lo podes hacer?
[Insertar otras preguntas cuya respuesta sea lo que voy a explicar aqui]

mi primer opcion fue leventar un servercito de python en otro puerto, pero eso me complicaba porque violaba el same origin policy y tenia que hacer cosas feas con iframes.

entonces me puse a leer el codigo fuente de couchdb (no hay mucha info en otro lado sobre como hacerlo) y vi que la mayoria de los handlers son extensiones en couchdb.

Si se fijan en el archivo de configuracion, van a ver algo como


[httpd_global_handlers]
/ = {couch_httpd_misc_handlers, handle_welcome_req, <<"Welcome">>}
favicon.ico = {couch_httpd_misc_handlers, handle_favicon_req, "/usr/share/couchdb/www"}

_utils = {couch_httpd_misc_handlers, handle_utils_dir_req, "/usr/share/couchdb/www"}

[snip]


lo que hace eso es que mapea urls a handlers escritos en erlang, el primer elemento de la tupla es el modulo, el segundo es la funcion y el resto son parametros a la funcion.

mi primer experimento fue hacer un redirect para que la url de mi couchapp no quede tan fea, agregue estas linea en /etc/couchdb/local.ini (las otras se veran mas adelante):


editor = {fresita, redirect_editor}
_export = {fresita, export_document}
_style = {fresita, export_style}


y escribi un modulo que se ve asi:

-module(fresita).
-export([redirect_editor/1, export_document/1, export_style/1]).

redirect_editor(Req) ->
couch_httpd:send_redirect(Req, "/fresita/_design/fresita/index.html").

export_document(Req) ->
"/" ++ UrlPath = couch_httpd:path(Req),
[_Export, Database, InputId, Output, StyleId, Format, _FileName] = string:tokens(UrlPath, "/"),
Command = "python /var/lib/fop/export.py --type id --input " ++ InputId
++ " --output /tmp/" ++ Output ++ " --style " ++ StyleId ++ " --format "
++ Format ++ " --url http://localhost:5984/" ++ Database ++ "/",
log(UrlPath),
log(Command),
os:cmd(Command),
couch_httpd:serve_file(Req, Output ++ "." ++ Format, "/tmp/").

export_style(Req) ->
"/" ++ UrlPath = couch_httpd:path(Req),
[_Style, Database, StyleId] = string:tokens(UrlPath, "/"),
os:cmd("python /var/lib/fop/export.py --type style --style " ++
StyleId ++ " --url http://localhost:5984/" ++ Database ++ "/>/tmp/" ++
StyleId),
couch_httpd:serve_file(Req, StyleId, "/tmp/").

log(Text) ->
{ok, Device} = file:open("/tmp/fresita.log", [append]),
file:write(Device, Text ++ "\n").


lo que hace redirect es simplemente redirigir la url a otra que es mas compleja, los otros dos corren procesos externos y devuelven el contenido al browser (el primero genera pdf y rtf usando apache fop y el segundo hace un merge de algunas cosas).

con esto quiero documentar algo que seguro me voy a olvidar en el futuro y aportar mi granito de arena a couchdb para hacer cosas que se complican si solo tenes couchdb y el frontend en el browser.

para hacer andar esto compilas el archivo con erlc archivo.erl y lo pones en algun lugar donde erlang busque, yo lo puse donde estan todas las otras extensiones de couchdb (/usr/lib/couchdb/erlang/lib/couch-0.10.0/ebin/)

sábado, noviembre 07, 2009

llega el momento en la vida de toda persona..

... en la que escribe un lenguaje funcional para la maquina virtual de erlang.

les presento en sociedad a un proyecto que empezo hace dos semanas basado en una idea que tuve hace unos meses :)

los invito a pasar por la escueta pagina que le hice.

www.marianoguerra.com.ar/efene

todavia le faltan algunas cosas pero ya es totalmente funcional.

Seguidores

Archivo del Blog