Secciones

Artículos para tus primeros pasos

Si estás empezando a introducirte en el mundo de Groovy y Grails, no te pierdas nuestros artículos básicos: 

Entrevistas con los expertos
 

Los protagonistas te cuentan de qué van los proyectos más importantes del mundo Groovy:


Un proyecto de:
ImaginaWorks
Campus Escuela de Groovy

Groovy + Java = Scripting++

jueves 10/09/2009

Internet es una fuente inagotable de información. Tanto, que es imposible estar al tanto de todo lo que se publica sin ayuda, y esa ayuda tiene normalmente forma de herramientas de agregación y/u organización como BlogLines. En el ámbito de Groovy, tenemos dos magníficos sitios que agregan gran parte de la información que se publica a diario sobre nuestro lenguaje favorito: GroovyBlogs.org, dedicado a agragar feeds RSS, y GroovyTweets.org, que muestra los tweets de personas y grupos más o menos relevantes en el ámbito de Groovy.

Además, tenemos las listas de correo. Si somos suficientemente constantes para seguirlas a diario, nos mantendrán informados sobre el día a día de los desarrollos y las incidencias de proyectos como Groovy y Grails. Recientemente, además, se ha presentado gr8forums.org, un sitio de foros al estilo tradicional sobre Groovy, Grails, Griffon, y demás tecnologías relacionadas.

Como veréis, las fuentes de información empiezan a aumentar en volumen, y disparidad, y estar al tanto de todo lo que ocurre puede llegar a robar mucho tiempo al día. Por eso vamos a aprovechar esta circunstancia para hablar de las posibilidades de Groovy como lenguaje de scripting y de su capacidad para tratar distintas fuentes de información. Vamos a desarrollar un script que:

  1. Definirá una serie de fuentes de información.
  2. Tratará cada fuente de forma específica, ya sea leyendo un feed RSS o procesando HTML de una web.
  3. Compondrá un resumen con la información obtenida, a partir de una plantilla definida previamente.
  4. Enviará por FTP el archivo generado a un servidor web.


Una vez terminado el script, lo único que tendremos que hacer será programar una tarea automática (cron, si estamos en unix) que lo ejecute a intervalos de tiempo para que el visitante siempre tenga una referencia de lo último que se está publicando.

Y ya que nos ponemos, reservamos un dominio y lo presentamos ante todos vosotros: AllAboutGroovy.com

Requisitos previos

Antes de empezar, hay algunas librerías que debemos colocar en la carpeta $HOME/.groovy/lib para que todo funcione:


La parte principal del script

La filosofía de este script es la siguiente: existe una lista de recursos a procesar, y para recurso hay una función que hace el trabajo específico (leer un feed rss, o procesar html con una estructura particular) y devuelve un fragmento de html. Una vez obtenido, se sustituyen todos los fragmentos en una plantilla html que podéis ver aquí.

Esta es la parte fundamental:

import java.util.regex.Matcher
import java.util.regex.Pattern
import java.text.DateFormat
import org.apache.commons.net.ftp.FTPClient

def fNames = [
'Timestamp',
'GroovyBlogs', 
'GroovyTweets', 
'GroovyMailList', 
'GrailsMailList',
'GR8Forums'
]

def fragments = [:]
//XMLSlurper form HTML parsing
def htmlSlurp = new XmlSlurper(new org.cyberneko.html.parsers.SAXParser())
//XMLSlurper form XML parsing
def xmlSlurp = new XmlSlurper()

def fragment
def t0
/* ===========================================================================================
1. Obtain information and generate the model.
============================================================================================== */
fNames.each{fName ->
	t0 = System.currentTimeMillis()
	println "** $fName"
	try{
		fragment = "parse$fName"(htmlSlurp, xmlSlurp)
		fragments."$fName" = fragment
	}catch(Exception x){
		fragments."$fName" = x.message
	}	
	printTS(t0)	
}

/* ===========================================================================================
2. parse the Template and generate index.html
============================================================================================== */
def idx = new File("web/index.html")
def tpl = new File("web/index.tpl.html").text
idx.delete()

fragments.each{k,v->
	//println "Reemplazando $k por $v"
	tpl = tpl.replaceAll("##$k##",Matcher.quoteReplacement(v))
}

idx.write(tpl)


/* ===========================================================================================
3. Upload index.html to web server.
============================================================================================== */
def ftpServer = "www.allaboutgroovy.com"
def ftpUser = "allaboutgroovy"
def ftpPass = "4llab0utgr00vy"

def ftpClient = new FTPClient()
ftpClient.connect( ftpServer )
ftpClient.login( ftpUser, ftpPass )
println "Connected to $ftpServer. $ftpClient.replyString"

ftpClient.changeWorkingDirectory( './httpdocs' )
println "Directory changed. $ftpClient.replyString"

ftpClient.enterLocalPassiveMode()
idx.withInputStream{ fis -> ftpClient.storeFile( idx.name, fis ) }
println "Upload completed: $ftpClient.replyString"

ftpClient.logout()
ftpClient.disconnect()



Las funciones de procesamiento
/* ===========================================================================================
     ++       Auxiliary functions to parse each individual information source        ++
============================================================================================== */


/*
 parse GR8Forums (html)
*/
def parseGR8Forums(htmlSlurp, xmlSlurp){
	def u = 'http://gr8forums.org/search.php?search_id=active_topics'
	println "\tProcesando $u"
	def html = htmlSlurp.parse(u)
	def result = '
    ' def elements = html.'**'.findAll{it.name()=='A' && it.@class.toString().equals('topictitle')} println "\tEnlaces encontrados: ${elements.size()}" def i = 0 elements.each{ result += "
  • ${it.text().trim()}
  • " } result += '
' return result } /* parse Groovy mail list (RSS Feed) */ def parseGroovyMailList(htmlSlurp, xmlSlurp){ def u = 'http://www.nabble.com/codehaus---Groovy-ft11866.xml' println "\tProcesando $u" feed = xmlSlurp.parse(u) def result = '
    ' def i = 0 println "\tLeyendo ${feed.entry.size()} elementos..." feed.entry.each{ result += "
  • ${it.title}
  • " } result += '
' return result } /* parse Grails mail list (RSS Feed) */ def parseGrailsMailList(htmlSlurp, xmlSlurp){ def u = 'http://www.nabble.com/grails---user-ft11861.xml' println "\tProcesando $u" feed = xmlSlurp.parse(u) def result = '
    ' def i = 0 println "\tLeyendo ${feed.entry.size()} elementos..." feed.entry.each{ result += "
  • ${it.title}
  • " } result += '
' return result } /* Small hack to generate Timestamp as an item in the model. */ def parseTimestamp(htmlSlurp, xmlSlurp){ new Date().toString() } /* parse GroovyBlogs.org (html) */ def parseGroovyBlogs(htmlSlurp, xmlSlurp){ def u = 'http://groovyblogs.org' println "\tProcesando $u" def html = htmlSlurp.parse(u) def result = '
    ' def elements = html.'**'.findAll{it.name()=='A' && it.@href.toString().contains('/jump/') && it.text()} println "\tEnlaces encontrados: ${elements.size()}" def i = 0 elements.each{ result += "
  • ${it.text().trim()}
  • " } result += '
' return result } /* parse GroovyTweets.org (html) */ def parseGroovyTweets(htmlSlurp, xmlSlurp){ def u = 'http://groovytweets.org' println "\tProcesando $u" def html = htmlSlurp.parse(u) def result = '
    ' def elements = html.'**'.findAll{it.name()=='DIV' && it.@id.toString().startsWith('tweet')} println "\tTweets encontrados: ${elements.size()}" def name, status, links, nodes def i = 0 elements.each{ name = it.'**'.findAll{it.name()=='A' && it.@class == 'screenNameLink'}[0] name = "${name.text().trim()}" status = it.'**'.findAll{it.name()=='SPAN' && it.@class == 'statusText'}[0] /* GroovyTweets uses EasyThumb to embed thumbs in every link. if the tweet has no links we can just take status.text(). if it has one or more links, we have to parse it more deeply to remove the spans. */ nodes = status.'**'.findAll{it.name()=='SPAN' && it.@class=='thumb'} nodes.each{thumb-> thumb.replaceBody("") } status = replaceLinks(status.text()) status = replaceTwitterSearches(status) status = replaceTwitterUsers(status) result += "
  • $name: ${status}
  • " } result +='
' return result }



Funciones de utilidad

 

/* ===========================================================================================
     ++       Utilities        ++
============================================================================================== */

def printTS(t0){
	def t = System.currentTimeMillis() - t0
	println "$t ms."
}


def replaceTwitterSearches(input){
	def s = findTwitterSearches(input)
	s.each{
		input = input.replaceAll(it[0..it.size()-1],"$it")
	}	
	return input
}


def replaceTwitterUsers(input){
	def s = findTwitterUsers(input)
	s.each{
		input = input.replaceAll(it[0..it.size()-1],"$it")
	}	
	return input
}


def replaceLinks(input){	
	def urls = findUrls(input)
	urls.each{
		input = input.replaceAll(it,"$it")
	}	
	return input
}

def findUrls(text) {
	def URL_PATTERN = Pattern.compile("\\b(https?|ftp|file)://[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|]",Pattern.CASE_INSENSITIVE)
	def links = applyPattern(URL_PATTERN, text)
  return links;
}

def findTwitterSearches(text){
	def pattern = Pattern.compile("\\#\\w+\\W",Pattern.CASE_INSENSITIVE)
	def items = applyPattern(pattern, text)
  return items;	
}

def findTwitterUsers(text){
	def pattern = Pattern.compile("@\\w+\\s",Pattern.CASE_INSENSITIVE)
	def items = applyPattern(pattern, text)
  return items;	
}

def applyPattern(pat, text){
	Matcher m = pat.matcher(text);
  def items = []
  while (m.find()) {
  	items.add(m.group());
  }
  
  return items
}

 

Podéis ver el script completo aquí. Como véis, no se trata de un script muy grande, y permite unificar lo mejor de dos mundos: el de la automatización de tareas en el servidor y las librerías existentes para la plataforma Java.


Contenidos relacionados:



0 comentarios:

Tienes que estar registrado para iniciar sesión y poder publicar tus comentarios