viernes, 14 de diciembre de 2007

IE no cachea los background-image's

He desarrollado dos widgets a partir de datepicker: un menu contextual y un tooltip. No han costado demasiado pero, como siempre, ejecutando las aplicaciones contra localhost todo es simple y bonito y al llevarlo a un servidor de verdad te das cuenta de cosillas.

En este caso, volviendo a caer de mi propio guindo, las imágenes declaradas como background-image en una CSS no son cacheadas por IE. Y también en este caso, como en la mayoría de casos, preguntando a Google se llega a Roma.

Teniendo cargado prototype.js

if(Prototype.Browser.IE) document.execCommand("BackgroundImageCache", false, true);

Lo puede hacer, pero no lo hace. A veces me cuesta defender mi principio de no primacía de nungún navegador.

jueves, 13 de diciembre de 2007

Tuneando acts_as_list

Tengo un modelo de datos con dos tablas as y bs. Por tanto, tengo los modelos a.rb y b.rb donde

class A < ActiveRecord::Base
 has_many :bs, :dependent => :destroy
end

class B < ActiveRecord::Base
 belongs_to :a
 acts_as_tree :order => 'position'
 acts_as_list :scope => a
end


Es decir, la tabla bs contiene los campos a_id, position y parent_id de forma que los registros de bs relacionados con un mismo registro de as se comporten como una lista.

Ahora bien, he de cambiar esto. Necesito que los registros de bs relacionados con un mismo registro de as y con parent_id = null se comporten como una lista independiente.

 acts_as_list :scope => 'a_id = #{a_id} and parent_id is null'

Finalmente, necesito que los registros de bs con un mismo parent_id y relacionados a un mismo registro de as también se comporten como una lista independiente.

 acts_as_list :scope => 'a_id = #{a_id} and parent_id #{parent.nil? ? \'is null\' : \'= \' + parent_id.to_s }'

Como se dice en la documentación, acerca de :scope
It‘s also possible to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
...y eso hemos hecho.

jueves, 29 de noviembre de 2007

Curiosidad javascript o de cómo darte cuenta de que no lo haces bien

Dos de la mañana y descubro que mi script, en determinadas ocasiones no va. Manejando strings con fechas y parseando el año, el mes y el día. Por alguna razón cuando mezclo el 8 con números inferiores la cosa no va. DEF CON DOS¡¡ Forro mi script de alerts y descubro que lo que yo creía que era un 8 en realidad no lo es.
parseInt('01') = 1
parseInt('02') = 2
...
parseInt('07') = 7
parseInt('08') = 0 (ein?)
parseInt('09') = 0 (comor?)
Después de cagarme en todo lo cagable (laico, santo, real, imaginario...) descubro que no estoy sólo y que hay que hacer las cosas como mandan los cánones... Y me voy a dormir más feliz que un regaliz (con un blog más en mi lista de favoritos).

Viva Google¡¡¡

sábado, 24 de noviembre de 2007

Conferencia Rails II: reacciones

Me pareció bastante infantil la reacción que algunas personas tuvieron ante las palabras de los representantes de SUN y Microsoft tanto en el debate sobre frameworks como en la mesa de empresas.

El mensaje principal de ambas empresas fue bastante claro: estamos haciendo esfuerzos porque las aplicaciones Ruby corran en nuestros entornos. Es cierto que cuando los ejecutivos de estas empresas utilizan el nosotros, parecen representar a un dios único y todopoderoso cuya razón es indiscutible. Y también es cierto que, sobre todo Microsoft, tiene siempre ese tufillo a te quiero vender una moto inseparable de sus productos. Pero, no por ello, todo lo que huele a empresa grande y mundo corporativo tiene que ser malo.

La primera reacción fue de rechazo. ¿Qué pintan SUN y Microsoft aquí? se preguntaban. Este rechazo resulta incomprensible a mi modo de ver. Igual parece un poco maquiavélico pero cuantos más entornos permitan instalar mis aplicaciones RoR, más clientes potenciales tendré, más podré desarrollar mi actividad y más oportunidades hallaré de mostrar las bondades de esta tecnología.

La segunda reacción fue de temor a la invasión. ¡¡No nos moverán!! le faltó gritar a alguno. Pensar que el desarrollo de RoR va a venir marcado por lo que necesiten SUN para JRuby o Microsoft para IronRuby es un poco inocente cuanto menos.

El buen hacer está en la humildad y en el orgulllo, en la experiencia y en el talento, en la fusión y en la pureza, todo ello en su justa medida.

Conferencia Rails I: tengo una noticia buena y otra mala

El viernes pasado, según llegué a casa de la Conferencia Rails 2007, me preguntó mi chica que cómo había ido la cosa. Al mismo tiempo me pasó a mi hijo pequeño con un pañal limpio y un biberón para que me reincorporase a mis labores de hogar, con lo que intenté resumir.

'Ha habido cosas buenas y cosas no tan buenas', le dije. 'Primero las no tan buenas', dijo ella.

Las menos buenas fueron, en mi opinión, la falta de autocrítica, por una parte, y la falta de experiencia, por otra. La primera provocó que incluso cuando Obie Fernández dijo que no creía en grupos de más de cuatro personas porque es imposible juntar 4 buenos programadores de Rails (idea que suscribo a pies juntillas), algunas personas se sientieran ofendidas. La segunda, obvia, en un entorno con tan corta edad es normal que (como dijo José Cabrera) los nativos en el entorno tengan experiencias cortas. Además yo no estoy particularmente de acuerdo en aquello de que un joven que lleva picando desde los 13 por puro interés a los 23 tenga 10 años de experiencia. Para programar for the hell of it (que se dice en Texas) sí; para hacer aplicaciones que se venden, sinceramente, no.

Las buenas, muchas y variadas: mucho talento, muchas ideas, expansión en USA, aplicaciones impensables.

Jorge Valdano decía que
Un buen líder ha de apostar por el talento
, y no puedo estar más de acuerdo con él. Pero también decía que
El talento necesita su lugar. Hay que ubicar a la las personas en el lugar preciso para aprovechar las virtudes y esconder sus defectos. De este modo llegaremos a la excelencia
y tampoco creo que esté equivocado en esto.

La expansión en USA la comento Obie mientra lo llevaba a la cena en mi coche . Comentó que allí es más fácil hacer de Rails un trabajo y que Rails está en la corriente principal del desarrollo web de aquel país.

Finalmente, resalto fuera de todos los temas en boga sobre Ruby y sobre Rails, dos nuevas ideas que vi en funcionamiento: RoR aplicado a la inteligencia artificial en el marketing y RoR para voz IP. Seguro que de aquellas dos demos salen cosas interesantes.

Salud y buenos alimentos¡

martes, 13 de noviembre de 2007

Cycle

Hasta ayer, sólo había visto la llamada a cycle que hace ajax-scaffold para alternar el color de las filas de una tabla. Sin embargo, cuando necesité alternar dos tipos de elementos y resetear los dos ciclos independientemente, tuve que leer la documentación de TextHelper

En resumen: podemos llamar a la helper pasando como último parámetro una Hash donde damos un nombre al objeto Cycle que internamente crea.

cycle("rowBlue1", "rowBlue2", {:name => 'tr'})
cycle("cellText1", "cellText2", {:name => 'td'})


De esta forma, podemos resetear cada uno de ellos de forma independiente:

reset_cycle('tr') reset_cycle('td')

Rápido, sencillo, eficaz.

jueves, 8 de noviembre de 2007

Ruby on Rails en el SIMO?

Ayer me comentó mi amigo Javi que había participado en esta conferencia dentro de las conferencias del SIMO 2007. En ella se habló de distintos frameworks de desarrollo sobre distintos lenguajes y entornos (Drupal, Zend, RoR, .NET...). Por lo que me ha contado, poca chicha y poca limoná.

También ayer hubo un monográfico sobre Ruby on Rails en el que participó Agustín. Veremos qué cuenta al respecto.

Por otro lado, hay que ir pensando en pagar la Conferencia Rails, no?

Salud¡

viernes, 2 de noviembre de 2007

Un poco más de SOAP - cabecera de seguridad

En un acceso por Web Service he tenido que utilizar una cabecera de seguridad. Para crear los nodos previstos por el estándar WSSE he tenido que hacer lo siguiente:

driver = ClassThatXtendsSOAPRPCDriver.new
sh = SOAP::SOAPHeader.new
sec = SOAP::SOAPElement.new('wsse:Security')
sec.add(SOAP::SOAPElement.new('wsse:UsernameToken'))
sec['wsse:UsernameToken'].add(SOAP::SOAPElement.new('wsse:Username', 'Iamtheman'))
sec['wsse:UsernameToken'].add(SOAP::SOAPElement.new('wsse:Password', 'Iamthelaw'))
sec.extraattr['mustUnderstand'] = '1'
sec.extraattr['xmlns:wsse'] = 'url_for_wssecurity_xsd'
sh.add('wsse:Security', sec)
sb = SOAP::SOAPBody.new
sb.add('OTA_HotelAvailRQ', SOAP::SOAPElement.new('my_node'))
...
my_response = driver.invoke(sh, sb)


He tenido que trabajar de esta forma porque con soap4r no he podido generar un driver adecuado a mis necesidades. Con la versión 1.5.7 hay nodos dentro de los ficheros XSD que no sabe interpretar y por ello no genera bien los tipos de datos que utiliza el driver. Parece que esto se arregla en la 1.5.8. Tendremos que probarlo.

Salud¡

viernes, 26 de octubre de 2007

Que viene, que viene.. uh¡ uh¡

Queda menos de un mes para la Conferencia Rails 2007 de Madrid y hace ya una semana que se ha cerrado la lista de ponencias. Voy a tener problemas para elegir en algunos casos.

Voy a tener que ir afeitado porque veo amigos y conocidos entre los ponentes: Javier Ramírez, Xavi Noria, Alberto Romero y Agustín Cuenca compartirán sus cositas con todos nosotros.

Suerte para todos¡

Esta semana hemos seguido dando candela a los crawlers con Firewatir, Mechanize y rest-open-uri y hemos accedido a algún que otro web service. Nada superinteresante.

Salud¡

miércoles, 17 de octubre de 2007

Firewatir revisited (y lo que te rondaré morena)

Algunos detalles interesantes sobre el uso de Firewatir:
  • Con la última versión de la gema y de la extensión de Firefox no es necesario arrancar éste con firefox.exe -jssh.
  • No he conseguido que funcione en oculto. Watir es, en general, mucho más rápido en modo oculto y pensé que Firewatir también lo sería. Por tanto, hasta ahora su ejecución me resulta lenta. Lo que sí se puede hacer es minimizar la ventana de Firefox.
  • Con la última versión de la gema podemos saltar los alert y confirm del navegador de manera sencilla:

    my_firefox.startClicker('OK', 10, nil, nil)
    my_firefox.button(:value, "Acepto").click()


    donde el primer parámetro es el botón del messagebox que queremos pinchar ('OK' o 'Cancel'). En versiones anteriores, se hacía de esta otra manera:

    my_firefox.button(:value, "Acepto").click_no_wait
    my_firefox.click_jspopup_button('OK')
    my_firefox.wait()

  • En mi opinión, la gestión de ventanas no está muy conseguida. En la última versión cada

    my_firefox = Firefox.start('http://danimataonrails')

    abre una ventana nueva, lo cual clarifica un bastante el manejo de ventanas y hace recomendable un

    my_firefox.close()

    al terminar el crawling.

Seguiremos informando.

martes, 16 de octubre de 2007

A tiro hecho (I)

La mitad de las veces que busco en google información sobre ruby busco las mismas cosas, las que soy incapaz de memorizar dado mi estado neuronal. Una de ellas es ésta: el formateo de Time, Date y DateTime.

Los que más uso, extaído del link...

%d - Day of the month (01..31)
%H - Hour of the day, 24-hour clock (00..23)
%m - Month of the year (01..12)
%M - Minute of the hour (00..59)
%S - Second of the minute (00..60)
%w - Day of the week (Sunday is 0, 0..6)
%y - Year without a century (00..99)
%Y - Year with century

Por cierto, te has apuntado ya a la conferencia Rails de Madrid?

domingo, 14 de octubre de 2007

Firewatir sobre Windows

Después de darme de cabezazos contra un servidor y no poder terminar un crawler ni con rest-open-uri ni con mechanize, he terminado por programarlo sobre firewatir.

El punto de mayor información sobre fire watir es éste.

Y después de instalar la extensión de Firefox, instalar la gema FireWatir, lo y arrancar firefox con firefox.exe -jssh el código es muy sencillo:

require 'firewatir'
include FireWatir

#me hago con un navegador
my_firefox = Firefox.new
#voy a la página que me interesa
my_firefox.goto('http://www.danimata.com')
#relleno un combo
my_firefox.select_list(:name, 'Genre').select_value('man')
#relleno un campo de texto
my_firefox.text_field(:name, 'FirstName').set('Daniel')
#pincho el botón que submite la página
my_firefox.button(:value, "Continue").click


Así de fácil es y así se lo hemos contado. Como siempre, salud¡

jueves, 4 de octubre de 2007

SOAP hasta en la SOPA

Madre del amor¡

Llevo tres días intentando desentrañar cómo acceder a un Web Service que no tiene descubrimiento por wsdl. Lo único que tenía era unos xsd medio decentes (y digo medio decentes porque xsd2ruby cascaba al ejecutarlo contra ellos) y unos fuentes java que se supone que funcionan. Además, el acceso se realiza sin SOAP action.

Después de intentar emular al script wsdl2ruby y generar los objetos y métodos que él genera con estas manitas y mi tricotosa he tenido que generar un mensaje con su SOAPHeader y su SOAPBody y enviarlo por el método invoke de SOAP::RPC::Driver. Entre todas las pruebas que he tenido que hacer destaco estas tres:

  • después de observar un mensaje recibido como error del servidor de destino, vi que su SOAPBody contenía objetos SOAPStruct e intenté emularlo para crear la estructura XML que el servidor entiende.
  • tuve que cambiar todos los SOAPStruct por SOAPElement porque el servidor aplicaba sus xsd al contenido recibido y los nodos enviados llevaban el atributo encodingstyle, no aceptado en el formato esperado.
  • cada SOAPElement se puede tratar como Hash y cada SOAPStruct como Hash y como Array.
Unas líneas de código como muestra...

driver = SOAP::RPC::Driver.new("http://localhost:8003", "http://my.namespaces")

sh = SOAP::SOAPHeader.new
sb = SOAP::SOAPBody.new
sb.add('Message', SOAP::SOAPElement.new('Message'))
sb[0].extraattr['xmlns'] = 'http://my.namespaces'
sb[0].extraattr['xmlns:xsi'] = 'http://www.w3.org/2001/XMLSchema-instance'
sb[0].extraattr['xsi:schemaLocation'] = 'http://my.schemas'
sb[0].add(SOAP::SOAPElement.new('Header'))
sb[0].add(SOAP::SOAPElement.new('Body'))
sb[0]['Header'].add(SOAP::SOAPElement.new('MessageID', "000000")) sb[0]['Body'].add(SOAP::SOAPElement.new('Operation'))
sb[0]['Body']['Operation'].add(SOAP::SOAPElement.new('Query', 'This is my query'))
sb[0]['Body']['Operation'].extraattr['OperationName'] = 'QueryRequest'
sb[0]['Body']['Operation'].extraattr['OperationType'] = 'My type'


...

@resp = driver.invoke(sh, sb)

...

code = @resp[1]['Message']['Body']['Operation']['Responses'] ['Response']['Reference']['Code'].data
0.upto((@resp[1]['Message']['Body']['Operation']['Responses'] ['Response']['Records'].members.size rescue 0) - 1) do |r|
  puts @resp[1]['Message']['Body']['Operation']['Responses'] ['Response']['Records'][r]['Id'].data
end

Salud¡

viernes, 14 de septiembre de 2007

Memory leaks re-revisited

De nuevo, mejorando lo que hay.

La extensión de Time mucho mejor así:

class Time
  def method_missing(method_name, *args)
    cadena = self.strftime('%Y/%m/%d')
    return cadena.send(method_name, *args)
  end
end


Gracias Javi por tu ayuda.

Salud¡

Memory leaks revisited

Qué hay de nuevo?

He tenido que apretar un tornillo que se había quedado suelto con el tema del cambio en el acceso a sqlserver. Ocurre que, con ADO.rb, los datos de tipo datetime eran devueltos como Time si se obtienen a través de un modelo o como String si se obtienen mediante una query directa a la base de datos. Con ODBC siempre son devueltos como Time.

Por ello he tenido que extender el objeto Time para que no casquen los accesos con [], por ejemplo, en los casos en los que ADO.rb retornaba String.

En environment.rb (o mejor en algún fichero incluido en éste):

class Time
  def [](range = nil)
    return self.strftime('%Y/%m/%d')
  end
end

Salud¡

jueves, 13 de septiembre de 2007

Memory leaks conectando a sqlserver

Danimata al aparato.

Llevo N días viendo cómo una de nuestras aplicaciones, que trabaja contra sqlserver, padece de alzehimer, quicir, pierde memoria con cada petición.

Mi conexión era la siguiente:

adapter: sqlserver
database: dddddd
username: uuuuuu
password: xxxxxx
host: hhhhhh

Mi amigo Xavi me ha pasado un par de links en el que esto mismo le ocurre aquí y allá a otras personas que trabajan contra sqlserver a través de ADO.rb de la librería ruby-dbi. Recomiendan pasarse a ODBC.

He creado el origen de datos y mi conexión ha quedado como sigue:

adapter: sqlserver
database: dddddd
username: uuuuuu
password: xxxxxx
host: DBI:ADO:Provider=SQLOLEDB;Data Source=midatasource;

Lo he hecho, y nada de nada, el alzehimer continúa. Claro que no me había deshecho totalmente de ADO.rb. De nuevo Xavi con la solución. Con lo que me queda la siguiente conexión:

adapter: sqlserver
mode: odbc
dsn: my_dsn
database: dddddd
username: uuuuuu
password: xxxxxx

Et voilá¡ Todo funcionanado sin pérdidas de memoria y con la sensación de que las conexiones funcionan mejor y más rápido.

Como siempre, salud¡

martes, 28 de agosto de 2007

Dime con quién andas y te diré que RESCUE necesitas

Últimamente estoy decubriendo el uso de rescue y creo que estoy enamorado. Estamos acostumbrados a picar conociendo al dedillo lo que puede ocurrir y lo que no en la ejecución de nuestros programas y eventualmente dejamos de lado el control de errores. rescue no sólo ayuda en esta tarea sino que además se integra camaleónicamente el código de forma que apenas impacta en el mismo.

I love rescue.

viernes, 17 de agosto de 2007

Link interesante

Madre del amor que abandonado tengo esto.

El día 29 nació mi 'segundo proyecto a largo plazo'. Se llama Samuel y es tan bueno como su hermano Hugo.

A lo que iba: mi amigo Diego me ha pasado este link con un minitutorial de ActiveRecord bastante completo.

Tengo pendiente comentar el manejo de documentos Word y Excel. No se me ha olvidado pero me faltan manos y horas.

Como siempre, salud¡

jueves, 19 de julio de 2007

Una red social y una aplicación de facturación

Mi amigo Xavi Noria está a punto de terminar facturagem, la nueva ASP que ASPgems pone a disposición de autónomos, pymes y medianas empresas para la gestión de sus facturas.

Por otra parte, hemos participado en el desarrollo de dialogo, una red social 100% española que está pegando fuerte.

Como siempre, salud¡

miércoles, 13 de junio de 2007

win32ole + openoffice

Recientemente he tenido que construir un proceso ruby que fuese capaz de tratar documentos hechos con ms-word. Para ello, se puede encontrar información en este blog. En un próximo comentario haré un resumen de lo básico en tratamiento de documentos ms-word y hojas de cáculo ms-excel con la librería win32ole.

Sin embargo, como paso intermedio, estuve buscando información acerca de cómo tratar documentos de ms-word a través de openoffice y utilizando también la librería antesmencionada. Poca encontré, la verdad, pero un poco de aquí unido a la información que obtuve del blog que he recomendado y listo.

Una vez hecho

require 'win32ole'

estamos en disposición de crear un 'manejador' de openoffice de la siguiente manera:

serviceManager = WIN32OLE.new("com.sun.star.ServiceManager")

Lo siguiente es abrir el escritorio de openoffice

desktop = serviceManager.createInstance("com.sun.star.frame.Desktop")

con lo que ya estamos listos para abrir documentos, editarlos y guardarlos igual que si trabajásemos directamente con la aplicación.

Podemos abrir un documento de la siguiente manera:

document = desktop.loadComponentFromURL('file:///c:/IRPHsin.doc', '_blank', 0, [])

y salvarlo con un nuevo nombre:

document.storeAsURL('file:///c:/IRPHsin2.doc', [])

Se puede manejar fácilmente su título

document.title = "Nuevo título"

o el nombre del fichero...

document.filename = "nombre.doc"

para luego guardarlo...

document.save

o acceder a su texto a través de...

document.text

Como esto ya está muy pesado, seguiremos depués de la publicidad.

Salud¡

miércoles, 25 de abril de 2007

Controlando el ratón

Hoy he necesitado tener controlado el punto exacto en el que se encontraba el ratón al pinchar sobre los links y botones de mi aplicación. Mirando aquí y allá lo he conseguido con el siguiente código javascript:

clickY = 0;
offsetY = 0;
clickIE = document.all ? true : false;
if (!clickIE) document.captureEvents(Event.MOUSEDOWN);
document.onmousedown = md;
function md(e){
 if (clickIE) {
  if (document.documentElement && document.documentElement.scrollTop){
   offsetY = document.documentElement.scrollTop;
  }
  else{
   offsetY = document.body.scrollTop;
  }
  clickY = event.clientY + offsetY;
 }
 else {
  clickY = e.pageY;
  if (self.pageYOffset){
   offsetY = self.pageYOffset;
  }
 }
 if (clickY < 0){clickY = 0;}
 return true;
}


Con ello tenemos controlados en todo momento dos valores muy interesantes para abrir popups o menús de tipo drop down hechos con capas:

clickY, el punto en el eje Y del navegador donde se ha hecho click con el ratón
offsetY, el punto en el eje Y del navegador hasta donde se ha hecho scroll


Que aporveche¡

miércoles, 18 de abril de 2007

Envío de mails

Aunque esta información está en muchos sitios, la voy a escribir para no tener que mirarala en ningún sitio e ir a tiro hecho.

Para enviar mails desde una aplicación rails, hay que seguir los siguientes pasos:

1 - Fichero de configuración

Hay que crear el archivo mailer.rb en el directorio config de la aplicación. Éste contiene la configuración del sevidor de mail que vamos a utilizar. Por ejemplo:

ActionMailer::Base.delivery_method = :smtp
ActionMailer::Base.default_charset = "utf-8"
ActionMailer::Base.server_settings = {
:address => "smtp.danimata.com",
:port => 25,
:domain => 'www.danimata.com',
:user_name => "si1329@danimata.com",
:password => "aikman11",
:authentication => :login
}

2 - Controlador

En el controlador, por ejemplo form_controller.rb, crearemos tantas acciones como consideremos necesarias. Se puede utilizar una única acción para enviar todos los mails en función de los datos recibidos o bien una por cada tipo de mail que necesitemos. Lo importante es que en estas acciones es donde se harán las comprobaciones pertienetes y se llamará a los métodos del nuestro modelo mailer.

3 - Modelo

Debe de heredar de ActionMailer::Base. Tendrá un método mail_tipo_de_mail(parametros) por cada tipo de mail a enviar. En ellos se asigna valor a las variables globales que se utilizan para el envío. Por ejemplo:

def mail_new_password(user, new_passwd)
@subject = 'Nuevo password para AutoGassi'
@recipients = user.email
@from = MAIL_FROM
@sent_on = Time.now
@headers = {}

@charset = 'utf-8'
@content_type = "text/html"

@body["email"] = user.email
@body["first_name"] = user.first_name
@body["last_name"] = user.last_name
@body["passwd"] = new_passwd
end

4 - Vista

Por cada método del modelo, existirá una vista del mismo nombre. En ellas se formatea la información como se considere oportuno. Resulta muy fácil hacerlo como HTML. Por ejemplo, mail_new_password.rhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>AutoGassi</title>
</head>
<body>
Estimad@ <%= h(@first_name) %> <%= h(@last_name) %>,

<br/>Ésta es tu nueva contraseña de acceso a la web danimata.com:

<br/>Nuevo password: <%=@passwd%>

<br/>Puedes cambiarla una vez hayas entrado en la aplicación.
</body>
</html>

Para información más detallada, consultar rubyonrails.org.

Salud.

viernes, 30 de marzo de 2007

Manejo de fechas vacías con dynarch_calendar

En la aplicación en la que estoy trabajando necesito poder manejar fechas vacías con dynarch_calendar y no encuentro cómo hacerlo utilizando los métodos y parámetro existentes. He tenido que meter un par de cambios en el código del bundle que paso a enumerar:

- dynarch_calendar.rb

La última línea del método dynarch_date_select queda como

buffer << "<script>convert_date_container_to_dynarch_calendar('#{object_name}', '#{method_name}', #{index}, '#{object.send(method_name) ? initial_date : ''}', '#{object.send(method_name) ? initial_display : ''}', '#{image_url}')</script>\n"

de forma que si el campo fecha está vacío pase los parámetros initial_date e initial_display en vacío.

- convert_calendar_field.js

Cuando llega vacío y se utiliza en la línea 22

date = Date.parseDate(initial_date, ifFormat);

genera un objeto Date con la fecha del día. Por ello hay que trucar la asignación de los campos hidden en las líneas 28-30 de la siguiente manera:

dynarch_contents += "<input type='hidden' " + name_and_id('1i', 'year') + " value='" + (initial_date != '' ? date.getFullYear() : '') + "'>\n";
dynarch_contents += "<input type='hidden' " + name_and_id('2i', 'month') + " value='" + (initial_date != '' ? (date.getMonth() + 1) : '') + "'>\n";
dynarch_contents += "<input type='hidden' " + name_and_id('3i', 'day') + " value='" + (initial_date != '' ? date.getDate() : '') + "'>\n";


- Mi formulario

La línea que genera el calendario en mi formulario queda de la siguiente manera:

<%= dynarch_date_select 'vehicle', 'garage_out_date', {:date_format => "%d/%m/%Y", :use_month_names => spanish_month_names, :include_blank => true} %>

donde

def spanish_month_names
return ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre']
end

con lo que conseguimos que si no hay javascript habilitado, los combos que se generan se vean en castellano y con una opción en blanco para poder dejar la fecha vacía.

jueves, 22 de marzo de 2007

PDA y rails

He estado recientemente adaptando una aplicación hecha con ajaxscaffold para que pueda ser utilizada a través de una PDA con Intenet Explorer Mobile 5. Me he encontrado algunos inconvenientes lógicos y predecibles y otros no tanto.

En resumen:

- por alguna razón, los métodos de prototype.js sólo funcionan 'a veces'.
- lo mismo ocurre con las peticiones AJAX.
- el código generado con script/gnerate ajaxscaffold por sí mismo no funciona en PDA.
- en general, en una PDA no caben tantos datos como en una pantalla de portátil o sobremesa.
- el coste en tiempo de hacer que cierto código que funciona en portátil o sobremesa funcione también en PDA es, en general, mayor que el duplicar código.
- la conexión a través de PDA falla bastante y, en ocasiones, exige recargar páginas.

Por tanto, me he visto obligado a tomar las siguientes decisiones

- controlar en todos los controladores implicados si el dispositivo conectado es PDA.
- duplicar vistas en lugar de 'enmarañar' el código.
- eliminar AJAX de las vistas para PDA.
- reducir los formularios y los datos mostrados en las vistas para PDA.
- utilizar un layout propio para PDA que incluye hojas de estilo y ficheros de javascript reducidos.

Finalmente, para controlar el dispositivo he utilizado el siguiente método al que invoco before_filter.

def check_device
  @mobile_device = !request.env['HTTP_X_WAP_PROFILE'].blank?
  return true
end


Es muy posible que haya alguna manera mejor de controlarlo, pero revisando las cabeceras HTTP del request me pareció el parámetro más fiable de comprobar.

jueves, 15 de marzo de 2007

button_to_popup

En rails es muy sencillo hacer que un link abra una URL en un popup. Basta con utilizar link_to y utilizar el parámetro :popup.

Un ejemplo:

<%= link_to('Abrir ventana', {:action => 'my_action'}, :popup => ['ventana', 'height = 650, width = 750, top = 20, left = 100, scrollbars = 0, toolbar = 0, status = 0, menubar = 0, location = 0']) %>

Si queremos cambiar el link por un botón, no existe un método que por sí mismo te ofrezca la funcionalidad. Sin embargo, podemos darle una vuelta de tuerca al método button_to para que lo haga con una ayudita de javasctipt.

El ejemplo anterior quedaría de la siguiente manera:

<%= button_to('Abrir ventana', {:action => 'my_action'}, {:onclick => "this.form.target = 'ventana'; window.open('','ventana', 'height = 650, width = 750, top = 20, left = 100, scrollbars = 0, toolbar = 0, status = 0, menubar = 0, location = 0')"}) %>

viernes, 23 de febrero de 2007

Me cuesta entender

¿Porqué tenemos que rendirnos a picar N hojas de estilo para que una aplicación se vea correctamente en otros tantos navegadores/versiones de navegador?
Es absurdo. ¿Os imagináis a un fontanero instalando 6 WCs para 6 diferentes tipos de culo que se puedan sentar a hacer sus cositas? Uno para culos, otro para culitos, otro para culazos, otro para peludos, uno más para sólo meones y uno especial para grandes volúmenes de 'datos'.
¿A que no? Pues va siendo hora de que nos rebelemos contra esta esclavitud y procuremos hacer las cosas lo más sencillas posible a la vez que usables y entendibles.
Tampoco entiendo porqué nos empeñamos en hablar/escribir con acrónimos. Mi favorito es WUSIWYG (What You See Is What You Get). No sé si se hace por 'modernez' o por pura vagancia. Pero bueno, esto es otra historia.

Salud¡

viernes, 2 de febrero de 2007

miércoles, 17 de enero de 2007

Cómo subir ficheros con ajax_scaffold

Necesitamos tener tres plugins instalados:

- ajax_scaffold generator: para generar las tablas y los formularios.
- acts_as_attachment: para manejo automágico de archivos.
- respond_to_parent: para ejecutar los ficheros rjs desde un iframe en la ventana padre.

Una vez instalados los tres plugins, lo primero es generar el ajax_scaffold que necesitemos

>ruby script/generate ajax_scafold [my_table]

Con ello se genera automáticamente y, entre otras cosas, el controlador, el modelo y las vistas necesarias para el funcionamiento del ajax_scaffold.

En el modelo, añadiremos una línea similar a ésta

acts_as_attachment :storage => :file_system, :file_system_path => "[path_to_my_files]"

en el que indicamos al sistema que este tipo de registros llevan asociado un fichero.

Además es necesario modificar la tabla asociada como indica este link. En general, añadiendo estas cinco filas a la tabla mediante migration es suficiente:

add_column :my_table, :content_type, :string add_column :my_table, :filename, :string add_column :my_table, :size, :integer add_column :my_table, :parent_id, :integer add_column :my_table, :thumbnail, :string

En el partial _new_edit.rhtml hemos de cambiar el formulario para que no ejecute una llamada ajax sino un envío post.

<%= form_tag (
  url_for(@options.merge(:controller => '/[my_controller]')),
    {:id => element_form_id(@options), :target => 'hiddenIframe', :multipart => true } )%>


Además, hay que añadir un iframe oculto que reciba la petición.

<div style="OVERFLOW: hidden; WIDTH: 0px; HEIGHT: 0px"> <iframe id="hiddenIframe" name="hiddenIframe" src="" width="0" height="0"></iframe> <div>

Finalmente, en el controlador es necesario hacer que tras la actualización de datos se ejecute el archivo rjs que corresponda teniendo en cuenta que no se recibe una llamada ajax (xhr?).

responds_to_parent do
render :action => '[my_action].rjs'
end

jueves, 11 de enero de 2007

La consola. Cómo escribir caracteres no ASCII

Esto es un cambio radical en la calidad de vida de cualquier programador de Rails en Windows. Abrir la consola y ejecutar each o cualquier otra sentencia con bloques de código es 'escriptacular'. Para ello hay que editar el fichero $RUBY_HOME/bin/irb.bat. Hemos de buscar el tag :WinNT y comprobar que la línea siguiente tiene esta pinta: "%~d0%~p0ruby" -x "%~f0" %* -r irb/completion --noreadline E voilá¡