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á¡

viernes, 17 de noviembre de 2006

Combos dependientes OnRails

Es muy común en la aplicaciones de hoy en día que sea necesario recargar los valores de un combo en función de lo que se haya seleccionado en uno previo.

La manera más fácil de conseguir este comportamiento en una página hecha con Rails es utlizar observe_field.

Funciona de la siguiente manera. Imaginemos el siguiente código para pintar el contenido de las tablas empresa y área.

<%= select 'usuario', 'empresa_id' , Empresa.find(:all).collect {|p| [ p.name, p.id ] } %>
<%= select 'usuario', 'area_id' , Area.find(:all).collect {|p| [ p.name, p.id ] } %>


Como las áreas son una subdivisión de una empresa queremos que el contenido del combo de áreas sea dependiente de la empresa seleccionada. Para ello codificaremos

<%= observe_field 'usuario_empresa_id', :url => @options.merge(:controller => '/areas', :action => 'combo_por_empresa'), :update => 'usuario_area_id', :with => 'combo_empresa_id' %>

Lo que estamos haciendo es controlar el evento onchange del primer combo de manera que su manejador invoque una petición ajax al controlador áreas con la acción combo_por_empresa y pasando el valor de ese primer combo en el parámetro combo_empresa_id.

Sólo nos queda incluir esta acción en el controlador con dos operaciones:

- las selección de los nuevos datos a pintar

@areas = Area.find(:all, :conditions => ['empresa_id = ?', params[:combo_empresa_id]])

- el render del fichero rhtml que pintará las nuevas opciones

return render(:partial => 'combo_por_empresa', :layout => false) if request.xhr?

Finalmente, en _combo_por_empresa.rhtml generamos el html con las nuevas opciones.

<%= options_for_select(@areas.collect {|p| [ p.name, p.id ]}) %>

Es muy recomendable mirar la documentación de observe_field en api.rubyonrails.org.

Salud.