12 November 2009 ~ 0 Comentarios

Leyendo APIs desde Rails

Esta semana tuve que integrar un sitio hecho en Rails con el API de wine.com. Mientras esta aplicación está en desarrollo se hospeda en un grid container pequeño de media temple.

El problema…

Todo estaba funcionando bien en mi computadora cuando hice la integración usando el gem “hpricot”, pero al subirlo al grid container tuve problemas. El log de Rails indicaba que el archivo hpricot_scan.so no era accesible.

Encontré la solución en un blog, el problema era que la versión de Ruby bajo la que operan los grid containers es la 1.8.5 y para esta versión hay unos problemas que requiren que se modifique el código fuente del gem.

OK, procedí a hacer los cambios en el código fuente tal como dice el artículo, pero me surgió otro problema…

/usr/lib/ruby/1.8/fileutils.rb:754: command not found: ragel -v

El  “rake” para regenerar el gem con los cambios requería “ragel” en el servidor…

Ya que los containers son pedazos virtuales de una maquina, no tengo acceso root para instalar nuevos paquetes, esto me impidió solucionar el problema:

  • Instalando una versión más nueva de Ruby, por ejemplo 1.8.6 o 1.8.7
  • Instalando Ragel y completar el proceso de regenerar la gem hpricot

Otra opción era pasar todo a un servidor dedicado y volver a preparar el ambiente de desarrollo, pasar la base de datos, etc… esto es factible pero no cuando necesito que mi cliente vea el update mañana.

La otra opción era hacer la integración nuevamente con algo diferente a  hpricot… opte por esta.

La Solución

Encontré un blog post que compara tres formas de leer XML desde Rails… este me salvó ya que pude hacer la integración utilizando un método que no requiere librerías externas.

El artículo compara REXML, Hpricot y libxml-ruby … de todas la más lenta es REXML, pero es la que no depende de librerías externas y por lo tanto la que termine utilizando (probé con libxml pero tuve un problema similar).

Claro que esta es una solución temporal, solo para que mi contraparte en el proyecto pueda ver el avance.

Ejemplo de Uso de REXML

La solución con REXML es fácil de implementar y quiero compartirla en caso que alguien necesite leer XMLs desde una aplicación de Rails sin necesidad de usar librerías separadas.

Para el ejemplo usaremos el API de Twitter… este es el XML de mi timeline: http://twitter.com/statuses/user_timeline/corp.xml

Primero crearemos una función que recibe como parametro el usuario de twitter que queremos consultar, luego lee el XML correspondiente, lo almacena en un arreglo llamado twitts y lo devuelve con return twitts:

  1. def get_twitts(user)
  2.  apiurl = "http://twitter.com/statuses/user_timeline/#{user}.xml"
  3.  url = URI.parse(apiurl)
  4.  
  5.  request = Net::HTTP::Get.new(apiurl)
  6.  response,body = Net::HTTP.new(url.host, url.port).start {|http| http.request(request) }
  7.  twitts=[]
  8.  
  9.  doc = REXML::Document.new(body)
  10.  doc.elements.each(‘statuses/status’) do |s|
  11.    h = {}
  12.    %w[created_at text source].each do |a|
  13.      h[a.intern] = s.elements[a].text
  14.    end
  15.    h[:screen_name] = s.elements[‘user’].elements[‘screen_name’].text
  16.    h[:profile_image_url] = s.elements[‘user’].elements[‘profile_image_url’].text
  17.    twitts << h
  18.  end
  19.  return twitts
  20. end

Ahora ya podríamos usar esta función en nuestro controller, enviandole el usuario como parámetro.

  1. def show_mytwitts
  2.   @twitts = get_twitts("corp")
  3. end

Para mostrar los twitts que almacenamos en el controller, la vista sería algo así:

  1. <%for twitt in @twitts do%>
  2.   <img src="<%=twitt[:profile_image_url]%>" width="25" />
  3.   <strong><%=twitt[:screen_name]%>:</strong> <%=twitt[:text]%>
  4.  
  5.   Enviado desde <%=twitt[:source]%>
  6. <hr />
  7. <%end%>

Conclusión

Hay muchas formas de leer XML desde Rails, de hecho las mejores y más elegantes facilitan mucho más el acceso a los datos por medio de integración con los modelos.

Lo que pude implementar con REXML es una solución rápida y simple que sirve para la etapa en que estas en desarrollo y para que tu cliente (socio o contraparte) pueda ver los resultados de la integración al API deseado.

Para producción conviene utilizar soluciones más eficientes. Mientras investigaba encontré esta presentación que describe muchas formas elegantes y más eficientes de leer XML con Rails:

Leave a Reply