miércoles, 25 de noviembre de 2009

Google Search Web Client con Google's Go

Luego de haber danhado el ubuntu (al setear mal el environment del root y otros danhos colaterales); y tener ciertos problemas para compilar los fuentes de GO, como resultado de estos dias de aprendizaje publico este post con un ejemplo de un web server que hace una busqueda simple en google a traves de GO! y Google Ajax Search
Voy a asumir que ya se tiene un linux con GO compilado y listo para funcionar; las instrucciones de instalacion se pueden seguir en este link.

Bueno ahora si empezamos a escribir el codigo:
Vamos a tener los siguientes archivos Go:
google_go_search.go - El web server
search_handler.go - El manejador de requests de busqueda
page_templates.go - El render del html


******************************page_templates.go*************************************************
En este archivo vamos a tener la logica para hacer render de la pagina de inicio y la pagina de resultados de las busquedas, por ahora vamos a hacer algo basico para probar que la plantilla funcione, asi que vamos a crear una funcion con el render html que simplemente va a generar un boton con un alert (El html tiene un * para poder mostrarlo aqui en el post):

package templates
import(
"os";
)

func RenderHome()(htmlTemplate string, err os.Error)
{
htmlTemplate=`
<*html>
<*head>
<*title>Gishac's Google Search with GO!<*/title>
<*script language="javascript">
function doSearch(){
alert("Google's go search rocks");
}
<*/script>
<*/head>
<*body>
<*div style="width:100%">
<*div align="center">
<*span> Google Go's Search!!!
<*input value="Search with Go!" id="search" onclick="doSearch();" type="button">
<*/div>
<*/div>
<*body><*/html>
`;
return;
}

***************************************************************************************

Aqui por ahora no tenemos ninguna logica especial, simplemente una funcion que retorna una plantilla html, por lo cual no entrare en detalle con este archivo.


Vamos a crear el web server para hacer render de nuestra pagina de inicio, a un costado de cada linea esta su explicacion:

******************************google_go_search.go***********************************************

package main
import(

"./page_templates";
//Este es el nombre el archivo de templates que importamos
"flag";
"http";

"io";
"strconv";
"log"; // Imports de librerias nativa de Go
)
var port = flag.Int("port",8087,"Puerto donde se ejecuta el web server") //Definimos el flag con el numero de puerto para el server

func main()
{

http.Handle("/",http.HandlerFunc(HandleHomePage)); //Inicializamos el servidor web y le pasamos una funcion que atiene el request http para la pagina de inicio /
log.Stdoutf("Iniciando el web server: http://localhost:%d\nPara detener el servidor presioar Ctrl+C\n", *port);
err := http.ListenAndServe(":"+strconv.Itoa(*port), nil);
//Se levanta el servidor web
if err != nil { panic("Error: %s\n", err.String()) }
log.Stdout("Fin de sesion web"); }

//Funcion que atiende el request http en /
func HandleHomePage(c *http.Conn, req *http.Request)
{

homePageTemplate, err := templates.RenderHome(); // Aqui llamamos a la funcion que creamos en el package templates
if err != nil { panic("Error al hacer render: " + err.String()) }
io.WriteString(c, homePageTemplate); //Hacemos render del html que nos devuelve el metodo
}


********************************************************************************************
En esta instancia ya podemos compilar nuestros archivos y mandar a ejecutar el web server!!! :D

Para compilar y ejecutar el programa escribimos los siguientes comandos con el siguiente resultado:



Listo, podemos probar nuestro web server y la funcionalidad de la plantilla html que escribimos!



Hasta ahora todo es felicidad xD asi que vamos a modificar la plantilla para colocar un textbox donde vamos a escribir la busqueda que haremos en google desde la aplicacion, y ademas definir el metodo post y el action que luego vamos a controlar desde el codigo del web server.

****************************page_templates.go************************
.
.
.
func RenderHome()(htmlTemplate string, err os.Error)
{
htmlTemplate=`
<*title>Gishac's Google Search with GO!
<*form method="POST" action="/search"> //Definimos el post que va a ser manejado en /search
<*div style="width: 100%;">
<*div align="center">
<*span style="margin-top: 15px;" > Google Go's Search!!!

<*table>
<*tbody>
<*td>
<*input id="search_box" name="search_box" style="width: 300px;" type="text">
<*/td>
<*td>
<*input value="Search with Go!" id="search" type="submit"> //El boton ahora es un submit
<*/td>
<*/tr>
<*/tbody>
<*/div>
<*/div>
<*/form>


`;
return;
}

*********************************************************************************************

Con esto tenemos la pagina de busqueda lista para hacer su request:




Ahora viene le parte donde captamos el request de busqueda, hacemos un request http al servicio de google ajax search, que nos devuelve un string en formato json que lo podemos "parsear" con el api de json de google's go.
Para captar el request agregamos otro handler http al web server


******************************google_go_search.go***********************************************
http.Handle("/search",http.HandlerFunc(HandleSearchResult)); //funcion delegada a manejar el request
.
.

//Funcion que maneja request en la pagina de busqueda /search
func HandleSearchResult(c *http.Conn, req *http.Request){
req.ParseForm();
searchText := req.FormValue("search_box");
log.Stdoutf("Texto a buscar:%s\n", searchText);
searchResults, err:= searchHandler.GoogleSearch(searchText); //Hacemos la busqueda del texto ingresado
if err != nil {
panic("Error al hacer busqueda: " + err.String())
}
searchResultsPageTemplate, err := templates.RenderSearchResult(searchResults); //Llamamos al metodo que hace el render de los resultados
io.WriteString(c, searchResultsPageTemplate);
}

*******************************************************************************************

La parte mas importante del ejemplo el http get de la busqueda y el parse json de los resultados obtenidos.

**************************************search_handler.go***********************************
Primero definimos una estructura que respresenta el item json con cada respuesta de la busqueda, el formato de la respuesta del api de google es el siguiente:

{"responseData": {"results":[{"GsearchResultClass":"GwebSearch","unescapedUrl":"http://code.google.com/","url":"http://code.google.com/u/gishac/","visibleUrl":"code.google.com","cacheUrl":"http://www.google.com/search","title":"my title","titleNoFormatting":"title 2","content":"xyz"}

Por lo tanto tenemos que definir una estructura de similares caracteristicas ya que el api de json de go's se maneja a traves de indices clave/valor.

type GoogleSearchResult struct{
GsearchResultClass string;
unescapedUrl string;
url string;
visibleUrl string;
cacheUrl string;
title string;
titleNoFormatting string;
content string;
}


Ahora un vistazo al codigo que hace el request y llena las estructuras

//Funcion de busqueda
func GoogleSearch(searchText string)(searchResult string, err os.Error)
{
searchResult="Google search done";
var finalURL = "";
req := new(http.Request); //Creamos estructura httpRequest
req.URL, err = http.ParseURL("http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=" + http.URLEscape(searchText)); //Url para hacer el request de busqueda
if err != nil {
log.Stdout("Error parse URL" + err.String());
return "", err
}
resp := new(http.Response); //Creamos estructura para obtener la respuesta
log.Stdout("Request " + req.URL.String());
resp,finalURL,err = http.Get(req.URL.String()); //Hacemos el get
if err != nil {
log.Stdout("Error send request" + err.String() + finalURL);
return "", err
}
var rawSearchResult = "";
rawSearchResult = getResponseBody(resp); //Obtenemos la respuesta en formato string
jsonResult, err := parseJson(rawSearchResult); //Hacemos el parse para tener la respuesta en estructuras json

var i int;
var data json.Json;
data = jsonResult.Get("responseData").Get("results"); //De la estructura json sacamos el arreglo con los resultados de la busqueda
var objectArray []GoogleSearchResult; //Creamos un arreglo dcon el tipo de estructura definido para almacenar los resultados de busqueda
objectArray = make([]GoogleSearchResult, data.Len()); //Hacemos el allocate para el arreglo
for i = 0; i < style="font-weight: bold;">//Recorremos todos los resultados de la busqueda para ir creando cada estructura

var elem = data.Elem(i);
objectArray[i] = *parseSearchResults(elem);
}
searchResult =renderResultHTML(objectArray);
//Armamos el html con los resultados de la busqueda
return;
}


//Funcion para hacer parse de elemento jSon a estructura GoogleSearhResult
func parseSearchResults(elem json.Json)*GoogleSearchResult{
itemResult := new(GoogleSearchResult);
itemResult.GsearchResultClass = elem.Get("GsearchResultClass").String();
itemResult.unescapedUrl = elem.Get("unescapedUrl").String();
itemResult.url = elem.Get("url").String();
.
.
.
return itemResult;
}

//Funcion para generar el html de salida
func renderResultHTML(searchResults []GoogleSearchResult) string{
var i int;
var html = `<*div style="'width:100%;heigth:100%'"><*table>`;
for i = 0; i < html="html+`<*tr" style="padding-top:10px"><*td style="padding-top:10px;border-bottom:1px solid #336699"><*span>` + searchResults[i].title +`<*/span><*br/><*a target="_blank" href="` + searchResults[i].url + `">` + searchResults[i].url + `**
`;
html=html+ `<*span>`+ searchResults[i].content +`<*/span><*br /><*/td><*/tr>`;
}
html=html+`<*/table><*/div>`;
return html;
}


Con esto ya podemos probar la aplicacion de busqueda y nos muestra el siguiente resultado





Con eso queda en evidencia que para ser un lenguaje experimental tiene muchisima funcionalidad (aun no he probado las goroutines), al principio hay que acostumbrarse un poco a la sintaxis y a la ideologia que trata de transmitir go, pero termina siendo agradable y rapido.

Se pueden descargar los fuentes en el siguiente link.
http://sites.google.com/site/gishac/downloads/google_go_search_files.zip?attredirects=0


gish@c

viernes, 20 de noviembre de 2009

Scala & Lift Web Framework

Bueno despues de casi un año de haber abierto el blog he decidido crear un primer articulo y lo voy a basar en crear un pequeno proyecto en Scala+Lift y luego migrarlo a Eclipse Galileo (Voy a comer las ñ(nh), tildes, etc ya que este teclado en Ubuntu no me funca el Alt izq jeje).
Para empezar hay que que tener el JDK de Sun


Eclipse Galileo y el plugin de Scala IDE.
Despues de googlear un poco sobre proyectos web con Scala encontre muy buenas referencias sobre Lift WebFramework para intregrarlo en el proyecto (Xq sera que ese background me recuerda a Twitter??? xD )
Como vamos a crear el esqueleto del proyecto de manera independiente vamos a hacerlo desde Maven.
Luego de bajar y descomprimir el tar.gz de Maven hay que configurar un par de variables de ambiente asi que nos vamos a modificar el profile de nuestro usuario:

Respaldamos el profile

gishac@gishac-ubuntu:/etc$ sudo cp profile profile_backup

Agregamos un par de variables de ambiente que necesita Maven al profile
Para editar el profile
gishac@gishac-ubuntu:/etc$ sudo gedit profile

Colocamos la siguiente linea (en mi caso puse la r
uta de instalacion del jdk) al final del archivo
JAVA_HOME=/usr/lib/jvm/java-6-sun-1.6.0.15 export JAVA_HOME

Colocamos las siguientes lineas donde se define el home del Maven al final del archivo (esta es la ruta donde descomprimimos el tar de Maven, en mi caso) y modificamos la variable PATH

M2_HOME=/home/gishac/program_files/apache-maven-2.2.1 export M2_HOME
PATH=$PATH:$JAVA_HOME/bin:$M2_HOME/bin


Despues de editar el profile reiciamos... (Windows style)

Ahora si podemos verificar si todo esta OK



Ahora si ya tenemos todo listo para crear el proyecto, comenzamos con la estructura del proyecto Lift desde maven con los siguientes comandos:

mvn archetype:generate-U

Nos muestra la lista de archetypes y escogemos el 32

Aqui llenamos algunos valores que nos pide para el archetype lift: groupId, artifactId, version(en blanco), y el package:

Luego de confirmar con Y obtenemos el resultado de crear el esqueleto del proyecto

Para darle un check a nuestro proyecto levantamos el web server, primero nos ubicamos en el directorio donde se nos genero el proyecto y luego desde el maven iniciamos el jetty:

cd scalademo
mvn jetty:run

Ahora si ya tenemos levantada nuestra web app :D

Para facilitarnos la edicion importamos el proyecto en Eclipse como un proyecto Maven, se debe tener instalado un plugin de Maven

http://m2eclipse.sonatype.org/

Una vez importado el proyecto creamos las configuraciones de Run y Stop basado en Maven.

Click derecho sobre el proyecto-> Run As -> Run Configurations
Sobre la opcion de Maven Build creamos 2 nuevas configuraciones:

Jetty Start

Jetty Stop


Listo ahora si podemos modificar desde Eclipse nuestra aplicacion Scala y lo vemos reflejado al hacer deploy sobre el web server, teniendo como directorios mas importantes:

src/main/scala

src/main/web-app

src/main/web-app/templates-hidden

Algo importante en modificar es en el archivo Boot.scala dentro del package bootstrap.liftweb, al momento de indicarle en que paquete el framework debe buscar las clases de snippets, la linea debe quedar asi para nuestro caso:

LiftRules.addToPackages("scalademo")

En este punto ya podemos modificar los archivos html para mostrar lo que querramos, guardamos y mandamos a correr el proyecto:

Run as-> Maven Build-> Seleccionamos la configuracion Jetty_Run_Config

Listo, tenemos un pequenho inicio a Scala+Lift con Eclipse!