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, 25 de abril de 2007
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.
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.
- 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.
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')"}) %>
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¡
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
Primera aplicación full Rails que publicamos
Ha llegado, por fin, nuestra primera release de la aplicación de control de gastos.
www.gastosgem.com
No dejéis de echarle un ojo¡
www.gastosgem.com
No dejéis de echarle un ojo¡
jueves, 25 de enero 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
- 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á¡
Suscribirse a:
Entradas (Atom)