Agenda simple en Rails usando ActiveScaffold

posteo a continuación un pequeño ejemplo para crear desde cero una pequeña y simple agenda en Rails utilizando ActiveScaffold. Debido a incompatibilidades entre Rails Edge y ActiveScaffold, recomiendo usar Rails 2.0.x para construir el ejemplo. Sugiero ver el demo online de ActiveScaffold para ver su funcionamiento.

Este ejemplo lo desarrollé con fines didácticos y no contempla validaciones, callbacks y utiliza unicamente asociaciones "uno a muchos".

Las tablas utilizadas son:

contacts

  • last_name
  • first_name

phones

  • phone
  • contact_id

phones

  • phone
  • contact_id

emails

  • email
  • contact_id

addresses

  • address
  • contact_id

creamos el proyecto

rails -d mysql contacts

creamos los modelos

  1. script/generate Model Contact lastName:string firstName:string
  2. script/generate Model Phone phone:string contact_id:integer
  3. script/generate Model Email email:string contact_id:integer
  4. script/generate Model Address address:string contact_id:integer

editamos /contact/config/database.yml

  1. development:
  2. adapter: mysql
  3. encoding: utf8
  4. database: contacts_development
  5. username: root
  6. password:
  7. host: localhost
  8.  
  9. test:
  10. adapter: mysql
  11. encoding: utf8
  12. database: contacts_test
  13. username: root
  14. password:
  15. host: localhost
  16.  
  17. production:
  18. adapter: mysql
  19. encoding: utf8
  20. database: contacts_production
  21. username: root
  22. password:
  23. host: localhost

invocamos a rake para que cree la db por nosotros, en base a la configuración de database.yml

  1. rake db:create


corremos las migrations con rake de modo de poblar nuestra db con las tablas que hemos definido en las migrate cuando creamos los modelos

  1. rake db:migrate


Ahora que tenemos las tablas en db, tenemos que definir las asociaciones en los modelos.

/contact/app/models/contact.rb

  1. class Contact < ActiveRecord::Base
  2. has_many :phones
  3. has_many :addresses
  4. has_many :emails
  5. end

Noten la pluralización en :phones, :addresses y :emails. Esto se hace así debido a que el método has_many apunta a "muchos". Si no se contempla esta convención, se obtiene un error de SQL al intentar construir una sentencia con una columna inexistente.

/contact/app/models/phone.rb

  1. class Phone < ActiveRecord::Base
  2. belongs_to :contact
  3. end

/contact/app/models/address.rb
  1. class Address < ActiveRecord::Base
  2. belongs_to :contact
  3. end

/contact/app/models/email.rb
  1. class Email < ActiveRecord::Base
  2. belongs_to :contact
  3. end


Con estas asociaciones definidas, que representan las relaciones entre tablas, podemos jugar un poco con los datos desde script/console, o bien avanzar hacia ActiveScaffold.

Una vez que tengamos algunos datos en la db, generaremos código para que nos facilite el trabajo
Para realizar scaffold tradicional de Rails se debe comenzar desde el principio, es decir, el scaffold generará por nosotros los
controladores, modelos y vistas.
En vez de eso utilizaremos ActiveScaffold, pero antes debemos generar los controladores asociados a los modelos.
Podemos descargar ActiveScaffold de aqui: http://activescaffold.googlecode.com/files/active_scaffold_1.1.1.zip

despues, descomprimimos la carpeta contenida en el zip en
/contact/vendor/plugin/

Ahora generamos los controllers

  1. script/generate controller Contact
  2. script/generate controller Phone
  3. script/generate controller Email
  4. script/generate controller Address


luego, en la carpeta /contacts/app/views/layouts creamos un archivo denominado application.html.erb que actuará como layout por defecto para toda la aplicación:

dentro de application.html.erb incluimos esto:

/contact/app/views/layouts/application.html.erb

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  2. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  3. <html xmlns="http://www.w3.org/1999/xhtml">
  4. <head>
  5. <meta http-equiv="Content-Type" content="text/html;.
  6. charset=iso-8859-1" />
  7. <meta http-equiv="Content-Language" content="en-us" />
  8. <title>Agenda simple</title>
  9.  
  10. <div class="codeblock"><code><font color="#000000"><font color="#0000BB"><?php<br /></font><font color="#007700">= </font><font color="#0000BB">javascript_include_tag </font><font color="#007700">:</font><font color="#0000BB">defaults <br />?></font></font></code></div>
  11. <div class="codeblock"><code><font color="#000000"><font color="#0000BB"><?php<br /></font><font color="#007700">= </font><font color="#0000BB">active_scaffold_includes <br />?></font></font></code></div>
  12. </head>
  13. <body id="library">
  14. <
  15. <h1>Agenda simple</h1>
  16. <h3>powered by Ruby on Rails</h3>
  17.  
  18. <div id="content">
  19. <div class="codeblock"><code><font color="#000000"><font color="#0000BB"><?php<br /></font><font color="#007700">= </font><font color="#0000BB">yield </font><font color="#007700">-<br /></font><font color="#0000BB">?></font></font></code></div>
  20. </div>
  21. <div id="sidebar"></div>
  22.  
  23. </body>
  24. </html>

En este código, la magia la generan estas dos lineas, que incluyen a ActiveScaffold al proyecto:

<%= javascript_include_tag :defaults %>
<%= active_scaffold_includes %>

y en el contact_controller.rb agregamos

active_scaffold :<your_model_name>

en nuestro caso

/contact/app/models/contact.rb

  1. class ContactController < ApplicationController
  2. active_scaffold :contact
  3. end


corremos script/server y en el browser apuntamos a http://localhost:3000/contact.
Si no tuvimos problemas deberíamos poder ver un lindo CRUD con muchas funcionalidades. Si en cambio vemos unicamente links sin ningún formato, entonces se debe a que el documento creado para nuestro layout tiene algún problema, como ser la extensión, etc.

Si llegásemos a ver que en vez de obtener un dato a partir de las asociaciones, apareciese algo similar a esto: #
entonces debemos editar el modelo afectado y crear un método que establezca el valor correctamente. Tomemos por ejemplo el modelo Address:

  1. class Address < ActiveRecord::Base
  2. belongs_to :contact
  3. def to_label
  4. "#{address}"
  5. end
  6. end

donde "#{address}" contiene a address, atributo del modelo Address. Lo que aquí se realiza es una evaluación de lo contenido en las comillas dobles. En dicha evaluación, Ruby realiza una interpolación debido a que encuentra la orden para hacerla (#{}) y devuelve el valor contenido en address en esa instancia.
Grabamos y refrescamos en browser y el problema habrá desaparecido. Cuando ActiveScaffold llama al método to_label para el modelo evaluado, encuentra este override y lo ejecuta, retornando el valor que deseamos.
La misma definición la deberíamos realizar en todos los otros modelos afectados.

En otro post próximo exploraremos las posibilidades parametrización de ActiveScaffold. Espero que les sea de utilidad.