tag:blogger.com,1999:blog-68877619882957251702024-03-13T01:15:05.360-07:00El rincón malditoprint "Maldición!"Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.comBlogger106125tag:blogger.com,1999:blog-6887761988295725170.post-27668032026929927292012-10-25T16:52:00.000-07:002012-10-25T16:55:15.288-07:00Error importando OAuthSignatureMethod_HMAC_SHA1 en python-oauth<div style="text-align: justify;"><span style="font-size:85%;">Si estás utilizando la librería <a href="http://pypi.python.org/pypi/oauth/1.0.1">python-oauth</a> para autenticarte contra un servicio por medio de OAuth y te consigues con el error:
<code><pre>AttributeError: 'module' object has no attribute 'OAuthSignatureMethod_HMAC_SHA1'</pre></code><br/>
Entonces tengo la solución para ti.<br/><br/>
El problema es que la clase no está en el módulo <b>oauth</b>, sino en <b>oauth.oauth</b>. Entonces, en lugar de hacer:
<code><pre>import oauth</pre></code><br/>
Prueba con:
<code><pre>try:
import oauth.oauth
except:
import oauth</pre></code><br/>
De nada ;)
<br/><br/>
Referencias:
<ul>
<li><a href="http://code.google.com/p/oauth-python-twitter/issues/detail?id=4">Issue reportado en el repositorio de Google Code de oauth-python-twitter</a></li>
<li><a href="http://code.google.com/p/oauth-python-twitter/issues/attachmentText?id=4&aid=8904247377172589732&name=oauth_import_fix.diff&token=pQEzLjcc3vk-03dcyjjA05j-mCc%3A1351208015065">Patch enviado para el issue anterior</a></li>
</ul>
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com3tag:blogger.com,1999:blog-6887761988295725170.post-6525539160161452652012-10-09T17:21:00.000-07:002012-10-09T17:21:11.953-07:00Personalizar el unity-greeter de LightDM en ArchLinux<div style="text-align: justify;"><span style="font-size:85%;">Buscando opciones para un gestor de inicio de sesión bonito y ligero recordé a <a href="https://launchpad.net/lightdm">LightDM</a>. Sé que seguramente me van a hablar de <a href="http://slim.berlios.de/">SLiM</a>, pero ha estado abandonado mucho tiempo y no es tan lindo y funcional como LightDM, así que decidí no utilizarlo.
<h2>Instalación</h2>
LightDM se encuentra disponible en AUR, así que una buena forma de instalarlo es con yaourt:
<code>$ yaourt -S lightdm lightdm-unity-greeter</code><br/>
Para más información de cómo instalar LigthDM visita la <a href="https://wiki.archlinux.org/index.php/LightDM">wiki de ArchLinux</a>.
<h2>Configuración</h2>
Esta es la parte divertida. Muchas recetas en internet te hablan de editar el infame <b>/etc/lightdm/lightdm-unity-greeter.conf</b>, pero la verdad es que la versión más nueva no utiliza ese archivo sino que, "en teoría", utiliza dinámicamente la imagen establecida como fondo de pantalla para el fondo del greeter. En mi caso siempre usaba la misma imagen, la que trae Ubuntu por defecto. Incluso agregué mi imagen a la ruta <b>/usr/share/backgrounds/</b> y tampoco funcionaba.
<br/><br/>
¿Qué hacemos? Pues editemos el schema del dconf a mano. Abrimos con nuestro editor favorito el archivo <b>usr/share/glib-2.0/schemas/com.canonical.unity-greeter.gschema.xml</b> y ajustamos los valores correspondientes.<br/><br/>
Yo edité solamente la línea del background y del color de fondo:
<pre><code><key name="background" type="s">
<default>'/usr/share/backgrounds/poweroff.jpg'</default>
<summary>Background file to use, either an image path or a color</summary>
</key>
...
<key name="background-color" type="s">
<default>'#444444'</default>
<summary>Background color (e.g. #772953), set before wallpaper is seen</summary>
</key>
</code></pre><br/>
En el key con nombre "background" cambié la ruta al archivo de fondo, apuntando a <b>/usr/share/backgrounds/poweroff.jpg</b> y en el key con nombre "background-color" cambié el color a <b>#444444</b>. Ustedes editen los campos como mejor les parezca.<br/><br/>
Para que estos cambios tengan efecto debemos recompilar los esquemas del dconf, así que ejecutamos:
<code># glib-compile-schemas /usr/share/glib-2.0/schemas/</code><br/>
Reiniciamos el servicio y ya estaremos disfrutando de nuestros cambios en el gestor.<br/><br/>
Espero que les haya servido esta receta.<br/><br/>
<h2>Referencias</h2>
<ul>
<li><a href="http://rohanprabhu.com/?p=240">http://rohanprabhu.com/?p=240</a></li>
<li><a href="http://askubuntu.com/questions/64001/how-do-i-change-the-wallpaper-in-lightdm/121594#121594">http://askubuntu.com/questions/64001/how-do-i-change-the-wallpaper-in-lightdm/121594#121594</a></li>
</ul>
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com3tag:blogger.com,1999:blog-6887761988295725170.post-62988890961189497572012-09-18T20:49:00.000-07:002012-09-18T20:54:37.126-07:00Respaldar archivos usando Rsync<div style="text-align: justify;"><span style="font-size:85%;">En ocasiones nos toca respaldar gran cantidad de información de un equipo a otro y una de las maneras más cómodas de hacerlo es a través de la red.
Para esta tarea usaremos <a href="http://es.wikipedia.org/wiki/Rsync">Rsync</a> que, al igual que <a href="http://damncorner.blogspot.com/2008/03/respaldo-usando-netcat.html">netcat</a>, son unas navajas suizas en lo que a red se refiere. Rsync, entre otras cosas, nos permite sincronizar directorios a través de la red y de esa forma lo usaremos para enviar nuestros archivos de un equipo a otro.<br/>
<h2>Comando y parámetros</h2>
Lo primero que necesitamos es acceso vía SSH al equipo remoto. Puede ser por clave pública o con usuario y contraseña. Luego nos ubicamos en la carpeta que queremos respaldar y ejecutamos el comando:
<code>$ rsync -ravz archivo_a_respaldar usuario@IP:directorio_destino</code>
Donde:
<ul>
<li><b>r</b>: recorre los directorios de manera recursiva</li>
<li><b>a</b>: conserva los atributos de los archivos (usuario, permisos, etc)</li>
<li><b>v</b>: imprime información en pantalla</li>
<li><b>z</b>: comprime los datos antes de enviar</li>
</ul>
Luego de la autenticación comenzará la copia de los archivos.
Es importante resaltar que en el <b>directorio_destino</b> debemos colocar la ruta absoluta a la carpeta.<br/>
<h2>Copiar un archivo</h2>
Supongamos que queremos respaldar el archivo <b>backup.tar.gz</b> en un equipo remoto con IP <b>192.168.0.3</b>, el usuario es <b>satanas</b> y el directorio destino es el home del usuario, el comando nos quedaría así:<br/>
<code>$ rsync -avz backup.tar.gz satanas@192.168.0.3:~</code><br/>
El símbolo <b>~</b> puede sustituirse por la ruta absoluta <b>/home/satanas</b>.<br/>
<h2>Copiar un directorio y todos sus subdirectorios</h2>
Ahora supongamos que queremos respaldar el directorio <b>/tmp/music</b> en el mismo equipo anterior pero ahora el destino es la carpeta <b>/home/satanas/musica</b>, el comando nos quedaría así:
<code>$ rsync -ravz /tmp/music satanas@192.168.0.3:/home/satanas/musica</code>
<br/><br/>
Con esta receta podremos pasar nuestro respaldo de archivos de un equipo a otro sin mucho inconveniente. Espero que les sirva
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com9tag:blogger.com,1999:blog-6887761988295725170.post-83531318765590795872012-03-22T12:08:00.000-07:002012-04-03T13:12:28.400-07:00Tip: Obtener el espacio usado de un directorio<div style="text-align: justify;"><span style="font-size:85%;">
<h2>Situación</h2>
Queremos conocer el espacio en disco usado por un directorio (y sus subdirectorios inmediatos) con un comando de consola.
<br/>
<h2>Comando</h2>
<code><pre>$ du -h -s /tu/directorio/*</pre></code>
<br/>
<h2>Resultado</h2>
El comando te imprimirá algo como:
<code><pre>$ du -h --summarize /tu/directorio/*
2.5M /var/backups
286M /var/cache
139M /var/lib
4.1k /var/local
4.1k /var/lock
98M /var/log
4.4M /var/mail
4.1k /var/opt
103k /var/run
66k /var/spool
4.1k /var/tmp</pre></code>
<br/>
<h2>Explicación</h2>
El comando <b>du</b> te da un estimado del espacio utilizado por un directorio, la opción <b>-h</b> te imprime los números en potencias de 1024, <b>--sumarize</b> muestra solo el total para cada elemento y <b>/tu/directorio/*</b> corresponde al directorio que deseas analizar. El * al final es la clave para indicarle al comando que queremos ver los subdirectorios.<br/><br/>
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com5tag:blogger.com,1999:blog-6887761988295725170.post-44237105100371270292012-03-10T13:03:00.001-08:002012-03-10T13:03:37.093-08:00Configurar opciones por defecto para el comando gem<div style="text-align: justify;"><span style="font-size:85%;">Cada vez que voy a instalar una gema con el comando <b>gem</b> uso los parámetros <b>--no-rdoc</b> y <b>--no-ri</b> para que no me genere la documentación, principalmente porque nunca la uso y además demora demasiado el tiempo de instalación de las gemas. Pero escribir esos parámetros cada vez que ejecuto el comando es fastidioso.<br/><br/>
Afortunadamente gem busca el archivo de configuración <b>~/.gemrc</b> en el directorio personal del usuario y aplica las opciones que estén definidas, así que creando ese archivo en nuestro home con la siguiente línea no tendremos que tipear las opciones nunca más:<br/><br/>
<code><pre>gem: --no-rdoc --no-ri</pre></code>
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com0tag:blogger.com,1999:blog-6887761988295725170.post-31511394586938921602012-02-14T10:04:00.000-08:002012-02-14T10:04:52.974-08:00Enviar mensajes a través de D-Bus usando Python<div style="text-align: justify;"><span style="font-size:85%;"><a href="http://www.freedesktop.org/wiki/Software/dbus">D-Bus</a> es un sistema que permite la comunicación entre diferentes procesos. Es desarrollado como parte del proyecto <a href="http://www.freedesktop.org/">freedesktop.org</a> buscando ofrecer una solución simple y común para los distintos entornos de escritorio.<br/>
<h1>Conceptos básicos</h1><br/>
<h2>Tipos de Bus</h2>
Hay dos tipos de buses que se pueden usar con D-Bus. El bus de sesión, que se crea con cada sesión de usuario y es local a esa sesión, y el bus de sistema. Este último es global, se inicia cuando arranca el equipo y generalmente se utiliza para comunicarse con procesos como udev, NetworkManager o HAL.
<h2>Rutas a Objetos</h2>
Cada lenguaje de programación tiene sus objetos nativos (usualmente representados por clases). La ruta a un objeto es la forma en que D-Bus permite hacer referencia a un objeto nativo y que las aplicaciones remotas puedan usarlo. Un ruta a un objeto luce como la ruta de un archivo (Unix-like) y es común generarlas como un nombre de dominio en reversa, por ejemplo: /org/gnome/myapp/MyObject. Sin embargo, cada desarrollador puede usar la ruta que mejor le parezca, siempre y cuando sea única.
<h2>Métodos y Señales</h2>
Los métodos son operaciones (con o sin parámetros) que pueden invocarse en un objeto y que eventualmente pueden devolver un resultado. Las señales son notificaciones que se envían al bus y son recibidas por los observadores (objetos que escuchan o se conectan a esas señales). Estas señales también pueden enviar datos de interés para el receptor.
<h2>Objetos Proxy</h2>
Un objeto proxy no es más que un objeto Python que viene a representar a un objeto remoto en otro proceso. Esto nos permite emplear los métodos del objeto remoto como si fueran métodos nativos. Para instanciar un objeto proxy necesitamos la "ruta del objeto".
<h2>Estructura Básica</h2>
Para usar D-Bus es importante entender la estructura e interacciones básicas. En palabras simples, lo que tenemos son dos aplicaciones que se "hablan" entre sí a través de un canal común. Generalmente una de ellas actúa como "servidor", ofreciendo métodos y señales que podrán ser usados por una aplicación "cliente". En la imagen a continuación se ilustra claramente el concepto.<br/><br/>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSw6xY8gvEQLTiXwkKPc3vZdLxrFo1VKHidunFIWxHhhxnUq-owgjtvPD8FeyglvseMquVscIW3OEZtc0pi11hZkOTv9HuCxs7qDRr0ZGs0BSKAlQ0NiU19ZlHnePaEEEKklsSRZkHRIgz/s1600/diagram.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="226" width="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSw6xY8gvEQLTiXwkKPc3vZdLxrFo1VKHidunFIWxHhhxnUq-owgjtvPD8FeyglvseMquVscIW3OEZtc0pi11hZkOTv9HuCxs7qDRr0ZGs0BSKAlQ0NiU19ZlHnePaEEEKklsSRZkHRIgz/s320/diagram.png" /></a></div><br/><br/>
Un ejemplo bastante común es el funcionamiento de un reproductor de música (como el servidor) que ofrece métodos para informar sobre su estado actual y una aplicación de notificaciones (como cliente) que usa estos métodos para mostrar diálogos en el escritorio cada vez que cambia una canción.<br/><br/>
<h1>Aplicación "servidor"</h1><br/>
Lo primero que debemos hacer para empezar a trabajar con D-Bus es conectarnos a un bus. En nuestro caso será al bus de sesión porque no nos interesa interactuar con procesos del sistema operativo.<br/><br/>
Adicionalmente, para ejecutar llamadas asíncronas a los métodos (y que la aplicación no se bloquee mientras espera) debemos configurar un bucle principal. Para el momento de escribir este post, python-dbus solo soporta el bucle principal de GLib, así que usaremos esas librerías para crear nuestro loop.<br/><br/>
Los import que necesitamos para esto serían:
<pre><code>import dbus
import dbus.service
import gobject
from dbus.mainloop.glib import DBusGMainLoop</code></pre><br/>
Luego, definimos (en variable globales) el nombre del bus y la ruta de nuestro objeto:
<pre><code>DBUS_BUSNAME = 'org.example.ExampleDBus'
DBUS_MYOBJECT_PATH = '/org/example/ExampleDBus/MyObject'</code></pre><br/>
Ahora definimos la clase de nuestro server, con la inicialización mínima para que funcione D-Bus:
<pre><code>class DBusService(dbus.service.Object):
def __init__(self):
# Le indicamos a D-Bus que usaremos el loop de GLib como bucle
# predeterminado
DBusGMainLoop(set_as_default=True)
# Establecemos la conexión al bus de sesión
self.session_bus = dbus.SessionBus()
name = dbus.service.BusName(DBUS_BUSNAME, self.session_bus)
# Inicializamos el objeto D-Bus
dbus.service.Object.__init__(self, self.session_bus, DBUS_MYOBJECT_PATH)
# Arrancamos el bucle principal
loop = gobject.MainLoop()
# Colocamos el loop dentro de un try/except para detectar cuando el
# usuario presione Ctrl + C y finalizar la aplicación limpiamente
try:
print "Servicio DBus iniciado"
loop.run()
except KeyboardInterrupt:
loop.quit()
print "Servicio DBus finalizado"</code></pre><br/>
Si se fijan en el código anterior, nuestra clase hereda de <b>dbus.service.Object</b> (porque estamos construyendo un objeto D-Bus que será instanciado por una aplicación remota). En la inicialización le indicamos a D-Bus que usaremos el loop de GLib, establecemos la conexión al bus de sesión y arrancamos el loop.<br/><br/>
He decidido colocar la ejecución del loop principal dentro de un try/except para que el usuario pueda usar Ctrl + C para salir elegantemente de la aplicación.<br/><br/>
Hecho esto podemos proceder a definir un par de métodos. Hagamos un método que imprima un saludo y otro que nos devuelva un valor, en este caso la hora.
<pre><code> @dbus.service.method(DBUS_BUSNAME)
def say_hello(self, name):
print "Hola, %s" % name
@dbus.service.method(DBUS_BUSNAME)
def get_time(self):
return time.strftime("%H:%M")</code></pre><br/>
En los párrafos previos comenté que un objeto proxy no es más que una representación de un objeto D-Bus en un objeto Python, así que para exportar un método nativo como método D-Bus usamos el decorador <b>@dbus.service.method</b> y le pasamos como parámetro el nombre del bus. Luego definimos nuestras funciones como lo haríamos normalmente en cualquier clase de Python y ya con esto tendríamos un servidor muy básico listo para funcionar.<br/><br/>
El código completo debería quedar así:
<pre><code>#!/usr/bin/python
# -*- coding: utf-8 -*-
import time
import dbus
import gobject
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop
DBUS_BUSNAME = 'org.example.ExampleDBus'
DBUS_MYOBJECT_PATH = '/org/example/ExampleDBus/MyObject'
class DBusService(dbus.service.Object):
def __init__(self):
# Le indicamos a D-Bus que usaremos el loop de GLib como bucle
# predeterminado
DBusGMainLoop(set_as_default=True)
# Establecemos la conexión al bus de sesión
self.session_bus = dbus.SessionBus()
name = dbus.service.BusName(DBUS_BUSNAME, self.session_bus)
# Inicializamos el objeto D-Bus
dbus.service.Object.__init__(self, self.session_bus, DBUS_MYOBJECT_PATH)
# Arrancamos el bucle principal
loop = gobject.MainLoop()
# Colocamos el loop dentro de un try/except para detectar cuando el
# usuario presione Ctrl + C y finalizar la aplicación limpiamente
try:
print "Servicio DBus iniciado"
loop.run()
except KeyboardInterrupt:
loop.quit()
print "Servicio DBus finalizado"
@dbus.service.method(DBUS_BUSNAME)
def say_hello(self, name):
print "Hola, %s" % name
@dbus.service.method(DBUS_BUSNAME)
def get_time(self):
return time.strftime("%H:%M")
@dbus.service.signal(DBUS_BUSNAME)
def kill(self):
return 'killed'
if __name__ == '__main__':
service = DBusService()
</code></pre><br/>
<h1>Aplicación "cliente"</h1>
La aplicación cliente es mucho más simple. La única librería que necesitamos es la de D-Bus, el nombre del bus y la ruta del objeto.
<pre><code>import dbus
DBUS_BUSNAME = 'org.example.ExampleDBus'
DBUS_MYOBJECT_PATH = '/org/example/ExampleDBus/MyObject'</code></pre><br/>
Ahora pasemos a crear la clase. Al igual que en el servidor, necesitamos establecer la conexión con el bus y "mapear" el objeto D-Bus en un objeto Python. En este caso la variable <b>self.my_object</b> es la que contiene la representación de dicho objeto.
<pre><code>class DBusClient:
def __init__(self):
self.session_bus = dbus.SessionBus()
self.my_object = self.session_bus.get_object(DBUS_BUSNAME,
DBUS_MYOBJECT_PATH)</code></pre><br/>
Para llamar a un método del objeto remoto debemos importar el método correspondiente, y como estaremos usando varios métodos vamos a crear una función interna que nos ayude con esta tarea.
<pre><code> def __get_dbus_method(self, name):
return self.my_object.get_dbus_method(name)</code></pre><br/>
Procedemos a crear nuestros métodos nativos que obtendrán el método remoto del objeto D-Bus y lo llamarán con los parámetros correspondientes según sea el caso.
<pre><code> def say_hello(self, name):
# Almacenamos el método en una variable y luego lo llamamos con los
# parámetros correspondientes
method = self.__get_dbus_method('say_hello')
method(name)
def print_time(self):
method = self.__get_dbus_method('get_time')
# Obtenemos el valor de retorno del método y la imprimimos en pantalla
current_time = method()
print "Son las %s" % current_time</code></pre><br/>
Con eso ya deberíamos tener un cliente funcional. El código completo del cliente debería quedar algo como:
<pre><code>#!/usr/bin/python
# -*- coding: utf-8 -*-
import dbus
DBUS_BUSNAME = 'org.example.ExampleDBus'
DBUS_MYOBJECT_PATH = '/org/example/ExampleDBus/MyObject'
class DBusClient:
def __init__(self):
self.session_bus = dbus.SessionBus()
self.my_object = self.session_bus.get_object(DBUS_BUSNAME,
DBUS_MYOBJECT_PATH)
def __get_dbus_method(self, name):
return self.my_object.get_dbus_method(name)
def say_hello(self, name):
# Almacenamos el método en una variable y luego lo llamamos con los
# parámetros correspondientes
method = self.__get_dbus_method('say_hello')
method(name)
def print_time(self):
method = self.__get_dbus_method('get_time')
# Obtenemos el valor de retorno del método y la imprimimos en pantalla
current_time = method()
print "Son las %s" % current_time
if __name__ == '__main__':
client = DBusClient()
client.say_hello('Pedro')
client.say_hello('Maria')
client.print_time()</code></pre>
Es importante que observe que al ejecutar el script del cliente se llamará al método <b>say_hello</b> dos veces (con los parámetros 'Pedro' y 'Maria' respectivamente) y luego al método <b>print_time</b>.<br/><br/>
<h1>Pruebas</h1>
Para probar abrimos dos terminales. En la primera ejecutamos el script del servidor (yo lo llamé dbus_service.py) y veremos algo como:
<pre><code>$ python dbus_service.py
Servicio DBus iniciado</code></pre>
Con un mensaje indicándonos que el servicio está corriendo y esperando ser utilizado.<br/><br/>
En la segunda terminal ejecutamos el script del cliente (lo llamé dbus_client.py) y veremos algo como:
<pre><code>$ python dbus_client.py
Son las 12:55</code></pre>
Muy bien, imprimió la hora pero... ¿No se suponía que también habíamos llamado al método <b>say_hello</b> dos veces? ¿Qué pasó?<br/><br/>
Bueno, volvamos a ver la terminal del servicio y veremos nuestro ansiado resultado:
<pre><code>$ python dbus_service.py
Servicio DBus iniciado
Hola, Pedro
Hola, Maria</code></pre><br/>
Tal como lo esperabamos, el método <b>say_hello</b> imprime en la instancia del servidor el nombre envíado desde el cliente y por otro lado, el cliente imprime la hora que le devuelve el método <b>get_time</b> desde el servidor. Hemos enviado mensajes en ambas direcciones usando D-Bus, ¡todo un éxito!<br/><br/>
Espero que esta receta simple les haya ayudado a comprender como funciona D-Bus y como usarlo desde Python. Más adelante estaré escribiendo otros posts sobre cómo emitir/recibir señales y cómo enviar grandes cantidades de datos "al vuelo" a través de D-Bus.<br/><br/>
Fuentes:
<ul>
<li><a href="http://es.wikipedia.org/wiki/D-Bus">http://es.wikipedia.org/wiki/D-Bus</a></li>
<li><a href="http://dbus.freedesktop.org/doc/dbus-tutorial.html">http://dbus.freedesktop.org/doc/dbus-tutorial.html</a></li>
<li><a href="http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html">http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html</a></li>
<li><a href="http://dbus.freedesktop.org/doc/dbus-specification.html">http://dbus.freedesktop.org/doc/dbus-specification.html</a></li>
</ul>
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com1tag:blogger.com,1999:blog-6887761988295725170.post-34376862780647426882012-02-01T13:28:00.000-08:002012-02-01T13:28:52.927-08:00Mostrar el log de ActiveRecord en la consola de Rails<div style="text-align: justify;"><span style="font-size:85%;">Si usualmente utilizas la consola interactiva de Ruby On Rails para realizar pruebas, correr tareas de Rake, entre otras cosas y te resultaría sumamente útil ver el log de lo que esta haciendo el ActiveRecord, entonces este tip es para ti.<br/><br/>
Simplemente ejecuta la consola desde la carpeta raíz de tu aplicación Rails:<br/><br/>
Para rails 3.x:<br/>
<code>$ rails c</code><br/>
Para rails 2.x:<br/>
<code>$ script/console</code><br/>
Y luego indícale al logger que use la salida estándar:<br/>
<code>ActiveRecord::Base.logger = Logger.new(STDOUT)</code><br/>
Con eso los mensajes de log del ActiveRecord se imprimirán en la consola.
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com0tag:blogger.com,1999:blog-6887761988295725170.post-34063331179393066282012-01-29T08:01:00.000-08:002012-01-29T08:01:59.484-08:00Implementar un Singleton en Python<div style="text-align: justify;"><span style="font-size:85%;">
<a href="http://en.wikipedia.org/wiki/Singleton_pattern">Singleton</a> es un patrón de diseño cuya función es evitar que un objeto pueda ser instanciado más de una vez. En este post traigo una receta simple para implementar el patrón singleton en Python.
<br/><br/>
En esta implementación utilizaremos un archivo de bloqueo (lock file) para indicar si la aplicación está en ejecución o no. Python cuenta con el módulo <a href="http://docs.python.org/library/fcntl.html">fcntl</a> que nos proporciona una interfaz bastante cómoda para el control de archivos pero está disponible solo para Linux/Unix, eso implica que debemos usar medidas alternativas para Windows.
<br/><br/>
<h2>Detectar SO e importar módulos</h2>
Lo primero que debemos hacer es detectar el sistema operativo y ejecutar los import correspondientes para cada caso. Usaremos además el módulo <b>tempfile</b> para generar el lock file como un archivo temporal del sistema.
<pre><code>#!/usr/bin/python2
# -*- coding: utf-8 -*-
import os
import sys
import tempfile
OS = None
if sys.platform.startswith('linux'):
OS = 'linux'
import fcntl
elif sys.platform.startswith('win32'):
OS = 'windows'
</code></pre>
<br/><br/>
<h2>Definir la clase Singleton</h2>
Después de detectar el sistema operativo definimos la clase <b>Singleton</b>. Básicamente, esta clase será la encargada de crear el lock file al inicio o generar una advertencia y termina la ejecución en caso de que el archivo ya exista (es decir, que ya existe una instancia de la aplicación en ejecución).
<pre><code>class Singleton:
def __init__(self):
# Variable para almacenar el file descriptor
self.fd = None
# Ruta para el lock file en la carpeta temporal del sistema
self.filepath = os.path.abspath(os.path.join(tempfile.gettempdir(),
'myapp.pid'))
if OS == 'linux':
# Para el caso de linux usamos el módulo fcntl para crear el archivo
# y bloquearlo automáticamente. Si la operación falla es porque el
# archivo ya existe y está bloqueado.
self.fd = open(self.filepath, 'w')
try:
fcntl.lockf(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
self.__exit()
elif OS == 'windows':
try:
# Para el caso windows simplemente creamos el archivo "a mano",
# pero verificamos primero si el archivo existe e intentamos
# removerlo (para casos en que la ejecución previa haya sido
# interrumpida)
if os.path.exists(self.filepath):
os.unlink(self.filepath)
self.fd = os.open(self.filepath, os.O_CREAT|os.O_EXCL|os.O_RDWR)
except OSError, err:
if err.errno == 13:
self.__exit()
def __del__(self):
# Para el caso de windows también debemos destruir el archivo "a mano"
# al finalizar la ejecución del programa.
if OS == 'windows':
if self.fd:
os.close(self.fd)
os.unlink(self.filepath)
def __exit(self):
print 'Ya hay una instancia en ejecución. Saliendo'
sys.exit(-1)
</code></pre>
En el __init__ se observa que creamos una variable para almacenar el file descriptor (fd), luego generamos una ruta para el lock file. Posteriormente creamos un lock file con el módulo fcntl (en el caso linux) o creamos un archivo regular (para el caso windows). Adicionalmente, en el caso windows necesitamos hacernos cargo del archivo al finalizar la ejecución, para eso sobreescribimos el método __del__ y colocamos nuestro código. Adicionalmente tenemos la función __exit(), que es la encargada de detener la ejecución del programa de forma elegante.
<br/><br/>
<h2>Clase de pruebas</h2>
Con los pasos anteriores tenemos lista nuestra implementación simple del patrón singleton. Ahora, ¿Cómo la usamos? Creamos una clase (por ejemplo MyApp) que herede de singleton y ponemos un bucle infinito para que se mantenga haciendo "algo".
<pre><code>class MyApp(Singleton):
def __init__(self):
Singleton.__init__(self)
print 'Ejecutando MyApp'
# Creamos un bucle infinito solo para mantener la aplicación en
# ejecución
while 1:
continue
</code></pre>
<br/><br/>
<h2>¿Cómo se vería nuestro script?</h2>
El código completo de nuestro script se vería así:
<pre><code>#!/usr/bin/python2
# -*- coding: utf-8 -*-
import os
import sys
import tempfile
OS = None
if sys.platform.startswith('linux'):
OS = 'linux'
import fcntl
elif sys.platform.startswith('win32'):
OS = 'windows'
class Singleton:
def __init__(self):
# Variable para almacenar el file descriptor
self.fd = None
# Ruta para el lock file en la carpeta temporal del sistema
self.filepath = os.path.abspath(os.path.join(tempfile.gettempdir(),
'myapp.pid'))
if OS == 'linux':
# Para el caso de linux usamos el módulo fcntl para crear el archivo
# y bloquearlo automáticamente. Si la operación falla es porque el
# archivo ya existe y está bloqueado.
self.fd = open(self.filepath, 'w')
try:
fcntl.lockf(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
self.__exit()
elif OS == 'windows':
try:
# Para el caso windows simplemente creamos el archivo "a mano",
# pero verificamos primero si el archivo existe e intentamos
# removerlo (para casos en que la ejecución previa haya sido
# interrumpida)
if os.path.exists(self.filepath):
os.unlink(self.filepath)
self.fd = os.open(self.filepath, os.O_CREAT|os.O_EXCL|os.O_RDWR)
except OSError, err:
if err.errno == 13:
self.__exit()
def __del__(self):
# Para el caso de windows también debemos destruir el archivo "a mano"
# al finalizar la ejecución del programa.
if OS == 'windows':
if self.fd:
os.close(self.fd)
os.unlink(self.filepath)
def __exit(self):
print 'Ya hay una instancia en ejecución. Saliendo'
sys.exit(-1)
class MyApp(Singleton):
def __init__(self):
Singleton.__init__(self)
print 'Ejecutando MyApp'
# Creamos un bucle infinito solo para mantener la aplicación en
# ejecución
while 1:
continue
if __name__ == '__main__':
app = MyApp()
</code></pre>
<br/><br/>
<h2>Probando el singleton</h2>
Para probarlo abrimos un terminal, nos colocamos en la carpeta donde esté ubicado nuestro script y lo ejecutamos por primera vez. Eso nos dará como resultado algo como:
<pre><code>$ python myapp.py
Ejecutando MyApp
</code></pre>
<br/><br/>
Abrimos una terminal nueva (sin cerrar la terminal anterior) e intentamos ejecutar la aplicación por segunda vez. Eso nos devolverá:
<pre><code>$ python myapp.py
Ya hay una instancia en ejecución. Saliendo
</code></pre>
¡Y voilá! Logramos que un script de Python pueda ser ejecutado una sola vez.
<br/><br/>
Hay implementaciones más complejas que almacenan el ID del proceso dentro del lock file y cada vez que se intenta ejecutar una nueva instancia se lee el ID y se verifica que realmente exista un proceso en ejecución con ese identificador. Pero como dije al principio, esta es una receta simple, así que as implementaciones más complejas las dejamos como tareas para el lector ;)
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com1tag:blogger.com,1999:blog-6887761988295725170.post-44649933118465583372012-01-28T08:32:00.000-08:002012-01-28T15:05:01.565-08:00Mi propuesta para Canaima-Instalador<div style="text-align: justify;">
<span style="font-size: 85%;">Actualmente estoy colaborando con el desarrollo de <a href="https://gitorious.org/canaima-gnu-linux/canaima-instalador">canaima-instalador</a>, una aplicación para <a href="http://canaima.softwarelibre.gob.ve/">Canaima GNU/Linux</a> que permitirá al usuario instalar/probar Canaima de una manera fácil e intuitiva, y he decidido hacer una propuesta sobre cómo debe lucir y funcionar el nuevo instalador. La propuesta a continuación.<br /><br />
<h2>Paso 1: LiveCD</h2>
Lo primero que hace falta cambiar es el menú de inicio del liveCD. Actualmente tenemos una pantalla llena de opciones que, más allá de facilitar el uso, pueden confundir a los usuarios menos experimentados. Mi sugerencia es que el liveCD arranque automáticamente y presente un menú inferior con las posibles opciones para usuarios más experimentados.<br /><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDk_JzJOEWp16JZY5cNvfEZMAXM8qnXDvUBjIqrdgsO1XgspapR4uuunsBNmmfXIyiwK35gbI2Gf6D7NcZ3vG83Mejl-1CBY8-cFURMNDOMlOe7emZEh-xKkrX6EATW5qFle1EjiOV9i8j/s1600/step1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDk_JzJOEWp16JZY5cNvfEZMAXM8qnXDvUBjIqrdgsO1XgspapR4uuunsBNmmfXIyiwK35gbI2Gf6D7NcZ3vG83Mejl-1CBY8-cFURMNDOMlOe7emZEh-xKkrX6EATW5qFle1EjiOV9i8j/s400/step1.png" width="400" /></a></div><br />
<h2>Paso 2: Bienvenida</h2>
Aquí es donde comienza a ejecutarse canaima-instalador. La idea es que sea tipo OEM, es decir, que no cargue el escritorio sino lo mínimo necesario para ejecutarse. Se le preguntará al usuario si desea probar la distribución o instalar. Para el primer caso se cierra canaima-instalador y se continúa con la carga del escritorio, para el segundo caso se sigue el flujo normal de la aplicación.<br /><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjDEiObKIgTPolQhE4br44flm4ac6fh_L1BXNpgWlU68q3mGP9UaV-UtGqnGeRdy5EBwGxEj6lk1m334iihKnqmv7v0w9ERuu4VyLRnikCIMxu-DEqyTG_x1MbnopWrNzlkTG08rEesMl-/s1600/step2.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="300" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjDEiObKIgTPolQhE4br44flm4ac6fh_L1BXNpgWlU68q3mGP9UaV-UtGqnGeRdy5EBwGxEj6lk1m334iihKnqmv7v0w9ERuu4VyLRnikCIMxu-DEqyTG_x1MbnopWrNzlkTG08rEesMl-/s400/step2.png" /></a></div><br />
<h2>Paso 3: Requisitos</h2>
Se le muestra al usuario cuáles son los requisitos mínimos necesarios para obtener mejores resultados al instalar Canaima. En caso de que no se cumpla con un requisito crítico (por ejemplo, el espacio en disco) la instalación no debería continuar.<br /><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfSJ4MQ07XsdmvVZuAZEW8ACbxGi9-Iim_nJIjAm7ZAosZdwmnMzBu2jL0weGsWjAJHh0WkuuVtGdDPEVQ_tCfTFfP50Tm14CZinevahdiLPvoOdQ8VovtOIki8meD4ksu52pbUu9e6DiV/s1600/step3.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="300" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfSJ4MQ07XsdmvVZuAZEW8ACbxGi9-Iim_nJIjAm7ZAosZdwmnMzBu2jL0weGsWjAJHh0WkuuVtGdDPEVQ_tCfTFfP50Tm14CZinevahdiLPvoOdQ8VovtOIki8meD4ksu52pbUu9e6DiV/s400/step3.png" /></a></div><br />
<h2>Paso 4: Modo de instalación</h2>
Acá (luego de haber analizado el hardware) se le presentan al usuario las distintas opciones que tiene para instalar. En teoría todas deberían ser automáticas excepto la de "Particionamiento Avanzado" que ejecutará una instancia de gparted y bloqueará canaima-instalador hasta que el usuario termine de definir sus particiones. Una vez que el usuario haga clic en "Siguiente" no podrá volver atrás. Acá termina la primera fase de instalación y comienza el copiado de los archivos en el disco duro en segundo plano. La idea es aprovechar el tiempo, mientras se copian los archivos solicitamos al usuario el resto de la información y una vez terminada la copia se ejecutan las tareas correspondientes. De esta forma logramos minimizar considerablemente el tiempo de instalación (tal como lo hace Ubuntu).<br /><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkKlz-AYuVp-HhqK-FoavrtXjylPClUh8qMAa7dwKhM9HRkXjwItlwp7BsMsfX1B18RdkpJRgKBGJ4wUG_cF0R4tD1EXFi1Co6SDhurr3db0iUCum3f4LjWzlb1G1CgtswBxr8Cmj7m-U6/s1600/step4.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="300" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkKlz-AYuVp-HhqK-FoavrtXjylPClUh8qMAa7dwKhM9HRkXjwItlwp7BsMsfX1B18RdkpJRgKBGJ4wUG_cF0R4tD1EXFi1Co6SDhurr3db0iUCum3f4LjWzlb1G1CgtswBxr8Cmj7m-U6/s400/step4.png" /></a></div><br />
<h2>Paso 5: Configuración de teclado</h2>
Como expliqué en el punto anterior, mientras se copian los archivos en segundo plano solicitamos el resto de la información al usuario. Acá se le pedirá que seleccione la distribución del teclado. Es importante observar que el botón de "Anterior" no estará disponible en este paso.<br /><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgghS_SYutFSmiVPwcP3T11Uif5K_PWsVDOcIsOEkcAot8PtBedBRygBXkkeynqsm7PEcw3oYueenvod_tLvkJ7C4jfDN2g9WniJKa71EzJi4aPp1BWDw3iVg2Pd1Ngh1gull1BAKK4HT2i/s1600/step6.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="300" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgghS_SYutFSmiVPwcP3T11Uif5K_PWsVDOcIsOEkcAot8PtBedBRygBXkkeynqsm7PEcw3oYueenvod_tLvkJ7C4jfDN2g9WniJKa71EzJi4aPp1BWDw3iVg2Pd1Ngh1gull1BAKK4HT2i/s400/step6.png" /></a></div><br />
<h2>Paso 6: Configuración de usuarios</h2>
En esta fase se le pedirán los datos de las cuentas (root y usuario regular). Es importante habilitar un link (o botón) de ayuda donde se pueda explicar qué significa root y por qué es importante definir esa contraseña. Es importante recalcar que si el usuario no ha terminado de introducir la información complementaria y la copia de archivos finaliza entonces las tareas en segundo plano se detendrán hasta que se culmine esta fase.<br /><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6YZaXIECv3V0uvn2DqO3EkkCjb6lGVYrt6ZoxWhBNJMFHpU6wG_r8O4NJxawJeAy3JHQn0KHzA9-5ViHPyKWcaXQMiKGhj_IB2fWSfBw_pvIxgCnL9ZQ7V-KTgsLDRQ4y6iWkHdKRYBez/s1600/step6.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="300" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6YZaXIECv3V0uvn2DqO3EkkCjb6lGVYrt6ZoxWhBNJMFHpU6wG_r8O4NJxawJeAy3JHQn0KHzA9-5ViHPyKWcaXQMiKGhj_IB2fWSfBw_pvIxgCnL9ZQ7V-KTgsLDRQ4y6iWkHdKRYBez/s400/step6.png" /></a></div><br />
<h2>Paso 7: Imágenes aleatorias sobre las bondades de Canaima</h2>
Al igual que Ubuntu y otros sistemas operativos, podemos mostrar una serie de imágenes aleatorias que informen al usuario sobre las bondades de Canaima GNU/Linux y del Software Libre mientras termina la instalación.<br /><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhU6xdKRY22XzeRHVe0Nh0QMWDGzzwgi3A4Rgf3BdeoTtbkEMxJdj7QNwQfGBZtaM1CcBo7IcL_vPGqhgrhbNNRCyjLOEu8Ggru4aerQXT4IU46ssza2lyPl73Q9yJmrQcVYZCQD6uvQIE7/s1600/step7.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="300" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhU6xdKRY22XzeRHVe0Nh0QMWDGzzwgi3A4Rgf3BdeoTtbkEMxJdj7QNwQfGBZtaM1CcBo7IcL_vPGqhgrhbNNRCyjLOEu8Ggru4aerQXT4IU46ssza2lyPl73Q9yJmrQcVYZCQD6uvQIE7/s400/step7.png" /></a></div><br />
<h2>Paso 8: Fin de la instalación</h2>
Al finalizar todo exitosamente se le mostrará un pequeño diálogo al usuario para que reinicie el equipo y comience a disfrutar de las bondandes del SL.<br /><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgry7VXkfkpOimlihYu45snIcrQ99u_t8HBmwjU8MlgpKLaFA1fjZQO1avaRQqYkIKbRLy-BtsFqmv5VFlWn2cNSTbYYMWlNdu26-B2_Jas31dcyq_51MR6e7MgWfxKjmOs98AWOFPoAol-/s1600/step8.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="300" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgry7VXkfkpOimlihYu45snIcrQ99u_t8HBmwjU8MlgpKLaFA1fjZQO1avaRQqYkIKbRLy-BtsFqmv5VFlWn2cNSTbYYMWlNdu26-B2_Jas31dcyq_51MR6e7MgWfxKjmOs98AWOFPoAol-/s400/step8.png" /></a></div><br /><br />
Bueno, esta es mi idea de lo que debería ser un instalador fácil e intuitivo para Canaima GNU/Linux. Se escuchan comentarios
Update para los haters: Sí, mi propuesta está basada casi enteramente en el instalador de Ubuntu porque me parece un excelente instalador ¿Cuál es el problema con eso?
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com8tag:blogger.com,1999:blog-6887761988295725170.post-73197684674892781672011-10-31T17:00:00.000-07:002012-09-19T08:55:01.939-07:00Configurar el Touchpad Synaptics de una HP Pavilion dm4 en Debian<div style="text-align: justify;"><span style="font-size:85%;">
En un artículo anterior expliqué <a href="http://damncorner.blogspot.com/2011/10/configurar-video-intel-i915-kms-en.html">cómo configurar el video Intel i915 con KMS en una HP Pavilion dm4</a>. Esta vez explicaré cómo configurar el Touchpad Synaptics de esa misma laptop.<br/><br/>
<h2>Leer el manual</h2>
Lo primero que debes hacer si deseas configurar correctamente el Touchpad es leer el manual (<a href="http://es.wikipedia.org/wiki/RTFM">RTFM</a>).
<code><pre>$ man synaptics</pre></code>
<br/>
Yo configuré el touchpad a mi gusto, así que puedes probarlo y si no te gusta lo adaptas al tuyo. Sin embargo es una tarea de ensayo y error que no podrás completar a menos que conozcas todas las opciones que tienes disponibles.<br/><br/>
<h2>Dependencias</h2>
Lo único que necesitamos es un Xorg bien configurado y los drivers synaptics. Para instalar los drivers ejecutamos:<br/><br/>
En Debian:
<code><pre># aptitude install xserver-xorg-input-synaptics</pre></code><br/><br/>
<h2>Entender cómo funciona el Touchpad</h2>
Básicamente no es un Touchpad, es un ClickPad y eso lo hace un poco especial. ¿Diferencias? Pues la superficie táctil también funciona como botón, que actua como uno u otro (left, right) dependiendo de la zona que se presione. No tenemos un área táctil con unos botones separados, tenemos con todo incluido, eso debe quedar <b>muy</b> claro.<br/><br/>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggLNgDzfly-oKbfCCHb_BMyqmKXtyLijHC_0ZJgq9jkk4aoeFmYmF_-8Pn-VLmBkRznDRz08y-rKUnJJOY7pdfRHhjzUczLJn32NxI5-7o8GUsvh3LmXP-1rxW6Tt1USMHbZ3gzcH56RSn/s1600/touchpad.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="240" width="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggLNgDzfly-oKbfCCHb_BMyqmKXtyLijHC_0ZJgq9jkk4aoeFmYmF_-8Pn-VLmBkRznDRz08y-rKUnJJOY7pdfRHhjzUczLJn32NxI5-7o8GUsvh3LmXP-1rxW6Tt1USMHbZ3gzcH56RSn/s320/touchpad.png" /></a></div><br/><br/>
<h2>Configuración</h2>
Xorg provee una serie de plantillas predeterminadas para la configuración de dispositivos. Estas plantillas se encuentran en <b>/usr/share/X11/xorg.conf.d/</b> y basta con copiar a <b>/etc/X11/xorg.conf.d/</b> la que necesitemos y comenzar a modificarla. Xorg se encargará de cargar estos valores luego de los valores predeterminados. Para nuestro caso usaremos la plantilla <b>50-synaptics.conf</b>.<br/><br/>
<code><pre># mkdir /etc/X11/xorg.conf.d/
# cp /usr/share/X11/xorg.conf.d/50-synaptics.conf /etc/X11/xorg.conf.d/</pre></code><br/>
Lo que haremos será modificar el <b>InputClass</b> para agregar los parámetros que deseamos configurar. En el manual de synaptics tenemos bien documentados cada una de las opciones con su descripción y posibles valores. <br/><br/>
Mi configuración quedó así:<br/>
<code><pre>Section "InputClass"
Identifier "touchpad catchall"
Driver "synaptics"
MatchIsTouchpad "on"
MatchDevicePath "/dev/input/event*"
Option "Protocol" "auto-dev"
Option "SHMConfig" "true"
# Edges
Option "LeftEdge" "1500"
Option "RightEdge" "5200"
Option "TopEdge" "1350"
Option "BottomEdge" "3500"
# Finger press
Option "FingerLow" "28"
Option "FingerHigh" "35"
Option "FingerPress" "70"
# Taps time
Option "MaxTapTime" "180"
Option "MaxTapMove" "220"
Option "MaxDoubleTapTime" "180"
Option "SingleTapTimeout" "180"
Option "ClickTime" "100"
Option "FastTaps" "0"
# Emulate
Option "EmulateMidButtonTime" "75"
Option "EmulateTwoFingerMinZ" "70"
Option "EmulateTwoFingerMinW" "5"
# Scrolling
Option "VertScrollDelta" "100"
Option "HorizScrollDelta" "0"
Option "VertEdgeScroll" "1"
Option "HorizEdgeScroll" "0"
Option "CornerCoasting" "0"
Option "VertTwoFingerScroll" "1"
Option "HorizTwoFingerScroll" "1"
# Pointer speed
Option "MinSpeed" "0.5"
Option "MaxSpeed" "7.0"
Option "AccelFactor" "0.35"
Option "TrackstickSpeed" "0"
Option "EdgeMotionMinZ" "29"
Option "EdgeMotionMaxZ" "59"
Option "EdgeMotionMinSpeed" "1"
Option "EdgeMotionMaxSpeed" "40"
Option "EdgeMotionUseAlways" "0"
# Scrolling flags
Option "UpDownScrolling" "0"
Option "LeftRightScrolling" "0"
Option "UpDownScrollRepeat" "0"
Option "LeftRightScrollRepeat" "0"
Option "ScrollButtonRepeat" "100"
# Touchpad mouse on/off
Option "TouchpadOff" "0"
Option "GuestMouseOff" "0"
# Dragging
Option "LockedDrags" "0"
Option "LockedDragsTimeout" "5000"
# Corners
Option "RTCornerButton" "0"
Option "RBCornerButton" "2"
Option "LTCornerButton" "0"
Option "LBCornerButton" "1"
# Tap
Option "TapButton1" "1"
Option "TapButton2" "3"
Option "TapButton3" "2"
# Click
Option "ClickFinger1" "1"
Option "ClickFinger2" "0"
Option "ClickFinger3" "0"
# Circular
Option "CircularScrolling" "0"
Option "CircScrollDelta" "0.1"
Option "CircScrollTrigger" "0"
Option "CircularPad" "0"
# Palm
Option "PalmDetect" "1"
Option "PalmMinWidth" "7"
Option "PalmMinZ" "40"
Option "CoastingSpeed" "0"
# Grab
Option "GrabEventDevice" "1"
Option "TapAndDragGesture" "1"
# Area
Option "AreaLeftEdge" "0"
Option "AreaRightEdge" "0"
Option "AreaTopEdge" "0"
Option "AreaBottomEdge" "3500"
EndSection
Section "InputClass"
Identifier "Ignore mouse devs"
MatchDevicePath "/dev/input/mouse*"
Driver "synaptics"
Option "Ignore" "on"
EndSection
</pre></code>
<br/>
<b>Actualización</b>: Acá pueden ver la configuración actual que tengo en mi <a href="https://github.com/satanas/hp-dm4-config/blob/master/10-synaptics.conf">repo github</a><br/><br/>
No explicaré en detalle cada una de las opciones (para eso está el manual de synaptics), solo explicaré las más relevantes o las "problemáticas".
Lo primero es ignorar las pulsaciones en el área de los botones. Para eso definimos el rango de acción del dispositivo (límites):
<code><pre>
# Edges
Option "LeftEdge" "1500"
Option "RightEdge" "5200"
Option "TopEdge" "1350"
Option "BottomEdge" "3500"
# Area
Option "AreaLeftEdge" "0"
Option "AreaRightEdge" "0"
Option "AreaTopEdge" "0"
Option "AreaBottomEdge" "3500"
</pre></code>
<br/>
Colocando el límite inferior (BottomEdge y AreaBottomEdge) en 3500 evitamos que el cursor se mueva al pasar sobre el área de botones. Estableciendo el límite derecho (RightEdge) a 5200 evitamos que el cursor se mueva en la columna derecha (para habilitar la zona de scrolling vertical). La imagen a continuación ilustra estos ajustes:<br/><br/>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg20nf03Bllelp9MS5s8xkfL83lMr-4cjFPopWeRahLznGdYmwHlRarZiqrJrj8Dx0EPfI5x5Zp7TAq2JA_3cuQAUdmySWrmoOCvBswf6_9Ju3V1VaJZoaFxLcGc8AOegwc239h8hwZ-KD3/s1600/touchpad-grid.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="240" width="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg20nf03Bllelp9MS5s8xkfL83lMr-4cjFPopWeRahLznGdYmwHlRarZiqrJrj8Dx0EPfI5x5Zp7TAq2JA_3cuQAUdmySWrmoOCvBswf6_9Ju3V1VaJZoaFxLcGc8AOegwc239h8hwZ-KD3/s320/touchpad-grid.png" /></a></div><br/><br/>
El dispositivo solo tiene un botón y a simple vista no encontré la forma de configurarlo para que funcione como ambos. Estuve <a href="http://vejeta.com/portal/Members/zoperman/chuletas/gnu/soporte-para-los-clickpad-en-hp-pavillion-dv7-4160es">compilando el driver synaptics</a> con un <a href="http://www.spinics.net/lists/linux-input/msg08198.html">patch para agregar el soporte de clickpads</a> pero no he logrado generar el .ko, así que simplemente dejé su funcionamiento por defecto (left click) y configuré el click izquierdo con tap, el doble click con doble tap y el click derecho con tap de dos dedos. <br/><br/>
Eso podemos verlo en:
<code><pre>
# Tap
Option "TapButton1" "1"
Option "TapButton2" "3"
Option "TapButton3" "2"
</pre></code>
<br/>
El botón para activar/desactivar el clickpad tampoco pude hacerlo funcionar pero creo que es cuestión de seguir trasteando hasta lograr una solución. Además de eso, el resto de la configuración es el ajuste de valores para la sensibilidad, aceleración y velocidad del puntero, pero eso lo dejo como tarea para el lector.<br/><br/>
<h2>Tarea para la casa</h2>
Una de las herramientas más importantes para lograr una efectiva configuración es <b>synclient</b>. Gracias a esta herramienta pude determinar las coordenadas que definen los límites de mi dispositivo (que en las imágenes de arriba están marcados con azul y rojo) y verificar los valores de configuración <br/><br/>
Usando el siguiente comando podemos verificar la actividad del touchpad en vivo:
<code><pre>$ synclient -m 10</pre></code><br/>
La salida debería ser algo como:
<code><pre> time x y z f w l r u d m multi gl gm gr gdx gdy
874.169 5392 4317 3 0 0 0 0 0 0 0 00000000
874.269 5392 4317 0 0 0 0 0 0 0 0 00000000
874.670 5392 4317 1 0 0 0 0 0 0 0 00000000
874.770 5392 4317 7 0 0 0 0 0 0 0 00000000
874.870 4143 1708 9 1 11 0 0 0 0 0 00000000
874.970 4225 1811 3 0 0 0 0 0 0 0 00000000
875.070 4225 1811 2 0 0 0 0 0 0 0 00000000</pre></code>
<br/>
Donde:
<ul>
<li><b>x, y</b>: definen las coordenadas de la pulsación</li>
<li><b>z</b>: la presión aplicada</li>
<li><b>f</b>: el número de dedos tocando la superficie</li>
<li><b>w</b>: es una medida del ancho del dedo</li>
<li><b>l,r,m</b>: el estado de los botones left, right y middle</li>
</ul>
<br/>
Para más información:
<code><pre>$ man synclient</pre></code>
<br/>
Espero que con esta guía puedas configurar tu ClickPad y usarlo decentemente. Me queda pendiente por investigar la función de deshabilitar el touchpad mientras se escribe, los botones left-right y el botón para activar. Cuando lo logré postearé nuevamente :)<br/><br/>
Fuentes: <br/>
<ul>
<li><a href="http://wiki.debian.org/SynapticsTouchpad">http://wiki.debian.org/SynapticsTouchpad</a></li>
<li><a href="https://wiki.archlinux.org/index.php/Touchpad_Synaptics">https://wiki.archlinux.org/index.php/Touchpad_Synaptics</a></li>
<li><a href="http://sansmicrosoft.blogspot.com/2010/10/pavilion-dm4-1160-touchpad-erratic-bis.html">http://sansmicrosoft.blogspot.com/2010/10/pavilion-dm4-1160-touchpad-erratic-bis.html</a></li>
<li><a href="https://bugs.archlinux.org/task/20830">https://bugs.archlinux.org/task/20830</a></li>
<li><a href="http://who-t.blogspot.com/2010/11/how-to-ignore-configuration-errors.html">http://who-t.blogspot.com/2010/11/how-to-ignore-configuration-errors.html</a></li>
<li><a href="http://askubuntu.com/questions/15208/touchpad-issues-on-hp-pavilion-dm4-cant-right-click">http://askubuntu.com/questions/15208/touchpad-issues-on-hp-pavilion-dm4-cant-right-click</a></li>
</ul>
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com6tag:blogger.com,1999:blog-6887761988295725170.post-65066907588201223732011-10-28T12:23:00.000-07:002011-10-28T12:23:54.185-07:00Configurar video Intel i915 + KMS en Debian Wheezy<div style="text-align: justify;"><span style="font-size:85%;">
Es esta receta explicaré como configurar y activar el <a href="http://en.wikipedia.org/wiki/Mode-setting">KMS</a> (Kernel Mode Setting) con el driver Intel i915 en una <b>HP Pavilion dm4</b> . Las instrucciones están hechas para <b>Debian Wheezy</b> (testing a la fecha) y requiere de un <b>kernel >= 2.6.29</b>.<br/><br/>
<h2>Instalar dependencias</h2>
Suponiendo que tenemos Xorg instalado y corriendo (quizás funcionando con el driver vesa) procedemos instalar el driver intel y sus dependencias:<br/>
<code><pre># aptitude install libgl1-mesa-glx libglu1-mesa xserver-xorg-video-intel mesa-utils</pre></code>
<br/>
<h2>Activar el KMS</h2>
Luego vamos al archivo <b>/etc/modprobe.d/i915-kms.conf</b> y agregamos la línea:
<code><pre>options i915 modeset=1</pre></code>
<br/>
Regeneramos la imagen del kernel con:
<code><pre># update-initramfs -u</pre></code>
<br/>
Editamos el archivo <b>/etc/default/grub</b> y modificamos esta línea:
<code><pre>GRUB_CMDLINE_LINUX_DEFAULT="quiet i915.modeset=1 video=LVDS-1:e"</pre></code>
<br/>
El parámetro <b>i915.modeset=1</b> habilita el KMS al momento del booteo y para que la pantalla no se quede en negro luego de cargar el módulo, debemos agregar el parámetro <b>video=LVDS-1:e</b>.<br/><br/>
Es importante no cargar ningún otro driver para framebuffer que no sea el de Intel y eliminar de los parámetros de booteo cualquier opción del tipo <b>vga=xxx</b> o <b>video=xxx</b>.<br/><br/>
El parámetro <b>video=LVDS-1:e</b> tiene una razón de ser muy particular. El BIOS de esta máquina tiene un bug; cuando inicia, indica erróneamente que la tapa de la laptop está cerrada. El kernel, basándose en esa información, manda a apagar la pantalla y por eso todo se queda en negro, no es que se congela ni que deja de funcionar. Pueden corroborar esto cerrando y abriendo la tapa de la laptop cuando se apaga. Verán que el video vuelve mágicamente y que el equipo no se había detenido, seguía funcionando pero a ciegas.<br/><br/>
Es bien conocido que el kernel no usa el BIOS para casi ninguna de sus tareas, sin embargo a partir del kernel 2.6.32 los desarrolladores decidieron "escuchar" al BIOS en este tipo de eventos, así que hasta que no se masifique el patch que corrige la situación debemos aplicar este correctivo.<br/><br/>
Si desean más información pueden revisar el reporte y la <a href="https://bugs.launchpad.net/ubuntu/+source/linux/+bug/515246">resolución del bug en Launchpad</a> o el <a href="http://lists.freedesktop.org/archives/intel-gfx/2010-March/006240.html">reporte del error en fredesktop.org</a><br/><br/>
Luego de ese paréntesis técnico continuamos con nuestra configuración. Reconfiguramos el grub para que se apliquen los cambios anteriores:
<br/>
<code><pre># update-grub</pre></code>
<br/>
<h2>Configuración de Xorg</h2>
Lo siguiente es crear y personalizar el archivo de configuración de Xorg. <br/><br/>
A partir de la versión 1.8, Xorg ya no necesita archivos de configuración. En lugar de eso intenta detectar y configurar todo el hardware (video y dispositivos de entrada) por si mismo. Sin embargo existen casos especiales que requieren algunos "ajustes". Este es uno de esos casos.<br/><br/>
Procedemos entonces a detener el servidor X. Si tenemos un entorno de escritorio basta con detener el gestor de sesión (gdm, kdm, slim, xdm, etc), sino matamos las X.<br/><br/>
Hecho esto pasamos a ejecutar:
<code><pre># Xorg -configure</pre></code>
<br/>
Eso debería generarnos un archivo <b>xorg.conf.new</b> que servirá como base. A ese archivo le editamos la sección "<b>Device</b>" para que nos quede algo como:
<code><pre>Section "Device"
Option "DRI" "True"
Option "Tiling" "True"
Option "XAANoOffscreenPixmaps" "True"
Identifier "Card0"
Driver "intel"
BusID "PCI:0:2:0"
EndSection</pre></code>
<br/>
Y agregamos al final del archivo la siguiente sección:
<code><pre>Section "Extensions"
Option "Composite" "enable"
EndSection</pre></code>
<br/>
Observen que indicamos el driver de video y algunas opciones para mejorar el desempeño gráfico. Si conocen otras opciones (que apliquen para tarjetas Intel) también pueden agregarlas acá.<br/><br/>
Con esto terminamos nuestra configuración del servidor X.<br/><br/>
<h2>Reiniciar</h2>
Reiniciamos y ya deberíamos disfrutar de nuestro video usando KMS. Verán que la transición entre modos de video es casi instantánea y el parpadeo se redujo considerablemente. Además de que compiz y todas esas yerbas aromáticas se ven con una suavidad asombrosa.<br/><br/>
<h2>Deshabilitar el KMS</h2>
Si desean deshabilitar el KMS basta con desactivar el modeset en la línea:
<code><pre>GRUB_CMDLINE_LINUX_DEFAULT="quiet i915.modeset=0 video=LVDS-1:e"</pre></code><br/>
Reconfiguramos el grub:
<code><pre># update-grub</pre></code><br/>
Y KMS desactivado.<br/><br/>
Espero que esta receta les haya servido de ayuda. En un próximo post explicaré cómo configurar el Touchpad Synaptics.<br/><br/>
Fuentes:<br>
<ul>
<li><a href="http://wiki.debian.org/KernelModesetting">http://wiki.debian.org/KernelModesetting</a></li>
<li><a href="https://wiki.archlinux.org/index.php/Intel">https://wiki.archlinux.org/index.php/Intel</a></li>
<li><a href="http://en.gentoo-wiki.com/wiki/Intel_GMA">http://en.gentoo-wiki.com/wiki/Intel_GMA</a></li>
</ul>
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com6tag:blogger.com,1999:blog-6887761988295725170.post-46449860824479139052011-09-17T18:12:00.000-07:002011-10-29T20:57:03.765-07:00Obtener audio de un video de Youtube<div style="text-align: justify;"><span style="font-size:85%;">¿No les ha pasado que a veces encuentran en Youtube videos que son unas verdaderas joyas musicales y les gustaría tenerlos en su colección musical? Bueno con este truco es muy fácil hacerlo.<br/><br/>
Primero instalamos el Youtube Downloader. Desde Debian sería:<br/>
<code># aptitude install youtube-dl</code><br/>
Instalamos el ffmpeg (en caso de no tenerlo):<br/>
<code># aptitude install ffmpeg</code><br/>
Luego descargamosel video desde Youtube y extraemos el audio:<br/>
<code>$ youtube-dl <url_del_video> --extract-audio</code><br/>
Eso nos generará un archivo .aac, ahora procedemos a convertirlo a mp3 (o al formato de nuestra preferencia):<br/>
<code>$ ffmpeg -i <nombre_del_archivo>.aac -ar 22050 -ab 32 <nombre_del_archivo>.mp3</code><br/>
Y ya podremos disfrutar de la pieza de audio en nuestro reproductor favorito a partir del video de Youtube<br/>
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com1tag:blogger.com,1999:blog-6887761988295725170.post-37090246370076188412011-05-28T09:32:00.000-07:002011-05-28T09:32:54.372-07:00Instalar RVM en Debian Wheezy<div style="text-align: justify;"><span style="font-size:85%;"><a href="https://rvm.beginrescueend.com/">RVM</a> (Ruby Version Manager) es una fantástica herramienta que te permite gestionar muchas versiones de Ruby (y de sus gemas) en un mismo equipo. Las gemas se manejan mediante contenedores (gemsets), allí puedes instalar lo que desees sin afectar el resto de los componentes. Es un principio muy parecido al de las máquinas virtuales.<br />
<br />
La receta es para Debian pero se puede aplicar para cualquier distro, solo hay que tener en cuenta los nombres de los paquetes a instalar.<br />
<br />
Comencemos entonces con las dependencias. Necesitamos git, las herramientas básicas para compilar, los fuentes del readline y del ssl:<br />
<br />
<code># aptitude install libreadline5-dev git curl build-essential libssl-dev</code><br />
<br />
Luego, como usuario ejecutamos el siguiente script:<br />
<br />
<code>$ bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)</code><br />
<br />
Ese script descarga rvm de git, lo compila y lo instala. Antes de usarlo debemos agregar una línea a nuestro .bash_profile, para eso ejecutamos:<br />
<br />
<code>$ echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm" # Load RVM function' >> ~/.bash_profile</code><br />
<br />
Hacemos un:<br />
<br />
<code>$ source ~/.bash_profile</code><br />
<br />
Y verificamos que todo haya salido bien:<br />
<br />
<code>$ type rvm | head -1</code><br />
<br />
El resultado de ese comando debería ser: <b>rvm es una función</b>. Si no obtenemos ese resultado algo ha ido mal. Les recomiendo entonces revisar la <a href="https://rvm.beginrescueend.com/rvm/install/">documentación de instalación</a> o la <a href="https://rvm.beginrescueend.com/support/troubleshooting/">página de problemas solucionados</a>.<br />
<br />
Ahora podemos ejecutar rvm como un comando más de consola. Instalemos un par de paquetes que nos harán falta antes de instalar una versión de Ruby:<br />
<br />
<code>$ rvm package install openssl<br />
$ rvm package install readline</code><br />
<br />
Instalamos una versión de Ruby (por ejemplo: 1.8.7):<br />
<br />
<code>$ rvm install 1.8.7</code><br />
<br />
Y configuramos la versión que deseamos usar por defecto en el sistema:<br />
<br />
<code>$ rvm use 1.8.7 --default<br />
Using /home/tu_usuario/.rvm/gems/ruby-1.8.7-p334</code><br />
<br />
Ahora podemos crear gemsets a placer, por ejemplo:<br />
<br />
<code>$ rvm gemset create pruebas1<br />
'pruebas1' gemset created (/home/tu_usuario/.rvm/gems/ruby-1.8.7-p334@pruebas1).</code><br />
<br />
Notese que el <b>@</b> en el mensaje anterior nos indica que el gemset ha sido asociado a la versión 1.8.7 de Ruby que habíamos instalado (y seleccionado para usar) previamente. Ahora instalemos otro gemset de pruebas para entender la magia del asunto.<br />
<br />
<code>$ rvm gemset create pruebas2<br />
'pruebas2' gemset created (/home/tu_usuario/.rvm/gems/ruby-1.8.7-p334@pruebas2).</code><br />
<br />
Podemos movernos a través de los gemsets usando el comando <b>use</b> con la siguiente forma:<br />
<br />
<code>$ rvm use 1.8.7@pruebas1<br />
Using /home/tu_usuario/.rvm/gems/ruby-1.8.7-p334 with gemset pruebas1</code><br />
<br />
Ahora probemos instalar una gema en el contenedor 'pruebas1':<br />
<br />
<code>$ gem install xml-simple --no-rdoc --no-ri<br />
Fetching: xml-simple-1.0.15.gem (100%)<br />
Successfully installed xml-simple-1.0.15<br />
1 gem installed</code><br />
<br />
Particularmente uso los argumentos <b>--no-rdoc --no-ri</b> para evitar la instalación de la documentación y otros archivos adicionales que no utilizo y demoran considerablemente la puesta a punto de la gema.<br />
<br />
Veamos entonces qué tenemos ahora en 'pruebas1':<br />
<br />
<code>$ gem list --local<br />
<br />
*** LOCAL GEMS ***<br />
<br />
rake (0.8.7)<br />
xml-simple (1.0.15)</code><br />
<br />
Una nueva gema instalada en nuestro gemset. Y en 'pruebas2' ¿qué tenemos?:<br />
<br />
<code>$ rvm use 1.8.7@pruebas2<br />
Using /home/tu_usuario/.rvm/gems/ruby-1.8.7-p334 with gemset pruebas2<br />
<br />
$ gem list --local<br />
<br />
*** LOCAL GEMS ***<br />
<br />
rake (0.8.7)</code><br />
<br />
¡Oh! No tenemos nada instalado. Eso quiere decir que nuestros contenedores están aislados uno del otro y que podemos instalar cosas que cada uno de ellos sin temor a romper algo en el otro (siempre teniendo cuidado del gemset que está en uso).<br />
<br />
Si en algún momento dañamos un gemset basta con ejecutar:<br />
<br />
<code>$ rvm gemset delete pruebas1<br />
WARN: Are you SURE you wish to remove the entire gemset directory 'pruebas1' (/home/tu_usuario/.rvm/gems/ruby-1.8.7-p334@pruebas1)?<br />
(anything other than 'yes' will cancel) > </code><br />
<br />
Respondemos <b>yes</b> y listo, desaparece el gemset con todo lo que tenía adentro.<br />
<br />
Muy bonito todo, pero se preguntarán ¿Cómo le digo a un determinado proyecto que use un gemset específico? ¿O es que debo especificarle manualmente que gemset utilizará cada vez que vaya a ejecutarlo?<br />
<br />
Pues la respuesta es muy simple, basta con crear un archivo <b>.rvmrc</b> en la raíz del proyecto con algo parecido a esto:<br />
<br />
<code>rvm use 1.8.7@pruebas2</code><br />
<br />
Y desde ese momento, todo lo que está dentro de esa carpeta usará la versión del Ruby y el gemset especificado en el archivo.<br />
<br />
<br />
NOTA: Es importante aclarar que Ruby, RubyGems, las gemas o cualquier otras cosa relacionada NO debe instalarse usando la paquetería de la distribución. TODO debe manejarse a través RVM, de lo contrario corromperemos toda la instalación.<br />
<br />
Espero que esta receta les sea de utilidad, a mi me ha cambiado la vida xD<br />
<br />
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com6tag:blogger.com,1999:blog-6887761988295725170.post-40159920751176375822011-05-24T21:34:00.000-07:002011-05-28T07:39:58.744-07:00Debian Wheezy sin interfaz inalámbrica luego de un safe-upgrade<div style="text-align: justify;"><span style="font-size:85%;">Me ocurrió hace unos días que actualicé mi Debian y repentinamente me quedé sin conexión inalámbrica. Uso Debian Testing (Wheezy para la fecha) en una HP Pavilion dv2000 y la tarjeta de red inalámbrica es una Intel PRO/Wireless 4965 AG.<br />
<br />
Leyendo y leyendo diferentes manuales todos afirmaban que con solo instalar el paquete firmware-iwlwifi del repo non-free la cosa funcionaba a la perfección y me constaba porque así lo había hecho en un principio. Sin embargo, luego de actualizar no anduvo más y lo curioso es que el <b>lspci</b> me devolvía señales de vida:<br />
<br />
<code>07:00.0 Network controller: Intel Corporation PRO/Wireless 4965 AG or AGN [Kedron] Network Connection (rev 61)</code><br />
<br />
El paquete en efecto estaba instalado:<br />
<br />
<code>$ aptitude search iwl | grep ^i<br />
i firmware-iwlwifi - Binary firmware for Intel Wireless 3945, 4</code><br />
<br />
El <b>lsmod</b> me mostraba los módulos cargados y todo estaba bien:<br />
<br />
<code><pre>$ lsmod | grep iwl
iwlagn 122417 0
iwlcore 50368 1 iwlagn
mac80211 160285 2 iwlagn,iwlcore
cfg80211 106889 3 iwlagn,iwlcore,mac80211</pre></code><br />
<br />
Incluso, los binarios del firmware también parecían estar en orden:<br />
<br />
<code># find / -name "*ucode*"<br />
/sys/module/iwlagn/parameters/ucode_alternative<br />
/lib/firmware/iwlwifi-6050-5.ucode<br />
/lib/firmware/iwlwifi-3945-2.ucode<br />
/lib/firmware/iwlwifi-6000g2b-5.ucode<br />
/lib/firmware/iwlwifi-5000-5.ucode<br />
/lib/firmware/iwlwifi-5000-1.ucode<br />
/lib/firmware/iwlwifi-4965-2.ucode<br />
/lib/firmware/iwlwifi-5000-2.ucode<br />
/lib/firmware/iwlwifi-4965-1.ucode<br />
/lib/firmware/iwlwifi-6000-4.ucode<br />
/lib/firmware/iwlwifi-6000g2a-5.ucode<br />
/lib/firmware/iwlwifi-1000-3.ucode<br />
/lib/firmware/iwlwifi-6050-4.ucode<br />
/lib/firmware/iwlwifi-3945-1.ucode<br />
/lib/firmware/iwlwifi-5150-2.ucode<br />
/lib/firmware/iwlwifi-100-5.ucode</code><br />
<br />
Luego intento ver el log del kernel y me encuentro con algo que me dio algunas pistas:<br />
<br />
<code><pre># tail -f /var/log/syslog
May 24 23:03:31 mpn82 kernel: [ 6214.119061] iwlagn: Intel(R) Wireless WiFi Link AGN driver for Linux, in-tree:
May 24 23:03:31 mpn82 kernel: [ 6214.119064] iwlagn: Copyright(c) 2003-2010 Intel Corporation
May 24 23:03:31 mpn82 kernel: [ 6214.119140] iwlagn 0000:07:00.0: PCI INT A -> GSI 19 (level, low) -> IRQ 19
May 24 23:03:31 mpn82 kernel: [ 6214.119153] iwlagn 0000:07:00.0: setting latency timer to 64
May 24 23:03:31 mpn82 kernel: [ 6214.119187] iwlagn 0000:07:00.0: Detected Intel(R) Wireless WiFi Link 4965AGN, REV=0x4
May 24 23:03:31 mpn82 kernel: [ 6214.157779] iwlagn 0000:07:00.0: device EEPROM VER=0x36, CALIB=0x5
May 24 23:03:31 mpn82 kernel: [ 6214.157782] iwlagn 0000:07:00.0: Device SKU: 0Xb
May 24 23:03:31 mpn82 kernel: [ 6214.157979] iwlagn 0000:07:00.0: Tunable channels: 11 802.11bg, 13 802.11a channels
May 24 23:03:31 mpn82 kernel: [ 6214.158062] iwlagn 0000:07:00.0: irq 46 for MSI/MSI-X
May 24 23:03:31 mpn82 NetworkManager[1140]: <info> found WiFi radio killswitch rfkill4 (at /sys/devices/pci0000:00/0000:00:1c.3/0000:07:00.0/ieee80211/phy2/rfkill4) (driver <unknown>)
May 24 23:03:31 mpn82 kernel: [ 6214.161805] iwlagn 0000:07:00.0: loaded firmware version 228.61.2.24
May 24 23:03:31 mpn82 kernel: [ 6214.162128] ieee80211 phy2: Selected rate control algorithm 'iwl-agn-rs'
May 24 23:03:31 mpn82 NetworkManager[1140]: SCPlugin-Ifupdown: devices added (path: /sys/devices/pci0000:00/0000:00:1c.3/0000:07:00.0/net/wlan0, iface: wlan0)
May 24 23:03:31 mpn82 NetworkManager[1140]: SCPlugin-Ifupdown: device added (path: /sys/devices/pci0000:00/0000:00:1c.3/0000:07:00.0/net/wlan0, iface: wlan0): no ifupdown configuration found.
May 24 23:03:31 mpn82 NetworkManager[1140]: <info> (wlan0): driver supports SSID scans (scan_capa 0x01).
May 24 23:03:31 mpn82 NetworkManager[1140]: <info> (wlan0): new 802.11 WiFi device (driver: 'iwlagn' ifindex: 6)
May 24 23:03:31 mpn82 NetworkManager[1140]: <info> (wlan0): exported as /org/freedesktop/NetworkManager/Devices/3
May 24 23:03:31 mpn82 NetworkManager[1140]: <info> (wlan0): now managed
May 24 23:03:31 mpn82 NetworkManager[1140]: <info> (wlan0): device state change: 1 -> 2 (reason 2)
May 24 23:03:31 mpn82 NetworkManager[1140]: <info> (wlan0): bringing up device.
May 24 23:03:31 mpn82 NetworkManager[1140]: <info> (wlan0): deactivating device (reason: 2).</pre></code><br />
<br />
1.- El firmware se estaba cargando con la versión más actualizada según la <a href="http://intellinuxwireless.org/?n=downloads&f=ucodes_6000">página de Intel</a><br />
<code>May 24 23:03:31 mpn82 kernel: [ 6214.161805] iwlagn 0000:07:00.0: loaded firmware version 228.61.2.24</code><br />
<br />
2.- Por alguna razón la interfaz se estaba apagando luego que cargaba el NetworkManager<br />
<br />
Decidí buscar un poco en internet y me conseguí con este <a href="https://bugzilla.redhat.com/show_bug.cgi?id=595931">reporte de bug en Red Hat</a> que involucraba al NetworkManager, la interfaz wireless y un mensaje de error muy similar al mío.<br />
<br />
Probé entonces con:<br />
<br />
<code><pre># rfkill list
0: hp-wifi: Wireless LAN
Soft blocked: yes
Hard blocked: no
2: hp-bluetooth: Bluetooth
Soft blocked: yes
Hard blocked: no
4: phy2: Wireless LAN
Soft blocked: yes
Hard blocked: yes</pre></code><br />
<br />
Jum... phy2 y hp-wifi bloqueados por software, adicionalmente phy2 bloqueado por hardware. Raro, raro.<br />
<br />
Hago un:<br />
<br />
<code># rfkill unblock 0 && rfkill unblock 4</code><br />
<br />
Y voilá, volvió a enceder el led de la interfaz inalámbrica e inmediatamente se conectó a internet :D<br />
<br />
Cosas tontas pero que te salvan la vida. Espero que les sea de utilidad<br />
<br />
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com4tag:blogger.com,1999:blog-6887761988295725170.post-36558892620947061062011-05-02T10:15:00.000-07:002011-05-02T10:23:35.329-07:00LoadError con el readline al intentar abrir una consola de Rails usando rvm<div style="text-align: justify;"><span style="font-size:85%;">Si usando rvm, intentas abrir una consola de Rails (con <strong>rails c</strong> o con <strong>script/console</strong>) y te escupe un error como este:<br />
<br />
<code>no such file to load -- readline (LoadError)</code><br />
<br />
Entonces sigue estos pasos y resolverás el problema.<br />
<br />
Primero, debemos instalar el paquete del readline en rvm (y no en el Sistema Operativo). Para eso removemos la versión actual de Ruby que podamos tener:<br />
<br />
<code>$ rvm remove 1.8.7</code><br />
<br />
Instalamos el paquete readline:<br />
<br />
<code>$ rvm install package</code><br />
<br />
E instalamos Ruby pero indicándole la ubicación del paquete readline:<br />
<br />
<code>$ rvm install 1.8.7 --with-readline-dir=$rvm_path/usr</code><br />
<br />
Acto seguido, entramos a la carpeta en donde rvm almacena los fuentes de las extensiones de Ruby, específicamente del readline. En mi caso:<br />
<br />
<code>$ cd ~/.rvm/src/ruby-1.8.7-p302/ext/readline </code><br />
<br />
Luego (como root) instalamos las dependencias necesarias. En Debian sería:<br />
<br />
<code># aptitude update<br />
# aptitude install libreadline5-dev</code><br />
<br />
Procedemos (como usuario) a compilar e instalar la extensión en cuestión:<br />
<br />
<code>$ ruby extconf.rb<br />
$ make<br />
$ make install</code><br />
<br />
Y con eso ya deberíamos disfrutar nuevamente de la consola interactiva de Rails sin problemas.<br />
<br />
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com1tag:blogger.com,1999:blog-6887761988295725170.post-16634091579432631902011-04-22T07:43:00.000-07:002011-04-22T07:43:54.451-07:00Habilitar la función de "tap" en un TouchPad<div style="text-align: justify;"><span style="font-size:85%;">En estos días estaba usando mi netbook y me di cuenta que al hacer tap sobre el touchpad no se ejecutaba el click del ratón y no fue hasta hoy que me digné a buscar la solución. Es tan simple como agregar una línea en un archivo de texto.<br />
<br />
Buscamos el archivo <strong>/etc/modprobe.d/options.conf</strong>, si no existe lo creamos y copiamos la siguiente línea:<br />
<br />
<code>options psmouse proto=imps</code><br />
<br />
Luego hacemos (como root):<br />
<br />
<code># modprobe -r psmouse<br />
# modprobe psmouse proto=imps</code><br />
<br />
Y a disfrutar del tap xD<br />
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com2tag:blogger.com,1999:blog-6887761988295725170.post-19983627354876812972011-02-28T06:29:00.000-08:002011-02-28T06:29:10.775-08:00Resaltado de sintaxis HAML en Gedit<div style="text-align: justify;"><span style="font-size:85%;">Trabajando con Ruby On Rails me ha tocado escribir código usando <a href="http://haml-lang.com/">HAML</a>. Particularmente no me agrada mucho (entre otras cosas porque no tiene soporte multilínea, porque no identa correctamente las cadenas de texto plano, porque agrega otra capa más de interpretación a la aplicación, etc) pero cuando toca usarlo es bueno hacerlo con un buen soporte. Y cuando hablo de soporte me refiero al resaltado de sintaxis.<br />
<br />
Para escribir código usualmente uso un editor de texto (Gedit o Geany), así que en ésta receta explicaré como agregar soporte para el resaltado de sintaxis en Gedit.<br />
<br />
Primero agregamos soporte para que las extensiones usadas comúnmente en Rails sean reconocidas por el sistema. Abrimos una terminal y escribimos:<br />
<br />
<pre><code>wget http://dl.dropbox.com/u/16349833/rails.xml
sudo cp rails.xml /usr/share/mime/packages/rails.xml
sudo update-mime-database /usr/share/mime</code></pre><br />
Luego agregamos los archivos de definición de sintaxis en la carpeta del sourceview de GTK ejecutando:<br />
<br />
<pre><code>wget http://dl.dropbox.com/u/16349833/gedit_rails_syntax.zip
sudo unzip gedit_rails_syntax.zip -d /usr/share/gtksourceview-2.0/language-specs</pre></code><br />
<br />
Ahora con nuestro editor de texto favorito (y como root) abrimos el archivo <strong>/usr/share/gtksourceview-2.0/language-specs/ruby.lang</strong> para modificar el sourceview de GTK y agregar soporte para nuevas extensiones. Buscamos la línea:<br />
<br />
<pre><code><property name="globs">*.rb</property></pre></code><br />
Y la cambiamos por:<br />
<br />
<pre><code><property name="globs">*.rb;*.rake;*.rjs</property></pre></code><br />
Por último, abrimos el archivo <strong>/usr/share/gtksourceview-2.0/language-specs/html.lang</strong> y buscamos la línea que dice:<br />
<br />
<pre><code><property name="globs">*..html;*.htm</property></pre></code><br />
Y la cambiamos por:<br />
<br />
<pre><code><property name="globs">*.html;*.htm;*.erb;*.rhtml</property></pre></code><br />
Cerramos todas las instancias de Gedit y al abrir de nuevo ya nuestro código HAML debería verse resaltado.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkFFah8ZvvvcGsuFGPq1xkx7DLYEm6u13lepVfuvfcnOju83GJb0JQaq3MyEOcpVOEN5mpSpsl4DL-VJj3as1hhYlGwH64t_uQ1lyTg8WHpRsx1ytPhUheLAKBKoNyW4ZL8tuU6DsyoWrL/s1600/gedit-haml.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="227" width="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkFFah8ZvvvcGsuFGPq1xkx7DLYEm6u13lepVfuvfcnOju83GJb0JQaq3MyEOcpVOEN5mpSpsl4DL-VJj3as1hhYlGwH64t_uQ1lyTg8WHpRsx1ytPhUheLAKBKoNyW4ZL8tuU6DsyoWrL/s320/gedit-haml.png" /></a></div><br />
Existen recetas que instalan una serie de plugins (con soporte para haml, rjs, etc) y te dejan al Gedit como TextMate, pero como no me gusta TextMate entonces prefiero hacerlo a patica xD<br />
<br />
Espero que la receta les haya servido de ayuda.<br />
<br />
Referencias:<br />
<a href="http://groups.google.com/group/haml/browse_thread/thread/6c8babd81a46b6b8/b5100d80d9182c71?pli=1">http://groups.google.com/group/haml/browse_thread/thread/6c8babd81a46b6b8/b5100d80d9182c71?pli=1</a><br />
<a href="http://blog.adsdevshop.com/2008/04/19/erb-syntax-highlighting-in-gedit/">http://blog.adsdevshop.com/2008/04/19/erb-syntax-highlighting-in-gedit/</a><br />
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com0tag:blogger.com,1999:blog-6887761988295725170.post-1436046033963670422010-12-21T15:42:00.000-08:002010-12-21T15:42:36.342-08:00¿Cómo desarrollar para Android? - Parte II<div style="text-align: justify;"><span style="font-size:85%;">En la <a href="http://damncorner.blogspot.com/2010/11/como-desarrollar-para-android-parte-i.html">entrega anterior</a> expliqué como instalar el ambiente de desarrollo para trabajar con Android. Ahora explicaré algunos fundamentos necesarios para poder entrar en calor.<br />
<br />
Podemos decir que Android está compuesto por varias capas (muy al estilo del modelo <a href="http://es.wikipedia.org/wiki/Modelo_OSI">OSI</a>).<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://developer.android.com/images/system-architecture.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="432" width="600" src="http://developer.android.com/images/system-architecture.jpg" /></a></div><br />
Esas capas las podemos diferenciar en 5 grupos:<br />
<br />
<ul><li><b>Capa 1 - Kernel:</b> La base de Android es sólida como una roca. Claro, esa base es el kernel Linux. Android usa Linux para manejar todo lo relacionado con el hardware (drivers), gestionar la memoria, los procesos y muchas otras tareas operativas. Sin embargo nuestra aplicación nunca tocará el kernel.</li>
<li><b>Capa 2 - Librerías nativas:</b> La siguiente capa que se ubica por encima del kernel son las librerías nativas. Allí tenemos una serie de herramientas que vienen precompiladas por el fabricante y que nos permiten manejar desde renderizado web hasta bases de datos.</li>
<li><b>Capa 3 - Núcleo:</b> Sobre ésta capa tenemos el núcleo de Android. Allí reside la máquina virtual Dalvik y las librerías de la plataforma.</li>
<li><b>Capa 4 - Framework para aplicaciones:</b> Más arriba está el conjunto de herramientas que nos permite crear y manejar las aplicaciones, notificaciones, recursos y muchas otras cosas.</li>
<li><b>Capa 5 - Aplicaciones:</b> Y por último tenemos la capa de aplicaciones, aquí es donde se encuentran los widgets y todas las aplicaciones que tienen contacto con el usuario.</li></ul><br />
Ya conocemos un poco mejor la arquitectura de Android, ahora veamos con qué contamos para desarrollar. Parte del framework para aplicaciones son los componentes. Estuadiaremos los más importantes por el momento, ellos son:<br />
<br />
<ul><li><b>Activities:</b> Los activities (o actividades) son las interfaces que permiten al usuario interactuar con la aplicación. Generalmente una aplicación está compuesta de muchas actividades. Una actividad puede ser mostrar la lista de contactos y otra puede ser enviar mensajes de texto</li>
<li><b>Intents:</b> Un intent (o como se diría en español, una "intención") no es más que una acción. En Android todas las acciones se manejan con intenciones ;)</li>
<li><b>Services:</b> Es una tarea que se ejecuta en segundo plano, sin interfaz gráfica y sin interacción directa con el usuario. Podemos pensar en el ejemplo típico: un reproductor de música. A todos nos gusta que la música siga sonando incluso si dejamos de ver el reprouctor para pasarnos al navegador web. Eso es un servicio</li>
<li><b>Content Providers:</b> Es un contenedor que permite agrupar un conjunto de datos de una aplicación y ponerlos a disposición de otras aplicaciones. Podemos verlo como una manera de compartir información global entre aplicaciones</li></ul><br />
Por último vamos a mencionar las fases del ciclo de vida de una aplicación.<br />
<br />
Lo primero que debemos entender es que (a diferencia de un sistema operativo de escritorio) en Android una aplicación no está casada con un proceso. En Android existe una sola aplicación que obtiene el foco y se presenta frente al usuario. Mientras ésta aplicación se encuentra en el frente, las demás aplicaciones entran en estados de pausa, detención o incluso, si la memoria es escasa, Android puede llegar a cerrarlas para liberar recursos y sin estar mediando muchas palabras.<br />
<br />
Pero no se asusten, si la aplicación es "asesinada" por Android su estado se almacena para que pueda recuperarse cuando el usuario la ejecute nuevamente. Es en este ínterin de cerrar y abrir aplicaciones en que se liberan los procesos. Debemos ver a un proceso como un simple contenedor desechable para las aplicaciones, no más. Siempre ten presente éste comportamiento al momento de diseñar tus aplicaciones.<br />
<br />
El ciclo de vida de una aplicación lo podemos ver en la siguiente imagen.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://developer.android.com/images/activity_lifecycle.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="711" width="545" src="http://developer.android.com/images/activity_lifecycle.png" /></a></div><br />
A continuación describo brevemente cada una de sus fases:<br />
<br />
<ul><li><b>onCreate():</b> Se ejecuta la primera vez que la aplicación se muestra. Aquí se pueden realizar toda la inicialización estática (conexiones a bases de datos, creación de interfaces, etc). Recibe un parámetro que puede contener el estado anterior de la aplicación (si fue almacenado correctamente)</li>
<li><b>onStart():</b> Se ejecuta justo antes de que la aplicación se haga visible al usuario</li>
<li><b>onRestart():</b> Llamado justo después que la aplicación ha sido detenida. Justo antes de comenzar de nuevo</li>
<li><b>onResume():</b> Se llamará cuando la aplicación esté lista para interactuar con el usuario. Acá se pueden inicializar cosas como música y animaciones</li>
<li><b>onPause():</b> Se ejecuta justo antes de que la aplicación pase a segundo plano porque otra aplicación ha sido ejecutada. Este método puede ser lo último que vea tu aplicación antes de morir, pues Android puede matar una aplicación pausado sin previo aviso. Es por eso que este es un buen lugar para guardar el estado de tu aplicación.</li>
<li><b>onStop():</b> Es llamado cuando la aplicación ya no es visible al usuario y no se necesitará por un rato. Tal como se indicó en el punto anterior, este método es posible que ni siquiera se ejecute</li>
<li><b>onDestroy():</b> Se llama justo después que la aplicación es destruida. Acá debes hacer el sepelio y esas cosas. Tal como se indicó en el punto anterior, este método es posible que ni siquiera se ejecute</li></ul><br />
Con toda ésta base teórica podemos decir que estamos listos para empezar a escribir el código de nuestra primera aplicación. Ese será el tema de la próxima entrega.<br />
<br />
Para más información sobre éste tema puedes consultar la <a href="http://developer.android.com/guide/topics/fundamentals.html">documentación oficial de Android</a><br />
<br />
<a href="http://damncorner.blogspot.com/2010/11/como-desarrollar-para-android-parte-i.html">¿Cómo desarrollar para Android? - Parte I</a><br />
<br />
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com1tag:blogger.com,1999:blog-6887761988295725170.post-39772722018875820302010-11-24T05:21:00.000-08:002010-11-24T05:21:59.571-08:00Python For Android - Mito o Realidad<div style="text-align: justify;"><span style="font-size:85%;">Estuve unas cuantas horas investigando sobre la posibilidad de desarrollar con Python sobre Android y a continuación les daré un resumen de los resultados y conclusiones que obtuve.<br />
<br />
Acá les dejo el script que usé para probar una parte de los widgets disponibles en la API por si quieren probarlo y echarle un ojo (no me detendré a explicarlo mucho porque el código prácticamente se explica a sí mismo). Además supongo que siguieron los pasos de mi tutorial sobre <a href="http://damncorner.blogspot.com/2010/11/como-desarrollar-para-android-parte-i.html">¿Cómo desarrollar para Android?</a> y tienen configurado su entorno de desarrollo y saben cómo usar el emulador.<br />
<br />
<code>droid = android.Android()<br />
<br />
tiempo = droid.dialogGetInput("Escriba la cantidad de tiempo que desea para el demo (10-100)","Tiempo:")<br />
print tiempo<br />
<br />
tiempo = int(tiempo[1])<br />
if tiempo < 10:
tiempo = 10
elif tiempo > 100:<br />
tiempo = 100<br />
<br />
droid.dialogCreateHorizontalProgress('Barra de Progreso', 'Hola, estoy progresando...', tiempo)<br />
droid.dialogShow()<br />
for i in range(tiempo):<br />
time.sleep(0.5) <br />
droid.dialogSetCurrentProgress(i)<br />
droid.dialogDismiss()<br />
<br />
droid.dialogCreateSpinnerProgress('Spinner de progreso', 'Mirá, mirá... estoy girando') <br />
droid.dialogShow()<br />
time.sleep(tiempo)<br />
droid.dialogDismiss()<br />
<br />
droid.notify('Test de Python' , 'Oh! puedo notificar también')<br />
<br />
droid.dialogCreateAlert(title, message)<br />
droid.dialogSetPositiveButtonText('Si')<br />
droid.dialogSetNegativeButtonText('No')<br />
droid.dialogSetNeutralButtonText('Cancelar')<br />
droid.dialogShow()<br />
response = droid.dialogGetResponse().result <br />
print response<br />
droid.makeToast('El resultado de la ejecucion del boton es: %s' %response['which'])<br />
<br />
droid.dialogCreateAlert('Selecciona un color')<br />
colores = ('amarillo', 'azul', 'rojo')<br />
droid.dialogSetItems(colores)<br />
droid.dialogShow()<br />
respuesta = droid.dialogGetResponse().result<br />
print respuesta<br />
droid.makeToast('El color seleccionado es: %s' %colores[respuesta['item']]) <br />
<br />
droid.makeToast('Bueno, chao')</code><br />
<br />
Para correrlo deben instalar algunos paquetes en el cel. Pueden hacerlo con el comando del SDK: <br />
<br />
<code>$ adb install /ruta/del/archivo.apk</code><br />
<br />
con los QR que están en la página del proyecto <a href="http://code.google.com/p/android-scripting">SL4A</a> o con el método que mejor les parezca.<br />
<br />
Ok, las cosas a instalar son:<br />
<br />
<ul><li><a href="http://code.google.com/p/android-scripting/downloads/detail?name=sl4a_r3.apk&can=2&q=">SL4A</a></li>
<li><a href="http://code.google.com/p/android-scripting/downloads/detail?name=python_for_android_r1.apk&can=2&q=">Python For Android</a></li>
</ul><br />
Luego que las instalen van al dispositivo/emulador y buscan el icono de <strong>Python For Android</strong>, tap e instalan. Eso tardará unos minutos mientras descarga unas cosas. Al terminar entonces enviamos el script de prueba al cel, podemos usar el comando:<br />
<br />
<code>$ adb push /ruta/del/script.py /sdcard/sl4a/scripts/</code><br />
<br />
Eso lo copiará en la carpeta del SL4A. Ahora cuando ejecutemos el Python For Android veremos en la lista de scripts nuestra infame creación, tap en el script y ejecutar. Lo demás es disfrutar las ventanitas y controles :P<br />
<br />
<h2>Conclusiones</h2><br />
<ol><li>La API de SL4A es reducida, es decir si bien está bastante adelantada aún faltan muchas cosas/funciones por implementar.</li>
<li>Python For Android a diferencia de lo que pensaba no soporta widgets gráficos en Android, solamente notificaciones, toast y cosas básicas tipo entrada de texto, selección, etc. Así que nos olvidamos de interfaces gráficas ricas en imágenes y esas cosas.</li>
<li>Sí existen algunos issues de performance, de hecho un pana que está trabajando duro con Android me dice que los desarrolladores todos los días se matan por tratar de mejorar el rendimiento pues de por si ya es algo lento (hablo de apps nativas). Sin embargo la versión de Python es cross-compiled y corre incluso más rápido que Jython</li>
<li>Hay formas de empaquetar scripts en .apk y aunque son algo un poco experimentales funcionan bastante bien (son, digamos, "tweaks" del método de construcción de paquetes con Java)</li>
<li>En cuanto a estabilidad no hay mayores issues, se puede usar para aplicaciones de producción</li></ol><br />
<br />
Finalmente puedo decir (muy a mi pesar) que no podemos contar con ésta alternativa para desarrollar aplicaciones ricas en interfaces gráficas, sin embargo es una opción interesante a tener en cuenta cuando madure un poco más. Personalmente le haré seguimiento tipo ninja y esperaré con ansias que en el futuro se puedan crear aplicaciones tan buenas como se hace con Java.<br />
<br />
Más información en:<br />
<br />
* <a href="http://code.google.com/p/android-scripting/">http://code.google.com/p/android-scripting/</a><br />
*<a href="http://code.google.com/p/android-scripting/wiki/FAQ">http://code.google.com/p/android-scripting/wiki/FAQ</a><br />
* <a href="http://code.google.com/p/android-scripting/wiki/Tutorials">http://code.google.com/p/android-scripting/wiki/Tutorials</a><br />
* <a href="http://code.google.com/p/android-scripting/wiki/ApiReference">http://code.google.com/p/android-scripting/wiki/ApiReference</a><br />
<br />
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com2tag:blogger.com,1999:blog-6887761988295725170.post-14601833959841415552010-11-20T06:10:00.000-08:002010-11-20T06:10:57.800-08:00¿Cómo desarrollar para Android? - Parte I<div style="text-align: justify;"><span style="font-size:85%;"><br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiY1eh2KOPyTEb8atiNLP0bjPuxsaHp9SRxhFykcAfmG4BOwXQ5XHLlwMENKFBkjHGrcDJIelGtkT2oUHOz8b-ivoKGVCv_EnSDtfXU5_RWVnJaccy2UNuX1km3u2FBll81e0RuFcumRBOO/s1600/android_logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="128" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiY1eh2KOPyTEb8atiNLP0bjPuxsaHp9SRxhFykcAfmG4BOwXQ5XHLlwMENKFBkjHGrcDJIelGtkT2oUHOz8b-ivoKGVCv_EnSDtfXU5_RWVnJaccy2UNuX1km3u2FBll81e0RuFcumRBOO/s200/android_logo.jpg" width="200" /></a></div><br />
Actualmente me he visto en la necesidad de crear aplicaciones para Android y como no había incursionado en ese mundo, he decidido hacer una serie de artículos para documentar todo el proceso.<br />
<br />
En ésta primera entrega explicaré los pasos que seguí para poner a punto el entorno de desarrollo. Debo resaltar que las recetas están hechas para Debian GNU/Linux, sin embargo puedes adaptarla a tu distro favorita con unos leves ajustes.<br />
<br />
Para los que no están familiarizados con mis tutoriales les recuerdo que todas las instrucciones que comiencen con el caracter $ se ejecutan como usuario normal y las que comiencen con # se ejecutan como superusuario (root), por ejemplo:<br />
<br />
<code>$ ls -l (instrucción ejecutada como usuario)<br />
# aptitude update (instrucción ejecutada como root)</code><br />
<br />
Dicho esto comenzamos. <br />
<br />
<h2>Instalar dependencias</h2><br />
Lo primero que debemos hacer es instalar los paquetes necesarios para el desarrollo, que básicamente son: la máquina virtual de Java (JRE) y el entorno de desarrollo (JDK). En una distribución Debian GNU/Linux bastaría con:<br />
<br />
<code># aptitude update<br />
# aptitude install sun-java6-jdk sun-java6-jre<br />
# aptitude remove gcj-jdk</code><br />
<br />
Si se dan cuenta usamos las implementaciones de SUN y removimos cualquier posible instalación del compilador GNU de Java (gcj) pues en la <a href="http://developer.android.com/sdk/requirements.html">documentación oficial</a> nos dicen que gcj NO es compatible con Android.<br />
<br />
Para más información sobre los requerimientos del sistema visita: <a href="http://developer.android.com/sdk/requirements.html">http://developer.android.com/sdk/requirements.html</a><br />
<br />
Luego que instalamos los paquetes nos aseguramos de que el sistema seleccione las opciones correctas por defecto de cada ejecutable, pare eso usamos:<br />
<br />
<code># update-alternatives --config javac<br />
# update-alternatives --config java</code><br />
<br />
Y en ambos casos, seleccionamos las opciones de SUN.<br />
<br />
<h2>Instalar Ant</h2><br />
Ant es una herramienta que nos permite construir aplicaciones Java. En nuestro caso, nos permitirá compilar y construir archivos .apk (los instalables en Android) a partir de nuestro código fuente.<br />
<br />
Nos vamos a la <a href="http://ant.apache.org/bindownload.cgi">página de descargas de Ant</a>, descargamos uno de los archivos comprimidos que se encuentran en la sección "Current Release of Ant" y lo descomprimimos en una carpeta de nuestra preferencia. En mi caso lo hice en /opt y me quedó la carpeta: <strong>/opt/apache-ant-1.8.1</strong>.<br />
<br />
<h2>Instalar el Android SDK</h2><br />
Bueno, teniendo las herramientas y dependencias listas, vamos a instalar el entorno de desarrollo de Android. Nos vamos a la <a href="http://developer.android.com/sdk/index.html">página del SDK de Android</a> y descargamos la versión que corresponda a nuestra plataforma. Al igual que con Ant, descomprimimos el archivo en la carpeta de nuestra preferencia. Ésta vez también lo descomprimí en /opt, quedando en la carpeta: <strong>/opt/android-sdk-linux_x86</strong>.<br />
<br />
Ahora editamos el archivo ~/.bashrc para agregar la ruta de los binarios al PATH del sistema y establecer las variables de entorno ANT_HOME y JAVA_HOME. Eso lo logramos agregando las siguientes líneas:<br />
<br />
<code>export PATH=${PATH}:/opt/android-sdk-linux_x86/tools<br />
export PATH=${PATH}:/opt/apache-ant-1.8.1/bin<br />
export ANT_HOME=/opt/apache-ant-1.8.1<br />
export JAVA_HOME=/usr/lib/jvm/java-6-sun/jre</code><br />
<br />
Recuerda cambiar las rutas de esas variables por las rutas donde se encuentran tus binarios. Si no sabes muy bien hacía dónde debe apuntar JAVA_HOME puedes ejecutar el comando:<br />
<br />
<code># find / -name "tools.jar" | grep sun</code><br />
<br />
Y entonces agrega la ruta que te arroje.<br />
<br />
<em>Nota: Debes cerrar las terminales abiertas y volverlas a abrir para que los cambios en las rutas surtan efecto.</em><br />
<br />
<h2>Instalar plataformas de Android</h2><br />
<em>¿Plataformas? ¿De qué me hablas?</em> - Seguramente te estarás haciendo esas preguntas. Las plataformas son las "versiones" de Android disponibles (por ejemplo Android 1.6, Android 2.2) y necesitamos <strong>al menos una</strong> para poder compilar la aplicación y configurar un Android Virtual Device (AVD) para probarla.<br />
<br />
Esto es muy simple, basta con abrir una terminal y ejecutar:<br />
<br />
<code>$ android</code><br />
<br />
Nos aparecerá una aplicación (AVD Manager) como la que se muestra a continuación y allí nos vamos a la sección <strong>Available Packages</strong>, actualizamos el repositorio y seleccionamos la(s) plataforma(s) que deseamos instalar.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT5AyCiSbH0XfRi66G4h1VChErGwmZQeQMMX7a2A-KTZFBlDnzumR7iWXlja1NrkFHZ0XdpEp5PBG3Do3alxL_1BgpUy6PUEqnERJDeBoLmED71gQB762dbMv_-coBeWoYVpc2LSFUNcVW/s1600/avd_manager.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="186" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT5AyCiSbH0XfRi66G4h1VChErGwmZQeQMMX7a2A-KTZFBlDnzumR7iWXlja1NrkFHZ0XdpEp5PBG3Do3alxL_1BgpUy6PUEqnERJDeBoLmED71gQB762dbMv_-coBeWoYVpc2LSFUNcVW/s320/avd_manager.png" width="320" /></a></div><br />
Finalmente creamos un nuevo dispositivo virtual en la sección <strong>Virtual Device</strong>. Hacemos clic en el botón <strong>New...</strong>, le asignamos un nombre, seleccionamos una plataforma y hacemos clic en <strong>Create AVD</strong>. <br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEit7-mZwIhRIa65bGYSSVjESYxugOkvAfK1cWdEF43DE60fMXEr9IH2ka6wuIB7swXi_bf9o7eeKzHqmDiWwOd7tMwi753ZXqm8nKurLaWqx-nLydUfLRkTnN6gBTqCvL8FF0xBwqEaJDcg/s1600/avd.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEit7-mZwIhRIa65bGYSSVjESYxugOkvAfK1cWdEF43DE60fMXEr9IH2ka6wuIB7swXi_bf9o7eeKzHqmDiWwOd7tMwi753ZXqm8nKurLaWqx-nLydUfLRkTnN6gBTqCvL8FF0xBwqEaJDcg/s320/avd.png" width="240" /></a></div><br />
Para probar nuestro flamante dispositivo lo seleccionamos y hacemos clic en <strong>Start...</strong>. Luego clic en <strong>Launch</strong> y al cabo de unos segundos tendremos el emulador corriendo una instancia de Android :)<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMpK8M9WL4AfeNzeIa8OiYpJa9ONZGBSsmNaCj3jixY7GhRO1vhiwD824qj4u5mAKS7bKnSAyzSU_4l-TWiAGW47bjM3hy_5W5XUPBORGwqYoe879btD1gDehJCQP9gMQqkweFU1AOb1ms/s1600/emulador.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="226" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMpK8M9WL4AfeNzeIa8OiYpJa9ONZGBSsmNaCj3jixY7GhRO1vhiwD824qj4u5mAKS7bKnSAyzSU_4l-TWiAGW47bjM3hy_5W5XUPBORGwqYoe879btD1gDehJCQP9gMQqkweFU1AOb1ms/s320/emulador.png" width="320" /></a></div><br />
<h2>Desarrollo</h2><br />
Ahora viene la parte divertida, crear la aplicación. En las próximas entregas estaré hablando sobre el desarrollo, por los momentos pueden jugar creando un <a href="http://developer.android.com/resources/tutorials/hello-world.html">Hello World</a> y pueden leer sobre cómo <a href="http://developer.android.com/guide/developing/eclipse-adt.html">desarrollar con Eclipse</a> y cómo <a href="http://developer.android.com/guide/developing/other-ide.html">desarrollar con otros IDEs</a><br />
<br />
<h2>Compilar y construir el ejecutable</h2><br />
Luego de que tengamos nuestra aplicación lista debemos crear el instalador. Eso es tan simple como navegar hasta la carpeta raíz del proyecto (código fuente) y ejecutar:<br />
<br />
<code>$ ant debug</code><br />
<br />
Si nuestro proyecto se llama "HelloAndroid" esto generará un archivo HelloAndroid-debug.apk y ese será nuestro instalador.<br />
<br />
<h2>Probar en el emulador</h2><br />
Finalmente, una de las partes más esperadas. ¿Cómo se verá nuestra aplicación en un dispositivo? Pues fácil, en una consola ejecutamos:<br />
<br />
<code>$ android</code><br />
<br />
E iniciamos nuestro dispositivo virtual. Luego que el dispositivo esté funcionando ejecutamos en otra consola:<br />
<br />
<code>$ adb install /ruta/de/nuestro/instalador/HelloAndroid-debug.apk</code><br />
<br />
Y con eso enviaremos la aplicación al dispositivo. La buscamos en el menú y voilá! A jugar :D<br />
<br />
Espero que les haya sido de utilidad éste tutorial. Próximamente seguiré documentando mis travesías en el mundo de Android. Cambio y fuera.<br />
<br />
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com2tag:blogger.com,1999:blog-6887761988295725170.post-61403395658256600582010-07-27T13:40:00.000-07:002010-07-27T13:40:13.954-07:00MPD + Sonata: Una combinación perfecta para reproducir música<div style="text-align: justify;"><span style="font-size:85%;">Lo primero que debo aclarar antes de empezar éste post es que la forma de reproducir música con éstas herramientas es completamente diferente a la tradicional. Si no te interesa reproducir música como un verdadero geek entonces huye y busca otras aplicaciones, éstas no son para tí.<br />
<br />
Si eres valiente, continuemos entonces. MPD es un demonio (o sea un servicio) para reproducir música, corre en segundo plano y no tiene interfaz gráfica. De hecho se inicia como cualquier otro servicio de tu equipo (red, hal, udev, etc).<br />
<br />
¿Cómo demonios hago para interactuar con él? te preguntarás. Sencillo, está basado en una arquitectura cliente-servidor así que existen aplicaciones que se comunican con él (clientes) y que te permiten manejarlo. Ahí es donde entra Sonata y compañía.<br />
<br />
Lo primero que debemos hacer es instalar las dependencias:<br />
<br />
<code># aptitude install mpd mpc sonata</code><br />
<br />
Yo instalaré 2 clientes, Sonata y MPC, porque quiero controlar la reproducción vía SSH desde mi celular xD (sí, sí, muy geek... pero me gusta la comodidad)<br />
<br />
Luego vamos a nuestro directorio personal, creamos la carpeta <b>.mpd</b> y dentro de ésta creamos dos carpetas más, <b>playlists</b> y <b>music</b><br />
<br />
<code>$ cd ~<br />
$ mkdir -p .mpd/playlists<br />
$ mkdir -p .mpd/music</code><br />
<br />
Dentro de la carpeta <b>.mpd</b> creamos los archivos <b>mpd.db</b>, <b>mpd.log</b> y <b>mpd.error</b><br />
<br />
<code>$ touch .mpd/mpd.db<br />
$ touch .mpd/mpd.log<br />
$ touch .mpd/mpd.error</code><br />
<br />
Ahora, por cada carpeta de música que tengamos en nuestro sistema creamos un enlace simbólico dentro de music:<br />
<br />
<code>$ ln -s /ruta/de_la/carpeta1 .mpd/music<br />
$ ln -s /ruta/de_la/carpeta2 .mpd/music<br />
...</code><br />
<br />
Luego (como root) editamos el archivo de configuración del MPD en <b>/etc/mpd.conf</b> y modificamos las siguientes opciones:<br />
<br />
<code><pre>music_directory /home/tu_usuario/.mpd/music
playlist_directory /home/tu_usuario/.mpd/playlists
db_file /home/tu_usuario/.mpd/mpd.db
log_file /home/tu_usuario/.mpd/mpd.log
error_file /home/tu_usuario/.mpd/mpd.error</pre></code><br />
<br />
Comentamos la línea de usuario para evitar problemas con los permisos:<br />
<br />
<code><pre>#user "mpd"</pre></code><br />
<br />
Y buscamos el apartado de audio y lo configuramos para ALSA o para PulseAudio:<br />
<br />
ALSA:<br />
<code><pre>audio_output {
type "alsa"
name "My ALSA Device"
}</pre></code><br />
<br />
PulseAudio:<br />
<code><pre>audio_output {
type "pulse"
name "My PulseAudio Device"
}</pre></code><br />
<br />
Establecemos un mezclador por software descomentando la siguiente línea:<br />
<br />
<code>mixer_type "software"</code><br />
<br />
Y voilá! Luego reiniciamos el servicio y creamos la base de datos de tags:<br />
<br />
<code># /etc/init.d/mpd restart --create-db</code><br />
<br />
Ahora desde Sonata (o desde mpc) agregamos las canciones que queramos a la lista y empezamos a disfrutar. Es tan genial ésta combinación que podemos cerrar el Sonata e incluso hasta la sesión gráfica y la música seguirá sonando. <br />
<br />
Nos quedará algo tan mínimo como esto:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGvfRbN8EGifVxVZOghDBgSx8YOi4_QE4f9ppOE1C3jPFAqLElaAelaBn011JHfGEgWqjZZ0YArNjmMt2klzmDz8dly_mEvYKKO7c-7SIU3fyMd2zd2pJXJgHkkQx0UM0QE3_cJhSJEk2l/s1600/sonata_mini.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGvfRbN8EGifVxVZOghDBgSx8YOi4_QE4f9ppOE1C3jPFAqLElaAelaBn011JHfGEgWqjZZ0YArNjmMt2klzmDz8dly_mEvYKKO7c-7SIU3fyMd2zd2pJXJgHkkQx0UM0QE3_cJhSJEk2l/s450/sonata_mini.png" /></a><br />
</div><br />
O en su versión extendida:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTJbbf2is9J3qMWLwg5bt1UV5QobeZnMHBnWKKL5y0iBjEPSEooGPbR9drbPwJDq0FZwPtBdsHUrODs1LUyCv6JI3Oad8YT9ES2QjfPrz9ezPvjB6g6_-1uV7G1TNORb5P-AmDvutZ4bv6/s1600/sonata_ext.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTJbbf2is9J3qMWLwg5bt1UV5QobeZnMHBnWKKL5y0iBjEPSEooGPbR9drbPwJDq0FZwPtBdsHUrODs1LUyCv6JI3Oad8YT9ES2QjfPrz9ezPvjB6g6_-1uV7G1TNORb5P-AmDvutZ4bv6/s470/sonata_ext.png" /></a><br />
</div><br />
Y con unas agradables notificaciones:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiheoRH3w-QJbiVwiglSZp23grJ9Y9MJUYoU5U2QiOjFxL9vuzQngC3i5oZBndOpbsg4kPU7jWUeAPk8GT3IKB73yi9ZYfXDQ-15KoKmziJBIz0VEVJfsmpmaVHhJjPmNXI0XKmg99U5M2l/s1600/sonata_notif.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiheoRH3w-QJbiVwiglSZp23grJ9Y9MJUYoU5U2QiOjFxL9vuzQngC3i5oZBndOpbsg4kPU7jWUeAPk8GT3IKB73yi9ZYfXDQ-15KoKmziJBIz0VEVJfsmpmaVHhJjPmNXI0XKmg99U5M2l/s541/sonata_notif.png" /></a><br />
</div><br />
Díganme... ¿No es una maravilla? Bueno de aquí en adelante los dejo para que experimenten y se enamoren.<br />
<br />
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com7tag:blogger.com,1999:blog-6887761988295725170.post-16992288176728460692010-07-10T11:48:00.000-07:002010-07-10T11:48:03.100-07:00Webcomic: Los FUUU de la vida (I)<div style="text-align: justify;">Con éste webcomic quiero inaugurar un espacio muy querido y vivido por todos nosotros... Los FUUUU de la vida. He aquí la primera entrega. Enjoy!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">Clic en la imagen para agrandar<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZdRj8SJI8e2AkaXdqOeDANDMhd4wtMKm3-L86AOZRw9berbqWUiYTdXD5pz8218kRoFX3oxxB1S3W1SUmptxrMywdjKRuw1j_tzKJlVZCFOmnxlkKz7jF7M5j2lVIwQ1nB4ZBLyjbhwnt/s1600/10+-+Los+fuuu+de+la+vida+1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="110" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZdRj8SJI8e2AkaXdqOeDANDMhd4wtMKm3-L86AOZRw9berbqWUiYTdXD5pz8218kRoFX3oxxB1S3W1SUmptxrMywdjKRuw1j_tzKJlVZCFOmnxlkKz7jF7M5j2lVIwQ1nB4ZBLyjbhwnt/s320/10+-+Los+fuuu+de+la+vida+1.png" width="320" /></a><br />
</div><span style="font-size: 85%;"></span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com0tag:blogger.com,1999:blog-6887761988295725170.post-70933026990798453502010-06-02T20:37:00.000-07:002010-06-02T20:37:25.713-07:00Respuesta a críticas destructivas sobre Turpial<div style="text-align: justify;"><span style="font-size:85%;">Me considero una persona tolerante y abierta a las críticas y sugerencias. No hay cosa que me guste más que el feedback de las personas sobre las cosas que hago y más aún sobre Turpial. Sin embargo cosa que no tolero es la mediocridad.<br />
<br />
He decidido crear este post para colocar la respuesta que le he dado a @MichelEnLaRed respecto a algunos comentarios que me ha hecho por Twitter sobre Turpial, que en lugar de contribuir al desarrollo del proyecto empañan todo el esfuerzo que duramente se viene haciendo. No pretendo agregar nada a la respuesta que le dí por Twitter, solo la pegaré aquí para unificarla y permitir su mejor lectura.<br />
<br />
El comentario que derramó la gota fue el siguiente (sí, hubo 3 o 4 comentarios previos en el mismo tono que dejé pasar por consideración y tolerancia):<br />
<br />
<div align='center'><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaMolON28MU0fhXYCHYpx0S6fBbw_uHMmMrbNrvpn9eMDE0E4rO_-WV3e4TXIo55kjWvKYRmqc9SRVVKFI7svCMdTB1svQs5TRDoDkzoa7x90diVMRbV-zmjBuRCVCLPV75goYLyjT0HL9/s1600/comentario.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaMolON28MU0fhXYCHYpx0S6fBbw_uHMmMrbNrvpn9eMDE0E4rO_-WV3e4TXIo55kjWvKYRmqc9SRVVKFI7svCMdTB1svQs5TRDoDkzoa7x90diVMRbV-zmjBuRCVCLPV75goYLyjT0HL9/s320/comentario.png" /></a></div><br />
Mi respuesta fue:<br />
<br />
<blockquote>No había querido responder tus comentarios porque no tenía nada bueno que decirte, pero ya que insistes me tomaré la molestia.<br />
<br />
1.- No sé de dónde sacas información para formar juicios de valor sobre mi incapacidad para solventar el cuelgue de Turpial, no estás suscrito a la lista de desarrollo, no te conectas al canal IRC, ni siquiera te veo involucrado como colaborador. Para tu información actualmente estoy usando la API nueva que he diseñado para resolver los problemas de Turpial, pero<br />
está en estado experimental. En el desarrollo de software primero se prueban las cosas antes de lanzarlas al público, son controles de calidad y pruebas q debe pasar la aplicación antes de que pueda ser "usada" por el público en general. Pero veo que no conoces mucho al respecto y por eso lanzas esa sarta de comentarios estériles que en lugar de contribuir desmeritan el fuerte trabajo que he estado haciendo estos últimos días para que ustedes puedan disfrutar de un buen cliente.<br />
<br />
2.- Soy una persona con un trabajo de verdad y una vida de verdad, nada me gustaría más que poder dedicarle el 100% de mi tiempo al Turpial, pero lamentablemente eso no me da para comer en estos momentos, así que los pocos instantes libres que me quedan al día son los que invierto en desarrollar el Turpial. Considera eso antes de llamar "incapaz" a otra persona.<br />
<br />
3.- Si no te gusta el desempeño de Turpial o te parece que no programo lo suficientemente rápido pues usa otro cliente y ya. Hay centenares de clientes de twitter allí afuera esperando por tí, que no se cuelgan y con programadores realmente "capaces". Turpial es un hobby para mí y la idea es que la comunidad pueda disfrutar, beneficiarse y sentirse a gusto con mi hobby y pues con eso poner en alto el nombre de nuestra patria, hay mucho talento en éste país y debemos sacarle provecho.<br />
<br />
No tienes ningún compromiso para usar Turpial, eres libre de escoger... así como el software, completamente libre. Si realmente deseas apoyar Turpial entonces razona y analiza las cosas antes de decirlas, involucrate y colabora, así funciona el software libre y es uno de los principios básicos del socialismo, unión, comunidad, participación... <br />
<br />
La mejor forma de cambiar las cosas es formando parte de ellas, no necesitas ser programador para ayudar a Turpial, existen muchas cosas por hacer, eres bienvenido siempre y cuando tus ganas sean de trabajar y de hacer las cosas mejor pero no si lo que tienes es ganas de joder, eso se lo dejamos a los mediocres. Puedes empezar leyendo el archivo de la lista de correos http://smlk.es/6JfSkX, y si gustas únete y aporta tus ideas como un ser racional, pensante e inteligente que eres.<br />
<br />
Saludos y gracias por el apoyo<br />
</blockquote><br />
Me disculpo de antemano si hiero algunas susceptibilidades pero las cosas son como son. Las críticas, cuando son constructivas, vienen acompañadas de un planteamiento o una solución.<br />
<br />
Mis 2 lochas<br />
<br />
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com32tag:blogger.com,1999:blog-6887761988295725170.post-77561607526135969052010-04-11T20:44:00.000-07:002010-04-11T20:45:53.806-07:00Turpial, KumbiaPHP y SigueMiLink trabajando juntos<div style="text-align: justify;"><span style="font-size:85%;">Hace unos días mi buen amigo Deivinson Tejeda (desarrollador del framework <a href="http://www.kumbiaphp.com">KumbiaPHP</a>) me comentó sobre un nuevo acortador de URLs hecho en España llamado <a href="http://smlk.es">SigueMiLink</a>. <br />
<br />
Se preguntarán: ¿Bueno y cuál es la gracia del asunto? Pues lo interesante del asunto es que:<br />
<br />
<ol><li>El acortador de URLs SigueMiLink está hecho en KumbiaPHP</li>
<li>Los creadores de SigueMiLink le plantearon a Deivinson la posibilidad de incluirlo en Turpial</li>
<li>Turpial y KumbiaPHP son como proyectos hermanos :) (ambos son Venezolanos)</li>
</ol><br />
Al parecer todo se confabuló para crear un círculo de apoyo entre los 3 proyectos y así fue. Ahora me complace anunciar que Turpial soporta a SigueMiLink como nuevo servicio acortador de URLs y que tanto los amigos de SigueMiLink como los de KumbiaPHP apoyan fuertemente a Turpial.<br />
<br />
Ojalá se puedan repetir más situaciones como ésta, en la que varios proyectos pueden colaborar entre sí por un fin común... La libertad del software :D<br />
<br />
</span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com4tag:blogger.com,1999:blog-6887761988295725170.post-92045557734700735082010-04-05T19:10:00.000-07:002010-04-05T19:10:21.803-07:00Webcomic: Unfollow<div style="text-align: justify;">Un webcomic para liberar un poco el estrés de la programación... ¡Que lo disfruten! :)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">Clic en la imagen para agrandar<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzUWj-rPUINzQ52qAvwdVT34wVDE51TyZcvNoigvO5lew4IrG9UTNne5XfNyZAGpZTAkQwD7zPTQJdHDui3VI9lQ9ODZ6YXEgKBdfzxAei8Ntg42zdNdeL5zGfBSvQwMueM2deR-xXjbS4/s1600/09+-+Unfollow.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzUWj-rPUINzQ52qAvwdVT34wVDE51TyZcvNoigvO5lew4IrG9UTNne5XfNyZAGpZTAkQwD7zPTQJdHDui3VI9lQ9ODZ6YXEgKBdfzxAei8Ntg42zdNdeL5zGfBSvQwMueM2deR-xXjbS4/s320/09+-+Unfollow.png" width="320" /></a></div><span style="font-size: 85%;"></span></div>Anonymoushttp://www.blogger.com/profile/02127235651227821801noreply@blogger.com0