Desarrollo rápido de aplicaciones con Hobo sobre Ruby on Rails

Hola de nuevo.

Últimamente he estado trastando con Hobo, un generador de aplicaciones web sobre Ruby on Rails, y estoy muy contento con los resultados.  Iba a escribir a unos amigos para explicarles cómo usarlos pero he pensado: mejor lo escribo en el blog aquél que tengo olvidado y así le vale a alguien más!

Sobre Ruby on Rails hay mucha bibliografía escrita, pero sobre Hobo no demasiado.  Me encantaría que, tras seguir esta guía, puedas disfrutar no sólo de una aplicación funcionando en local, sino también una copia en un servidor de internet.  Y no sólo eso, sino que me encantaría que tuvieras un entorno de desarrollo completo con control de versiones usando git, que es algo así como un Subversion más evolucionado.

Cuando se consigue tener todo eso y te planteas explicar a los demás cómo conseguirlo hay un problema: ¿recuerdas todos los pasos?  El ordenador ya está configurado, así que es posible que si me olvido algún paso muchos de vosotros no consigáis ningún resultado, por lo que me voy a complicar la vida para que esto no pase.  Me he instalado una máquina virtual con Ubuntu “pelao” tal y como viene al instalarlo del CD del 8.10.  Sobre esa distribución “pelada” ejecutaré todos los pasos del tutorial.  Muchos de esos pasos son iguales en el Mac, pero aún así introduciré comentarios para tratar las posibles diferencias.

Instalando Git

Git es un sistema de control más avanzado que Subversion que está haciendo furor últimamente.  Para saber las diferencias o pormenores tenéis múltiples páginas explicándolo por internet, así que me centraré sólo en instalarlo y usarlo y si necesitáis más argumentos y/o documentación os aconsejo que googleéis un poquito, pues encontraréis personas que lo dominan y comprenden mejor que yo.

Entramos en el terminal y tecleamos ‘git’ para saber si ya lo tenemos instalado:

txinto@TxintUbuntu:~$ git
El programa «git» no está instalado actualmente.  Puede instalarlo escribiendo:
sudo apt-get install git-core
bash: git: orden no encontrada
txinto@TxintUbuntu:~$

Bien, parece ser que no tenemos git instalado, así que le hacemos caso.

txinto@TxintUbuntu:~$ sudo apt-get install git-core
[sudo] password for txinto:
Leyendo lista de paquetes… Hecho
Creando árbol de dependencias
Leyendo la información de estado… Hecho
…….. (se pasa un buen rato)

Desempaquetando patch (de …/patch_2.5.9-5_i386.deb) …
Procesando activadores para man-db …
Configurando liberror-perl (0.17-1) …
Configurando libdigest-sha1-perl (2.11-2build2) …
Configurando git-core (1:1.5.6.3-1.1ubuntu2.1) …
Configurando patch (2.5.9-5) …

txinto@TxintUbuntu:~$

Ahora ya tenemos instalado git, ahora vamos a por ruby, como siempre antes de hacerlo miramos a ver si está instalado:

txinto@TxintUbuntu:~$ ruby
El programa «ruby» no está instalado actualmente.  Puede instalarlo escribiendo:
sudo apt-get install ruby
bash: ruby: orden no encontrada
txinto@TxintUbuntu:~$

Volvemos a hacerle caso, nos documentamos un poquito en esta página: http://www.pablasso.com/2009/01/12/%C2%BFcomo-instalar-ruby-on-rails-en-ubuntu

txinto@TxintUbuntu:~$ sudo apt-get install ruby-full build-essential
Leyendo lista de paquetes… Hecho
Creando árbol de dependencias
Leyendo la información de estado… Hecho
Se instalaron de forma automática los siguientes paquetes y ya no son necesarios.

(…… tarda un buen rato!!! )

Configurando ruby1.8 (1.8.7.72-1ubuntu0.1) …
Configurando ruby (4.2) …
Procesando activadores para libc6 …
ldconfig deferred processing now taking place
txinto@TxintUbuntu:~$

Luego vamos a necesitar utilizar el comando “gem” para instalar Rails, así que lo instalamos siguiendo las instrucciones extraídas de la misma página.  Sólo para ilustrar, podríamos decir que las gems (gemas) son algo así como un sistema de instalación de programas por paquetes propio de Ruby (e independiente del sistema operativo que estéis usando).  Esta vez no pongo la salida que nos da el sistema pues es bastante engorroso hacerlo cada vez:

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

Bien, ya tenemos las gems, que nos valdrán para instalar los paquetes que rails necesita para funcionar.  El primer paquete a instalar es el propio Rails.

NOTA: En el momento de escribir esta nota, la plataforma Hobo sólo funciona bien con Rails versión 2.2.2, aunque la versión actual de Rails es la 2.3.2.  Normalmente (como vemos en el ejemplo que escribí en su día) la sentencia sería “sudo gem install rails” a secas, pero en este instante nos instalaría la 2.3.2, por lo que si la situación se mantiene deberemos hacer “sudo gem install rails -v 2.2.2″ .  Si por cualquier razón hemos instalado una versión más nueva, os recomiendo desinstalarla, por ejemplo “sudo gem uninstall rails -v 2.2.2″.

txinto@TxintUbuntu:~$ sudo gem install rails
[sudo] password for txinto:
Successfully installed rake-0.8.4
Successfully installed activesupport-2.2.2
Successfully installed activerecord-2.2.2
Successfully installed actionpack-2.2.2
Successfully installed actionmailer-2.2.2
Successfully installed activeresource-2.2.2
Successfully installed rails-2.2.2
7 gems installed
Installing ri documentation for rake-0.8.4…
Installing ri documentation for activesupport-2.2.2…
Installing ri documentation for activerecord-2.2.2…
Installing ri documentation for actionpack-2.2.2…
Installing ri documentation for actionmailer-2.2.2…
Installing ri documentation for activeresource-2.2.2…
Installing RDoc documentation for rake-0.8.4…
Installing RDoc documentation for activesupport-2.2.2…
Installing RDoc documentation for activerecord-2.2.2…
Installing RDoc documentation for actionpack-2.2.2…
Installing RDoc documentation for actionmailer-2.2.2…
Installing RDoc documentation for activeresource-2.2.2…
txinto@TxintUbuntu:~$

Ahora ya tenemos rails instalado.  Ha sido muy sencillo, ¿verdad?  Vamos a probar que funciona creando un proyecto y ejecutando el servidor de la aplicación de ese proyecto.  Para crear un proyecto simplemente vamos a una carpeta de trabajo y ejecutamos el comando rails con el nombre del proyecto que queremos.

txinto@TxintUbuntu:~$ mkdir dev
txinto@TxintUbuntu:~$ cd dev
txinto@TxintUbuntu:~/dev$

txinto@TxintUbuntu:~/dev$ rails prueba1
create
create  app/controllers
create  app/helpers

create  app/models
(…..)

create  log/production.log
create  log/development.log
create  log/test.log
txinto@TxintUbuntu:~/dev$

Bien! rails ha funcionado, y nos ha creado una aplicación del estilo “Hola mundo”, que no tardaremos en ejecutar.  Pero una aplicación Rails es, por definición, un servidor de aplicaciones web, así que ha creado mucho más que un ejecutable, ha creado una aplicación web basada en objetos, vistas, controladores y con un mecanismo de persistencia sobre base de datos.  Casi nada, para arrancar la aplicación abriremos otro terminal, cambiaremos al directorio prueba1 que nos acaba de crear rails, y ejecutaremos el servidor en el puerto que deseemos:

txinto@TxintUbuntu:~/dev/prueba1$ script/server -p 3000
=> Booting WEBrick…
=> Rails 2.2.2 application started on http://0.0.0.0:3000
=> Ctrl-C to shutdown server; call with –help for options
[2009-03-12 18:53:27] INFO  WEBrick 1.3.1
[2009-03-12 18:53:27] INFO  ruby 1.8.7 (2008-08-11) [i486-linux]
[2009-03-12 18:53:27] INFO  WEBrick::HTTPServer#start: pid=32253 port=3000

Bien, por eso lo hemos ejecutado en otro terminal, porque como es un servidor sigue en funcionamiento.  En el primer terminal continuaremos con nuestras operaciones y en el segundo terminal controlaremos la ejecución del servidor.  Ahora tenemos una aplicación web “escuchando” peticiones web a través del puerto 3000.  Para poder ver esa aplicación abriremos un explorador y entraremos en la dirección localhost:3000, que apunta a la máquina local y al puerto 3000, donde se encuentra “escuchando” la aplicación.  Como explorador cualquiera nos vale, por ejemplo Firefox es una buena herramienta para apoyar nuestro desarrollo con Rails.

Veréis que la página muestra un logotipo de Rails y nos da la bienvenida.  Si miramos el terminal desde el que abrimos el servidor veremos cómo nos informa sobre las peticiones web que recibe y sobre las respuestas que genera.

127.0.0.1 – - [12/Mar/2009:18:56:30 WET] “GET / HTTP/1.1″ 200 7385
- -> /
127.0.0.1 – - [12/Mar/2009:18:56:30 WET] “GET /javascripts/prototype.js HTTP/1.1″ 200 129738
http://localhost:3000/ -> /javascripts/prototype.js
127.0.0.1 – - [12/Mar/2009:18:56:30 WET] “GET /javascripts/effects.js HTTP/1.1″ 200 38675
http://localhost:3000/ -> /javascripts/effects.js
127.0.0.1 – - [12/Mar/2009:18:56:30 WET] “GET /images/rails.png HTTP/1.1″ 200 6646
http://localhost:3000/ -> /images/rails.png
127.0.0.1 – - [12/Mar/2009:18:56:31 WET] “GET /favicon.ico HTTP/1.1″ 200 0
- -> /favicon.ico

Bien, ya hemos comprobado que el Rails funciona.  Podríamos continuar desarrollando una aplicación Rails, pero no lo vamos a hacer, vamos a por el pack completo.  Además hay cientos de páginas que hablan de Rails, ahora es realmente cuando empezamos a pisar terreno inexplorado: desarrollar una aplicación usando Hobo sobre Rails sobre Ruby, manteniendo un proyecto en local y un “espejo” en internet, controlado todo ello con un sistema de control de versiones Git.  Allá vamos!!!

Vamos a seguir la siguiente estrategia:

  • Instalaremos hobo en nuestro equipo y crearemos un proyecto hobo vacío para comprobar que funciona.
  • Nos daremos de alta en el hosting de aplicaciones web Herokugarden.com.
  • Crearemos allí una aplicación web vacía en Rails, con control de versiones con Git.
  • Clonaremos la aplicación en un directorio de desarrollo en local.
  • La “hoboizaremos”, es decir, haremos que la aplicación Rails vacía que hemos clonado de Herokugarden.com sea una aplicación Hobo.
  • Sincronizaremos la copia local con la de Herokugarden.com.
  • Haremos que la aplicación de Herokugarden pueda ejecutarse, instalándole al hosting de aplicaciones todas las extensiones que Hobo necesita.
  • Sincronizaremos de nuevo con la copia en local.
  • Haremos un cambio en la aplicación alojada en Herokugarden.
  • Sincronizaremos y comprobaremos que la copia local incorpora el nuevo cambio.
  • Haremos un cambio en nuestra copia en local.
  • Sincronizaremos y comprobaremos que la copia de Herokugarden incorpora el nuevo cambio.

¡Cuanta cosa!  ¿Podremos con ello?

txinto@TxintUbuntu:~/dev$ sudo gem sources -a http://gems.github.com

Password:

http://gems.github.com added to sources

txinto@TxintUbuntu:~/dev$ sudo gem install mislav-will_paginate
Successfully installed mislav-will_paginate-2.3.8
1 gem installed
Installing ri documentation for mislav-will_paginate-2.3.8…
Installing RDoc documentation for mislav-will_paginate-2.3.8…
txinto@TxintUbuntu:~/dev$ sudo gem install hobo
Successfully installed hobo-0.8.5
1 gem installed
Installing ri documentation for hobo-0.8.5…
Installing RDoc documentation for hobo-0.8.5…
txinto@TxintUbuntu:~/dev$ hobo prueba2

Generating Rails app…
create
create  app/controllers
create  app/helpers
create  app/models
create  app/views/layouts
(…..)

exists  app/helpers/
create  app/views/front
exists  test/functional/
create  app/controllers/front_controller.rb
create  test/functional/front_controller_test.rb
create  app/helpers/front_helper.rb
create  app/views/front/index.dryml
txinto@TxintUbuntu:~/dev$

Ya tenemos instalado Hobo, vamos a comprobarlo arrancando la aplicación Rails que Hobo nos ha creado.  Para ello iremos al segundo terminal y abortaremos el servidor de aplicaciones que arrancamos anteriormente.  Entraremos después al directorio del nuevo proyecto (Pulsando Ctrl+C)y arrancaremos el nuevo servidor en el mismo puerto.

^C[2009-03-12 19:19:14] INFO  going to shutdown …
[2009-03-12 19:19:14] INFO  WEBrick::HTTPServer#start done.

txinto@TxintUbuntu:~/dev/prueba1$ cd ..
txinto@TxintUbuntu:~/dev$ cd prueba2/
txinto@TxintUbuntu:~/dev/prueba2$ script/server -p 3000
=> Booting WEBrick…
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require’: no such file to load — sqlite3 (MissingSourceFile)
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:31:in `require’
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:153:in `require’

¡Vaya!  Algo sucedió, pero no hay que alarmarse.  Las aplicaciones web sobre Rails utilizan objetos para manejar la información.  Esos objetos, cuando no están en memoria, han de hacerse persistentes en una base de datos.  Por defecto Rails utiliza una base de datos Sqlite3 para guardar esos objetos.  La anterior aplicación era demasiado básica, no tenía ningún objeto y por tanto nunca iba a buscar datos a la base de datos, por lo que no nos dió error.  Sin embargo, una aplicación Hobo tiene ya cierta información en su interior, como por ejemplo una tabla de usuarios que sustenta un servicio de identificación mediante e-mail y password.  SQLite3 no está instalado aún en nuestro sistema y, por lo tanto, el sistema nos informa de ese error.

txinto@TxintUbuntu:~/dev/prueba2$ sudo apt-get install sqlite3 libsqlite3-dev
[sudo] password for txinto:
Leyendo lista de paquetes… Hecho
Creando árbol de dependencias
Leyendo la información de estado… Hecho
sqlite3 ya está en su versión más reciente.

(….)

Desempaquetando libsqlite3-dev (de …/libsqlite3-dev_3.5.9-3ubuntu1_i386.deb) …
Configurando libsqlite3-dev (3.5.9-3ubuntu1) …
txinto@TxintUbuntu:~/dev/prueba2$ sudo gem install sqlite3-ruby
Building native extensions.  This could take a while…
Successfully installed sqlite3-ruby-1.2.4
1 gem installed
Installing ri documentation for sqlite3-ruby-1.2.4…
Installing RDoc documentation for sqlite3-ruby-1.2.4…
txinto@TxintUbuntu:~/dev/prueba2$

Volvemos a intentar arrancar el servidor (ya aviso de que la alegría no será completa, pero de momento todo parecerá ir bien).

txinto@TxintUbuntu:~/dev/prueba2$ script/server -p 3000
=> Booting WEBrick…
=> Rails 2.2.2 application started on http://0.0.0.0:3000
=> Ctrl-C to shutdown server; call with –help for options
[2009-03-12 19:30:01] INFO  WEBrick 1.3.1
[2009-03-12 19:30:01] INFO  ruby 1.8.7 (2008-08-11) [i486-linux]
[2009-03-12 19:30:01] INFO  WEBrick::HTTPServer#start: pid=421 port=3000

Abrimos otra vez el explorador en la posición localhost:3000, ¿qué pasará ahora?  La aplicación funciona, sin embargo en la pantalla nos aparecen ciertos mensajes que distan mucho de ser atractivos.

Hobo::Dryml::DrymlException in Front#index

Showing app/views/front/index.dryml where line # raised:

No such taglib: /home/txinto/dev/prueba2/app/views {:type=>:include, :src=>"taglibs/auto/rapid/cards", :template_dir=>"app/views/taglibs"} /home/txinto/dev/prueba2/app/views/taglibs/auto/rapid/cards.dryml

Pero tranquilos, todo marcha según lo previsto, esto sucede porque funciona la base de datos, y existen los objetos, pero la base de datos aún no tiene las tablas necesarias para albergar esos objetos.  Para lograr que todo esté correctamente sincronizado tenemos que generar y ejecutar una migración de Hobo.

txinto@TxintUbuntu:~/dev$ cd prueba2
txinto@TxintUbuntu:~/dev/prueba2$ script/generate hobo_migration

———- Up Migration ———-
create_table :users do |t|
t.string   :crypted_password, :limit => 40
t.string   :salt, :limit => 40
t.string   :remember_token
t.datetime :remember_token_expires_at
t.string   :name
t.string   :email_address
t.boolean  :administrator, :default => false
t.datetime :created_at
t.datetime :updated_at
t.string   :state, :default => “active”
t.datetime :key_timestamp
end
———————————-

———- Down Migration ——–
drop_table :users
———————————-
What now: [g]enerate migration, generate and [m]igrate now or [c]ancel? m

Migration filename:
(you can type spaces instead of ‘_’ — every little helps)
Filename [hobo_migration_1]:
create  db/migrate
create  db/migrate/20090312193612_hobo_migration_1.rb
(in /home/txinto/dev/prueba2)
==  HoboMigration1: migrating =================================================
– create_table(:users)
-> 0.1203s
==  HoboMigration1: migrated (0.1207s) ========================================

txinto@TxintUbuntu:~/dev/prueba2$

¿Qué es una migración de Hobo?  Daremos una explicación un tanto “ligera” para contentar nuestra curiosidad.  Cuando definimos nuevas clases de objetos en Hobo necesitaremos crear las tablas que los alberguen.  Al ejecutar script/generate hobo_migration Hobo mira la descripción de los objetos y la compara con la base de datos, anotando los cambios en unos ficheros llamados “migraciones”.  Cuando pulsamos la ‘m’ hacemos que se ejecute el fichero de migración, esta ejecución consiste en traducir los cambios que hay detallados en el archivo a sentencias SQL que transformarán la base de datos.  De esa forma podemos ir cambiando nuestro diagrama de clases de la aplicación y que la base de datos se vaya actualizando casi-automáticamente.  Cada migración produce un fichero que muestra los cambios a hacer para progresar y a deshacer si queremos volver al paso anterior.

Bien, explicado más o menos este punto, vamos a alegrarnos la vista recargando la página localhost:3000 y viendo como nuestra aplicación Hobo nos da la bienvenida.

¿Fantástico eh? Como vemos Hobo nos da un sistema de registro y autenticación de usuarios, un aspecto bastante consistente en el apartado gráfico, un campo de búsqueda…

Ahora vamos a ver qué nos ofrece Herokugarden.com.  Aviso que el proceso de registro de Herokugarden.com puede demorarse varios días, pues la web no crea el usuario automáticamente, sino que al cabo de uno o dos días envía al usuario un e-mail de activación de la cuenta.  Habrá que tener paciencia!

Vamos a la página Herokugarden.com, nos registramos y, cuando por fin entramos en nuestra sesión, nos llevará a la página de las aplicaciones que el nuestro usuario posee (de momento ninguna, claro): http://herokugarden.com/myapps.  En este punto aconsejaremos utilizar el explorador Firefox, pues la página hace uso intensivo de la tecnología Ajax y otras aplicaciones pueden no mostrar bien algunas partes de la web, complicando el seguir este tutorial.  Pulsaremos sobre “Create new App”.  Herokugarden se inventará un nombre aleatorio para nuestra aplicación, la arrancará y nos redirigirá a su página de bienvenida.  Veremos una página idéntica a nuestro primer proyecto Rails (prueba1), con el logotipo de Rails.  Veremos que abajo hay una barra gris para controlar la edición y ejecución de nuestra aplicación web.  Veremos un icono con el símbolo ZZ, y también un link llamado “app settings”, pulsaremos el link “app settings” y nos llevará a la página de personalización de la aplicación.

Una vez aquí podemos renombrar ese nombre tan feo por uno de nuestro gusto.  Yo voy a poner “txinto-prueba3″, ya que el identificador que usemos ha de ser único, os aconsejo que os pongáis vuestro nombre seguido de prueba3, ya que si todos quisiéramos poner como nombre sólo “prueba3″ el segundo que intentara seguir el tutorial no podría usar ese nombre.  A partir de ahora en el tutorial deberéis sustituir txinto-prueba3 por el nombre único de vuestra aplicación.  Ahora pulsaremos en “edit” para que nos lleve al entorno de desarrollo on-line de Herokugarden (una verdadera maravilla a golpe de Ajax).

A la izquierda vemos el directorio de proyecto.  Iremos a la carpeta public y editaremos el index.html, para ver al menos un cambio antes de exportarlo.  Por ejemplo podemos ir a donde pone “Work locally and deploy to Heroku” y lo cambiamos por, por ejemplo “Este cambio lo he hecho yo!”.  Pulsamos “Save” y le damos al icono SS para ver la aplicación.  Y recargamos la página (Ctrl+R) para ver el cambio.

Volvemos al editor para echarle un vistazo al control de versiones incorporado en Herokugarden.com, y basado en Git.  Pulsamos ZZ de nuevo para ir al editor y pulsaremos sobre la palabra Revisions de la columna izquierda.

Veremos que, aparte de algún archivo extra, el index.html se encuentra entre los cambios realizados.  En la caja de texto escribiremos, por ejemplo, “Versión inicial de aplicación Rails creada en HerokuGarden.com, con un cambio inicial en la pantalla de bienvenida para comprobar que todo funciona”.  Y después pulsamos Commit, para hacer permanentes estos cambios.

Bien, ya tenemos una aplicación Rails (aún no Hobo) en un servidor web y bajo un control de versiones Git.  Luego vamos a seguir los pasos necesarios para tener una copia ejecutable en local, con todo lo que Hobo nos ofrece.

Lo primero es instalar las herramientas que Herokugarden nos ofrece para lograr esa sincronización con su servidor de aplicaciones.

txinto@TxintUbuntu:~/dev/prueba2$ cd ..

txinto@TxintUbuntu:~/dev/prueba2$ sudo gem install herokugarden
[sudo] password for txinto:

—> Installing herokugarden v0.4.2
—> Migrate local checkouts using the git:transition command:

cd myapp/
herokugarden git:transition

Successfully installed herokugarden-0.4.2
1 gem installed
Installing ri documentation for herokugarden-0.4.2…
Installing RDoc documentation for herokugarden-0.4.2…
txinto@TxintUbuntu:~/dev$ herokugarden clone txinto-prueba3
Enter your Heroku credentials.
Email: ######@#####.####
Password:
No ssh public key found in /home/txinto/.ssh/id_[rd]sa.pub.  You may want to specify the full path to the keyfile.
txinto@TxintUbuntu:~/dev$

Bueno, ¡casi lo conseguimos!  Hemos podido instalar las herramientas de herokugarden, sin embargo no hemos conseguido bajarnos la aplicación a un directorio local.  ¿Por qué es esto?  Herokugarden usa una autenticación mediante clave pública del usuario, y esa clave pública no la tenemos creada.  No quiero entrar mucho en un tema que tampoco domino demasiado, pero investigando por la web he conseguido resolverlo haciendo lo siguiente:

txinto@TxintUbuntu:~/dev$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/txinto/.ssh/id_rsa):
Created directory ‘/home/txinto/.ssh’.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/txinto/.ssh/id_rsa.
Your public key has been saved in /home/txinto/.ssh/id_rsa.pub.
The key fingerprint is:
(……………….)
+—————–+
txinto@TxintUbuntu:~/dev$ herokugarden clone txinto-prueba3
Enter your Heroku credentials.
Email: txinto@iac.es
Password:
Uploading ssh public key /home/txinto/.ssh/id_rsa.pub
git clone git@herokugarden.com:txinto-prueba3.git
Initialized empty Git repository in /home/txinto/dev/txinto-prueba3/.git/
The authenticity of host ‘herokugarden.com (75.101.141.116)’ can’t be established.
RSA key fingerprint is 8b:48:5e:67:0e:c9:16:47:32:f2:87:0c:1f:c8:60:ad.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added ‘herokugarden.com,75.101.141.116′ (RSA) to the list of known hosts.
remote: Generating pack…
remote: Done counting 69 objects.

(……..)

Resolving deltas: 100% (19/19), done.
txinto@TxintUbuntu:~/dev$

Bien!  Ya hemos clonado el proyecto, vamos a ver si funciona.  Para ello volveremos al segundo terminal, aquel que tenemos colgado con el servidor de prueba2.  Lo abortaremos con Ctrl+C, cambiaremos al directorio recién creado, y luego arrancaremos el servidor de la nueva aplicación.

^C[2009-03-12 20:35:44] INFO  going to shutdown …

[2009-03-12 20:35:44] INFO  WEBrick::HTTPServer#start done.
txinto@TxintUbuntu:~/dev/prueba2$ cd ..
txinto@TxintUbuntu:~/dev$ cd txinto-prueba3/
txinto@TxintUbuntu:~/dev/txinto-prueba3$ script/server -p 3000
=> Booting WEBrick…
=> Rails 2.2.2 application started on http://0.0.0.0:3000
=> Ctrl-C to shutdown server; call with –help for options
[2009-03-12 20:38:17] INFO  WEBrick 1.3.1
[2009-03-12 20:38:17] INFO  ruby 1.8.7 (2008-08-11) [i486-linux]
[2009-03-12 20:38:17] INFO  WEBrick::HTTPServer#start: pid=1473 port=3000

Si ahora abrimos el navegador en la página localhost:3000 veremos que ahora tenemos en local nuestra aplicación.

Ahora vamos a “hoboizar” la aplicación que, como hemos podido ver, aún es simplemente una aplicación Rails.

NOTA: Para más detalles de lo que voy haciendo podéis leer la página http://cookbook.hobocentral.net/recipes/19-deploy-a-hobo-app-on-heroku

txinto@TxintUbuntu:~/dev/txinto-prueba3$ git submodule add git://github.com/tablatom/hobo.git vendor/plugins/hobo
Initialized empty Git repository in /home/txinto/dev/txinto-prueba3/vendor/plugins/hobo/.git/
remote: Counting objects: 19928, done.
remote: Compressing objects: 100% (5759/5759), done.
remote: Total 19928 (delta 13664), reused 19648 (delta 13416)
Receiving objects: 100% (19928/19928), 3.57 MiB | 28 KiB/s, done.
Resolving deltas: 100% (13664/13664), done.
txinto@TxintUbuntu:~/dev/txinto-prueba3$

Ahora hemos de crear un fichero dentro del directorio txinto-prueba3/lib/tasks/ con el nombre git_submodules.rake, y rellenarlo con una secuencia de comandos concreta:

txinto@TxintUbuntu:~/dev/txinto-prueba3$ mkdir lib
txinto@TxintUbuntu:~/dev/txinto-prueba3$ mkdir lib/tasks
txinto@TxintUbuntu:~/dev/txinto-prueba3$ gedit lib/tasks/git_submodules.rake

En el fichero escribiremos las siguientes líneas de código y después grabaremos:

task :git_submodules do
  puts `git submodule init 2>&1`
  puts `git submodule update 2>&1`
end

Y continuamos con el resto de comandos que hemos visto en la página donde nos hemos documentado:

txinto@TxintUbuntu:~/dev/txinto-prueba3$ script/generate hobo –add-routes
create  app/views/taglibs
create  app/views/taglibs/themes
create  app/views/taglibs/application.dryml
create  public/hobothemes
create  app/models
create  app/models/guest.rb
create  public/stylesheets
create  public/stylesheets/application.css
create  public/javascripts/dryml-support.js
create  config/initializers/hobo.rb
txinto@TxintUbuntu:~/dev/txinto-prueba3$ script/generate hobo_rapid –import-tags
create  public/javascripts/hobo-rapid.js
create  public/javascripts/lowpro.js
create  public/javascripts/IE7.js
create  public/javascripts/blank.gif
create  public/stylesheets/reset.css
create  public/stylesheets/hobo-rapid.css
create  public/hobothemes/clean/
create  public/hobothemes/clean/images
create  public/hobothemes/clean/images/fieldbg.gif
create  public/hobothemes/clean/images/spinner.gif
create  public/hobothemes/clean/images/small_close.png
create  public/hobothemes/clean/images/pencil.png
create  public/hobothemes/clean/stylesheets
create  public/hobothemes/clean/stylesheets/rapid-ui.css
create  public/hobothemes/clean/stylesheets/clean.css
create  app/views/taglibs/themes/clean/
create  app/views/taglibs/themes/clean/clean.dryml
txinto@TxintUbuntu:~/dev/txinto-prueba3$ script/generate hobo_user_model user
exists  app/models/
create  test/unit/
create  test/fixtures/
create  app/views/user_mailer
create  app/models/user.rb
create  test/unit/user_test.rb
create  test/fixtures/users.yml
create  app/models/user_mailer.rb
create  app/views/user_mailer/forgot_password.erb
txinto@TxintUbuntu:~/dev/txinto-prueba3$ script/generate hobo_user_controller user
exists  app/controllers/
exists  app/helpers/
create  app/views/users
create  test/functional/
create  app/controllers/users_controller.rb
create  test/functional/users_controller_test.rb
create  app/helpers/users_helper.rb
txinto@TxintUbuntu:~/dev/txinto-prueba3$ script/generate hobo_front_controller front –delete-index –add-routes
exists  app/controllers/
exists  app/helpers/
create  app/views/front
exists  test/functional/
create  app/controllers/front_controller.rb
create  test/functional/front_controller_test.rb
create  app/helpers/front_helper.rb
create  app/views/front/index.dryml
txinto@TxintUbuntu:~/dev/txinto-prueba3$ script/generate hobo_migration

———- Up Migration ———-
create_table :users do |t|
t.string   :crypted_password, :limit => 40
t.string   :salt, :limit => 40
t.string   :remember_token
t.datetime :remember_token_expires_at
t.string   :name
t.string   :email_address
t.boolean  :administrator, :default => false
t.datetime :created_at
t.datetime :updated_at
t.string   :state, :default => “active”
t.datetime :key_timestamp
end
———————————-

———- Down Migration ——–
drop_table :users
———————————-
What now: [g]enerate migration, generate and [m]igrate now or [c]ancel? m

Migration filename:
(you can type spaces instead of ‘_’ — every little helps)
Filename [hobo_migration_1]:
create  db/migrate
create  db/migrate/20090411014159_hobo_migration_1.rb
(in /home/txinto/dev/txinto-prueba3)
==  HoboMigration1: migrating =================================================
– create_table(:users)
-> 0.0704s
==  HoboMigration1: migrated (0.0731s) ========================================

txinto@TxintUbuntu:~/dev/txinto-prueba3$

Bien, con esto habríamos acabado la “hoboización”.  Este es un proceso que sólo tendremos que hacer una vez por proyecto, y la razón de que tengamos que hacerlo así es la siguiente: herokugarden no contempla desarrollos sobre Hobo, y la práctica recomendada es crear en Herokugarden un proyecto Rails, crear una copia de desarrollo local, hoboizarla, y volverla a subir a Herokugarden.  A partir de que lo consigamos, el ciclo de desarrollo será igual que cualquier proyecto que use control de versiones.

Ahora vamos a probar que realmente el proyecto funciona correctamente ejecutando su servidor en nuestro segundo terminal:

^C[2009-04-11 02:49:56] INFO  going to shutdown …
[2009-04-11 02:49:56] INFO  WEBrick::HTTPServer#start done.
txinto@TxintUbuntu:~/dev/txinto-prueba3$ script/server -p 3000
=> Booting WEBrick…
=> Rails 2.2.2 application started on http://0.0.0.0:3000
=> Ctrl-C to shutdown server; call with –help for options
[2009-04-11 02:50:40] INFO  WEBrick 1.3.1
[2009-04-11 02:50:40] INFO  ruby 1.8.7 (2008-08-11) [i486-linux]
[2009-04-11 02:50:40] INFO  WEBrick::HTTPServer#start: pid=3946 port=3000

Si visitamos la página http://localhost:3000 veremos nuestra flamante aplicación Hobo dándonos la bienvenida!!

Ahora nos toca subir la aplicación modificada al servidor herokugarden, siguiendo las instrucciones de la página http://cookbook.hobocentral.net/recipes/19-deploy-a-hobo-app-on-heroku .

txinto@TxintUbuntu:~/dev/txinto-prueba3$ git add .
txinto@TxintUbuntu:~/dev/txinto-prueba3$ git commit -am “Hoboizamos la aplicación”
Created commit ff0098c: Hoboizamos la aplicación
39 files changed, 2337 insertions(+), 278 deletions(-)
create mode 100644 .gitmodules
create mode 100644 app/controllers/front_controller.rb
create mode 100644 app/controllers/users_controller.rb
create mode 100644 app/helpers/front_helper.rb
create mode 100644 app/helpers/users_helper.rb
create mode 100644 app/models/guest.rb
create mode 100644 app/models/user.rb
create mode 100644 app/models/user_mailer.rb
create mode 100644 app/views/front/index.dryml
create mode 100644 app/views/taglibs/application.dryml
create mode 100644 app/views/taglibs/auto/rapid/cards.dryml
create mode 100644 app/views/taglibs/auto/rapid/forms.dryml
create mode 100644 app/views/taglibs/auto/rapid/pages.dryml
create mode 100644 app/views/taglibs/themes/clean/clean.dryml
create mode 100644 app/views/user_mailer/forgot_password.erb
create mode 100644 config/initializers/hobo.rb
create mode 100644 db/migrate/20090411014159_hobo_migration_1.rb
create mode 100644 db/schema.rb
create mode 100644 lib/tasks/git_submodules.rake
create mode 100644 public/hobothemes/clean/images/fieldbg.gif
create mode 100644 public/hobothemes/clean/images/pencil.png
create mode 100644 public/hobothemes/clean/images/small_close.png
create mode 100644 public/hobothemes/clean/images/spinner.gif
create mode 100644 public/hobothemes/clean/stylesheets/clean.css
create mode 100644 public/hobothemes/clean/stylesheets/rapid-ui.css
delete mode 100644 public/index.html
create mode 100644 public/javascripts/IE7.js
create mode 100644 public/javascripts/blank.gif
create mode 100644 public/javascripts/dryml-support.js
create mode 100644 public/javascripts/hobo-rapid.js
create mode 100644 public/javascripts/lowpro.js
create mode 100644 public/stylesheets/application.css
create mode 100644 public/stylesheets/hobo-rapid.css
create mode 100644 public/stylesheets/reset.css
create mode 100644 test/fixtures/users.yml
create mode 100644 test/functional/front_controller_test.rb
create mode 100644 test/functional/users_controller_test.rb
create mode 100644 test/unit/user_test.rb
create mode 160000 vendor/plugins/hobo
txinto@TxintUbuntu:~/dev/txinto-prueba3$ git push
Warning: Permanently added the RSA host key for IP address ’75.101.142.103′ to the list of known hosts.
Counting objects: 81, done.
Compressing objects: 100% (54/54), done.
Writing objects: 100% (70/70), 41.22 KiB, done.
Total 70 (delta 5), reused 0 (delta 0)
refs/heads/master: 9d9fbb8fe526701310ea19a19b2eed7ff8ef458e -> ff0098cdc9b59532c5148886c860514057c51999
To git@herokugarden.com:txinto-prueba3.git
9d9fbb8..ff0098c  master -> master
HEAD is now at ff0098c… Hoboizamos la aplicación
Running migrations…
rake aborted!
uninitialized constant Hobo

(See full trace by running task with –trace)
(in /mnt/home/userapps/61283)
App restarting…
######################################################################## 100.0%

Deployed to http://untitled-ee7419.herokugarden.com
txinto@TxintUbuntu:~/dev/txinto-prueba3$ herokugarden rake txinto-prueba3 git_submodules
(in /mnt/home/userapps/61283)
Submodule ‘vendor/plugins/hobo’ (git://github.com/tablatom/hobo.git) registered for path ‘vendor/plugins/hobo’
Initialized empty Git repository in /userapps/61283/vendor/plugins/hobo/.git/
Submodule path ‘vendor/plugins/hobo’: checked out ‘e19949cc0b988b15c1d07b5e771881d77a1606d6′
txinto@TxintUbuntu:~/dev/txinto-prueba3$ herokugarden rake txinto-prueba3 db:migrate
(in /mnt/home/userapps/61283)
==  HoboMigration1: migrating =================================================
– create_table(:users)
-> 0.1051s
==  HoboMigration1: migrated (0.1052s) ========================================

txinto@TxintUbuntu:~/dev/txinto-prueba3$ herokugarden rake txinto-prueba3 hobo:generate_taglibs
(in /mnt/home/userapps/61283)
txinto@TxintUbuntu:~/dev/txinto-prueba3$

Ya tenemos la aplicación hoboizada y subida a Herokugarden.com, ahora intentaremos verla en funcionamiento visitando la página de nuestra aplicación (en el ejemplo http://txinto-prueba3.herokugarden.com).  Vemos que la aplicación no funciona:

The server for txinto-prueba3 failed to start. Details:

/usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:445:in

`load_missing_constant’: uninitialized constant Hobo (NameError) from

/usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:77:in

`const_missing’ from

/usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:89:in

`const_missing’ from

/mnt/home/userapps/61283/config/initializers/hobo.rb:1 from

/usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:142:in

`load_without_new_constant_marking’ from

/usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:142:in

`load’ from

/usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:521:in

`new_constants_in’ from

/usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:142:in

`load’ from

/usr/lib/ruby/gems/1.8/gems/rails-2.2.2/lib/initializer.rb:550:in

`load_application_initializers’ … 21 levels… from

/usr/lib/ruby/gems/1.8/gems/rack-0.4.0/lib/rack/builder.rb:22:in

`instance_eval’ from

/usr/lib/ruby/gems/1.8/gems/rack-0.4.0/lib/rack/builder.rb:22:in

`initialize’ from /home/heroku_rack/heroku.ru:1:in `new’ from

/home/heroku_rack/heroku.ru:1

Bueno, como siempre no nos alarmamos: resulta que la aplicación está “hoboizada” pero… en el servidor no está instalado Hobo!  Bien, para cada aplicación en Herokugarden hay una dirección que nos permite ir a su ventana de edición: http://edit.txinto-prueba3.herokugarden.com en nuestro ejemplo.

En la pantalla de edición vemos, a la izquierda, una carpeta llamada “vendor”.  Si pulsamos sobre ella con el ratón vemos que aparece una opción llamada “Gems & plugins”.  Si pulsamos sobre “Gems & plugins” nos lleva a una ventana especial donde podemos instalarle gemas y plugins.  En el desplegable seleccionamos “Available” y hacemos una búsqueda de la palabra “hobo”.  Nos aparecerán las gemas hobo, hobosupport y hobofields.  Pulsamos sobre “install en las tres para instalarlas.  Ahora (para no perder la página de instalación de plugins) abrimos una ventana nueva del explorador apuntando a la aplicación (en nuestro ejemplo http://txinto-prueba3.herokugarden.com).  Luego buscamos el plugin (no la gem, sino el plugin) llamado will_paginate y lo instalamos también.  Ya podemos visitar la página de nuestra aplicación http://txinto-prueba3.herokugarden.com y ver que ejecuta sin problemas!

Resumiendo: creamos en Herokugarden una aplicación Rails, comprobamos que funcionara, la descargamos en local y “hoboizamos” esa copia, luego subimos la aplicación “hoboizada” de nuevo al servidor e instalamos las gemas y los plugins que hicieron falta para que el servidor funcionara.

Volvemos a la página http://edit.txinto-prueba3.herokugarden.com y pulsamos sobre “Revisions” (a la izquierda).  Vamos a hacer un “commit” de los últimos cambios, para ello escribiremos un texto explicatorio de las últimas acciones realizadas (p.e. “Ahora la aplicación ya es Hobo y ejecuta en Herokugarden”) en la caja de texto y pulsaremos el botón “Commit”.

Vamos a nuestro terminal en local, ahora vamos a conseguir que en nuestra copia en local estén los cambios realizados en el servidor Herokugarden, es decir, vamos a utilizar Git para bajar al directorio local la copia actual del servidor:

txinto@TxintUbuntu:~/dev/txinto-prueba3$ git pull
remote: Generating pack…
remote: Done counting 7 objects.
remote: Result has 4 objects.
remote: Deltifying 4 objects…
remote:  100% (4/4) done
remote: Total 4 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (4/4), done.
From git@herokugarden.com:txinto-prueba3
ff0098c..8be926b  master     -> origin/master
Updating ff0098c..8be926b
Fast forward
vendor/plugins/will_paginate |    1 +
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 160000 vendor/plugins/will_paginate
txinto@TxintUbuntu:~/dev/txinto-prueba3$

Y comprobamos el correcto funcionamiento arrancando de nuevo el servidor local en el puerto 3000 y visitando la página http://localhost:3000 .

^C[2009-04-11 03:23:21] INFO  going to shutdown …
[2009-04-11 03:23:21] INFO  WEBrick::HTTPServer#start done.
txinto@TxintUbuntu:~/dev/txinto-prueba3$ script/server -p 3000
=> Booting WEBrick…
=> Rails 2.2.2 application started on http://0.0.0.0:3000
=> Ctrl-C to shutdown server; call with –help for options
[2009-04-11 03:23:27] INFO  WEBrick 1.3.1
[2009-04-11 03:23:27] INFO  ruby 1.8.7 (2008-08-11) [i486-linux]
[2009-04-11 03:23:27] INFO  WEBrick::HTTPServer#start: pid=4507 port=3000

Ahora vamos a dejarnos ya de tanta instalación y vamos a programar un poquito.  Haremos los siguientes cambios en local:

  • Crearemos una clase llamada Curso, que tendrá como atributos un nombre y una edad típica.
  • Crearemos una clase llamada Alumno, que tendrá como atributo un nombre y que pertenecerá a un Curso.

Una vez funcionen estos cambios en la copia en local, los subiremos a la web.

txinto@TxintUbuntu:~/dev/txinto-prueba3$ script/generate hobo_model_resource Curso nombre:string edad:integer
exists  app/models/
exists  app/controllers/
exists  app/helpers/
create  app/views/cursos
exists  test/functional/
exists  test/unit/
dependency  hobo_model
exists    app/models/
exists    test/unit/
exists    test/fixtures/
create    app/viewhints
create    app/models/curso.rb
create    app/viewhints/curso_hints.rb
create    test/unit/curso_test.rb
create    test/fixtures/cursos.yml
create  app/controllers/cursos_controller.rb
create  test/functional/cursos_controller_test.rb
create  app/helpers/cursos_helper.rb
txinto@TxintUbuntu:~/dev/txinto-prueba3$ script/generate hobo_model_resource Alumno nombre:string curso_id:integer
exists  app/models/
exists  app/controllers/
exists  app/helpers/
create  app/views/alumnos
exists  test/functional/
exists  test/unit/
dependency  hobo_model
exists    app/models/
exists    test/unit/
exists    test/fixtures/
exists    app/viewhints
create    app/models/alumno.rb
create    app/viewhints/alumno_hints.rb
create    test/unit/alumno_test.rb
create    test/fixtures/alumnos.yml
create  app/controllers/alumnos_controller.rb
create  test/functional/alumnos_controller_test.rb
create  app/helpers/alumnos_helper.rb
txinto@TxintUbuntu:~/dev/txinto-prueba3$ script/generate hobo_migration

———- Up Migration ———-
create_table :alumnos do |t|
t.string   :nombre
t.integer  :curso_id
t.datetime :created_at
t.datetime :updated_at
end

create_table :cursos do |t|
t.string   :nombre
t.integer  :edad
t.datetime :created_at
t.datetime :updated_at
end
———————————-

———- Down Migration ——–
drop_table :alumnos
drop_table :cursos
———————————-
What now: [g]enerate migration, generate and [m]igrate now or [c]ancel? m

Migration filename:
(you can type spaces instead of ‘_’ — every little helps)
Filename [hobo_migration_2]:
exists  db/migrate
create  db/migrate/20090411022845_hobo_migration_2.rb
(in /home/txinto/dev/txinto-prueba3)
==  HoboMigration2: migrating =================================================
– create_table(:alumnos)
-> 0.0822s
– create_table(:cursos)
-> 0.0379s
==  HoboMigration2: migrated (0.1236s) ========================================

Editaremos el archivo del modelo alumno.rb:

txinto@TxintUbuntu:~/dev/txinto-prueba3$ gedit app/models/alumno.rb

y añadiremos la línea belongs_to :curso tras la sección fields do .. end:

class Alumno < ActiveRecord::Base

hobo_model # Don’t put anything above this

fields do
nombre   :string
curso_id :integer
timestamps
end

belongs_to :curso, :accessible => true

# — Permissions — #

def create_permitted?
acting_user.administrator?
end

def update_permitted?
acting_user.administrator?
end

def destroy_permitted?
acting_user.administrator?
end

def view_permitted?(field)
true
end

end

Y la línea “has_many :alumnos” en el controlador curso.rb:

txinto@TxintUbuntu:~/dev/txinto-prueba3$ gedit app/models/curso.rb

también tras fields do .. end, así:

class Curso < ActiveRecord::Base

hobo_model # Don’t put anything above this

fields do
nombre :string
edad   :integer
timestamps
end

has_many :alumnos, :accessible => true

# — Permissions — #

def create_permitted?
acting_user.administrator?
end

def update_permitted?
acting_user.administrator?
end

def destroy_permitted?
acting_user.administrator?
end

def view_permitted?(field)
true
end

end

Ahora visitaremos http://localhost y nos registramos en la web (opción “sign up” de arriba a la derecha).  Por defecto, el primer usuario que se registra pasa a ser del tipo administrador, y el resto son usuarios normales a menos de que un administrador les otorgue categoría de administrador.  Por tanto, al ser el primer usuario que se da de alta en el sistema, éste usuario será administrador.  El proceso de registro es análogo al de miles de sitios web, por lo que no daremos detalles.

Una vez estemos dados de alta en el sistema, podremos dar de alta uno o dos alumnos.

Hay una cosa que no nos gusta demasiado: cuando pulsamos sobre “Alumnos” nos intenta enseñar una lista de alumnos, pero en vez de mostrarnos los nombres que hemos creado nos muestra “Alumno 1″, “Alumno 2″… y lo mismo con los cursos.  Eso sucede porque Hobo busca (por convención) el nombre de los objetos que crea dentro de un atributo name.  De momento no nos preocuparemos, probaremos a subir la aplicación a la web y veremos que funciona.  Una vez allí cambiaremos los nombres del atributo para que se ajuste a la convención y volveremos a bajar una versión actualizada a la copia local de trabajo:

txinto@TxintUbuntu:~/dev/txinto-prueba3$ git add .
txinto@TxintUbuntu:~/dev/txinto-prueba3$ git commit -am “Cursos y Alumnos creados”
Created commit cd1973e: Cursos y Alumnos creados
21 files changed, 465 insertions(+), 1 deletions(-)
create mode 100644 app/controllers/alumnos_controller.rb
create mode 100644 app/controllers/cursos_controller.rb
create mode 100644 app/helpers/alumnos_helper.rb
create mode 100644 app/helpers/cursos_helper.rb
create mode 100644 app/models/alumno.rb
create mode 100644 app/models/alumno.rb~
create mode 100644 app/models/curso.rb
create mode 100644 app/models/curso.rb~
create mode 100644 app/viewhints/alumno_hints.rb
create mode 100644 app/viewhints/curso_hints.rb
create mode 100644 db/migrate/20090411022845_hobo_migration_2.rb
create mode 100644 test/fixtures/alumnos.yml
create mode 100644 test/fixtures/cursos.yml
create mode 100644 test/functional/alumnos_controller_test.rb
create mode 100644 test/functional/cursos_controller_test.rb
create mode 100644 test/unit/alumno_test.rb
create mode 100644 test/unit/curso_test.rb
txinto@TxintUbuntu:~/dev/txinto-prueba3$ git push
Counting objects: 56, done.
Compressing objects: 100% (32/32), done.
Writing objects: 100% (37/37), 5.27 KiB, done.
Total 37 (delta 7), reused 0 (delta 0)
To git@herokugarden.com:txinto-prueba3.git
8be926b..cd1973e  master -> master
refs/heads/master: 8be926ba92254fa3e907d6d108a099d75f384315 -> cd1973e186d01c7736511f4e75b39864b5b880f3
HEAD is now at cd1973e… Cursos y Alumnos creados
Running migrations…
(in /mnt/home/userapps/61283)
==  HoboMigration2: migrating =================================================
– create_table(:alumnos)
-> 2.7750s
– create_table(:cursos)
-> 2.1738s
==  HoboMigration2: migrated (4.9490s) ========================================

App restarting…
######################################################################## 100.0%

Deployed to http://untitled-ee7419.herokugarden.com
txinto@TxintUbuntu:~/dev/txinto-prueba3$

Vemos que la copia en remoto (http://txinto-prueba3.herokugarden.com) nos muestra las solapas “Alumnos” y “Cursos” pero que no nos deja autenticarnos, ni tampoco dentro de estas solapas aparecen los datos que hemos creado en local.  La razón es sencilla: el sistema de control de versiones sincroniza los programas, no los datos.  No importa, volveremos a crear allí algunos datos para ver que el programa funciona.

Ahora vamos a http://edit.txinto-prueba3.herokugarden.com, y seleccionamos la carpeta app, y dentro de ella models, y dentro “curso.rb”.  Veremos el código de la clase Curso.  Dentro de fields do .. end cambiaremos la palabra “nombre” por “name” y pulsaremos sobre el botón Save de arriba a la derecha.  Lo mismo haremos con app/models/alumno.rb.

En este instante, el software de la página ya está modificado, pero queda modificar la base de datos para que la columna que guarda el nombre se llame “name”.  Esto se consigue de forma muy sencilla ejecutando una migración Hobo.  Abajo a la izquierda, justo encima de la palabra Heroku vemos un símbolo similar a una rueda dentada.  Si pulsamos sobre él nos aparece un menú con las opciones Generate, Console y Rake.  Escogemos Generate y en la ventana que aparece escribimos hobo_migration y pulsamos Run.  Tardará un poquito. (NOTA: En este mismo momento no me funciona esto)

Ahora visitamos http://txinto-prueba3.herokugarden.com y veremos que, efectivamente, en la lista de alumnos se muestran los alumnos por su nombre, y en la lista de cursos también.

NOTA: Aunque el tutorial no está concluido, voy a publicarlo en este momento ya que hay personas que están esperando por él para poder ponerse a trastear con el Hobo y el Rails.  Ojalá tenga tiempo en breve para poder subir unas cuantas imágenes que ayuden y para poder completar el tutorial.  Un saludo muy afectuoso.

WebObjects: qué es y para qué sirve?

Hola de nuevo. Como siempre me enfrento a la escritura de un texto informal que intente ayudar a alguien a abrirse paso a través de la programación en Mac.

Muchos de los que hayan intentado programar en Mac se han topado con una palabra un poco extraña y, mirando la documentación, no han conseguido enterarse de qué es ni para qué sirve. Esta palabra podría ser WebObjects .

¿A qué se parece WebObjects? He decidido empezar con esta pregunta por que, no sé si por desgracia, cada día me doy más cuenta de que ahorramos tiempo y dolores de cabeza si, ante una nueva herramienta o tecnología, somos capaces de encontrar alguna analogía con otras herramientas similares y, si ésta va acompañada de algún tipo de orientación o comparación, mejor.

  • Se parece a EJB (Enterprise Java Beans).
    • Razónes:
      • Orientado para crear aplicaciones web orientadas a objetos
      • Utiliza un motor de persistencia para que los datos descansen sobre una base de datos.
      • Utiliza Java como lenguaje principal.
      • Se puede considerar que WebObjects es, en la práctica, también una tecnología abierta, ya que existen herramientas no-mac (WOLips sobre Eclipse) y no-webobjects (servidores Tomcat, p.e.) que permiten desarrollar aplicaciones WebObjects en cualquier plataforma y sin necesidad de un servidor de aplicaciones WebObjects.
    • Diferencias:
      • Motor de persistencia único y propio, no permite escoger entre un abanico de posibilidades, en eso EJB es mejor.
      • Mucho más elegante y evolucionado. EJB (dentro de Java EE), seguramente dentro de uno o dos años habrá alcanzado el grado de acabado de WebObjects actual, y desde una perspectiva más abierta que WebObjects, pero a día de hoy existe mucha diferencia entre dejar que WebObjects haga las cosas por tí y tenértelo que hacer todo con EJB.
      • Mucha mayor productividad que EJB/Java EE. Tanto en el tema de la gestión y número de cambios como en la generación automática de páginas de gestión de contenidos.
  • Ruby on Rails:
    • Razones:
      • Orientado para crear aplicaciones web orientadas a objetos
      • Utiliza un motor de persistencia para que los datos descansen sobre una base de datos.
      • Se puede considerar que WebObjects es, en la práctica, también una tecnología abierta.
      • Productividad muy similar, tanto en la gestión de los cambios como en la generación de páginas de gestión de datos.
      • Elegancia muy similar, acabado similar, aunque Ruby on Rails es un poco más “espartano” en cuanto a la construcción de las vistas.
    • Diferencias:
      • Utiliza Java como lenguaje principal en vez de Ruby.
      • Permite, aparte de aplicaciones web, generar de una forma rápida aplicaciones cliente no basadas en web (Java+Swing).
      • La construcción de las páginas dinámicas para las vistas está más evolucionada, con WOBuilder, una herramienta visual que permite unir los datos a mostrar con la situación en la que serán mostrados.
      • Ruby on Rails parece un poco más eficiente, y lleva un poco más allá el concepto de metaprogramación.
      • WebObjects proporciona herramientas gráficas para crear las entidades y relacionarlas entre sí.

¿Qué es WebObjects? Definiciones “serias y exactas” las hay por toda la web, así que la única que falta es la mía: informal y totalmente inexacta. Allá vamos: WebObjects es una tecnología, y un conjunto de herramientas para el desarrollo y explotación de aplicaciones web y servicios web.

¿Para qué sirve WebObjects? Pues viendo la definición (espero que no os sintáis timados): para desarrollar aplicaciones web y servicios web… de una forma elegante, rápida, sencilla y eficiente.

Me quedé igual… ¿me lo explica? Un poco de autobiografía:

Llevaba años programando algunas aplicaciones web de servidor, para ello utilizaba HTML+PHP+MySQL. Las páginas se generaban dinámicamente a base de incrustar un código en PHP que era capaz de extraer información de una BD de MySQL. Ese código estaba “incrustado” en la propia página HTML (renombrada como .php), y para nada era orientado a objeto. El desarrollo para Web no se parecía para nada a lo que habíamos hecho anteriormente en software para las plataformas: nada de orientación a objetos, nada de separación modelo/vista/controlador, etc. Ahí teníamos liado código HTML, código PHP y sentencias MySQL, todo “rebujao”. Con pericia y autodisciplina eras capaz de utilizar las importaciones o inclusiones para “separar” un poquillo el MySQL. El código PHP hacía manipulaciones tanto de los datos a presentar como de los datos a guardar… Además, para cada entidad (o pseudoobjeto) presente en la BD mediante una tabla había que “currarse” una página de altas, otra de bajas, una de modificación y otra para borrar… un tedio. Aún así uno acababa orgulloso de su trabajo.

Me encontraba como cuando programaba microcontroladores y tenía que basarme en mi pericia y autodisciplina para intentar que los structs y librerías se comportaran de forma parecida a los objetos.

Mi aplicación crecía y crecía y había que rehacer las páginas cada vez que cambiabas las entidades. Si hacía un buen diseño software de las entidades mis sentencias SQL empezaban a poblarse de “inner join’s”… buf! prefiero no recordarlo.

Un día empecé a desarrollar una web que servía mapas dinámicamente, utilizando la API de Google Maps y sacando datos de MySQL. Hice una aplicación con PHP y logré ver el mapa que quería pero… ¿soportaría la aplicación muchas consultas? ¿sería fácil de mantener? La cantidad de inner joins era preocupante, y sobre la eficiencia… la gran mayoría de las veces no existía ningún cambio en los datos pero había que ejecutar todas aquellas consultas cada vez que había que refrescar la página. Como maqueta estaba bien pero hacía falta buscar otra cosa.

También tenía la necesidad de que unos dispositivos móviles cambiaran la información de la base de datos y, en seguida, me estaba enfrentando a 20 conexiones simultáneas, todas de ellas escribiendo datos, por lo que mi SGBD (Sistema Gestor de Bases de Datos) empezaba a “trontollar”.

Un día buscando cómo salir del atolladero me encontré de narices con el WebObjects. Ya lo había visto cuando aún no tenía tan claras mis necesidades y no había conseguido entender de qué forma podía ayudarme pero, cuando volví a leer por segunda vez su descripción y características… voilà!!!

WebObjects es muchas cosas: para empezar es una tecnología de servidor (como pueda ser PHP) para generar páginas dinámicas. Pero a diferencia de PHP no es un lenguaje, sino un marco de trabajo que utiliza Java como lenguaje principal para la manipulación de los datos o de las presentaciones.

Los datos en WebObjects pueden seguir descansando en una BD, como MySQL, Oracle, etc. a través de JDBC (y otros conectores) pero… no los utiliza desde la BD. Una aplicación desarrollada con WebObjects consiste en un servidor de aplicaciones en forma de ejecutable para Java Virtual Machine, que interacciona con los datos de la base de datos, pero que utiliza objetos Java para trabajar. Es decir: la base de datos es la copia persistente en tablas de los objetos java de la aplicación. La aplicación utiliza los objetos Java de una forma similar a la que una MMU utiliza la caché de memoria, me explico: la primera vez que se requiere un registro de la base de datos la aplicación Java lo pide y crea un objeto Java con esos datos y relaciones. Posteriormente, cada consulta o modificación que se haga de ese objeto se realiza sobre el objeto Java, y sólo las modificaciones se realizan también sobre la base de datos. Esto hace que los accesos a la base de datos sean mucho menos numerosos y también reduce drásticamente el número de conexiones: si la aplicación Java está bien diseñada puede soportar todas las conexiones posibles y el SGBD sólo ha de soportar una: la que le une con la aplicación Java.

Aparte de la eficiencia para con la base de datos WebObjects nos proporciona algo muy interesante: un lenguaje de programación orientado a objetos, que además está muy extendido, el Java. Gracias a él pueden utilizarse todos los beneficios de la orientación a objeto tanto para generar las páginas dinámicas como para manipular la información.

Otro punto muy destacable es que nos permite separar de una forma elegante y rigurosa el modelo, la vista y el controlador. Las páginas HTML que funcionan como vistas contienen “incrustaciones” de elementos que permiten generar contenido dinámico, pero esas “incrustaciones” no son ni lenguaje Java (es decir, ni lógica de negocio) ni tampoco código SQL (es decir, ni gestión de los datos ni de la persistencia). Las “incrustaciones” no son más que “ligaduras” entre la información a mostrar y el lugar dentro de las vistas en la que se va a mostrar esa información. Gracias a estas “ligaduras” es posible mantener las vistas alejadas de toda la lógica de negocio y de la manipulación de los registros de la base de datos.

¿Qué ganamos también? Independencia de la tecnología de bases de datos. Si tenemos un conector JDBC para nuestro SGBD, entonces WebObjects puede utilizar ese SGBD. Y además olvidaremos el código SQL, el motor de persistencia de WebObjects nos permitirá realizar nuestras búsquedas y actualizaciones en los objetos de memoria y, transparentemente, consultará y modificará los registros que crea necesarios. Utilizaremos los sistemas de búsqueda e iteración de Java sin saber si los datos deberán ser traídos de la base de datos o no, modificaremos las propiedades de los objetos sin saber si esto conllevará una sentencia de actualización sobre la base de datos.

Sigamos con las ventajas peculiares de WebObjects, hay una que me encanta, porque es muy “agile modelling”: sólo hemos de cambiar la definición de las entidades en un sólo sitio. Para que nos hagamos una idea interioricemos la siguiente frase: los objetos que la aplicación maneja forman parte de una clase, para ser guardados y recuperados de una base de datos estas clases deben ser reescritas en forma de entidades, y los objetos que pertenecen a las clases serán, por tanto, registros en las tablas asociadas a esas entidades.

Un objeto pepe de la clase Persona es un registro o fila dentro de la tabla persona que está descrita mediante la entidad Persona en un diagrama ER (Entidad Relación).

Imaginemos que en la clase Persona queremos añadir un nuevo atributo edad: WebObjects sólo nos pedirá que en un modelo añadamos ese campo. Tanto la clase Persona como las vistas, como la tabla persona de la BD reflejarán ese atributo y, lo que es mejor: lo sabrán gestionar.

Y por último algo que me hizo pensar que había gastado demasiado tiempo con el PHP… WebObjects genera directamente todas las páginas necesarias para poder gestionar todos los objetos de todas las clases de una manera inmediata y, además, configurable. Es decir, que con sólo decir que existe una entidad Persona no sólo nos crea una clase Persona y nos permite manejarla transparentemente, sino que además de crear la tabla persona en la BD también crea todas las vistas de altas, bajas, modificaciones, búsquedas y listados de personas, todas ellas personalizables mediante un sistema de reglas.

Ah, sé que había dicho “por último” pero es que falta algo: todo lo que hace, evidentemente, se sustenta en creación automática de código (generación de vistas, generación de código SQL, etc…) pero es que, además, la creación de ese código no es puntual, sino que es constante. Es decir, no es que tengamos un asistente a principio del proyecto que genere todo el código y que, a cada cambio, haya que volver a generar, ni mucho menos, sino que… EN TIEMPO DE EJECUCIÓN LA APLICACIÓN ES CAPAZ DE CONSULTAR SU MODELO Y GENERAR LAS PÁGINAS DE GESTIÓN DE ESA INFORMACIÓN, Y TAMBIÉN GENERA EN TIEMPO REAL LA CLASE ASOCIADA A LAS ENTIDADES. Esto lleva al extremo lo de que los cambios sólo hay que realizarlos en un sitio (paradigma “no te repitas”, paradigma “viajar con poco equipaje”), y además también lleva al extremo la anticipación o bienvenida a los cambios (paradigma “el cambio existe y ocurre, acéptalo”). A menos que específicamente se quiera crear una definición estática de la clase Persona no existirá un fichero Persona.java en la aplicación, por lo que no habrá que modificarlo nunca. Aunque en ejecución podremos disfrutar de la existencia de la clase Persona como si la hubiéramos codificado.

Pero… ¿cómo hace todo eso? Altoooo… responder a eso es una misión demasiado ambiciosa para el trabajo que tenía pensado hacer hoy… conmutemos la pregunta por: ¿cuál sería la forma normal de trabajar con WebObjetcs? ¿Cuál sería nuestro día a día?

¿Cuál sería nuestro día a día con WebObjects? Estos son los pasos que yo doy:

  1. Utilizo una aplicación de edición de UML para realizar un diseño orientado a objetos de la solución que quiero llevar a cabo, aprovechando para ello todas las bondades de la orientación a objetos.
  2. Con EOModeler creo un modelo Enterprise Objects (muy parecido a un modelo Entidad Relación) en el que describo la conexión que quiero tener con la base de datos que quiero que se encargue de mantener los datos.
  3. Aún en EOModeler construyo un modelo estático del tipo Entidad Relación que “case” con el diseño en UML que creé en el punto 1. Para ello presto especial atención a cómo quiero que se mapeen las relaciones entre clases madre e hija (herencia) sobre el diagrama Entidad Relación (que no entiende de herencias). Normalmente suelo optar por el Mapeado Vertical, aunque más tarde puedo decidir cambiarlo si hay optimizaciones para la eficiencia que resulten claramente provechosas.
  4. Hago que EOModeler genere todas las sentencias SQL necesarias para crear todas las tablas en nuestra base de datos. No tengo que crearlas a mano, ni la estructura de las tablas ni la gestión de su contenido, todo eso nos lo evita WebObjects.
  5. Una vez que está hecho el modelo abro XCode y le digo al asistente que quiero desarrollar un proyecto basado en la plantilla Direct to Web de WebObjects, y le digo que quiero que tenga soporte para servir servicios web y también para consumirlos.
  6. Cuando el asistente me pregunte por el modelo EOModel, le indico que debe usar el que desarrollé con EOModeler entre los puntos 2 y 4. El asistente se hará una copia de ese modelo y nunca utilizará el original, esta copia se encontrará dentro de la carpeta del proyecto.
  7. Tal como se abre el proyecto, se compila y nos aparece una web para gestionar todas las altas, bajas y modificaciones de todas las entidades del modelo. La primera ventana será un login en el que podré marcar “asistente” y pulsar para entrar.
  8. Mientras recorro las páginas creadas, si alguna de ellas no me gusta, puedo pulsar el botón “Customize” para traer ante mí un asistente en forma de aplicación Java que nos permitirá definir, mediante unas reglas, qué queremos que se vea y qué no. Nos permitirá navegar por los objetos y también utilizar las relaciones para ir a buscar información complementaria que se encuentre en otros objetos.
  9. Una vez que la web está casi a mi gusto comenzaré a desarrollar los WebComponents, que son los que realmente conseguirán que la aplicación se vea como yo la quiero.
  10. Si necesito implementar lógicas de negocio concretas puedo empezar a “congelar” ciertas clases (crear los ficheros Java de la clase) y modificarlas añadiéndoles métodos, etc.
  11. Si he de dar servicios web los creo (eso ya es menos inmediato, ya que no he conseguido integrar en el mismo proyecto la tecnología “Direct to Web” y la “Direct to Web Services”, así que los servicios web acabo haciéndolos “a mano”).
  12. Al final la web que yo quiero suele estar hecha enteramente a base de WebComponents, aunque me guardo todas las páginas “Direct to Web” como una forma de modificar y consultar los datos a bajo nivel para las tareas de mantenimiento y prueba de la aplicación. Si le añado un buen mecanismo de autenticación, las páginas “Direct to Web” pueden servir para usuarios administradores u operadores, que requieran acceso directo a datos para subsanar errores de los usuarios, etc.
  13. Y si hay un nuevo cambio, toco el modelo con EOModeler de nuevo. Intentando, eso sí, que no se me quede muy desactualizado el diagrama UML.

A continuación alguna bibliografía:

Páginas de desarrollo sobre Mac OS X

Hola de nuevo. Cuando uno empieza en esto de programar en Mac OS X se encuentra solito. Por suerte existe la ADC de Apple, cuya pertenencia es gratuita y te da información equivalente a la que tendría un desarrollador .NET con soporte MSDN. Las listas de correo además te dan gran parte de ese soporte personalizado, si uno sabe preguntar, claro.

Sin embargo internet nos ofrece las experiencias de desarrolladores de software sobre Mac OS X, de forma que nos podamos nutrir de ellas y, alcanzado cierto nivel, incluso complementarlas.

Voy a citar una lista que (como ya avisé) no es exhaustiva, de sitios que me parecen de gran utilidad práctica, ya que siguen en mi retina por que, básicamente, me sacaron de algún apuro.

Mac Programadores : Sitio para la programación Mac OS X en castellano. De vez en cuando se hacen eco de alguna oferta de trabajo para desarrolladores Mac OS X. Es muy interesante la sección de reportajes, ya que suelen publicar en PDF guías, tutoriales o información en un formato más serio de lo que se hace en la página que estás leyendo ;o) .

Cocoa Samurai : Este hombre me sacó de algún atolladero en el pasado. En especial son interesantes sus divagaciones sobre el futuro de la programación en Mac OS X (iPhone, etc…) y, sobre todo, su pequeña intro al desarrollo con Core Data.

CocoaCast : Iniciación a la programación Mac OS X (más concretamente cocoa) utilizando los podcast (audio e imágenes) en una especie de curso.

Cocoa Radio : En realidad Cocoa Radio no es exactamente un recurso para el programador, sino un foro donde se suele hablar de las tecnologías (un buen sitio para estar al día) y donde se entrevista a los principales desarrolladores de aplicaciones Mac OS X de la comunidad internacional.

Late Night Cocoa : Muy similar a Cocoa Radio pero quizá deriva más hacia los tutoriales. Ya tenemos 3 emisoras!!

Mac Dev Center: Basándome en mi experiencia… cada vez que busques información sobre cualquier tecnología de programación para Mac te va a aparecer algún artículo en este sitio con una pequeña introducción. Es rara la vez que puedas llegar a profundizar gracias a ellos, pero cumple muy bien su función en los inicios de cualquier desarrollo. Es un gran escaparate para publicitar libros del tema de la editorial que más esfuerzos dedica a cubrir nuestro área de interés.

Estos no son todos los sitios ni los recursos pero todo es empezar…

Configurando un Mac para desarrollo

Hola! Un Mac viene mucho más preparado para desarrollar software de lo que cualquier profano o switcher (novato en el mundo Mac que viene de otras plataformas) podría pensar.

De fábrica el Mac viene con un entorno de programación más que capacitado para desarrollar aplicaciones binarias, aplicaciones Java, aplicaciones Web, servicios Web, etc. Lo que pasa es que viene en el DVD de instalación, no viene pre-instalado.

El entorno de programación se llama XCode, y es capaz de ayudarnos a programar cualquier tipo de aplicación basado en cualquiera de los lenguajes usuales: C, Objective C, C/C++, Java, …

Deberemos buscar las herramientas en el DVD de instalación de Mac OS X, y se nos instalarán en el directorio /Developer de nuestro Mac.

También sería interesante empezar a instalar cosas que seguramente acabaremos utilizando en nuestro camino de desarrolladores:

De momento hay para comenzar a entretenerse. Cosas que no recomiendo de inicio:

  1. Ponerse a instalar otro Apache diferente al que viene en el sistema, ni en paquete con MySQL ni nada de eso.
  2. Ponerse a instalar un servidor de SubVersion sin haber utilizado antes el control de versiones con algún otro servidor externo a vuestro Mac. Primero instalar el cliente y buscar algún proyecto del que poder hacer checkout y ver que todo funciona, y entender el funcionamiento.
  3. Ponerse a cacharrear con Eclipse y NetBeans hasta haber aprovechado un poquito el XCode. No digo que no sean buenas herramientas (que lo son) pero mejor antes probar las cosas de la forma que podamos contar con todo el apoyo Mac OS X, para luego explorar cosas más orientadas a multiplataforma.

Por último en el tema de la documentación, os tenéis que dar de alta (es gratuito) en el ADC (Apple Developer Connection) y aprovechar la documentación existente allí y en las listas de correo de desarrolladores de Apple.

Una vez dados de alta podremos acceder a todas las actualizaciones de las herramientas de desarrollador de Apple. Esta es una lista bastante rápida de herramientas a instalar, todo el mundo tiene la suya, esta es la que yo recomendaría a aquél programador ansioso de hacer cosas con Mac.

A ver si pronto os hablo de las tecnologías de desarrollo Mac (las que conozco) y así véis para qué empiezan a valer algunas “palabrotas” como Cocoa, WebObjects, etc.

Un saludo!

Declaración de intenciones (no muy serias?)

Hola!! En fin… otro blog más que pruebo. ¿Se quedará aquí? No lo sabemos (ni siquiera yo).

La diferencia con éste es que tengo una motivación, es el único blog que no hago con la intención de “vamos a ver qué es esto de los blogs”, sino que lo empiezo con la intención de acercar la programación Mac a la comunidad hispana.

Me ha pasado que recientemente que me he convertido en un “vendedor de macs”, ya van unos cuantos “incautos” que han acabado sucumbiendo a la manzana después de pedir mi opinión y, lo peor, es que varios de esos “incautos” son informáticos también.

Lo que suele pasar es lo de siempre: uno sólo aprende a dominar una tecnología cuando tiene un encargo o trabaja con ella, y eso en España y para la mayoría de esos “incautos” es una utopía. Sin embargo el que aquí suscribe ha tenido la gran suerte de poder dedicarse profesionalmente a desarrollar sobre Mac, por una de aquellas conjunciones astrales que hacen que sea la plataforma más adecuada para la aplicación… y que tengas unos jefes que sepan verlo. ;o)

En este último año he podido desarrollar utilizando (dentro de lo que sería la “familia tecnológica” Apple) tecnologías tan interesantes como (perdón por mezclar conceptos en mi lista): Cocoa, Core Data, WebObjects, … Casi todos estos términos antes de comenzar eran para mí como lo son para muchos de los que ahora lo leéis… cosas raras que no sabía ni lo que eran ni para qué se usaban, o lo que es más importante: no tenía ni idea de lo que esas palabritas podrían hacer por mí.

El objetivo de este blog es bastante simple: acercar esas tecnologías a la gente, por medio de explicaciones sencillas, abusando del lenguaje y de los términos, pero intentando no salirse de la practicidad, la didáctica y el sentido común. Aviso: TODAS mis definiciones serán inexactas y equivocadas a priori, no pretendo sentar cátedra ni ser inambiguo o convertir estas páginas en una referencia, ni definir con exactitud las cosas, sólo quiero acercar la tecnología a la gente, para así poder disfrutar un poquito más. Para poder conocer a una serie de “colaboradores” que debe haber por ahí, y quizá que esta sea una cocina y hablemos y charlemos con las manos en harina.

En fin, ojalá estas páginas sean útiles y os permitan saber por dónde empezar en esto de la programación manzanera.

Tx.