<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6925781032970580633</id><updated>2012-01-18T14:14:02.448-06:00</updated><title type='text'>Pablo Q.</title><subtitle type='html'>ruby, rails, linux, redes, libros, computinadas en general</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://pabloqs.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6925781032970580633/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://pabloqs.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Pablo</name><uri>http://www.blogger.com/profile/09019438687952508022</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>3</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6925781032970580633.post-2251823400346853501</id><published>2009-02-20T09:44:00.010-06:00</published><updated>2009-02-20T10:51:12.317-06:00</updated><title type='text'>ruby "retry" más elegante utilizando module_eval</title><content type='html'>Muchas veces necesitamos hacer un retry de un bloque de código n veces después de recibir una excepción. Por ejemplo podríamos recibir un "execution expired" tratando de conectarse a un mail server , un "Connection closed" transfiriendo un archivo por ftp, etc.&lt;br /&gt;&lt;br /&gt;Como hacerlo al estilo ruby?&lt;br /&gt;&lt;br /&gt;Si queremos hacer un retry de 2 o 3 veces es tan simple como esto:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;retries = 2&lt;br /&gt;begin&lt;br /&gt;raise "error"&lt;br /&gt;rescue =&gt; e&lt;br /&gt;puts e.message&lt;br /&gt;retry if (retries-=1)&gt;0&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;La sentencia &lt;span style="font-weight: bold;"&gt;raise&lt;/span&gt; simula la detección del error en el ejemplo, ya que llama la excepción (manejada por el Objecto Exception) enviando un mensaje llamado "error".&lt;br /&gt;&lt;br /&gt;El resultado es:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Cae al bloque rescue que maneja el error y imprime el error ("puts e.message").&lt;br /&gt;&lt;br /&gt;Como hacerlo más elegante? :)&lt;br /&gt;&lt;br /&gt;Me gustaría poder hacerlo así:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;ok = retry_two do&lt;br /&gt;raise 'error'&lt;br /&gt;end&lt;br /&gt;puts ok&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;con el siguiente resultado:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;false&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Es decir, ejecutar el bloque haciendo un retry de 2 escrito en texto que reciba un bloque y que devuelta si este fue ejecutado correctamente (asignándolo a la variable ok).&lt;br /&gt;&lt;br /&gt;Este código permite hacer eso:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;require 'rubygems'&lt;br /&gt;require 'linguistics'&lt;br /&gt;Linguistics::use(:en)&lt;br /&gt;module FortyRetry&lt;br /&gt;40.times do |n|&lt;br /&gt;    module_eval %Q{&lt;br /&gt;    def     retry_#{(n+1).en.numwords.gsub(' ','_').gsub('-','_')}&lt;br /&gt;        x = #{n} + 1&lt;br /&gt;        ok = true&lt;br /&gt;        begin&lt;br /&gt;            yield&lt;br /&gt;        rescue =&gt; e&lt;br /&gt;            puts e.message&lt;br /&gt;            retry if (x-=1)&gt;0&lt;br /&gt;            ok = false&lt;br /&gt;        end&lt;br /&gt;        ok&lt;br /&gt;    end&lt;br /&gt;    }&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Esta implementación utiliza "module_eval" que evalúa un string o bloque en el contexto de un modulo (utilicé module_eval en ves de define_method por ciertos problemas con los bloques explicados &lt;a href="http://groups.google.com/group/ruby-talk-google/browse_thread/thread/11b4468c280637d6"&gt;aquí&lt;/a&gt;). En este caso definí un modulo llamado "FortyRetry" porque esta genera 40 retries en el siguiente estilo:&lt;br /&gt;&lt;br /&gt;retry_numero_en_letras do&lt;br /&gt;...&lt;br /&gt;...&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;hagamosle un "puts FortyRetry.methods" para verlos:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;puts FortyRetry.methods.sort&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;....&lt;br /&gt;public_method_defined?&lt;br /&gt;public_methods&lt;br /&gt;respond_to?&lt;br /&gt;retry_eight&lt;br /&gt;retry_eighteen&lt;br /&gt;retry_eleven&lt;br /&gt;retry_fifteen&lt;br /&gt;retry_five&lt;br /&gt;retry_forty&lt;br /&gt;retry_four&lt;br /&gt;retry_fourteen&lt;br /&gt;retry_nine&lt;br /&gt;retry_nineteen&lt;br /&gt;retry_one&lt;br /&gt;retry_seven&lt;br /&gt;retry_seventeen&lt;br /&gt;retry_six&lt;br /&gt;retry_sixteen&lt;br /&gt;retry_ten&lt;br /&gt;retry_thirteen&lt;br /&gt;retry_thirty&lt;br /&gt;retry_thirty_eight&lt;br /&gt;retry_thirty_five&lt;br /&gt;retry_thirty_four&lt;br /&gt;retry_thirty_nine&lt;br /&gt;retry_thirty_one&lt;br /&gt;retry_thirty_seven&lt;br /&gt;retry_thirty_six&lt;br /&gt;retry_thirty_three&lt;br /&gt;retry_thirty_two&lt;br /&gt;retry_three&lt;br /&gt;retry_twelve&lt;br /&gt;retry_twenty&lt;br /&gt;retry_twenty_eight&lt;br /&gt;retry_twenty_five&lt;br /&gt;retry_twenty_four&lt;br /&gt;retry_twenty_nine&lt;br /&gt;retry_twenty_one&lt;br /&gt;retry_twenty_seven&lt;br /&gt;retry_twenty_six&lt;br /&gt;retry_twenty_three&lt;br /&gt;retry_twenty_two&lt;br /&gt;retry_two&lt;br /&gt;send&lt;br /&gt;singleton_met......&lt;br /&gt;&lt;br /&gt;La librería "linguistics" es la encargada de realizar la conversión de números a números-palabras.&lt;br /&gt;&lt;br /&gt;Por ejemplo:&lt;br /&gt;&lt;br /&gt;10.en.numwords =&gt; ten&lt;br /&gt;28.en.numwords =&gt; twenty eight&lt;br /&gt;&lt;br /&gt;entonces cuando hacemos:&lt;br /&gt;&lt;br /&gt;module_eval %Q{&lt;br /&gt;     def     retry_#{(n+1).en.numwords.gsub(' ','_').gsub('-','_')}&lt;br /&gt;....&lt;br /&gt;&lt;br /&gt;Crea los métodos en texto convirtiendo los números a texto-números.&lt;br /&gt;&lt;br /&gt;Ya podemos hacerlo elegantemente en nuestro código:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;include FortyRetry&lt;br /&gt;puts retry_seventeen{ raise 'error'}&lt;br /&gt;puts retry_six{ raise 'error'}&lt;br /&gt;puts retry_sixteen{ raise 'error'}&lt;br /&gt;puts retry_ten{ raise 'error'}&lt;br /&gt;puts retry_thirteen{ raise 'error'}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Con los siguientes resultados:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;false&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;false&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;false&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;false&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;error&lt;br /&gt;false&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Mucho más elegante sin duda :).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6925781032970580633-2251823400346853501?l=pabloqs.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pabloqs.blogspot.com/feeds/2251823400346853501/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://pabloqs.blogspot.com/2009/02/ruby-retry-mas-elegante-utilizando.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6925781032970580633/posts/default/2251823400346853501'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6925781032970580633/posts/default/2251823400346853501'/><link rel='alternate' type='text/html' href='http://pabloqs.blogspot.com/2009/02/ruby-retry-mas-elegante-utilizando.html' title='ruby &quot;retry&quot; más elegante utilizando module_eval'/><author><name>Pablo</name><uri>http://www.blogger.com/profile/09019438687952508022</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6925781032970580633.post-6331644715337103129</id><published>2009-01-15T22:11:00.029-06:00</published><updated>2009-01-16T20:01:15.263-06:00</updated><title type='text'>Función de busqueda: usando hash o sqlite3?</title><content type='html'>&lt;div style="text-align: justify;"&gt;Muchas veces necesitamos tener una función que devuelva varios valores usando como parámetro uno solo, la mayoría de las veces estás son pequeñas y se almacena usando hash, y si es más grande usualmente se utiliza una base de datos donde se monta una tabla con llave primaria el valor en el búsqueda (por lo tanto única).&lt;br /&gt;Este pequeño articulo no pretende predicar uno o el otro, sino mostrar su beneficios tanto en flexibilidad como en rendimiento.&lt;br /&gt;Cuando estamos hablando de grande cantidades de datos usualmente optamos por el uso de bases de datos, pero a veces estos motores salen sobrando para lo que realmente necesitamos (más ahora que la memoria RAM viene en gigas). Por esta razón se me vino a la mente sqlite que es una micro-base de datos (en su versión 3-3-6-10 pesa tan solo 206.85KiB) gratuita disponible en todos los sistemas operativos de mayor uso (linux, mac, windows).&lt;br /&gt;En este caso solo necesitamos tener los binarios accesibles y poder construir librería que manejen considerable cantidad de datos disponibles sin instalar grandes motores de base de datos para tareas simples, como por ejemplo funciones de conversiones de datos de alto costo computacional pre-computadas.&lt;br /&gt;El problema especifico se basa es la necesidad de una función que reciba un nombre de una empresa y devuelva este nombre en su versión "estandar".&lt;br /&gt;&lt;br /&gt;veamos el siguiente ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Mac Donalds&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Restaurantes Mac Donalds&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Rest Mac Donalds&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Mac Donalds S.A.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;La función que necesitamos recibiría estos nombres y devolvería su nombre "estandar"  que sería "Mac Donalds" para todos los casos anteriores.&lt;br /&gt;Esto es muy común en limpieza de datos. En mi caso estas relaciones de estandarización fueron hechas por personas "a pata" ayudados por software.&lt;br /&gt;La herramienta utiliza estos metadatos como fuente, y se vería maso menos así:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;std_name('Mac Donalds') =&gt; 'Mac Donalds'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;std_name('Restaurantes Mac Donalds') =&gt; 'Mac Donalds'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;std_name('Rest Mac Donalds') =&gt; 'Mac Donalds'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;std_name('Mac Donalds S.A.') =&gt; 'Mac Donalds'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Los metadatos están en un archivo CSV. Se realizaron dos versiones una usando hash's y array's en memoria y otra usando sqlite3.&lt;br /&gt;&lt;br /&gt;Estas realizan los siguientes tipos de búsqueda:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;búsqueda exacta:&lt;/span&gt; la versión por match exacto devuelve el standard name en donde el nombre es exactamente igual al suministrado.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;búsqueda aproximada al estilo 'like': &lt;/span&gt; en este caso devolverá el primer match en donde la palabra suministrada este contenida dentro del campo de búsqueda, al estilo de name "like '%campo_parametro%'”&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Versión Hash&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;archivo&lt;/span&gt;: std_names_hash.rb&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;STD_NAME = {}&lt;/code&gt;&lt;br /&gt;&lt;code&gt;File.new("#{File.dirname(__FILE__)}/STD_NAMES_MASTER.csv",'r').each{|line|&lt;/code&gt;&lt;br /&gt;&lt;code&gt; record = line.gsub(/[\n\r]/,'').split(",",-1)&lt;/code&gt;&lt;br /&gt;&lt;code&gt; STD_NAME[record[0]]=[record[1],record[2]]&lt;/code&gt;&lt;br /&gt;&lt;code&gt;}&lt;/code&gt;&lt;br /&gt;&lt;code&gt;STD_NAME_FLAT = STD_NAME.to_a&lt;/code&gt;&lt;br /&gt;&lt;code&gt;def std_names_fake_like name&lt;/code&gt;&lt;br /&gt;&lt;code&gt; res = STD_NAME_FLAT.detect{|v| v[0] =~ Regexp.new("^.*(#{name}).*$")}&lt;/code&gt;&lt;br /&gt;&lt;code&gt; res||=[nil,[nil,nil]]&lt;/code&gt;&lt;br /&gt;&lt;code&gt; res[1]&lt;/code&gt;&lt;br /&gt;&lt;code&gt;end&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;La preparación del Hash es bastante simple, un simple barrido secuencial archivo e su inserción. Para simular búsquedas estilo like convertí el hash en array para poder utilizar el método detect en conjunto con expresiones regulares. El método detect devuelve el primer valor que haga match en este caso con la expresión regular "^.*(valor).*$" usando el objeto Regexp, esta expresión regular equivale al "like '%valor%'" de sql.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Versión Sqlite3&lt;/span&gt;&lt;br /&gt;_______&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Nota&lt;/span&gt;&lt;/span&gt;: se necesita tener creada la base de datos corriendo el siente comando:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;~#sqlite3 test.db&lt;/code&gt;&lt;br /&gt;&lt;code&gt;SQLite version 3.4.2&lt;/code&gt;&lt;br /&gt;&lt;code&gt;Enter ".help" for instructions&lt;/code&gt;&lt;br /&gt;&lt;code&gt;sqlite&gt; .quit&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;Le damos '.quit' para salir.&lt;br /&gt;_______&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;archivo&lt;/span&gt;&lt;span style="font-style: italic;"&gt;: std_names_sqlite3.rb&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;`sqlite3 test.db "drop table std_names"`&lt;/code&gt;&lt;br /&gt;&lt;code&gt;`sqlite3 test.db "create table std_names(std_name TEXT,bus_name TEXT,sic TEXT)"`&lt;/code&gt;&lt;br /&gt;&lt;code&gt;`sqlite3 -separator , test.db ".import STD_NAMES_MASTER.csv std_names"`&lt;/code&gt;&lt;br /&gt;&lt;code&gt;`sqlite3 test.db "create index au_name_idx on std_names (std_name)"`&lt;/code&gt;&lt;br /&gt;&lt;code&gt;require 'dbi'&lt;/code&gt;&lt;br /&gt;&lt;code&gt;@dbh = DBI.connect('DBI:SQLite3:test.db', '', '')&lt;/code&gt;&lt;br /&gt;&lt;code&gt;def std_names name&lt;/code&gt;&lt;br /&gt;&lt;code&gt;@dbh.select_one("select bus_name,sic from std_names where std_name = '#{name}';") rescue [nil,nil,nil]&lt;/code&gt;&lt;br /&gt;&lt;code&gt;end&lt;/code&gt;&lt;br /&gt;&lt;code&gt;def std_names_with_like name&lt;/code&gt;&lt;br /&gt;&lt;code&gt;@dbh.select_one("select bus_name,sic from std_names where std_name like '%#{name}%';") rescue [nil,nil,nil]&lt;/code&gt;&lt;br /&gt;&lt;code&gt;end&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;La primera linea borra la tabla en caso de que exista, la segunda crea la tabla, la tercera inserta el archivo CSV y la cuarta crea un índice sobre la columna de búsqueda. Están definidos dos métodos, uno para búsquedas exactas y otro para busquedas utilizando like, estas dos devolviendo la primera ocurrencia que haga match.&lt;br /&gt;&lt;br /&gt;Para verificar que esta corriendo correctamente corrí cuatro variaciones de nombres de empresas que son la misma  (todas deberian de tener el mismo nombre estándar de empresa) para match, y un par de ejemplo match por aproximación con un segmento del nombre de la empresa.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;archivo&lt;/span&gt;&lt;span style="font-style: italic;"&gt;: test_run_std_names.rb&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;require 'std_names_hash'&lt;/code&gt;&lt;br /&gt;&lt;code&gt;require 'std_names_sqlite3'&lt;/code&gt;&lt;br /&gt;&lt;code&gt;puts "exact match:"&lt;/code&gt;&lt;br /&gt;&lt;code&gt;['SUPERCUTS 90198','SUPERCUTS 90250','SUPERCUTS 90471','SUPERCUTS 9765'].each{|business_name|&lt;/code&gt;&lt;br /&gt;&lt;code&gt; puts "hash:    #{STD_NAME[business_name][0]}" &lt;/code&gt;&lt;br /&gt;&lt;code&gt;            puts "sqlite3: #{std_names(business_name)[0]}"&lt;/code&gt;&lt;br /&gt;&lt;code&gt;}&lt;/code&gt;&lt;br /&gt;&lt;code&gt;puts "using like (or fake like):"&lt;/code&gt;&lt;br /&gt;&lt;code&gt;puts "Using sqlite3:"&lt;/code&gt;&lt;br /&gt;&lt;code&gt;puts std_names_with_like('PERCU')[0]&lt;/code&gt;&lt;br /&gt;&lt;code&gt;puts std_names_with_like('PERCUTS 9')[0]&lt;/code&gt;&lt;br /&gt;&lt;code&gt;puts "Using array, detect and regular expresion:"&lt;/code&gt;&lt;br /&gt;&lt;code&gt;puts std_names_fake_like('PERCU')[0]&lt;/code&gt;&lt;br /&gt;&lt;code&gt;puts std_names_fake_like('PERCUTS 9')[0]&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;Después de correrlo dio los siguientes resultados:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;exact match:&lt;/code&gt;&lt;br /&gt;&lt;code&gt;hash:    SUPERCUTS&lt;/code&gt;&lt;br /&gt;&lt;code&gt;sqlite3: SUPERCUTS&lt;/code&gt;&lt;br /&gt;&lt;code&gt;hash:    SUPERCUTS&lt;/code&gt;&lt;br /&gt;&lt;code&gt;sqlite3: SUPERCUTS&lt;/code&gt;&lt;br /&gt;&lt;code&gt;hash:    SUPERCUTS&lt;/code&gt;&lt;br /&gt;&lt;code&gt;sqlite3: SUPERCUTS&lt;/code&gt;&lt;br /&gt;&lt;code&gt;hash:    SUPERCUTS&lt;/code&gt;&lt;br /&gt;&lt;code&gt;sqlite3: SUPERCUTS&lt;/code&gt;&lt;br /&gt;&lt;code&gt;using like (or fake like):&lt;/code&gt;&lt;br /&gt;&lt;code&gt;Using sqlite3:&lt;/code&gt;&lt;br /&gt;&lt;code&gt;SUPERCUTS&lt;/code&gt;&lt;br /&gt;&lt;code&gt;SUPERCUTS&lt;/code&gt;&lt;br /&gt;&lt;code&gt;Using array, detect method and regular expresion:&lt;/code&gt;&lt;br /&gt;&lt;code&gt;SUPERCUTS&lt;/code&gt;&lt;br /&gt;&lt;code&gt;SUPERCUTS&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Hasta el momento las dos implementaciones soluciona el problema. Para medir cual de las dos solucionan mejor problema realicé pruebas de rendimiento, a tres niveles, carga de datos, match exacto y match aproximado. Esto se resume en el siguiente script.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;archivo&lt;/span&gt;&lt;span style="font-style: italic;"&gt;: test_hash_vs_sqlite3.rb&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;require 'benchmark'&lt;/code&gt;&lt;br /&gt;&lt;code&gt;puts 'load time:'&lt;/code&gt;&lt;br /&gt;&lt;code&gt;Benchmark.bm do |x|&lt;/code&gt;&lt;br /&gt;&lt;code&gt; x.report("hash:   ")   { require 'std_names_hash'}&lt;/code&gt;&lt;br /&gt;&lt;code&gt; x.report("sqlite3:") {require 'std_names_sqlite3'}&lt;/code&gt;&lt;br /&gt;&lt;code&gt;end&lt;/code&gt;&lt;br /&gt;&lt;code&gt;data = []&lt;/code&gt;&lt;br /&gt;&lt;code&gt;File.new('test_data.csv','r').each{|line| data.push line.split('","',-1)[9] if line.split('","',-1)[9]}&lt;/code&gt;&lt;br /&gt;&lt;code&gt;puts 'exact match:'&lt;/code&gt;&lt;br /&gt;&lt;code&gt;Benchmark.bm do |x|&lt;/code&gt;&lt;br /&gt;&lt;code&gt; x.report("hash:   "){&lt;/code&gt;&lt;br /&gt;&lt;code&gt;  data.each{|bus_name| STD_NAME[bus_name]}&lt;/code&gt;&lt;br /&gt;&lt;code&gt; }&lt;/code&gt;&lt;br /&gt;&lt;code&gt; x.report("sqlite3:"){&lt;/code&gt;&lt;br /&gt;&lt;code&gt;  data.each{|bus_name| std_names bus_name}&lt;/code&gt;&lt;br /&gt;&lt;code&gt; }&lt;/code&gt;&lt;br /&gt;&lt;code&gt;end&lt;/code&gt;&lt;br /&gt;&lt;code&gt;puts 'match using like:'&lt;/code&gt;&lt;br /&gt;&lt;code&gt;Benchmark.bm do |x|&lt;/code&gt;&lt;br /&gt;&lt;code&gt; x.report("hash:   "){&lt;/code&gt;&lt;br /&gt;&lt;code&gt;  data.each{|bus_name| std_names_fake_like(bus_name)}&lt;/code&gt;&lt;br /&gt;&lt;code&gt; }&lt;/code&gt;&lt;br /&gt;&lt;code&gt; x.report("sqlite3:"){&lt;/code&gt;&lt;br /&gt;&lt;code&gt;  data.each{|bus_name| std_names_with_like(bus_name)}&lt;/code&gt;&lt;br /&gt;&lt;code&gt; }&lt;/code&gt;&lt;br /&gt;&lt;code&gt;end&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;El primer benchmark mide el tiempo de carga en los dos casos, el recorrido secuencial para la implementación mediante hash y array, y la carga de datos a sqlite y la creación del indice sobre la tabla (con el comando: sqlite3 -separator , test.db ".import STD_NAMES_MASTER.csv std_names").&lt;br /&gt;El segundo mide el tiempo de las funciones de búsqueda "exacta" en hash y con sqlite. Para esta se utiliza datos de prueba en el archivo test_data.csv que contiene 10000 registros.&lt;br /&gt;El tercer benchmark mide el tiempo de ejecución de las funciones de busqueda con expresiones regulares para el caso del archivo y array, y el uso de la sentencia "like" en sql, para una búsqueda aproximada con un segmento del la llave. En este ulitmo caso se utilizaron 100 datos de prueba.&lt;br /&gt;Esto corrió en una Intel(R) Core(TM)2 Duo CPU T7100 @ 1.80GHz, con 2066088 kB de memoria.&lt;br /&gt;&lt;br /&gt;Estos fueron los resultados de correr el script &lt;span style="font-style: italic;"&gt;test_hash_vs_sqlite3.rb&lt;/span&gt;:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;load time:&lt;/code&gt;&lt;br /&gt;    &lt;code&gt;&lt;/code&gt;&lt;code&gt;user         system   total            real&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;hash:     3.500000   0.130000   3.630000 (  3.677747)&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;sqlite3:  0.360000   0.060000   5.340000 ( 21.539347)&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;exact match (test data 10000 rows):&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;        &lt;code&gt;&lt;/code&gt;&lt;code&gt;user         system   total            real&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;hash:     0.020000   0.000000   0.020000 (  0.022092)&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;sqlite3:  7.260000   0.430000   7.690000 (  7.856472)&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;match using like (test data 100 rows):&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;        &lt;code&gt;&lt;/code&gt;&lt;code&gt;user         system   total            real&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;hash:   599.450000  10.640000 610.090000 (627.246236)&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;sqlite3: 24.260000   2.530000  26.790000 ( 27.629173)&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Cabe mencionar que mi CPU se veía así (95% cpu)cuando computaba la función version array-expresion regular :D.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4N9yERzcJnA/SXEwCtciHXI/AAAAAAAAAC4/0Qf5Y4vz8ac/s1600-h/Screenshot-1.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 166px;" src="http://4.bp.blogspot.com/_4N9yERzcJnA/SXEwCtciHXI/AAAAAAAAAC4/0Qf5Y4vz8ac/s400/Screenshot-1.png" alt="" id="BLOGGER_PHOTO_ID_5292063860254186866" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Con los resultados podemos concluir:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Funciones de búsqueda exacta:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;utilice hash definitivamente&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Función de búsqueda aproximada:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Utilice la solución con motor de base de datos&lt;/li&gt;&lt;li&gt;Si tiene un motor de bases de datos instalado utilicelo, sino utilice algún motor portarle, gratuito y funcionará con tiempos respuesta bastante aceptables.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Si necesita las dos:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;combine las dos opciones para búsqueda exacta utilice hash, y para aproximadas la versión con base de datos, en serio vale la pena combinar la solución ya que los hash son invencibles en match exactos.&lt;/li&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-weight: bold;"&gt;Otras:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Las funciones de match exacto son  realmente eficientes, los datos de prueba fueron 10000 y corrieron  en 3.7 segundos es decir 2702 matcheos por segundo, para la versión  de sqlite3 465.1 registros por segundo.     Las funciones de match aproximado son mucho más lentas de  los de prueba fueron mucho menos y duró considerablemente más que  los otros tipos de match.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Para el caso de la versión  array-expresion regular tomó 627.2 segundos para computar 100  entradas, lo que corresponde a 0.159 por segundo, mientras que la  versión sqlite con like 3.6 registros por segundo. &lt;/li&gt;&lt;/ul&gt;Los ejemplos estan accesible por &lt;a href="http://github.com/pabloq/hash-vs-sqlite-examples/tree/master"&gt;github.com&lt;/a&gt; mediante el siguiente comando:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;git clone git://github.com/pabloq/hash-vs-sqlite-examples.git &lt;/span&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6925781032970580633-6331644715337103129?l=pabloqs.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pabloqs.blogspot.com/feeds/6331644715337103129/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://pabloqs.blogspot.com/2009/01/funcin-de-busqueda-usando-hash-o.html#comment-form' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6925781032970580633/posts/default/6331644715337103129'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6925781032970580633/posts/default/6331644715337103129'/><link rel='alternate' type='text/html' href='http://pabloqs.blogspot.com/2009/01/funcin-de-busqueda-usando-hash-o.html' title='Función de busqueda: usando hash o sqlite3?'/><author><name>Pablo</name><uri>http://www.blogger.com/profile/09019438687952508022</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_4N9yERzcJnA/SXEwCtciHXI/AAAAAAAAAC4/0Qf5Y4vz8ac/s72-c/Screenshot-1.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6925781032970580633.post-7727610737403228224</id><published>2009-01-01T11:03:00.012-06:00</published><updated>2009-01-03T09:49:43.682-06:00</updated><title type='text'>Instalando ruby "politics" en linux (Ubuntu 8.04)</title><content type='html'>&lt;div style="text-align: justify;"&gt;Revisando las presentaciones de &lt;a href="http://rubyconf2008.confreaks.com/"&gt;rubyconf2008&lt;/a&gt;, seleccionado las que más me llamaron la atención (aún sigo revisándolas, son demasiadas!) vi la de &lt;a href="http://www.mikeperham.com/"&gt;Mike Perham&lt;/a&gt; sobre "patterns in distributed processing" en la que presenta una implementación de procesamiento distribuido montado sobre &lt;a href="http://www.danga.com/memcached/"&gt;memcached&lt;/a&gt; ("high-performance, distributed memory object caching system").&lt;br /&gt;Esto puedo ser bastante util cuando se tiene un problema computacional que puede ser segmentado para su procesamiento paralelo.&lt;br /&gt;Tiene las siguientes dependencias:&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;memcached&lt;/span&gt; - the mechanism to elect a leader amongst a set of peers.  &lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;DRb&lt;/span&gt; - the mechanism to communicate between peers.  &lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;mDNS&lt;/span&gt; - the mechanism to discover peers.  &lt;/li&gt;&lt;/ul&gt;Instalando memcached:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;~# sudo apt-get install memcached&lt;br /&gt;Reading package lists... Done&lt;br /&gt;Building dependency tree&lt;br /&gt;Reading state information... Done&lt;br /&gt;Suggested packages:&lt;br /&gt;libcache-memcached-perl&lt;br /&gt;The following NEW packages will be installed:&lt;br /&gt;memcached&lt;br /&gt;0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.&lt;br /&gt;Need to get 44.6kB of archives.&lt;br /&gt;After this operation, 176kB of additional disk space will be used.&lt;br /&gt;Get:1 http://us.archive.ubuntu.com hardy/universe memcached 1.2.2-1 [44.6kB]&lt;br /&gt;Fetched 44.6kB in 2min1s (367B/s)&lt;br /&gt;Selecting previously deselected package memcached.&lt;br /&gt;(Reading database ... 169259 files and directories currently installed.)&lt;br /&gt;Unpacking memcached (from .../memcached_1.2.2-1_i386.deb) ...&lt;br /&gt;Setting up memcached (1.2.2-1) ...&lt;br /&gt;Starting memcached: memcached.&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;instalando mdns:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;~# sudo gem install net-mdns&lt;br /&gt;Successfully installed net-mdns-0.4&lt;br /&gt;1 gem installed&lt;br /&gt;Updating class cache with 1251 classes...&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;instalando el cliente de memcached (para ruby):&lt;br /&gt;&lt;code&gt;&lt;br /&gt;~# sudo gem install memcache-client&lt;br /&gt;Successfully installed rubyforge-1.0.1&lt;br /&gt;Successfully installed rake-0.8.3&lt;br /&gt;Successfully installed hoe-1.8.2&lt;br /&gt;Successfully installed ZenTest-3.11.0&lt;br /&gt;Successfully installed memcache-client-1.5.0&lt;br /&gt;5 gems installed&lt;br /&gt;Installing ri documentation for rubyforge-1.0.1...&lt;br /&gt;Installing ri documentation for rake-0.8.3...&lt;br /&gt;Installing ri documentation for hoe-1.8.2...&lt;br /&gt;Installing ri documentation for ZenTest-3.11.0...&lt;br /&gt;Installing ri documentation for memcache-client-1.5.0...&lt;br /&gt;Installing RDoc documentation for rubyforge-1.0.1...&lt;br /&gt;Installing RDoc documentation for rake-0.8.3...&lt;br /&gt;Installing RDoc documentation for hoe-1.8.2...&lt;br /&gt;Installing RDoc documentation for ZenTest-3.11.0...&lt;br /&gt;Installing RDoc documentation for memcache-client-1.5.0...&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Ahora lo siguiente es arrancar el server memcached:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;~# /etc/init.d/memcached start&lt;br /&gt;Starting memcached: memcached.&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Ya tenemos todo lo que necesitamos instalado. Ahora nos disponemos a instalar politics y corremos un pequeño ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;NOTA&lt;/span&gt;: para bajar la librería se necesita instalado el git (sudo apt-get install git &amp;amp; sudo apt-get install git-core)&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;~# git clone git://github.com/mperham/politics.git&lt;br /&gt;Initialized empty Git repository in /home/castor/projects/politics/.git/&lt;br /&gt;remote: Counting objects: 165, done.&lt;br /&gt;remote: Compressing objects: 100% (158/158), done.&lt;br /&gt;remote: Total 165 (delta 76), reused 0 (delta 0)&lt;br /&gt;Receiving objects: 100% (165/165), 32.50 KiB, done.&lt;br /&gt;Resolving deltas: 100% (76/76), done.&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Listo. Ahora tenemos una carpeta llamada politics, nada mas no situamos en ella, y vemos un poco la carpeta examples.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;~# cd politics/&lt;br /&gt;~# ls&lt;br /&gt;examples  History.rdoc  lib  LICENSE  Manifest  politics.gemspec  Rakefile  README.rdoc  test&lt;br /&gt;~# cd examples/&lt;br /&gt;~# ls&lt;br /&gt;queue_worker_example.rb  token_worker_example.rb&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Ok, ahora solo tratemos de correr uno de los ejemplos, en este caso el queue_worker_example.rb.&lt;br /&gt;&lt;br /&gt;Veamos como se ve por dentro:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;~#cat queue_worker_example.rb&lt;br /&gt;#gem 'mperham-politics'&lt;br /&gt;require 'politics'&lt;br /&gt;require 'politics/static_queue_worker'&lt;br /&gt;&lt;br /&gt;# Test this example by starting memcached locally and then in two irb sessions, run this:&lt;br /&gt;#&lt;br /&gt;=begin&lt;br /&gt;require 'queue_worker_example'&lt;br /&gt;p = Politics::QueueWorkerExample.new&lt;br /&gt;p.start&lt;br /&gt;=end&lt;br /&gt;#&lt;br /&gt;# You can then watch as one of them is elected leader.  You can kill the leader and verify&lt;br /&gt;# the backup process is elected after approximately iteration_length seconds.&lt;br /&gt;#&lt;br /&gt;module Politics&lt;br /&gt;class QueueWorkerExample&lt;br /&gt;include Politics::StaticQueueWorker&lt;br /&gt;TOTAL_BUCKETS = 20&lt;br /&gt;&lt;br /&gt;def initialize&lt;br /&gt;  register_worker 'queue-example', TOTAL_BUCKETS, :iteration_length =&gt; 60, :servers =&gt; memcached_servers&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def start&lt;br /&gt;  process_bucket do |bucket|&lt;br /&gt;    puts "PID #{$$} processing bucket #{bucket}/#{TOTAL_BUCKETS} at #{Time.now}..."&lt;br /&gt;    sleep 1.5&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def memcached_servers&lt;br /&gt;  ['127.0.0.1:11211']&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;Pura vida, entonces nada mas de crear un archivo test para correr varios QueueWorker y listo (como dice al inicio el archivo). Los server memcached se especifican en el método "memcached_servers" en nuestro caso lo tenemos corriendo localmente, si existen otros memchaded servers nada más de agregarlo ahí.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;El archivo de ejemplo quedaría mas o menos así:&lt;br /&gt;&lt;br /&gt;#test.rb&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;require 'rubygems'&lt;/span&gt;&lt;br /&gt;require 'queue_worker_example'&lt;br /&gt;p = Politics::QueueWorkerExample.new&lt;br /&gt;p.start&lt;br /&gt;&lt;br /&gt;Muy importante incluir 'rubygems' para que pueda acceder a los gems que instalamos antes.&lt;br /&gt;&lt;br /&gt;Y corramos el test en varias consolas (en este caso 3) y vemos a ver que hacen.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;~#ruby test.rb&lt;br /&gt;/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- politics (LoadError)&lt;br /&gt;from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'&lt;br /&gt;from ./queue_worker_example.rb:2&lt;br /&gt;from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require'&lt;br /&gt;from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'&lt;br /&gt;from test.rb:2&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Nos da un error, ya que no puede ver modulo 'politics'. Nada más modificamos las líneas dos y tres por la siguientes en el archivo &lt;span style="font-style: italic;"&gt;queue_worker_example.rb&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;require 'politics'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;require 'politics/static_queue_worker'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;por&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;require '&lt;span style="font-weight: bold;"&gt;../lib/&lt;/span&gt;politics'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;require '&lt;span style="font-weight: bold;"&gt;../lib/&lt;/span&gt;politics/static_queue_worker'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Listo. Ahora si, el script correrá. Para probarlo haré el siguiente pequeño experimento:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;1. Arranco los tres procesos, uno de ellos será elegido como líder y será el encargado de asignar el trabajo.&lt;br /&gt;2. Matamos este líder y vemos que tendrá que asignar otro líder.&lt;br /&gt;3. El nuevo lider debería de asignar todo el trabajo al tercer proceso (único worker que queda), este último worker ejecuta todo el trabajo.&lt;br /&gt;4. Esperamos que el último proceso worker termine y damos por concluido el experimento.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Paso 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-style: italic;"&gt;consola 1&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;~# ruby test.rb&lt;br /&gt;I, [2009-01-02T18:03:20.062033 #1925]  INFO -- : Registered #&lt;politics::queueworkerexample:0xb79c3030&gt; in group queue-example at port 58354&lt;br /&gt;I, [2009-01-02T18:03:20.065879 #1925]  INFO -- : druby://pc:58354 has been elected leader&lt;br /&gt;&lt;/politics::queueworkerexample:0xb79c3030&gt;&lt;/code&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-style: italic;"&gt;consola 2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;~# ruby test.rb&lt;br /&gt;I, [2009-01-02T18:03:22.317582 #1930]  INFO -- : Registered #&lt;politics::queueworkerexample:0xb7979ffc&gt; in group queue-example at port 48267&lt;br /&gt;I, [2009-01-02T18:03:24.347506 #1930]  INFO -- : druby://pc:48267 is processing 19&lt;br /&gt;PID 1930 processing bucket 19/20 at Fri Jan 02 18:03:24 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:03:25.852976 #1930]  INFO -- : druby://pc:48267 is processing 17&lt;br /&gt;PID 1930 processing bucket 17/20 at Fri Jan 02 18:03:25 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:03:27.357718 #1930]  INFO -- : druby://pc:48267 is processing 15&lt;br /&gt;PID 1930 processing bucket 15/20 at Fri Jan 02 18:03:27 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:03:28.864040 #1930]  INFO -- : druby://pc:48267 is processing 13&lt;br /&gt;PID 1930 processing bucket 13/20 at Fri Jan 02 18:03:28 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:03:30.367844 #1930]  INFO -- : druby://pc:48267 is processing 11&lt;br /&gt;PID 1930 processing bucket 11/20 at Fri Jan 02 18:03:30 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:03:31.872520 #1930]  INFO -- : druby://pc:48267 is processing 9&lt;br /&gt;PID 1930 processing bucket 9/20 at Fri Jan 02 18:03:31 -0600 2009...&lt;br /&gt;&lt;/politics::queueworkerexample:0xb7979ffc&gt;&lt;/code&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-style: italic;"&gt;consola 3&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;# ruby test.rb&lt;br /&gt;I, [2009-01-02T18:03:23.748698 #1934]  INFO -- : Registered #&lt;politics::queueworkerexample:0xb79dc030&gt; in group queue-example at port 47170&lt;br /&gt;I, [2009-01-02T18:03:24.777129 #1934]  INFO -- : druby://pc:47170 is processing 18&lt;br /&gt;PID 1934 processing bucket 18/20 at Fri Jan 02 18:03:24 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:03:26.280513 #1934]  INFO -- : druby://pc:47170 is processing 16&lt;br /&gt;PID 1934 processing bucket 16/20 at Fri Jan 02 18:03:26 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:03:27.793545 #1934]  INFO -- : druby://pc:47170 is processing 14&lt;br /&gt;PID 1934 processing bucket 14/20 at Fri Jan 02 18:03:27 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:03:29.295903 #1934]  INFO -- : druby://pc:47170 is processing 12&lt;br /&gt;PID 1934 processing bucket 12/20 at Fri Jan 02 18:03:29 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:03:30.800739 #1934]  INFO -- : druby://pc:47170 is processing 10&lt;br /&gt;PID 1934 processing bucket 10/20 at Fri Jan 02 18:03:30 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:03:32.305096 #1934]  INFO -- : druby://pc:47170 is processing 8&lt;br /&gt;PID 1934 processing bucket 8/20 at Fri Jan 02 18:03:32 -0600 2009...&lt;br /&gt;&lt;/politics::queueworkerexample:0xb79dc030&gt;&lt;/code&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Paso 2 y 3&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ahora matamos con 'Control + C' el primero (consola 1)  y vemos que pasa en las otras consolas.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-style: italic;"&gt;consola 1&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;../lib/politics/static_queue_worker.rb:195:in `sleep': Interrupt&lt;br /&gt;from ../lib/politics/static_queue_worker.rb:195:in `relax'&lt;br /&gt;from ../lib/politics/static_queue_worker.rb:97:in `process_bucket'&lt;br /&gt;from ./queue_worker_example.rb:26:in `start'&lt;br /&gt;from test.rb:4&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-style: italic;"&gt;consola 2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;E, [2009-01-02T18:03:33.376720 #1930] ERROR -- : Error talking to leader: druby://pc:58354 - #&lt;errno::econnrefused: connection="" refused=""&gt;&lt;br /&gt;I, [2009-01-02T18:04:33.380112 #1930]  INFO -- : druby://pc:48267 has been elected leader&lt;br /&gt;&lt;/errno::econnrefused:&gt;&lt;/code&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-style: italic;"&gt;consola 3&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;E, [2009-01-02T18:03:33.826448 #1934] ERROR -- : Error talking to leader: druby://pc:58354 - #&lt;errno::econnrefused: connection="" refused=""&gt;&lt;br /&gt;I&lt;br /&gt;&lt;/errno::econnrefused:&gt;&lt;/code&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Paso 4&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ya el líder está en la consola 2, el tercero nada más ejecutaría el trabajo restante.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-style: italic;"&gt;consola 3&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;I, [2009-01-02T18:04:33.829416 #1934]  INFO -- : druby://pc:47170 is processing 19&lt;br /&gt;PID 1934 processing bucket 19/20 at Fri Jan 02 18:04:33 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:35.334133 #1934]  INFO -- : druby://pc:47170 is processing 18&lt;br /&gt;PID 1934 processing bucket 18/20 at Fri Jan 02 18:04:35 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:36.836062 #1934]  INFO -- : druby://pc:47170 is processing 17&lt;br /&gt;PID 1934 processing bucket 17/20 at Fri Jan 02 18:04:36 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:38.340842 #1934]  INFO -- : druby://pc:47170 is processing 16&lt;br /&gt;PID 1934 processing bucket 16/20 at Fri Jan 02 18:04:38 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:39.844057 #1934]  INFO -- : druby://pc:47170 is processing 15&lt;br /&gt;PID 1934 processing bucket 15/20 at Fri Jan 02 18:04:39 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:41.349662 #1934]  INFO -- : druby://pc:47170 is processing 14&lt;br /&gt;PID 1934 processing bucket 14/20 at Fri Jan 02 18:04:41 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:42.853141 #1934]  INFO -- : druby://pc:47170 is processing 13&lt;br /&gt;PID 1934 processing bucket 13/20 at Fri Jan 02 18:04:42 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:44.356687 #1934]  INFO -- : druby://pc:47170 is processing 12&lt;br /&gt;PID 1934 processing bucket 12/20 at Fri Jan 02 18:04:44 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:45.860273 #1934]  INFO -- : druby://pc:47170 is processing 11&lt;br /&gt;PID 1934 processing bucket 11/20 at Fri Jan 02 18:04:45 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:47.363697 #1934]  INFO -- : druby://pc:47170 is processing 10&lt;br /&gt;PID 1934 processing bucket 10/20 at Fri Jan 02 18:04:47 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:48.869261 #1934]  INFO -- : druby://pc:47170 is processing 9&lt;br /&gt;PID 1934 processing bucket 9/20 at Fri Jan 02 18:04:48 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:50.372352 #1934]  INFO -- : druby://pc:47170 is processing 8&lt;br /&gt;PID 1934 processing bucket 8/20 at Fri Jan 02 18:04:50 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:51.873700 #1934]  INFO -- : druby://pc:47170 is processing 7&lt;br /&gt;PID 1934 processing bucket 7/20 at Fri Jan 02 18:04:51 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:53.376756 #1934]  INFO -- : druby://pc:47170 is processing 6&lt;br /&gt;PID 1934 processing bucket 6/20 at Fri Jan 02 18:04:53 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:54.880311 #1934]  INFO -- : druby://pc:47170 is processing 5&lt;br /&gt;PID 1934 processing bucket 5/20 at Fri Jan 02 18:04:54 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:56.384764 #1934]  INFO -- : druby://pc:47170 is processing 4&lt;br /&gt;PID 1934 processing bucket 4/20 at Fri Jan 02 18:04:56 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:57.888371 #1934]  INFO -- : druby://pc:47170 is processing 3&lt;br /&gt;PID 1934 processing bucket 3/20 at Fri Jan 02 18:04:57 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:04:59.392192 #1934]  INFO -- : druby://pc:47170 is processing 2&lt;br /&gt;PID 1934 processing bucket 2/20 at Fri Jan 02 18:04:59 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:05:00.897304 #1934]  INFO -- : druby://pc:47170 is processing 1&lt;br /&gt;PID 1934 processing bucket 1/20 at Fri Jan 02 18:05:00 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:05:02.399987 #1934]  INFO -- : druby://pc:47170 is processing 0&lt;br /&gt;PID 1934 processing bucket 0/20 at Fri Jan 02 18:05:02 -0600 2009...&lt;br /&gt;I, [2009-01-02T18:05:03.905741 #1934]  INFO -- : No more buckets in this iteration, sleeping for 29.474557 sec&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Listo ya instalamos los pre-requisitos, instalamos 'politics' y probamos con un ejemplo.&lt;br /&gt;&lt;br /&gt;En una próxima entrada, utilizaré 'politics' para distribuir trabajo en varios servers memcached.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6925781032970580633-7727610737403228224?l=pabloqs.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pabloqs.blogspot.com/feeds/7727610737403228224/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://pabloqs.blogspot.com/2009/01/instalando-ruby-politics-en-linux.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6925781032970580633/posts/default/7727610737403228224'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6925781032970580633/posts/default/7727610737403228224'/><link rel='alternate' type='text/html' href='http://pabloqs.blogspot.com/2009/01/instalando-ruby-politics-en-linux.html' title='Instalando ruby &quot;politics&quot; en linux (Ubuntu 8.04)'/><author><name>Pablo</name><uri>http://www.blogger.com/profile/09019438687952508022</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
