This is my blog, more about me at marianoguerra.github.io
🦋 @marianoguerra.org 🐘 @marianoguerra@hachyderm.io 🐦 @warianoguerra
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
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
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()
/etc/hosts
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!
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, >}
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/)