La Coctelera

por Guillermo Álvarez

image

El curioso bucle begin end while. Balbuceando en alto.

Creo que todos conocemos una sintaxis parecida:

  def function
    do something
    while condition 
      do something
    end
  end
En la mayoría de lenguajes tenemos que ejecutar el primer ciclo del loop fuera del loop.
Pero en Ruby no.
def temp_dir
  i = 0
  dir = File.join(Dir.tmpdir,'ginst', (i += 1).to_s)
  while(File.exists?(dir)) do
    dir = File.join(Dir.tmpdir,'ginst', (i += 1).to_s)
  end
  FileUtils.mkdir_p(dir)
end
Casi sin querer, descubrí la siguiente sintaxis
Esto hace que se ejecute el código la primera vez y luego...  si a la condición le apetece. Pero la primera, no te la quita nadie.
Esto me vino muy bien para hacer un generador de directorios temporales secuenciales:
def temp_dir(i=0)
  begin
    dir = File.join(Dir.tmpdir,'ginst', (i += 1).to_s)
  end while(File.exists?(dir))
  FileUtils.mkdir_p(dir)
end
Pero meditando un poco más, recordé una obviedad que tenía olvidada, y que la primera vez que la vi me sorprendió tanto como ahora, y es el poner el while a lo que quieras. Por ejemplo, si estás deprimido, qué mejor que estas cuatro palabras
puts 'Anímate' while true
Y si lo estás solo un poquito.
begin puts 'Venga tonto... ¡ Anímate ! ' end while false
Lo que me llevó a intentar hacer la función de los directorios en una linea
Primer intento:
def temp_dir(i=0)
  begin  dir = File.join(Dir.tmpdir,'ginst', (i += 1).to_s) end while (File.exists?(dir))
  FileUtils.mkdir_p(dir)
end
El problema que tenemos con esta sintaxis es que lo último en ejecutarse del bloque es la condición, por lo que no podremos obtener el valor devuelto. Ahora bien, si podemos pasar un begin..end...  ¿Quién nos prohibe pasar la sintaxis de toda la vida begin..begin..end..while..end .
def temp_dir(i=0)
  FileUtils.mkdir_p(begin begin dir = File.join(Dir.tmpdir,'ginst', (i += 1).to_s) end while File.exists?(dir) ; dir end)
end
De aquí, llama la atención el poder estar usando bloques dentro de argumentos con la misma facilidad que a mi se me convence para ir de fiesta.
Vale, hice una trampa. Inicializar la variable como parámetro. Pero tiene solución:
def temp_dir
  FileUtils.mkdir_p(begin begin dir = File.join(Dir.tmpdir,'ginst', (i ||=0 ; i+= 1).to_s) end while File.exists?(dir) ; dir end)
end
Pese a no ser práctico sobre ninguno de los casos expuestos, ya que ofuscar nuestro código, aunque mola, no es útil, me ha vuelto a llamar la atención, la versatilidad que puede haber en ruby.
Y como inquieto que soy... no puedo parar de preguntarme ¿y si...? Y sí funciona en ruby1.9, pero con la salvedad de que FileUtils.mkdir_p devuelve un array (creo que también los conocen como arreglos de algo) en vez de una cadena o string.
Y a tí... ¿ Hay alguna sintaxis de ruby que te llame especialmente la atención ?

21, abr | 0 comentarios Posteado por: cientifico En: compártelo Tags:

Crear y eliminar tags remotos con git

Manejar tags localmente es fácil:

> git tag -l # lista los tags locales
> git tag tag_name # Crea un tag local
> git tag -d tag_name # Borra el tag local
>
> git tag -d `git tag -l ` # Borra todos los tags locales

Pero manejar los remotos... no es tan intuitivo, así que me dejo esta
pequeña nota:

> git fetch --tags #Aunque ya lo hace por defecto
> git ls-remote --tags origin # Lista los tags remotos

Y para borrarlo, tendremos que borrar primero el local y luego hacer:

> git push origin :refs/tags/tag_name

---Adjuntos---

smime-3.p7s

19, abr | 0 comentarios Posteado por: cientifico En: compártelo Tags:

Variables especiales de ruby

A modo de chuletario personal, dejo esta imagen con las variables especiales de ruby.
Siempre conocida y sabida, hasta que requieres algunos de sus valores, es entonces cuando echas de menos este chuletario.

4, feb | 2 comentarios Posteado por: cientifico En: compártelo Tags: ruby, chuleta, variables, entorno, especiales, raras, jodidas de recordar, simbolos, raros, procees id, exit status, exceptions, line, number, regexp, match,

Increible video.

31, ene | 1 comentarios Posteado por: cientifico En: compártelo Tags: video,

Cucumber: Testeando ajax sin selenium (II)

Continuando con el artículo Testeando ajax sin selenium han mejorado algunas cosillas.

La versión anterio, tenía un problema y es que solo servía para visitar. ¿Pero que pasa con el resto de steps (como I press), que implican peticiones y que esperan ajax como respuesta? Pues aquí está la solución.

When /^(.*) as an ajax request/ do |task|
   When task
   response.body = get_ajax_html(response.body)
 end
 

Esto dota a las features de poder realizar cualquier acción que antes hacías y evaluar su respuesta. Por lo tanto, lo que antes era:

    And I press "Guardar"
 

Ahora es

    And I press "Guardar" as an ajax request
 

Como extensión, he visto que otras formas de page.update, usan la función javascript html en vez de replace with, por lo que la función que obtiene el cuerpo ha sido actualizada.

def get_replace_with_body(body)
   body=~/replaceWith\(\"(.*?)[^\\]\"\)/m
   body=~/\)\.html\(\"(.*?)[^\\]\"\)/m unless $1
   raise Exception.new('The ajax response doesn\'t containg a js replaceWith method') unless $1
   "#{$1}"
 end
 

Y ahora ya puedo testear casi todo el js sin usar selinium, configurar una máquina virtual, un xen, linux, firefox, selenium_server, etc...

23, ene | 0 comentarios Posteado por: cientifico En: compártelo Tags: page update, muerte selenium, me voy, servidor, feature, ruby, page, update, js, ajax, webrat, rails, cucumber, infierno, satanas,

Cucumber testeando ajax sin selenium

Como quería terminar una cosa, y lo que necesita más que test de vista o test de controlador era un test de integración, no me quedó otra que dotar a cucumber+webrat-selenium (nuestro entorno de feature testín) de capacidad de ajax.

Bueno, seamos serios, son un par de lineas que me permitieron hacer ejecutar la feature, y paso a comentar, ya que de tratarse de aplicaciones rails, este método es usable para la mayoría de page.update que realizamos.

page.update actualiza un elemento del dom con nuevo contenido, para eso lanza un javascript que es interpretado por el navegador. Suponiendo que usamos jquery (tal vez cambie la implementación para el antiguo y obsoleto prototype), este hará un replaceWith. Este código parsea el código devuelto por la aplicación y extrae la actualización de la página.

Pasemos ahora a ver el código:

# Desescapamos javascript, ya que escapado no nos sirve de mucho.
 # Así tendremos un html válido. Esto se resume en:
 #  < div id="\'hola\'">\tadios\t< /div>  ===>    adios  
 def unescape_javascript(javascript)
  javascript.gsub!('\n' , "\n")
  javascript.gsub!('\t' , "\t")
  javascript.gsub!("\\'", "'")
  javascript.gsub!('\"' , '"')
 end
 
 # Expresión regular encargada de sacar lo que realmente nos intersa.
 #  ...replaceWith(' ESTO NOS INTERESA ')...
 def get_replace_with_body(body)
  body=~/replaceWith\(\"(.*?)[^\\]\"\)/m
  raise Exception.new('The ajax response doesn\'t containg a js replaceWith method') unless $1
  "#{$1}"
 end
 
 # Combinado de las dos anteriores
 def get_ajax_html(text)
  unescape_javascript(get_replace_with_body(text))
 end
 

Y el step correspondiente.

When /^I visits by ajax (.*) $/ do |path|
  visits(path)
  response.body = get_ajax_html(response.body)
 end
 

Y la feature

 Scenario: Changing my name
    Given I am a logged in as a Candidate
    When I visits /profile/edit.js ajax
    And I fill in "candidate_profile_first_name" with "Guillermooo"
    And I fill in "candidate_profile_surnames" with "Alvarez"
    And I press "Guardar"
    Then I should see "Guillermooo Alvarez"
 

La solución es fea, torpe, poco optima. Pero creo que puede resolver la mayoría de peticiones ajax del servidor sin depender de un motor javascript externo.
Si alguien conoce alguna otra forma sin incorporar más dependencias al testing... que me lo haga saber.

23, ene | 1 comentarios Posteado por: cientifico En: compártelo Tags: muerte selenium, page update, feature, servidor, ruby, page, update, js, ajax, webrat, rails, cucumber,

Truquitos servidor

Basándonos en la teoría de que las máquinas están bien configuradas en lo que respecta el correo, yo voy a tener cientifico.net para correos personales y yolanda.cientifico.net para los correos locales de la máquina.

Esto permite que yo desde cualquier cuenta de correo envie un mensaje a guillermo @ yolanda cientifico PUNTO net.

Para que sirve esto.... pues para ser más comodón aún. Soy fiel usuario de scp (consola -> servidor) y webdav (escritorio<->servidor), pero poder enviar archivos a mi cuenta del servidor y al llegar allí, arrancar alpine o cualquier otro cliente de correo de consola, y poder guardarlo, es una gozada.

Ahí queda dicho.

5, ene | 0 comentarios Posteado por: cientifico En: compártelo Tags: alpine, scp, servidor, escritorio, correo,

DRB

DRB o Distributed Ruby es una abstracción de sockets tcp y serialización de objetos que trae integrada ruby por defecto.

Tenía ganas de jugar con drb y su abstracción superior sobre el patron tuple spaces que es una forma de implementar el paradigma de memoria asociativa (associative memory) desda linda, que es una forma de coordinar y comunicar diferentes procesos entre máquinas. Ya jugaremos con Rinda, pero como bien dice mi amigo Jack... Vayamos por partes.

En este caso vamos a poner un caso real en rails para procesar emails. No es que sea necesario utilizar DRb, es que es la única forma rentable que he encontrado. Recibimos e-mails con una frecuencia indeterminada, y los procesamos desde una tarea rake. Esto está bien salvo por que levantar una tarea rake que como dependencia tiene :environment, puede suponer bastante tiempo. Si supone 10 segundos y cada instancia supone 40 o 50 megas. 10 correos por segundo... tumbarían la máquina.

Pues lejos de ir más lejos se implementa una arquitectura cliente-servidor en el que el servidor es la tarea rake y el cliente es lo que se conecta con el MTA(Mail Transfer Agent) del sistema. Al grano:

El cliente, solo necesita esto

#!/usr/bin/env ruby 
 require 'drb' 
 URI="druby://localhost:9999"  
 server = DRbObject.new_with_uri URI 
 return server.process($stdin.read.to_s) 
 

Mientras que el servidor se arreglaría con esto

 
   desc 'Start Mail Daemon' 
   task :daemon => :environment do 
     require 'drb' 
     URI="druby://localhost:9999" 
     class Processor 
       def process(mail) 
         ModeloActiveRecordParaMails.create(TMail.new(mail)) 
       end 
     end  
     DRb.start_service URI, Processor.new 
     DRb.thread.join 
   end
 

Con esto estaría ya todo el trabajo hecho.

Creo que el código es bastante auto-explicativo. Si hay alguna duda... A los comentarios.

4, ene | 0 comentarios Posteado por: cientifico En: compártelo Tags: client server, start service, thread join, drb, ruby, rails, daemon, cliente, servidor, druby,