<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>El Codiguero</title>
	<atom:link href="http://blog.elcodiguero.com/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.elcodiguero.com</link>
	<description></description>
	<lastBuildDate>Sun, 01 Aug 2010 03:19:56 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Definición de funciones en Javascript</title>
		<link>http://blog.elcodiguero.com/javascript/definicion-de-funciones-en-javascript.html</link>
		<comments>http://blog.elcodiguero.com/javascript/definicion-de-funciones-en-javascript.html#comments</comments>
		<pubDate>Sun, 01 Aug 2010 03:19:56 +0000</pubDate>
		<dc:creator>Alvaro</dc:creator>
				<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://blog.elcodiguero.com/?p=31</guid>
		<description><![CDATA[Las funciones son un elemento vital en cualquier lenguaje de programación. Pero no todos los lenguajes las manejan de la misma forma; en muchos son tipos de datos especiales que solamente pueden definirse y luego llamarse con (o sin) parámetros, pero otros como Javascript las tratan como simples variables que pueden ser llamadas. A continuación [...]]]></description>
			<content:encoded><![CDATA[<p>Las funciones son un elemento vital en cualquier lenguaje de programación. Pero no todos los lenguajes las manejan de la misma forma; en muchos son tipos de datos especiales que solamente pueden definirse y luego llamarse con (o sin) parámetros, pero otros como Javascript las tratan como simples variables que pueden ser llamadas. A continuación intento describir de forma sencilla lo que se puede hacer con las funciones en Javascript.</p>
<h2>Funciones con nombre y funciones anónimas</h2>
<p>En todos los lenguajes se pueden declarar funciones con nombre, en javascript esto es tan simple como hacer:<br />
<code class="bloque prettyprint">function mi_funcion(parametros) { }</code><br />
Esto crea una función de nombre <em>mi_funcion</em>, que luego puede llamarse con los parámetros adecuados.</p>
<p>Algunos lenguajes, además, permiten declarar <a href="http://en.wikipedia.org/wiki/Anonymous_function">funciones anónimas</a>: funciones que no tienen un nombre y que sirven para esas ocasiones en las que no se quiere crear una función para usarla solamente una vez. En javascript una situación como esta puede ser crear una función que responda a un evento:<br />
<code class="bloque prettyprint">window.onload = function() {
}</code><br />
El código anterior crea una función sin nombre que se asigna al objeto <em>onload</em>, y que puede ejecutarse luego haciendo una llamada a <code>window.onload()</code></p>
<h2>Métodos de objetos</h2>
<p>Las funciones anónimas se usan también para agregar <em>métodos</em> a objetos, de hecho es la única forma de hacerlo ya que Javascript no posee una construcción del lenguaje para crear clases.<br />
<code class="bloque prettyprint">var miobjeto = {
    un_metodo : function(parametros) {
    }
}</code><br />
En este caso <var>miobjeto</var> posee un método <var>un_metodo</var>, que al llamarse como <code>miobjeto.un_metodo(parametros)</code> ejecuta la función anónima. Esto es posible gracias a que en Javascript las variables son un <em><a href="http://en.wikipedia.org/wiki/First-class_object">tipo de dato de primera clase</a></em></p>
<h2>Tipo de dato de primera clase</h2>
<p>Se puede encontrar una descripción en Wikipedia de lo que significa ser un tipo de dato de primera clase, pero puesto en términos sencillos quiere decir que las funciones son datos como cualquier otro y que pueden por tanto asignarse, crearse, pasarse como parámetro y manipularse casi de cualquier forma. Así, en Javascript suelen pasarse objetos de función a algunas construcciones comunes del lenguaje, por ejemplo:<br />
<code class="bloque prettyprint">function mi_funcion() {
    /* código */
}
setTimeout(mi_funcion, 1000);
setInterval(mi_funcion, 10);
algun_elemento.onclick = mi_funcion;
</code></p>
<div class="relacionados">
<h4>Enlaces relacionados</h4>
<ul>
<li><a href="http://en.wikipedia.org/wiki/Anonymous_function">Wikipedia: función anónima</a></li>
<li><a href="http://en.wikipedia.org/wiki/First-class_object">Wikipedia: Objeto de primera clase</a></li>
<li><a href="http://es.wikipedia.org/wiki/Javascript">Wikipedia: Javascript</a></li>
</ul>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.elcodiguero.com/javascript/definicion-de-funciones-en-javascript.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Base de datos de robots</title>
		<link>http://blog.elcodiguero.com/python/base-de-datos-de-robots.html</link>
		<comments>http://blog.elcodiguero.com/python/base-de-datos-de-robots.html#comments</comments>
		<pubDate>Sun, 27 Jun 2010 17:59:15 +0000</pubDate>
		<dc:creator>Alvaro</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://blog.elcodiguero.com/?p=26</guid>
		<description><![CDATA[¡Finalmente! Luego de mucho tiempo, terminé de adaptar las viejas entradas sobre web bots y las convertí en una sencilla aplicación Django, a modo de experimento. Django es un excelente framework para desarrollo web con Python, así que hace tiempo que quería aprender a usarlo. Fue necesario mucho estudio pero poco código, realmente es un [...]]]></description>
			<content:encoded><![CDATA[<p>¡Finalmente! Luego de mucho tiempo, terminé de adaptar las viejas entradas sobre web bots y las convertí en una sencilla aplicación Django, a modo de experimento.<br />
Django es un excelente <span lang="en">framework</span> para desarrollo web con Python, así que hace tiempo que quería aprender a usarlo. Fue necesario mucho estudio pero poco código, realmente es un placer trabajar con Django y seguramente este pequeño desarrollo se convierta en mi <em>patio de juegos</em> para seguir experimentando.</p>
<p>No creo que sea algo especialmente útil, pero aquí está (y aunque no sea útil lo seguiré actualizando <img src='http://blog.elcodiguero.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  ): <a href="http://codiguero.alwaysdata.net">http://codiguero.alwaysdata.net</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.elcodiguero.com/python/base-de-datos-de-robots.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>¿Javascript, o «script de Java»?</title>
		<link>http://blog.elcodiguero.com/opiniones/javascript-script-de-java.html</link>
		<comments>http://blog.elcodiguero.com/opiniones/javascript-script-de-java.html#comments</comments>
		<pubDate>Sat, 30 Jan 2010 04:15:12 +0000</pubDate>
		<dc:creator>Alvaro</dc:creator>
				<category><![CDATA[Opiniones y Comentarios]]></category>

		<guid isPermaLink="false">http://blog.elcodiguero.com/?p=25</guid>
		<description><![CDATA[He escuchado demasiadas veces la frase script de Java referida a Javascript. Probablemente se trate de una mala traducción que desafortunadamente fue considerada correcta, pero la verdad es que escucharla de un ingeniero en sistemas me choca, y mucho. Saber que gente que sabe piensa que Javascript es simplemente un subconjunto de Java es muy [...]]]></description>
			<content:encoded><![CDATA[<p>He escuchado demasiadas veces la frase <q>script de Java</q> referida a <a href="http://es.wikipedia.org/wiki/Javascript">Javascript</a>. Probablemente se trate de una mala traducción que desafortunadamente fue considerada correcta, pero la verdad es que escucharla de un ingeniero en sistemas me choca, y mucho. Saber que gente <q>que sabe</q> piensa que Javascript es simplemente un subconjunto de <a href="http://www.java.com">Java</a> es muy frustrante. Por eso es mi intención ahora mismo nombrar alguna de las diferencias que se me vienen a la mente, simplemente para tenerlo como referencia la próxima vez que me toque explicarle a alguien por qué Javascript NO significa <q>script de Java</q>.</p>
<p>Exponer las diferencias entre Java y Javascript se ha hecho muchas veces antes, pero tengo la sensación de que la mayoría de las veces que se escribió sobre el tema se hizo siendo Javascript un lenguaje despreciado y considerado casi un <q>juguetito</q> de las páginas web. Javascript es mucho más que <q>un Java básico y con algunas cosas raras</q>, e intentaré hacer valer eso en este artículo.</p>
<h3>Historia</h3>
<p>Dejaré que Wikipedia mencione los detalles sobre la <a href="http://es.wikipedia.org/wiki/Javascript#Historia_y_denominaci.C3.B3n">historia del lenguaje</a>. Lo principal en esta historia es que el lenguaje que hoy conocemos como Javascript comenzó su vida llamándose LiveScript: el cambio de nombre fue únicamente (según algunos) para aprovechar la publicidad que se hacía en la época a Java y el impulso comercial que éste estaba teniendo.</p>
<h3>Ejecución</h3>
<p>Partiendo en ambos lenguajes de un código fuente válido, el proceso que sigue un programa en Java es bastante diferente al que sigue un programa en Javascript.</p>
<h4>Java</h4>
<p>Un programa Java se compila, generando un archivo .class por cada clase o un <acronym xml:lang="en" title="JAva Archive"><a href="http://es.wikipedia.org/wiki/Jar">JAR</a></acronym> que incluye todas las clases.<br />
Es la <acronym xml:lang="en" title="Java Virtual Machine"><a href="http://es.wikipedia.org/wiki/JVM">JVM</a></acronym> (Máquina Virtual de Java) la que luego ejecuta el programa.</p>
<h4>Javascript (en un navegador)</h4>
<p>El código fuente Javascript se incluye en la página <acronym title="HyperText Markup Language" xml:lang="en"><a href="http://es.wikipedia.org/wiki/HTML">HTML</a></acronym> mediante una etiqueta <code class="linea">&lt;script></code> o <code class="linea">&lt;link></code> en el caso de un archivo externo. El navegador interpreta el código y lo ejecuta en un entorno restringido a la página desde la cual es vinculado.</p>
<h3>Sintaxis</h3>
<p>Tanto Java como Javascript utilizan una sintaxis basada en la de C.<br />
Se podría decir que Java se parece más a C++, mientras que Javascript se parece más a C.</p>
<h3>Tipos de datos</h3>
<p>Java incluye los <a href="http://es.wikipedia.org/wiki/Tipo_de_dato">tipos de datos</a> básicos <q>integer</q> (y derivados), <q>String</q>, <q>boolean</q>, <q>float</q> y <q>double</q>. Además, cada clase es un tipo de dato con el que podemos crear variables. Una variable de un tipo no puede redefinirse como de otro (a menos que <q>otro</q> sea superclase de <q>uno</q>), y se aplican comprobaciones estrictas de tipos. A esto se le llama <q><a href="http://es.wikipedia.org/wiki/Lenguaje_de_programación_fuertemente_tipado">tipado estático</a></q>.</p>
<p>Javascript solamente posee los tipos de datos <q>number</q>, <q>object</q>, <q>string</q>, <q>function</q>. Las variables se definen sin tipo, serán del tipo que sea aquello que se les está asignando. A una variable definida de cierto tipo se le puede asignar un valor de otro tipo (por ejemplo, a una variable entera puede asignársele una cadena), y el tipo de dato de la variable cambia. A esto se le llama <q><a href="http://es.wikipedia.org/wiki/Tipado_dinámico">tipado dinámico</a></q>.<br />
Veamos un par de ejemplos para ver la diferencia. Primero, código Java:<br />
<code class="prettyprint bloque">String variable = "Hola"; <-- variable se declara de tipo String
variable = 1; <-- dará un error de compilación, no se puede asignar un entero.</code><br />
Y ahora un código equivalente en Javascript:<br />
<code class="prettyprint bloque">var variable = "Hola"; <-- variable se declara sin tipo. Como se le asigna una cadena, tendrá el tipo String
variable = 1; <-- variable es ahora de tipo entero. No hay errores de ningún tipo :-D</code></p>
<h3>Ámbito de las variables</h3>
<p>Java, al igual que la mayoría de los lenguajes basados en C, tiene variables de bloque. Es decir, que una variable definida en un bloque de código (delimitado por las llaves { y }) existirá solamente dentro de ese bloque. En caso de producirse ambigüedad (una variable <q>local</q> al bloque se llama igual que una variable externa) tiene prioridad el espacio de nombres local.</p>
<p>En Javascript, por otra parte, solamente las funciones generan su propio espacio de nombres. Los bloques de código utilizan las variables definidas fuera, y si definen alguna variable ésta no desaparecerá cuando el programa salga del bloque, sino que seguirá definida en el espacio de nombres global (o de la función en la que esté el bloque).<br />
Esto es algo bastante molesto en realidad, ya que puede fácilmente generar conflictos y es una de las principales razones (por si no hubiera ya suficientes) para evitar las variables globales.</p>
<h3>Funcional</h3>
<p>En java las funciones NO son un tipo de datos. En Javascript sí: una función es una variable como cualquier otra, que se puede pasar como parámetro a otra función o asignar a una variable. Incluso se pueden declarar funciones anónimas para utilizarlas una sola vez y que no ocupen el espacio de nombres. Se dice que una función es un <a href="http://en.wikipedia.org/wiki/First-class_data_type"><q>tipo de dato de primera clase</q></a>. Esto, junto con otras características, hacen que javascript pueda considerarse un lenguaje funcional, y, en palabras del experto <a href="http://www.crockford.com">Douglas Crockford</a>, <q xml:lang="en">el primer lenguaje lambda en llegar a utilizarse masivamente</q>. (donde <em>lambda</em> proviene del <a href="http://es.wikipedia.org/wiki/Cálculo_lambda">cálculo lambda</a>, y es otro nombre para una función anónima.)</p>
<h3>Orientación a Objetos</h3>
<p>Java es orientado a objetos, como todos sabemos, y utiliza una orientación a objetos <em>clásica</em>, es decir, basada en clases. Los objetos son <em>ejemplares</em> de una clase (disculpen pero detesto utilizar <em>instancia</em> como traducción de <em>instance</em>), y la herencia se da entre clases.</p>
<p>Javascript es también orientado a objetos, pero no usa clases, aunque hay formas de simularlas y se utiliza algo similar a una clase al crear un objeto de tipo <strong>Date</strong>.<br />
En Javascript la herencia se da directamente entre objetos, mediante <a href="http://es.wikipedia.org/wiki/Programación_basada_en_prototipos">prototipos</a>. Todo objeto contiene un atributo oculto que lo vincula a su objeto padre, formando una cadena hasta el objeto padre <strong>Object</strong> (que en los navegadores es <samp>window</samp>).</p>
<p>Un objeto hereda todos los métodos y atributos de su objeto padre, y si al objeto padre se le agrega un método o atributo, los objetos hijos podrán ver inmediatamente el nuevo miembro añadido. Todo objeto contiene un atributo <q>prototype</q> al que se puede agregar miembros (atributos o métodos) para que todos los objetos hijos puedan verlos. Por ejemplo, si agrega un método metodo a <em>String.prototype</em>, todas las cadenas contendrán el método.</p>
<h3>Conclusión</h3>
<p>Espero que este repaso por algunas de las principales diferencias entre Java y Javascript sirva de algo, con un poco de suerte estaré ayudando a que ya nadie piense en Javascript como un hermanito menor de Java. Para aquellos que quieran profundizar en el lenguaje, recomiendo los videos disponibles en el <a href="http://developer.yahoo.com/yui/theater/" xml:lang="en">YUI Theater</a> y el libro <a xml:lang="en" href="http://www.amazon.com/exec/obidos/ASIN/0596517742/">Javascript: The Good Parts</a> de Douglas Crockford.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.elcodiguero.com/opiniones/javascript-script-de-java.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Slackware 13.1: Instalación y configuración básica</title>
		<link>http://blog.elcodiguero.com/linux/slackware-13-instalacion-y-configuracion-basica.html</link>
		<comments>http://blog.elcodiguero.com/linux/slackware-13-instalacion-y-configuracion-basica.html#comments</comments>
		<pubDate>Sat, 30 Jan 2010 03:59:20 +0000</pubDate>
		<dc:creator>Alvaro</dc:creator>
				<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://blog.elcodiguero.com/?p=24</guid>
		<description><![CDATA[Slackware es la distribución de GNU/Linux más antigua entre las que siguen desarrollándose. Se caracteriza por su fama de complicada, sin embargo una vez que el sistema está configurado el mantenimiento necesario es mínimo, y resulta tan sencilla de manejar como cualquier otra distribución. Lo único que el sistema pide a cambio de su estabilidad [...]]]></description>
			<content:encoded><![CDATA[<div class="t_centrado"><a href="http://www.slackware.com" title="Ir al sitio de Slackware"><img src="/archivos/slackware.png" alt="Slackware" /></a></div>
<p><a href="http://www.slackware.com">Slackware</a> es la distribución de <a href="http://es.wikipedia.org/wiki/GNU/Linux">GNU/Linux</a> más antigua entre las que siguen desarrollándose. Se caracteriza por su fama de <q>complicada</q>, sin embargo una vez que el sistema está configurado el mantenimiento necesario es mínimo, y resulta tan sencilla de manejar como cualquier otra distribución. Lo único que el sistema pide a cambio de su estabilidad y facilidad de mantenimiento es algo de tiempo leyendo manuales y ayudas para comprender lo que sucede y por qué. La instalación, configuración y uso de este sistema es probablemente la mejor forma de aprender a manejar un sistema Unix.</p>
<p>Queda claro que le tengo un aprecio bastante grande, sin embargo soy capaz de reconocer que, como todo, esta distribución no es perfecta y tiene sus problemas. Y para solucionar los problemas suele requerirse un conocimiento de lo que se hace. Es por eso que quiero compartir mi experiencia instalando Slackware 13.0 en mi computadora <a href="http://en.wikipedia.org/wiki/Dell_Vostro">Dell Vostro 1000</a>.</p>
<h3>Instalación</h3>
<p>La instalación en sí no es un problema, recomiendo <a href="http://manual.zenwalk.org/manual_es.pdf">esta guía de instalación</a> para aquellos que no hayan realizado este proceso antes. La guía es para la distribución <a href="http://www.zenwalk.org">ZenWalk</a>, pero el proceso es casi el mismo ya que <em>ZenWalk</em> es una de las tantas distribuciones derivadas de Slackware.</p>
<p>Un punto a recordar es que, una vez en el programa de instalación, debe iniciarse el proceso eligiendo la opción <strong>ADDSWAP</strong> en el menú, o <strong>TARGET</strong>. Estos pasos deben hacerse primero ya que son los que preparan el disco en el que se instalará, si no se hacen primero siempre se llega a un punto en la instalación en el que se debe iniciar todo el proceso de nuevo.<br />
Y esto siempre requiere salir del programa de instalación y abrirlo de nuevo, ya que la autodetección del disco CD/DVD de instalación funciona solamente la primera vez que se hace.</p>
<p>De todas formas, la instalación con las opciones por defecto (<em xml:lang="en">full install</em>, instalar todos los paquetes disponibles) puede iniciarse en menos de 5 minutos y terminarse en más o menos media hora.</p>
<p>Al terminar la copia de archivos se hacen algunos pasos para la configuración inicial del sistema, principalmente lo que respecta a la ubicación geográfica, la configuración de red y la instalación del gestor de arranque (<a href="http://es.wikipedia.org/wiki/Lilo_(Linux)">LILO</a>). Una vez finalizados estos pasos, el programa de instalación se cierra y se nos indica que debemos reiniciar el sistema.</p>
<h3>Configuración inicial</h3>
<p>Al arrancar el sistema, a diferencia de lo que sucede con otras distribuciones, el entorno gráfico no se inicia por defecto. Se nos presenta una pantalla de inicio de sesión en modo texto, en la que deberemos entrar como el usuario <q>root</q> y con la contraseña correspondiente (que habremos indicado en uno de los últimos pasos de la instalación).</p>
<p>Una vez dentro, hay varias tareas a realizar.</p>
<h4>Arrancar por defecto el entorno gráfico</h4>
<p>Esto se hace editando el archivo <samp>/etc/inittab</samp> y cambiando el <a href="http://es.wikipedia.org/wiki/Nivel_de_ejecución">nivel de ejecución</a> predeterminado. Lo haremos con <samp><a href="http://es.wikipedia.org/wiki/Vim">vim</a></samp><br />
<samp class="bloque">root@alvaro:/# vim /etc/inittab</samp><br />
Una vez que se abra el archivo, hay que ir a la línea que contiene<br />
<samp class="bloque"># Default runlevel. (Do not set to 0 or 6)
id:3:initdefault:</samp></p>
<p><samp>vim</samp> no se parece en nada a los editores <q>normales</q>, por lo que para quien lo usa por primera vez puede resultar bastante extraño. La forma más sencilla de editar lo necesario y salir es llevar el cursor hacia el <strong>3</strong> en la línea mencionada antes, y presionar la siguiente secuencia de teclas:</p>
<ul>
<li><strong>r</strong> : Pondrá el editor en el modo adecuado para reemplazar un caracter</li>
<li><strong>4</strong> : escribirá un 4, reemplazando al 3</li>
<li><strong>:wq</strong> : este comando guarda el archivo y sale de <samp class="linea">vim</samp>
</ul>
<p>Con esto, se indica que el nivel de ejecución por defecto debe ser el nivel 4. Normalmente cualquier documentación de GNU/Linux que se encuentre indica que el nivel 4 no se utiliza, y además casi todas las demás distribuciones utilizan el nivel 5 para el modo gráfico. En esto Slackware es diferente, en tanto que utiliza el nivel 4 para el modo gráfico y no utiliza el nivel 5.</p>
<h4>Crear un usuario sin privilegios</h4>
<p>La forma usual de utilizar el sistema es como usuario sin privilegios. Esto es, sin permiso para instalar programas o modificar el sistema, únicamente se tiene permiso para hacer modificaciones en la carpeta asignada al usuario. Este modo es ideal por la seguridad y para evitar errores fatales, que casi todos hemos cometido alguna vez. Añadiremos un usuario, entonces, con el comando <samp>adduser</samp> y escribiendo la información que el comando solicita.</p>
<p>Para que este usuario sea <q>usable</q>, es necesario añadirlo a varios grupos: <q>audio</q> (para que pueda utilizar la tarjeta de sonido), <q>cdrom</q>, <q>disk</q>, <q>plugdev</q> (para que pueda utilizar dispositivos extraíbles), y <q>video</q>. Esto se puede hacer facilmente; en uno de los pasos <samp>adduser</samp> muestra la siguiente línea:<br />
<samp class="bloque">Additional groups (comma separated) []:</samp><br />
Si se presiona la flecha hacia arriba, se obtiene una lista de los grupos necesarios.</p>
<h4>Utilizar el <q xml:lang="en">kernel</q> <q xml:lang="en">generic</q> en lugar de <q xml:lang="en">huge</q></h4>
<p>Para la instalación, y por defecto en la instalación completa, Slackware instala un núcleo Linux que tiene compilado casi todos los controladores disponibles. Esto implica que el núcleo por defecto es bastante grande e ineficiente, aunque en una instalación funciona bien. Para el uso diario es mejor cambiar a un núcleo que incluya solamente lo mínimo, y en el que los controladores estén incluidos en forma de módulos (de tal forma que solamente se cargarán aquellos que sean necesarios).<br />
Este cambio requiere de varias modificaciones, que quizás sea mejor hacer desde el entorno gráfico (se requiere edición de archivos, y es más fácil usar <samp>kwrite</samp> que <samp>vim</samp>.) Por lo que reiniciar en este momento e iniciar sesión como root en el entorno gráfico puede ser la mejor idea.</p>
<h5>Vincular al <q xml:lang="en">kernel</q> correcto</h5>
<p>Desde la consola, el siguiente comando<br />
<samp class="bloque">cd /boot ; ls -l</samp><br />
Debería mostrar algo como esto (eliminé algunas líneas innecesarias):<br />
<samp class="bloque">lrwxrwxrwx  1 root root      35 2008-12-18 00:49 System.map -> System.map-huge-smp-2.6.29.6-smp
-rw-r--r--  1 root root  965704 2008-11-21 02:20 System.map-generic-smp-2.6.29.6-smp
lrwxrwxrwx  1 root root      32 2008-12-18 00:49 vmlinuz -> vmlinuz-huge-smp-2.6.29.6-smp
-rw-r--r--  1 root root 2347888 2008-11-21 02:20 vmlinuz-generic-smp-2.6.29.6-smp</samp></p>
<p>Los archivos <samp>System.map</samp> y <samp>vmlinuz</samp> son enlaces que apuntan a las versiones <q xml:lang="en">huge</q> del núcleo (esto puede verse indicado por las flechas -> junto al nombre del archivo). Debemos vincularlas a las versiones <q xml:lang="en">generic</q>. Para esto, se utilizan los siguientes comandos:<br />
<samp class="bloque">rm System.map vmlinuz
ln -s vmlinuz-generic-smp-2.6.27.7-smp vmlinuz
ln -s System.map-generic-smp-2.6.27.7-smp System.map</samp><br />
Obviamente conviene hacer uso del autocompletado en <a href="http://es.wikipedia.org/wiki/Bash">bash</a>, para no tener que recordar todo el nombre del archivo (presionar tecla tabulador).</p>
<p>El siguiente paso es crear el <a href="http://es.wikipedia.org/wiki/Initrd">archivo initrd</a> correspondiente. Dado que el núcleo genérico no tiene los controladores en el núcleo mismo sino como módulos, es necesario brindarle algunos controladores iniciales para que pueda encontrar el disco duro, leerlo, y cargar el resto de los módulos que necesita. Estos controladores iniciales se guardan en el archivo initrd.<br />
Las instrucciones necesarias para crear este archivo están en el archivo <samp>README.initrd</samp> dentro de la carpeta <samp>/boot</samp>. Básicamente, para un sistema cuya partición principal es de tipo <samp><a href="http://es.wikipedia.org/wiki/Ext3">ext3</a></samp>, y está en <samp>/dev/sda1</samp> (la primera partición primaria del primer disco <a href="http://es.wikipedia.org/wiki/Sata">SATA</a>), el comando necesario es<br />
<samp class="bloque">mkinitrd -c -k 2.6.27.7-smp -m mbcache:jbd:ext3 -f ext3 -r /dev/sda1</samp><br />
(dentro de la carpeta <samp>/boot</samp>)<br />
El último paso ahora es configurar <span xml:lang="en">LILO</span> para que cargue este archivo <span xml:lang="en">initrd</span>. Habrá que editar el archivo <samp>/etc/lilo.conf</samp>, buscar la sección correspondiente y agregar una línea al bloque<br />
<samp class="bloque"># Linux bootable partition config begins
image = /boot/vmlinuz
root = /dev/sda1
label = Linux
read-only
# Linux bootable partition config ends</samp></p>
<p>que deberá quedar como<br />
<samp class="bloque"># Linux bootable partition config begins
initrd = /boot/initrd.gz
image = /boot/vmlinuz
root = /dev/sda1
label = Linux
read-only
# Linux bootable partition config ends</samp></p>
<p>Ya que estamos editando el archivo <samp>lilo.conf</samp>, conviene también editar el tiempo que se espera antes de arrancar el sistema, en la pantalla de selección de <acronym title="Sistema Operativo"><a href="http://es.wikipedia.org/wiki/SO">SO</a></acronym>. Por defecto este tiempo es 2 minutos, recomiendo cambiarlo a un tiempo que sea corto pero permita interrumpir el proceso en caso de ser necesario (por ejemplo, medio segundo)<br />
Habrá que ir hacia el principio del archivo lilo.conf y cambiar:<br />
<samp class="bloque"># This is given in tenths of a second, so 600 for every minute:
timeout = 1200</samp><br />
por<br />
<samp class="bloque"># This is given in tenths of a second, so 600 for every minute:
timeout = 50</samp></p>
<p>Una vez realizados estos cambios, hay que re-instalar <span xml:lang="en">LILO</span>. Este proceso es simplemente ejecutar el comando <samp>lilo</samp> en la terminal. Recomiendo ejecutar primero <samp>lilo -t</samp> (hacer una simulación de la instalación) y luego, si no hay errores (puede haber advertencias), ejecutar <samp>lilo</samp>.</p>
<p>Ahora, reiniciar el sistema nuevamente para cargar el núcleo genérico.</p>
<h3>Configuración de una computadora portátil</h3>
<p>Si tienes una computadora de escritorio, no se requieren más detalles de configuración, todo debería haber sido reconocido y autoconfigurado. El resto será cuestión de configurar los detalles del entorno gráfico. Pero si como yo tienes una computadora portátil, habrá otros detalles a configurar.</p>
<h4>Red inalámbrica</h4>
<p>Es muy probable que el dispositivo de red inalámbrica no tenga controladores para Linux. En este caso habrá que apelar a <a href="http://es.wikipedia.org/wiki/NDISwrapper">ndiswrapper</a>, un programa que es capaz de utilizar los controladores de Windows que estos dispositivos incluyen. ndiswrapper no está incluido en la distribución de Slackware, pero puede conseguirse sin dificultad en <a href="http://www.slackbuilds.org">SlackBuilds.org</a>.<br />
También se pueden encontrar allí otros controladores nativos para Linux, por ejemplo el controlador para las tarjetas Broadcom.</p>
<p>Vale la pena notar, sin embargo, que ndiswrapper no puede funcionar correctamente en un sistema de 64bit, no es capaz de cargar un controlador de 32bit de Windows en un sistema de 64bit. Así que, aún si tu equipo lo admite, asegúrate de tener los controladores necesarios antes de instalar Slackware64.</p>
<p>Para el control de la red inalámbrica Slackware incluye el programa <a href="http://wicd.sourceforge.net">wicd</a> en la carpeta <samp>/extra</samp>. Bastará entonces con instalar el paquete desde esa carpeta. Algo importante a recordar es que, para que <samp>wicd</samp> funcione correctamente, es necesario darle permisos de ejecución al archivo <samp>/etc/rc.d/rc.wicd</samp>, y <strong>remover la configuración que se encuentra en el archivo <samp>/etc/rc.d/rc.inet1.conf</samp></strong>. Para lograr esto simplemente se agrega una almohadilla <q>#</q> a toda línea de este archivo que no comience con una.<br />
Si todo va bien entonces, al reiniciar habrá un ícono de <samp>wicd</samp> al lado del reloj, pronto para conectarse a alguna de las redes detectadas.</p>
<h4>Synaptics, el controlador para el <q xml:lang="en">touchpad</q></h4>
<p>El controlador synaptics viene incluido en Slackware, y el nuevo X.Org incluido en Slackware 13 lo carga correctamente sin que haya que configurar nada. Pero por defecto desactiva funciones como el hacer clic tocando la superficie del <em>touchpad</em>.<br />
Para corregir este comportamiento, se pueden seguir dos caminos. El primero es editar el archivo xorg.conf, como se ha hecho <q>toda la vida</q>. El segundo es modificar la configuración de <a href="http://es.wikipedia.org/wiki/HAL_(software)">HAL</a>, lo cual es más acorde a la nueva forma de funcionar de X.Org.</p>
<p>El segundo camino es algo más complicado que el primero, pero requiere conocer menos datos. Si se opta por modificar xorg.conf deben conocerse entre otras cosas el nombre de dispositivo y el protocolo a usar. Sin embargo, modificando la configuración de HAL se indica al sistema qué opciones dar por defecto a un dispositivo que use el controlador synaptics.</p>
<p>En mi caso habilité el botón primario (izquierdo) al tocar con el dedo la superficie del touchpad, el secundario (derecho) en la esquina superior derecha, y el botón central en la esquina inferior derecha.<br />
También habilité el desplazamiento (<em>scroll</em>) horizontal y vertical deslizando el dedo sobre los bordes inferior y derecho respectivamente.<br />
<a href="/archivos/99-x11-synaptics.fdi">El archivo completo está aquí</a>. Las opciones posibles se detallan en la página del manual de synaptics.<br />
Este archivo debe copiarse a la carpeta <samp>/etc/hal/fdi/policy</samp></p>
<h4>Configuración de ahorro de energía</h4>
<p>En KDE4, el Gestor de Energía Guidance y el módulo de Gestión de Energía en Preferencias del Sistema pueden configurar sin problemas casi todos los aspectos de ahorro de energía, excepto por un detalle: qué se hace cuando se presiona el botón de encendido/apagado.<br />
<a href="http://www.linux.com/articles/54610">En esta página se explica cómo se debe configurar esto</a>, el artículo es viejo y la mayor parte de lo que se explica ya no es necesario (gracias a la combinación Guidance/Preferencias del sistema mencionada antes)<br />
Los pasos para que al presionar el botón la computadora <a href="http://en.wikipedia.org/wiki/Hibernate_(OS_feature)">hiberne</a> en vez de apagarse son los siguientes:</p>
<ol>
<li>Cambiar a la carpeta <samp>/etc/acpi</samp></li>
<li>Dejar en esta carpeta únicamente el archivo <samp>acpi_handler.sh</samp> y crear las carpetas <samp>events</samp> and <samp>actions</samp> en caso de ser necesario.
<li>
<li>Crear un archivo <samp>/etc/acpi/actions/pwrbtn.sh</samp> con permisos de ejecución, y con el siguiente contenido:<br />
<samp class="bloque">#!/bin/sh
pm-hibernate</samp>
</li>
<li>Crear un archivo <samp>/etc/acpi/events/pwrbtn</samp> con el siguiente contenido:<br />
<samp class="bloque">event=button[ /]power
action=/etc/acpi/actions/pwrbtn.sh</samp>
</li>
</ol>
<p>Listo, al presionar el botón de encendido la computadora hibernará en vez de apagarse. ¿Pero cómo restaurar?<br />
Lo que hace la hibernación es mover el contenido de la memoria <a href="http://es.wikipedia.org/wiki/Memoria_de_acceso_aleatorio">RAM</a> y de algunas otras cosillas a la partición <a href="http://es.wikipedia.org/wiki/Espacio_de_intercambio">swap</a>. Para poder restaurar el sistema es necesario indicarle al núcleo la partición de <span xml:lang="en">swap</span>.<br />
Hay que editar el archivo <samp>lilo.conf</samp> de nuevo, buscar la sección de Linux y añadirle la siguiente línea:<br />
<samp class="bloque">append = "resume=/dev/sda2" </samp><br />
(donde <samp>/dev/sda2</samp> es la partición de <span xml:lang="en">swap</span>).<br />
Para finalizar, hay que volver a ejecutar el comando <samp>lilo</samp> (como antes, primero <samp>lilo -t</samp> para asegurarse de que todo es correcto, luego <samp>lilo</samp> para hacer la instalación). Con esto, si la máquina fue hibernada, el núcleo restaurará el estado. En caso de que la máquina haya sido apagada, se hará un arranque normal. En mi caso tengo configuradas dos secciones idénticas, una con el <q xml:lang="en">resume</q> y otra sin él, simplemente para hacer pruebas de vez en cuando.</p>
<h3>Pasos siguientes</h3>
<h4>Instalación de programas</h4>
<p>Slackware no tiene repositorios de paquetes, todos los paquetes oficiales están en el DVD de instalación. Para instalar programas externos se recomienda utilizar SlackBuilds, que son scripts que automatizan la generación de paquetes a partir de archivos de código fuente. El repositorio más grande de SlackBuilds es <a href="http://slackbuilds.org">slackbuilds.org</a>, y la forma más sencilla de utilizar ese repositorio es a través de una herramienta llamada <a href="http://sbopkg.org/">sbopkg</a>.<br />
La mayor parte de los programas tienen su SlackBuild, pero no todos. Para estos últimos lo mejor es descargar el código fuente y crear un paquete con <a href="http://www.src2pkg.net/">src2pkg</a>. En la mayoría de los casos la compilación será exitosa. Si no lo es, no queda otra opción más que pedir ayuda en algún foro para intentar resolver el problema.</p>
<p>Existen también repositorios extraoficiales de paquetes binarios, siendo los más grandes <a href="http://linuxpackages.net">LinuxPackages</a> y <a href="http://slacky.eu">Slacky</a>.</p>
<h4>GNOME</h4>
<p>Por defecto Slackware no incluye el escritorio <a href="http://gnome.org">GNOME</a>. Hay varias formas de incluirlo, la mejor hoy en día es <a href="http://gnomeslackbuild.org/">GNOME SlackBuild</a>. Incluye no solamente el escritorio sino también programas relacionados con él, como Rythmbox, Brasero, Mono y muchos más. Vale la pena tener disponibles los repositorios para incluir también programas que no son de GNOME pero Slackware no incluye y GSB sí, como OpenOffice.</p>
<p>Algunos enlaces interesantes:</p>
<ul>
<li><a href="http://www.slackware.cl">SlackwareCL</a>: comunidad de usuarios de Slackware de Chile, tienen <a href="http://www.slackware.cl/?q=guia_slackware">una guía</a> que puede resultar interesante.</li>
<li><a href="http://www.slacky.eu/">Slacky</a>: comunidad de usuarios de Italia.</li>
</ul>
<p>Se supone que esta es solamente una guía básica para tener el sistema más o menos funcionando. Si algo no termina de quedar bien, recomiendo el excelente foro de Slackware en <a href="http://www.linuxquestions.org">LinuxQuestions</a>, o el no tan grande pero igualmente útil foro de Sistemas Unix en <a href="http://www.forosdelweb.com">Foros del Web</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.elcodiguero.com/linux/slackware-13-instalacion-y-configuracion-basica.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Consejos para evitar el SPAM</title>
		<link>http://blog.elcodiguero.com/varios/consejos-para-evitar-el-spam.html</link>
		<comments>http://blog.elcodiguero.com/varios/consejos-para-evitar-el-spam.html#comments</comments>
		<pubDate>Tue, 19 Jan 2010 00:53:26 +0000</pubDate>
		<dc:creator>Alvaro</dc:creator>
				<category><![CDATA[Varios]]></category>

		<guid isPermaLink="false">http://blog.elcodiguero.com/?p=23</guid>
		<description><![CDATA[Introducción El spam, o correo basura es sin duda alguna la parte más molesta de trabajar con correo electrónico. Otros términos que se usan frecuentemente son UCE (siglas en inglés de Correo electrónico comercial no solicitado) o UBE (siglas en inglés de Correo electrónico masivo no solicitado). La peor parte del correo basura es que [...]]]></description>
			<content:encoded><![CDATA[<h3>Introducción</h3>
<p>El <q xml:lang="en">spam</q>, o <q>correo basura</q> es sin duda alguna la parte más molesta de trabajar con correo electrónico.<br />
Otros términos que se usan frecuentemente son <acronym xml:lang="en" title="Unsolicited Commercial E-mail">UCE</acronym> (siglas en inglés de <q>Correo electrónico comercial no solicitado</q>) o <acronym xml:lang="en" title="Unsolicited Bulk E-mail">UBE</acronym> (siglas en inglés de <q>Correo electrónico masivo no solicitado</q>).<br />
La peor parte del correo basura es que los costos para quien lo envía es casi nulo. Quien debe asumir el costo de transporte, almacenamiento y filtrado (automático y manual) de los mensajes es el proveedor de correo del destinatario. También hay que considerar la pérdida de tiempo que insume el filtrado manual de los mensajes, sobretodo cuando la cuenta de correo ha sido comprometida y recibe grandes cantidades de basura.</p>
<p>Si bien los proveedores de correo tienen cada vez mejores filtros automáticos que nos evitan tener que eliminar manualmente la mayoría de los mensajes basura, aún hay muchas precauciones y buenas prácticas que uno puede tomar para evitar que la casilla se inunde.<br />
Sobretodo es importante conocer y difundir las mejores prácticas, y enseñar a los novatos a reconocer las amenazas que el <q xml:lang="en">spam</q> trae consigo (fraudes, estafas, venta de productos ilegales, etc). Aunque parezca increíble, hay mucha gente sin experiencia que cae con facilidad en estos engaños.</p>
<h3>¿Cómo obtienen las direcciones de correo los <q xml:lang="en">spammers</q></h3>
<h4>Direcciones de correo visibles en la web</h4>
<p>Los <q xml:lang="en">spammers</q> (aquellos quienes envían el correo basura) utilizan varios métodos para recolectar nuestras direcciones de correo. Uno de ellos es el uso de <q>robots</q> que recorren las páginas web buscando direcciones. Es decir, si mi dirección de correo electrónico está públicamente visible en una página web, tarde o temprano un robot <q xml:lang="en">spammer</q> la encontrará.</p>
<h4>Cadenas</h4>
<p>Por regla general las cadenas deberían eliminarse sin prestarles mucha atención. Sin embargo, las personas suelen reenviarlas a todos sus contactos, lo cual provoca que luego de 4 o 5 reenvíos el cuerpo del mensaje contenga decenas de direcciones. Si de casualidad el mensaje sigue reenviándose, podría caer eventualmente en poder de un <q xml:lang="en">spammer</q>, muchas veces debido a <a href="http://es.wikipedia.org/wiki/Spam#Troyanos_y_ordenadores_zombis">troyanos u otra clase de programas maliciosos</a>.</p>
<p>Sin entrar en los medios técnicos (que deberían estar a cargo de los administradores de los servidores de correo, no de los usuarios), aún hay algunas prácticas que pueden seguirse para evitar el daño causado por el correo basura.</p>
<h3>Evitar el <q xml:lang="en">spam</q> antes de que llegue</h3>
<p>La mejor manera de evitar el correo basura es <strong>evitar que nuestra dirección de correo electrónico termine en la base de datos</strong> de algún delincuente (en muchos países del mundo el envío de <q xml:lang="en">spam</q> es un delito).</p>
<p>Una buena práctica es <strong>tener al menos dos cuentas diferentes</strong>, una para uso personal y otra para suscripciones a sitios web. Con esto se expone solamente una de las cuentas, que además como no se utiliza para temas personales se puede reemplazar de forma sencilla si comienza a recibir gran cantidad de mensajes basura.</p>
<p>En conjunción con esta práctica, es importante dejar la dirección de correo solamente en sitios medianamente confiables, ya que uno nunca sabe qué puede hacer el creador del sitio con la información almacenada.</p>
<p>Otro punto a considerar es el de las cadenas, como se mencionó arriba. Todos tenemos algún conocido o amigo que no conoce (o pasa por alto) el problema que conlleva el reenviar correo incluyendo todas las direcciones en el campo <q>Para</q>, y sin borrar las cabeceras del mensaje que quiere reenviar.<br />
Entonces, para evitar que nuestra cuenta termine en alguna máquina infectada por un troyano (que a su vez envíe la información al servidor de un <q xml:lang="en">spammer</q>), es importante explicarle a la gente lo importante que es NO reenviar cadenas, o si se reenvían hacerlo con los cuidados necesarios: enviando solamente el contenido y no las cabeceras de los mensajes anteriores, y poniendo las direcciones en el campo <q>CCO</q> (copia oculta) en vez de en <q>CC</q> para que así cada destinatario no vea las direcciones de los demás.</p>
<h3>Qué hacer cuando ya se está recibiendo correo basura</h3>
<p>La mayoría de las veces la precaución no es suficiente: la cuenta comienza a recibir correo basura de todas formas.<br />
La estrategia en este punto deja de ser unicamente la protección de la cuenta (aunque claramente esto se debe seguir haciendo) para pasar a ser el pretender que la cuenta no existe. Esto es, cualquier respuesta que se envíe a los <q xml:lang="en">spammers</q> solamente logrará que nos llegue aún más basura. Por este motivo nunca, bajo ningún concepto, se debe responder a ningún correo no solicitado.</p>
<p>Pero no responder no es suficiente para <q>fingir</q> que la cuenta no existe: el cliente de correo que usemos nos puede traicionar (Thunderbird, Outlook Express, o incluso los clientes web de Gmail o Yahoo). Por esto se recomienda NO abrir mensajes sospechosos, o abrirlos únicamente si el cliente de correo está configurado para mostrarlos en <q>modo texto</q>.<br />
El motivo es sencillo: si el cliente de correo admite formato de texto, admitirá imágenes y otras clases de archivos externos. Es muy sencillo incluir un código extra para que a la vez que se envía la petición para obtener esa imagen o archivo externo, se envíe una confirmación de que la cuenta de correo es válida.</p>
<p>No deberíamos tampoco hacer clic en ningún enlace que el correo incluya. En el pasado ha habido fallos en todos los clientes de correo y navegadores, que permiten ir hacia un sitio falseando su dirección. Es decir, cuando uno piensa que está en el sitio de su banco, al que llegó a través de un enlace en un correo, bien podría estar en realidad en un sitio fraudulento.</p>
<p>Una de las reglas generales que siempre deberían seguirse es: <strong>si el mensaje no fue solicitado, proceder con precaución</strong>. No importa si parece venir de una empresa o sitio web conocido, las direcciones de correo electrónico son fácilmente falseables. Incluso algo tan confiable a primera vista como <q>soporte@microsoft.com</q> podría ser en realidad un fraude.</p>
<p>Afortunadamente para nosotros los hispanohablantes, la mayoría del correo basura se envía en inglés. Por lo que si no tenemos ningún conocido que hable en inglés, se puede descartar con seguridad cualquier correo que no esté en español.</p>
<h4>Nunca comprar</h4>
<p>Hay que reconocerlo, a veces las ofertas de lo que se vende son interesantes. Pero con el bajo costo que tiene enviar un correo, una sola compra seguramente compense el envío de millones de mensajes no solicitados. Por este motivo es fundamental no comprar nada que se oferte a través de <q xml:lang="en">spam</q>, ya que las ventas que se consiguen son el principal motivo para enviarlo (y eso sin contar que en la mayoría de los casos no se trata de comerciantes sino de estafadores, por lo que solamente terminaríamos perdiendo el dinero).</p>
<h3>Sustitución de cuentas</h3>
<p>A pesar de todo lo comentado, hay que saber reconocer cuando nos han vencido: si la cuenta de correo recibe demasiada basura, no queda otra opción que reemplazarla. Por esto es importante tener una cuenta para asuntos personales y otra para lo demás, es de suponer que la cuenta utilizada solamente para asuntos personales estará más protegida, menos gente la conocerá.</p>
<p>Lamentablemente, debido a los delincuentes una herramienta tan útil como el correo electrónico a veces se vuelve inusable. Es imprescindible entonces poner todos un granito de arena para evitar que la plaga se vuelva aún peor.</p>
<div class="relacionados">
<h4>Enlaces relacionados</h4>
<ul>
<li><a href="http://es.wikipedia.org/wiki/Spam">Wikipedia: Spam</a></li>
</ul>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.elcodiguero.com/varios/consejos-para-evitar-el-spam.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>¿Cómo escribir un archivo robots.txt?</title>
		<link>http://blog.elcodiguero.com/varios/como-escribir-archivo-robots-txt.html</link>
		<comments>http://blog.elcodiguero.com/varios/como-escribir-archivo-robots-txt.html#comments</comments>
		<pubDate>Tue, 19 Jan 2010 00:42:11 +0000</pubDate>
		<dc:creator>Alvaro</dc:creator>
				<category><![CDATA[Varios]]></category>

		<guid isPermaLink="false">http://blog.elcodiguero.com/?p=22</guid>
		<description><![CDATA[Un archivo robots.txt es un archivo de texto simple, que sirve para indicar a los robots (en especial a los robots de los buscadores) qué partes del sitio no debería indexar. Se considera que se puede indexar cualquier carpeta o archivo que no esté explícitamente prohibido por este archivo. Los buscadores más importantes lo respetan [...]]]></description>
			<content:encoded><![CDATA[<p>Un archivo <code>robots.txt</code> es un archivo de texto simple, que sirve para indicar a los robots (en especial a los robots de los buscadores) qué partes del sitio no debería indexar. Se considera que se puede indexar cualquier carpeta o archivo que no esté explícitamente prohibido por este archivo.</p>
<p>Los buscadores más importantes lo respetan (de otra forma su utilidad sería bastante escasa).</p>
<p>Hay varios motivos para usar un archivo <code>robots.txt</code>:</p>
<ul>
<li>Evitar el indexado de las imágenes</li>
<li>Evitar que un robot consuma demasiado ancho de banda</li>
<li>Dirigir a los buscadores a las páginas especialmente preparadas para ellos</li>
</ul>
<h3>¿Cómo crearlo?</h3>
<p>El archivo <code>robots.txt</code> es un archivo de texto simple, que se ubica en la raíz del sitio. El formato del archivo es extremadamente simple, consta de 1 o más conjuntos, separados por una línea en blanco, de:</p>
<ul>
<li>Una línea <code>User-agent</code>, indicando el nombre de un robot o "*"; (todos los robots).
<p>Si bien la especificación de robotstxt.org no impone que se respeten mayúsculas y minúsculas, para máxima compatibilidad es mejor respetarlas. Esto es: escribir <code>User-agent</code> con la U mayúscula, y escribir el nombre del robot tal cual se identifica.</p>
<p>Otro detalle a considerar es que no se aceptan expresiones regulares, solamente el nombre de un robot (y solo uno) o el asterisco "*" para que coincida con todos.</p>
</li>
<li>Una o más líneas <code>Disallow</code> (no permitir), cada una indicando una carpeta o archivo del sitio que no se quiere que el robot (indicado por la línea User-agent) indexe. En esta línea tampoco se admiten <em>comodines</em>, debe darse la ruta completa. Cada línea <code>Disallow</code> debe contener solamente <strong>una</strong> ruta.</li>
</ul>
<p>He aquí un ejemplo, en el que se niega a todos los robots el acceso a la carpeta /admin y al <em>Googlebot</em> el acceso a la carpeta <code>/imagenes</code> y al archivo <code>/javascript/ultrasecreto.js</code></p>
<p><code class="prettyprint bloque">User-agent: *
Disallow: /admin/

User-agent: Googlebot
Disallow: /imagenes/
Disallow: /javascript/ultrasecreto.js</code></p>
<p>Los robots que respetan sus directivas lo buscan cada vez que se conectan a un servidor, así que si el archivo no existe se generarán muchos errores 404 causados por robots buscándolo.</p>
<h3>¿Cómo saber el nombre que hay que usar para un robot?</h3>
<p>La mayoría de los robots "serios" acompañan su cabecera User Agent con una URL en la que se puede encontrar información sobre ellos, que suele incluir los datos necesarios para guiar al robot usando el <code>robots.txt</code>.</p>
<h3>Seguridad</h3>
<p>Es importante destacar que aunque sirva para indicar a los robots por qué partes del sitio no deben entrar, el archivo <code>robots.txt</code> no debe ser usado como mecanismo de seguridad. Los robots reciben la indicación de no entrar, pero nada les impide hacerlo.<br />
Además, todas las rutas que se indiquen quedan visibles para cualquiera que quiera verlas, nada impide a cualquier usuario ver el archivo, sobretodo considerando que siempre se encuentra en la misma ubicación (la raíz del sitio).</p>
<p>Si se necesita seguridad, se necesita un sistema basado en programación del lado del servidor, como siempre (ya sea un paquete PHP o autenticación HTTP)</p>
<div class="relacionados">
<h4>Enlaces relacionados</h4>
<ul>
<li><a href="http://www.robotstxt.org/robotstxt.html">Preguntas frecuentes sobre el archivo robots.txt</a></li>
<li><a href="http://es.wikipedia.org/wiki/Robots.txt">Wikipedia: robots.txt</a></li>
</ul>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.elcodiguero.com/varios/como-escribir-archivo-robots-txt.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Validar una dirección de correo electrónico</title>
		<link>http://blog.elcodiguero.com/php/validar-una-direccion-de-correo-electronico.html</link>
		<comments>http://blog.elcodiguero.com/php/validar-una-direccion-de-correo-electronico.html#comments</comments>
		<pubDate>Tue, 12 Jan 2010 01:21:45 +0000</pubDate>
		<dc:creator>Alvaro</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.elcodiguero.com/?p=20</guid>
		<description><![CDATA[Cualquier sistema que utilice el correo electrónico debería tener algún tipo de validación para evitar errores y datos basura. Pero ¿cómo validar correctamente? Antes que nada hay que tener por seguro que no hay forma (rápida y práctica) de comprobar que una dirección de correo existe y tiene a un usuario detrás, solamente se puede [...]]]></description>
			<content:encoded><![CDATA[<p>Cualquier sistema que utilice el correo electrónico debería tener algún tipo de validación para evitar errores y datos basura. Pero ¿cómo validar correctamente?<br />
Antes que nada hay que tener por seguro que no hay forma (rápida y práctica) de comprobar que una dirección de correo existe y tiene a un usuario detrás, solamente se puede comprobar que la dirección tiene una forma válida y apunta a un dominio activo.</p>
<p>Como suele suceder, el problema puede atacarse de diferentes formas. Hay sistemas que simplemente validan que la información ingresada contenga una arroba, hay otros que usan expresiones regulares complejas que fallan en cuanto la dirección ingresada no es <em>común</em> (aunque sea válida).<br />
La forma que voy a plantear no es novedosa, y es bastante sencilla, a la vez que efectiva.</p>
<h3>Una expresión regular permisiva</h3>
<p>Debe poder validar a cualquier dirección de correo válida, esto quiere decir que debe ser también bastante genérica. El patrón debe validar que exista una arroba, un nombre de usuario, y una parte que indique el dominio.<br />
A pesar de que probablemente nunca se usen nombres de usuario complejos en una dirección de correo, la verdad es que se puede usar cualquier caracter en esta parte de la dirección.<br />
Por lo que la expresión regular que debe validar esto no puede ser otra que<br />
<code class="prettyprint bloque">.+</code><br />
Que representa a una cadena conteniendo al menos 1 caracter.</p>
<p>El dominio es un poco más complicado. Muchos cometen el error de solamente permitir dos o tres partes separadas por punto, o rechazar direcciones cuya última parte contenga más de 3 caracteres. Con esto se supone que incluyen a todos los dominios de primer nivel (.com, .net, .org... y los de los países, que tienen 2 letras). Pero se olvidan que Internet cambia, y ya tenemos <a href="http://es.wikipedia.org/wiki/TLD"><acronym title="Top Level Domain - Dominio de Nivel Superior">TLD</acronym></a> .info y .aero (de cuatro letras) e incluso .museum (de seis).</p>
<p>Debido a esto, y considerando que lentamente están apareciendo los dominios con <strong>ñ</strong> y letras acentuadas, y que quizás pronto las direcciones comiencen a aceptar más caracteres <a href="http://es.wikipedia.org/wiki/UTF-8">UTF-8</a>, es que también para validar el dominio se debe utilizar un patrón genérico.<br />
El único requisito en este paso es que contenga al menos 2 secuencias de caracteres separadas por un punto. Debe haber al menos un punto, ya que no hay dominios que no desciendan de alguno de los TLD (en realidad sí los hay en redes locales, pero no sirven en internet).<br />
<code class="prettyprint bloque">.+\..+</code><br />
Este patrón representa a <em>una secuencia de caracteres no vacía, seguida de un punto, y seguida de otra secuencia de caracteres no vacía</em>.</p>
<p>Finalmente, juntando las partes el patrón queda:<br />
<code class="prettyprint bloque">^.+@.+\..+$</code></p>
<h3>Validación del dominio con PHP</h3>
<p>Una versión anterior de este artículo recomendaba validar el dominio con <a href="http://php.net/checkdnsrr">checkdnsrr()</a>. Esta parte ha sido eliminada, y el motivo es bastante simple, a decir verdad.</p>
<p>La función <code>checkdnsrr</code> solamente puede determinar si un dominio está registrado, y con el parámetro adecuado permite saber si el dominio tiene un registro <a href="http://en.wikipedia.org/wiki/List_of_DNS_record_types">MX (Mail eXchange)</a> asociado (es decir, si es capaz de recibir correo). Pero una entrada <a href="http://es.wikipedia.org/wiki/DNS"><acronym title="Domain Name System - Sistema de Nombres de Dominio">DNS</acronym></a> correcta no implica que realmente haya un servidor funcionando en el dominio.<br />
Otras opciones, como <a href="http://php.net/gethostbyname">gethostbyname</a> por ejemplo, tienen sus propios problemas:</p>
<ul>
<li>Pueden fallar si el servidor remoto está temporalmente fuera de servicio, lo cual no es tan extraño en servidores pequeños.</li>
<li>Si el equipo remoto no existe, la llamada a <code>gethostbyname</code> demora mucho en completarse, no por PHP sino por la forma en la que funciona el protocolo DNS.</li>
<li>Podría haber un fallo de conectividad temporal de parte del servidor que ejecuta el programa PHP, lo cual provocaría que la función fallase.</li>
</ul>
<p>Por estos motivos es que la validación del dominio funcionará en la mayoría de los casos pero fallará en otros, y si no se puede saber que será exitosa en <strong>todos</strong> los casos, no se puede confiar en ella.</p>
<p>De todas formas se incluyen los enlaces a las páginas del manual de PHP que corresponden a estas funciones, para que cada uno decida si hacer la validación o no.</p>
<p>Vale recordar lo que se mencionó al principio: no existe forma práctica de comprobar con certeza que la dirección es válida aún cuando se pudiese validar el dominio, dado que la dirección podría ser válida en forma pero no existir en el servidor, o podría estar mal escrita por error.<br />
La única manera de saber con seguridad si la dirección es válida es enviar un correo de confirmación antes de habilitar al usuario a continuar en el sitio. Pero esto tampoco es la solución en todos los casos; a veces solicitar confirmación al usuario no tiene sentido (por ejempo en un formulario de contacto).</p>
<p>A continuación el código de mi función de validación:<br />
<code class="prettyprint bloque">function validar_correo($correo) {
    return <a href="http://php.net/preg_match">preg_match</a>('/^.+@(.+\..+)$/', $correo);
}</code></p>
<p>A modo de resumen: la validación de direcciones de correo es compleja y no vale la pena intentar validar más que la forma. La mayoría de los métodos más estrictos con los que uno se puede llegar a encontrar suelen rechazar direcciones perfectamente correctas (aunque relativamente raras).</p>
<div class="relacionados">
<h4>Enlaces relacionados</h4>
<ul>
<li><a href="http://es.wikipedia.org/wiki/TLD">Wikipedia: <acronym title="Top Level Domain - Dominio de Nivel Superior">TLD</acronym></a></li>
<li><a href="http://es.wikipedia.org/wiki/UTF-8">Wikipedia: UTF-8</a></li>
<li><a href="http://es.wikipedia.org/wiki/DNS">Wikipedia: DNS</a></li>
<li><a href="http://es.wikipedia.org/wiki/Expresiones_regulares">Wikipedia: Expresiones regulares</a></li>
<li><a href="http://php.net/checkdnsrr">PHP: checkdnsrr()</a></li>
<li><a href="http://php.net/gethostbyname">PHP: gethostbyname</a></li>
<li><a href="http://php.net/preg_match">PHP: preg_match</a></li>
</ul>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.elcodiguero.com/php/validar-una-direccion-de-correo-electronico.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Validar parámetros enteros con PHP</title>
		<link>http://blog.elcodiguero.com/php/validar-parametros-enteros-con-php.html</link>
		<comments>http://blog.elcodiguero.com/php/validar-parametros-enteros-con-php.html#comments</comments>
		<pubDate>Fri, 08 Jan 2010 02:01:49 +0000</pubDate>
		<dc:creator>Alvaro</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.elcodiguero.com/?p=19</guid>
		<description><![CDATA[Introducción ¿Cómo hacer para estar seguro de que un parámetro obtenido por GET (o POST) es un número entero? Intentando resolver esta pregunta he intentado varias técnicas a lo largo del tiempo, y haciendo esas pruebas he llegado a entender más sobre cómo funcionan las comparaciones y las conversiones automáticas de tipos en PHP. Convirtiendo [...]]]></description>
			<content:encoded><![CDATA[<h3>Introducción</h3>
<p>¿Cómo hacer para estar seguro de que un parámetro obtenido por GET (o POST) es un número entero?<br />
Intentando resolver esta pregunta he intentado varias técnicas a lo largo del tiempo, y haciendo esas pruebas he llegado a entender más sobre cómo funcionan las comparaciones y las conversiones automáticas de tipos en PHP.</p>
<h3>Convirtiendo a un valor entero</h3>
<p><code>intval</code> es una función que obtiene el valor entero de lo que se le pase como parámetro. Es equivalente a forzar una conversión anteponiendo <code>(int)</code> a un valor. Es decir, las siguientes dos líneas son equivalentes:<br />
<code class="prettyprint bloque">$valor = intval($parametro);
$valor = (int) $parametro;</code></p>
<p>De esta forma se obtiene un valor entero en todos los casos, aunque no necesariamente este valor es igual al que queremos validar ya que cualquier cadena puede ser convertida a un entero, incluso una cadena que no contenga números (si no comienza con números, su valor es cero).</p>
<p>Pero una conversión no es una validación, así que debemos hallar una forma de asegurarnos de que el valor almacenado en <var>$parametro</var> es un número entero.</p>
<h3>Primer intento: is_int</h3>
<p>La primera idea es comprobar con la función <a href="http://www.php.net/is_int">is_int</a>.<br />
Esta función, como su nombre lo indica, comprueba que una variable sea de tipo entero, devolviendo <code>true</code> en caso de que lo sea, y <code>false</code> en caso contrario.</p>
<p>Parece ideal, sin embargo hay un problema: los valores que se reciben vía GET o POST son siempre cadenas, sin importar su contenido. El manual de la función lo advierte:</p>
<blockquote cite="http://www.php.net/is_int"><p>Para probar si una variable es un número o una cadena numérica (como en el caso de la entrada de un formulario, que es siempre una cadena), debe usar is_numeric().</p></blockquote>
<p>Lo podemos comprobar fácilmente:<br />
<code class="prettyprint bloque">gettype($_GET['var']);</code><br />
Devuelve "string", aunque el valor de <code>$_GET['var']</code> sea 12, por ejemplo.<br />
<code class="prettyprint bloque">is_int($_GET['uno']);</code><br />
Devuelve <code>false</code>, en el mismo caso que el anterior.</p>
<h3>Segundo intento: is_numeric</h3>
<p>La función <a href="http://www.php.net/is_numeric">is_numeric</a> toma cualquier una variable (o valor) de cualquier tipo, y comprueba si tiene formato de número. Esto es, si es una cadena que representa un entero, o un flotante en cualquiera de las notaciones válidas para PHP.<br />
Serviría para nuestro propósito si no fuese porque no acepta solamente enteros sino también números con coma (punto, en realidad), y por lo tanto devolverá <code>true</code> para un valor como <samp>10.33</samp>, por ejemplo.</p>
<h3>Tercer intento: comparación con entero</h3>
<p>Otra opción puede ser hacer una comparación como<br />
<code class="prettyprint bloque">$_GET['var'] == (int) $_GET['var'];</code><br />
En teoría funcionaría, pero no es así.<br />
PHP es un tanto especial en lo que refiere a las conversiones de tipos: si uno de los valores a comparar es entero, convertirá al otro en entero antes de compararlos. Por lo tanto, la comparación propuesta <strong>siempre</strong> será cierta, ya que el lado izquierdo de la comparación será convertido. El resultado es que el código anterior es equivalente al siguiente, que es siempre verdadero y por lo tanto no nos sirve:<br />
<code class="prettyprint bloque">(int) $_GET['var'] == (int) $_GET['var'];</code></p>
<h3>Cuarto intento: comparación de idénticos</h3>
<p>Uno podría pensar que si PHP convierte a entero ambos lados y por eso no sirve el ejemplo anterior, bastaría con usar el operador <code>===</code> (comparación sin conversión) para saltear el problema. Pero no es así. Los valores siempre serán de tipos diferentes (ya que las variables GET son siempre cadenas) y por lo tanto la comparación sería siempre falsa.</p>
<h3>Quinto intento: complicado pero funciona</h3>
<p>Finalmente decidí probar algo más complejo pero que evita todos los problemas de los casos anteriores.</p>
<p>¿Qué hacer? La comparación con un entero es necesaria, porque después de todo lo que necesitamos es un entero, pero no podemos comparar una cadena con un entero porque la cadena será convertida.<br />
La solución que se me ocurrió es simplemente volver a convertir al entero en una cadena:<br />
<code class="prettyprint bloque">$_GET['var'] == (string) intval($_GET['var']);</code>o<br />
<code class="prettyprint bloque">$_GET['var'] == (string) (int) $_GET['var'];</code></p>
<p>Por ejemplo, si <code>$_GET['var']</code> tiene el valor <samp>"12"</samp>, el proceso sería:<br />
<samp class="prettyprint bloque">intval("12") # --> entero 12
(string) intval("12") # --> cadena "12"
"12" == "12" # --> verdadero, contienen el mismo valor.</samp><br />
Si ahora <code>$_GET['var']</code> tiene el valor <samp>"hola"</samp>:<br />
<samp class="prettyprint bloque">intval("hola") # --> entero 0
(string) intval("hola") # --> cadena "0"
"12" == "0" # --> falso, la cadena pasada no es un entero.</samp><br />
Y finalmente, el caso que hace que las conversiones fallen: una cadena con valores numéricos y letras. Supongamos que <code>$_GET['var']</code> tiene el valor <samp>"12hola"</samp>.<br />
<samp class="prettyprint bloque">intval("12hola") # --> entero 12
(string) intval("12hola") # --> cadena "12"
"12hola" == "12" # --> falso. Al evitar la conversión automática, no hay problemas.</samp></p>
<h3>Intento final: ctype_digit</h3>
<p>Esta es una actualización de este artículo. Luego de mucho tiempo, di casi de casualidad con esta función, que sirve justamente para validar si una cadena contiene únicamente dígitos. Es increíble cómo uno puede utilizar un lenguaje durante años, y seguir encontrando cosas que no conocía, en particular esta función que no es para nada nueva, existe desde PHP 4.0.4</p>
<p>Es importante aclarar que esta función sirve solamente para variables de tipo cadena, ya que lo que hace es comprobar que cada caracter en el valor pasado como parámetro está en el rango ASCII que corresponde a los dígitos del 0 al 9 (valores entre 48 y 57 inclusive). No sirve para datos de tipo entero, y esto está claramente especificado en su <a href="http://www.php.net/ctype_digit">página del manual</a>.</p>
<p>Todo el trabajo que pasé intentando validar un entero se reduce, entonces a:<br />
<code class="prettyprint bloque">if (ctype_digit($variable)) {
    echo "La variable es entera";
}</code></p>
<div class="relacionados">
<h4>Enlaces relacionados</h4>
<ul>
<li><a href="http://www.php.net/manual/en/language.types.type-juggling.php">Manipulación de tipos en PHP</a></li>
<li><a href="http://www.php.net/is_int">PHP: is_int</a></li>
<li><a href="http://www.php.net/is_numeric">PHP: is_numeric</a></li>
<li><a href="http://www.php.net/gettype">PHP: gettype</a></li>
<li><a href="http://www.php.net/ctype_digit">PHP: ctype_digit</a></li>
</ul>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.elcodiguero.com/php/validar-parametros-enteros-con-php.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>CSV: simple y versátil</title>
		<link>http://blog.elcodiguero.com/varios/csv-simple-y-versatil.html</link>
		<comments>http://blog.elcodiguero.com/varios/csv-simple-y-versatil.html#comments</comments>
		<pubDate>Fri, 08 Jan 2010 00:09:38 +0000</pubDate>
		<dc:creator>Alvaro</dc:creator>
				<category><![CDATA[Varios]]></category>

		<guid isPermaLink="false">http://blog.elcodiguero.com/?p=18</guid>
		<description><![CDATA[¿Qué es un archivo CSV? CSV es un tipo de archivo de texto que contiene son archivos de valores separados por comas (de ahí su nombre). Es un formato bien simple y práctico. No tiene una documentación oficial, pero buscando en internet encontré el RFC 4180, Common Format and MIME Type for Comma-Separated Values (CSV) [...]]]></description>
			<content:encoded><![CDATA[<h3>¿Qué es un archivo CSV?</h3>
<p><a href="http://es.wikipedia.org/wiki/CSV"><acronym title="Comma Separated Values">CSV</acronym></a> es un tipo de archivo de texto que contiene son archivos de <em>valores separados por comas</em> (de ahí su nombre). Es un formato bien simple y práctico.<br />
No tiene una documentación oficial, pero buscando en internet encontré el <a href="http://www.rfc-editor.org/rfc/rfc4180.txt">RFC 4180, <q xml:lang="en">Common Format and MIME Type for Comma-Separated Values (CSV) Files</q></a>. Al contrario de muchos RFC, que son especificaciones completas de formatos y protocolos, este parece ser simplemente un resumen de las características de los CSV, y un intento por documentar formalmente el formato.</p>
<p>Hay 5 reglas básicas que definen un archivo CSV, a saber:</p>
<ol>
<li>Cada campo se separa con una coma, al último de la fila no le sigue una coma</li>
<li>No se toman en cuenta posibles espacios entre las comas separadoras.</li>
<li>Cada fila se separa con un salto de línea (<code>CRLF</code>, aunque se soporta también <code>LF</code>)</li>
<li>Los campos pueden o no estar delimitados por comillas (necesario en particular cuando se necesita incluir una coma en el campo o un salto de línea).</li>
<li>Si se necesita incluir comillas ", hay que duplicarlas: para que se tome "b" como el campo completo, hay que escribirlo como ""b""</li>
</ol>
<h3>Detalles</h3>
<p>En caso de querer ponerlo para descargar, es bueno saber que el tipo <acronym title="Multipurpose Internet Mail Extensions">MIME</acronym> del formato es <code>text/csv</code>.<br />
La codificación de caracteres por defecto se toma como <strong>US-ASCII</strong>, pero puede usarse el parámetro <code>charset</code> de la cabecera <code>Content-Type</code> para especificar otra. Si no se usa <code>charset</code>, el programa que trabaje con el archivo CSV deberá decidir qué codificación usa.<br />
Este es el caso más común, ya que cuando uno abre el archivo con un programa como Excel u <a href="http://www.openoffice.org/">OpenOffice</a> Calc, no existe una cabecera que indique el tipo MIME.</p>
<p>PHP provee 2 funciones para el manejo de estos archivos: <a href="http://www.php.net/fputcsv">fputcsv</a> y <a href="http://www.php.net/fgetcsv">fgetcsv</a>.<br />
<code>fgetcsv</code> extrae los campos separados por comas de un archivo, línea por línea, mientras que <code>fputcsv</code> permite hacer el proceso contrario.</p>
<p>Siendo un formato tan simple, resulta muy práctico para transmitir o almacenar información que luego debe ser procesada por otro sistema. También es la forma más sencilla de generar un archivo que pueda ser leído por los programas de hojas de cálculo (Excel, Calc, Gnumeric, etc), aunque no guarda información de formato de texto (negritas, colores, tamaños de letra, etc).</p>
<p>En caso de que no sea suficiente, siempre se puede recurrir a formatos más complejos, como <a href="http://es.wikipedia.org/wiki/XML"><acronym title="eXtensible Markup Language">XML</acronym></a> o  <a href="http://es.wikipedia.org/wiki/JSON"><acronym title="JavaScript Object Notation">JSON</acronym></a>.</p>
<div class="relacionados">
<h4>Enlaces relacionados:</h4>
<ul>
<li><a href="http://es.wikipedia.org/wiki/CSV">Wikipedia: CSV</a></li>
<li><a href="http://www.rfc-editor.org/rfc/rfc4180.txt">RFC 4180, “Common Format and MIME Type for Comma-Separated Values (CSV) Files”</a></li>
</ul>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.elcodiguero.com/varios/csv-simple-y-versatil.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Transacciones en MySQL</title>
		<link>http://blog.elcodiguero.com/mysql/transacciones-en-mysql.html</link>
		<comments>http://blog.elcodiguero.com/mysql/transacciones-en-mysql.html#comments</comments>
		<pubDate>Sat, 02 Jan 2010 21:11:36 +0000</pubDate>
		<dc:creator>Alvaro</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.elcodiguero.com/?p=16</guid>
		<description><![CDATA[Hay situaciones en las que necesitamos hacer más de una consulta al mismo tiempo, y todas tienen que ser correctas para que los datos sean consistentes y tengan sentido, de otro modo tendríamos información "colgada" y desvinculada. En el mejor de los casos esta información provocaría un desperdicio de espacio en la base de datos, [...]]]></description>
			<content:encoded><![CDATA[<p>Hay situaciones en las que necesitamos hacer más de una consulta al mismo tiempo, y todas tienen que ser correctas para que los datos sean consistentes y tengan sentido, de otro modo tendríamos información "colgada" y desvinculada.<br />
En el mejor de los casos esta información provocaría un desperdicio de espacio en la base de datos, pero lo más probable es que además lleve a errores a la hora de mostrarse y de hacer cálculos con ella.</p>
<p>Parte de este problema se resuelve teniendo una buena estructura de base de datos, con claves foráneas relacionando los campos correspondientes.</p>
<p>Pero para mayor seguridad, es bueno usar lo que en jerga <em>databasera</em> se conoce como transacciones.</p>
<p>Las transacciones sirven para asegurar la consistencia de la información, asegurando que un conjunto de sentencias se ejecuten correctamente, o no se ejecuten.</p>
<p>En un principio MySQL no las soportaba, esto cambió a partir de la versión 4, y con el uso de tablas <a href="http://es.wikipedia.org/wiki/InnoDB">InnoDB</a>. Nótese que el motor por defecto para las tablas en <a href="http://www.mysql.com">MySQL</a> hasta las versiones 5.* era <a href="http://es.wikipedia.org/wiki/MyISAM">MyISAM</a>, que no soporta transacciones.</p>
<h3>Un ejemplo</h3>
<p>Supongamos que un sitio web bancario tiene 2 usuarios, ambos trabajando sobre la misma cuenta.</p>
<p>El usuario 1 pide incrementar su saldo en 10, mientras que el usuario 2 pide disminuirlo (a través de un formulario, por ejemplo)</p>
<p>El programador del sistema no puede decidir el orden en el que se ejecutarán las consultas, así que bien podría suceder lo siguiente:<br />
<code class="prettyprint bloque">$balance = ... SELECT balance FROM cuentas WHERE cuenta=X // usuario 1</code><br />
<code class="prettyprint bloque">$balance = ... SELECT balance FROM cuentas WHERE cuenta=X // usuario 2</code></p>
<p>En este punto, existen dos copias de la aplicación que contienen una variable <var>$balance</var> cada una. Supongamos que ambas necesitan actualizar el valor en la base de datos:<br />
<code class="prettyprint bloque">UPDATE cuentas SET balance=$balance+10 WHERE cuenta=X // usuario 1</code><br />
<code class="prettyprint bloque">UPDATE cuentas SET balance=$balance-10 WHERE cuenta=X // usuario 2</code><br />
El resultado es que ambas copias del programa ejecutaron sus consultas con la información de balance que tenían, por lo que el resultado final es como si la consulta del usuario 1 no se hubiera ejecutado nunca, ya que el usuario 2 actualiza el registro con información vieja. Al final, en vez de quedar con el mismo saldo, la cuenta termina perdiendo 10.</p>
<p>Lo que se necesita para este conjunto de consultas, es lo que se denomina <a href="http://es.wikipedia.org/wiki/ACID">ACID</a>, un acrónimo inglés que quiere decir Atomicidad, Consistencia, Aislamiento y Durabilidad. Recomiendo leer la información de Wikipedia para entender de qué se trata esto, pero lo importante es lo siguiente: las transacciones son un conjunto de consultas que se ejecutan como si fuesen una. Y por esto, permiten asegurar la consistencia de los datos, ya que si en mitad del proceso una consulta falla, todos los cambios producidos por consultas anteriores pueden ser revertidos.</p>
<h3>¿Cómo usar transacciones?</h3>
<p>Usar transacciones es muy simple: antes de ejecutar la primer consulta, se ejecuta una que solamente contiene <code>BEGIN</code>.<br />
Luego se ejecutan las consultas que deban ejecutarse. Si éstas resultan exitosas, se termina la transacción con <code>COMMIT</code><br />
Esto provoca que los cambios hechos por las consultas anteriores sean permanentes.<br />
Si las consultas fallan en algún paso, se puede volver al estado anterior al comienzo de la transacción ejecutando <code>ROLLBACK</code></p>
<p>Aunque los datos no sean realmente escritos a la o las tablas involucradas hasta ejecutar el <code>COMMIT</code>, las consultas devuelven lo mismo que si lo fueran, es decir, para saber si una consulta falló basta con ver el valor de retorno de <code>mysql_query</code> y para ver el número de filas afectadas sigue valiendo usar <code>mysql_num_rows</code></p>
<p>Mientras la transacción está ejecutándose, los datos (en el caso de InnoDB las filas y en el caso de MyISAM las tablas) afectados quedan bloqueados, nadie puede acceder a ellos. Cualquier consulta que tenga que ver con los mismos datos será demorada hasta que la transacción termine.<br />
Esto implica que usar transacciones es un poco más lento que no usarlas, pero a la vez implica que los datos involucrados no pueden ser modificados por otra copia de la aplicación, y por lo tanto se evita la situación planteada al principio como ejemplo.</p>
<p>Dicha situación, implementada de forma "transaccional" quedaría:</p>
<p><code class="prettyprint bloque">mysql_query("BEGIN");
$balance = ..... mysql_query("SELECT balance FROM cuentas WHERE cuenta=X");
$resultado = mysql_query("UPDATE cuentas SET balance=$balance+10 WHERE cuenta=X");

if ($resultado !== false)
    // la consulta fue exitosa
    mysql_query("COMMIT");
else
    mysql_query("ROLLBACK");</code></p>
<p>(falta revisar que la consulta que obtiene el balance sea exitosa, pero de todas formas esto es solo un ejemplo)</p>
<h3>autocommit</h3>
<p>MySQL tiene una variable de entorno llamada <var>autocommit</var>, que por defecto tiene el valor 1. Configurado de esta manera no se pueden usar transacciones, porque MySQL automáticamente hace un <code>COMMIT</code> luego de cada consulta.</p>
<p>Para usar transacciones entonces, hay que poner <var>autocommit</var> a 0 (desactivarlo).<br />
<strong>Nota</strong>: si autocommit se pone a cualquier número N &gt; 1, MySQL hace un COMMIT automático luego de N consultas.</p>
<p>Para cambiar el valor de autocommit, simplemente se usa</p>
<p><code class="prettyprint bloque">SET autocommit = 0;</code></p>
<p>He aquí lo que sé sobre las transacciones. Es un tema interesante y cualquiera que programe un sistema relativamente complejo debería considerar si necesita transacciones para asegurar la "salud" de sus tablas y datos.</p>
<div class="relacionados">
<h4>Enlaces relacionados</h4>
<ul>
<li><a href="http://www.databasejournal.com/features/mysql/article.php/3382171">Transactions in MySQL - DatabaseJournal</a></li>
<li><a href="http://www.webmonkey.com/webmonkey/backend/databases/tutorials/tutorial2.html">Transactions in MySQL - Webmonkey</a></li>
<li><a href="http://es.wikipedia.org/wiki/MySQL">MySQL - Wikipedia</a></li>
<li><a href="http://es.wikipedia.org/wiki/ACID">ACID - Wikipedia</a></li>
<li><a href="http://es.wikipedia.org/wiki/MyISAM">MyISAM - Wikipedia</a></li>
<li><a href="http://es.wikipedia.org/wiki/InnoDB">InnoDB - Wikipedia</a></li>
<li><a href="http://es.wikipedia.org/wiki/Transacción_(base_de_datos)">Transacción - Wikipedia</a></li>
</ul>
</div>
<p>Como lamentablemente suele suceder, las versiones en inglés de los artículos de Wikipedia contienen información más detallada que las versiones en español.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.elcodiguero.com/mysql/transacciones-en-mysql.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
