martes, 15 de marzo de 2016

Importar un XML en ruby

Para ello usaremos la gema "nokogiri".

XML

Vamos a jugar con el ejemplo de w3schools que se encuentra en http://www.w3schools.com/xml/xml_attributes.asp
<messages>
  <note id="501">
    <to>Tove</to>
    <from>Jani</from>
    <heading>Reminder</heading>
    <body>Don't forget me this weekend!</body>
  </note>
  <note id="502">
    <to>Jani</to>
    <from>Tove</from>
    <heading>Re: Reminder</heading>
    <body>I will not</body>
  </note>
</messages>
Lo vamos a guardar en un fichero llamado "example.xml".

Importación

La fase de importación y parsing la hace la propia librearía. Lo que no está tan bien explicado en la documentación es cómo ir consiguiendo los registros sucesivamente y el valor de los campos de los mismos.

Vamos a probar un script como el siguiente

require 'nokogiri'

fichero='./example.xml'

xmlfeed = Nokogiri::XML(open(fichero))
all_items = xmlfeed.xpath("//messages/note")

fields = [ 'to','from','heading','body' ]
all_items.each do |item|
    fields.each do |f|
        entrada = item.at(f)
        valor = entrada.text
        puts "#{f} = #{valor}"
    end
end
#Fin del script
Vamos por partes.

La importación se hace con el bloque:

xmlfeed = Nokogiri::XML(open(fichero))
all_items = xmlfeed.xpath("//messages/note")

Para el fichero podríamos tirar de URL también.
Hasta ahora nosotros no hemos dicho nada de la estructura del XML... ¿ O si ? Si. La librería ha importado todo el XML, pero le hemos dicho que los regustros son eso que está dentro de la estructura de documento en el segundo nivel, esto es "//messages/note". Esto es una de las cosas que debemos hacer bien porque si no el desglose en registros no se hace correctamente.

Los campos que vamos a estudiar deben ser conocidos. Responden a una estructura predeterminada.
fields = [ 'to','from','heading','body' ]
Por supuesto que podríamos obtenerlos pero no complicaremos más el ejemplo.

Finalmente para ilustrar el ejemplo, imprimimos los campos uno a uno por consola.

miércoles, 9 de marzo de 2016

Ruby sin rails: parser de opciones en el script

El siguiente es un script de ejemplo de cómo se usa un parser de opciones en nuestros scripts de ruby. Un poco más abajo explicamos las cuestiones más relevantes. Usamos la gema optparse. Si tienes dudas sobre cómo instalar la gema para usarla en un script, puede serte útil el post de uso de gemas sin Rails.

require 'optparse'                                                                                             

options = {}
options[:fichero] = ""
options[:lineas] = -1

OptionParser.new { |opts|
    opts.banner = "Usage: #{File.basename($0)} -l  -f "

    opts.on( '-l', '--lineas N', 'lineas del fichero') do |arg|
        options[:lineas] = arg.to_i
    end

    opts.on( '-f', '--file FILENAME', 'fichero a tratar') do |arg|
        options[:fichero] = arg
    end

}.parse!

puts "Opciones: fichero = #{options[:fichero]}, lineas=#{options[:lineas]}"

# Resto del script
# ...
#Fin del script



Uso normal del script

$ ruby test_parse.rb -f prueba.txt
Opciones: fichero = prueba.txt, lineas=-1
Queda a juicio del programador considerar si el script debe o no seguir en base a los valores que han quedado en las variables. Este comentario me lo puedo ahorrar, pero lo destaco para distinguir entre una opción incorrecta y un valor incorrecto de una opción que si existe.

Ayuda del script (-h)

El parse hace cosas automáticamente. Suelen ser el tipo de cosas que todos deberíamos gestionar cuando creamos nuestros scripts. La principal es la ayuda. Observa la linea del script

opts.banner = "Usage: #{File.basename($0)} -l  -f "


Indicando el "usage" en la forma anterior, el resto lo hace el parser. Lo comprobamos indicando con "-h".

$ ruby test_parse.rb -h                                                      
Usage: test_parse.rb -l -f                                                                   
   -l, --lineas  N                  líneas del fichero  
   -f, --file FILENAME              fichero a leer



Errores del parser

Cuando la opción no está incluida entre las declaradas obtenemos un error que nos informa de ello. Además observa que el script no continúa y que la variable "$?" informa del error.

$ ruby test_parse.rb -i 10
test_parse.rb:23:in `
': invalid option: -i (OptionParser::InvalidOption) $ echo $? 1


martes, 1 de marzo de 2016

Ruby on Rails: despliegue en Apache

Para poner una aplicación Ruby on Rails en linea podemos usar el WEBrick y hacer algún escarceo pero para dejarlo desatendidamente en linea lo suyo es poner un Apache o un nginx. Como bien sabes tanto Apache como nginx son servidores web y solo llegan hasta la capa HTTP. Para usar páginas dinámicas producidas con cualquier aplicación se necesita un módulo que la ejecute. Esto es idéntico para PHP, Java, python y por supuesto Ruby.

El módulo que más se usa para Ruby on Rails es Passenger. También es posible usar Passenger para lenguajes como node.js o python.

Los pasos que vamos a dar son los siguientes:
1.- Instalar passenger
2.- Compilar para nuestro apache y sistema
3.- Configurar Apache para nuestra aplicación



INSTALACIÓN DE PASSENGER

Se instala como una gema. Comprueba si lo tienes instalado en tu sistema:
$ gem list passenger

*** LOCAL GEMS ***
passenger (5.0.24, 5.0.23

Si no es el caso, instalas la gema:
$ gem install passenger


COMPILACIÓN PARA APACHE

Este paso deberías hacerlo como root. Para la versión 5.0.24, en mi caso lo tengo en "/var/lib/gems/2.2.0/gems/passenger-5.0.24". Es un proceso guiado de varios pasos. Ejecúta lo siguiente sin más:
# /var/lib/gems/2.2.0/gems/passenger-5.0.24/bin/passenger-install-apache2-module


Es posible que el proceso falle por la falta de alguna librería. Esto es muy específico de cada equipo. Lee atentamente los mensajes de error e instala los paquetes necesarios para la compilación en tu sistema.

Configuración del Passenger

El siguiente es un ejemplo de fichero para usar en Apache2. Lo habitual es colocarlo en el /etc/apache2/sites-available y usar a2ensite/a2dissite (hace un enlace simbólico a /etc/apache2/sites-enabled). Una vez creado este fichero, es necesario reiniciar Apache.

Seguramente estarás familiarizado con la configuración de apache. Si no es tu caso te diré que hemos supuesto:
  • Suponemos que el servicio estará en la máquina con nombre DNS www.servidorpruebaruby.com y escuchará en el puerto 8080
  • Vamos a usar la versión 2.2 de Ruby
  • Suponemos que la aplicación está ubicada en /var/www/rails/RoRapp
  • Queremos poner producción en linea. Cambiando "RailsEnv" puedes especificar otro entorno como development o test.


Aquí tienes el fichero de configuración para Apache concordante con lo que hemos comentado en el post:

LoadModule passenger_module /var/lib/gems/2.2.0/gems/passenger-5.0.24/buildout/apache2/mod_passenger.so
<ifmodule mod_passenger.c="">
 PassengerRoot /var/lib/gems/2.2.0/gems/passenger-5.0.24
 PassengerDefaultRuby /usr/bin/ruby2.2
</ifmodule>

Listen www.servidorpruebaruby.com:8080
NameVirtualHost  www.servidorpruebaruby.com:8080

<virtualhost www.servidorpruebaruby.com:8080="">
   ServerName www.servidorpruebaruby.com
   DocumentRoot /var/www/rails/RoRapp/public
   RailsEnv production
   # RailsEnv development

   ErrorLog /var/log/apache2/RoRapp_error_log
   TransferLog /var/log/apache2/RoRapp_access_log
   LogFormat "%v %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" 
   LogLevel warn

   AllowOverride all              
</virtualhost>