Pasajeros al tren! o introducción a mod_rails para Apache 2

Posted by calas Fri, 25 Apr 2008 10:25:00 GMT

El problema

Ruby on Rails ha sido el boom en los últimos dos o tres años en cuanto a desarrollo web se refiere. Muchos desarrolladores nos hemos pasado felices y contentos a desarrollar en Ruby, un lenguaje de programación hasta ahora casi desconocido y que desde el lanzamiento de Rails ha empezado tomar tanto protagonismo que ahora ocupa el noveno lugar en la lista de los lenguajes de programación más utilizados.

La creciente comunidad de programadores en ruby se ha volcado con el opensource y están saliendo a la luz muchos proyectos interesantes como JRuby, una implementación completa de Ruby sobre JVM; Rubinus, otra implementación sobre una VM pero esta vez sobre C; YARV, otra máquina virtual en C que cuentan es muy rápida y que será la VM de Ruby 2.0 (ya está incluida en la versión 1.9.0 aunque esta no está lista para producción aun sino que es una especie de preview la versión 2.0); Merb, un framework de desarrollo Web inspirado en Rails que cada vez está más interesante; Camping, un miniframework para la creación de Webs sencillas y toda una lista enorme de proyectos.

Uno de los problemas que encontraban muchos desarrolladores de PHP era la dificultad para hacer el deploy de aplicaciones Rails en los shared-hostings. Hasta ahora no había una manera fácil de hacer un deploy con Apache, o se recurría a fastcgi que es la solución en los hostings compartidos como Dreamhost o se contrataba un VPS o servidor dedicado donde un Apache, Lighthttpd o Nginx hacían de proxy y balanceador entre los servidores mongrel, thin o lo que fuera de las aplicaciones rails.

La solución

Y por qué hablo en pasado si hasta día de hoy sigue siendo la forma de hacer deploy? Este mismo blog está servido por un nginx que hace de proxy a 2 mongrels.

Es que el pasado 11 de Abril se anunció Phusion Passenger (aka mod_rails) que promete que hacer un deploy sobre apache sea extremadamente sencillo, solo cuestión de subir los ficheros sin hacer nada de configuración especifica de Rails, mongrel u otro servidor.

La instalación

Anoche me he puesto a probarlo y he quedado impresionado. Al punto de que toda la instalación, incluida la de apache, me tomó solo unos 10 o 15 minutos.

No es mi objetivo contarles como instalar Rails, Apache 2 o ruby, sobre eso hay muchas cosas escritas y pueden buscar en google. O pedírmelo en un comentario y ya haré otro post.

Así que descontando todo eso la cosa se reduce a:

1- Instalar Phusion Passenger:

      
    sudo gem install passenger
  

2- Compilar, instalar y configurar el módulo de apache:

  
   sudo passenger-install-apache2-module
  

Este comando les indicará si les falta alguna dependencia y si todo va bien les mostrará 3 lineas al final que tienen que incluir en su configuración de Apache, en mi caso fueron:

  
    LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-1.0.1/ext/apache2/mod_passenger.so
    RailsSpawnServer /usr/lib/ruby/gems/1.8/gems/passenger-1.0.1/bin/passenger-spawn-server
    RailsRuby /usr/bin/ruby1.8
  

Pero no siempre son iguales, así que no vale hacer copy & paste

3- Configurar un virtual host para tu aplicación.

En la configuración de apache incluir un virtual host para la aplicación de Rails donde indiques el directorio donde esta reside:

  
    <VirtualHost *>
      ServerName myrailsapp.com
      DocumentRoot /home/sites/myrailsapp/public
    </VirtualHost>
  

4- El robocop:

Abrir el navegador e ir http://myrailsapp.com (o la url de tu sitio si quieres funcione ;)) ya debería estar funcionando. El environment que se carga por defecto es el de producción, pero hay varias opciones de configuración que puedes ver en la guía de usuario de passenger

Todavía tengo que hacer muchas pruebas para pasar mis aplicaciones de la combinación nginx+mongrel_cluster a mod_rails aunque los benchmarks muestran excelentes resultados habría que valorar variables como consumo de recursos, etc. Nginx consume menos recursos que apache pero los mongrels son muy hambrientos y terminan consumiendo mucho. Otro tema a valorar es la robustez pero ya eso serían temas para otros posts.

El futuro.

Mirando el panorama el futuro de Rails y los demás frameworks basados en Ruby parece prometedor. Uno de los beta testers de passenger fue Dallas de Dreamhost, por lo que cabe esperar que dentro de poco sus planes de hosting incluyan mod_rails.

El problema seguiría siendo los pocos recursos disponibles en un ambiente de alojamiento compartido pero los chicos de Engine Yard están haciendo un esfuerzo importante en Rubinius, Sun en JRuby y Ruby 2.0 saldrá con YARV incorporado. Todos estos proyectos mejoran muchísimo el rendimiento actual de Ruby, corrigiendo muchos errores de implementación de las versiones actuales. Los chicos de Phussion prometen un consumo de 33% menos de memoria usando mod_rails con Ruby Enterprise Edition, aunque todavía no está disponible.

Creo que pronto veremos a los shared hosting tradicionales ofreciendo un buen soporte para aplicaciones en Ruby on Rails y el mundo será un mejor lugar, al menos para los programadores de webs. Creo que así volvería a activar mi cuenta en Dreamhost.

Actualmente Phussion Passenger solo soporta aplicaciones en Ruby on Rails, al fin y al cabo es mod_rails y no mod_ruby, pero al ser un proyecto opensource puedo apostar a que dentro de muy poco tiempo la comunidad habrá aportado soporte para Camping, Merb, Nitro o cualquier otro framework.

Posted in  | Tags , , ,  | no comments

Javascript No Obstrusivo o cuando Ajax es un problema

Posted by joahking Thu, 24 Apr 2008 09:51:00 GMT

Ajax ha sido un boom en el desarrollo web en los últimos tiempos, es un hecho.

Pero no es una alfombra mágica, y mal usado puede crear problemas de rendimiento. Puedes probar una busqueda en Google, pero los puntos problematicos son estos más o menos:

Por ejemplo, los Cascade Selects que me han regalado en Navidad mejorarían el rendimiento si mandara a la vista todos los datos necesarios y construir los selects usando OPTGROUP y no pedir pequeñas cantidades por Ajax cada vez que elijo Galicia en vez de Murcia.

Esto es precisamente lo que intenta resolver el nunca suficientemente alabado Ryan Bates en su railscast Dynamic Select Menus. Su idea es en la vista pedirle js a un javascript controller:

<!-- views/somethings/new.html.erb -->
   <% javascript 'esta_accion' %>

# application_helper.rb
def javascript(*files)
  content_for(:head) { javascript_include_tag(*files) }
end

# y en el javascript_controller
def esta_accion
  @datos = Dato.find :all
end

Luego el erb parseará el views/javascripts/esta_accion.js.erb que se encargará de generarnos un javascript a la medida.

Hasta aquí todo bien, pero si lo miramos con el tipo de ojo correcto esto no nos resuelve el problema completamente. Mirando los logs notamos que para generar la vista new de los somethings estamos haciéndole dos peticiones al Rails.

Fíjate que este dos es una palabra maleta: no solo lleva dentro otra petición Rails (¡petición es otra palabra maleta!), sino y esto es peor: si el proceso es más complejo que mis simples selects estaré repitiendo código del negocio de la acción new de Something en otro controller (oh Dios! dira el Pana, unDRY code! y saldrá corriendo a desinfectarse las manos).

O sea que eliminamos los muchos accesos al DOM y el Ajax pero a muy mal precio, ¿porqué no reducimos el round-trip a solo una request?

¡Que entre Unobstrusive Javascript !

no voy a extenderme diciendo qué es el Javascript No Obstrusivo, para eso están los gurus y Mr. Google.

Resumiendo la idea es sacar el javascript del html y pegarle eventos a los elementos desde un javascript después que la página esté cargada:


<!-- algún html define el elemento -->
   <button id="alertable">Click me!</button>

<!-- algún javascript le pega el evento onclick -->
  document.observe(
     "dom:ready", 
     function() {
         document.getElementById("alertable").addEventListener( 'click',
            function() { 
               alert("Gracias, he esperado toda mi vida este momento!"); 
            }, 
         false);
      });
Hay un screencast muy bueno en railsenvy sobre cómo integrar UJS en Rails. Quedaría así resumiendo:

<!-- en el layout -->
<head>
    <%= yield :unobstrusive_javascript %>
</head>

<!-- views/algos/show.html.erb -->
<% content_for :unobtrusive_javascript do -%>
  <script type="text/javascript" charset="utf-8">
    document.observe(
        "dom:ready", 
        arrancaUserInterface('<%= @datos_para_la_UI.to_json %>'), 
        false
    );
  </script>
<% end %>

Wow! Gracias Jason, ya no necesitamos más el javascripts_controller, estoy contento con esta solución pero igual me falta la elegancia de la solución anterior, donde no tenía javascript en el head de mi html, sino en un hermoso esta_accion.js.

Talvez UJS4Rails es lo que estoy buscando, pero…

Si este fuera el mejor de los mundos posibles ¿cómo querría que esto quedara?

Me gustaría que en el procesamiento MVC de la petición Rails transformara la V en V + js, así tendriamos unobstrusive js out of the box, REST y algunas otras golosinas. Pero quien sabe si Rails Edge….

Posted in  | Tags , , , ,  | no comments

Instalación de Radiant CMS

Posted by The Turing Machine Sun, 28 Oct 2007 15:31:00 GMT

Esta es una guía de referencia para instalar y configurar el Radiant CMS, a medida que vaya desarrollando el proyecto que estoy realizando, iré posteando los problemas y soluciones que encuentre en el camino.

Sin más rodeos, vamos a lo que nos interesa. Para instalar el radiant ejecutamos


gem install radiant -y


Para crear un nuevo proyecto, hacemos:



% radiant --database [mysql|postresql|sqlite3] ruta/del/proyecto


Ahora, configuramos el fichero config/database.yml de acuerdo a nuestra configuración. Una vez creada la base de datos. Ejecutamos



% rake [environment] db:bootstrap


Si queremos iniciar en desarrollo, omitimos el parametro environment, de lo contrario pasamos el nombre del environment, test o production.

Cuando nos lo solicite, le diremos que vamos a usar el template de blog estilizado, esto para tener unos ficheros básicos de estilo, y algunas páginas por defecto, las cuales nos servirán de guía, y que luego reemplazaremos por nuestras propias páginas. De cualquier forma tienes la opción de usar el template vacío y empezar todo desde cero.

Luego, ejecutamos nuestro servidor para ver que esté todo funcionando.



% script/server -e production


Accesamos nuestro servidor, y ya podemos empezar a usar el radiant.

Para utilizar el radiant, sugiero leer el siguiente tutorial: Art of Mission Radiant CMS

Instalando Extensiones

Para encontrar información sobre las extensiones disponibles, podemos entrar en http://dev.radiantcms.org/radiant/wiki/Extensions

Seguramente, en nuestra páginas, vamos a querer tener attachments, como ficheros o imágenes, por lo que primeramente vamos a instalar la extensión Page Attachments. Para esto hacemos:



% cd ruta/del/proyecto
% svn checkout http://svn.seancribbs.com/svn/rails/plugins/extensions/page_attachments vendor/extensions/page_attachments


Luego, realizamos las migraciones necesarias en nuestro environment, e inicializamos:



rake db:migrate:extensions
rake radiant:extensions:page_attachments:update


Reiniciamos el servidor. En este momento, ya podemos subir ficheros a nuestras páginas. La extensión de Page Attachments, nos permite crear miniaturas (thumbnails) de las imágenes que cargamos. Para esto, debemos tener instalado ImageScience, RMagick o MiniMagick.

Lo primero es instalar ImageMagick. podemos obtenerlo de esta página. Después de instalarlo, tuve ciertos problemas para crear los thumbnails con algunos formatos de imágenes. Para saber que formatos podemos utilizar ejecutamos



% identify -list format


Si alguno de los que nos interesa no está en la lista, tendremos que configurar el ImageMagick, pero ese es tema para otro post.

Luego tenemos que instalar el Mini Magick para esto ejecutamos:



% gem install mini_magick


Ahora editamos el fichero /vendor/extensions/page_attachments/app/models/page_attachment.rb tal que tengamos algo similar a esto:



  has_attachment :storage => :file_system,
                 :thumbnails => { :thumb => '50x50!' },
                 :resize_to => '400x300',
                 :processor => :mini_magick,
                 :max_size => 10.megabytes


:resize_to, convierte las imágenes automáticamente, si lo omitimos no hace nada. :thumbnails => { :thumb => ‘50×50!’ }, aquí podemos indicar los distintos tamaños que queremos para las miniaturas. :processor, nombre del procesador de imagenes.

Para más información sobre estas opciones, debemos leer sobre el plugin attachment_fu, aquí una buena referencia, aunque sólo en inglés.

Guardamos los cambios en el fichero, y reiniciamos. Ya podremos crear thumbnails de nuestras imágenes y llamarlas usando las etiquetas r:attachment.

Pronto publicaré más información de cosas que vaya necesitando en Radiant.

Posted in  | Tags ,  | 1 comment

Uno de Emacs on Rails

Posted by calas Sun, 21 Oct 2007 07:20:00 GMT

Emacs + Rails es una buena combinación para trabajar con Ruby on Rails utilizando Open Source y en cualquier plataforma. Al final todo el equipo lo está utilizando y pasamos por el momento de comprar una macbook y Textmate. Emacs es muy potente y totalmente configurable. Quizás deberíamos hacer un post sobre este tema porque este no va de eso. Sino de un pequeño tip cuando ya lo estás utilizando.

Problema

Resulta que el emacs-rails tiene una combinación de teclas muy útiles cuando estás programando y me refiero a M-S up con la cual podremos movernos fácilmente entre un método del controlador y su correspondiente vista, y viceversa.

El problema con esta combinación es que no tiene definidos todos los tipos de archivo en los que puede estar contenida una vista.

Yo por ejemplo estoy utilizando el plugin Rtex de Bruce Williams y tengo una vista que se llama etiquetas.rtex, en el controlador de impresiones tengo un método que se llama etiquetas, pero no puedo moverme fácilmente entre ambos búferes, abrirlos o crear las vistas.

Solución

Después de mirar un poquito el rails.el dentro de emacs-rails encontré la variable que contiene la lista de extensiones de archivo válidas para las plantillas erb: rails-template-list.

En el fichero ~/.emacs añadí las lineas:

 

 (add-to-list 'rails-templates-list "rtex")
 (add-to-list 'auto-mode-alist '("\\.rtex$" . tex-mode))

 

Y problema resuelto.

El fichero se abre con el modo mayor tex-mode así que añadí estas lineas para que también tuviera Ruby mode utilizando mmm-mode

 

  (add-hook 'tex-mode-hook
           (lambda ()
             (setq mmm-classes '(erb-code))
             (mmm-mode-on)))
 

Nota: Se asume que ya tienes emacs-rails, mmm-mode, ruby-mode, etc… Todo bien instalado y configurado.

Referencias

Manual de referencia de Elisp (En Inglés)

Emacs on Rails (En Inglés)

Posted in  | Tags , , , ,  | no comments

Stop_deletion_if_has_children: metaprogramación en Rails

Posted by joahking Mon, 15 Oct 2007 02:34:00 GMT

¿Has intentado definir en una asociación que se detenga el borrado del objeto si existen otros que lo refieran? Por ejemplo: Padre, tiene Hijos y Nietos, y no quieres que sea destruido si tiene algún hijo o nieto. La asociación has_many de Rails provee tres opciones para mantener la integridad de datos con :dependent => {:destroy | :delete_all | nullify}. Pero pasa por alto este caso tan común.

Por suerte Rails brinda posibilidades aun en el caso de que algo falte, y esta es una buena oportunidad para un plugin y usar un poco de metaprogramación. El plugin stop_deletion_if_has_children resuelve esto de manera sencilla:

La declaración de la restricción en el modelo es así:


# padre.rb
has many :hijos
has_many :nietos
stop_deletion_if_has_children :hijo, :nieto

(Nótese que el singular es importante en la declaración de cada parámetro pasado al plugin, según la implementación dada.)


# init.rb
require 'stop_deletion_if_has_children'
ActiveRecord::Base.send(:extend,Qvitta::StopDeletionIfHasChildren)

El plugin recorrerá la lista de objetos enlazados pasados como parámetros y chequeará que no existan records en el momento de destruir el objeto.

Esto es lo que hace:

# stop_deletion_if_has_children.rb
module Qvitta #:nodoc:
  module StopDeletionIfHasChildren #:nodoc:
    def stop_deletion_if_has_children(*children)
      define_method "children_check" do
        ret = true
        for child in children do
          if self.send("#{child}".to_s.pluralize).length > 0
            self.errors.add_to_base "Error: #{self.class} contiene #{child.to_s.camelize.pluralize}" 
            ret = false
          end
        end
        return ret
      end
      before_destroy :children_check
    end
  end
end

Las claves en el código anterior son:
  • *children: el asterisco le dice a Rails que el parámetro del método es un arreglo de valores.
  • before_destroy :children_check: que interpone el método children_check justo antes de la destrucción del objeto, y permite la destrucción return true o la detiene return false (se devuelven todos los mensajes de errores posibles en una sola pasada).
La metaprogramación viene dada por las lineas:
  • define_method "children_check" do: define al vuelo el método children_check.

Si como yo, tienes modelos con nombres en español la linea corta:

if self.send("#{child}".to_s.pluralize).length > 0

puede fallar pues pluralize lo hará en ingles. En ese caso necesitas hacer el chequeo desde los descendientes hacia el padre con esta más larga:

if "#{child}".to_s.camelize.constantize.send("find_all_by_#{self.class.to_s.downcase}_id",self.id).length > 0

Esta linea más larga nos da el pie didáctico para más de metaprogramación:
  • camelize: para convertir la cadena ‘hijo’ en ‘Hijo’.
  • contantize: que convierte la cadena ‘Hijo’ en la clase Hijo.
  • send: que invoca el método pasado como parámetro en la clase obtenida con el tratamiento anterior (en nuestro caso: Hijo.find_all_by_padre_id).

Pues eso es. A ver si con un poco de suerte (y tiempo) ponemos el repositorio de plugins de Qvitta accesible esta semana, y asi se podrá descargar.

Posted in  | Tags , , , ,  | no comments

Mi helper de hoy

Posted by calas Wed, 10 Oct 2007 08:30:00 GMT

Se me ha ocurrido la idea de ir posteando mis helpers a medida que los haga (siempre que sean útiles y reutilizables). Así que hoy va el primero, muy sencillo para ir poniendo en las vistas lo que me falta por hacer, por ejemplo en la vista pongo:

 
  <%= todo "Pasar el todo el estilo a un CSS" %>
 

El código en application_helper.rb la verdad que es una tontería:


  def todo(msg)
    "<strong>TODO: </strong>#{msg}<br />" 
  end

Simple verdad?

Se podría mejorar añadiendole clases y estilos (en linea; no en una hoja de estilos a no ser que sea una de pruebas que no va a pasar a producción, al fin y al cabo no debe quedar ninguna etiqueta de estas en una aplicación en producción)

ACTUALIZACIÓN:

Un pequeño cambio de estilo:


  def todo(msg)
    "<p style='color: red'><strong>TODO: </strong>#{msg}</p>" 
  end

El resultado:

TODO: Pasar el todo el estilo a un CSS

De esta manera tengo mis vistas con muchos recordatorios de las cosas que me faltan por hacer.

Posted in  | Tags , , ,  | no comments