October 8th, 2009 por CaDs

Cualquiera que haya programado en Rails conocerá algo de YAML ya que se usa, entre muchas otras cosas, para la configuración de la base de datos.

YAML en breve, es un lenguaje para la serialización de datos bastante fácil de entender y cómodo de usar (podéis ver ejemplos aquí).

No sería genial poder definir nuestros archivos de configuración usando YAML como los properties en Java?

Pues es muy sencillo.

Paso1: Crear el archivo de configuración dentro de la carpeta /config. Para este ejemplo yo he creado un archivo llamado config.yml con el siguiente contenido:

/config/config.yml


:attribute1:
  :param1: my config stuff
  :param2: more config stuff
  :param3: yet another param

:attribute2:
  :param1: more config 
  :param2: blahblahbla
  :param3: another thing

Paso 2: Cargar el archivo de configuración al levantar nuestra app. Para ello crearemos un inicializador dentro de la carpeta /initializers.
En mi caso yo he creado un archivo llamado config_loader.rb con el siguiente contenido:

/initializers/config_loader.rb


config_file = File.read(RAILS_ROOT + "/config/config.yml")
CONFIG = YAML.load(config_file)

Y eso es todo, tras reiniciar vuestra aplicación podréis acceder a vuestros parámetros dentro de vuestro código llamándolos con: CONFIG[:attribute1][:param1]

Enjoy! :)

  • Share/Bookmark
August 6th, 2009 por CaDs

Instalar Ruby y Rails en general suele ser un proceso sencillo e indoloro, pero instalar el mysql gem de vez en cuando da algún que otro dolor de cabeza.
En concreto si usan Leopard tras instalar/actualizar correctamente ruby y rails (que vienen por defecto en el sistema) al hacer el clásico sudo gem install mysql nos dará un bonito error.

La solución a esto es modificar el comando ligeramente para pasarle algunos parámetros adicionales. De manera que quede algo así:

sudo env ARCHFLAGS=”-arch i386″ gem install mysql — –with-mysql-config=/path/to/mysql_config

Siendo /path/to/mysql_config la ruta donde resida la configuración de vuestro mysql (por defecto/usr/local/mysql/bin/mysql_config )

Enjoy!

  • Share/Bookmark
June 23rd, 2009 por Jorge Yau

Jacob Nielsen discute en uno de sus rants, si debemos esconder o no las contraseñas en los formularios de registro, etc. Yo en lo personal prefiero tener los campos escondidos, pero entiendo el porque a algunas personas les puede ser dificil utilizarlos así.

Por mi parte, lo que he estado haciendo desde hace un rato ya, es agregar un botón de esconder y mostrar el password, a continuiación el código Javascript para lograr esto. Y si te preguntas porque no utilizo mejor jQuery, la razón es que hay problemas de compatibilidad en diferentes browsers, jQuery no es capaz de cambiar el “type” del campo, de “password” a “text” y viceversa.

Field:


<form name="formname">
<label>Password <a href="javascript:void(0);" onclick='this.innerHTML=(show_hide_password(document.formname["password"])=="text"?"Hide":"Show");'>Show</a></label>
<input type="password" name="password" id="password" />
</form>

Javascript:


function show_hide_password(elem) {
  var eData={v:elem.value, t:elem.type, s:elem.size, n:elem.name, c:elem.className, i:elem.id},
  newElem, newType=(eData.t=='password'?'text':'password');
  try {
    newElem=document.createElement("<input type='"+newType+"' name='"+eData.n+"' value='"+eData.v+"' size='"+eData.s+"' class='"+eData.c+"' id='"+eData.i+"'>");
  }
  catch(e) {
    newElem=document.createElement('input');
    newElem.name=eData.n;
    newElem.type=newType;
    newElem.size=eData.s;
    newElem.value=eData.v;
    newElem.className=eData.c;
    newElem.id=eData.i;
  }
  elem.parentNode.replaceChild(newElem, elem);
  return newElem.type;
}

Parte del código es mío y honestamente no recuerdo donde conseguí parte de la función así que no puedo darle el credito apropiado.

  • Share/Bookmark
May 19th, 2009 por Jorge Yau

Twitter Friendly Links es un plugin para Wordpress que te permite crear enlaces cortos para las entradas en tu blog, sin la necesidad de servicios como TinyURL o bit.ly, la desventaja de utilizar estos servicios es que al propagar estos enlaces, no estamos propagando enlaces a nuestro blog, si no al servicio, los buscadores no reconocen estos enlaces como enlaces a nuestro blog, por ende el ranking no sube y además perdemos la forma de saber de donde provino el visitante, ya que en nuestras estadísticas aparecen como provenientes de X servicio de enlaces en vez del sitio que nos está enlazando.

El uso de este plugin es sencillo, tan solo instala y activa el plugin, inmediatamente se generaran enlaces para tu blog, por ejemplo: http://tequilog.com/123

A continuación un vídeo demostrando el uso del plugin (en inglés)

Twitter Friendly Links por Konstantin Kovshenin

  • Share/Bookmark
March 2nd, 2009 por CaDs

XMPP es uno de los protocolos de comunicación más utilizados hoy en día. Probablemente lo conozcáis como Jabber, y es un protocolo estándar para la mensajería instantánea.
XMPP está optimizado para la comunicación entre humanos, pero hay una creciente tendencia a utilizarlo como protocolo de comunicación entre máquinas.
Habitualmente, cuando tenemos un sistema distribuido necesitamos comunicar los diversos eventos que nuestra lógica genera entre diversas máquinas. Tecnologías como JMS, en el caso de Java, proporcionan un framework orientado a mensajes para integrar con tus aplicaciones.
Pero XMPP ofrece otra manera de trabajar un tanto distinta y un poco más estándar.
Para el ejemplo de este post usaré Java como lenguaje de programación y Smack, una librería de la gente de Jive Software (pueden encontrarse librerías similares como XMPP4R para rails, o XMPPhp para php).

Lo primero que necesitamos para enviar y recibir mensajes es un servidor XMPP. Openfire es un buen ejemplo de lo que debe ser un servidor XMPP. Ligero, fácil de configurar y rápido de instalar. Por supuesto podéis elegir cualquier otro servidor, a fin de cuentas lo único que nos interesa es que despache los mensajes.
Configurar Openfire es relativamente sencillo, así que me saltaré esa parte. De todas formas podéis ver la guía de instalación aquí.

Una vez instalado y configurado nuestro servidor es cuestión de comenzar a enviar y a procesar mensajes.
Supongamos que hemos creado dos cuentas para nuestras máquinas. Una llamada cliente y otra llamada servidor (original no?).
Así pues podemos crear nuestra aplicación cliente y generar una clase que se encargue de establecer la conexión con el servidor.
Usando la clase org.jivesoftware.smack.XMPPConnection de Smack es algo como:


XMPPConnector connector = new XMPPConnector("url.miserver.com") ;
connector.login("cliente","password");

De este modo tenemos a nuestra máquina cliente “logueada” en nuestro servidor Jabber.
Siguiendo el mismo procedimiento podemos “loguear” nuestra máquina servidor en otra clase que se encargue de levantar la conexión para la aplicación del servidor.


XMPPConnector connector = new XMPPConnector("url.miserver.com") ;
connector.login("servidor","password");

Teniendo ambas máquinas levantadas es hora de que nuestro cliente envíe el primer mensaje al servidor. Para ello usaremos la clase org.jivesoftware.smack.packet.Message


Message message = new Message() ;
message.setTo("servidor@url.miserver.com") ;
message.setSubject("message test") ;
message.setBody("Hello world") ;
connection.sendPacket(message) ;

Y eso es todo, nuestro conector, el que hemos creado previamente, se encarga del resto.
Esto como veis no tiene utilidad alguna. Sería distinto en el caso de que estuvierais escribiendo un código para que vuestra aplicación os avisara de determinados eventos, pero en nuestro caso, enviar un mensaje del tipo “Hello World” al servidor no tendrá mucho efecto.

La cosa cambia cuando configuramos un listener que se encargue de procesar ciertos mensajes. Un listener es algo así como un filtro que, siempre que se cumplan las condiciones que nosotros escribamos, actuará sobre el mensaje recibido.
Para ello usaremos la clase org.jivesoftware.smack.PacketFilter para crear un filtro


PacketFilter filter = new AndFilter(new PacketTypeFilter(Message.class), new FromContainsFilter("cliente@url.miserver.com"));

En este caso estamos creando un filtro que valida que lo recibido sea un objeto de la clase Message y que provenga de “cliente@url.miserver.com”.
Y ahora registramos nuestro listener usando la clase org.jivesoftware.smack.PacketListener


PacketListener myListener = new PacketListener() {
        public void processPacket(Packet packet) {
            if (packet instanceof Message) {
                Message msg = (Message) packet;
                // Process message
                DoWhatYouWantWithTheMessage();
                ...
            }
        }
    };
connector.addPacketListener(myListener, filter);

En este ejemplo se crea un nuevo PacketListener y se define el método processPacket que es el encargado de ejecutar la lógica de nuestra aplicación cuando se reciba un mensaje que cumpla las condiciones definidas en nuestro filtro.
Como esto ha quedado algo abstracto pongamos un ejemplo algo más práctico.
Supongamos que tenemos una clase llamada BeanUsuario que tiene como atributos un String email y un String password.
Ahora vamos a enviar un mensaje a nuestro servidor, donde reside la base de datos de nuestra aplicación, para que almacene un usuario. Para ello usaremos el método setProperty que nos permite enviar cualquier objeto Java (que sea serializable) dentro del mensaje.


//Código del Cliente
//Creamos el conector
XMPPConnector connector = new XMPPConnector("url.miserver.com") ;
//Nos logueamos como cliente
connector.login("cliente","password");
//Creamos el bean de usuario
BeanUsuario bean = new BeanUsuario("carlos@hola.com", "mipassword") ;
//Creamos un nuevo mensaje
Message message = new Message() ;
//Ponemos como destinatario nuestro servidor
message.setTo("servidor@url.miserver.com") ;
//Metemos el bean de nuestro usuario en el mensaje
message.setProperty("ususario", bean) ;
//Enviamos el mensaje
connector.sendPacket(message) ;

//Código del Servidor
//Creamos la conexión
XMPPConnector connector = new XMPPConnector("url.miserver.com") ;
//Nos logueamos como servidor
connector.login("servidor","password");
//Creamos un filtro para recibir los mensajes que vengan del cliente
PacketFilter filter = new AndFilter(new PacketTypeFilter(Message.class), new FromContainsFilter("cliente@url.miserver.com"));
//Creamos el listener y el método donde se procesará el mensaje
PacketListener myListener = new PacketListener() {
        public void processPacket(Packet packet) {
            if (packet instanceof Message) {
                Message msg = (Message) packet;
                //Obtenemos el bean del usuario
                BeanUsuario bean = (BeanUsuario)msg.getProperty("usuario") ;
               //Almacenamos el usuario en nuestra base de datos
                gestorBaseDatos.store(bean) ;
            }
        }
    }; 
// Registramos el listener.
connector.addPacketListener(myListener, filter);

Y listo, de esta forma tenemos nuestra mini aplicación distribuida perfectamente comunicada y coordinada.
Claro que en un entorno del Mundo Real ™ la cosa sería algo más compleja. Deberíamos enviar un mensaje de confirmación al usuario, tal vez meter esto dentro de un thread… en fin las posibilidades son muchas. Pero la base es esta.

Qué os parece? pensáis que XMPP puede terminar imponiéndose como estándar de comunicación entre máquinas? Qué soléis usar para comunicar vuestras aplicaciones distribuídas?

  • Share/Bookmark
December 12th, 2008 por CaDs

Hacer una búsqueda dinámica para que vayan apareciendo los resultados a medida que escribimos en el formulario es relativamente sencillo con rails.
Para ello sólo necesitamos los siguientes elementos:
Primero de todo necesitamos cargar los elementos ‘ajaxianos’ que vienen con rails. Para ello basta con incluir:


<%=  javascript_include_tag :defauts %>

Generalmente es una buena práctica incluir este tag en el layout general, pero eso depende de cada uno.

Vista:


<% form_tag 'search' do %>  
<div class="table_title">
    <table>
      <tr>
        <td>Buscar:</td>
        <td><%= text_field_tag("query", params[:query], :autocomplete => "off" ) %></td>
      </tr>
    </table>
      
</div>
<% end %>
    
    <%= observe_field 'query',  :frequency => 2,
                        :update => search_results",                
                :url => {:controller => "friends", :action => "search"},
                 :with => "query" %>

<div id="search_results">
</div>

Con esto definimos un campo de búsqueda llamado query donde iremos haciendo las búsquedas.
Adicionalmente con observe_field indicamos que debemos realizar cada cierto tiempo una búsqueda con los parámetros de query usando la acción search del controlador friends.
Y también definimos un div llamado search_results, inicialmente vacío, que será actualizado con los resultados de esa búsqueda.

Controller:


def search    
    session[:query] = params[:query].strip if params[:query]

    if session[:query] and request.xhr?
      @friends = Friend.all(:conditions => ["first_name LIKE ?", "%#{session[:query]}%"], :order => "first_name ASC")     
      render :partial => "search_results", :layout => false, :locals => {:searchresults => @friends} 
    end
  end

Donde hacemos una búsqueda con los parámetros que hemos obtenido desde nuestra vista y la presentamos en un partial llamado search_results pasándole los resultados de la búsqueda.
El código de este partial puede ser a gusto de cada uno, simplemente debe presentar los resultados.
Un ejemplo sería:


<% if searchresults.length == 0 %>
        <p>No se han encontrado resultados</p>
<% elsif params[:query] == "" %>
        <p>No hay términos de búsqueda</p>
<% else %>
<div class ="search_results">
  <strong>Amigos Encontrados:</strong>
  <table>
  <% for friend in searchresults %>     
    <tr>
      <td>
              <%=friend.last_name%>, <%=friend.first_name%>  
      </td>
    </tr>
  <% end %>
  </table>
</div>
<% end %>

Modelo:
Un modelo básico que nos serviría para este ejemplo sería algo así.


class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table (:friends,:options => 'DEFAULT CHARSET=UTF8') do |t|
      t.column :first_name, :string
      t.column :last_name, :string
      t.timestamps
    end
  end

  def self.down
    drop_table :friends
  end
end


Y eso sería todo, ya tenemos una búsqueda dinámica en menos de 5 minutos.

Enjoy

  • Share/Bookmark
December 10th, 2008 por CaDs

En general Test suele ser sinónimo de aburrimiento, al menos en lo que a la opinión popular se refiere. Conozco pocos programadores que realmente disfruten escribiendo tests o que lo hagan sin una petición explícita ya sea por parte de sus jefes, el departamento de calidad… etc.

En general creo que existe el concepto de que uno programa su código y debe haber otra persona que se encargue de hacer el testing y reportarnos los bugs.
Si bien el concepto de Test y QA (Quality Assurance) están relacionados, lo uno no debiera reemplazar a lo otro.

Sobre si se debe escribir tests o no, o qué técnica es la verdadera y única (TDD, BDD, ?DD….) se han escrito infinidad de páginas, y la verdad no me interesa entrar en polémicas, así que os voy a explicar desde mi punto de vista por qué usar tests es conveniente para cualquier programador.

Un test se podría definir como una inversión a largo plazo, es cierto que inicialmente consume parte del tiempo (que como programadores estamos deseando invertir en desarrollar nuestra lógica y hacer una bonita interfaz) pero a lo largo de la vida del proyecto uno disfruta de los beneficios.

Pero dejémonos de rodeos y teoría, vamos a entrar en materia

Para este ejemplo usaré rspec, un framework de testing para Ruby on Rails.
Sus creadores lo definen como un BDD (Behavior Driven Development) framework para ruby, y sin entrar en mayores detalles sobre lo que representa el BDD diré que la filosofía detrás de esta metodología de testing consiste en centrarnos más en el cómo debe comportarse nuestra aplicación sin necesidad de preocuparnos de los detalles de implementación.

Toda esta parafernalia técnica está muy bien, pero para mí simplemente significa que escribir tests deja de ser una tarea aburrida y pasan a formar parte fundamental del código y sobre todo de la documentación.
Y esto es importante Test = Documentación, porque cuando podemos presentar nuestros tests como la documentación fiable y actualizada de nuestro código estamos matando dos pájaros de un tiro (y nos ahorramos la pesada tarea de hacer esos documentos en word con screenshots y frases cortas del tipo “X hace Y”.

Nota: He usado rspec como ejemplo y como framework para mis proyectos porque me gusta, eso no quiere decir que no podamos obtener los mismos resultados usando otros framework de testing.
Ejemplos perfectamente válidos son JUnit, RUnit, PyUnit, PHPUnit … y un largo etcétera. Prácticamente cualquier lenguaje de programación dispone de varios frameworks para testing que podréis usar según vuestras preferencias.
Igualmente da lo mismo qué filosofía de testing decidáis usar, TDD, BDD, WhateverDD, un test simplemente consiste en tener la seguridad de que independientemente de los cambios que se hagan en el código en las diversas refactorizaciones o ampliaciones del mismo, lo que funcionaba antes sigue funcionando.

Hechas las aclaraciones pertinentes…

Instalar rspec es relativamente sencillo, instalamos el gem:

[sudo] gem install rspec-rails

Y en nuestra aplicación corremos el script:

ruby script/generate rspec

Este script nos creará una nueva carpeta en nuestro proyecto llamada spec…

… con varias subcarpetas.
Para los que estéis acostumbrados a Rails y a MVC en general veréis que existe una carpeta para los controladores, otra para los modelos y otra para las vistas. Estas carpetas contendrán los tests (o specs) que escribiremos para cada uno de esos elementos.
Adicionalmente tenemos la carpeta de helpers donde podremos escribir funciones auxiliares que vayamos a usar en nuestos tests y la carpeta de fixtures, donde podemos definir algo así como los mock objects que usaremos.

Bien, cómo creamos un test? Fácil. Rspec pone a nuestra disposición varios scripts que podemos usar integrados con Rails.
Por ejemplo, al crear un controlador habitualmente escribimos el comando:

./script/generate controller user

(siendo user un nombre cualquiera)

Bien, rspec nos permite hacer lo mismo pero con un sutil cambio:

./script/generate rspec_controller user

Este comando, además de generar nuestro habitual /app/controllers/user_controller.rb


class UserController < ApplicationController
end

creará un archivo en la carpeta /spec/controllers llamado user_controller_spec.rb donde podremos escribir nuestros specs.


require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe UserController do

  #Delete this example and add some real ones
  it "should use UserController" do
    controller.should be_an_instance_of(UserController)
  end

end

Qué contiene este archivo? primero carga el spec_helper para hacer disponibles las funciones auxiliares que hayamos definido y por otro lado comienza a describir nuestro controlador con el comando describe.

Describe sigue la habitual estructura de bloques de RoR conformando un bloque…


describe UserController do
#Specs varios...
end

…donde comenzaremos a definir qué queremos que haga nuestro controlador.
Rspec nos genera un ejemplo (que borraremos en breve) que si bien no nos aporta gran cosa, sí nos da una pequeña idea de como escribir nuestros specs:


it "should descriptión de la acción" do
#Comportamiento deseado
end

En este punto estamos preparados para comenzar a codificar.
Los puristas del TDD dicen que “todo comienza con un test que falla”. Así que siguiendo este consejo vamos a escribir nuestro primer test sin haber escrito todavía una sola línea de código.
Borramos el test que nos viene por defecto y escribimos nuestro primer test, algo básico que nos sirva como ejemplo podría ser:


require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe UserController do

  it "should render the register page" do
    get :register
    response.should render_template 'register'
  end

end


Este sencillo spec puede entenderlo cualquier persona, aun sin conocer nada de programación (vale, esa persona debe conocer algo de inglés) y nos sirve para definir el comportamiento que deseamos para nuestra aplicación.

En este caso es trivial, estamos accediendo con un método get a la acción register y esperamos que la respuesta muestre la página de registro dentro del controlador de usuarios.
Así que en cierto modo estamos definiendo para nuestra aplicación el comportamiento de un usuario. Si un usuario hace click en el link de registrar nuestra aplicación navegará a la página de registro donde el usuario puede meter sus datos y registrarse.

Así pues hemos escrito nuestro primer test y estamos listos para ver si funciona.
Para ello rspec nos proporciona algunos scripts que podemos usar:

./script/spec path/to/directory/with/specs

correrá todos los spec que existan en un determinado directorio, y

./script/spec path/to/individual_spec.rb

correrá un determinado spec.

En nuestro caso escribiremos:

./script/spec spec/controllers/user_controller_spec.rb 

Y la respuesta que obtendremos probablemente será algo como:
ActionController::UnknownAction in ‘UserController should render the register page’
No action responded to register. Actions:
…..
Finished in 0.097963 seconds

1 example, 1 failure

Tenemos nuestro primer fallo!, así que según los puristas del TDD vamos por el buen camino.
Por lo que leemos al parecer se está quejando de que no reconoce una acción llamada register, así que vamos a definirla.


def register
#Código para registrar un usuario, vacío por el momento    
end

Volvemos a correr nuestro test

./script/spec spec/controllers/user_controller_spec.rb 

y ahora la respuesta debiera ser algo como:
Finished in 0.223996 seconds

1 example, 0 failures

Funciona!, a partir de ahora hemos definido que al acceder a la acción registrar usuarios siempre debemos presentar la página de registro.

Este ejemplo, aunque parezca trivial y una tontería, nos sirve para definir cómo trabajar con tests.
Comenzamos escribiendo algunas especificaciones para nuestro código y luego nos aseguramos de que el código cumpla con lo definido.

Por qué no al revés? Por qué no escribir primero el código y luego los test?
Hay diversas razones que nos animan a hacerlo de esta manera.

    Por un lado en muchas ocasiones tenemos ideas, ya sean propias o de nuestros clientes, que se desean incorporar a un proyecto. Estas ideas, requisitos, funcionalidades, etc… especificaciones a fin de cuentas (o specs para abreviar) definen el comportamiento de la aplicación y deben ser escritas independientemente de la implementación que se vaya a realizar posteriormente. (otra cosa es poder hacerlo o no, pero eso no nos importa en este momento)
    Codificando inicialmente y posteriormente escribiendo los tests de alguna manera estamos imponiendo nuestra implementación sobre la especificación, pudiendo llegar a “corromperla” en cierto modo.
    Por otro lado en ocasiones simplemente deseamos anotar cosas que deseamos que nuestra aplicación haga, sin tiempo para ponernos a codificar. Tal vez estemos redactando un documento de especificación y deseamos presentar lo que hace nuestra aplicación y obtener el feedback de nuestro cliente antes de emplear recursos.

En fin, las razones son varias. No me considero un evangelista, de esos hay muchos por ahí pululando. Si os interesa realmente el tema buscad sobre TDD o BDD en google y probablemente encontréis más de lo que podáis digerir.

Siguiendo con nuestro ejemplo, alguien podría decir que el test es un poco ciego. Estamos esperando que se presente la página de registro pero en realidad no tenemos página de registro y sin embargo el test funciona.
Esto nos lleva a dos conceptos interesantes.

    Convention over configuration: Rails sabe que debe existir un template con el mismo nombre que la acción que define, así que siguiendo la convención, rspec sabe que en algún momento debe existir un template con ese nombre, así que no se preocupa de si está presente o no.
    Rspec es flexible: La cantidad de opciones que nos permite rspec es abrumadora, de hecho es una de las causas por las que tiene una curva de aprendizaje no tan suave como a muchos nos gustaría. Sus programadores tuvieron en cuenta las diversas capas del MVC a la hora del diseño y por tanto permite la abstracción en cuanto a la capa de presentación.

No obstante si ninguna de estos conceptos te convence, no hay problema, podemos usar un comando para que rspec integre las restricciones de las vistas dentro de los tests de nuestro controlador, basta con escribir integrate_views.


require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe UserController do
  integrate_views
  
  it "should render the register page" do
    get :register
    response.should render_template 'register'
  end

end


Si volvemos a correr nuestro test ahora debieramos obtener algo como:

1)
ActionView::MissingTemplate in ‘UserController should render the register page’
Missing template user/register.erb in view path /Users/cads/post/app/views:
….
Finished in 0.101716 seconds

1 example, 1 failure

Ahora efectivamente se queja de que le falta la página de registro, así que debemos proporcionársela para que se quede contento.
Un ejemplo rápido podría ser:
app/views/user/register.html.erb


<div id="title">
  <h2>User Registration Page</h2>
</div>
<div id="form_fields">
  Username: <%= text_field_tag 'user_name', nil, :size => 20 %>
  <br/>
  Password: <%= password_field_tag 'password', nil, :size => 20 %>
</div>
<div id="buttons">
  <%= submit_tag "Register" %>
</div>

Si corremos ahora nuestro spec vemos que ya está contento:

Finished in 0.096693 seconds

1 example, 0 failures

Rspec proporciona una potente herramienta para testear interfaces, permitiéndonos validar incluso la existencia de tags en las vistas.


it "should render the register page" do
    get :register
    response.should render_template 'register'
    #Vamos a checkear que existan los 3 divs principales
    response.should have_tag("div[id=title]")
    response.should have_tag("div[id=form_fields]")
    response.should have_tag("div[id=buttons]")
end

Pero esto nos desvía, para más información sobre rspec podéis visitar su página de documentación (buena suerte)

El si se debe incluir la validación de interfaces dentro de los tests de un controlador es motivo de polémica y discusiones.
Podemos crear specs asociados a vistas y dedicarnos por completo a testear cada una de las vistas de nuestra aplicación.
Para ello bastaría con crear el archivo spec/views/user/template_spec.rb con un formato similar al anterior.

Nota: Si al generar nuestro rspec_controller indicamos las acciones del controlador rspec generará automáticamente specs para cada una de las vistas asociadas a las acciones.
Así pues, si hubiéramos generado nuestro user_controller de esta manera:

./script/generate rspec_controller user register

rspec habría generado: spec/views/user/register.html.erb_spec.rb


describe "/user/register do
  before(:each) do
    render 'user/register'
  end
  
  #Delete this example and add some real ones or delete this file
  it "should tell you where to find the file" do
    response.should have_tag('p', %r[Find me in app/views/user/register])
  end
end

Pero como digo, no quiero complicaros la existencia con las ramificaciones de rspec.

Retomando nuestro spec, vamos a generar otra descripción no tan trivial:


it "should register a user" do
    post :register, :user => {:user_name => 'Anakin', :password => 'yoda'}
    #Si lo ha creado bien debe redireccionarnos a la pagina del profile
    response.should redirect_to :action => 'profile'
    #El usuario Anakin debe existir y estar almacenado en nuesta base de datos
    User.find_by_user_name('Anakin').should_not be_nil
end

Ahora tenemos dos specs que podemos correr, con el resultado:

.F

1)
‘UserController should register a user’ FAILED
expected redirect to {:action=>”profile”}, got no redirect
./spec/controllers/user_controller_spec.rb:18:
./script/spec:5:

Finished in 0.099972 seconds

2 examples, 1 failure

Nuevamente tenemos un test que falla, y tenemos una nueva funcionalidad y comportamiento que nos interesa, así que ahora podemos implementarla:


class UserController < ApplicationController
  
  def register
    #Registrará un usuario
    if request.post? and params[:user]
      user = User.new(params[:user])
      if user.save
        redirect_to :action => 'profile'
      end
    end
  end
  
  def profile
    #Traerá el perfil del usuario
  end
  
end


Ahora el código debiera cumplir con lo que pide el test, así que volvemos a correr nuestro spec y…

.F

1)
NameError in ‘UserController should register a user’
uninitialized constant UserController::User
….
Finished in 0.102123 seconds

2 examples, 1 failure

Cierto! se nos había olvidado crear el modelo User.

Como vemos, a partir de un simple test vamos dando forma a nuestra aplicación, escribiendo código que cubra las necesidades de nuestras especificaciones y asegurándonos de esta manera que todo funcione según lo definido.
Así pues generamos nuestro modelo:

./script/generate rspec_model user

Definimos los atributos:
db/migrate/create_users.rb


class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.column :user_name, :string
      t.column :password, :string
      t.timestamps
    end
  end

  def self.down
    drop_table :users
  end
end

migramos


rake db:migrate

y


rake db:test:prepare

y volvemos a correr nuestro spec:

..

Finished in 0.109147 seconds

2 examples, 0 failures

Y listo, nuevamente todo funciona nuevamente.

Si os habéis fijado a la hora de generar el modelo he usado un rspec_model.
Al igual que con los controladores y las vistas, rspec nos permite crear tests para nuestros modelos y al igual que antes podemos encontrar el spec generado en spec/models/user_spec.rb


require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe User do
  before(:each) do
    @valid_attributes = {
    }
  end

  it "should create a new instance given valid attributes" do
    User.create!(@valid_attributes)
  end
end

El cual, incialmente es trivial como el resto de sus compañeros, pero nos permite validar el correcto comportamiento de los modelos.
Por ejemplo si quisieramos asegurarnos de que debe existir un user_name y un password para nuestro modelo user podemos escribir:


class User < ActiveRecord::Base
  validates_presence_of :user_name, :password
end

Lo cual nos asegura de que nuestros usuarios para ser válidos deben tener un nombre de usuario y un password.
Si corremos ahora nuestro user_spec.rb debiera fallar:

./script/spec spec/models/user_spec.rb
F

1)
ActiveRecord::RecordInvalid in ‘User should create a new instance given valid attributes’
Validation failed: Password can’t be blank, User name can’t be blank

Finished in 0.158742 seconds

1 example, 1 failure

Ahora requerimos que nuestros usuarios tengan ciertos atributos, asi que podemos requerirlos en el test y validar que todo siga correctamente.
Para ello basta con definir dentro de @valid_attributes los atributos que requiramos como obligatorios:


require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe User do
  before(:each) do
    @valid_attributes = { :user_name => 'Sheldom', :password => 'Leonard'
    }
  end

  it "should create a new instance given valid attributes" do
    User.create!(@valid_attributes)
  end
end

Si ahora volvemos a correr nuestro test veremos que…

.

Finished in 0.1016 seconds

1 example, 0 failures

Todo vuelve a funcionar.

Con esto termino este laaargo post.
La idea es que partiendo de tests podemos desarrollar el resto de la aplicación de una manera ordenada y con la seguridad de que si en el futuro, debemos refactorizar nuestro código (algo bastante habitual) y teniendo tests no “contaminados” con los detalles de la implementación, podemos estar relativamente seguros de que todo está bien sin tener que volver a navegar manualmente por todas y cada una de las funcionalidades de nuestra aplicación.

Nota: Correr los tests a mano cada vez que uno hace un cambio es bastante pesado y cansino, así que hay diversas herramientas que se encargan de hacerlo por nosotros.
Yo en concreto uso rspactor que se encarga de correr automáticamente los specs relacionados cada vez que se guardo un cambio en algun archivo y genera notificaciones con growl.
Para los que no uséis mac sé que hay versiones similares que corren en windows o en linux, pero ahora mismo no tengo el enlace por aquí.

Enjoy

  • Share/Bookmark
December 8th, 2008 por Antonio Touriño

Esta charla la dieron Adam Wiggins (creador de RESTClient) y Blake Mizerany (creador de Sinatra), ambos desarrolladores en Heroku.



El audio está en inglés.

  • Share/Bookmark
November 17th, 2008 por Jorge Yau

Hay varias formas de publicar tus aplicaciones en un ambiente de producción, esto se te puede complicar si vienes de un ambiente de PHP y Apache, estas acostumbrado a solo crear un Virtual Host apuntando a un directorio donde tienes todos los archivos de la aplicación.

Con Ruby on Rails la cosa es un poco más complicada, existes diversas opciones para remplazar Apache como tu servidor web, mi recomendación es usar Nginx+mongrel, el cual es un servidor que usa una décima parte de los recursos que normalmente Apache utiliza, el proceso es más complicado y requiere de un nivel mas alto de conocimiento sobre el funcionamiento de servidores, recursos, etc.

A continuación, la forma más rápida y sencilla de publicar una aplicación en un ambiente de producción.

Necesitas contar con Apache2, si no lo tienes instalado solo escribe lo siguiente en tu línea de comando:

$ sudo apt-get install apache2 apache2.2-common apache2-mpm-prefork apache2-prefork-dev apache2-utils libexpat1 ssl-cert libapr1 libaprutil1 libmagic1 libpcre3 libpq5 openssl

Luego de tener Apache2 procede a installar Passenger con el siguiente comando:

$ sudo gem install passenger
$ passenger-install-apache2-module

Luego solo es cosa de configurar un nuevo Virtual Host en la configuración de tu Apache, ya sea en httpd.conf o como un sitio en /etc/apache2/sites-enabled/, de la misma forma en la que configuras un sitio normal.

<VirtualHost *:80>
    ServerName www.tudominio.com
    DocumentRoot /var/www/tuaplicacion/public
</VirtualHost>

Una vez configurado, reinicia tu Apache, y podrás ver tu aplicación en www.tudominio.com

Si requieres más información, visita el sitio de Passenger y lee la documentación

  • Share/Bookmark
November 17th, 2008 por CaDs

Buscando sitios para hostear mis aplicaciones me he dado cuenta de que en algunos sitios ofrecen una imagen de Ubuntu para administrar como deseemos y en el caso de querer que nos dejen pre-instalado ruby y rails cobran un dinero adicional.
Curiosamente instalar Ruby y Rails en una Instalación de Ubuntu en blanco es relativamente sencillo así que les vamos a ahorrar algunos dólares.

1. Instalamos Ruby:

sudo apt-get install ruby-full build-essential

2.Instalamos Ruby Gems:

wget http://rubyforge.org/frs/download.php/38646/rubygems-1.2.0.tgz
tar xzvf rubygems-1.2.0.tgz
cd rubygems-1.2.0
sudo ruby setup.rb
sudo ln -s /usr/bin/gem1.8 /usr/bin/gem
sudo gem update --system

3. Teniendo Gems Instalado podemos usarlas para Instalar Rails…

sudo gem install rails

4. … y Mongrel:

sudo gem install mongrel

Listo, ya tenéis la base para correr vuestras aplicaciones RoR en una nueva Instalación de Ubuntu :)

  • Share/Bookmark