<?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 Futirifoken &#187; ruby</title>
	<atom:link href="http://www.gazer.com.ar/tag/ruby/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.gazer.com.ar</link>
	<description>El arte de no decir nada ;-)</description>
	<lastBuildDate>Mon, 26 Dec 2011 19:56:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Filtro de Contenido para T!</title>
		<link>http://www.gazer.com.ar/2011/05/11/filtro-de-contenido-para-t/</link>
		<comments>http://www.gazer.com.ar/2011/05/11/filtro-de-contenido-para-t/#comments</comments>
		<pubDate>Wed, 11 May 2011 17:18:35 +0000</pubDate>
		<dc:creator>Gazer</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.gazer.com.ar/?p=1347</guid>
		<description><![CDATA[Hoy estuve leyendo este post donde los dueños de Taringa declara que &#8220;no podemos investigar el destino final de cada uno de los 20.000 post diarios&#8221;. Lejos de ser algo &#8220;colectivamente inteligente&#8221; como declaración, es una terrible boludez . Con aplicar algunos filtros automáticos, permitir que cualquiera de forma rápida y fácil reporte los contenidos [...]]]></description>
			<content:encoded><![CDATA[<p>Hoy estuve leyendo <a href="http://www.taringa.net/posts/taringa/10557222/La-situacion-de-Taringa_-explicada-por-nosotros.html">este</a> post donde los dueños de Taringa declara que &#8220;no podemos investigar el destino final de cada uno de los 20.000 post diarios&#8221;. Lejos de ser algo &#8220;colectivamente inteligente&#8221; como declaración, es una terrible boludez <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . Con aplicar algunos filtros automáticos, permitir que cualquiera de forma rápida y fácil reporte los contenidos ilegales y actuando más o menos rápido en su eliminación uno se cubre el 90% del orto. El otro 10% hay que pelearlo por carta documento porque hay gente muy al pedo ahí afuera, pero ni en pedo llegamos a un juicio oral teniendo un poco de criterio <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<p>El problema es que filtrar a mano es caro, y además filtrar hace que nuestros usuarios bajen pues no tiene ya toda la piratería al alcance de la mano <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> . Pero veamos como hacer un filtro para determinar por heurística los posts que posiblemente sean ilegales, de manera de poder revisar a mano solo los que tal ve son malos, y así bajar el trabajo necesario y por ahí bajar el costo y aumentar el profit <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<p>No lo voy a explicar muy en detalle, pero básicamente el programa siguiente hace un scraping de la home de T! y agarra los &#8216;Ultimos posts&#8217;. Para cada uno de ellos, lee el contenido y aplica una heurística muy básica para ver si es potencialmente ilegal :</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'rubygems'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'net/http'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'hpricot'</span>
&nbsp;
AGENT = <span style="color:#996600;">&quot;Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.10) Gecko/20100915 Ubuntu/10.04 (lucid) Firefox/3.6.10&quot;</span>
&nbsp;
url = <span style="color:#CC00FF; font-weight:bold;">URI</span>.<span style="color:#9900CC;">parse</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;http://www.taringa.net/&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
http = <span style="color:#6666ff; font-weight:bold;">Net::HTTP</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;www.taringa.net&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
html = http.<span style="color:#9900CC;">request_get</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'/'</span>, <span style="color:#006600; font-weight:bold;">&#123;</span><span style="color:#996600;">'User-Agent'</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> AGENT<span style="color:#006600; font-weight:bold;">&#125;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
doc = Hpricot<span style="color:#006600; font-weight:bold;">&#40;</span>html.<span style="color:#9900CC;">body</span><span style="color:#006600; font-weight:bold;">&#41;</span>
<span style="color:#006600; font-weight:bold;">&#40;</span>doc<span style="color:#006600; font-weight:bold;">/</span><span style="color:#996600;">&quot;html/body/div[2]/div[4]/div[4]/div/div[2]/ul/li/a&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>link<span style="color:#006600; font-weight:bold;">|</span>
  text = http.<span style="color:#9900CC;">request_get</span><span style="color:#006600; font-weight:bold;">&#40;</span>link.<span style="color:#9900CC;">attributes</span><span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'href'</span><span style="color:#006600; font-weight:bold;">&#93;</span>, <span style="color:#006600; font-weight:bold;">&#123;</span><span style="color:#996600;">'User-Agent'</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> AGENT<span style="color:#006600; font-weight:bold;">&#125;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
  doc1 = Hpricot<span style="color:#006600; font-weight:bold;">&#40;</span>text.<span style="color:#9900CC;">body</span><span style="color:#006600; font-weight:bold;">&#41;</span>
  content = <span style="color:#006600; font-weight:bold;">&#40;</span>doc1<span style="color:#006600; font-weight:bold;">/</span><span style="color:#996600;">&quot;html/body/div[2]/div[4]/div[4]/div/div[2]/div[2]&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">first</span>.<span style="color:#9900CC;">to_s</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">if</span> <span style="color:#006600; font-weight:bold;">&#40;</span>content =~ <span style="color:#006600; font-weight:bold;">/</span>megaupload<span style="color:#006600; font-weight:bold;">/</span>i<span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">||</span> <span style="color:#006600; font-weight:bold;">&#40;</span>content =~ <span style="color:#006600; font-weight:bold;">/</span>free<span style="color:#006600; font-weight:bold;">/</span>i<span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">||</span> <span style="color:#006600; font-weight:bold;">&#40;</span>content =~ <span style="color:#006600; font-weight:bold;">/</span>mediafire<span style="color:#006600; font-weight:bold;">/</span>i<span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">||</span> <span style="color:#006600; font-weight:bold;">&#40;</span>content =~ <span style="color:#006600; font-weight:bold;">/</span>fileserve<span style="color:#006600; font-weight:bold;">/</span>i<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;#{link.inner_html.strip} posible post ilegal&quot;</span>
  <span style="color:#9966CC; font-weight:bold;">else</span>
    <span style="color:#9966CC; font-weight:bold;">if</span> <span style="color:#006600; font-weight:bold;">&#40;</span>content =~ <span style="color:#006600; font-weight:bold;">/</span>Este post es privado<span style="color:#006600; font-weight:bold;">/</span>i<span style="color:#006600; font-weight:bold;">&#41;</span>
        <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;#{link.inner_html.strip} es privado. Al menos si es ilegal no lo ve todo el mundo :)&quot;</span>
    <span style="color:#9966CC; font-weight:bold;">else</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;#{link.inner_html.strip} parece legal.&quot;</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Con esto podemos saber :</p>
<ul>
<li>Cuantos posts parecen legales</li>
<li>Cuantos parecen ilegales</li>
<li>Cuantos no son públicos, por lo que podemos revisarlos o no, no es tan terrible</li>
</ul>
<p>Con un poco de bash sacamos las stats :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">gazer<span style="color: #000000; font-weight: bold;">@</span>Max:~$ ruby tfilter.rb <span style="color: #000000; font-weight: bold;">&gt;</span> post.txt
<span style="color: #000000;">54</span> post.txt
gazer<span style="color: #000000; font-weight: bold;">@</span>Max:~$ <span style="color: #c20cb9; font-weight: bold;">grep</span> <span style="color: #ff0000;">'posible post ilegal'</span> post.txt <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #c20cb9; font-weight: bold;">wc</span> <span style="color: #660033;">-l</span>
<span style="color: #000000;">2</span></pre></div></div>

<p>Solo el <strong>3.7%</strong> de los posts analizados parecería ser ilegales, por lo que en lugar de tener que revisar 20.000 posts por día solo deberia revisar 740, el número es otro. Si de esos ademas sumás el report del user, seguro no te quedan mucho más y hasta por ahí lo podés moderar mientras desayunas <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<p>Obviamente mi filtro se basa solamente en linksharing (que es el 90% del problema de T! y del uso que sus usuarios le dan al site). Seguramente haya servicios de upload que no puse, pero para demostrar que filtrar el contenido es una terrible pelotudez y que Taringa! no lo hace porque no le conviene, alcanza y sobra <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<p><a href='http://www.gazer.com.ar/wp-content/uploads/2011/05/post.txt'>Posts analizados</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.gazer.com.ar/2011/05/11/filtro-de-contenido-para-t/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Procesando archivos en background con Rails</title>
		<link>http://www.gazer.com.ar/2010/09/08/procesando-archivos-en-background-con-rails/</link>
		<comments>http://www.gazer.com.ar/2010/09/08/procesando-archivos-en-background-con-rails/#comments</comments>
		<pubDate>Wed, 08 Sep 2010 16:42:29 +0000</pubDate>
		<dc:creator>Gazer</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[ruby on rails]]></category>

		<guid isPermaLink="false">http://www.gazer.com.ar/?p=1297</guid>
		<description><![CDATA[Procesar archivos en paneles de administración es un bajón, sobre todo cuando son cada vez más grande y cada vez más. En uno de nuestros proyectos tenemos ya unos cinco archivos CSV diferentes a procesar donde cada uno cumple alguna función específica. Algunos de estos archivos superan los 5 Mb por upload, por lo que [...]]]></description>
			<content:encoded><![CDATA[<p>Procesar archivos en paneles de administración es un bajón, sobre todo cuando son cada vez más grande y cada vez más. En uno de nuestros proyectos tenemos ya unos cinco archivos CSV diferentes a procesar donde cada uno cumple alguna función específica. Algunos de estos archivos superan los 5 Mb por upload, por lo que se hizo necesario desacoplar el procesamiento del archivo del request que realiza el upload.</p>
<p>Para manejar procesos en background en rails hay muchas opciones buenas, algunas más complejas con colas de trabajo, workers distribuidos y otras cosas que nosotros no necesitamos. Lo importante para nosotros era encontrar alguno que permita :</p>
<ul>
<li>Procesar en background (obvio <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> )</li>
<li>Poder saber en que estado está el trabajo (% completado)</li>
<li>Al menos un proceso en background a la vez (si hay más que espere su turno, los uploads son semanales o mensuales por lo que no hay tanto solapamiento entre tareas como para justificar múltiples workers)</li>
</ul>
<p>Y nada más. Buscando y buscando caí con <a href="http://github.com/ncr/background-fu">BackgroundFu</a> que cumplía con lo necesario.</p>
<h3>Procesando los archivos</h3>
<p>Como todos los archivos son de texto con algún separador (algunos &#8216;;&#8217;, otros tabs o comas) empezamos por lo básico y crear una clase base que maneje algunos parámetros comunes.</p>
<p>En <a href="http://github.com/ncr/background-fu">BackgroundFu</a> uno puede encolar cualquier clase, por lo que nuestro worker es simplemente una clase con un método <code>process</code> (que también es arbitrario el nombre, podría ser cualquier otro).</p>
<p>Como el archivo que sube el usuario está en un directorio temporal, antes de mandar al proceso en background en necesario guardarlo en algún lugar seguro, para poder saber que al momento de procesarlo va a seguir estando, para ello encapsulamos este comportamiento en un método especial para encolar.</p>
<p>Nuestra completa termina siendo algo como (más adelante veremos en detalle algunas cosas) :</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> FileWorker
  <span style="color:#9966CC; font-weight:bold;">include</span> <span style="color:#6666ff; font-weight:bold;">BackgroundFu::WorkerMonitoring</span>
&nbsp;
  attr_reader <span style="color:#ff3333; font-weight:bold;">:total_lines</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> <span style="color:#0000FF; font-weight:bold;">self</span>.<span style="color:#9900CC;">enqueue</span><span style="color:#006600; font-weight:bold;">&#40;</span>file, <span style="color:#006600; font-weight:bold;">*</span>args<span style="color:#006600; font-weight:bold;">&#41;</span>
    filename = <span style="color:#996600;">&quot;#{RAILS_ROOT}/tmp/#{self.name}-#{Time.now.to_i}&quot;</span>
    <span style="color:#CC00FF; font-weight:bold;">File</span>.<span style="color:#CC0066; font-weight:bold;">open</span><span style="color:#006600; font-weight:bold;">&#40;</span>filename, <span style="color:#996600;">&quot;wb&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#006600; font-weight:bold;">|</span>f<span style="color:#006600; font-weight:bold;">|</span> f.<span style="color:#9900CC;">write</span><span style="color:#006600; font-weight:bold;">&#40;</span>file.<span style="color:#9900CC;">read</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">&#125;</span>
    Job.<span style="color:#9900CC;">enqueue</span>!<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#0000FF; font-weight:bold;">self</span>.<span style="color:#9900CC;">name</span>, :<span style="color:#996600;">'process'</span>, filename, <span style="color:#006600; font-weight:bold;">*</span>args<span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">id</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> process<span style="color:#006600; font-weight:bold;">&#40;</span>filename, <span style="color:#006600; font-weight:bold;">*</span>args<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#0066ff; font-weight:bold;">@lines</span> = <span style="color:#006666;">0</span>
    <span style="color:#0066ff; font-weight:bold;">@total_lines</span> = <span style="color:#996600;">`wc -l #{filename}`</span>.<span style="color:#9900CC;">to_i</span>
    record_progress<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">0</span><span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
    result = real_process<span style="color:#006600; font-weight:bold;">&#40;</span>filename, <span style="color:#006600; font-weight:bold;">*</span>args<span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
    record_progress<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">100</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#CC00FF; font-weight:bold;">File</span>.<span style="color:#9900CC;">unlink</span><span style="color:#006600; font-weight:bold;">&#40;</span>filename<span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
    result
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  protected
  <span style="color:#9966CC; font-weight:bold;">def</span> line_processed!
    <span style="color:#0066ff; font-weight:bold;">@lines</span> <span style="color:#006600; font-weight:bold;">+</span>= <span style="color:#006666;">1</span>
&nbsp;
    update<span style="color:#006600; font-weight:bold;">&#40;</span>@lines<span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> update<span style="color:#006600; font-weight:bold;">&#40;</span>lines<span style="color:#006600; font-weight:bold;">&#41;</span>
    record_progress<span style="color:#006600; font-weight:bold;">&#40;</span>lines, <span style="color:#0066ff; font-weight:bold;">@total_lines</span><span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Veamos un poco más en detalle algunas partes.</p>
<h3>Encolado</h3>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">  <span style="color:#9966CC; font-weight:bold;">def</span> <span style="color:#0000FF; font-weight:bold;">self</span>.<span style="color:#9900CC;">enqueue</span><span style="color:#006600; font-weight:bold;">&#40;</span>file, <span style="color:#006600; font-weight:bold;">*</span>args<span style="color:#006600; font-weight:bold;">&#41;</span>
    filename = <span style="color:#996600;">&quot;#{RAILS_ROOT}/tmp/#{self.name}-#{Time.now.to_i}&quot;</span>
    <span style="color:#CC00FF; font-weight:bold;">File</span>.<span style="color:#CC0066; font-weight:bold;">open</span><span style="color:#006600; font-weight:bold;">&#40;</span>filename, <span style="color:#996600;">&quot;wb&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#006600; font-weight:bold;">|</span>f<span style="color:#006600; font-weight:bold;">|</span> f.<span style="color:#9900CC;">write</span><span style="color:#006600; font-weight:bold;">&#40;</span>file.<span style="color:#9900CC;">read</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">&#125;</span>
    Job.<span style="color:#9900CC;">enqueue</span>!<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#0000FF; font-weight:bold;">self</span>.<span style="color:#9900CC;">name</span>, :<span style="color:#996600;">'process'</span>, filename, <span style="color:#006600; font-weight:bold;">*</span>args<span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">id</span>
  <span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Este método es quien guarda en un lugar seguro el archivo. Utilizando el nombre de la clase actual (recuerden que este método está en una clase base, de donde luego vamos a heredar) y un timestamp por las dudas que se lleguen a hacer 2 uploads (cosa que en la práctica no pasa, pero por las dudas está).</p>
<p>Luego de guardar el archivo se encola el trabajo pasándole el archivo (el path completo) y cualquier otro argumento que se quiera.</p>
<p>Esto hará que luego el daemon de background instancia la clase &#8216;self.name&#8217; y llame al método process pasandole nuestros parámetros.</p>
<h3>Procesamiento</h3>
<p>El método genérico de procesamiento hace uso de un método que la clase base no tiene &#8216;real_process&#8217; que es el método que las clases hijas definarán para realizar el trabajo final :</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">  <span style="color:#9966CC; font-weight:bold;">def</span> process<span style="color:#006600; font-weight:bold;">&#40;</span>filename, <span style="color:#006600; font-weight:bold;">*</span>args<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#0066ff; font-weight:bold;">@lines</span> = <span style="color:#006666;">0</span>
    <span style="color:#0066ff; font-weight:bold;">@total_lines</span> = <span style="color:#996600;">`wc -l #{filename}`</span>.<span style="color:#9900CC;">to_i</span>
    record_progress<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">0</span><span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
    result = real_process<span style="color:#006600; font-weight:bold;">&#40;</span>filename, <span style="color:#006600; font-weight:bold;">*</span>args<span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
    record_progress<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">100</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#CC00FF; font-weight:bold;">File</span>.<span style="color:#9900CC;">unlink</span><span style="color:#006600; font-weight:bold;">&#40;</span>filename<span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
    result
  <span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>En este caso el método se encarga de saber cual es el total de lineas (de una manera tal vez no muy linda, pero que anda <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> ) y borrar el archivo al terminar el proceso.</p>
<h3>Un worker real</h3>
<p>Para que esto tenga sentido, debemos crear un worker que haga realmente algo, entonces por ejemplo podríamos tener un worker que sume puntos a los usuarios desde un archivo :</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> UserPointProcessor <span style="color:#006600; font-weight:bold;">&lt;</span> FileWorker
  private
  <span style="color:#9966CC; font-weight:bold;">def</span> real_process<span style="color:#006600; font-weight:bold;">&#40;</span>file<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Base</span>.<span style="color:#9900CC;">transaction</span> <span style="color:#9966CC; font-weight:bold;">do</span>
      FasterCSV.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#CC00FF; font-weight:bold;">File</span>.<span style="color:#9900CC;">read</span><span style="color:#006600; font-weight:bold;">&#40;</span>file<span style="color:#006600; font-weight:bold;">&#41;</span>, <span style="color:#ff3333; font-weight:bold;">:col_sep</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">&quot;<span style="color:#000099;">\t</span>&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>row<span style="color:#006600; font-weight:bold;">|</span>
         <span style="color:#008000; font-style:italic;"># Hacer algo con row</span>
&nbsp;
         line_processed!
      <span style="color:#9966CC; font-weight:bold;">end</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
    <span style="color:#996600;">&quot;Podemos retornar algun valor o status de exito&quot;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>De esta forma podemos ahora en algún controller hacer el encolado del trabajo :</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">  <span style="color:#9966CC; font-weight:bold;">def</span> add_points
    <span style="color:#9966CC; font-weight:bold;">if</span> request.<span style="color:#9900CC;">post</span>?
      redirect_to admin_job_path<span style="color:#006600; font-weight:bold;">&#40;</span>UserPointProcessor.<span style="color:#9900CC;">enqueue</span><span style="color:#006600; font-weight:bold;">&#40;</span>params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:file</span><span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">and</span> <span style="color:#0000FF; font-weight:bold;">return</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
    render <span style="color:#996600;">'file_upload'</span>
  <span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>En este caso el template <code>file_upload</code> es un form con un <em>file field</em> para hacer el upload, sin ningún otro campo. La ruta <code>admin_job_path</code> nos la provee directamente <a href="http://github.com/ncr/background-fu">BackgroundFu</a>, generándonos también las vistas con AJAX para actualizar el estado, una linda barra de progreso que nos muestra el tiempo estimado para terminar entre otros datos útiles (o no tanto <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> ).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gazer.com.ar/2010/09/08/procesando-archivos-en-background-con-rails/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HTML5 : Guardar el contenido de &lt;canvas&gt; en el servidor</title>
		<link>http://www.gazer.com.ar/2010/04/13/html5-guardar-el-contenido-de-canvas-en-el-servidor/</link>
		<comments>http://www.gazer.com.ar/2010/04/13/html5-guardar-el-contenido-de-canvas-en-el-servidor/#comments</comments>
		<pubDate>Tue, 13 Apr 2010 03:57:34 +0000</pubDate>
		<dc:creator>Gazer</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[canvas]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[Sinatra]]></category>

		<guid isPermaLink="false">http://www.gazer.com.ar/?p=1267</guid>
		<description><![CDATA[Hoy tenía ganas de hacer algo distinto (vamos, no quería trabajar ) así que me puse a jugar con HTML5, entre otras cosas con el tag &#60;canvas&#62; para poder dibujar. Luego de bajar varios ejemplos y tener un &#8216;paint&#8217; andando lo que quería era guardar la imagen, y llegué a este post donde el autor [...]]]></description>
			<content:encoded><![CDATA[<p>Hoy tenía ganas de hacer algo distinto (vamos, no quería trabajar <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> ) así que me puse a jugar con HTML5, entre otras cosas con el tag &lt;canvas&gt; para poder dibujar. Luego de bajar varios ejemplos y tener un &#8216;paint&#8217; andando lo que quería era guardar la imagen, y llegué a este <a href="http://www.nihilogic.dk/labs/canvas2image/">post</a> donde el autor deja guardar en PNG, JPG hasta incluso en BMP el contenido del canvas.</p>
<p>Pero que el usuario se pueda guardar la imagen no era mi idea, era más bien guardarle en el servidor. Por ejemplo para hacer una firma digital y usarla después en el sitio para firmar los posts como si fueran documentos <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> .</p>
<p>Al principio pensé que iba a ser <em>super</em> complicado, pero me encontré que todo fue mucho más fácil de lo que imaginé. Así que vamos por partes.</p>
<p>Vamos a empezar por el server. Como era muy poco código para este ejemplo, hice una aplicación en <a href="http://www.sinatrarb.com/">Sinatra</a> que me permite mostrar un canvas y luego hacer un POST con los datos. Es ideal en este caso, ya que el server queda de tan solo unas 20 líneas de código :</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'sinatra'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'RMagick'</span>
&nbsp;
get <span style="color:#996600;">'/'</span> <span style="color:#9966CC; font-weight:bold;">do</span>
   erb <span style="color:#ff3333; font-weight:bold;">:index</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
post <span style="color:#996600;">'/'</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  <span style="color:#008000; font-style:italic;"># Ya completaremos ...</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Como se puede ver tenemos una vista en el root y el POST lo manejamos también en la raiz. Para procesar la imagen decidí usar la única gema que conozco, RMagick, que buscando en la documentación de ImageMagick me <a href="http://www.imagemagick.org/script/formats.php">encontré</a> con grata sorpresa que sale leer &#8216;Base64-encoded inline image&#8217; , que resulta que es justo el formato en que se obtienen los datos desde el canvas <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . La única diferencia es que hay que preceder el string con &#8216;inline:&#8217; y ya estamos, nuestro método POST queda así :</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">post <span style="color:#996600;">'/'</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  data = <span style="color:#996600;">'inline:'</span> <span style="color:#006600; font-weight:bold;">+</span> request.<span style="color:#9900CC;">body</span>.<span style="color:#9900CC;">read</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">begin</span>
    image = <span style="color:#6666ff; font-weight:bold;">Magick::Image</span>.<span style="color:#9900CC;">read</span><span style="color:#006600; font-weight:bold;">&#40;</span>data<span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">first</span>
&nbsp;
    image.<span style="color:#9900CC;">write</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'image.png'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#996600;">&quot;alert('Image saved successfully')&quot;</span>
  <span style="color:#9966CC; font-weight:bold;">rescue</span>
    <span style="color:#996600;">&quot;alert('Image format not recognized')&quot;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Habría que revisar la documentación y bugs de ImageMagick para ver que tipos de implicancia puede llegar a tener en la seguridad del sitio o del sistema, pero son cosas que no vienen al caso <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<p>La vista (reducida para el ejemplo) puede resumirse en el siguiente HTML :</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">  &lt;canvas id=&quot;example&quot; width=&quot;200&quot; height=&quot;200&quot;&gt;
    This text is displayed if your browser does not support HTML5 Canvas.
  &lt;/canvas&gt;
&nbsp;
  &lt;input type=&quot;button&quot; id=&quot;submit&quot; value=&quot;Submit&quot; /&gt;</pre></div></div>

<p>Y un poco de javascript :</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #339933;">&lt;</span>script type<span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;text/javascript&quot;</span><span style="color: #339933;">&gt;</span>
  $<span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    $<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;input&quot;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">click</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #006600; font-style: italic;">// jQuery todavía no habla bien con HTML5, hack para</span>
      <span style="color: #006600; font-style: italic;">// no usar un plugin.</span>
      <span style="color: #003366; font-weight: bold;">var</span> c <span style="color: #339933;">=</span> $<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;#example&quot;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      $.<span style="color: #660066;">post</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'/'</span><span style="color: #339933;">,</span> c.<span style="color: #660066;">toDataURL</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;script&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #006600; font-style: italic;">// Un dibujito estático para probar</span>
    <span style="color: #003366; font-weight: bold;">var</span> example <span style="color: #339933;">=</span> document.<span style="color: #660066;">getElementById</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'example'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #003366; font-weight: bold;">var</span> context <span style="color: #339933;">=</span> example.<span style="color: #660066;">getContext</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'2d'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    context.<span style="color: #660066;">fillStyle</span> <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;rgb(255,0,0)&quot;</span><span style="color: #339933;">;</span>
    context.<span style="color: #660066;">fillRect</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">30</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">30</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">50</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">50</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>
<span style="color: #339933;">&lt;/</span>script<span style="color: #339933;">&gt;</span></pre></div></div>

<p>Y ya estamos, al presionar el botón se envían los datos de la imagen dibujada, si está todo bien ImageMagick creará el archivo en el filesystem y por último devolverá algo de javascript para informar al usuario como salió todo. Obviamente si tuviéramos un sistema de usuarios en el server podríamos guardar la imagen para cada uno, pero de nuevo, no viene al caso.</p>
<p>El resultado final :</p>
<p><img src="http://www.gazer.com.ar/wp-content/uploads/2010/04/image.png" alt="" title="image" width="200" height="200" class="aligncenter size-full wp-image-1268" /></p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ identify <span style="color: #660033;">-verbose</span> image.png 
Image: image.png
  Format: PNG <span style="color: #7a0874; font-weight: bold;">&#40;</span>Portable Network Graphics<span style="color: #7a0874; font-weight: bold;">&#41;</span>
  Class: DirectClass
  Geometry: 200x200+<span style="color: #000000;">0</span>+<span style="color: #000000;">0</span>
  Resolution: 72x72
  Print <span style="color: #c20cb9; font-weight: bold;">size</span>: 2.77778x2.77778
  Colorspace: RGB
  Depth: <span style="color: #000000;">8</span><span style="color: #000000; font-weight: bold;">/</span><span style="color: #000000;">1</span>-bit
  Histogram:
     <span style="color: #000000;">37500</span>: <span style="color: #7a0874; font-weight: bold;">&#40;</span>  <span style="color: #000000;">0</span>,  <span style="color: #000000;">0</span>,  <span style="color: #000000;">0</span>,  <span style="color: #000000;">0</span><span style="color: #7a0874; font-weight: bold;">&#41;</span> <span style="color: #666666; font-style: italic;">#00000000 none</span>
      <span style="color: #000000;">2500</span>: <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #000000;">255</span>,  <span style="color: #000000;">0</span>,  <span style="color: #000000;">0</span>,<span style="color: #000000;">255</span><span style="color: #7a0874; font-weight: bold;">&#41;</span> <span style="color: #666666; font-style: italic;">#FF0000 red</span>
  Page geometry: 200x200+<span style="color: #000000;">0</span>+<span style="color: #000000;">0</span>
  Compression: Zip
  Properties:
    create-date: <span style="color: #000000;">2010</span>-04-13T00:<span style="color: #000000;">20</span>:<span style="color: #000000;">15</span>-03:00
    modify-date: <span style="color: #000000;">2010</span>-04-13T00:<span style="color: #000000;">20</span>:<span style="color: #000000;">15</span>-03:00
    signature: 0a42f3c85d6364e38f14dac554d26c62a90b84b06ac7f22264c9d964657ba8d6
  Tainted: False
  Filesize: 503b
  Number pixels: 39.1kb
  Version: ImageMagick 6.5.1-<span style="color: #000000;">0</span> <span style="color: #000000;">2009</span>-08-<span style="color: #000000;">27</span> Q16 OpenMP http:<span style="color: #000000; font-weight: bold;">//</span>www.imagemagick.org</pre></div></div>

<p>Probado en :</p>
<ul>
<li>Chromium 5.0.372.0 (44042) Ubuntu</li>
<li>Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.9) Gecko/20100401 Ubuntu/9.10 (karmic) Firefox/3.5.9</li>
</ul>
<p>Código completo : <a href='http://www.gazer.com.ar/wp-content/uploads/2010/04/html5_canvas_submit.zip'>html5_canvas_submit</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.gazer.com.ar/2010/04/13/html5-guardar-el-contenido-de-canvas-en-el-servidor/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Migrando datos desde PHP a Rails</title>
		<link>http://www.gazer.com.ar/2010/01/22/migrando-datos-desde-php-a-rails/</link>
		<comments>http://www.gazer.com.ar/2010/01/22/migrando-datos-desde-php-a-rails/#comments</comments>
		<pubDate>Fri, 22 Jan 2010 20:02:41 +0000</pubDate>
		<dc:creator>Gazer</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[active record]]></category>
		<category><![CDATA[migracion]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.gazer.com.ar/?p=1227</guid>
		<description><![CDATA[Por esas cosas de la vida me encontré todo el día de hoy migrando datos viejos de un sistema hecho en PHP a uno hecho en Rails. Las cosas venían bastante simples definiendo modelos de ActiveRecord para las tablas de la base de datos vieja y reinsertando con modelos apuntando a las tablas nuevas. Pero [...]]]></description>
			<content:encoded><![CDATA[<p>Por esas cosas de la vida me encontré todo el día de hoy migrando datos viejos de un sistema hecho en PHP a uno hecho en Rails. Las cosas venían bastante simples definiendo modelos de ActiveRecord para las tablas de la base de datos vieja y reinsertando con modelos apuntando a las tablas nuevas. Pero &#8230; siempre hay un pero.</p>
<p>Resulta que el hermoso sistema anterior para evitar tener muchas tablas (o vaya a saber por qué) tenía en una parte un tabla donde cada field era un gran TEXT que contenía un array de PHP <a href="http://php.net/manual/en/function.serialize.php" target="_blank">serializado</a>.</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> UserTextField <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Base</span>
  set_table_name <span style="color:#996600;">&quot;usertextfield&quot;</span>
  set_primary_key <span style="color:#996600;">&quot;userid&quot;</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> folders
    field3
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
$<span style="color:#006600; font-weight:bold;">&gt;</span> user = UserTextField.<span style="color:#9900CC;">find</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">3</span><span style="color:#006600; font-weight:bold;">&#41;</span>
$<span style="color:#006600; font-weight:bold;">&gt;</span> user.<span style="color:#9900CC;">folders</span> <span style="color:#008000; font-style:italic;"># =&gt; 'a:2:{i:0;s:4:&quot;bkps&quot;;i:1;s:6:&quot;listas&quot;;'</span></pre></div></div>

<p>Justo cuando estaba por ponerme a parsear texto me encontré con <a href="http://github.com/jqr/php-serialize">php-serialize</a> que permite serializar y deserializar estos string en cómodos tipos nativos de Ruby.</p>
<p>El código final queda entonces algo como :</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> UserTextField <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Base</span>
  set_table_name <span style="color:#996600;">&quot;usertextfield&quot;</span>
  set_primary_key <span style="color:#996600;">&quot;userid&quot;</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> folders
    PHP.<span style="color:#9900CC;">unserialize</span><span style="color:#006600; font-weight:bold;">&#40;</span>field3<span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
$<span style="color:#006600; font-weight:bold;">&gt;</span> user = UserTextField.<span style="color:#9900CC;">find</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">3</span><span style="color:#006600; font-weight:bold;">&#41;</span>
$<span style="color:#006600; font-weight:bold;">&gt;</span> user.<span style="color:#9900CC;">folders</span> <span style="color:#008000; font-style:italic;"># =&gt; [&quot;bkps&quot;, &quot;listas&quot;]</span></pre></div></div>

<p>Y la migración de datos pudo continuar sin problemas <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gazer.com.ar/2010/01/22/migrando-datos-desde-php-a-rails/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Regalando cosas por Bluetooth</title>
		<link>http://www.gazer.com.ar/2009/08/12/regalando-cosas-por-bluetooth/</link>
		<comments>http://www.gazer.com.ar/2009/08/12/regalando-cosas-por-bluetooth/#comments</comments>
		<pubDate>Wed, 12 Aug 2009 14:54:27 +0000</pubDate>
		<dc:creator>Gazer</dc:creator>
				<category><![CDATA[GNU+Linux]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[bluetooth]]></category>
		<category><![CDATA[j2me]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.gazer.com.ar/?p=1186</guid>
		<description><![CDATA[El año pasado Movistar había lanzado una campaña muy pedorra en el subte, en donde unos carteles en el piso te invitaban a prender tu Bluetooth y te enviaban un file. Lo que te enviaban era una simple imagen, con tanto texto que en mi celular era casi ilegible y no tenía consigna alguna. Sin [...]]]></description>
			<content:encoded><![CDATA[<p>El año pasado Movistar había lanzado una campaña muy pedorra en el subte, en donde unos carteles en el piso te invitaban a prender tu Bluetooth y te enviaban un file. Lo que te enviaban era una simple imagen, con tanto texto que en mi celular era casi ilegible y no tenía consigna alguna.</p>
<p>Sin embargo esto sirvió para que me encaprichara y quisiera armar algo similar para la oficina, orientado a que un cliente que viene a una reunión se pueda llevar un regalo, que en este caso es un juego J2ME.</p>
<p>Hacerlo realmente es una boludez. El real problema, que no voy a tratar acá, es tener una placa Bluetooth soportada por Linux (creo que esta solución aplica Windows, pero no lo probé), lo que puede resultar complicado. Yo en mi anterior laptop fallé en cada intento. Hoy en día en mi MacBook anda todo out-of-the-box por suerte así que pude jugar un poco.</p>
<p>El protocolo que se utiliza para intercambiar cosas es Object Exchange (OBEX) y tenemos una excelente biblioteca llamada <a href="http://dev.zuckschwerdt.org/openobex/">OpenObex</a>. A nosotros nos interesa particularmente ObexFTP que nos da el File Transfer sobre Obex.</p>
<p>El primer problema que tuve es que en Jaunty no está el binding de ruby, por lo que me tuve que bajar el <a href="https://launchpad.net/ubuntu/karmic/+source/obexftp/0.22-1">source y diff</a> de Karmic y crear mis libobexftp-ruby_0.22-1_i386.deb y sus dependencias.</p>
<p>Salvando eso, con los ejemplos del <a href="http://dev.zuckschwerdt.org/openobex/wiki/ObexFtpExampleClientRuby">wiki</a> sale muy fácil. La biblioteca nos permite descubrir devices, abrir channels y enviar archivos en pocas líneas. Acá un en ruby ejemplo :</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#008000; font-style:italic;">#!/usr/bin/env ruby</span>
&nbsp;
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'obexftp'</span>
&nbsp;
<span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Scanning BT...&quot;</span>
intfs = Obexftp.<span style="color:#9900CC;">discover</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#6666ff; font-weight:bold;">Obexftp::BLUETOOTH</span><span style="color:#006600; font-weight:bold;">&#41;</span>
dev = intfs.<span style="color:#9900CC;">first</span> <span style="color:#008000; font-style:italic;"># Es un array, podríamos iterar sobre todas las encontradas</span>
&nbsp;
channel = Obexftp.<span style="color:#9900CC;">browsebt</span><span style="color:#006600; font-weight:bold;">&#40;</span>dev, <span style="color:#6666ff; font-weight:bold;">Obexftp::PUSH</span><span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
cli = <span style="color:#6666ff; font-weight:bold;">Obexftp::Client</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#6666ff; font-weight:bold;">Obexftp::BLUETOOTH</span><span style="color:#006600; font-weight:bold;">&#41;</span>
<span style="color:#CC0066; font-weight:bold;">puts</span> cli.<span style="color:#9900CC;">connectpush</span><span style="color:#006600; font-weight:bold;">&#40;</span>dev, channel<span style="color:#006600; font-weight:bold;">&#41;</span>
<span style="color:#CC0066; font-weight:bold;">puts</span> cli.<span style="color:#9900CC;">put_file</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'ver.jpg'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
<span style="color:#CC0066; font-weight:bold;">puts</span> cli.<span style="color:#9900CC;">disconnect</span></pre></div></div>

<p>Hacer lo mismo en Python, Perl o cualqueir otro <a href="http://dev.zuckschwerdt.org/openobex/wiki/ObexFtpBindings">lenguaje soportado</a> es igual de simple.</p>
<p>Obviamente es muy minimalista: agarra el primer device encontrado, abre un channel para hacer un PUSH (si el device no soporta PUSH retorna -1 según creo), luego abre la conexión y le envía el archivo.</p>
<p>Desde el celular vemos un mensaje de que se está abriendo una conexión y luego el detalle de lo que se quiere enviar, nombre del archivo, tipo de archivo, tamaño, etc. Podemos aceptarlo o rechazarlo. De aceptarlo se descarga pero no se guarda ni se instala, es un paso extra que debemos decidir si lo hacemos o no.</p>
<p>Un problema que encontramos para enviar juegos es que algunos celulares están bloqueados para esa función (para así vendértelos por el portal WAP oficial de tu carrier). Ya lo pudimos probar con varios celulares Nokia, Motorola y Samsung y funciona razonablemente bien.</p>
<p>Sobre este ejemplo nosotros tenés un poco más de trabajo, ya que guardamos los device ID y el contenido enviado, así cuando volvés te damos un contenido diferente <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . Si además no podés recibir el juego, te pasamos una imágen simpática <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> .</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gazer.com.ar/2009/08/12/regalando-cosas-por-bluetooth/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Sitemaps vía crawling</title>
		<link>http://www.gazer.com.ar/2009/07/15/sitemaps-via-crawling/</link>
		<comments>http://www.gazer.com.ar/2009/07/15/sitemaps-via-crawling/#comments</comments>
		<pubDate>Wed, 15 Jul 2009 17:19:30 +0000</pubDate>
		<dc:creator>Gazer</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[has_sitemap]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.gazer.com.ar/?p=1148</guid>
		<description><![CDATA[Hoy me pidieron agregar un Sitemap para uno de los trabajos que hicimos para el gobierno y me encontré con que los plugins que uso para esta tarea no me cerraban de forma cómoda. El problema es que este sitio tiene, además del contenido dinámico, muchas páginas estáticas que no puedo referenciar desde un modelo, [...]]]></description>
			<content:encoded><![CDATA[<p>Hoy me pidieron agregar un <a href="http://www.sitemaps.org/es/">Sitemap</a> para uno de los trabajos que hicimos para el gobierno y me encontré con que los plugins que uso para esta tarea no me cerraban de forma cómoda. El problema es que este sitio tiene, además del contenido dinámico, muchas páginas estáticas que no puedo referenciar desde un modelo, por lo que debía forzarlas y era bastante molesto.</p>
<p>Buscando encontré <a href="http://www.webficient.com/2008/09/06/google-sitemap-ruby-on-rails">una solución</a> práctica para este caso (donde hay pocas páginas, menos de 1k) que usa un crawler para recorrer todo el sitio y obtener las URLs a agregar al sitemap. El script que presentan me sirvió, aunque tuve que hacerle algunos cambios menores.</p>
<p>El primer problema que tenía era que me agregaba páginas que no deben ir en un sitemap (ni ser indexadas) como las de login, recuperar clave, form de registración, etc. Por lo que tuve que modificar ligeramente el código para no seguir los enlaces que estuvieran marcados con <code>rel="nofollow"</code> y para eso modifiqué en el método <code>extract_and_call_urls</code> la última línea como sigue :</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">links.<span style="color:#9900CC;">each</span><span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#006600; font-weight:bold;">|</span>link<span style="color:#006600; font-weight:bold;">|</span>
   extract_and_call_urls<span style="color:#006600; font-weight:bold;">&#40;</span>link.<span style="color:#9900CC;">href</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">unless</span>
      !can_follow?<span style="color:#006600; font-weight:bold;">&#40;</span>link<span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">||</span> ignore_url?<span style="color:#006600; font-weight:bold;">&#40;</span>link.<span style="color:#9900CC;">href</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">||</span> 
      <span style="color:#0066ff; font-weight:bold;">@visited_pages</span>.<span style="color:#9966CC; font-weight:bold;">include</span>?<span style="color:#006600; font-weight:bold;">&#40;</span>link.<span style="color:#9900CC;">href</span><span style="color:#006600; font-weight:bold;">&#41;</span> 
<span style="color:#006600; font-weight:bold;">&#125;</span></pre></div></div>

<p>Y definiendo el nuevo método :</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"> <span style="color:#9966CC; font-weight:bold;">def</span> can_follow?<span style="color:#006600; font-weight:bold;">&#40;</span>link<span style="color:#006600; font-weight:bold;">&#41;</span>
   <span style="color:#0000FF; font-weight:bold;">return</span> <span style="color:#0000FF; font-weight:bold;">false</span> <span style="color:#9966CC; font-weight:bold;">if</span> link.<span style="color:#0000FF; font-weight:bold;">nil</span>? <span style="color:#006600; font-weight:bold;">||</span>
   <span style="color:#006600; font-weight:bold;">&#40;</span>link.<span style="color:#9900CC;">attributes</span><span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">&quot;rel&quot;</span><span style="color:#006600; font-weight:bold;">&#93;</span> <span style="color:#006600; font-weight:bold;">&amp;&amp;</span> link.<span style="color:#9900CC;">attributes</span><span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">&quot;rel&quot;</span><span style="color:#006600; font-weight:bold;">&#93;</span>.<span style="color:#9966CC; font-weight:bold;">include</span>?<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;nofollow&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
   <span style="color:#0000FF; font-weight:bold;">true</span>
 <span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Entonces, cuando el crawler encuentra un enlace que el <em>developer</em> marcó que no debe seguirse en una indexación (esto es principalmente para los crawlers de los search engines) se ignora y no se agrega al sitemap.</p>
<p>El otro cambio menor fue que tenía algunas URLs con el path completo y por default siempre me agregaba al inicio el domain name, por lo que me quedaban URLs inválidas, por lo que hice la siguiente modificación :</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#008000; font-style:italic;"># Antes</span>
xml.<span style="color:#9900CC;">loc</span><span style="color:#006600; font-weight:bold;">&#40;</span>@starting_url <span style="color:#006600; font-weight:bold;">+</span> url<span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Después</span>
xml.<span style="color:#9900CC;">loc</span><span style="color:#006600; font-weight:bold;">&#40;</span>url.<span style="color:#9966CC; font-weight:bold;">include</span>?<span style="color:#006600; font-weight:bold;">&#40;</span>@starting_url<span style="color:#006600; font-weight:bold;">&#41;</span> ? url : <span style="color:#006600; font-weight:bold;">&#40;</span>@starting_url <span style="color:#006600; font-weight:bold;">+</span> url<span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">&#41;</span></pre></div></div>

<p>Una vez probado el script hice una tarea rake para poder correrla fácil desde un cronjob :</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#008000; font-style:italic;"># lib/tasks/sitemap.rake</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'lib/crawler'</span>
&nbsp;
desc <span style="color:#996600;">&quot;Generate the sitemap file&quot;</span>
task <span style="color:#ff3333; font-weight:bold;">:sitemap</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff3333; font-weight:bold;">:environment</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  start_url = ENV<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">&quot;URL&quot;</span><span style="color:#006600; font-weight:bold;">&#93;</span> <span style="color:#006600; font-weight:bold;">||</span> <span style="color:#996600;">&quot;http://localhost:3000&quot;</span>
  Crawler.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>start_url, <span style="color:#006600; font-weight:bold;">&#40;</span>ENV<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">&quot;CREDS&quot;</span><span style="color:#006600; font-weight:bold;">&#93;</span> <span style="color:#9966CC; font-weight:bold;">if</span> ENV<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">&quot;CREDS&quot;</span><span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">&#41;</span>, ENV<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">&quot;QUIET&quot;</span><span style="color:#006600; font-weight:bold;">&#93;</span> <span style="color:#006600; font-weight:bold;">||</span> <span style="color:#0000FF; font-weight:bold;">false</span>, ENV<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">&quot;SITEMAP&quot;</span><span style="color:#006600; font-weight:bold;">&#93;</span> <span style="color:#006600; font-weight:bold;">||</span> <span style="color:#0000FF; font-weight:bold;">false</span>, ENV<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">&quot;DEBUG&quot;</span><span style="color:#006600; font-weight:bold;">&#93;</span> <span style="color:#006600; font-weight:bold;">||</span> <span style="color:#0000FF; font-weight:bold;">false</span><span style="color:#006600; font-weight:bold;">&#41;</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Y listo, lo último fue hacer un deploy y configurar un cron.dayli para que cree el sitemap actualizado :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">rake sitemap <span style="color: #007800;">URL</span>=http:<span style="color: #000000; font-weight: bold;">//</span>www.haciendoelcolon.buenosaires.gob.ar <span style="color: #007800;">SITEMAP</span>=<span style="color: #c20cb9; font-weight: bold;">true</span></pre></div></div>

<p>Así una vez por día se actualiza el sitemap y se hace un ping a google para que sepa que debe pasar a reindexar el contenido.</p>
<p>Esto tiene varias desventajas (pero aún así para este sitio sirve a su propópito) :</p>
<ul>
<li>No se puede priorizar cada tipo de contenido fácilmente</li>
<li>La fecha de última modificación es inexacta</li>
<li>Carga el webserver para generar el sitemap</li>
</ul>
<p>Código completo : <a href='http://www.gazer.com.ar/wp-content/uploads/2009/07/crawler.rb'>crawler.rb</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.gazer.com.ar/2009/07/15/sitemaps-via-crawling/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Refactoring de &#8220;Fat Methods&#8221; &#8211; Episodio 5</title>
		<link>http://www.gazer.com.ar/2009/03/18/refactoring-de-fat-methods-episodio-5/</link>
		<comments>http://www.gazer.com.ar/2009/03/18/refactoring-de-fat-methods-episodio-5/#comments</comments>
		<pubDate>Wed, 18 Mar 2009 14:30:00 +0000</pubDate>
		<dc:creator>Gazer</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[refactoring]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.gazer.com.ar/?p=1040</guid>
		<description><![CDATA[Pasar programadores PHP a Ruby no siempre es una tarea trivial, mayormente porque lo primero que tratan de hacer es escribir código en lugar de pensar que es lo que quieren hacer . No es que me queje, a todos nos pasa que resolvemos el problema como primero sale (más aún cuando las fechas de [...]]]></description>
			<content:encoded><![CDATA[<p>Pasar programadores PHP a Ruby no siempre es una tarea trivial, mayormente porque lo primero que tratan de hacer es escribir código en lugar de pensar que es lo que quieren hacer <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . No es que me queje, a todos nos pasa que resolvemos el problema como primero sale (más aún cuando las fechas de entrega son apretadas <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<p>El otro día queriendo modificar un código me encuentro con este before_filter en un controller :</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">def</span> setprofile
  <span style="color:#0066ff; font-weight:bold;">@viewer</span> = <span style="color:#0000FF; font-weight:bold;">false</span>
  <span style="color:#9966CC; font-weight:bold;">if</span> <span style="color:#006600; font-weight:bold;">&#40;</span>!params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'id'</span><span style="color:#006600; font-weight:bold;">&#93;</span> <span style="color:#006600; font-weight:bold;">&amp;&amp;</span> !params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'profile_id'</span><span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#0066ff; font-weight:bold;">@profile</span> = <span style="color:#0066ff; font-weight:bold;">@passport</span>
  <span style="color:#9966CC; font-weight:bold;">else</span>
    <span style="color:#9966CC; font-weight:bold;">if</span> params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'profile_id'</span><span style="color:#006600; font-weight:bold;">&#93;</span>
      pid = params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'profile_id'</span><span style="color:#006600; font-weight:bold;">&#93;</span>
    <span style="color:#9966CC; font-weight:bold;">else</span>
      pid = params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'id'</span><span style="color:#006600; font-weight:bold;">&#93;</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
    <span style="color:#0066ff; font-weight:bold;">@profile</span> = Passport.<span style="color:#9900CC;">find_by_id</span><span style="color:#006600; font-weight:bold;">&#40;</span>pid<span style="color:#006600; font-weight:bold;">&#41;</span>			
    <span style="color:#9966CC; font-weight:bold;">if</span> <span style="color:#006600; font-weight:bold;">&#40;</span>!@profile<span style="color:#006600; font-weight:bold;">&#41;</span> 
      redirect_to<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:controller</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'my3dg'</span>, <span style="color:#ff3333; font-weight:bold;">:action</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'show'</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">and</span> <span style="color:#0000FF; font-weight:bold;">return</span> <span style="color:#0000FF; font-weight:bold;">false</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
    <span style="color:#9966CC; font-weight:bold;">if</span> <span style="color:#006600; font-weight:bold;">&#40;</span>@profile.<span style="color:#9900CC;">id</span> != <span style="color:#0066ff; font-weight:bold;">@passport</span>.<span style="color:#9900CC;">id</span><span style="color:#006600; font-weight:bold;">&#41;</span>
      <span style="color:#0066ff; font-weight:bold;">@viewer</span> = <span style="color:#0000FF; font-weight:bold;">true</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>			
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></td></tr></table></div>

<p>Lo primero que pense &#8220;uff, que paja, que complicado&#8221; hasta que miré mejor la estructura y ahí comprendí que en realidad era muy simple, solo que estaba escrito de forma complicada. Lo primero que me gustó de Ruby cuando empecé a entenderlo fue el uso de &#8220;||&#8221; para reemplazar esos molestos if anidados cuando tengo más de una opción. &#8220;Si no está a, poner a en el valor default&#8221;. En ruby es un simple &#8220;a || valor_default&#8221; (ojo con los valores de verdad en ruby).</p>
<p>Lo otro que veo mucho (incluso en los trabajos que corrijo en la facultad) es utilizar un if para después setear una variable en true o false, cuando casi todos los lenguajes aceptan expresiones booleanas del lado derecho de la igualdad.</p>
<p>Refactorizando el método anterior obtenemos el siguiente código :</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
</pre></td><td class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">def</span> setprofile
  <span style="color:#0066ff; font-weight:bold;">@profile</span> = Passport.<span style="color:#9900CC;">find_by_id</span><span style="color:#006600; font-weight:bold;">&#40;</span>params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'profile_id'</span><span style="color:#006600; font-weight:bold;">&#93;</span> <span style="color:#006600; font-weight:bold;">||</span> params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'id'</span><span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">||</span> <span style="color:#0066ff; font-weight:bold;">@passport</span>
  <span style="color:#0066ff; font-weight:bold;">@viewer</span> = <span style="color:#0066ff; font-weight:bold;">@profile</span>.<span style="color:#9900CC;">id</span> != <span style="color:#0066ff; font-weight:bold;">@passport</span>.<span style="color:#9900CC;">id</span>
  redirect_to<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:controller</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'my3dg'</span>, <span style="color:#ff3333; font-weight:bold;">:action</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'show'</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">unless</span> <span style="color:#0066ff; font-weight:bold;">@profile</span>
  <span style="color:#0000FF; font-weight:bold;">return</span> <span style="color:#0000FF; font-weight:bold;">false</span> <span style="color:#9966CC; font-weight:bold;">unless</span> <span style="color:#0066ff; font-weight:bold;">@profile</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></td></tr></table></div>

<p>Como nota, uso find_by_id porque retorna nil en caso de no encontrar el registro, en cambio find tira una excepción RecordNotFound, de esa forma puedo acoplar otro &#8220;||&#8221; para setear el valor default (@passport es la credencial del usuario loggeado en este caso).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gazer.com.ar/2009/03/18/refactoring-de-fat-methods-episodio-5/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Do it right! &#8211; Manejo de dependencias</title>
		<link>http://www.gazer.com.ar/2009/03/09/do-it-right-manejo-de-dependencias/</link>
		<comments>http://www.gazer.com.ar/2009/03/09/do-it-right-manejo-de-dependencias/#comments</comments>
		<pubDate>Mon, 09 Mar 2009 23:48:02 +0000</pubDate>
		<dc:creator>Gazer</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[GNU+Linux]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[deploy]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.gazer.com.ar/?p=1021</guid>
		<description><![CDATA[En estos días he tenido varias discusiones sobre algunas prácticas muy acostumbradas en varios proyectos, que a mi parecer están muy lejos de lo útil. Es por eso que decidí escribir un par de artículos denominados &#8220;Do it right!&#8221; (hazlo bien!, si mi inglés no es tan malo como pienso ). En esta primer entrega [...]]]></description>
			<content:encoded><![CDATA[<p>En estos días he tenido varias discusiones sobre algunas prácticas muy acostumbradas en varios proyectos, que a mi parecer están muy lejos de lo útil. Es por eso que decidí escribir un par de artículos denominados &#8220;Do it right!&#8221; (hazlo bien!, si mi inglés no es tan malo como pienso <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> ). En esta primer entrega voy a tratar el tema de manejo de dependencias.</p>
<p><strong>Escenario</strong></p>
<p>Muchas veces uno se encuentra con un software que necesita y le es útil. Vamos a suponer de entrada que no tiene paquete para su distribución y/o sistema operativo favorito. En al leer el README vemos que depende de muchas cosas : alguna biblioteca de procesamiento de imágenes, algún captcha system o lo que sea.</p>
<p>Mirando mejor nos encontramos con una realidad muy fea : todas las dependencias están incluidas en el archivo que nos bajamos originalmente, y alguna veces se cargan por métodos poco ortodoxos.</p>
<p>Cualquiera que haya bajado cosas en PHP o Java seguramente se ha encontrado con esto : Clases bajadas de phpclasses.org o miles de archivos JAR en un directorio lib de la aplicación Java que se cargan en el classpath. También lo he visto en varios proyectos Rails últimamente.</p>
<p>La excusa que siempre escucho es &#8220;<em>Because it makes the app more self contained</em>&#8220;, si, si, BWTF. Pero la pregunta es : ¿es la forma correcta?. Si respondes que si, estás muy lejos de la realidad y seguramente tu sysadmin te odia mucho, pero mucho <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<p><strong>El Problema</strong></p>
<p>Entonces, ¿cuál es el problema con esto?. En principio parece una maravilla, nos facilita todo, descomprimimos y ya está!, el sysadmin (nosotros) agradecido, pero si el sysdamin es otra persona y se respeta los va a putear mucho, pero muuuuuucho.</p>
<p>Uno de los problemas con empaquetar todas las dependencias es la facilidad de solucionar problemas a futuro. El mejor ejemplo que he vivido es el típico cambio de time zone en nuestro país. PHP, por ejemplo, tiene su propia DB de timezones, y si actualizamos el tzdata de nuestro servidor, maravillosamente PHP ni se entera. Está bien que en este caso como programadores no podemos hacer mucho, es el lenguaje el que está mal, pero el ejemplo ilustra mi punto.</p>
<p>Un software con el que tuve que trabajar, vBulletin, va aún más allá. Tiene su propia DB de timezones por arriba de la que tiene PHP!, hardcodeada en un array!. ¿La excusa? Que la de PHP no anda bien en Windows y es mejor de esta manera.</p>
<p>Otro ejemplo que puedo dar es el otro día cuando migré unos sitios en el trabajo. Uno de los admines se quejó que dejó de andar porque usaba la función &#8220;dl&#8221; de PHP para cargar extensiones en tiempo de ejecución! (es decir, cualquier script PHP puede cargar un .so que extiende PHP de cualquier manera, incluso para saltearse otras medidas de seguridad), lo que no solo es una chanchada, sino que es un potencial problema de seguridad.</p>
<p>Tener el manejo de dependencias con la aplicación además nos prohíbe actualizar fácilmente un bug de seguridad, usabilidad o lo que sea de esa dependencia. No podemos en muchos casos hacer un <em>upgrade</em> de ese paquete que está embebido dentro de un todo llamado &#8220;aplicación&#8221;.</p>
<p>En rails he visto ya muchos que acostumbran a usar &#8220;rake gems:unpack&#8221; para meter las gemas de las que dependen en el directorio vendors/gems, así no las tienen que instalar aparte. Rubygems disgusta en muchos ambientes, no voy a entrar en eso hoy <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . Si uno depende  de una versión específica la forma correcta sería especificar en el environment.rb la versión, y luego que el que hace el deploy haga un &#8220;rake gems:install&#8221; para asegurar que se cumplan las dependencias. Con Capistrano es una papa hacer esta tarea automáticamente <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> .</p>
<p><strong>Entonces, ¿nunca más lo hago?</strong></p>
<p>No me gustan los extremos y creo que estas malas prácticas que se ven hoy tiene usos muy específicos.</p>
<p>Supongamos que el proyecto X usa la lib Y, pero tal cual la provee upstreem no me sirve. Lo lógico es hacer un patch para upstream solucionando el problema o agregando la característica.</p>
<p>Mientras upstream acepta el patch y realiza un nuevo release, ahí si tiene sentido tal vez, y de forma temporal tener esa dependencia embebida.</p>
<p>Un ejemplo donde yo lo estoy haciendo es con el uso de la gema contacts, que depende de la gema json. En una aplicación con Rails 1.2.6 no hay ningún problema, pero en otra que usa Rails 2.1 si, porque este último integra un parser de JSON y conflictua con la gema del mismo nombre. La opción lógica sería migrar la aplicación 1.2.6 a una nueva versión de Rails, cosa que no siempre es fácil y se necesita el tiempo. Como no se pudo, es más fácil meter en uno de los proyectos la gema de manera que no moleste al resto de los proyectos en el transcurso de la actualzación.</p>
<p>Seguro hay algún otro caso, ahora no se me ocurre alguno que realmente lo justifique.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gazer.com.ar/2009/03/09/do-it-right-manejo-de-dependencias/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Instalá tus Gemas con apt-get</title>
		<link>http://www.gazer.com.ar/2009/01/15/instala-tus-gemas-con-apt-get/</link>
		<comments>http://www.gazer.com.ar/2009/01/15/instala-tus-gemas-con-apt-get/#comments</comments>
		<pubDate>Thu, 15 Jan 2009 14:01:16 +0000</pubDate>
		<dc:creator>Gazer</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[debian]]></category>
		<category><![CDATA[gema]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.gazer.com.ar/?p=998</guid>
		<description><![CDATA[Finalmente y pese a todas la quejas de los debianista de que era &#8220;dificil&#8221; o &#8220;imposible&#8221; ya hay una solución (o al menos eso parece, todavía no la probé ). La gente que hizo Passenger (a.k.a mod_rails) creó DebGem, que permite instalar prácticamente cualquier gema usando apt-get. El servicio no va a ser gratuito según [...]]]></description>
			<content:encoded><![CDATA[<p>Finalmente y pese a todas la quejas de los debianista de que era &#8220;dificil&#8221; o &#8220;imposible&#8221; ya hay una solución (o al menos eso parece, todavía no la probé <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> ).</p>
<p>La gente que hizo Passenger (a.k.a mod_rails) creó <a href="http://www.debgem.com/">DebGem</a>, que permite instalar prácticamente cualquier gema usando apt-get. El servicio no va a ser gratuito según parece una vez que pase la fase Beta, pero de todos modos demuestra que las gemas son empaquetables <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> .</p>
<p>¿El precio? No lo dicen, pero aclaran que &#8220;subscription probably only costs about 1-2 hours of your time per month&#8221;. Veremos si tiene éxito y si motiva a terceros a crear algo similar <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> .</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gazer.com.ar/2009/01/15/instala-tus-gemas-con-apt-get/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>State Machine</title>
		<link>http://www.gazer.com.ar/2008/12/08/state-machine/</link>
		<comments>http://www.gazer.com.ar/2008/12/08/state-machine/#comments</comments>
		<pubDate>Mon, 08 Dec 2008 18:33:39 +0000</pubDate>
		<dc:creator>Gazer</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[videojuegos]]></category>

		<guid isPermaLink="false">http://www.gazer.com.ar/?p=702</guid>
		<description><![CDATA[Varios días atrás tuve que hacer un juego simple, un memotest para ser exacto, para correr en unos &#8220;kioskos&#8221; para un cliente. Ya que tenía pendiente aprender a usar RubyGame, lo hicimos con este framework para ver que onda, ya que hasta ahora veníamos usando pyGame. El juego salió super rápido, sin mayores problemas, pero [...]]]></description>
			<content:encoded><![CDATA[<p>Varios días atrás tuve que hacer un juego simple, un memotest para ser exacto, para correr en unos &#8220;kioskos&#8221; para un cliente. Ya que tenía pendiente aprender a usar RubyGame, lo hicimos con este framework para ver que onda, ya que hasta ahora veníamos usando pyGame.</p>
<p>El juego salió super rápido, sin mayores problemas, pero la lógica de juego no me gustaba porque teníamos que andar trackeando el estado actual a mano, muchos ifs y comprobaciones que hacían del loop de juego un choclo de código.</p>
<p>Es por eso que me puse a ver un poco como aprovechar el tener bloques de código para encapsular la lógica del juego un poco más prolijo. Antes de comenzar encontré la gema <a href="http://statemachine.rubyforge.org/">Statemachine</a> pero a primera vista no la entendí mirando los ejemplos <img src='http://www.gazer.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  y luego de jugar un rato no me terminó de convencer ya que parece mucho más de lo que yo necesitaba.</p>
<p>El resultado de un par de horas de tirar &#8220;magia&#8221; fue poder definir la lógica de la siguiente manera (el ejemplo está simplificado, omitiendo los efectos y parte de la lógica) :</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> Logic
  <span style="color:#9966CC; font-weight:bold;">include</span> StateMachine
  <span style="color:#008000; font-style:italic;"># Esperando interacción del usuario</span>
  state <span style="color:#ff3333; font-weight:bold;">:user_input</span> <span style="color:#9966CC; font-weight:bold;">do</span>
    <span style="color:#0066ff; font-weight:bold;">@events</span>.<span style="color:#9900CC;">each</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#006600; font-weight:bold;">|</span>event<span style="color:#006600; font-weight:bold;">|</span>
      <span style="color:#9966CC; font-weight:bold;">case</span> event
      <span style="color:#9966CC; font-weight:bold;">when</span> MouseDownEvent
        selected event.<span style="color:#9900CC;">pos</span>
      <span style="color:#9966CC; font-weight:bold;">when</span> QuitEvent
        end_game
      <span style="color:#9966CC; font-weight:bold;">end</span>
    <span style="color:#006600; font-weight:bold;">&#125;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># Oculta las piezas seleccionadas cuando no hubo match</span>
  state <span style="color:#ff3333; font-weight:bold;">:clear</span> <span style="color:#9966CC; font-weight:bold;">do</span>
    <span style="color:#0066ff; font-weight:bold;">@selected</span>.<span style="color:#9900CC;">each</span> <span style="color:#006600; font-weight:bold;">&#123;</span><span style="color:#006600; font-weight:bold;">|</span>f<span style="color:#006600; font-weight:bold;">|</span> f.<span style="color:#9900CC;">hide</span> <span style="color:#006600; font-weight:bold;">&#125;</span>
    <span style="color:#0066ff; font-weight:bold;">@selected</span> = <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#006600; font-weight:bold;">&#93;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># Cambio de estado</span>
  transition <span style="color:#ff3333; font-weight:bold;">:user_input</span>, <span style="color:#ff3333; font-weight:bold;">:clear</span> <span style="color:#9966CC; font-weight:bold;">do</span>
    <span style="color:#0066ff; font-weight:bold;">@selected</span>.<span style="color:#9900CC;">size</span> == <span style="color:#006666;">2</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># Cambio de estado</span>
  transition <span style="color:#ff3333; font-weight:bold;">:clear</span>, <span style="color:#ff3333; font-weight:bold;">:user_input</span> <span style="color:#9966CC; font-weight:bold;">do</span>
    <span style="color:#0000FF; font-weight:bold;">true</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Cada declaración de <code>state</code> tiene el código que se debe ejecutar cuando estamos en dicho estado, mientras que las <code>transition</code> son usadas automáticamente para saber a qué estado nos debemos mover. La primer <code>transition</code> que retorne <code>true</code>, se toma el estado destino y se asigna como el actual.</p>
<p>Por el lado del game loop, lo único que se debe hacer es llamar a un método que se encarga de ejecutar el estado actual y luego verificar si alguna transición retorna &#8220;true&#8221; y se cambia al nuevo estado.</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> Game
  <span style="color:#9966CC; font-weight:bold;">include</span> Rubygame
  <span style="color:#9966CC; font-weight:bold;">include</span> Logic
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> event_loop
    <span style="color:#CC0066; font-weight:bold;">loop</span> <span style="color:#9966CC; font-weight:bold;">do</span>
      current_state
&nbsp;
      <span style="color:#0000FF; font-weight:bold;">return</span> <span style="color:#9966CC; font-weight:bold;">if</span> game_ended?
&nbsp;
      draw
      <span style="color:#0066ff; font-weight:bold;">@clock</span>.<span style="color:#9900CC;">tick</span>
      <span style="color:#0066ff; font-weight:bold;">@screen</span>.<span style="color:#9900CC;">update</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>En <code>Game</code> hay otros métodos auxiliares como <code>game_ended</code>, <code>draw</code> y <code>selected</code>, que no vienen mucho al caso en este momento.</p>
<p>El próximo paso ahora es limpiar un poco esto, ver si no hay una forma mejor de hacerla y publicar el esqueleto completo (la idea a futuro es tener un generator) para poder tener un mini framework para hacer juegos simples.</p>
<p>Si buscan un framework interesante les recomiendo <a href="http://www.shatteredruby.com/">Shattered Ruby</a> (<a href="http://github.com/mikkel/shattered/tree/master">git repo</a>), aunque al momento de escribir este post el sitio principal no responde.</p>
<p><a href='http://www.gazer.com.ar/wp-content/uploads/2008/12/state_machine.rb'>state_machine.rb</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.gazer.com.ar/2008/12/08/state-machine/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

